| 133 if (token->allocated) { |
130 if (token->allocated) { |
| 134 cx_strfree(&token->content); |
131 cx_strfree(&token->content); |
| 135 } |
132 } |
| 136 } |
133 } |
| 137 |
134 |
| |
135 static bool json_isdigit(char c) { |
| |
136 // TODO: remove once UCX has public API for this |
| |
137 return c >= '0' && c <= '9'; |
| |
138 } |
| |
139 |
| |
140 static bool json_isspace(char c) { |
| |
141 // TODO: remove once UCX has public API for this |
| |
142 return c == ' ' || c == '\t' || c == '\r' || c == '\n' || c == '\v' || c == '\f'; |
| |
143 } |
| |
144 |
| 138 static int num_isexp(const char *content, size_t length, size_t pos) { |
145 static int num_isexp(const char *content, size_t length, size_t pos) { |
| 139 if (pos >= length) { |
146 if (pos >= length) { |
| 140 return 0; |
147 return 0; |
| 141 } |
148 } |
| 142 |
149 |
| 143 int ok = 0; |
150 int ok = 0; |
| 144 for (size_t i = pos; i < length; i++) { |
151 for (size_t i = pos; i < length; i++) { |
| 145 char c = content[i]; |
152 char c = content[i]; |
| 146 if (isdigit(c)) { |
153 if (json_isdigit(c)) { |
| 147 ok = 1; |
154 ok = 1; |
| 148 } else if (i == pos) { |
155 } else if (i == pos) { |
| 149 if (!(c == '+' || c == '-')) { |
156 if (!(c == '+' || c == '-')) { |
| 150 return 0; |
157 return 0; |
| 151 } |
158 } |
| 252 CX_JSON_NO_DATA : CX_JSON_INCOMPLETE_DATA; |
259 CX_JSON_NO_DATA : CX_JSON_INCOMPLETE_DATA; |
| 253 } |
260 } |
| 254 |
261 |
| 255 // current token type and start index |
262 // current token type and start index |
| 256 CxJsonTokenType ttype = json->uncompleted.tokentype; |
263 CxJsonTokenType ttype = json->uncompleted.tokentype; |
| 257 size_t token_start = json->buffer.pos; |
264 size_t token_part_start = json->buffer.pos; |
| |
265 |
| |
266 bool escape_end_of_string = ttype == CX_JSON_TOKEN_STRING |
| |
267 && json->uncompleted.content.ptr[json->uncompleted.content.length-1] == '\\'; |
| 258 |
268 |
| 259 for (size_t i = json->buffer.pos; i < json->buffer.size; i++) { |
269 for (size_t i = json->buffer.pos; i < json->buffer.size; i++) { |
| 260 char c = json->buffer.space[i]; |
270 char c = json->buffer.space[i]; |
| 261 if (ttype != CX_JSON_TOKEN_STRING) { |
271 if (ttype != CX_JSON_TOKEN_STRING) { |
| 262 // currently non-string token |
272 // currently non-string token |
| 266 json->buffer.pos++; |
276 json->buffer.pos++; |
| 267 continue; |
277 continue; |
| 268 } else if (ctype == CX_JSON_TOKEN_STRING) { |
278 } else if (ctype == CX_JSON_TOKEN_STRING) { |
| 269 // begin string |
279 // begin string |
| 270 ttype = CX_JSON_TOKEN_STRING; |
280 ttype = CX_JSON_TOKEN_STRING; |
| 271 token_start = i; |
281 token_part_start = i; |
| 272 } else if (ctype != CX_JSON_NO_TOKEN) { |
282 } else if (ctype != CX_JSON_NO_TOKEN) { |
| 273 // single-char token |
283 // single-char token |
| 274 json->buffer.pos = i + 1; |
284 json->buffer.pos = i + 1; |
| 275 *result = (CxJsonToken){ctype, false, {NULL, 0}}; |
285 *result = (CxJsonToken){ctype, false, {NULL, 0}}; |
| 276 return CX_JSON_NO_ERROR; |
286 return CX_JSON_NO_ERROR; |
| 277 } else { |
287 } else { |
| 278 ttype = CX_JSON_TOKEN_LITERAL; // number or literal |
288 ttype = CX_JSON_TOKEN_LITERAL; // number or literal |
| 279 token_start = i; |
289 token_part_start = i; |
| 280 } |
290 } |
| 281 } else { |
291 } else { |
| 282 // finish token |
292 // finish token |
| 283 if (ctype != CX_JSON_NO_TOKEN) { |
293 if (ctype != CX_JSON_NO_TOKEN) { |
| 284 *result = token_create(json, false, token_start, i); |
294 *result = token_create(json, false, token_part_start, i); |
| 285 if (result->tokentype == CX_JSON_NO_TOKEN) { |
295 if (result->tokentype == CX_JSON_NO_TOKEN) { |
| 286 return CX_JSON_BUFFER_ALLOC_FAILED; // LCOV_EXCL_LINE |
296 return CX_JSON_BUFFER_ALLOC_FAILED; // LCOV_EXCL_LINE |
| 287 } |
297 } |
| 288 if (result->tokentype == CX_JSON_TOKEN_ERROR) { |
298 if (result->tokentype == CX_JSON_TOKEN_ERROR) { |
| 289 return CX_JSON_FORMAT_ERROR_NUMBER; |
299 return CX_JSON_FORMAT_ERROR_NUMBER; |
| 292 return CX_JSON_NO_ERROR; |
302 return CX_JSON_NO_ERROR; |
| 293 } |
303 } |
| 294 } |
304 } |
| 295 } else { |
305 } else { |
| 296 // currently inside a string |
306 // currently inside a string |
| 297 if (json->tokenizer_escape) { |
307 if (escape_end_of_string) { |
| 298 json->tokenizer_escape = false; |
308 escape_end_of_string = false; |
| 299 } else { |
309 } else { |
| 300 if (c == '"') { |
310 if (c == '"') { |
| 301 *result = token_create(json, true, token_start, i + 1); |
311 *result = token_create(json, true, token_part_start, i + 1); |
| 302 if (result->tokentype == CX_JSON_NO_TOKEN) { |
312 if (result->tokentype == CX_JSON_NO_TOKEN) { |
| 303 return CX_JSON_BUFFER_ALLOC_FAILED; // LCOV_EXCL_LINE |
313 return CX_JSON_BUFFER_ALLOC_FAILED; // LCOV_EXCL_LINE |
| 304 } |
314 } |
| 305 json->buffer.pos = i + 1; |
315 json->buffer.pos = i + 1; |
| 306 return CX_JSON_NO_ERROR; |
316 return CX_JSON_NO_ERROR; |
| 307 } else if (c == '\\') { |
317 } else if (c == '\\') { |
| 308 json->tokenizer_escape = true; |
318 escape_end_of_string = true; |
| 309 } |
319 } |
| 310 } |
320 } |
| 311 } |
321 } |
| 312 } |
322 } |
| 313 |
323 |
| 314 if (ttype != CX_JSON_NO_TOKEN) { |
324 if (ttype != CX_JSON_NO_TOKEN) { |
| 315 // uncompleted token |
325 // uncompleted token |
| 316 size_t uncompleted_len = json->buffer.size - token_start; |
326 size_t uncompleted_len = json->buffer.size - token_part_start; |
| 317 if (json->uncompleted.tokentype == CX_JSON_NO_TOKEN) { |
327 if (json->uncompleted.tokentype == CX_JSON_NO_TOKEN) { |
| 318 // current token is uncompleted |
328 // current token is uncompleted |
| 319 // save current token content |
329 // save current token content |
| 320 CxJsonToken uncompleted = { |
330 CxJsonToken uncompleted = { |
| 321 ttype, true, |
331 ttype, true, |
| 322 cx_strdup(cx_strn(json->buffer.space + token_start, uncompleted_len)) |
332 cx_strdup(cx_strn(json->buffer.space + token_part_start, uncompleted_len)) |
| 323 }; |
333 }; |
| 324 if (uncompleted.content.ptr == NULL) { |
334 if (uncompleted.content.ptr == NULL) { |
| 325 return CX_JSON_BUFFER_ALLOC_FAILED; // LCOV_EXCL_LINE |
335 return CX_JSON_BUFFER_ALLOC_FAILED; // LCOV_EXCL_LINE |
| 326 } |
336 } |
| 327 json->uncompleted = uncompleted; |
337 json->uncompleted = uncompleted; |
| 328 } else { |
338 } else { |
| 329 // previously we also had an uncompleted token |
339 // previously we also had an uncompleted token |
| 330 // combine the uncompleted token with the current token |
340 // combine the uncompleted token with the current token |
| 331 assert(json->uncompleted.allocated); |
341 assert(json->uncompleted.allocated); |
| 332 cxmutstr str = cx_strcat_m(json->uncompleted.content, 1, |
342 cxmutstr str = cx_strcat_m(json->uncompleted.content, 1, |
| 333 cx_strn(json->buffer.space + token_start, uncompleted_len)); |
343 cx_strn(json->buffer.space + token_part_start, uncompleted_len)); |
| 334 if (str.ptr == NULL) { |
344 if (str.ptr == NULL) { |
| 335 return CX_JSON_BUFFER_ALLOC_FAILED; // LCOV_EXCL_LINE |
345 return CX_JSON_BUFFER_ALLOC_FAILED; // LCOV_EXCL_LINE |
| 336 } |
346 } |
| 337 json->uncompleted.content = str; |
347 json->uncompleted.content = str; |
| 338 } |
348 } |
| 341 } |
351 } |
| 342 |
352 |
| 343 return CX_JSON_INCOMPLETE_DATA; |
353 return CX_JSON_INCOMPLETE_DATA; |
| 344 } |
354 } |
| 345 |
355 |
| |
356 // converts a Unicode codepoint to utf8 |
| |
357 static unsigned codepoint_to_utf8(uint32_t codepoint, char *output_buf) { |
| |
358 if (codepoint <= 0x7F) { |
| |
359 *output_buf = (char)codepoint; |
| |
360 return 1; |
| |
361 } else if (codepoint <= 0x7FF) { |
| |
362 output_buf[0] = (char)(0xC0 | ((codepoint >> 6) & 0x1F)); |
| |
363 output_buf[1] = (char)(0x80 | (codepoint & 0x3F)); |
| |
364 return 2; |
| |
365 } else if (codepoint <= 0xFFFF) { |
| |
366 output_buf[0] = (char)(0xE0 | ((codepoint >> 12) & 0x0F)); |
| |
367 output_buf[1] = (char)(0x80 | ((codepoint >> 6) & 0x3F)); |
| |
368 output_buf[2] = (char)(0x80 | (codepoint & 0x3F)); |
| |
369 return 3; |
| |
370 } else if (codepoint <= 0x10FFFF) { |
| |
371 output_buf[0] = (char)(0xF0 | ((codepoint >> 18) & 0x07)); |
| |
372 output_buf[1] = (char)(0x80 | ((codepoint >> 12) & 0x3F)); |
| |
373 output_buf[2] = (char)(0x80 | ((codepoint >> 6) & 0x3F)); |
| |
374 output_buf[3] = (char)(0x80 | (codepoint & 0x3F)); |
| |
375 return 4; |
| |
376 } |
| |
377 |
| |
378 return 0; // LCOV_EXCL_LINE |
| |
379 } |
| |
380 |
| |
381 // converts a utf16 surrogate pair to utf8 |
| |
382 static inline uint32_t utf16pair_to_codepoint(uint16_t c0, uint16_t c1) { |
| |
383 return ((c0 - 0xD800) << 10) + (c1 - 0xDC00) + 0x10000; |
| |
384 } |
| |
385 |
| |
386 static unsigned unescape_unicode_string(cxstring str, char *utf8buf) { |
| |
387 // str is supposed to start with "\uXXXX" or "\uXXXX\uXXXX" |
| |
388 // remaining bytes in the string are ignored (str may be larger!) |
| |
389 |
| |
390 if (str.length < 6 || str.ptr[0] != '\\' || str.ptr[1] != 'u') { |
| |
391 return 0; |
| |
392 } |
| |
393 |
| |
394 unsigned utf8len = 0; |
| |
395 cxstring ustr1 = { str.ptr + 2, 4}; |
| |
396 uint16_t utf16a, utf16b; |
| |
397 if (!cx_strtou16_lc(ustr1, &utf16a, 16, "")) { |
| |
398 uint32_t codepoint; |
| |
399 if (utf16a < 0xD800 || utf16a > 0xE000) { |
| |
400 // character is in the Basic Multilingual Plane |
| |
401 // and encoded as a single utf16 char |
| |
402 codepoint = utf16a; |
| |
403 utf8len = codepoint_to_utf8(codepoint, utf8buf); |
| |
404 } else if (utf16a >= 0xD800 && utf16a <= 0xDBFF) { |
| |
405 // character is encoded as a surrogate pair |
| |
406 // get next 6 bytes |
| |
407 if (str.length >= 12) { |
| |
408 if (str.ptr[6] == '\\' && str.ptr[7] == 'u') { |
| |
409 cxstring ustr2 = { str.ptr+8, 4 }; |
| |
410 if (!cx_strtou16_lc(ustr2, &utf16b, 16, "") |
| |
411 && utf16b >= 0xDC00 && utf16b <= 0xDFFF) { |
| |
412 codepoint = utf16pair_to_codepoint(utf16a, utf16b); |
| |
413 utf8len = codepoint_to_utf8(codepoint, utf8buf); |
| |
414 } |
| |
415 } |
| |
416 } |
| |
417 } |
| |
418 } |
| |
419 return utf8len; |
| |
420 } |
| |
421 |
| 346 static cxmutstr unescape_string(const CxAllocator *a, cxmutstr str) { |
422 static cxmutstr unescape_string(const CxAllocator *a, cxmutstr str) { |
| 347 // TODO: support more escape sequences |
423 // note: this function expects that str contains the enclosing quotes! |
| 348 // we know that the unescaped string will be shorter by at least 2 chars |
424 |
| 349 cxmutstr result; |
425 cxmutstr result; |
| 350 result.length = 0; |
426 result.length = 0; |
| 351 result.ptr = cxMalloc(a, str.length - 1); |
427 result.ptr = cxMalloc(a, str.length - 1); |
| 352 if (result.ptr == NULL) return result; // LCOV_EXCL_LINE |
428 if (result.ptr == NULL) return result; // LCOV_EXCL_LINE |
| 353 |
429 |
| 356 char c = str.ptr[i]; |
432 char c = str.ptr[i]; |
| 357 if (u) { |
433 if (u) { |
| 358 u = false; |
434 u = false; |
| 359 if (c == 'n') { |
435 if (c == 'n') { |
| 360 c = '\n'; |
436 c = '\n'; |
| |
437 } else if (c == '"') { |
| |
438 c = '"'; |
| 361 } else if (c == 't') { |
439 } else if (c == 't') { |
| 362 c = '\t'; |
440 c = '\t'; |
| 363 } |
441 } else if (c == 'r') { |
| |
442 c = '\r'; |
| |
443 } else if (c == '\\') { |
| |
444 c = '\\'; |
| |
445 } else if (c == '/') { |
| |
446 c = '/'; // always unescape, we don't need settings here |
| |
447 } else if (c == 'f') { |
| |
448 c = '\f'; |
| |
449 } else if (c == 'b') { |
| |
450 c = '\b'; |
| |
451 } else if (c == 'u') { |
| |
452 char utf8buf[4]; |
| |
453 unsigned utf8len = unescape_unicode_string( |
| |
454 cx_strn(str.ptr + i - 1, str.length + 1 - i), |
| |
455 utf8buf |
| |
456 ); |
| |
457 if(utf8len > 0) { |
| |
458 i += utf8len < 4 ? 4 : 10; |
| |
459 // add all bytes from utf8buf except the last char |
| |
460 // to the result (last char will be added below) |
| |
461 utf8len--; |
| |
462 c = utf8buf[utf8len]; |
| |
463 for (unsigned x = 0; x < utf8len; x++) { |
| |
464 result.ptr[result.length++] = utf8buf[x]; |
| |
465 } |
| |
466 } else { |
| |
467 // decoding failed, ignore the entire sequence |
| |
468 result.ptr[result.length++] = '\\'; |
| |
469 } |
| |
470 } else { |
| |
471 // TODO: discuss the behavior for unrecognized escape sequences |
| |
472 // most parsers throw an error here - we just ignore it |
| |
473 result.ptr[result.length++] = '\\'; |
| |
474 } |
| |
475 |
| 364 result.ptr[result.length++] = c; |
476 result.ptr[result.length++] = c; |
| 365 } else { |
477 } else { |
| 366 if (c == '\\') { |
478 if (c == '\\') { |
| 367 u = true; |
479 u = true; |
| 368 } else { |
480 } else { |
| 373 result.ptr[result.length] = 0; |
485 result.ptr[result.length] = 0; |
| 374 |
486 |
| 375 return result; |
487 return result; |
| 376 } |
488 } |
| 377 |
489 |
| 378 static CxJsonValue* create_json_value(CxJson *json, CxJsonValueType type) { |
490 static cxmutstr escape_string(cxmutstr str, bool escape_slash) { |
| |
491 // note: this function produces the string without enclosing quotes |
| |
492 // the reason is that we don't want to allocate memory just for that |
| |
493 CxBuffer buf = {0}; |
| |
494 |
| |
495 bool all_printable = true; |
| |
496 for (size_t i = 0; i < str.length; i++) { |
| |
497 unsigned char c = str.ptr[i]; |
| |
498 bool escape = c < 0x20 || c == '\\' || c == '"' |
| |
499 || (escape_slash && c == '/'); |
| |
500 |
| |
501 if (all_printable && escape) { |
| |
502 size_t capa = str.length + 32; |
| |
503 char *space = malloc(capa); |
| |
504 if (space == NULL) return cx_mutstrn(NULL, 0); |
| |
505 cxBufferInit(&buf, space, capa, NULL, CX_BUFFER_AUTO_EXTEND); |
| |
506 cxBufferWrite(str.ptr, 1, i, &buf); |
| |
507 all_printable = false; |
| |
508 } |
| |
509 if (escape) { |
| |
510 cxBufferPut(&buf, '\\'); |
| |
511 if (c == '\"') { |
| |
512 cxBufferPut(&buf, '\"'); |
| |
513 } else if (c == '\n') { |
| |
514 cxBufferPut(&buf, 'n'); |
| |
515 } else if (c == '\t') { |
| |
516 cxBufferPut(&buf, 't'); |
| |
517 } else if (c == '\r') { |
| |
518 cxBufferPut(&buf, 'r'); |
| |
519 } else if (c == '\\') { |
| |
520 cxBufferPut(&buf, '\\'); |
| |
521 } else if (c == '/') { |
| |
522 cxBufferPut(&buf, '/'); |
| |
523 } else if (c == '\f') { |
| |
524 cxBufferPut(&buf, 'f'); |
| |
525 } else if (c == '\b') { |
| |
526 cxBufferPut(&buf, 'b'); |
| |
527 } else { |
| |
528 char code[6]; |
| |
529 snprintf(code, sizeof(code), "u%04x", (unsigned int) c); |
| |
530 cxBufferPutString(&buf, code); |
| |
531 } |
| |
532 } else if (!all_printable) { |
| |
533 cxBufferPut(&buf, c); |
| |
534 } |
| |
535 } |
| |
536 if (!all_printable) { |
| |
537 str = cx_mutstrn(buf.space, buf.size); |
| |
538 } |
| |
539 cxBufferDestroy(&buf); |
| |
540 return str; |
| |
541 } |
| |
542 |
| |
543 static CxJsonValue* json_create_value(CxJson *json, CxJsonValueType type) { |
| 379 CxJsonValue *v = cxCalloc(json->allocator, 1, sizeof(CxJsonValue)); |
544 CxJsonValue *v = cxCalloc(json->allocator, 1, sizeof(CxJsonValue)); |
| 380 if (v == NULL) return NULL; // LCOV_EXCL_LINE |
545 if (v == NULL) return NULL; // LCOV_EXCL_LINE |
| 381 |
546 |
| 382 // initialize the value |
547 // initialize the value |
| 383 v->type = type; |
548 v->type = type; |
| 539 if (state < 3) { |
704 if (state < 3) { |
| 540 // push expected end state to the stack |
705 // push expected end state to the stack |
| 541 json_add_state(json, 10 + state); |
706 json_add_state(json, 10 + state); |
| 542 switch (token.tokentype) { |
707 switch (token.tokentype) { |
| 543 case CX_JSON_TOKEN_BEGIN_ARRAY: { |
708 case CX_JSON_TOKEN_BEGIN_ARRAY: { |
| 544 if (create_json_value(json, CX_JSON_ARRAY) == NULL) { |
709 if (json_create_value(json, CX_JSON_ARRAY) == NULL) { |
| 545 return_rec(CX_JSON_VALUE_ALLOC_FAILED); // LCOV_EXCL_LINE |
710 return_rec(CX_JSON_VALUE_ALLOC_FAILED); // LCOV_EXCL_LINE |
| 546 } |
711 } |
| 547 json_add_state(json, JP_STATE_VALUE_BEGIN_AR); |
712 json_add_state(json, JP_STATE_VALUE_BEGIN_AR); |
| 548 return_rec(CX_JSON_NO_ERROR); |
713 return_rec(CX_JSON_NO_ERROR); |
| 549 } |
714 } |
| 550 case CX_JSON_TOKEN_BEGIN_OBJECT: { |
715 case CX_JSON_TOKEN_BEGIN_OBJECT: { |
| 551 if (create_json_value(json, CX_JSON_OBJECT) == NULL) { |
716 if (json_create_value(json, CX_JSON_OBJECT) == NULL) { |
| 552 return_rec(CX_JSON_VALUE_ALLOC_FAILED); // LCOV_EXCL_LINE |
717 return_rec(CX_JSON_VALUE_ALLOC_FAILED); // LCOV_EXCL_LINE |
| 553 } |
718 } |
| 554 json_add_state(json, JP_STATE_OBJ_NAME_OR_CLOSE); |
719 json_add_state(json, JP_STATE_OBJ_NAME_OR_CLOSE); |
| 555 return_rec(CX_JSON_NO_ERROR); |
720 return_rec(CX_JSON_NO_ERROR); |
| 556 } |
721 } |
| 557 case CX_JSON_TOKEN_STRING: { |
722 case CX_JSON_TOKEN_STRING: { |
| 558 if ((vbuf = create_json_value(json, CX_JSON_STRING)) == NULL) { |
723 if ((vbuf = json_create_value(json, CX_JSON_STRING)) == NULL) { |
| 559 return_rec(CX_JSON_VALUE_ALLOC_FAILED); // LCOV_EXCL_LINE |
724 return_rec(CX_JSON_VALUE_ALLOC_FAILED); // LCOV_EXCL_LINE |
| 560 } |
725 } |
| 561 cxmutstr str = unescape_string(json->allocator, token.content); |
726 cxmutstr str = unescape_string(json->allocator, token.content); |
| 562 if (str.ptr == NULL) { |
727 if (str.ptr == NULL) { |
| 563 return_rec(CX_JSON_VALUE_ALLOC_FAILED); // LCOV_EXCL_LINE |
728 return_rec(CX_JSON_VALUE_ALLOC_FAILED); // LCOV_EXCL_LINE |
| 566 return_rec(CX_JSON_NO_ERROR); |
731 return_rec(CX_JSON_NO_ERROR); |
| 567 } |
732 } |
| 568 case CX_JSON_TOKEN_INTEGER: |
733 case CX_JSON_TOKEN_INTEGER: |
| 569 case CX_JSON_TOKEN_NUMBER: { |
734 case CX_JSON_TOKEN_NUMBER: { |
| 570 int type = token.tokentype == CX_JSON_TOKEN_INTEGER ? CX_JSON_INTEGER : CX_JSON_NUMBER; |
735 int type = token.tokentype == CX_JSON_TOKEN_INTEGER ? CX_JSON_INTEGER : CX_JSON_NUMBER; |
| 571 if (NULL == (vbuf = create_json_value(json, type))) { |
736 if (NULL == (vbuf = json_create_value(json, type))) { |
| 572 return_rec(CX_JSON_VALUE_ALLOC_FAILED); // LCOV_EXCL_LINE |
737 return_rec(CX_JSON_VALUE_ALLOC_FAILED); // LCOV_EXCL_LINE |
| 573 } |
738 } |
| 574 if (type == CX_JSON_INTEGER) { |
739 if (type == CX_JSON_INTEGER) { |
| 575 if (cx_strtoi64(token.content, &vbuf->value.integer, 10)) { |
740 if (cx_strtoi64(token.content, &vbuf->value.integer, 10)) { |
| 576 return_rec(CX_JSON_FORMAT_ERROR_NUMBER); |
741 return_rec(CX_JSON_FORMAT_ERROR_NUMBER); |
| 581 } |
746 } |
| 582 } |
747 } |
| 583 return_rec(CX_JSON_NO_ERROR); |
748 return_rec(CX_JSON_NO_ERROR); |
| 584 } |
749 } |
| 585 case CX_JSON_TOKEN_LITERAL: { |
750 case CX_JSON_TOKEN_LITERAL: { |
| 586 if ((vbuf = create_json_value(json, CX_JSON_LITERAL)) == NULL) { |
751 if ((vbuf = json_create_value(json, CX_JSON_LITERAL)) == NULL) { |
| 587 return_rec(CX_JSON_VALUE_ALLOC_FAILED); // LCOV_EXCL_LINE |
752 return_rec(CX_JSON_VALUE_ALLOC_FAILED); // LCOV_EXCL_LINE |
| 588 } |
753 } |
| 589 if (0 == cx_strcmp(cx_strcast(token.content), cx_str("true"))) { |
754 if (0 == cx_strcmp(cx_strcast(token.content), cx_str("true"))) { |
| 590 vbuf->value.literal = CX_JSON_TRUE; |
755 vbuf->value.literal = CX_JSON_TRUE; |
| 591 } else if (0 == cx_strcmp(cx_strcast(token.content), cx_str("false"))) { |
756 } else if (0 == cx_strcmp(cx_strcast(token.content), cx_str("false"))) { |
| 732 } |
897 } |
| 733 cxFree(value->allocator, value); |
898 cxFree(value->allocator, value); |
| 734 } |
899 } |
| 735 |
900 |
| 736 CxJsonValue* cxJsonCreateObj(const CxAllocator* allocator) { |
901 CxJsonValue* cxJsonCreateObj(const CxAllocator* allocator) { |
| |
902 if (allocator == NULL) allocator = cxDefaultAllocator; |
| 737 CxJsonValue* v = cxMalloc(allocator, sizeof(CxJsonValue)); |
903 CxJsonValue* v = cxMalloc(allocator, sizeof(CxJsonValue)); |
| 738 if (v == NULL) return NULL; |
904 if (v == NULL) return NULL; |
| 739 v->allocator = allocator; |
905 v->allocator = allocator; |
| 740 v->type = CX_JSON_OBJECT; |
906 v->type = CX_JSON_OBJECT; |
| 741 cx_array_initialize_a(allocator, v->value.object.values, 16); |
907 cx_array_initialize_a(allocator, v->value.object.values, 16); |
| 753 } |
919 } |
| 754 return v; |
920 return v; |
| 755 } |
921 } |
| 756 |
922 |
| 757 CxJsonValue* cxJsonCreateArr(const CxAllocator* allocator) { |
923 CxJsonValue* cxJsonCreateArr(const CxAllocator* allocator) { |
| |
924 if (allocator == NULL) allocator = cxDefaultAllocator; |
| 758 CxJsonValue* v = cxMalloc(allocator, sizeof(CxJsonValue)); |
925 CxJsonValue* v = cxMalloc(allocator, sizeof(CxJsonValue)); |
| 759 if (v == NULL) return NULL; |
926 if (v == NULL) return NULL; |
| 760 v->allocator = allocator; |
927 v->allocator = allocator; |
| 761 v->type = CX_JSON_ARRAY; |
928 v->type = CX_JSON_ARRAY; |
| 762 cx_array_initialize_a(allocator, v->value.array.array, 16); |
929 cx_array_initialize_a(allocator, v->value.array.array, 16); |
| 763 if (v->value.array.array == NULL) { cxFree(allocator, v); return NULL; } |
930 if (v->value.array.array == NULL) { cxFree(allocator, v); return NULL; } |
| 764 return v; |
931 return v; |
| 765 } |
932 } |
| 766 |
933 |
| 767 CxJsonValue* cxJsonCreateNumber(const CxAllocator* allocator, double num) { |
934 CxJsonValue* cxJsonCreateNumber(const CxAllocator* allocator, double num) { |
| |
935 if (allocator == NULL) allocator = cxDefaultAllocator; |
| 768 CxJsonValue* v = cxMalloc(allocator, sizeof(CxJsonValue)); |
936 CxJsonValue* v = cxMalloc(allocator, sizeof(CxJsonValue)); |
| 769 if (v == NULL) return NULL; |
937 if (v == NULL) return NULL; |
| 770 v->allocator = allocator; |
938 v->allocator = allocator; |
| 771 v->type = CX_JSON_NUMBER; |
939 v->type = CX_JSON_NUMBER; |
| 772 v->value.number = num; |
940 v->value.number = num; |
| 773 return v; |
941 return v; |
| 774 } |
942 } |
| 775 |
943 |
| 776 CxJsonValue* cxJsonCreateInteger(const CxAllocator* allocator, int64_t num) { |
944 CxJsonValue* cxJsonCreateInteger(const CxAllocator* allocator, int64_t num) { |
| |
945 if (allocator == NULL) allocator = cxDefaultAllocator; |
| 777 CxJsonValue* v = cxMalloc(allocator, sizeof(CxJsonValue)); |
946 CxJsonValue* v = cxMalloc(allocator, sizeof(CxJsonValue)); |
| 778 if (v == NULL) return NULL; |
947 if (v == NULL) return NULL; |
| 779 v->allocator = allocator; |
948 v->allocator = allocator; |
| 780 v->type = CX_JSON_INTEGER; |
949 v->type = CX_JSON_INTEGER; |
| 781 v->value.integer = num; |
950 v->value.integer = num; |
| 785 CxJsonValue* cxJsonCreateString(const CxAllocator* allocator, const char* str) { |
954 CxJsonValue* cxJsonCreateString(const CxAllocator* allocator, const char* str) { |
| 786 return cxJsonCreateCxString(allocator, cx_str(str)); |
955 return cxJsonCreateCxString(allocator, cx_str(str)); |
| 787 } |
956 } |
| 788 |
957 |
| 789 CxJsonValue* cxJsonCreateCxString(const CxAllocator* allocator, cxstring str) { |
958 CxJsonValue* cxJsonCreateCxString(const CxAllocator* allocator, cxstring str) { |
| |
959 if (allocator == NULL) allocator = cxDefaultAllocator; |
| 790 CxJsonValue* v = cxMalloc(allocator, sizeof(CxJsonValue)); |
960 CxJsonValue* v = cxMalloc(allocator, sizeof(CxJsonValue)); |
| 791 if (v == NULL) return NULL; |
961 if (v == NULL) return NULL; |
| 792 v->allocator = allocator; |
962 v->allocator = allocator; |
| 793 v->type = CX_JSON_STRING; |
963 v->type = CX_JSON_STRING; |
| 794 cxmutstr s = cx_strdup_a(allocator, str); |
964 cxmutstr s = cx_strdup_a(allocator, str); |
| 796 v->value.string = s; |
966 v->value.string = s; |
| 797 return v; |
967 return v; |
| 798 } |
968 } |
| 799 |
969 |
| 800 CxJsonValue* cxJsonCreateLiteral(const CxAllocator* allocator, CxJsonLiteral lit) { |
970 CxJsonValue* cxJsonCreateLiteral(const CxAllocator* allocator, CxJsonLiteral lit) { |
| |
971 if (allocator == NULL) allocator = cxDefaultAllocator; |
| 801 CxJsonValue* v = cxMalloc(allocator, sizeof(CxJsonValue)); |
972 CxJsonValue* v = cxMalloc(allocator, sizeof(CxJsonValue)); |
| 802 if (v == NULL) return NULL; |
973 if (v == NULL) return NULL; |
| 803 v->allocator = allocator; |
974 v->allocator = allocator; |
| 804 v->type = CX_JSON_LITERAL; |
975 v->type = CX_JSON_LITERAL; |
| 805 v->value.literal = lit; |
976 v->value.literal = lit; |
| 806 return v; |
977 return v; |
| 807 } |
978 } |
| 808 |
979 |
| 809 // LCOV_EXCL_START |
980 // LCOV_EXCL_START |
| 810 // never called as long as malloc() does not return NULL |
981 // never called as long as malloc() does not return NULL |
| 811 static void cx_json_arr_free_temp(CxJsonValue** values, size_t count) { |
982 static void json_arr_free_temp(CxJsonValue** values, size_t count) { |
| 812 for (size_t i = 0; i < count; i++) { |
983 for (size_t i = 0; i < count; i++) { |
| 813 if (values[i] == NULL) break; |
984 if (values[i] == NULL) break; |
| 814 cxJsonValueFree(values[i]); |
985 cxJsonValueFree(values[i]); |
| 815 } |
986 } |
| 816 free(values); |
987 free(values); |
| 820 int cxJsonArrAddNumbers(CxJsonValue* arr, const double* num, size_t count) { |
991 int cxJsonArrAddNumbers(CxJsonValue* arr, const double* num, size_t count) { |
| 821 CxJsonValue** values = calloc(count, sizeof(CxJsonValue*)); |
992 CxJsonValue** values = calloc(count, sizeof(CxJsonValue*)); |
| 822 if (values == NULL) return -1; |
993 if (values == NULL) return -1; |
| 823 for (size_t i = 0; i < count; i++) { |
994 for (size_t i = 0; i < count; i++) { |
| 824 values[i] = cxJsonCreateNumber(arr->allocator, num[i]); |
995 values[i] = cxJsonCreateNumber(arr->allocator, num[i]); |
| 825 if (values[i] == NULL) { cx_json_arr_free_temp(values, count); return -1; } |
996 if (values[i] == NULL) { json_arr_free_temp(values, count); return -1; } |
| 826 } |
997 } |
| 827 int ret = cxJsonArrAddValues(arr, values, count); |
998 int ret = cxJsonArrAddValues(arr, values, count); |
| 828 free(values); |
999 free(values); |
| 829 return ret; |
1000 return ret; |
| 830 } |
1001 } |
| 832 int cxJsonArrAddIntegers(CxJsonValue* arr, const int64_t* num, size_t count) { |
1003 int cxJsonArrAddIntegers(CxJsonValue* arr, const int64_t* num, size_t count) { |
| 833 CxJsonValue** values = calloc(count, sizeof(CxJsonValue*)); |
1004 CxJsonValue** values = calloc(count, sizeof(CxJsonValue*)); |
| 834 if (values == NULL) return -1; |
1005 if (values == NULL) return -1; |
| 835 for (size_t i = 0; i < count; i++) { |
1006 for (size_t i = 0; i < count; i++) { |
| 836 values[i] = cxJsonCreateInteger(arr->allocator, num[i]); |
1007 values[i] = cxJsonCreateInteger(arr->allocator, num[i]); |
| 837 if (values[i] == NULL) { cx_json_arr_free_temp(values, count); return -1; } |
1008 if (values[i] == NULL) { json_arr_free_temp(values, count); return -1; } |
| 838 } |
1009 } |
| 839 int ret = cxJsonArrAddValues(arr, values, count); |
1010 int ret = cxJsonArrAddValues(arr, values, count); |
| 840 free(values); |
1011 free(values); |
| 841 return ret; |
1012 return ret; |
| 842 } |
1013 } |
| 844 int cxJsonArrAddStrings(CxJsonValue* arr, const char* const* str, size_t count) { |
1015 int cxJsonArrAddStrings(CxJsonValue* arr, const char* const* str, size_t count) { |
| 845 CxJsonValue** values = calloc(count, sizeof(CxJsonValue*)); |
1016 CxJsonValue** values = calloc(count, sizeof(CxJsonValue*)); |
| 846 if (values == NULL) return -1; |
1017 if (values == NULL) return -1; |
| 847 for (size_t i = 0; i < count; i++) { |
1018 for (size_t i = 0; i < count; i++) { |
| 848 values[i] = cxJsonCreateString(arr->allocator, str[i]); |
1019 values[i] = cxJsonCreateString(arr->allocator, str[i]); |
| 849 if (values[i] == NULL) { cx_json_arr_free_temp(values, count); return -1; } |
1020 if (values[i] == NULL) { json_arr_free_temp(values, count); return -1; } |
| 850 } |
1021 } |
| 851 int ret = cxJsonArrAddValues(arr, values, count); |
1022 int ret = cxJsonArrAddValues(arr, values, count); |
| 852 free(values); |
1023 free(values); |
| 853 return ret; |
1024 return ret; |
| 854 } |
1025 } |
| 856 int cxJsonArrAddCxStrings(CxJsonValue* arr, const cxstring* str, size_t count) { |
1027 int cxJsonArrAddCxStrings(CxJsonValue* arr, const cxstring* str, size_t count) { |
| 857 CxJsonValue** values = calloc(count, sizeof(CxJsonValue*)); |
1028 CxJsonValue** values = calloc(count, sizeof(CxJsonValue*)); |
| 858 if (values == NULL) return -1; |
1029 if (values == NULL) return -1; |
| 859 for (size_t i = 0; i < count; i++) { |
1030 for (size_t i = 0; i < count; i++) { |
| 860 values[i] = cxJsonCreateCxString(arr->allocator, str[i]); |
1031 values[i] = cxJsonCreateCxString(arr->allocator, str[i]); |
| 861 if (values[i] == NULL) { cx_json_arr_free_temp(values, count); return -1; } |
1032 if (values[i] == NULL) { json_arr_free_temp(values, count); return -1; } |
| 862 } |
1033 } |
| 863 int ret = cxJsonArrAddValues(arr, values, count); |
1034 int ret = cxJsonArrAddValues(arr, values, count); |
| 864 free(values); |
1035 free(values); |
| 865 return ret; |
1036 return ret; |
| 866 } |
1037 } |
| 868 int cxJsonArrAddLiterals(CxJsonValue* arr, const CxJsonLiteral* lit, size_t count) { |
1039 int cxJsonArrAddLiterals(CxJsonValue* arr, const CxJsonLiteral* lit, size_t count) { |
| 869 CxJsonValue** values = calloc(count, sizeof(CxJsonValue*)); |
1040 CxJsonValue** values = calloc(count, sizeof(CxJsonValue*)); |
| 870 if (values == NULL) return -1; |
1041 if (values == NULL) return -1; |
| 871 for (size_t i = 0; i < count; i++) { |
1042 for (size_t i = 0; i < count; i++) { |
| 872 values[i] = cxJsonCreateLiteral(arr->allocator, lit[i]); |
1043 values[i] = cxJsonCreateLiteral(arr->allocator, lit[i]); |
| 873 if (values[i] == NULL) { cx_json_arr_free_temp(values, count); return -1; } |
1044 if (values[i] == NULL) { json_arr_free_temp(values, count); return -1; } |
| 874 } |
1045 } |
| 875 int ret = cxJsonArrAddValues(arr, values, count); |
1046 int ret = cxJsonArrAddValues(arr, values, count); |
| 876 free(values); |
1047 free(values); |
| 877 return ret; |
1048 return ret; |
| 878 } |
1049 } |
| 1076 } |
1247 } |
| 1077 } |
1248 } |
| 1078 |
1249 |
| 1079 // the name |
1250 // the name |
| 1080 actual += wfunc("\"", 1, 1, target); |
1251 actual += wfunc("\"", 1, 1, target); |
| 1081 // TODO: escape the string |
1252 cxmutstr name = escape_string(member->name, settings->escape_slash); |
| 1082 actual += wfunc(member->name.ptr, 1, |
1253 actual += wfunc(name.ptr, 1, name.length, target); |
| 1083 member->name.length, target); |
1254 if (name.ptr != member->name.ptr) { |
| |
1255 cx_strfree(&name); |
| |
1256 } |
| 1084 actual += wfunc("\"", 1, 1, target); |
1257 actual += wfunc("\"", 1, 1, target); |
| 1085 const char *obj_name_sep = ": "; |
1258 const char *obj_name_sep = ": "; |
| 1086 if (settings->pretty) { |
1259 if (settings->pretty) { |
| 1087 actual += wfunc(obj_name_sep, 1, 2, target); |
1260 actual += wfunc(obj_name_sep, 1, 2, target); |
| 1088 expected += 4 + member->name.length; |
1261 expected += 4 + member->name.length; |
| 1144 expected++; |
1317 expected++; |
| 1145 break; |
1318 break; |
| 1146 } |
1319 } |
| 1147 case CX_JSON_STRING: { |
1320 case CX_JSON_STRING: { |
| 1148 actual += wfunc("\"", 1, 1, target); |
1321 actual += wfunc("\"", 1, 1, target); |
| 1149 // TODO: escape the string |
1322 cxmutstr str = escape_string(value->value.string, settings->escape_slash); |
| 1150 actual += wfunc(value->value.string.ptr, 1, |
1323 actual += wfunc(str.ptr, 1, str.length, target); |
| 1151 value->value.string.length, target); |
1324 if (str.ptr != value->value.string.ptr) { |
| |
1325 cx_strfree(&str); |
| |
1326 } |
| 1152 actual += wfunc("\"", 1, 1, target); |
1327 actual += wfunc("\"", 1, 1, target); |
| 1153 expected += 2 + value->value.string.length; |
1328 expected += 2 + value->value.string.length; |
| 1154 break; |
1329 break; |
| 1155 } |
1330 } |
| 1156 case CX_JSON_NUMBER: { |
1331 case CX_JSON_NUMBER: { |
| 1157 // TODO: locale bullshit |
1332 int precision = settings->frac_max_digits; |
| 1158 // TODO: formatting settings |
1333 // because of the way how %g is defined, we need to |
| 1159 snprintf(numbuf, 32, "%g", value->value.number); |
1334 // double the precision and truncate ourselves |
| 1160 size_t len = strlen(numbuf); |
1335 precision = 1 + (precision > 15 ? 30 : 2 * precision); |
| 1161 actual += wfunc(numbuf, 1, len, target); |
1336 snprintf(numbuf, 40, "%.*g", precision, value->value.number); |
| 1162 expected += len; |
1337 char *dot, *exp; |
| |
1338 unsigned char max_digits; |
| |
1339 // find the decimal separator and hope that it's one of . or , |
| |
1340 dot = strchr(numbuf, '.'); |
| |
1341 if (dot == NULL) { |
| |
1342 dot = strchr(numbuf, ','); |
| |
1343 } |
| |
1344 if (dot == NULL) { |
| |
1345 // no decimal separator found |
| |
1346 // output everything until a possible exponent |
| |
1347 max_digits = 30; |
| |
1348 dot = numbuf; |
| |
1349 } else { |
| |
1350 // found a decimal separator |
| |
1351 // output everything until the separator |
| |
1352 // and set max digits to what the settings say |
| |
1353 size_t len = dot - numbuf; |
| |
1354 actual += wfunc(numbuf, 1, len, target); |
| |
1355 expected += len; |
| |
1356 max_digits = settings->frac_max_digits; |
| |
1357 if (max_digits > 15) { |
| |
1358 max_digits = 15; |
| |
1359 } |
| |
1360 // locale independent separator |
| |
1361 if (max_digits > 0) { |
| |
1362 actual += wfunc(".", 1, 1, target); |
| |
1363 expected++; |
| |
1364 } |
| |
1365 dot++; |
| |
1366 } |
| |
1367 // find the exponent |
| |
1368 exp = strchr(dot, 'e'); |
| |
1369 if (exp == NULL) { |
| |
1370 // no exponent - output the rest |
| |
1371 if (max_digits > 0) { |
| |
1372 size_t len = strlen(dot); |
| |
1373 if (len > max_digits) { |
| |
1374 len = max_digits; |
| |
1375 } |
| |
1376 actual += wfunc(dot, 1, len, target); |
| |
1377 expected += len; |
| |
1378 } |
| |
1379 } else { |
| |
1380 // exponent found - truncate the frac digits |
| |
1381 // and then output the rest |
| |
1382 if (max_digits > 0) { |
| |
1383 size_t len = exp - dot - 1; |
| |
1384 if (len > max_digits) { |
| |
1385 len = max_digits; |
| |
1386 } |
| |
1387 actual += wfunc(dot, 1, len, target); |
| |
1388 expected += len; |
| |
1389 } |
| |
1390 actual += wfunc("e", 1, 1, target); |
| |
1391 expected++; |
| |
1392 exp++; |
| |
1393 size_t len = strlen(exp); |
| |
1394 actual += wfunc(exp, 1, len, target); |
| |
1395 expected += len; |
| |
1396 } |
| 1163 break; |
1397 break; |
| 1164 } |
1398 } |
| 1165 case CX_JSON_INTEGER: { |
1399 case CX_JSON_INTEGER: { |
| 1166 snprintf(numbuf, 32, "%" PRIi64, value->value.integer); |
1400 snprintf(numbuf, 32, "%" PRIi64, value->value.integer); |
| 1167 size_t len = strlen(numbuf); |
1401 size_t len = strlen(numbuf); |
| 1199 void *target, |
1433 void *target, |
| 1200 const CxJsonValue *value, |
1434 const CxJsonValue *value, |
| 1201 cx_write_func wfunc, |
1435 cx_write_func wfunc, |
| 1202 const CxJsonWriter *settings |
1436 const CxJsonWriter *settings |
| 1203 ) { |
1437 ) { |
| 1204 if (settings == NULL) { |
|
| 1205 settings = &cx_json_writer_default; |
|
| 1206 } |
|
| 1207 assert(target != NULL); |
1438 assert(target != NULL); |
| 1208 assert(value != NULL); |
1439 assert(value != NULL); |
| 1209 assert(wfunc != NULL); |
1440 assert(wfunc != NULL); |
| 1210 |
1441 |
| |
1442 CxJsonWriter writer_default = cxJsonWriterCompact(); |
| |
1443 if (settings == NULL) { |
| |
1444 settings = &writer_default; |
| |
1445 } |
| 1211 return cx_json_write_rec(target, value, wfunc, settings, 0); |
1446 return cx_json_write_rec(target, value, wfunc, settings, 0); |
| 1212 } |
1447 } |