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