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( |