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