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 | }