src/json.c

changeset 1547
12da0654e4a9
parent 1543
7b66371d6da3
child 1548
12315ee158ad
equal deleted inserted replaced
1546:c8dd35f3ea53 1547:12da0654e4a9
25 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 25 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
26 * POSSIBILITY OF SUCH DAMAGE. 26 * POSSIBILITY OF SUCH DAMAGE.
27 */ 27 */
28 28
29 #include "cx/json.h" 29 #include "cx/json.h"
30 #include "cx/kv_list.h"
30 31
31 #include <string.h> 32 #include <string.h>
32 #include <assert.h> 33 #include <assert.h>
33 #include <stdio.h> 34 #include <stdio.h>
34 #include <inttypes.h> 35 #include <inttypes.h>
38 * RFC 8259 39 * RFC 8259
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 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->object.values,
56 obj->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->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 44
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);
128 } 48 }
471 result.ptr[result.length] = 0; 391 result.ptr[result.length] = 0;
472 392
473 return result; 393 return result;
474 } 394 }
475 395
476 static cxmutstr escape_string(cxmutstr str, bool escape_slash) { 396 static cxmutstr escape_string(cxstring str, bool escape_slash) {
477 // note: this function produces the string without enclosing quotes 397 // note: this function produces the string without enclosing quotes
478 // the reason is that we don't want to allocate memory just for that 398 // the reason is that we don't want to allocate memory just for that
479 CxBuffer buf = {0}; 399 CxBuffer buf = {0};
480 400
481 bool all_printable = true; 401 bool all_printable = true;
517 } 437 }
518 } else if (!all_printable) { 438 } else if (!all_printable) {
519 cxBufferPut(&buf, c); 439 cxBufferPut(&buf, c);
520 } 440 }
521 } 441 }
522 if (!all_printable) { 442 cxmutstr ret;
523 str = cx_mutstrn(buf.space, buf.size); 443 if (all_printable) {
444 // don't copy the string when we don't need to escape anything
445 ret = cx_mutstrn((char*)str.ptr, str.length);
446 } else {
447 ret = cx_mutstrn(buf.space, buf.size);
524 } 448 }
525 cxBufferDestroy(&buf); 449 cxBufferDestroy(&buf);
526 return str; 450 return ret;
451 }
452
453 static CxJsonObject json_create_object_map(const CxAllocator *allocator) {
454 // TODO: we might want to add a comparator that is sorting the elements by their key
455 CxMap *map = cxKvListCreateAsMap(allocator, NULL, CX_STORE_POINTERS);
456 if (map == NULL) return NULL; // LCOV_EXCL_LINE
457 cxDefineDestructor(map, cxJsonValueFree);
458 return map;
459 }
460
461 static void json_free_object_map(CxJsonObject obj) {
462 cxMapFree(obj);
527 } 463 }
528 464
529 static CxJsonValue* json_create_value(CxJson *json, CxJsonValueType type) { 465 static CxJsonValue* json_create_value(CxJson *json, CxJsonValueType type) {
530 CxJsonValue *v = cxCalloc(json->allocator, 1, sizeof(CxJsonValue)); 466 CxJsonValue *v = cxCalloc(json->allocator, 1, sizeof(CxJsonValue));
531 if (v == NULL) return NULL; // LCOV_EXCL_LINE 467 if (v == NULL) return NULL; // LCOV_EXCL_LINE
535 v->allocator = json->allocator; 471 v->allocator = json->allocator;
536 if (type == CX_JSON_ARRAY) { 472 if (type == CX_JSON_ARRAY) {
537 cx_array_initialize_a(json->allocator, v->array.array, 16); 473 cx_array_initialize_a(json->allocator, v->array.array, 16);
538 if (v->array.array == NULL) goto create_json_value_exit_error; // LCOV_EXCL_LINE 474 if (v->array.array == NULL) goto create_json_value_exit_error; // LCOV_EXCL_LINE
539 } else if (type == CX_JSON_OBJECT) { 475 } else if (type == CX_JSON_OBJECT) {
540 cx_array_initialize_a(json->allocator, v->object.values, 16); 476 v->object = json_create_object_map(json->allocator);
541 v->object.indices = cxCalloc(json->allocator, 16, sizeof(size_t)); 477 if (v->object == NULL) goto create_json_value_exit_error; // LCOV_EXCL_LINE
542 if (v->object.values == NULL ||
543 v->object.indices == NULL)
544 goto create_json_value_exit_error; // LCOV_EXCL_LINE
545 } 478 }
546 479
547 // add the new value to a possible parent 480 // add the new value to a possible parent
548 if (json->vbuf_size > 0) { 481 if (json->vbuf_size > 0) {
549 CxJsonValue *parent = json->vbuf[json->vbuf_size - 1]; 482 CxJsonValue *parent = json->vbuf[json->vbuf_size - 1];
553 if (cx_array_simple_add_a(&value_realloc, parent->array.array, v)) { 486 if (cx_array_simple_add_a(&value_realloc, parent->array.array, v)) {
554 goto create_json_value_exit_error; // LCOV_EXCL_LINE 487 goto create_json_value_exit_error; // LCOV_EXCL_LINE
555 } 488 }
556 } else if (parent->type == CX_JSON_OBJECT) { 489 } else if (parent->type == CX_JSON_OBJECT) {
557 // the member was already created after parsing the name 490 // the member was already created after parsing the name
558 assert(json->uncompleted_member.name.ptr != NULL); 491 // store the pointer of the uncompleted value in the map
559 json->uncompleted_member.value = v; 492 assert(json->uncompleted_member_name.ptr != NULL);
560 if (json_add_objvalue(parent, json->uncompleted_member)) { 493 if (cxMapPut(parent->object, json->uncompleted_member_name, v)) {
561 goto create_json_value_exit_error; // LCOV_EXCL_LINE 494 goto create_json_value_exit_error; // LCOV_EXCL_LINE
562 } 495 }
563 json->uncompleted_member.name = (cxmutstr) {NULL, 0}; 496 cx_strfree_a(json->allocator, &json->uncompleted_member_name);
564 } else { 497 } else {
565 assert(false); // LCOV_EXCL_LINE 498 assert(false); // LCOV_EXCL_LINE
566 } 499 }
567 } 500 }
568 501
622 if (json->vbuf != json->vbuf_internal) { 555 if (json->vbuf != json->vbuf_internal) {
623 cxFreeDefault(json->vbuf); 556 cxFreeDefault(json->vbuf);
624 } 557 }
625 cxJsonValueFree(json->parsed); 558 cxJsonValueFree(json->parsed);
626 json->parsed = NULL; 559 json->parsed = NULL;
627 if (json->uncompleted_member.name.ptr != NULL) { 560 cx_strfree_a(json->allocator, &json->uncompleted_member_name);
628 cx_strfree_a(json->allocator, &json->uncompleted_member.name);
629 json->uncompleted_member = (CxJsonObjValue){{NULL, 0}, NULL};
630 }
631 } 561 }
632 562
633 void cxJsonReset(CxJson *json) { 563 void cxJsonReset(CxJson *json) {
634 const CxAllocator *allocator = json->allocator; 564 const CxAllocator *allocator = json->allocator;
635 cxJsonDestroy(json); 565 cxJsonDestroy(json);
784 // add new entry 714 // add new entry
785 cxmutstr name = unescape_string(json->allocator, token.content); 715 cxmutstr name = unescape_string(json->allocator, token.content);
786 if (name.ptr == NULL) { 716 if (name.ptr == NULL) {
787 return_rec(CX_JSON_VALUE_ALLOC_FAILED); // LCOV_EXCL_LINE 717 return_rec(CX_JSON_VALUE_ALLOC_FAILED); // LCOV_EXCL_LINE
788 } 718 }
789 assert(json->uncompleted_member.name.ptr == NULL); 719 assert(json->uncompleted_member_name.ptr == NULL);
790 json->uncompleted_member.name = name; 720 json->uncompleted_member_name = name;
791 assert(json->vbuf_size > 0); 721 assert(json->vbuf_size > 0);
792 722
793 // next state 723 // next state
794 json_add_state(json, JP_STATE_OBJ_COLON); 724 json_add_state(json, JP_STATE_OBJ_COLON);
795 return_rec(CX_JSON_NO_ERROR); 725 return_rec(CX_JSON_NO_ERROR);
862 792
863 void cxJsonValueFree(CxJsonValue *value) { 793 void cxJsonValueFree(CxJsonValue *value) {
864 if (value == NULL || value->type == CX_JSON_NOTHING) return; 794 if (value == NULL || value->type == CX_JSON_NOTHING) return;
865 switch (value->type) { 795 switch (value->type) {
866 case CX_JSON_OBJECT: { 796 case CX_JSON_OBJECT: {
867 CxJsonObject obj = value->object; 797 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; 798 break;
875 } 799 }
876 case CX_JSON_ARRAY: { 800 case CX_JSON_ARRAY: {
877 CxJsonArray array = value->array; 801 CxJsonArray array = value->array;
878 for (size_t i = 0; i < array.array_size; i++) { 802 for (size_t i = 0; i < array.array_size; i++) {
896 if (allocator == NULL) allocator = cxDefaultAllocator; 820 if (allocator == NULL) allocator = cxDefaultAllocator;
897 CxJsonValue* v = cxMalloc(allocator, sizeof(CxJsonValue)); 821 CxJsonValue* v = cxMalloc(allocator, sizeof(CxJsonValue));
898 if (v == NULL) return NULL; 822 if (v == NULL) return NULL;
899 v->allocator = allocator; 823 v->allocator = allocator;
900 v->type = CX_JSON_OBJECT; 824 v->type = CX_JSON_OBJECT;
901 cx_array_initialize_a(allocator, v->object.values, 16); 825 v->object = json_create_object_map(allocator);
902 if (v->object.values == NULL) { // LCOV_EXCL_START 826 if (v->object == NULL) { // LCOV_EXCL_START
903 cxFree(allocator, v);
904 return NULL;
905 // LCOV_EXCL_STOP
906 }
907 v->object.indices = cxCalloc(allocator, 16, sizeof(size_t));
908 if (v->object.indices == NULL) { // LCOV_EXCL_START
909 cxFree(allocator, v->object.values);
910 cxFree(allocator, v); 827 cxFree(allocator, v);
911 return NULL; 828 return NULL;
912 // LCOV_EXCL_STOP 829 // LCOV_EXCL_STOP
913 } 830 }
914 return v; 831 return v;
1047 val, count 964 val, count
1048 ); 965 );
1049 } 966 }
1050 967
1051 int cx_json_obj_put(CxJsonValue* obj, cxstring name, CxJsonValue* child) { 968 int cx_json_obj_put(CxJsonValue* obj, cxstring name, CxJsonValue* child) {
1052 cxmutstr k = cx_strdup_a(obj->allocator, name); 969 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 } 970 }
1064 971
1065 CxJsonValue* cx_json_obj_put_obj(CxJsonValue* obj, cxstring name) { 972 CxJsonValue* cx_json_obj_put_obj(CxJsonValue* obj, cxstring name) {
1066 CxJsonValue* v = cxJsonCreateObj(obj->allocator); 973 CxJsonValue* v = cxJsonCreateObj(obj->allocator);
1067 if (v == NULL) return NULL; 974 if (v == NULL) return NULL;
1159 value->array.array_size, 1066 value->array.array_size,
1160 true // arrays need to keep order 1067 true // arrays need to keep order
1161 ); 1068 );
1162 } 1069 }
1163 1070
1164 CxIterator cxJsonObjIter(const CxJsonValue *value) { 1071 CxMapIterator cxJsonObjIter(const CxJsonValue *value) {
1165 return cxIterator( 1072 return cxMapIterator(value->object);
1166 value->object.values,
1167 sizeof(CxJsonObjValue),
1168 value->object.values_size,
1169 true // TODO: objects do not always need to keep order
1170 );
1171 } 1073 }
1172 1074
1173 CxJsonValue *cx_json_obj_get(const CxJsonValue *value, cxstring name) { 1075 CxJsonValue *cx_json_obj_get(const CxJsonValue *value, cxstring name) {
1174 size_t index = json_find_objvalue(value, name); 1076 CxJsonValue *v = cxMapGet(value->object, name);
1175 if (index >= value->object.values_size) { 1077 if (v == NULL) {
1176 return &cx_json_value_nothing; 1078 return &cx_json_value_nothing;
1177 } else { 1079 } else {
1178 return value->object.values[index].value; 1080 return v;
1179 } 1081 }
1180 } 1082 }
1181 1083
1182 CxJsonValue *cx_json_obj_remove(CxJsonValue *value, cxstring name) { 1084 CxJsonValue *cx_json_obj_remove(CxJsonValue *value, cxstring name) {
1183 size_t index = json_find_objvalue(value, name); 1085 CxJsonValue *v = NULL;
1184 if (index >= value->object.values_size) { 1086 cxMapRemoveAndGet(value->object, name, &v);
1185 return NULL; 1087 return v;
1186 } else {
1187 CxJsonObjValue kv = 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->object.values_size--;
1191 memmove(value->object.values + index, value->object.values + index + 1, (value->object.values_size - index) * sizeof(CxJsonObjValue));
1192 return kv.value;
1193 }
1194 } 1088 }
1195 1089
1196 CxJsonWriter cxJsonWriterCompact(void) { 1090 CxJsonWriter cxJsonWriterCompact(void) {
1197 return (CxJsonWriter) { 1091 return (CxJsonWriter) {
1198 false, 1092 false,
1199 true,
1200 6, 1093 6,
1201 false, 1094 false,
1202 4, 1095 4,
1203 false 1096 false
1204 }; 1097 };
1205 } 1098 }
1206 1099
1207 CxJsonWriter cxJsonWriterPretty(bool use_spaces) { 1100 CxJsonWriter cxJsonWriterPretty(bool use_spaces) {
1208 return (CxJsonWriter) { 1101 return (CxJsonWriter) {
1209 true,
1210 true, 1102 true,
1211 6, 1103 6,
1212 use_spaces, 1104 use_spaces,
1213 4, 1105 4,
1214 false 1106 false
1270 } else { 1162 } else {
1271 actual += wfunc(begin_obj, 1, 1, target); 1163 actual += wfunc(begin_obj, 1, 1, target);
1272 expected++; 1164 expected++;
1273 } 1165 }
1274 depth++; 1166 depth++;
1275 size_t elem_count = value->object.values_size; 1167 CxMapIterator member_iter = cxJsonObjIter(value);
1276 for (size_t look_idx = 0; look_idx < elem_count; look_idx++) { 1168 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->object.indices[look_idx];
1281 CxJsonObjValue *member = &value->object.values[elem_idx];
1282
1283 // possible indentation 1169 // possible indentation
1284 if (settings->pretty) { 1170 if (settings->pretty) {
1285 if (cx_json_writer_indent(target, wfunc, settings, depth)) { 1171 if (cx_json_writer_indent(target, wfunc, settings, depth)) {
1286 return 1; // LCOV_EXCL_LINE 1172 return 1; // LCOV_EXCL_LINE
1287 } 1173 }
1288 } 1174 }
1289 1175
1290 // the name 1176 // the name
1291 actual += wfunc("\"", 1, 1, target); 1177 actual += wfunc("\"", 1, 1, target);
1292 cxmutstr name = escape_string(member->name, settings->escape_slash); 1178 cxstring key = cx_strn(member->key->data, member->key->len);
1179 cxmutstr name = escape_string(key, settings->escape_slash);
1293 actual += wfunc(name.ptr, 1, name.length, target); 1180 actual += wfunc(name.ptr, 1, name.length, target);
1294 if (name.ptr != member->name.ptr) { 1181 if (name.ptr != key.ptr) {
1295 cx_strfree(&name); 1182 cx_strfree(&name);
1296 } 1183 }
1297 actual += wfunc("\"", 1, 1, target); 1184 actual += wfunc("\"", 1, 1, target);
1298 const char *obj_name_sep = ": "; 1185 const char *obj_name_sep = ": ";
1299 if (settings->pretty) { 1186 if (settings->pretty) {
1300 actual += wfunc(obj_name_sep, 1, 2, target); 1187 actual += wfunc(obj_name_sep, 1, 2, target);
1301 expected += 4 + member->name.length; 1188 // FIXME: is this really correct? should be the (escaped) name.length
1189 expected += 4 + key.length;
1302 } else { 1190 } else {
1303 actual += wfunc(obj_name_sep, 1, 1, target); 1191 actual += wfunc(obj_name_sep, 1, 1, target);
1304 expected += 3 + member->name.length; 1192 // FIXME: is this really correct? should be the (escaped) name.length
1193 expected += 3 + key.length;
1305 } 1194 }
1306 1195
1307 // the value 1196 // the value
1308 if (cx_json_write_rec(target, member->value, wfunc, settings, depth)) return 1; 1197 if (cx_json_write_rec(target, member->value, wfunc, settings, depth)) return 1;
1309 1198
1310 // end of object-value 1199 // end of object-value
1311 if (look_idx < elem_count - 1) { 1200 if (member_iter.index < member_iter.elem_count - 1) {
1312 const char *obj_value_sep = ",\n"; 1201 const char *obj_value_sep = ",\n";
1313 if (settings->pretty) { 1202 if (settings->pretty) {
1314 actual += wfunc(obj_value_sep, 1, 2, target); 1203 actual += wfunc(obj_value_sep, 1, 2, target);
1315 expected += 2; 1204 expected += 2;
1316 } else { 1205 } else {
1359 expected++; 1248 expected++;
1360 break; 1249 break;
1361 } 1250 }
1362 case CX_JSON_STRING: { 1251 case CX_JSON_STRING: {
1363 actual += wfunc("\"", 1, 1, target); 1252 actual += wfunc("\"", 1, 1, target);
1364 cxmutstr str = escape_string(value->string, settings->escape_slash); 1253 cxmutstr str = escape_string(cx_strcast(value->string),
1254 settings->escape_slash);
1365 actual += wfunc(str.ptr, 1, str.length, target); 1255 actual += wfunc(str.ptr, 1, str.length, target);
1366 if (str.ptr != value->string.ptr) { 1256 if (str.ptr != value->string.ptr) {
1367 cx_strfree(&str); 1257 cx_strfree(&str);
1368 } 1258 }
1369 actual += wfunc("\"", 1, 1, target); 1259 actual += wfunc("\"", 1, 1, target);

mercurial