| 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) { |
| 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 |
| 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); |
| 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 } |
| 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 |
| 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 { |
| 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) { |