| 65 } else { |
65 } else { |
| 66 return &obj->value.object.values[index]; |
66 return &obj->value.object.values[index]; |
| 67 } |
67 } |
| 68 } |
68 } |
| 69 |
69 |
| 70 static int json_add_objvalue(CxJsonValue *obj, CxJsonObjValue member) { |
70 static int json_add_objvalue(CxJsonValue *objv, CxJsonObjValue member) { |
| 71 assert(obj->type == CX_JSON_OBJECT); |
71 assert(obj->type == CX_JSON_OBJECT); |
| 72 CxArrayReallocator value_realloc = cx_array_reallocator(obj->allocator, NULL); |
72 const CxAllocator * const al = objv->allocator; |
| 73 return cx_array_simple_add_sorted_a( |
73 CxJsonObject *obj = &(objv->value.object); |
| 74 &value_realloc, obj->value.object.values, |
74 |
| 75 member, json_cmp_objvalue |
75 // determine the index where we need to insert the new member |
| |
76 size_t index = cx_array_binary_search_sup( |
| |
77 obj->values, |
| |
78 obj->values_size, |
| |
79 sizeof(CxJsonObjValue), |
| |
80 &member, json_cmp_objvalue |
| 76 ); |
81 ); |
| |
82 |
| |
83 // is the name already present? |
| |
84 if (index < obj->values_size && 0 == json_cmp_objvalue(&member, &obj->values[index])) { |
| |
85 // free the original value |
| |
86 cx_strfree_a(al, &obj->values[index].name); |
| |
87 cxJsonValueFree(obj->values[index].value); |
| |
88 // replace the item |
| |
89 obj->values[index] = member; |
| |
90 |
| |
91 // nothing more to do |
| |
92 return 0; |
| |
93 } |
| |
94 |
| |
95 // determine the old capacity and reserve for one more element |
| |
96 CxArrayReallocator arealloc = cx_array_reallocator(al, NULL); |
| |
97 size_t oldcap = obj->values_capacity; |
| |
98 if (cx_array_simple_reserve_a(&arealloc, obj->values, 1)) return 1; |
| |
99 |
| |
100 // check the new capacity, if we need to realloc the index array |
| |
101 size_t newcap = obj->values_capacity; |
| |
102 if (newcap > oldcap) { |
| |
103 if (cxReallocateArray(al, &obj->indices, newcap, sizeof(size_t))) { |
| |
104 return 1; |
| |
105 } |
| |
106 } |
| |
107 |
| |
108 // check if append or insert |
| |
109 if (index < obj->values_size) { |
| |
110 // move the other elements |
| |
111 memmove( |
| |
112 &obj->values[index+1], |
| |
113 &obj->values[index], |
| |
114 (obj->values_size - index) * sizeof(CxJsonObjValue) |
| |
115 ); |
| |
116 // increase indices for the moved elements |
| |
117 for (size_t i = 0; i < obj->values_size ; i++) { |
| |
118 if (obj->indices[i] >= index) { |
| |
119 obj->indices[i]++; |
| |
120 } |
| |
121 } |
| |
122 } |
| |
123 |
| |
124 // insert the element and set the index |
| |
125 obj->values[index] = member; |
| |
126 obj->indices[obj->values_size] = index; |
| |
127 obj->values_size++; |
| |
128 |
| |
129 return 0; |
| 77 } |
130 } |
| 78 |
131 |
| 79 static void token_destroy(CxJsonToken *token) { |
132 static void token_destroy(CxJsonToken *token) { |
| 80 if (token->allocated) { |
133 if (token->allocated) { |
| 81 cx_strfree(&token->content); |
134 cx_strfree(&token->content); |
| 321 |
374 |
| 322 return result; |
375 return result; |
| 323 } |
376 } |
| 324 |
377 |
| 325 static CxJsonValue* create_json_value(CxJson *json, CxJsonValueType type) { |
378 static CxJsonValue* create_json_value(CxJson *json, CxJsonValueType type) { |
| 326 CxJsonValue *v = cxMalloc(json->allocator, sizeof(CxJsonValue)); |
379 CxJsonValue *v = cxCalloc(json->allocator, 1, sizeof(CxJsonValue)); |
| 327 if (v == NULL) return NULL; // LCOV_EXCL_LINE |
380 if (v == NULL) return NULL; // LCOV_EXCL_LINE |
| 328 |
381 |
| 329 // initialize the value |
382 // initialize the value |
| |
383 v->type = type; |
| |
384 v->allocator = json->allocator; |
| 330 if (type == CX_JSON_ARRAY) { |
385 if (type == CX_JSON_ARRAY) { |
| 331 cx_array_initialize_a(json->allocator, v->value.array.array, 16); |
386 cx_array_initialize_a(json->allocator, v->value.array.array, 16); |
| 332 if (v->value.array.array == NULL) goto create_json_value_exit_error; // LCOV_EXCL_LINE |
387 if (v->value.array.array == NULL) goto create_json_value_exit_error; // LCOV_EXCL_LINE |
| 333 } else if (type == CX_JSON_OBJECT) { |
388 } else if (type == CX_JSON_OBJECT) { |
| 334 cx_array_initialize_a(json->allocator, v->value.object.values, 16); |
389 cx_array_initialize_a(json->allocator, v->value.object.values, 16); |
| 335 if (v->value.object.values == NULL) goto create_json_value_exit_error; // LCOV_EXCL_LINE |
390 v->value.object.indices = cxCalloc(json->allocator, 16, sizeof(size_t)); |
| 336 } else { |
391 if (v->value.object.values == NULL || |
| 337 memset(v, 0, sizeof(CxJsonValue)); |
392 v->value.object.indices == NULL) |
| 338 } |
393 goto create_json_value_exit_error; // LCOV_EXCL_LINE |
| 339 v->type = type; |
394 } |
| 340 v->allocator = json->allocator; |
|
| 341 |
395 |
| 342 // add the new value to a possible parent |
396 // add the new value to a possible parent |
| 343 if (json->vbuf_size > 0) { |
397 if (json->vbuf_size > 0) { |
| 344 CxJsonValue *parent = json->vbuf[json->vbuf_size - 1]; |
398 CxJsonValue *parent = json->vbuf[json->vbuf_size - 1]; |
| 345 assert(parent != NULL); |
399 assert(parent != NULL); |
| 655 for (size_t i = 0; i < obj.values_size; i++) { |
709 for (size_t i = 0; i < obj.values_size; i++) { |
| 656 cxJsonValueFree(obj.values[i].value); |
710 cxJsonValueFree(obj.values[i].value); |
| 657 cx_strfree_a(value->allocator, &obj.values[i].name); |
711 cx_strfree_a(value->allocator, &obj.values[i].name); |
| 658 } |
712 } |
| 659 cxFree(value->allocator, obj.values); |
713 cxFree(value->allocator, obj.values); |
| |
714 cxFree(value->allocator, obj.indices); |
| 660 break; |
715 break; |
| 661 } |
716 } |
| 662 case CX_JSON_ARRAY: { |
717 case CX_JSON_ARRAY: { |
| 663 CxJsonArray array = value->value.array; |
718 CxJsonArray array = value->value.array; |
| 664 for (size_t i = 0; i < array.array_size; i++) { |
719 for (size_t i = 0; i < array.array_size; i++) { |
| 682 CxJsonValue* v = cxMalloc(allocator, sizeof(CxJsonValue)); |
737 CxJsonValue* v = cxMalloc(allocator, sizeof(CxJsonValue)); |
| 683 if (v == NULL) return NULL; |
738 if (v == NULL) return NULL; |
| 684 v->allocator = allocator; |
739 v->allocator = allocator; |
| 685 v->type = CX_JSON_OBJECT; |
740 v->type = CX_JSON_OBJECT; |
| 686 cx_array_initialize_a(allocator, v->value.object.values, 16); |
741 cx_array_initialize_a(allocator, v->value.object.values, 16); |
| 687 if (v->value.object.values == NULL) { cxFree(allocator, v); return NULL; } |
742 if (v->value.object.values == NULL) { // LCOV_EXCL_START |
| |
743 cxFree(allocator, v); |
| |
744 return NULL; |
| |
745 // LCOV_EXCL_STOP |
| |
746 } |
| |
747 v->value.object.indices = cxCalloc(allocator, 16, sizeof(size_t)); |
| |
748 if (v->value.object.indices == NULL) { // LCOV_EXCL_START |
| |
749 cxFree(allocator, v->value.object.values); |
| |
750 cxFree(allocator, v); |
| |
751 return NULL; |
| |
752 // LCOV_EXCL_STOP |
| |
753 } |
| 688 return v; |
754 return v; |
| 689 } |
755 } |
| 690 |
756 |
| 691 CxJsonValue* cxJsonCreateArr(const CxAllocator* allocator) { |
757 CxJsonValue* cxJsonCreateArr(const CxAllocator* allocator) { |
| 692 CxJsonValue* v = cxMalloc(allocator, sizeof(CxJsonValue)); |
758 CxJsonValue* v = cxMalloc(allocator, sizeof(CxJsonValue)); |
| 820 val, count |
886 val, count |
| 821 ); |
887 ); |
| 822 } |
888 } |
| 823 |
889 |
| 824 int cxJsonObjPut(CxJsonValue* obj, cxstring name, CxJsonValue* child) { |
890 int cxJsonObjPut(CxJsonValue* obj, cxstring name, CxJsonValue* child) { |
| 825 // TODO: optimize - issue #462 |
|
| 826 for (size_t i = 0; i < obj->value.object.values_size; i++) { |
|
| 827 if (0 == cx_strcmp(name, cx_strcast(obj->value.object.values[i].name))) { |
|
| 828 // free the original value |
|
| 829 cxJsonValueFree(obj->value.object.values[i].value); |
|
| 830 obj->value.object.values[i].value = child; |
|
| 831 return 0; |
|
| 832 } |
|
| 833 } |
|
| 834 |
|
| 835 cxmutstr k = cx_strdup_a(obj->allocator, name); |
891 cxmutstr k = cx_strdup_a(obj->allocator, name); |
| 836 if (k.ptr == NULL) return -1; |
892 if (k.ptr == NULL) return -1; |
| 837 CxJsonObjValue kv = {k, child}; |
893 CxJsonObjValue kv = {k, child}; |
| 838 return json_add_objvalue(obj, kv); |
894 if (json_add_objvalue(obj, kv)) { |
| |
895 cx_strfree_a(obj->allocator, &k); |
| |
896 return 1; |
| |
897 } else { |
| |
898 return 0; |
| |
899 } |
| 839 } |
900 } |
| 840 |
901 |
| 841 CxJsonValue* cxJsonObjPutObj(CxJsonValue* obj, cxstring name) { |
902 CxJsonValue* cxJsonObjPutObj(CxJsonValue* obj, cxstring name) { |
| 842 CxJsonValue* v = cxJsonCreateObj(obj->allocator); |
903 CxJsonValue* v = cxJsonCreateObj(obj->allocator); |
| 843 if (v == NULL) return NULL; |
904 if (v == NULL) return NULL; |
| 995 } else { |
1056 } else { |
| 996 actual += wfunc(begin_obj, 1, 1, target); |
1057 actual += wfunc(begin_obj, 1, 1, target); |
| 997 expected++; |
1058 expected++; |
| 998 } |
1059 } |
| 999 depth++; |
1060 depth++; |
| 1000 CxIterator iter = cxJsonObjIter(value); |
1061 size_t elem_count = value->value.object.values_size; |
| 1001 // TODO: unsorted output - realize after implementing index array |
1062 for (size_t look_idx = 0; look_idx < elem_count; look_idx++) { |
| 1002 cx_foreach(CxJsonObjValue*, member, iter) { |
1063 // get the member either via index array or directly |
| |
1064 size_t elem_idx = settings->sort_members |
| |
1065 ? look_idx |
| |
1066 : value->value.object.indices[look_idx]; |
| |
1067 CxJsonObjValue *member = &value->value.object.values[elem_idx]; |
| |
1068 if (settings->sort_members) { |
| |
1069 depth++;depth--; |
| |
1070 } |
| |
1071 |
| 1003 // possible indentation |
1072 // possible indentation |
| 1004 if (settings->pretty) { |
1073 if (settings->pretty) { |
| 1005 if (cx_json_writer_indent(target, wfunc, settings, depth)) return 1; |
1074 if (cx_json_writer_indent(target, wfunc, settings, depth)) { |
| |
1075 return 1; // LCOV_EXCL_LINE |
| |
1076 } |
| 1006 } |
1077 } |
| 1007 |
1078 |
| 1008 // the name |
1079 // the name |
| 1009 actual += wfunc("\"", 1, 1, target); |
1080 actual += wfunc("\"", 1, 1, target); |
| 1010 // TODO: escape the string |
1081 // TODO: escape the string |
| 1022 |
1093 |
| 1023 // the value |
1094 // the value |
| 1024 if (cx_json_write_rec(target, member->value, wfunc, settings, depth)) return 1; |
1095 if (cx_json_write_rec(target, member->value, wfunc, settings, depth)) return 1; |
| 1025 |
1096 |
| 1026 // end of object-value |
1097 // end of object-value |
| 1027 if (iter.index < iter.elem_count - 1) { |
1098 if (look_idx < elem_count - 1) { |
| 1028 const char *obj_value_sep = ",\n"; |
1099 const char *obj_value_sep = ",\n"; |
| 1029 if (settings->pretty) { |
1100 if (settings->pretty) { |
| 1030 actual += wfunc(obj_value_sep, 1, 2, target); |
1101 actual += wfunc(obj_value_sep, 1, 2, target); |
| 1031 expected += 2; |
1102 expected += 2; |
| 1032 } else { |
1103 } else { |
| 1047 actual += wfunc("}", 1, 1, target); |
1118 actual += wfunc("}", 1, 1, target); |
| 1048 expected++; |
1119 expected++; |
| 1049 break; |
1120 break; |
| 1050 } |
1121 } |
| 1051 case CX_JSON_ARRAY: { |
1122 case CX_JSON_ARRAY: { |
| 1052 // TODO: implement array wrapping |
|
| 1053 actual += wfunc("[", 1, 1, target); |
1123 actual += wfunc("[", 1, 1, target); |
| 1054 expected++; |
1124 expected++; |
| 1055 CxIterator iter = cxJsonArrIter(value); |
1125 CxIterator iter = cxJsonArrIter(value); |
| 1056 cx_foreach(CxJsonValue*, element, iter) { |
1126 cx_foreach(CxJsonValue*, element, iter) { |
| 1057 if (cx_json_write_rec( |
1127 if (cx_json_write_rec( |