Add the 'I' format for both pack and unpack
[jansson.git] / src / pack_unpack.c
1 /*
2  * Copyright (c) 2009-2011 Petri Lehtinen <petri@digip.org>
3  * Copyright (c) 2011 Graeme Smecher <graeme.smecher@mail.mcgill.ca>
4  *
5  * Jansson is free software; you can redistribute it and/or modify
6  * it under the terms of the MIT license. See LICENSE for details.
7  */
8
9 #include <jansson.h>
10 #include "jansson_private.h"
11
12 typedef struct {
13     const char *fmt;
14     char token;
15     json_error_t *error;
16     size_t flags;
17     int line;
18     int column;
19 } scanner_t;
20
21 static const char *type_names[] = {
22     "object",
23     "array",
24     "string",
25     "integer",
26     "real",
27     "true",
28     "false",
29     "null"
30 };
31
32 #define type_name(x) type_names[json_typeof(x)]
33
34 static void next_token(scanner_t *s)
35 {
36     const char *t = s->fmt;
37     s->column++;
38
39     /* skip space and ignored chars */
40     while(*t == ' ' || *t == '\t' || *t == '\n' || *t == ',' || *t == ':') {
41         if(*t == '\n') {
42             s->line++;
43             s->column = 1;
44         }
45         else
46             s->column++;
47
48         t++;
49     }
50
51     s->token = *t;
52
53     t++;
54     s->fmt = t;
55 }
56
57 static void set_error(scanner_t *s, const char *fmt, ...)
58 {
59     va_list ap;
60     va_start(ap, fmt);
61     jsonp_error_vset(s->error, s->line, s->column, fmt, ap);
62     va_end(ap);
63 }
64
65 static json_t *pack(scanner_t *s, va_list *ap);
66
67 static json_t *pack_object(scanner_t *s, va_list *ap)
68 {
69     json_t *object = json_object();
70     next_token(s);
71
72     while(s->token != '}') {
73         const char *key;
74         json_t *value;
75
76         if(!s->token) {
77             set_error(s, "Unexpected end of format string");
78             goto error;
79         }
80
81         if(s->token != 's') {
82             set_error(s, "Expected format 's', got '%c'\n", *s->fmt);
83             goto error;
84         }
85
86         key = va_arg(*ap, const char *);
87         if(!key) {
88             set_error(s, "NULL object key");
89             goto error;
90         }
91
92         next_token(s);
93
94         value = pack(s, ap);
95         if(!value)
96             goto error;
97
98         if(json_object_set_new(object, key, value)) {
99             set_error(s, "Unable to add key \"%s\"", key);
100             goto error;
101         }
102
103         next_token(s);
104     }
105
106     return object;
107
108 error:
109     json_decref(object);
110     return NULL;
111 }
112
113 static json_t *pack_array(scanner_t *s, va_list *ap)
114 {
115     json_t *array = json_array();
116     next_token(s);
117
118     while(s->token != ']') {
119         json_t *value;
120
121         if(!s->token) {
122             set_error(s, "Unexpected end of format string");
123             goto error;
124         }
125
126         value = pack(s, ap);
127         if(!value)
128             goto error;
129
130         if(json_array_append_new(array, value)) {
131             set_error(s, "Unable to append to array");
132             goto error;
133         }
134
135         next_token(s);
136     }
137     return array;
138
139 error:
140     json_decref(array);
141     return NULL;
142 }
143
144 static json_t *pack(scanner_t *s, va_list *ap)
145 {
146     switch(s->token) {
147         case '{':
148             return pack_object(s, ap);
149
150         case '[':
151             return pack_array(s, ap);
152
153         case 's': /* string */
154         {
155             const char *str = va_arg(*ap, const char *);
156             if(!str) {
157                 set_error(s, "NULL string");
158                 return NULL;
159             }
160             return json_string(str);
161         }
162
163         case 'n': /* null */
164             return json_null();
165
166         case 'b': /* boolean */
167             return va_arg(*ap, int) ? json_true() : json_false();
168
169         case 'i': /* integer from int */
170             return json_integer(va_arg(*ap, int));
171
172         case 'I': /* integer from json_int_t */
173             return json_integer(va_arg(*ap, json_int_t));
174
175         case 'f': /* real */
176             return json_real(va_arg(*ap, double));
177
178         case 'O': /* a json_t object; increments refcount */
179             return json_incref(va_arg(*ap, json_t *));
180
181         case 'o': /* a json_t object; doesn't increment refcount */
182             return va_arg(*ap, json_t *);
183
184         default:
185             set_error(s, "Unrecognized format character '%c'", s->token);
186             return NULL;
187     }
188 }
189
190 static int unpack(scanner_t *s, json_t *root, va_list *ap);
191
192 static int unpack_object(scanner_t *s, json_t *root, va_list *ap)
193 {
194     int ret = -1;
195     int wildcard = 0;
196
197     /* Use a set (emulated by a hashtable) to check that all object
198        keys are accessed. Checking that the correct number of keys
199        were accessed is not enough, as the same key can be unpacked
200        multiple times.
201     */
202     hashtable_t *key_set;
203
204     if(!(s->flags & JSON_UNPACK_ONLY)) {
205         key_set = hashtable_create(jsonp_hash_key, jsonp_key_equal, NULL, NULL);
206         if(!key_set) {
207             set_error(s, "Out of memory");
208             return -1;
209         }
210     }
211
212     if(!json_is_object(root)) {
213         set_error(s, "Expected object, got %s", type_name(root));
214         goto error;
215     }
216     next_token(s);
217
218     while(s->token != '}') {
219         const char *key;
220         json_t *value;
221
222         if(wildcard) {
223             set_error(s, "Expected '}' after '*', got '%c'", s->token);
224             goto error;
225         }
226
227         if(!s->token) {
228             set_error(s, "Unexpected end of format string");
229             goto error;
230         }
231
232         if(s->token == '*') {
233             wildcard = 1;
234             next_token(s);
235             continue;
236         }
237
238         if(s->token != 's') {
239             set_error(s, "Expected format 's', got '%c'\n", *s->fmt);
240             goto error;
241         }
242
243         key = va_arg(*ap, const char *);
244         if(!key) {
245             set_error(s, "NULL object key");
246             goto error;
247         }
248
249         next_token(s);
250
251         value = json_object_get(root, key);
252         if(unpack(s, value, ap))
253             goto error;
254
255         if(!(s->flags & JSON_UNPACK_ONLY))
256             hashtable_set(key_set, (void *)key, NULL);
257
258         next_token(s);
259     }
260
261     if(s->flags & JSON_UNPACK_ONLY)
262         wildcard = 1;
263
264     if(!wildcard && key_set->size != json_object_size(root)) {
265         long diff = (long)json_object_size(root) - (long)key_set->size;
266         set_error(s, "%li object items left unpacked", diff);
267         goto error;
268     }
269
270     ret = 0;
271
272 error:
273     if(!(s->flags & JSON_UNPACK_ONLY))
274         hashtable_destroy(key_set);
275
276     return ret;
277 }
278
279 static int unpack_array(scanner_t *s, json_t *root, va_list *ap)
280 {
281     size_t i = 0;
282     int wildcard = 0;
283
284     if(!json_is_array(root)) {
285         set_error(s, "Expected array, got %s", type_name(root));
286         return -1;
287     }
288     next_token(s);
289
290     while(s->token != ']') {
291         json_t *value;
292
293         if(wildcard) {
294             set_error(s, "Expected ']' after '*', got '%c'", s->token);
295             return -1;
296         }
297
298         if(!s->token) {
299             set_error(s, "Unexpected end of format string");
300             return -1;
301         }
302
303         if(s->token == '*') {
304             wildcard = 1;
305             next_token(s);
306             continue;
307         }
308
309         value = json_array_get(root, i);
310         if(!value) {
311             set_error(s, "Array index %lu out of range", (unsigned long)i);
312             return -1;
313         }
314
315         if(unpack(s, value, ap))
316             return -1;
317
318         next_token(s);
319         i++;
320     }
321
322     if(s->flags & JSON_UNPACK_ONLY)
323         wildcard = 1;
324
325     if(!wildcard && i != json_array_size(root)) {
326         long diff = (long)json_array_size(root) - (long)i;
327         set_error(s, "%li array items left upacked", diff);
328         return -1;
329     }
330
331     return 0;
332 }
333
334 static int unpack(scanner_t *s, json_t *root, va_list *ap)
335 {
336     switch(s->token)
337     {
338         case '{':
339             return unpack_object(s, root, ap);
340
341         case '[':
342             return unpack_array(s, root, ap);
343
344         case 's':
345             if(!json_is_string(root)) {
346                 set_error(s, "Expected string, got %s", type_name(root));
347                 return -1;
348             }
349
350             if(!(s->flags & JSON_VALIDATE_ONLY)) {
351                 const char **str;
352
353                 str = va_arg(*ap, const char **);
354                 if(!str) {
355                     set_error(s, "NULL string");
356                     return -1;
357                 }
358
359                 *str = json_string_value(root);
360             }
361             return 0;
362
363         case 'i':
364             if(!json_is_integer(root)) {
365                 set_error(s, "Expected integer, got %s", type_name(root));
366                 return -1;
367             }
368
369             if(!(s->flags & JSON_VALIDATE_ONLY))
370                 *va_arg(*ap, int*) = json_integer_value(root);
371
372             return 0;
373
374         case 'I':
375             if(!json_is_integer(root)) {
376                 set_error(s, "Expected integer, got %s", type_name(root));
377                 return -1;
378             }
379
380             if(!(s->flags & JSON_VALIDATE_ONLY))
381                 *va_arg(*ap, json_int_t*) = json_integer_value(root);
382
383             return 0;
384
385         case 'b':
386             if(!json_is_boolean(root)) {
387                 set_error(s, "Expected true or false, got %s", type_name(root));
388                 return -1;
389             }
390
391             if(!(s->flags & JSON_VALIDATE_ONLY))
392                 *va_arg(*ap, int*) = json_is_true(root);
393
394             return 0;
395
396         case 'f':
397             if(!json_is_real(root)) {
398                 set_error(s, "Expected real, got %s", type_name(root));
399                 return -1;
400             }
401
402             if(!(s->flags & JSON_VALIDATE_ONLY))
403                 *va_arg(*ap, double*) = json_real_value(root);
404
405             return 0;
406
407         case 'F':
408             if(!json_is_number(root)) {
409                 set_error(s, "Expected real or integer, got %s",
410                           type_name(root));
411                 return -1;
412             }
413
414             if(!(s->flags & JSON_VALIDATE_ONLY))
415                 *va_arg(*ap, double*) = json_number_value(root);
416
417             return 0;
418
419         case 'O':
420             if(!(s->flags & JSON_VALIDATE_ONLY))
421                 json_incref(root);
422             /* Fall through */
423
424         case 'o':
425             if(!(s->flags & JSON_VALIDATE_ONLY))
426                 *va_arg(*ap, json_t**) = root;
427
428             return 0;
429
430         case 'n':
431             /* Never assign, just validate */
432             if(!json_is_null(root)) {
433                 set_error(s, "Expected null, got %s", type_name(root));
434                 return -1;
435             }
436             return 0;
437
438         default:
439             set_error(s, "Unknown format character '%c'", s->token);
440             return -1;
441     }
442 }
443
444 json_t *json_vpack_ex(json_error_t *error, size_t flags,
445                       const char *fmt, va_list ap)
446 {
447     scanner_t s;
448     json_t *value;
449
450     jsonp_error_init(error, "");
451
452     if(!fmt || !*fmt) {
453         jsonp_error_set(error, 1, 1, "Null or empty format string");
454         return NULL;
455     }
456
457     s.error = error;
458     s.flags = flags;
459     s.fmt = fmt;
460     s.line = 1;
461     s.column = 0;
462
463     next_token(&s);
464     value = pack(&s, &ap);
465
466     next_token(&s);
467     if(s.token) {
468         json_decref(value);
469         set_error(&s, "Garbage after format string");
470         return NULL;
471     }
472
473     return value;
474 }
475
476 json_t *json_pack_ex(json_error_t *error, size_t flags, const char *fmt, ...)
477 {
478     json_t *value;
479     va_list ap;
480
481     va_start(ap, fmt);
482     value = json_vpack_ex(error, flags, fmt, ap);
483     va_end(ap);
484
485     return value;
486 }
487
488 json_t *json_pack(const char *fmt, ...)
489 {
490     json_t *value;
491     va_list ap;
492
493     va_start(ap, fmt);
494     value = json_vpack_ex(NULL, 0, fmt, ap);
495     va_end(ap);
496
497     return value;
498 }
499
500 int json_vunpack_ex(json_t *root, json_error_t *error, size_t flags,
501                     const char *fmt, va_list ap)
502 {
503     scanner_t s;
504
505     jsonp_error_init(error, "");
506
507     if(!fmt || !*fmt) {
508         jsonp_error_set(error, 1, 1, "Null or empty format string");
509         return -1;
510     }
511
512     s.error = error;
513     s.flags = flags;
514     s.fmt = fmt;
515     s.line = 1;
516     s.column = 0;
517
518     next_token(&s);
519
520     if(unpack(&s, root, &ap))
521         return -1;
522
523     next_token(&s);
524     if(s.token) {
525         set_error(&s, "Garbage after format string");
526         return -1;
527     }
528
529     return 0;
530 }
531
532 int json_unpack_ex(json_t *root, json_error_t *error, size_t flags, const char *fmt, ...)
533 {
534     int ret;
535     va_list ap;
536
537     va_start(ap, fmt);
538     ret = json_vunpack_ex(root, error, flags, fmt, ap);
539     va_end(ap);
540
541     return ret;
542 }
543
544 int json_unpack(json_t *root, const char *fmt, ...)
545 {
546     int ret;
547     va_list ap;
548
549     va_start(ap, fmt);
550     ret = json_vunpack_ex(root, NULL, 0, fmt, ap);
551     va_end(ap);
552
553     return ret;
554 }