| 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; // LCOV_EXCL_LINE |
|
| 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) { |
| 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 |
| 718 } |
651 } |
| 719 cxmutstr str = unescape_string(json->allocator, token.content); |
652 cxmutstr str = unescape_string(json->allocator, token.content); |
| 720 if (str.ptr == NULL) { |
653 if (str.ptr == NULL) { |
| 721 return_rec(CX_JSON_VALUE_ALLOC_FAILED); // LCOV_EXCL_LINE |
654 return_rec(CX_JSON_VALUE_ALLOC_FAILED); // LCOV_EXCL_LINE |
| 722 } |
655 } |
| 723 vbuf->value.string = str; |
656 vbuf->string = str; |
| 724 return_rec(CX_JSON_NO_ERROR); |
657 return_rec(CX_JSON_NO_ERROR); |
| 725 } |
658 } |
| 726 case CX_JSON_TOKEN_INTEGER: |
659 case CX_JSON_TOKEN_INTEGER: |
| 727 case CX_JSON_TOKEN_NUMBER: { |
660 case CX_JSON_TOKEN_NUMBER: { |
| 728 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; |
| 729 if (NULL == (vbuf = json_create_value(json, type))) { |
662 if (NULL == (vbuf = json_create_value(json, type))) { |
| 730 return_rec(CX_JSON_VALUE_ALLOC_FAILED); // LCOV_EXCL_LINE |
663 return_rec(CX_JSON_VALUE_ALLOC_FAILED); // LCOV_EXCL_LINE |
| 731 } |
664 } |
| 732 if (type == CX_JSON_INTEGER) { |
665 if (type == CX_JSON_INTEGER) { |
| 733 if (cx_strtoi64(token.content, &vbuf->value.integer, 10)) { |
666 if (cx_strtoi64(token.content, &vbuf->integer, 10)) { |
| 734 return_rec(CX_JSON_FORMAT_ERROR_NUMBER); |
667 return_rec(CX_JSON_FORMAT_ERROR_NUMBER); |
| 735 } |
668 } |
| 736 } else { |
669 } else { |
| 737 if (cx_strtod(token.content, &vbuf->value.number)) { |
670 if (cx_strtod(token.content, &vbuf->number)) { |
| 738 // TODO: at the moment this is unreachable, because the tokenizer is already stricter than cx_strtod() |
671 // TODO: at the moment this is unreachable, because the tokenizer is already stricter than cx_strtod() |
| 739 return_rec(CX_JSON_FORMAT_ERROR_NUMBER); // LCOV_EXCL_LINE |
672 return_rec(CX_JSON_FORMAT_ERROR_NUMBER); // LCOV_EXCL_LINE |
| 740 } |
673 } |
| 741 } |
674 } |
| 742 return_rec(CX_JSON_NO_ERROR); |
675 return_rec(CX_JSON_NO_ERROR); |
| 744 case CX_JSON_TOKEN_LITERAL: { |
677 case CX_JSON_TOKEN_LITERAL: { |
| 745 if ((vbuf = json_create_value(json, CX_JSON_LITERAL)) == NULL) { |
678 if ((vbuf = json_create_value(json, CX_JSON_LITERAL)) == NULL) { |
| 746 return_rec(CX_JSON_VALUE_ALLOC_FAILED); // LCOV_EXCL_LINE |
679 return_rec(CX_JSON_VALUE_ALLOC_FAILED); // LCOV_EXCL_LINE |
| 747 } |
680 } |
| 748 if (0 == cx_strcmp(cx_strcast(token.content), cx_str("true"))) { |
681 if (0 == cx_strcmp(cx_strcast(token.content), cx_str("true"))) { |
| 749 vbuf->value.literal = CX_JSON_TRUE; |
682 vbuf->literal = CX_JSON_TRUE; |
| 750 } 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"))) { |
| 751 vbuf->value.literal = CX_JSON_FALSE; |
684 vbuf->literal = CX_JSON_FALSE; |
| 752 } else { |
685 } else { |
| 753 vbuf->value.literal = CX_JSON_NULL; |
686 vbuf->literal = CX_JSON_NULL; |
| 754 } |
687 } |
| 755 return_rec(CX_JSON_NO_ERROR); |
688 return_rec(CX_JSON_NO_ERROR); |
| 756 } |
689 } |
| 757 default: { |
690 default: { |
| 758 return_rec(CX_JSON_FORMAT_ERROR_UNEXPECTED_TOKEN); |
691 return_rec(CX_JSON_FORMAT_ERROR_UNEXPECTED_TOKEN); |
| 858 } |
791 } |
| 859 |
792 |
| 860 return result; |
793 return result; |
| 861 } |
794 } |
| 862 |
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 |
| 863 void cxJsonValueFree(CxJsonValue *value) { |
827 void cxJsonValueFree(CxJsonValue *value) { |
| 864 if (value == NULL || value->type == CX_JSON_NOTHING) return; |
828 if (value == NULL || value->type == CX_JSON_NOTHING) return; |
| 865 switch (value->type) { |
829 switch (value->type) { |
| 866 case CX_JSON_OBJECT: { |
830 case CX_JSON_OBJECT: { |
| 867 CxJsonObject obj = value->value.object; |
831 json_free_object_map(value->object); |
| 868 for (size_t i = 0; i < obj.values_size; i++) { |
|
| 869 cxJsonValueFree(obj.values[i].value); |
|
| 870 cx_strfree_a(value->allocator, &obj.values[i].name); |
|
| 871 } |
|
| 872 cxFree(value->allocator, obj.values); |
|
| 873 cxFree(value->allocator, obj.indices); |
|
| 874 break; |
832 break; |
| 875 } |
833 } |
| 876 case CX_JSON_ARRAY: { |
834 case CX_JSON_ARRAY: { |
| 877 CxJsonArray array = value->value.array; |
835 CxJsonArray array = value->array; |
| 878 for (size_t i = 0; i < array.array_size; i++) { |
836 for (size_t i = 0; i < array.data_size; i++) { |
| 879 cxJsonValueFree(array.array[i]); |
837 cxJsonValueFree(array.data[i]); |
| 880 } |
838 } |
| 881 cxFree(value->allocator, array.array); |
839 cxFree(value->allocator, array.data); |
| 882 break; |
840 break; |
| 883 } |
841 } |
| 884 case CX_JSON_STRING: { |
842 case CX_JSON_STRING: { |
| 885 cxFree(value->allocator, value->value.string.ptr); |
843 cxFree(value->allocator, value->string.ptr); |
| 886 break; |
844 break; |
| 887 } |
845 } |
| 888 default: { |
846 default: { |
| 889 break; |
847 break; |
| 890 } |
848 } |
| 896 if (allocator == NULL) allocator = cxDefaultAllocator; |
854 if (allocator == NULL) allocator = cxDefaultAllocator; |
| 897 CxJsonValue* v = cxMalloc(allocator, sizeof(CxJsonValue)); |
855 CxJsonValue* v = cxMalloc(allocator, sizeof(CxJsonValue)); |
| 898 if (v == NULL) return NULL; |
856 if (v == NULL) return NULL; |
| 899 v->allocator = allocator; |
857 v->allocator = allocator; |
| 900 v->type = CX_JSON_OBJECT; |
858 v->type = CX_JSON_OBJECT; |
| 901 cx_array_initialize_a(allocator, v->value.object.values, 16); |
859 v->object = json_create_object_map(allocator); |
| 902 if (v->value.object.values == NULL) { // LCOV_EXCL_START |
860 if (v->object == NULL) { // LCOV_EXCL_START |
| 903 cxFree(allocator, v); |
|
| 904 return NULL; |
|
| 905 // LCOV_EXCL_STOP |
|
| 906 } |
|
| 907 v->value.object.indices = cxCalloc(allocator, 16, sizeof(size_t)); |
|
| 908 if (v->value.object.indices == NULL) { // LCOV_EXCL_START |
|
| 909 cxFree(allocator, v->value.object.values); |
|
| 910 cxFree(allocator, v); |
861 cxFree(allocator, v); |
| 911 return NULL; |
862 return NULL; |
| 912 // LCOV_EXCL_STOP |
863 // LCOV_EXCL_STOP |
| 913 } |
864 } |
| 914 return v; |
865 return v; |
| 918 if (allocator == NULL) allocator = cxDefaultAllocator; |
869 if (allocator == NULL) allocator = cxDefaultAllocator; |
| 919 CxJsonValue* v = cxMalloc(allocator, sizeof(CxJsonValue)); |
870 CxJsonValue* v = cxMalloc(allocator, sizeof(CxJsonValue)); |
| 920 if (v == NULL) return NULL; |
871 if (v == NULL) return NULL; |
| 921 v->allocator = allocator; |
872 v->allocator = allocator; |
| 922 v->type = CX_JSON_ARRAY; |
873 v->type = CX_JSON_ARRAY; |
| 923 cx_array_initialize_a(allocator, v->value.array.array, 16); |
874 cx_array_initialize_a(allocator, v->array.data, 16); |
| 924 if (v->value.array.array == NULL) { cxFree(allocator, v); return NULL; } |
875 if (v->array.data == NULL) { cxFree(allocator, v); return NULL; } |
| 925 return v; |
876 return v; |
| 926 } |
877 } |
| 927 |
878 |
| 928 CxJsonValue* cxJsonCreateNumber(const CxAllocator* allocator, double num) { |
879 CxJsonValue* cxJsonCreateNumber(const CxAllocator* allocator, double num) { |
| 929 if (allocator == NULL) allocator = cxDefaultAllocator; |
880 if (allocator == NULL) allocator = cxDefaultAllocator; |
| 930 CxJsonValue* v = cxMalloc(allocator, sizeof(CxJsonValue)); |
881 CxJsonValue* v = cxMalloc(allocator, sizeof(CxJsonValue)); |
| 931 if (v == NULL) return NULL; |
882 if (v == NULL) return NULL; |
| 932 v->allocator = allocator; |
883 v->allocator = allocator; |
| 933 v->type = CX_JSON_NUMBER; |
884 v->type = CX_JSON_NUMBER; |
| 934 v->value.number = num; |
885 v->number = num; |
| 935 return v; |
886 return v; |
| 936 } |
887 } |
| 937 |
888 |
| 938 CxJsonValue* cxJsonCreateInteger(const CxAllocator* allocator, int64_t num) { |
889 CxJsonValue* cxJsonCreateInteger(const CxAllocator* allocator, int64_t num) { |
| 939 if (allocator == NULL) allocator = cxDefaultAllocator; |
890 if (allocator == NULL) allocator = cxDefaultAllocator; |
| 940 CxJsonValue* v = cxMalloc(allocator, sizeof(CxJsonValue)); |
891 CxJsonValue* v = cxMalloc(allocator, sizeof(CxJsonValue)); |
| 941 if (v == NULL) return NULL; |
892 if (v == NULL) return NULL; |
| 942 v->allocator = allocator; |
893 v->allocator = allocator; |
| 943 v->type = CX_JSON_INTEGER; |
894 v->type = CX_JSON_INTEGER; |
| 944 v->value.integer = num; |
895 v->integer = num; |
| 945 return v; |
896 return v; |
| 946 } |
897 } |
| 947 |
898 |
| 948 CxJsonValue* cx_json_create_string(const CxAllocator* allocator, cxstring str) { |
899 CxJsonValue* cx_json_create_string(const CxAllocator* allocator, cxstring str) { |
| 949 if (allocator == NULL) allocator = cxDefaultAllocator; |
900 if (allocator == NULL) allocator = cxDefaultAllocator; |
| 951 if (v == NULL) return NULL; |
902 if (v == NULL) return NULL; |
| 952 v->allocator = allocator; |
903 v->allocator = allocator; |
| 953 v->type = CX_JSON_STRING; |
904 v->type = CX_JSON_STRING; |
| 954 cxmutstr s = cx_strdup_a(allocator, str); |
905 cxmutstr s = cx_strdup_a(allocator, str); |
| 955 if (s.ptr == NULL) { cxFree(allocator, v); return NULL; } |
906 if (s.ptr == NULL) { cxFree(allocator, v); return NULL; } |
| 956 v->value.string = s; |
907 v->string = s; |
| 957 return v; |
908 return v; |
| 958 } |
909 } |
| 959 |
910 |
| 960 CxJsonValue* cxJsonCreateLiteral(const CxAllocator* allocator, CxJsonLiteral lit) { |
911 CxJsonValue* cxJsonCreateLiteral(const CxAllocator* allocator, CxJsonLiteral lit) { |
| 961 if (allocator == NULL) allocator = cxDefaultAllocator; |
912 if (allocator == NULL) allocator = cxDefaultAllocator; |
| 962 CxJsonValue* v = cxMalloc(allocator, sizeof(CxJsonValue)); |
913 CxJsonValue* v = cxMalloc(allocator, sizeof(CxJsonValue)); |
| 963 if (v == NULL) return NULL; |
914 if (v == NULL) return NULL; |
| 964 v->allocator = allocator; |
915 v->allocator = allocator; |
| 965 v->type = CX_JSON_LITERAL; |
916 v->type = CX_JSON_LITERAL; |
| 966 v->value.literal = lit; |
917 v->literal = lit; |
| 967 return v; |
918 return v; |
| 968 } |
919 } |
| 969 |
920 |
| 970 // LCOV_EXCL_START |
921 // LCOV_EXCL_START |
| 971 // never called as long as malloc() does not return NULL |
922 // never called as long as malloc() does not return NULL |
| 1040 |
991 |
| 1041 int cxJsonArrAddValues(CxJsonValue* arr, CxJsonValue* const* val, size_t count) { |
992 int cxJsonArrAddValues(CxJsonValue* arr, CxJsonValue* const* val, size_t count) { |
| 1042 CxArrayReallocator value_realloc = cx_array_reallocator(arr->allocator, NULL); |
993 CxArrayReallocator value_realloc = cx_array_reallocator(arr->allocator, NULL); |
| 1043 assert(arr->type == CX_JSON_ARRAY); |
994 assert(arr->type == CX_JSON_ARRAY); |
| 1044 return cx_array_simple_copy_a(&value_realloc, |
995 return cx_array_simple_copy_a(&value_realloc, |
| 1045 arr->value.array.array, |
996 arr->array.data, |
| 1046 arr->value.array.array_size, |
997 arr->array.data_size, |
| 1047 val, count |
998 val, count |
| 1048 ); |
999 ); |
| 1049 } |
1000 } |
| 1050 |
1001 |
| 1051 int cx_json_obj_put(CxJsonValue* obj, cxstring name, CxJsonValue* child) { |
1002 int cx_json_obj_put(CxJsonValue* obj, cxstring name, CxJsonValue* child) { |
| 1052 cxmutstr k = cx_strdup_a(obj->allocator, name); |
1003 return cxMapPut(obj->object, name, child); |
| 1053 if (k.ptr == NULL) return -1; |
|
| 1054 CxJsonObjValue kv = {k, child}; |
|
| 1055 if (json_add_objvalue(obj, kv)) { |
|
| 1056 // LCOV_EXCL_START |
|
| 1057 cx_strfree_a(obj->allocator, &k); |
|
| 1058 return 1; |
|
| 1059 // LCOV_EXCL_STOP |
|
| 1060 } else { |
|
| 1061 return 0; |
|
| 1062 } |
|
| 1063 } |
1004 } |
| 1064 |
1005 |
| 1065 CxJsonValue* cx_json_obj_put_obj(CxJsonValue* obj, cxstring name) { |
1006 CxJsonValue* cx_json_obj_put_obj(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; |
| 1103 if (cxJsonObjPut(obj, name, v)) { cxJsonValueFree(v); return NULL;} |
1044 if (cxJsonObjPut(obj, name, v)) { cxJsonValueFree(v); return NULL;} |
| 1104 return v; |
1045 return v; |
| 1105 } |
1046 } |
| 1106 |
1047 |
| 1107 CxJsonValue *cxJsonArrGet(const CxJsonValue *value, size_t index) { |
1048 CxJsonValue *cxJsonArrGet(const CxJsonValue *value, size_t index) { |
| 1108 if (index >= value->value.array.array_size) { |
1049 if (index >= value->array.data_size) { |
| 1109 return &cx_json_value_nothing; |
1050 return &cx_json_value_nothing; |
| 1110 } |
1051 } |
| 1111 return value->value.array.array[index]; |
1052 return value->array.data[index]; |
| 1112 } |
1053 } |
| 1113 |
1054 |
| 1114 CxJsonValue *cxJsonArrRemove(CxJsonValue *value, size_t index) { |
1055 CxJsonValue *cxJsonArrRemove(CxJsonValue *value, size_t index) { |
| 1115 if (index >= value->value.array.array_size) { |
1056 if (index >= value->array.data_size) { |
| 1116 return NULL; |
1057 return NULL; |
| 1117 } |
1058 } |
| 1118 CxJsonValue *ret = value->value.array.array[index]; |
1059 CxJsonValue *ret = value->array.data[index]; |
| 1119 // TODO: replace with a low level cx_array_remove() |
1060 // TODO: replace with a low level cx_array_remove() |
| 1120 size_t count = value->value.array.array_size - index - 1; |
1061 size_t count = value->array.data_size - index - 1; |
| 1121 if (count > 0) { |
1062 if (count > 0) { |
| 1122 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*)); |
| 1123 } |
1064 } |
| 1124 value->value.array.array_size--; |
1065 value->array.data_size--; |
| 1125 return ret; |
1066 return ret; |
| 1126 } |
1067 } |
| 1127 |
1068 |
| 1128 char *cxJsonAsString(const CxJsonValue *value) { |
1069 char *cxJsonAsString(const CxJsonValue *value) { |
| 1129 return value->value.string.ptr; |
1070 return value->string.ptr; |
| 1130 } |
1071 } |
| 1131 |
1072 |
| 1132 cxstring cxJsonAsCxString(const CxJsonValue *value) { |
1073 cxstring cxJsonAsCxString(const CxJsonValue *value) { |
| 1133 return cx_strcast(value->value.string); |
1074 return cx_strcast(value->string); |
| 1134 } |
1075 } |
| 1135 |
1076 |
| 1136 cxmutstr cxJsonAsCxMutStr(const CxJsonValue *value) { |
1077 cxmutstr cxJsonAsCxMutStr(const CxJsonValue *value) { |
| 1137 return value->value.string; |
1078 return value->string; |
| 1138 } |
1079 } |
| 1139 |
1080 |
| 1140 double cxJsonAsDouble(const CxJsonValue *value) { |
1081 double cxJsonAsDouble(const CxJsonValue *value) { |
| 1141 if (value->type == CX_JSON_INTEGER) { |
1082 if (value->type == CX_JSON_INTEGER) { |
| 1142 return (double) value->value.integer; |
1083 return (double) value->integer; |
| 1143 } else { |
1084 } else { |
| 1144 return value->value.number; |
1085 return value->number; |
| 1145 } |
1086 } |
| 1146 } |
1087 } |
| 1147 |
1088 |
| 1148 int64_t cxJsonAsInteger(const CxJsonValue *value) { |
1089 int64_t cxJsonAsInteger(const CxJsonValue *value) { |
| 1149 if (value->type == CX_JSON_INTEGER) { |
1090 if (value->type == CX_JSON_INTEGER) { |
| 1150 return value->value.integer; |
1091 return value->integer; |
| 1151 } else { |
1092 } else { |
| 1152 return (int64_t) value->value.number; |
1093 return (int64_t) value->number; |
| 1153 } |
1094 } |
| 1154 } |
1095 } |
| 1155 |
1096 |
| 1156 CxIterator cxJsonArrIter(const CxJsonValue *value) { |
1097 CxIterator cxJsonArrIter(const CxJsonValue *value) { |
| 1157 return cxIteratorPtr( |
1098 return cxIteratorPtr( |
| 1158 value->value.array.array, |
1099 value->array.data, |
| 1159 value->value.array.array_size, |
1100 value->array.data_size, |
| 1160 true // arrays need to keep order |
1101 true // arrays need to keep order |
| 1161 ); |
1102 ); |
| 1162 } |
1103 } |
| 1163 |
1104 |
| 1164 CxIterator cxJsonObjIter(const CxJsonValue *value) { |
1105 CxMapIterator cxJsonObjIter(const CxJsonValue *value) { |
| 1165 return cxIterator( |
1106 return cxMapIterator(value->object); |
| 1166 value->value.object.values, |
|
| 1167 sizeof(CxJsonObjValue), |
|
| 1168 value->value.object.values_size, |
|
| 1169 true // TODO: objects do not always need to keep order |
|
| 1170 ); |
|
| 1171 } |
1107 } |
| 1172 |
1108 |
| 1173 CxJsonValue *cx_json_obj_get(const CxJsonValue *value, cxstring name) { |
1109 CxJsonValue *cx_json_obj_get(const CxJsonValue *value, cxstring name) { |
| 1174 size_t index = json_find_objvalue(value, name); |
1110 CxJsonValue *v = cxMapGet(value->object, name); |
| 1175 if (index >= value->value.object.values_size) { |
1111 if (v == NULL) { |
| 1176 return &cx_json_value_nothing; |
1112 return &cx_json_value_nothing; |
| 1177 } else { |
1113 } else { |
| 1178 return value->value.object.values[index].value; |
1114 return v; |
| 1179 } |
1115 } |
| 1180 } |
1116 } |
| 1181 |
1117 |
| 1182 CxJsonValue *cx_json_obj_remove(CxJsonValue *value, cxstring name) { |
1118 CxJsonValue *cx_json_obj_remove(CxJsonValue *value, cxstring name) { |
| 1183 size_t index = json_find_objvalue(value, name); |
1119 CxJsonValue *v = NULL; |
| 1184 if (index >= value->value.object.values_size) { |
1120 cxMapRemoveAndGet(value->object, name, &v); |
| 1185 return NULL; |
1121 return v; |
| 1186 } else { |
|
| 1187 CxJsonObjValue kv = value->value.object.values[index]; |
|
| 1188 cx_strfree_a(value->allocator, &kv.name); |
|
| 1189 // TODO: replace with cx_array_remove() / cx_array_remove_fast() |
|
| 1190 value->value.object.values_size--; |
|
| 1191 memmove(value->value.object.values + index, value->value.object.values + index + 1, (value->value.object.values_size - index) * sizeof(CxJsonObjValue)); |
|
| 1192 return kv.value; |
|
| 1193 } |
|
| 1194 } |
1122 } |
| 1195 |
1123 |
| 1196 CxJsonWriter cxJsonWriterCompact(void) { |
1124 CxJsonWriter cxJsonWriterCompact(void) { |
| 1197 return (CxJsonWriter) { |
1125 return (CxJsonWriter) { |
| 1198 false, |
1126 false, |
| 1199 true, |
|
| 1200 6, |
1127 6, |
| 1201 false, |
1128 false, |
| 1202 4, |
1129 4, |
| 1203 false |
1130 false |
| 1204 }; |
1131 }; |
| 1205 } |
1132 } |
| 1206 |
1133 |
| 1207 CxJsonWriter cxJsonWriterPretty(bool use_spaces) { |
1134 CxJsonWriter cxJsonWriterPretty(bool use_spaces) { |
| 1208 return (CxJsonWriter) { |
1135 return (CxJsonWriter) { |
| 1209 true, |
|
| 1210 true, |
1136 true, |
| 1211 6, |
1137 6, |
| 1212 use_spaces, |
1138 use_spaces, |
| 1213 4, |
1139 4, |
| 1214 false |
1140 false |
| 1270 } else { |
1196 } else { |
| 1271 actual += wfunc(begin_obj, 1, 1, target); |
1197 actual += wfunc(begin_obj, 1, 1, target); |
| 1272 expected++; |
1198 expected++; |
| 1273 } |
1199 } |
| 1274 depth++; |
1200 depth++; |
| 1275 size_t elem_count = value->value.object.values_size; |
1201 CxMapIterator member_iter = cxJsonObjIter(value); |
| 1276 for (size_t look_idx = 0; look_idx < elem_count; look_idx++) { |
1202 cx_foreach(const CxMapEntry *, member, member_iter) { |
| 1277 // get the member either via index array or directly |
|
| 1278 size_t elem_idx = settings->sort_members |
|
| 1279 ? look_idx |
|
| 1280 : value->value.object.indices[look_idx]; |
|
| 1281 CxJsonObjValue *member = &value->value.object.values[elem_idx]; |
|
| 1282 |
|
| 1283 // possible indentation |
1203 // possible indentation |
| 1284 if (settings->pretty) { |
1204 if (settings->pretty) { |
| 1285 if (cx_json_writer_indent(target, wfunc, settings, depth)) { |
1205 if (cx_json_writer_indent(target, wfunc, settings, depth)) { |
| 1286 return 1; // LCOV_EXCL_LINE |
1206 return 1; // LCOV_EXCL_LINE |
| 1287 } |
1207 } |
| 1288 } |
1208 } |
| 1289 |
1209 |
| 1290 // the name |
1210 // the name |
| 1291 actual += wfunc("\"", 1, 1, target); |
1211 actual += wfunc("\"", 1, 1, target); |
| 1292 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); |
| 1293 actual += wfunc(name.ptr, 1, name.length, target); |
1214 actual += wfunc(name.ptr, 1, name.length, target); |
| 1294 if (name.ptr != member->name.ptr) { |
|
| 1295 cx_strfree(&name); |
|
| 1296 } |
|
| 1297 actual += wfunc("\"", 1, 1, target); |
1215 actual += wfunc("\"", 1, 1, target); |
| 1298 const char *obj_name_sep = ": "; |
1216 const char *obj_name_sep = ": "; |
| 1299 if (settings->pretty) { |
1217 if (settings->pretty) { |
| 1300 actual += wfunc(obj_name_sep, 1, 2, target); |
1218 actual += wfunc(obj_name_sep, 1, 2, target); |
| 1301 expected += 4 + member->name.length; |
1219 expected += 4 + name.length; |
| 1302 } else { |
1220 } else { |
| 1303 actual += wfunc(obj_name_sep, 1, 1, target); |
1221 actual += wfunc(obj_name_sep, 1, 1, target); |
| 1304 expected += 3 + member->name.length; |
1222 expected += 3 + name.length; |
| |
1223 } |
| |
1224 if (name.ptr != key.ptr) { |
| |
1225 cx_strfree(&name); |
| 1305 } |
1226 } |
| 1306 |
1227 |
| 1307 // the value |
1228 // the value |
| 1308 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; |
| 1309 |
1230 |
| 1310 // end of object-value |
1231 // end of object-value |
| 1311 if (look_idx < elem_count - 1) { |
1232 if (member_iter.index < member_iter.elem_count - 1) { |
| 1312 const char *obj_value_sep = ",\n"; |
1233 const char *obj_value_sep = ",\n"; |
| 1313 if (settings->pretty) { |
1234 if (settings->pretty) { |
| 1314 actual += wfunc(obj_value_sep, 1, 2, target); |
1235 actual += wfunc(obj_value_sep, 1, 2, target); |
| 1315 expected += 2; |
1236 expected += 2; |
| 1316 } else { |
1237 } else { |
| 1359 expected++; |
1280 expected++; |
| 1360 break; |
1281 break; |
| 1361 } |
1282 } |
| 1362 case CX_JSON_STRING: { |
1283 case CX_JSON_STRING: { |
| 1363 actual += wfunc("\"", 1, 1, target); |
1284 actual += wfunc("\"", 1, 1, target); |
| 1364 cxmutstr str = escape_string(value->value.string, settings->escape_slash); |
1285 cxmutstr str = escape_string(cx_strcast(value->string), |
| |
1286 settings->escape_slash); |
| 1365 actual += wfunc(str.ptr, 1, str.length, target); |
1287 actual += wfunc(str.ptr, 1, str.length, target); |
| 1366 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) { |
| 1367 cx_strfree(&str); |
1291 cx_strfree(&str); |
| 1368 } |
1292 } |
| 1369 actual += wfunc("\"", 1, 1, target); |
|
| 1370 expected += 2 + value->value.string.length; |
|
| 1371 break; |
1293 break; |
| 1372 } |
1294 } |
| 1373 case CX_JSON_NUMBER: { |
1295 case CX_JSON_NUMBER: { |
| 1374 int precision = settings->frac_max_digits; |
1296 int precision = settings->frac_max_digits; |
| 1375 // because of the way how %g is defined, we need to |
1297 // because of the way how %g is defined, we need to |
| 1376 // double the precision and truncate ourselves |
1298 // double the precision and truncate ourselves |
| 1377 precision = 1 + (precision > 15 ? 30 : 2 * precision); |
1299 precision = 1 + (precision > 15 ? 30 : 2 * precision); |
| 1378 snprintf(numbuf, 40, "%.*g", precision, value->value.number); |
1300 snprintf(numbuf, 40, "%.*g", precision, value->number); |
| 1379 char *dot, *exp; |
1301 char *dot, *exp; |
| 1380 unsigned char max_digits; |
1302 unsigned char max_digits; |
| 1381 // 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 , |
| 1382 dot = strchr(numbuf, '.'); |
1304 dot = strchr(numbuf, '.'); |
| 1383 if (dot == NULL) { |
1305 if (dot == NULL) { |
| 1437 expected += len; |
1359 expected += len; |
| 1438 } |
1360 } |
| 1439 break; |
1361 break; |
| 1440 } |
1362 } |
| 1441 case CX_JSON_INTEGER: { |
1363 case CX_JSON_INTEGER: { |
| 1442 snprintf(numbuf, 32, "%" PRIi64, value->value.integer); |
1364 snprintf(numbuf, 32, "%" PRIi64, value->integer); |
| 1443 size_t len = strlen(numbuf); |
1365 size_t len = strlen(numbuf); |
| 1444 actual += wfunc(numbuf, 1, len, target); |
1366 actual += wfunc(numbuf, 1, len, target); |
| 1445 expected += len; |
1367 expected += len; |
| 1446 break; |
1368 break; |
| 1447 } |
1369 } |
| 1448 case CX_JSON_LITERAL: { |
1370 case CX_JSON_LITERAL: { |
| 1449 if (value->value.literal == CX_JSON_TRUE) { |
1371 if (value->literal == CX_JSON_TRUE) { |
| 1450 actual += wfunc("true", 1, 4, target); |
1372 actual += wfunc("true", 1, 4, target); |
| 1451 expected += 4; |
1373 expected += 4; |
| 1452 } else if (value->value.literal == CX_JSON_FALSE) { |
1374 } else if (value->literal == CX_JSON_FALSE) { |
| 1453 actual += wfunc("false", 1, 5, target); |
1375 actual += wfunc("false", 1, 5, target); |
| 1454 expected += 5; |
1376 expected += 5; |
| 1455 } else { |
1377 } else { |
| 1456 actual += wfunc("null", 1, 4, target); |
1378 actual += wfunc("null", 1, 4, target); |
| 1457 expected += 4; |
1379 expected += 4; |