ucx/json.c

changeset 115
e57ca2747782
parent 113
dde28a806552
equal deleted inserted replaced
114:3da24640513a 115:e57ca2747782
25 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 25 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
26 * POSSIBILITY OF SUCH DAMAGE. 26 * POSSIBILITY OF SUCH DAMAGE.
27 */ 27 */
28 28
29 #include "cx/json.h" 29 #include "cx/json.h"
30 #include "cx/kv_list.h"
30 31
31 #include <string.h> 32 #include <string.h>
32 #include <assert.h> 33 #include <assert.h>
33 #include <stdio.h> 34 #include <stdio.h>
34 #include <inttypes.h> 35 #include <inttypes.h>
39 * https://tools.ietf.org/html/rfc8259 40 * https://tools.ietf.org/html/rfc8259
40 */ 41 */
41 42
42 static CxJsonValue cx_json_value_nothing = {.type = CX_JSON_NOTHING}; 43 static CxJsonValue cx_json_value_nothing = {.type = CX_JSON_NOTHING};
43 44
44 static int json_cmp_objvalue(const void *l, const void *r) {
45 const CxJsonObjValue *left = l;
46 const CxJsonObjValue *right = r;
47 return cx_strcmp(cx_strcast(left->name), cx_strcast(right->name));
48 }
49
50 static size_t json_find_objvalue(const CxJsonValue *obj, cxstring name) {
51 assert(obj->type == CX_JSON_OBJECT);
52 CxJsonObjValue kv_dummy;
53 kv_dummy.name = cx_mutstrn((char*) name.ptr, name.length);
54 return cx_array_binary_search(
55 obj->value.object.values,
56 obj->value.object.values_size,
57 sizeof(CxJsonObjValue),
58 &kv_dummy,
59 json_cmp_objvalue
60 );
61 }
62
63 static int json_add_objvalue(CxJsonValue *objv, CxJsonObjValue member) {
64 assert(objv->type == CX_JSON_OBJECT);
65 const CxAllocator * const al = objv->allocator;
66 CxJsonObject *obj = &(objv->value.object);
67
68 // determine the index where we need to insert the new member
69 size_t index = cx_array_binary_search_sup(
70 obj->values,
71 obj->values_size,
72 sizeof(CxJsonObjValue),
73 &member, json_cmp_objvalue
74 );
75
76 // is the name already present?
77 if (index < obj->values_size && 0 == json_cmp_objvalue(&member, &obj->values[index])) {
78 // free the original value
79 cx_strfree_a(al, &obj->values[index].name);
80 cxJsonValueFree(obj->values[index].value);
81 // replace the item
82 obj->values[index] = member;
83
84 // nothing more to do
85 return 0;
86 }
87
88 // determine the old capacity and reserve for one more element
89 CxArrayReallocator arealloc = cx_array_reallocator(al, NULL);
90 size_t oldcap = obj->values_capacity;
91 if (cx_array_simple_reserve_a(&arealloc, obj->values, 1)) return 1;
92
93 // check the new capacity, if we need to realloc the index array
94 size_t newcap = obj->values_capacity;
95 if (newcap > oldcap) {
96 if (cxReallocateArray(al, &obj->indices, newcap, sizeof(size_t))) {
97 return 1;
98 }
99 }
100
101 // check if append or insert
102 if (index < obj->values_size) {
103 // move the other elements
104 memmove(
105 &obj->values[index+1],
106 &obj->values[index],
107 (obj->values_size - index) * sizeof(CxJsonObjValue)
108 );
109 // increase indices for the moved elements
110 for (size_t i = 0; i < obj->values_size ; i++) {
111 if (obj->indices[i] >= index) {
112 obj->indices[i]++;
113 }
114 }
115 }
116
117 // insert the element and set the index
118 obj->values[index] = member;
119 obj->indices[obj->values_size] = index;
120 obj->values_size++;
121
122 return 0;
123 }
124
125 static void token_destroy(CxJsonToken *token) { 45 static void token_destroy(CxJsonToken *token) {
126 if (token->allocated) { 46 if (token->allocated) {
127 cx_strfree(&token->content); 47 cx_strfree(&token->content);
48 token->allocated = false;
128 } 49 }
129 } 50 }
130 51
131 static int num_isexp(const char *content, size_t length, size_t pos) { 52 static int num_isexp(const char *content, size_t length, size_t pos) {
132 if (pos >= length) { 53 if (pos >= length) {
305 } 226 }
306 } 227 }
307 } 228 }
308 } 229 }
309 230
310 if (ttype != CX_JSON_NO_TOKEN) { 231 if (ttype == CX_JSON_NO_TOKEN) {
232 return CX_JSON_NO_DATA;
233 } else {
311 // uncompleted token 234 // uncompleted token
312 size_t uncompleted_len = json->buffer.size - token_part_start; 235 size_t uncompleted_len = json->buffer.size - token_part_start;
313 if (json->uncompleted.tokentype == CX_JSON_NO_TOKEN) { 236 if (json->uncompleted.tokentype == CX_JSON_NO_TOKEN) {
314 // current token is uncompleted 237 // current token is uncompleted
315 // save current token content 238 // save current token content
332 } 255 }
333 json->uncompleted.content = str; 256 json->uncompleted.content = str;
334 } 257 }
335 // advance the buffer position - we saved the stuff in the uncompleted token 258 // advance the buffer position - we saved the stuff in the uncompleted token
336 json->buffer.pos += uncompleted_len; 259 json->buffer.pos += uncompleted_len;
337 } 260 return CX_JSON_INCOMPLETE_DATA;
338 261 }
339 return CX_JSON_INCOMPLETE_DATA;
340 } 262 }
341 263
342 // converts a Unicode codepoint to utf8 264 // converts a Unicode codepoint to utf8
343 static unsigned codepoint_to_utf8(uint32_t codepoint, char *output_buf) { 265 static unsigned codepoint_to_utf8(uint32_t codepoint, char *output_buf) {
344 if (codepoint <= 0x7F) { 266 if (codepoint <= 0x7F) {
435 } else if (c == 'b') { 357 } else if (c == 'b') {
436 c = '\b'; 358 c = '\b';
437 } else if (c == 'u') { 359 } else if (c == 'u') {
438 char utf8buf[4]; 360 char utf8buf[4];
439 unsigned utf8len = unescape_unicode_string( 361 unsigned utf8len = unescape_unicode_string(
440 cx_strn(str.ptr + i - 1, str.length + 1 - i), 362 cx_strn(str.ptr + i - 1, str.length - i),
441 utf8buf 363 utf8buf
442 ); 364 );
443 if(utf8len > 0) { 365 if(utf8len > 0) {
444 i += utf8len < 4 ? 4 : 10; 366 i += utf8len < 4 ? 4 : 10;
445 // add all bytes from utf8buf except the last char 367 // add all bytes from utf8buf except the last char
454 result.ptr[result.length++] = '\\'; 376 result.ptr[result.length++] = '\\';
455 } 377 }
456 } else { 378 } else {
457 // TODO: discuss the behavior for unrecognized escape sequences 379 // TODO: discuss the behavior for unrecognized escape sequences
458 // most parsers throw an error here - we just ignore it 380 // most parsers throw an error here - we just ignore it
459 result.ptr[result.length++] = '\\'; 381 result.ptr[result.length++] = '\\'; // LCOV_EXCL_LINE
460 } 382 }
461 383
462 result.ptr[result.length++] = c; 384 result.ptr[result.length++] = c;
463 } else { 385 } else {
464 if (c == '\\') { 386 if (c == '\\') {
471 result.ptr[result.length] = 0; 393 result.ptr[result.length] = 0;
472 394
473 return result; 395 return result;
474 } 396 }
475 397
476 static cxmutstr escape_string(cxmutstr str, bool escape_slash) { 398 static cxmutstr escape_string(cxstring str, bool escape_slash) {
477 // note: this function produces the string without enclosing quotes 399 // note: this function produces the string without enclosing quotes
478 // the reason is that we don't want to allocate memory just for that 400 // the reason is that we don't want to allocate memory just for that
479 CxBuffer buf = {0}; 401 CxBuffer buf = {0};
480 402
481 bool all_printable = true; 403 bool all_printable = true;
517 } 439 }
518 } else if (!all_printable) { 440 } else if (!all_printable) {
519 cxBufferPut(&buf, c); 441 cxBufferPut(&buf, c);
520 } 442 }
521 } 443 }
522 if (!all_printable) { 444 cxmutstr ret;
523 str = cx_mutstrn(buf.space, buf.size); 445 if (all_printable) {
446 // don't copy the string when we don't need to escape anything
447 ret = cx_mutstrn((char*)str.ptr, str.length);
448 } else {
449 ret = cx_mutstrn(buf.space, buf.size);
524 } 450 }
525 cxBufferDestroy(&buf); 451 cxBufferDestroy(&buf);
526 return str; 452 return ret;
453 }
454
455 static CxJsonObject json_create_object_map(const CxAllocator *allocator) {
456 // TODO: we might want to add a comparator that is sorting the elements by their key
457 CxMap *map = cxKvListCreateAsMap(allocator, NULL, CX_STORE_POINTERS);
458 if (map == NULL) return NULL; // LCOV_EXCL_LINE
459 cxDefineDestructor(map, cxJsonValueFree);
460 return map;
461 }
462
463 static void json_free_object_map(CxJsonObject obj) {
464 cxMapFree(obj);
527 } 465 }
528 466
529 static CxJsonValue* json_create_value(CxJson *json, CxJsonValueType type) { 467 static CxJsonValue* json_create_value(CxJson *json, CxJsonValueType type) {
530 CxJsonValue *v = cxCalloc(json->allocator, 1, sizeof(CxJsonValue)); 468 CxJsonValue *v = cxCalloc(json->allocator, 1, sizeof(CxJsonValue));
531 if (v == NULL) return NULL; // LCOV_EXCL_LINE 469 if (v == NULL) return NULL; // LCOV_EXCL_LINE
532 470
533 // initialize the value 471 // initialize the value
534 v->type = type; 472 v->type = type;
535 v->allocator = json->allocator; 473 v->allocator = json->allocator;
536 if (type == CX_JSON_ARRAY) { 474 if (type == CX_JSON_ARRAY) {
537 cx_array_initialize_a(json->allocator, v->value.array.array, 16); 475 cx_array_initialize_a(json->allocator, v->array.data, 16);
538 if (v->value.array.array == NULL) goto create_json_value_exit_error; // LCOV_EXCL_LINE 476 if (v->array.data == NULL) goto create_json_value_exit_error; // LCOV_EXCL_LINE
539 } else if (type == CX_JSON_OBJECT) { 477 } else if (type == CX_JSON_OBJECT) {
540 cx_array_initialize_a(json->allocator, v->value.object.values, 16); 478 v->object = json_create_object_map(json->allocator);
541 v->value.object.indices = cxCalloc(json->allocator, 16, sizeof(size_t)); 479 if (v->object == NULL) goto create_json_value_exit_error; // LCOV_EXCL_LINE
542 if (v->value.object.values == NULL ||
543 v->value.object.indices == NULL)
544 goto create_json_value_exit_error; // LCOV_EXCL_LINE
545 } 480 }
546 481
547 // add the new value to a possible parent 482 // add the new value to a possible parent
548 if (json->vbuf_size > 0) { 483 if (json->vbuf_size > 0) {
549 CxJsonValue *parent = json->vbuf[json->vbuf_size - 1]; 484 CxJsonValue *parent = json->vbuf[json->vbuf_size - 1];
550 assert(parent != NULL); 485 assert(parent != NULL);
551 if (parent->type == CX_JSON_ARRAY) { 486 if (parent->type == CX_JSON_ARRAY) {
552 CxArrayReallocator value_realloc = cx_array_reallocator(json->allocator, NULL); 487 CxArrayReallocator value_realloc = cx_array_reallocator(json->allocator, NULL);
553 if (cx_array_simple_add_a(&value_realloc, parent->value.array.array, v)) { 488 if (cx_array_simple_add_a(&value_realloc, parent->array.data, v)) {
554 goto create_json_value_exit_error; // LCOV_EXCL_LINE 489 goto create_json_value_exit_error; // LCOV_EXCL_LINE
555 } 490 }
556 } else if (parent->type == CX_JSON_OBJECT) { 491 } else if (parent->type == CX_JSON_OBJECT) {
557 // the member was already created after parsing the name 492 // the member was already created after parsing the name
558 assert(json->uncompleted_member.name.ptr != NULL); 493 // store the pointer of the uncompleted value in the map
559 json->uncompleted_member.value = v; 494 assert(json->uncompleted_member_name.ptr != NULL);
560 if (json_add_objvalue(parent, json->uncompleted_member)) { 495 if (cxMapPut(parent->object, json->uncompleted_member_name, v)) {
561 goto create_json_value_exit_error; // LCOV_EXCL_LINE 496 goto create_json_value_exit_error; // LCOV_EXCL_LINE
562 } 497 }
563 json->uncompleted_member.name = (cxmutstr) {NULL, 0}; 498 cx_strfree_a(json->allocator, &json->uncompleted_member_name);
564 } else { 499 } else {
565 assert(false); // LCOV_EXCL_LINE 500 assert(false); // LCOV_EXCL_LINE
566 } 501 }
567 } 502 }
568 503
622 if (json->vbuf != json->vbuf_internal) { 557 if (json->vbuf != json->vbuf_internal) {
623 cxFreeDefault(json->vbuf); 558 cxFreeDefault(json->vbuf);
624 } 559 }
625 cxJsonValueFree(json->parsed); 560 cxJsonValueFree(json->parsed);
626 json->parsed = NULL; 561 json->parsed = NULL;
627 if (json->uncompleted_member.name.ptr != NULL) { 562 token_destroy(&json->uncompleted);
628 cx_strfree_a(json->allocator, &json->uncompleted_member.name); 563 cx_strfree_a(json->allocator, &json->uncompleted_member_name);
629 json->uncompleted_member = (CxJsonObjValue){{NULL, 0}, NULL};
630 }
631 } 564 }
632 565
633 void cxJsonReset(CxJson *json) { 566 void cxJsonReset(CxJson *json) {
634 const CxAllocator *allocator = json->allocator; 567 const CxAllocator *allocator = json->allocator;
635 cxJsonDestroy(json); 568 cxJsonDestroy(json);
638 571
639 int cxJsonFilln(CxJson *json, const char *buf, size_t size) { 572 int cxJsonFilln(CxJson *json, const char *buf, size_t size) {
640 if (cxBufferEof(&json->buffer)) { 573 if (cxBufferEof(&json->buffer)) {
641 // reinitialize the buffer 574 // reinitialize the buffer
642 cxBufferDestroy(&json->buffer); 575 cxBufferDestroy(&json->buffer);
576 if (buf == NULL) buf = ""; // buffer must not be initialized with NULL
643 cxBufferInit(&json->buffer, (char*) buf, size, 577 cxBufferInit(&json->buffer, (char*) buf, size,
644 NULL, CX_BUFFER_AUTO_EXTEND | CX_BUFFER_COPY_ON_WRITE); 578 NULL, CX_BUFFER_AUTO_EXTEND | CX_BUFFER_COPY_ON_WRITE);
645 json->buffer.size = size; 579 json->buffer.size = size;
646 return 0; 580 return 0;
647 } else { 581 } else {
717 } 651 }
718 cxmutstr str = unescape_string(json->allocator, token.content); 652 cxmutstr str = unescape_string(json->allocator, token.content);
719 if (str.ptr == NULL) { 653 if (str.ptr == NULL) {
720 return_rec(CX_JSON_VALUE_ALLOC_FAILED); // LCOV_EXCL_LINE 654 return_rec(CX_JSON_VALUE_ALLOC_FAILED); // LCOV_EXCL_LINE
721 } 655 }
722 vbuf->value.string = str; 656 vbuf->string = str;
723 return_rec(CX_JSON_NO_ERROR); 657 return_rec(CX_JSON_NO_ERROR);
724 } 658 }
725 case CX_JSON_TOKEN_INTEGER: 659 case CX_JSON_TOKEN_INTEGER:
726 case CX_JSON_TOKEN_NUMBER: { 660 case CX_JSON_TOKEN_NUMBER: {
727 int type = token.tokentype == CX_JSON_TOKEN_INTEGER ? CX_JSON_INTEGER : CX_JSON_NUMBER; 661 int type = token.tokentype == CX_JSON_TOKEN_INTEGER ? CX_JSON_INTEGER : CX_JSON_NUMBER;
728 if (NULL == (vbuf = json_create_value(json, type))) { 662 if (NULL == (vbuf = json_create_value(json, type))) {
729 return_rec(CX_JSON_VALUE_ALLOC_FAILED); // LCOV_EXCL_LINE 663 return_rec(CX_JSON_VALUE_ALLOC_FAILED); // LCOV_EXCL_LINE
730 } 664 }
731 if (type == CX_JSON_INTEGER) { 665 if (type == CX_JSON_INTEGER) {
732 if (cx_strtoi64(token.content, &vbuf->value.integer, 10)) { 666 if (cx_strtoi64(token.content, &vbuf->integer, 10)) {
733 return_rec(CX_JSON_FORMAT_ERROR_NUMBER); 667 return_rec(CX_JSON_FORMAT_ERROR_NUMBER);
734 } 668 }
735 } else { 669 } else {
736 if (cx_strtod(token.content, &vbuf->value.number)) { 670 if (cx_strtod(token.content, &vbuf->number)) {
737 return_rec(CX_JSON_FORMAT_ERROR_NUMBER); 671 // TODO: at the moment this is unreachable, because the tokenizer is already stricter than cx_strtod()
672 return_rec(CX_JSON_FORMAT_ERROR_NUMBER); // LCOV_EXCL_LINE
738 } 673 }
739 } 674 }
740 return_rec(CX_JSON_NO_ERROR); 675 return_rec(CX_JSON_NO_ERROR);
741 } 676 }
742 case CX_JSON_TOKEN_LITERAL: { 677 case CX_JSON_TOKEN_LITERAL: {
743 if ((vbuf = json_create_value(json, CX_JSON_LITERAL)) == NULL) { 678 if ((vbuf = json_create_value(json, CX_JSON_LITERAL)) == NULL) {
744 return_rec(CX_JSON_VALUE_ALLOC_FAILED); // LCOV_EXCL_LINE 679 return_rec(CX_JSON_VALUE_ALLOC_FAILED); // LCOV_EXCL_LINE
745 } 680 }
746 if (0 == cx_strcmp(cx_strcast(token.content), cx_str("true"))) { 681 if (0 == cx_strcmp(cx_strcast(token.content), cx_str("true"))) {
747 vbuf->value.literal = CX_JSON_TRUE; 682 vbuf->literal = CX_JSON_TRUE;
748 } else if (0 == cx_strcmp(cx_strcast(token.content), cx_str("false"))) { 683 } else if (0 == cx_strcmp(cx_strcast(token.content), cx_str("false"))) {
749 vbuf->value.literal = CX_JSON_FALSE; 684 vbuf->literal = CX_JSON_FALSE;
750 } else { 685 } else {
751 vbuf->value.literal = CX_JSON_NULL; 686 vbuf->literal = CX_JSON_NULL;
752 } 687 }
753 return_rec(CX_JSON_NO_ERROR); 688 return_rec(CX_JSON_NO_ERROR);
754 } 689 }
755 default: { 690 default: {
756 return_rec(CX_JSON_FORMAT_ERROR_UNEXPECTED_TOKEN); 691 return_rec(CX_JSON_FORMAT_ERROR_UNEXPECTED_TOKEN);
782 // add new entry 717 // add new entry
783 cxmutstr name = unescape_string(json->allocator, token.content); 718 cxmutstr name = unescape_string(json->allocator, token.content);
784 if (name.ptr == NULL) { 719 if (name.ptr == NULL) {
785 return_rec(CX_JSON_VALUE_ALLOC_FAILED); // LCOV_EXCL_LINE 720 return_rec(CX_JSON_VALUE_ALLOC_FAILED); // LCOV_EXCL_LINE
786 } 721 }
787 assert(json->uncompleted_member.name.ptr == NULL); 722 assert(json->uncompleted_member_name.ptr == NULL);
788 json->uncompleted_member.name = name; 723 json->uncompleted_member_name = name;
789 assert(json->vbuf_size > 0); 724 assert(json->vbuf_size > 0);
790 725
791 // next state 726 // next state
792 json_add_state(json, JP_STATE_OBJ_COLON); 727 json_add_state(json, JP_STATE_OBJ_COLON);
793 return_rec(CX_JSON_NO_ERROR); 728 return_rec(CX_JSON_NO_ERROR);
813 return_rec(CX_JSON_FORMAT_ERROR_UNEXPECTED_TOKEN); 748 return_rec(CX_JSON_FORMAT_ERROR_UNEXPECTED_TOKEN);
814 } 749 }
815 } else { 750 } else {
816 // should be unreachable 751 // should be unreachable
817 assert(false); 752 assert(false);
818 return_rec(-1); 753 return_rec(-1); // LCOV_EXCL_LINE
819 } 754 }
820 } 755 }
821 756
822 CxJsonStatus cxJsonNext(CxJson *json, CxJsonValue **value) { 757 CxJsonStatus cxJsonNext(CxJson *json, CxJsonValue **value) {
823 // check if buffer has been filled 758 // initialize output value
759 *value = &cx_json_value_nothing;
760
761 // check if the buffer has been filled
824 if (json->buffer.space == NULL) { 762 if (json->buffer.space == NULL) {
825 return CX_JSON_NULL_DATA; 763 return CX_JSON_NULL_DATA;
826 } 764 }
827
828 // initialize output value
829 *value = &cx_json_value_nothing;
830 765
831 // parse data 766 // parse data
832 CxJsonStatus result; 767 CxJsonStatus result;
833 do { 768 do {
834 result = json_parse(json); 769 result = json_parse(json);
856 } 791 }
857 792
858 return result; 793 return result;
859 } 794 }
860 795
796 CxJsonStatus cx_json_from_string(const CxAllocator *allocator,
797 cxstring str, CxJsonValue **value) {
798 *value = &cx_json_value_nothing;
799 CxJson parser;
800 cxJsonInit(&parser, allocator);
801 if (cxJsonFill(&parser, str)) {
802 // LCOV_EXCL_START
803 cxJsonDestroy(&parser);
804 return CX_JSON_BUFFER_ALLOC_FAILED;
805 // LCOV_EXCL_STOP
806 }
807 CxJsonStatus status = cxJsonNext(&parser, value);
808 // check if we consume the total string
809 CxJsonValue *chk_value = NULL;
810 CxJsonStatus chk_status = CX_JSON_NO_DATA;
811 if (status == CX_JSON_NO_ERROR) {
812 chk_status = cxJsonNext(&parser, &chk_value);
813 }
814 cxJsonDestroy(&parser);
815 if (chk_status == CX_JSON_NO_DATA) {
816 return status;
817 } else {
818 cxJsonValueFree(*value);
819 // if chk_value is nothing, the free is harmless
820 cxJsonValueFree(chk_value);
821 *value = &cx_json_value_nothing;
822 return CX_JSON_FORMAT_ERROR_UNEXPECTED_TOKEN;
823 }
824
825 }
826
861 void cxJsonValueFree(CxJsonValue *value) { 827 void cxJsonValueFree(CxJsonValue *value) {
862 if (value == NULL || value->type == CX_JSON_NOTHING) return; 828 if (value == NULL || value->type == CX_JSON_NOTHING) return;
863 switch (value->type) { 829 switch (value->type) {
864 case CX_JSON_OBJECT: { 830 case CX_JSON_OBJECT: {
865 CxJsonObject obj = value->value.object; 831 json_free_object_map(value->object);
866 for (size_t i = 0; i < obj.values_size; i++) {
867 cxJsonValueFree(obj.values[i].value);
868 cx_strfree_a(value->allocator, &obj.values[i].name);
869 }
870 cxFree(value->allocator, obj.values);
871 cxFree(value->allocator, obj.indices);
872 break; 832 break;
873 } 833 }
874 case CX_JSON_ARRAY: { 834 case CX_JSON_ARRAY: {
875 CxJsonArray array = value->value.array; 835 CxJsonArray array = value->array;
876 for (size_t i = 0; i < array.array_size; i++) { 836 for (size_t i = 0; i < array.data_size; i++) {
877 cxJsonValueFree(array.array[i]); 837 cxJsonValueFree(array.data[i]);
878 } 838 }
879 cxFree(value->allocator, array.array); 839 cxFree(value->allocator, array.data);
880 break; 840 break;
881 } 841 }
882 case CX_JSON_STRING: { 842 case CX_JSON_STRING: {
883 cxFree(value->allocator, value->value.string.ptr); 843 cxFree(value->allocator, value->string.ptr);
884 break; 844 break;
885 } 845 }
886 default: { 846 default: {
887 break; 847 break;
888 } 848 }
894 if (allocator == NULL) allocator = cxDefaultAllocator; 854 if (allocator == NULL) allocator = cxDefaultAllocator;
895 CxJsonValue* v = cxMalloc(allocator, sizeof(CxJsonValue)); 855 CxJsonValue* v = cxMalloc(allocator, sizeof(CxJsonValue));
896 if (v == NULL) return NULL; 856 if (v == NULL) return NULL;
897 v->allocator = allocator; 857 v->allocator = allocator;
898 v->type = CX_JSON_OBJECT; 858 v->type = CX_JSON_OBJECT;
899 cx_array_initialize_a(allocator, v->value.object.values, 16); 859 v->object = json_create_object_map(allocator);
900 if (v->value.object.values == NULL) { // LCOV_EXCL_START 860 if (v->object == NULL) { // LCOV_EXCL_START
901 cxFree(allocator, v);
902 return NULL;
903 // LCOV_EXCL_STOP
904 }
905 v->value.object.indices = cxCalloc(allocator, 16, sizeof(size_t));
906 if (v->value.object.indices == NULL) { // LCOV_EXCL_START
907 cxFree(allocator, v->value.object.values);
908 cxFree(allocator, v); 861 cxFree(allocator, v);
909 return NULL; 862 return NULL;
910 // LCOV_EXCL_STOP 863 // LCOV_EXCL_STOP
911 } 864 }
912 return v; 865 return v;
916 if (allocator == NULL) allocator = cxDefaultAllocator; 869 if (allocator == NULL) allocator = cxDefaultAllocator;
917 CxJsonValue* v = cxMalloc(allocator, sizeof(CxJsonValue)); 870 CxJsonValue* v = cxMalloc(allocator, sizeof(CxJsonValue));
918 if (v == NULL) return NULL; 871 if (v == NULL) return NULL;
919 v->allocator = allocator; 872 v->allocator = allocator;
920 v->type = CX_JSON_ARRAY; 873 v->type = CX_JSON_ARRAY;
921 cx_array_initialize_a(allocator, v->value.array.array, 16); 874 cx_array_initialize_a(allocator, v->array.data, 16);
922 if (v->value.array.array == NULL) { cxFree(allocator, v); return NULL; } 875 if (v->array.data == NULL) { cxFree(allocator, v); return NULL; }
923 return v; 876 return v;
924 } 877 }
925 878
926 CxJsonValue* cxJsonCreateNumber(const CxAllocator* allocator, double num) { 879 CxJsonValue* cxJsonCreateNumber(const CxAllocator* allocator, double num) {
927 if (allocator == NULL) allocator = cxDefaultAllocator; 880 if (allocator == NULL) allocator = cxDefaultAllocator;
928 CxJsonValue* v = cxMalloc(allocator, sizeof(CxJsonValue)); 881 CxJsonValue* v = cxMalloc(allocator, sizeof(CxJsonValue));
929 if (v == NULL) return NULL; 882 if (v == NULL) return NULL;
930 v->allocator = allocator; 883 v->allocator = allocator;
931 v->type = CX_JSON_NUMBER; 884 v->type = CX_JSON_NUMBER;
932 v->value.number = num; 885 v->number = num;
933 return v; 886 return v;
934 } 887 }
935 888
936 CxJsonValue* cxJsonCreateInteger(const CxAllocator* allocator, int64_t num) { 889 CxJsonValue* cxJsonCreateInteger(const CxAllocator* allocator, int64_t num) {
937 if (allocator == NULL) allocator = cxDefaultAllocator; 890 if (allocator == NULL) allocator = cxDefaultAllocator;
938 CxJsonValue* v = cxMalloc(allocator, sizeof(CxJsonValue)); 891 CxJsonValue* v = cxMalloc(allocator, sizeof(CxJsonValue));
939 if (v == NULL) return NULL; 892 if (v == NULL) return NULL;
940 v->allocator = allocator; 893 v->allocator = allocator;
941 v->type = CX_JSON_INTEGER; 894 v->type = CX_JSON_INTEGER;
942 v->value.integer = num; 895 v->integer = num;
943 return v; 896 return v;
944 } 897 }
945 898
946 CxJsonValue* cxJsonCreateString(const CxAllocator* allocator, const char* str) { 899 CxJsonValue* cx_json_create_string(const CxAllocator* allocator, cxstring str) {
947 return cxJsonCreateCxString(allocator, cx_str(str));
948 }
949
950 CxJsonValue* cxJsonCreateCxString(const CxAllocator* allocator, cxstring str) {
951 if (allocator == NULL) allocator = cxDefaultAllocator; 900 if (allocator == NULL) allocator = cxDefaultAllocator;
952 CxJsonValue* v = cxMalloc(allocator, sizeof(CxJsonValue)); 901 CxJsonValue* v = cxMalloc(allocator, sizeof(CxJsonValue));
953 if (v == NULL) return NULL; 902 if (v == NULL) return NULL;
954 v->allocator = allocator; 903 v->allocator = allocator;
955 v->type = CX_JSON_STRING; 904 v->type = CX_JSON_STRING;
956 cxmutstr s = cx_strdup_a(allocator, str); 905 cxmutstr s = cx_strdup_a(allocator, str);
957 if (s.ptr == NULL) { cxFree(allocator, v); return NULL; } 906 if (s.ptr == NULL) { cxFree(allocator, v); return NULL; }
958 v->value.string = s; 907 v->string = s;
959 return v; 908 return v;
960 } 909 }
961 910
962 CxJsonValue* cxJsonCreateLiteral(const CxAllocator* allocator, CxJsonLiteral lit) { 911 CxJsonValue* cxJsonCreateLiteral(const CxAllocator* allocator, CxJsonLiteral lit) {
963 if (allocator == NULL) allocator = cxDefaultAllocator; 912 if (allocator == NULL) allocator = cxDefaultAllocator;
964 CxJsonValue* v = cxMalloc(allocator, sizeof(CxJsonValue)); 913 CxJsonValue* v = cxMalloc(allocator, sizeof(CxJsonValue));
965 if (v == NULL) return NULL; 914 if (v == NULL) return NULL;
966 v->allocator = allocator; 915 v->allocator = allocator;
967 v->type = CX_JSON_LITERAL; 916 v->type = CX_JSON_LITERAL;
968 v->value.literal = lit; 917 v->literal = lit;
969 return v; 918 return v;
970 } 919 }
971 920
972 // LCOV_EXCL_START 921 // LCOV_EXCL_START
973 // never called as long as malloc() does not return NULL 922 // never called as long as malloc() does not return NULL
1018 967
1019 int cxJsonArrAddCxStrings(CxJsonValue* arr, const cxstring* str, size_t count) { 968 int cxJsonArrAddCxStrings(CxJsonValue* arr, const cxstring* str, size_t count) {
1020 CxJsonValue** values = cxCallocDefault(count, sizeof(CxJsonValue*)); 969 CxJsonValue** values = cxCallocDefault(count, sizeof(CxJsonValue*));
1021 if (values == NULL) return -1; 970 if (values == NULL) return -1;
1022 for (size_t i = 0; i < count; i++) { 971 for (size_t i = 0; i < count; i++) {
1023 values[i] = cxJsonCreateCxString(arr->allocator, str[i]); 972 values[i] = cxJsonCreateString(arr->allocator, str[i]);
1024 if (values[i] == NULL) { json_arr_free_temp(values, count); return -1; } 973 if (values[i] == NULL) { json_arr_free_temp(values, count); return -1; }
1025 } 974 }
1026 int ret = cxJsonArrAddValues(arr, values, count); 975 int ret = cxJsonArrAddValues(arr, values, count);
1027 cxFreeDefault(values); 976 cxFreeDefault(values);
1028 return ret; 977 return ret;
1042 991
1043 int cxJsonArrAddValues(CxJsonValue* arr, CxJsonValue* const* val, size_t count) { 992 int cxJsonArrAddValues(CxJsonValue* arr, CxJsonValue* const* val, size_t count) {
1044 CxArrayReallocator value_realloc = cx_array_reallocator(arr->allocator, NULL); 993 CxArrayReallocator value_realloc = cx_array_reallocator(arr->allocator, NULL);
1045 assert(arr->type == CX_JSON_ARRAY); 994 assert(arr->type == CX_JSON_ARRAY);
1046 return cx_array_simple_copy_a(&value_realloc, 995 return cx_array_simple_copy_a(&value_realloc,
1047 arr->value.array.array, 996 arr->array.data,
1048 arr->value.array.array_size, 997 arr->array.data_size,
1049 val, count 998 val, count
1050 ); 999 );
1051 } 1000 }
1052 1001
1053 int cxJsonObjPut(CxJsonValue* obj, cxstring name, CxJsonValue* child) { 1002 int cx_json_obj_put(CxJsonValue* obj, cxstring name, CxJsonValue* child) {
1054 cxmutstr k = cx_strdup_a(obj->allocator, name); 1003 return cxMapPut(obj->object, name, child);
1055 if (k.ptr == NULL) return -1; 1004 }
1056 CxJsonObjValue kv = {k, child}; 1005
1057 if (json_add_objvalue(obj, kv)) { 1006 CxJsonValue* cx_json_obj_put_obj(CxJsonValue* obj, cxstring name) {
1058 cx_strfree_a(obj->allocator, &k);
1059 return 1;
1060 } else {
1061 return 0;
1062 }
1063 }
1064
1065 CxJsonValue* cxJsonObjPutObj(CxJsonValue* obj, cxstring name) {
1066 CxJsonValue* v = cxJsonCreateObj(obj->allocator); 1007 CxJsonValue* v = cxJsonCreateObj(obj->allocator);
1067 if (v == NULL) return NULL; 1008 if (v == NULL) return NULL;
1068 if (cxJsonObjPut(obj, name, v)) { cxJsonValueFree(v); return NULL; } 1009 if (cxJsonObjPut(obj, name, v)) { cxJsonValueFree(v); return NULL; }
1069 return v; 1010 return v;
1070 } 1011 }
1071 1012
1072 CxJsonValue* cxJsonObjPutArr(CxJsonValue* obj, cxstring name) { 1013 CxJsonValue* cx_json_obj_put_arr(CxJsonValue* obj, cxstring name) {
1073 CxJsonValue* v = cxJsonCreateArr(obj->allocator); 1014 CxJsonValue* v = cxJsonCreateArr(obj->allocator);
1074 if (v == NULL) return NULL; 1015 if (v == NULL) return NULL;
1075 if (cxJsonObjPut(obj, name, v)) { cxJsonValueFree(v); return NULL; } 1016 if (cxJsonObjPut(obj, name, v)) { cxJsonValueFree(v); return NULL; }
1076 return v; 1017 return v;
1077 } 1018 }
1078 1019
1079 CxJsonValue* cxJsonObjPutNumber(CxJsonValue* obj, cxstring name, double num) { 1020 CxJsonValue* cx_json_obj_put_number(CxJsonValue* obj, cxstring name, double num) {
1080 CxJsonValue* v = cxJsonCreateNumber(obj->allocator, num); 1021 CxJsonValue* v = cxJsonCreateNumber(obj->allocator, num);
1081 if (v == NULL) return NULL; 1022 if (v == NULL) return NULL;
1082 if (cxJsonObjPut(obj, name, v)) { cxJsonValueFree(v); return NULL; } 1023 if (cxJsonObjPut(obj, name, v)) { cxJsonValueFree(v); return NULL; }
1083 return v; 1024 return v;
1084 } 1025 }
1085 1026
1086 CxJsonValue* cxJsonObjPutInteger(CxJsonValue* obj, cxstring name, int64_t num) { 1027 CxJsonValue* cx_json_obj_put_integer(CxJsonValue* obj, cxstring name, int64_t num) {
1087 CxJsonValue* v = cxJsonCreateInteger(obj->allocator, num); 1028 CxJsonValue* v = cxJsonCreateInteger(obj->allocator, num);
1088 if (v == NULL) return NULL; 1029 if (v == NULL) return NULL;
1089 if (cxJsonObjPut(obj, name, v)) { cxJsonValueFree(v); return NULL; } 1030 if (cxJsonObjPut(obj, name, v)) { cxJsonValueFree(v); return NULL; }
1090 return v; 1031 return v;
1091 } 1032 }
1092 1033
1093 CxJsonValue* cxJsonObjPutString(CxJsonValue* obj, cxstring name, const char* str) { 1034 CxJsonValue* cx_json_obj_put_string(CxJsonValue* obj, cxstring name, cxstring str) {
1094 CxJsonValue* v = cxJsonCreateString(obj->allocator, str); 1035 CxJsonValue* v = cxJsonCreateString(obj->allocator, str);
1095 if (v == NULL) return NULL; 1036 if (v == NULL) return NULL;
1096 if (cxJsonObjPut(obj, name, v)) { cxJsonValueFree(v); return NULL; } 1037 if (cxJsonObjPut(obj, name, v)) { cxJsonValueFree(v); return NULL; }
1097 return v; 1038 return v;
1098 } 1039 }
1099 1040
1100 CxJsonValue* cxJsonObjPutCxString(CxJsonValue* obj, cxstring name, cxstring str) { 1041 CxJsonValue* cx_json_obj_put_literal(CxJsonValue* obj, cxstring name, CxJsonLiteral lit) {
1101 CxJsonValue* v = cxJsonCreateCxString(obj->allocator, str);
1102 if (v == NULL) return NULL;
1103 if (cxJsonObjPut(obj, name, v)) { cxJsonValueFree(v); return NULL; }
1104 return v;
1105 }
1106
1107 CxJsonValue* cxJsonObjPutLiteral(CxJsonValue* obj, cxstring name, CxJsonLiteral lit) {
1108 CxJsonValue* v = cxJsonCreateLiteral(obj->allocator, lit); 1042 CxJsonValue* v = cxJsonCreateLiteral(obj->allocator, lit);
1109 if (v == NULL) return NULL; 1043 if (v == NULL) return NULL;
1110 if (cxJsonObjPut(obj, name, v)) { cxJsonValueFree(v); return NULL;} 1044 if (cxJsonObjPut(obj, name, v)) { cxJsonValueFree(v); return NULL;}
1111 return v; 1045 return v;
1112 } 1046 }
1113 1047
1114 CxJsonValue *cxJsonArrGet(const CxJsonValue *value, size_t index) { 1048 CxJsonValue *cxJsonArrGet(const CxJsonValue *value, size_t index) {
1115 if (index >= value->value.array.array_size) { 1049 if (index >= value->array.data_size) {
1116 return &cx_json_value_nothing; 1050 return &cx_json_value_nothing;
1117 } 1051 }
1118 return value->value.array.array[index]; 1052 return value->array.data[index];
1119 } 1053 }
1120 1054
1121 CxJsonValue *cxJsonArrRemove(CxJsonValue *value, size_t index) { 1055 CxJsonValue *cxJsonArrRemove(CxJsonValue *value, size_t index) {
1122 if (index >= value->value.array.array_size) { 1056 if (index >= value->array.data_size) {
1123 return NULL; 1057 return NULL;
1124 } 1058 }
1125 CxJsonValue *ret = value->value.array.array[index]; 1059 CxJsonValue *ret = value->array.data[index];
1126 // TODO: replace with a low level cx_array_remove() 1060 // TODO: replace with a low level cx_array_remove()
1127 size_t count = value->value.array.array_size - index - 1; 1061 size_t count = value->array.data_size - index - 1;
1128 if (count > 0) { 1062 if (count > 0) {
1129 memmove(value->value.array.array + index, value->value.array.array + index + 1, count * sizeof(CxJsonValue*)); 1063 memmove(value->array.data + index, value->array.data + index + 1, count * sizeof(CxJsonValue*));
1130 } 1064 }
1131 value->value.array.array_size--; 1065 value->array.data_size--;
1132 return ret; 1066 return ret;
1133 } 1067 }
1134 1068
1135 char *cxJsonAsString(const CxJsonValue *value) { 1069 char *cxJsonAsString(const CxJsonValue *value) {
1136 return value->value.string.ptr; 1070 return value->string.ptr;
1137 } 1071 }
1138 1072
1139 cxstring cxJsonAsCxString(const CxJsonValue *value) { 1073 cxstring cxJsonAsCxString(const CxJsonValue *value) {
1140 return cx_strcast(value->value.string); 1074 return cx_strcast(value->string);
1141 } 1075 }
1142 1076
1143 cxmutstr cxJsonAsCxMutStr(const CxJsonValue *value) { 1077 cxmutstr cxJsonAsCxMutStr(const CxJsonValue *value) {
1144 return value->value.string; 1078 return value->string;
1145 } 1079 }
1146 1080
1147 double cxJsonAsDouble(const CxJsonValue *value) { 1081 double cxJsonAsDouble(const CxJsonValue *value) {
1148 if (value->type == CX_JSON_INTEGER) { 1082 if (value->type == CX_JSON_INTEGER) {
1149 return (double) value->value.integer; 1083 return (double) value->integer;
1150 } else { 1084 } else {
1151 return value->value.number; 1085 return value->number;
1152 } 1086 }
1153 } 1087 }
1154 1088
1155 int64_t cxJsonAsInteger(const CxJsonValue *value) { 1089 int64_t cxJsonAsInteger(const CxJsonValue *value) {
1156 if (value->type == CX_JSON_INTEGER) { 1090 if (value->type == CX_JSON_INTEGER) {
1157 return value->value.integer; 1091 return value->integer;
1158 } else { 1092 } else {
1159 return (int64_t) value->value.number; 1093 return (int64_t) value->number;
1160 } 1094 }
1161 } 1095 }
1162 1096
1163 CxIterator cxJsonArrIter(const CxJsonValue *value) { 1097 CxIterator cxJsonArrIter(const CxJsonValue *value) {
1164 return cxIteratorPtr( 1098 return cxIteratorPtr(
1165 value->value.array.array, 1099 value->array.data,
1166 value->value.array.array_size, 1100 value->array.data_size,
1167 true // arrays need to keep order 1101 true // arrays need to keep order
1168 ); 1102 );
1169 } 1103 }
1170 1104
1171 CxIterator cxJsonObjIter(const CxJsonValue *value) { 1105 CxMapIterator cxJsonObjIter(const CxJsonValue *value) {
1172 return cxIterator( 1106 return cxMapIterator(value->object);
1173 value->value.object.values,
1174 sizeof(CxJsonObjValue),
1175 value->value.object.values_size,
1176 true // TODO: objects do not always need to keep order
1177 );
1178 } 1107 }
1179 1108
1180 CxJsonValue *cx_json_obj_get(const CxJsonValue *value, cxstring name) { 1109 CxJsonValue *cx_json_obj_get(const CxJsonValue *value, cxstring name) {
1181 size_t index = json_find_objvalue(value, name); 1110 CxJsonValue *v = cxMapGet(value->object, name);
1182 if (index >= value->value.object.values_size) { 1111 if (v == NULL) {
1183 return &cx_json_value_nothing; 1112 return &cx_json_value_nothing;
1184 } else { 1113 } else {
1185 return value->value.object.values[index].value; 1114 return v;
1186 } 1115 }
1187 } 1116 }
1188 1117
1189 CxJsonValue *cx_json_obj_remove(CxJsonValue *value, cxstring name) { 1118 CxJsonValue *cx_json_obj_remove(CxJsonValue *value, cxstring name) {
1190 size_t index = json_find_objvalue(value, name); 1119 CxJsonValue *v = NULL;
1191 if (index >= value->value.object.values_size) { 1120 cxMapRemoveAndGet(value->object, name, &v);
1192 return NULL; 1121 return v;
1193 } else {
1194 CxJsonObjValue kv = value->value.object.values[index];
1195 cx_strfree_a(value->allocator, &kv.name);
1196 // TODO: replace with cx_array_remove() / cx_array_remove_fast()
1197 value->value.object.values_size--;
1198 memmove(value->value.object.values + index, value->value.object.values + index + 1, (value->value.object.values_size - index) * sizeof(CxJsonObjValue));
1199 return kv.value;
1200 }
1201 } 1122 }
1202 1123
1203 CxJsonWriter cxJsonWriterCompact(void) { 1124 CxJsonWriter cxJsonWriterCompact(void) {
1204 return (CxJsonWriter) { 1125 return (CxJsonWriter) {
1205 false, 1126 false,
1206 true,
1207 6, 1127 6,
1208 false, 1128 false,
1209 4, 1129 4,
1210 false 1130 false
1211 }; 1131 };
1212 } 1132 }
1213 1133
1214 CxJsonWriter cxJsonWriterPretty(bool use_spaces) { 1134 CxJsonWriter cxJsonWriterPretty(bool use_spaces) {
1215 return (CxJsonWriter) { 1135 return (CxJsonWriter) {
1216 true,
1217 true, 1136 true,
1218 6, 1137 6,
1219 use_spaces, 1138 use_spaces,
1220 4, 1139 4,
1221 false 1140 false
1277 } else { 1196 } else {
1278 actual += wfunc(begin_obj, 1, 1, target); 1197 actual += wfunc(begin_obj, 1, 1, target);
1279 expected++; 1198 expected++;
1280 } 1199 }
1281 depth++; 1200 depth++;
1282 size_t elem_count = value->value.object.values_size; 1201 CxMapIterator member_iter = cxJsonObjIter(value);
1283 for (size_t look_idx = 0; look_idx < elem_count; look_idx++) { 1202 cx_foreach(const CxMapEntry *, member, member_iter) {
1284 // get the member either via index array or directly
1285 size_t elem_idx = settings->sort_members
1286 ? look_idx
1287 : value->value.object.indices[look_idx];
1288 CxJsonObjValue *member = &value->value.object.values[elem_idx];
1289 if (settings->sort_members) {
1290 depth++;depth--;
1291 }
1292
1293 // possible indentation 1203 // possible indentation
1294 if (settings->pretty) { 1204 if (settings->pretty) {
1295 if (cx_json_writer_indent(target, wfunc, settings, depth)) { 1205 if (cx_json_writer_indent(target, wfunc, settings, depth)) {
1296 return 1; // LCOV_EXCL_LINE 1206 return 1; // LCOV_EXCL_LINE
1297 } 1207 }
1298 } 1208 }
1299 1209
1300 // the name 1210 // the name
1301 actual += wfunc("\"", 1, 1, target); 1211 actual += wfunc("\"", 1, 1, target);
1302 cxmutstr name = escape_string(member->name, settings->escape_slash); 1212 cxstring key = cx_strn(member->key->data, member->key->len);
1213 cxmutstr name = escape_string(key, settings->escape_slash);
1303 actual += wfunc(name.ptr, 1, name.length, target); 1214 actual += wfunc(name.ptr, 1, name.length, target);
1304 if (name.ptr != member->name.ptr) {
1305 cx_strfree(&name);
1306 }
1307 actual += wfunc("\"", 1, 1, target); 1215 actual += wfunc("\"", 1, 1, target);
1308 const char *obj_name_sep = ": "; 1216 const char *obj_name_sep = ": ";
1309 if (settings->pretty) { 1217 if (settings->pretty) {
1310 actual += wfunc(obj_name_sep, 1, 2, target); 1218 actual += wfunc(obj_name_sep, 1, 2, target);
1311 expected += 4 + member->name.length; 1219 expected += 4 + name.length;
1312 } else { 1220 } else {
1313 actual += wfunc(obj_name_sep, 1, 1, target); 1221 actual += wfunc(obj_name_sep, 1, 1, target);
1314 expected += 3 + member->name.length; 1222 expected += 3 + name.length;
1223 }
1224 if (name.ptr != key.ptr) {
1225 cx_strfree(&name);
1315 } 1226 }
1316 1227
1317 // the value 1228 // the value
1318 if (cx_json_write_rec(target, member->value, wfunc, settings, depth)) return 1; 1229 if (cx_json_write_rec(target, member->value, wfunc, settings, depth)) return 1;
1319 1230
1320 // end of object-value 1231 // end of object-value
1321 if (look_idx < elem_count - 1) { 1232 if (member_iter.index < member_iter.elem_count - 1) {
1322 const char *obj_value_sep = ",\n"; 1233 const char *obj_value_sep = ",\n";
1323 if (settings->pretty) { 1234 if (settings->pretty) {
1324 actual += wfunc(obj_value_sep, 1, 2, target); 1235 actual += wfunc(obj_value_sep, 1, 2, target);
1325 expected += 2; 1236 expected += 2;
1326 } else { 1237 } else {
1348 CxIterator iter = cxJsonArrIter(value); 1259 CxIterator iter = cxJsonArrIter(value);
1349 cx_foreach(CxJsonValue*, element, iter) { 1260 cx_foreach(CxJsonValue*, element, iter) {
1350 if (cx_json_write_rec( 1261 if (cx_json_write_rec(
1351 target, element, 1262 target, element,
1352 wfunc, settings, depth) 1263 wfunc, settings, depth)
1353 ) return 1; 1264 ) {
1265 return 1; // LCOV_EXCL_LINE
1266 }
1354 1267
1355 if (iter.index < iter.elem_count - 1) { 1268 if (iter.index < iter.elem_count - 1) {
1356 const char *arr_value_sep = ", "; 1269 const char *arr_value_sep = ", ";
1357 if (settings->pretty) { 1270 if (settings->pretty) {
1358 actual += wfunc(arr_value_sep, 1, 2, target); 1271 actual += wfunc(arr_value_sep, 1, 2, target);
1367 expected++; 1280 expected++;
1368 break; 1281 break;
1369 } 1282 }
1370 case CX_JSON_STRING: { 1283 case CX_JSON_STRING: {
1371 actual += wfunc("\"", 1, 1, target); 1284 actual += wfunc("\"", 1, 1, target);
1372 cxmutstr str = escape_string(value->value.string, settings->escape_slash); 1285 cxmutstr str = escape_string(cx_strcast(value->string),
1286 settings->escape_slash);
1373 actual += wfunc(str.ptr, 1, str.length, target); 1287 actual += wfunc(str.ptr, 1, str.length, target);
1374 if (str.ptr != value->value.string.ptr) { 1288 actual += wfunc("\"", 1, 1, target);
1289 expected += 2 + str.length;
1290 if (str.ptr != value->string.ptr) {
1375 cx_strfree(&str); 1291 cx_strfree(&str);
1376 } 1292 }
1377 actual += wfunc("\"", 1, 1, target);
1378 expected += 2 + value->value.string.length;
1379 break; 1293 break;
1380 } 1294 }
1381 case CX_JSON_NUMBER: { 1295 case CX_JSON_NUMBER: {
1382 int precision = settings->frac_max_digits; 1296 int precision = settings->frac_max_digits;
1383 // because of the way how %g is defined, we need to 1297 // because of the way how %g is defined, we need to
1384 // double the precision and truncate ourselves 1298 // double the precision and truncate ourselves
1385 precision = 1 + (precision > 15 ? 30 : 2 * precision); 1299 precision = 1 + (precision > 15 ? 30 : 2 * precision);
1386 snprintf(numbuf, 40, "%.*g", precision, value->value.number); 1300 snprintf(numbuf, 40, "%.*g", precision, value->number);
1387 char *dot, *exp; 1301 char *dot, *exp;
1388 unsigned char max_digits; 1302 unsigned char max_digits;
1389 // find the decimal separator and hope that it's one of . or , 1303 // find the decimal separator and hope that it's one of . or ,
1390 dot = strchr(numbuf, '.'); 1304 dot = strchr(numbuf, '.');
1391 if (dot == NULL) { 1305 if (dot == NULL) {
1445 expected += len; 1359 expected += len;
1446 } 1360 }
1447 break; 1361 break;
1448 } 1362 }
1449 case CX_JSON_INTEGER: { 1363 case CX_JSON_INTEGER: {
1450 snprintf(numbuf, 32, "%" PRIi64, value->value.integer); 1364 snprintf(numbuf, 32, "%" PRIi64, value->integer);
1451 size_t len = strlen(numbuf); 1365 size_t len = strlen(numbuf);
1452 actual += wfunc(numbuf, 1, len, target); 1366 actual += wfunc(numbuf, 1, len, target);
1453 expected += len; 1367 expected += len;
1454 break; 1368 break;
1455 } 1369 }
1456 case CX_JSON_LITERAL: { 1370 case CX_JSON_LITERAL: {
1457 if (value->value.literal == CX_JSON_TRUE) { 1371 if (value->literal == CX_JSON_TRUE) {
1458 actual += wfunc("true", 1, 4, target); 1372 actual += wfunc("true", 1, 4, target);
1459 expected += 4; 1373 expected += 4;
1460 } else if (value->value.literal == CX_JSON_FALSE) { 1374 } else if (value->literal == CX_JSON_FALSE) {
1461 actual += wfunc("false", 1, 5, target); 1375 actual += wfunc("false", 1, 5, target);
1462 expected += 5; 1376 expected += 5;
1463 } else { 1377 } else {
1464 actual += wfunc("null", 1, 4, target); 1378 actual += wfunc("null", 1, 4, target);
1465 expected += 4; 1379 expected += 4;
1493 if (settings == NULL) { 1407 if (settings == NULL) {
1494 settings = &writer_default; 1408 settings = &writer_default;
1495 } 1409 }
1496 return cx_json_write_rec(target, value, wfunc, settings, 0); 1410 return cx_json_write_rec(target, value, wfunc, settings, 0);
1497 } 1411 }
1412
1413 static cxmutstr cx_json_to_string(CxJsonValue *value, const CxAllocator *allocator, CxJsonWriter *writer) {
1414 if (allocator == NULL) allocator = cxDefaultAllocator;
1415 CxBuffer buffer;
1416 if (cxBufferInit(&buffer, NULL, 128, allocator,
1417 CX_BUFFER_AUTO_EXTEND | CX_BUFFER_DO_NOT_FREE)) {
1418 return (cxmutstr){NULL, 0};
1419 }
1420 if (cx_json_write_rec(&buffer, value, cxBufferWriteFunc, writer, 0)
1421 || cxBufferTerminate(&buffer)) {
1422 // LCOV_EXCL_START
1423 buffer.flags &= ~CX_BUFFER_DO_NOT_FREE;
1424 cxBufferDestroy(&buffer);
1425 return (cxmutstr){NULL, 0};
1426 // LCOV_EXCL_STOP
1427 } else {
1428 cxmutstr str = cx_mutstrn(buffer.space, buffer.size);
1429 cxBufferDestroy(&buffer);
1430 return str;
1431 }
1432
1433 }
1434
1435 cxmutstr cxJsonToString(CxJsonValue *value, const CxAllocator *allocator) {
1436 CxJsonWriter writer = cxJsonWriterCompact();
1437 return cx_json_to_string(value, allocator, &writer);
1438 }
1439
1440 cxmutstr cxJsonToPrettyString(CxJsonValue *value, const CxAllocator *allocator) {
1441 CxJsonWriter writer = cxJsonWriterPretty(true);
1442 return cx_json_to_string(value, allocator, &writer);
1443 }

mercurial