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