1    | /*
2    |  * Copyright (c) 2007-2014, Lloyd Hilaiel <me@lloyd.io>
3    |  *
4    |  * Permission to use, copy, modify, and/or distribute this software for any
5    |  * purpose with or without fee is hereby granted, provided that the above
6    |  * copyright notice and this permission notice appear in all copies.
7    |  *
8    |  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
9    |  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
10   |  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
11   |  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
12   |  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
13   |  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
14   |  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
15   |  */
16   | 
17   | /**
18   |  * Interface to YAJL's JSON generation facilities.
19   |  **/
20   | 
21   | #include "yajl/yajl_gen.h"
22   | #include "yajl_buf.h"
23   | #include "yajl_encode.h"
24   | 
25   | #include <stdlib.h>
26   | #include <string.h>
27   | #include <stdio.h>
28   | #include <math.h>
29   | #include <stdarg.h>
30   | 
31   | typedef enum {
32   |     yajl_gen_start,
33   |     yajl_gen_map_start,
34   |     yajl_gen_map_key,
35   |     yajl_gen_map_val,
36   |     yajl_gen_array_start,
37   |     yajl_gen_in_array,
38   |     yajl_gen_complete,
39   |     yajl_gen_error
40   | } yajl_gen_state;
41   | 
42   | struct yajl_gen_t
43   | {
44   |     unsigned int flags;
45   |     unsigned int depth;
46   |     const char * indentString;
47   |     yajl_gen_state state[YAJL_MAX_DEPTH];
48   |     yajl_print_t print;
49   |     void * ctx; /* yajl_buf */
50   |     /* memory allocation routines */
51   |     yajl_alloc_funcs alloc;
52   | };
53   | 
54   | /*+
55   |  *  allow the modification of generator options subsequent to handle
56   |  *  allocation (via yajl_alloc)
57   |  *
58   |  *  \returns zero in case of errors, non-zero otherwise
59   |  +*/
60   | int
61   | yajl_gen_config(yajl_gen g, yajl_gen_option opt, ...)
62   | {
63   |     int rv = 1;
64   |     va_list ap;
65   |     va_start(ap, opt);
66   | 
67   |     switch(opt) {
68   |         case yajl_gen_beautify:
69   |         case yajl_gen_validate_utf8:
70   |         case yajl_gen_escape_solidus:
71   |             if (va_arg(ap, int)) g->flags |= opt;
72   |             else g->flags &= ~opt;
73   |             break;
74   |         case yajl_gen_indent_string: {
75   |             const char *indent = va_arg(ap, const char *);
76   |             g->indentString = indent;
77   |             for (; *indent; indent++) {
78   |                 if (*indent != '\n'
79   |                     && *indent != '\v'
80   |                     && *indent != '\f'
81   |                     && *indent != '\t'
82   |                     && *indent != '\r'
83   |                     && *indent != ' ')
84   |                 {
85   |                     g->indentString = NULL;
86   |                     rv = 0;
87   |                 }
88   |             }
89   |             break;
90   |         }
91   |         case yajl_gen_print_callback:
92   |             yajl_buf_free(g->ctx);
93   |             g->print = va_arg(ap, const yajl_print_t);
94   |             g->ctx = va_arg(ap, void *);
95   |             break;
96   |         default:
97   |             rv = 0;
98   |     }
99   | 
100  |     va_end(ap);
101  | 
102  |     return rv;
103  | }
104  | 
105  | 
106  | 
107  | /*+ allocate a generator handle
108  |  *
109  |  *  \param allocFuncs an optional pointer to a structure which allows
110  |  *                    the client to overide the memory allocation
111  |  *                    used by yajl.  May be NULL, in which case
112  |  *                    malloc/free/realloc will be used.
113  |  *
114  |  *  \returns an allocated handle on success, NULL on failure (bad params)
115  |  +*/
116  | yajl_gen
117  | yajl_gen_alloc(const yajl_alloc_funcs * afs)
118  | {
119  |     yajl_gen g = NULL;
120  |     yajl_alloc_funcs afsBuffer;
121  | 
122  |     /* first order of business is to set up memory allocation routines */
123  |     if (afs != NULL) {
124  |         if (afs->malloc == NULL || afs->realloc == NULL || afs->free == NULL) {
125  |             return NULL;
126  |         }
127  |     } else {
128  |         yajl_set_default_alloc_funcs(&afsBuffer);
129  |         afs = &afsBuffer;
130  |     }
131  | 
132  |     g = (yajl_gen) YA_MALLOC(afs, sizeof(struct yajl_gen_t));
133  |     if (!g) {
134  |         return NULL;
135  |     }
136  |     memset((void *) g, 0, sizeof(struct yajl_gen_t));
137  |     /* copy in pointers to allocation routines */
138  |     g->alloc = *afs;
139  | 
140  |     g->print = (yajl_print_t) &yajl_buf_append;
141  |     g->ctx = yajl_buf_alloc(&(g->alloc));
142  |     g->indentString = "    ";
143  | 
144  |     return g;
145  | }
146  | 
147  | /*+
148  |  *  Reset the generator state.  Allows a client to generate multiple json
149  |  *  entities in a stream. The "sep" string will be inserted to separate the
150  |  *  previously generated entity from the current, NULL means *no separation* of
151  |  *  entites (clients beware, generating multiple JSON numbers without a
152  |  *  separator, for instance, will result in ambiguous output)
153  |  *
154  |  *  Note: this call will not clear yajl's output buffer.  This may be
155  |  *  accomplished explicitly by calling yajl_gen_clear()
156  |  +*/
157  | void
158  | yajl_gen_reset(yajl_gen g, const char * sep)
159  | {
160  |     g->depth = 0;
161  |     memset((void *) &(g->state), 0, sizeof(g->state));
162  |     if (sep != NULL) {
163  |         g->print(g->ctx, sep, strlen(sep));
164  |     }
165  | }
166  | 
167  | /*+ free a generator handle +*/
168  | void
169  | yajl_gen_free(yajl_gen g)
170  | {
171  |     if (g->print == (yajl_print_t) &yajl_buf_append) {
172  |         yajl_buf_free((yajl_buf) g->ctx);
173  |     }
174  |     YA_FREE(&(g->alloc), g);
175  | }
176  | 
177  | #define INSERT_SEP                                                      \
178  |     if (g->state[g->depth] == yajl_gen_map_key ||                       \
179  |         g->state[g->depth] == yajl_gen_in_array) {                      \
180  |         g->print(g->ctx, ",", (size_t) 1);                              \
181  |         if ((g->flags & yajl_gen_beautify)) {                           \
182  |             g->print(g->ctx, "\n", (size_t) 1);                         \
183  |         }                                                               \
184  |     } else if (g->state[g->depth] == yajl_gen_map_val) {                \
185  |         g->print(g->ctx, ":", (size_t) 1);                              \
186  |         if ((g->flags & yajl_gen_beautify))  {                          \
187  |             g->print(g->ctx, " ", (size_t) 1);                          \
188  |         }                                                               \
189  |    }
190  | 
191  | #define INSERT_WHITESPACE                                               \
192  |     if ((g->flags & yajl_gen_beautify)) {                               \
193  |         if (g->state[g->depth] != yajl_gen_map_val) {                   \
194  |             unsigned int _i;                                            \
195  |             for (_i=0;_i<g->depth;_i++) {                               \
196  |                 g->print(g->ctx,                                        \
197  |                          g->indentString,                               \
198  |                          (size_t) strlen(g->indentString));             \
199  |             }                                                           \
200  |         }                                                               \
201  |     }
202  | 
203  | #define ENSURE_NOT_KEY                                  \
204  |     if (g->state[g->depth] == yajl_gen_map_key ||       \
205  |         g->state[g->depth] == yajl_gen_map_start)  {    \
206  |         return yajl_gen_keys_must_be_strings;           \
207  |     }                                                   \
208  | 
209  | /* check that we're not complete, or in error state.  in a valid state
210  |  * to be generating */
211  | #define ENSURE_VALID_STATE                        \
212  |     if (g->state[g->depth] == yajl_gen_error) {   \
213  |         return yajl_gen_in_error_state;                     \
214  |     } else if (g->state[g->depth] == yajl_gen_complete) {   \
215  |         return yajl_gen_generation_complete;                \
216  |     }
217  | 
218  | #define INCREMENT_DEPTH                                                 \
219  |     if (++(g->depth) >= YAJL_MAX_DEPTH) {                               \
220  |         return yajl_max_depth_exceeded;                                 \
221  |     }
222  | 
223  | #define DECREMENT_DEPTH                                                 \
224  |     if (--(g->depth) >= YAJL_MAX_DEPTH) {                               \
225  |         return yajl_gen_generation_complete;                            \
226  |     }
227  | 
228  | 
229  | #define APPENDED_ATOM \
230  |     switch (g->state[g->depth]) {                   \
231  |         case yajl_gen_start:                        \
232  |             g->state[g->depth] = yajl_gen_complete; \
233  |             break;                                  \
234  |         case yajl_gen_map_start:                    \
235  |         case yajl_gen_map_key:                      \
236  |             g->state[g->depth] = yajl_gen_map_val;  \
237  |             break;                                  \
238  |         case yajl_gen_array_start:                  \
239  |             g->state[g->depth] = yajl_gen_in_array; \
240  |             break;                                  \
241  |         case yajl_gen_map_val:                      \
242  |             g->state[g->depth] = yajl_gen_map_key;  \
243  |             break;                                  \
244  |         default:                                    \
245  |             break;                                  \
246  |     }
247  | 
248  | #define FINAL_NEWLINE                                                   \
249  |     if ((g->flags & yajl_gen_beautify) && g->state[g->depth] == yajl_gen_complete) { \
250  |         g->print(g->ctx, "\n", (size_t) 1);                             \
251  |     }
252  | 
253  | yajl_gen_status
254  | yajl_gen_integer(yajl_gen g, long long int number)
255  | {
256  |     char i[32];
257  |     int len;
258  | 
259  |     ENSURE_VALID_STATE; ENSURE_NOT_KEY; INSERT_SEP; INSERT_WHITESPACE;
260  |     len = sprintf(i, "%lld", number);
261  |     if (len < 0) {                     /* highly unlikely, perhaps impossible */
262  |         return yajl_gen_invalid_number;
263  |     }
264  |     g->print(g->ctx, i, (size_t) len);
265  |     APPENDED_ATOM;
266  |     FINAL_NEWLINE;
267  |     return yajl_gen_status_ok;
268  | }
269  | 
270  | #if defined(_WIN32) || defined(WIN32)
271  | #include <float.h>
272  | #define isnan _isnan
273  | #define isinf !_finite
274  | #endif
275  | 
276  | #if !defined(DBL_DIG)
277  | # if defined(__DBL_DIG__)
278  | #  define DBL_DIG	__DBL_DIG__
279  | # else
280  | #  define DBL_DIG	15		/* assumes binary64 IEEE 754 double */
281  | # endif
282  | #endif
283  | 
284  | /*+
285  |  *  generate a floating point number.  number may not be infinity or
286  |  *  NaN, as these have no representation in JSON.  In these cases the
287  |  *  generator will return 'yajl_gen_invalid_number'
288  |  +*/
289  | yajl_gen_status
290  | yajl_gen_double(yajl_gen g, double number)
291  | {
292  |     char i[32];
293  |     int len;
294  | 
295  |     ENSURE_VALID_STATE; ENSURE_NOT_KEY;
296  |     if (isnan(number) || isinf(number)) return yajl_gen_invalid_number;
297  |     INSERT_SEP; INSERT_WHITESPACE;
298  |     len = sprintf(i, "%.*g", DBL_DIG, number); /* xxx in theory we could/should
299  |                                                 * use DBL_DECIMAL_DIG for pure
300  |                                                 * serialization, but what about
301  |                                                 * to JSON readers that might not
302  |                                                 * be using IEEE 754 binary64 for
303  |                                                 * numbers? */
304  |     if (len < 0) {                    /* highly unlikely, or even impossible? */
305  |         return yajl_gen_invalid_number;
306  |     }
307  |     /*
308  |      * xxx perhaps forcing decimal notation should be controlled by a
309  |      * runtime-configurable option?
310  |      */
311  |     if (strspn(i, "0123456789-") == strlen(i)) {
312  |         strcat(i, ".0");
313  |     }
314  |     g->print(g->ctx, i, (size_t) len);
315  |     APPENDED_ATOM;
316  |     FINAL_NEWLINE;
317  |     return yajl_gen_status_ok;
318  | }
319  | 
320  | yajl_gen_status
321  | yajl_gen_number(yajl_gen g, const char * s, size_t l)
322  | {
323  |     ENSURE_VALID_STATE; ENSURE_NOT_KEY; INSERT_SEP; INSERT_WHITESPACE;
324  |     g->print(g->ctx, s, l);
325  |     APPENDED_ATOM;
326  |     FINAL_NEWLINE;
327  |     return yajl_gen_status_ok;
328  | }
329  | 
330  | yajl_gen_status
331  | yajl_gen_string(yajl_gen g, const unsigned char * str,
332  |                 size_t len)
333  | {
334  |     // if validation is enabled, check that the string is valid utf8
335  |     // XXX: This checking could be done a little faster, in the same pass as
336  |     // the string encoding
337  |     if (g->flags & yajl_gen_validate_utf8) {
338  |         if (!yajl_string_validate_utf8(str, len)) {
339  |             return yajl_gen_invalid_string;
340  |         }
341  |     }
342  |     ENSURE_VALID_STATE; INSERT_SEP; INSERT_WHITESPACE;
343  |     g->print(g->ctx, "\"", (size_t) 1);
344  |     yajl_string_encode(g->print, g->ctx, str, len, g->flags & yajl_gen_escape_solidus);
345  |     g->print(g->ctx, "\"", (size_t) 1);
346  |     APPENDED_ATOM;
347  |     FINAL_NEWLINE;
348  |     return yajl_gen_status_ok;
349  | }
350  | 
351  | yajl_gen_status
352  | yajl_gen_null(yajl_gen g)
353  | {
354  |     ENSURE_VALID_STATE; ENSURE_NOT_KEY; INSERT_SEP; INSERT_WHITESPACE;
355  |     g->print(g->ctx, "null", strlen("null"));
356  |     APPENDED_ATOM;
357  |     FINAL_NEWLINE;
358  |     return yajl_gen_status_ok;
359  | }
360  | 
361  | yajl_gen_status
362  | yajl_gen_bool(yajl_gen g, int boolean)
363  | {
364  |     const char * val = boolean ? "true" : "false";
365  | 
366  |     ENSURE_VALID_STATE; ENSURE_NOT_KEY; INSERT_SEP; INSERT_WHITESPACE;
367  |     g->print(g->ctx, val, (size_t)strlen(val));
368  |     APPENDED_ATOM;
369  |     FINAL_NEWLINE;
370  |     return yajl_gen_status_ok;
371  | }
372  | 
373  | yajl_gen_status
374  | yajl_gen_map_open(yajl_gen g)
375  | {
376  |     ENSURE_VALID_STATE; ENSURE_NOT_KEY; INSERT_SEP; INSERT_WHITESPACE;
377  |     INCREMENT_DEPTH;
378  | 
379  |     g->state[g->depth] = yajl_gen_map_start;
380  |     g->print(g->ctx, "{", (size_t) 1);
381  |     if ((g->flags & yajl_gen_beautify)) {
382  |         g->print(g->ctx, "\n", (size_t) 1);
383  |     }
384  |     FINAL_NEWLINE;
385  |     return yajl_gen_status_ok;
386  | }
387  | 
388  | yajl_gen_status
389  | yajl_gen_map_close(yajl_gen g)
390  | {
391  |     ENSURE_VALID_STATE;
392  |     DECREMENT_DEPTH;
393  | 
394  |     if ((g->flags & yajl_gen_beautify)) {
395  |         g->print(g->ctx, "\n", (size_t) 1);
396  |     }
397  |     APPENDED_ATOM;
398  |     INSERT_WHITESPACE;
399  |     g->print(g->ctx, "}", (size_t) 1);
400  |     FINAL_NEWLINE;
401  |     return yajl_gen_status_ok;
402  | }
403  | 
404  | yajl_gen_status
405  | yajl_gen_array_open(yajl_gen g)
406  | {
407  |     ENSURE_VALID_STATE; ENSURE_NOT_KEY; INSERT_SEP; INSERT_WHITESPACE;
408  |     INCREMENT_DEPTH;
409  |     g->state[g->depth] = yajl_gen_array_start;
410  |     g->print(g->ctx, "[", (size_t) 1);
411  |     if ((g->flags & yajl_gen_beautify)) {
412  |         g->print(g->ctx, "\n", (size_t) 1);
413  |     }
414  |     FINAL_NEWLINE;
415  |     return yajl_gen_status_ok;
416  | }
417  | 
418  | yajl_gen_status
419  | yajl_gen_array_close(yajl_gen g)
420  | {
421  |     ENSURE_VALID_STATE;
422  |     DECREMENT_DEPTH;
423  |     if ((g->flags & yajl_gen_beautify)) {
424  |         g->print(g->ctx, "\n", (size_t) 1);
425  |     }
426  |     APPENDED_ATOM;
427  |     INSERT_WHITESPACE;
428  |     g->print(g->ctx, "]", (size_t) 1);
429  |     FINAL_NEWLINE;
430  |     return yajl_gen_status_ok;
431  | }
432  | 
433  | /*+
434  |  *  access the null terminated generator buffer.  If incrementally
435  |  *  outputing JSON, one should call yajl_gen_clear to clear the
436  |  *  buffer.  This allows stream generation.
437  |  +*/
438  | yajl_gen_status
439  | yajl_gen_get_buf(yajl_gen g, const unsigned char ** buf,
440  |                  size_t * len)
441  | {
442  |     if (g->print != (yajl_print_t) &yajl_buf_append) {
443  |         return yajl_gen_no_buf;
444  |     }
445  |     *buf = yajl_buf_data((yajl_buf) g->ctx);
446  |     *len = yajl_buf_len((yajl_buf) g->ctx);
447  |     return yajl_gen_status_ok;
448  | }
449  | 
450  | 
451  | /*+
452  |  *  clear yajl's output buffer, but maintain all internal generation
453  |  *  state.  This function will not "reset" the generator state, and is
454  |  *  intended to enable incremental JSON outputing.
455  |  +*/
456  | void
457  | yajl_gen_clear(yajl_gen g)
458  | {
459  |     if (g->print == (yajl_print_t) &yajl_buf_append) {
460  |         yajl_buf_clear((yajl_buf) g->ctx);
461  |     }
462  | }