| 96 } |
96 } |
| 97 |
97 |
| 98 static CxJsonToken token_create(CxJson *json, bool isstring, size_t start, size_t end) { |
98 static CxJsonToken token_create(CxJson *json, bool isstring, size_t start, size_t end) { |
| 99 cxmutstr str = cx_mutstrn(json->buffer.space + start, end - start); |
99 cxmutstr str = cx_mutstrn(json->buffer.space + start, end - start); |
| 100 bool allocated = false; |
100 bool allocated = false; |
| 101 if (json->uncompleted.tokentype != CX_JSON_NO_TOKEN) { |
101 if (json->uncompleted_tokentype != CX_JSON_NO_TOKEN) { |
| 102 allocated = true; |
102 allocated = true; |
| 103 str = cx_strcat_m(json->uncompleted.content, 1, str); |
103 str = cx_strcat(json->uncompleted_content, 1, str); |
| 104 if (str.ptr == NULL) { // LCOV_EXCL_START |
104 if (str.ptr == NULL) { |
| 105 return (CxJsonToken){CX_JSON_NO_TOKEN, false, {NULL, 0}}; |
105 return (CxJsonToken){CX_JSON_NO_TOKEN, false, CX_NULLSTR}; // LCOV_EXCL_LINE |
| 106 } // LCOV_EXCL_STOP |
106 } |
| 107 } |
107 json->uncompleted_content = CX_NULLSTR; |
| 108 json->uncompleted = (CxJsonToken){0}; |
108 json->uncompleted_tokentype = CX_JSON_NO_TOKEN; |
| |
109 } |
| 109 CxJsonTokenType ttype; |
110 CxJsonTokenType ttype; |
| 110 if (isstring) { |
111 if (isstring) { |
| 111 ttype = CX_JSON_TOKEN_STRING; |
112 ttype = CX_JSON_TOKEN_STRING; |
| 112 } else { |
113 } else { |
| 113 cxstring s = cx_strcast(str); |
114 if (!cx_strcmp(str, "true") || !cx_strcmp(str, "false") |
| 114 if (!cx_strcmp(s, "true") || !cx_strcmp(s, "false") |
115 || !cx_strcmp(str, "null")) { |
| 115 || !cx_strcmp(s, "null")) { |
|
| 116 ttype = CX_JSON_TOKEN_LITERAL; |
116 ttype = CX_JSON_TOKEN_LITERAL; |
| 117 } else { |
117 } else { |
| 118 ttype = token_numbertype(str.ptr, str.length); |
118 ttype = token_numbertype(str.ptr, str.length); |
| 119 } |
119 } |
| 120 } |
120 } |
| 121 if (ttype == CX_JSON_TOKEN_ERROR) { |
121 if (ttype == CX_JSON_TOKEN_ERROR) { |
| 122 if (allocated) { |
122 if (allocated) { |
| 123 cx_strfree(&str); |
123 cx_strfree(&str); |
| 124 } |
124 } |
| 125 return (CxJsonToken){CX_JSON_TOKEN_ERROR, false, {NULL, 0}}; |
125 return (CxJsonToken){CX_JSON_TOKEN_ERROR, false, CX_NULLSTR}; |
| 126 } |
126 } |
| 127 return (CxJsonToken){ttype, allocated, str}; |
127 return (CxJsonToken){ttype, allocated, str}; |
| 128 } |
128 } |
| 129 |
129 |
| 130 static CxJsonTokenType char2ttype(char c) { |
130 static CxJsonTokenType char2ttype(char c) { |
| 160 } |
160 } |
| 161 |
161 |
| 162 static enum cx_json_status token_parse_next(CxJson *json, CxJsonToken *result) { |
162 static enum cx_json_status token_parse_next(CxJson *json, CxJsonToken *result) { |
| 163 // check if there is data in the buffer |
163 // check if there is data in the buffer |
| 164 if (cxBufferEof(&json->buffer)) { |
164 if (cxBufferEof(&json->buffer)) { |
| 165 return json->uncompleted.tokentype == CX_JSON_NO_TOKEN ? |
165 return json->uncompleted_tokentype == CX_JSON_NO_TOKEN ? |
| 166 CX_JSON_NO_DATA : CX_JSON_INCOMPLETE_DATA; |
166 CX_JSON_NO_DATA : CX_JSON_INCOMPLETE_DATA; |
| 167 } |
167 } |
| 168 |
168 |
| 169 // current token type and start index |
169 // current token type and start index |
| 170 CxJsonTokenType ttype = json->uncompleted.tokentype; |
170 CxJsonTokenType ttype = json->uncompleted_tokentype; |
| 171 size_t token_part_start = json->buffer.pos; |
171 size_t token_part_start = json->buffer.pos; |
| 172 |
172 |
| 173 bool escape_end_of_string = ttype == CX_JSON_TOKEN_STRING |
173 bool escape_end_of_string = ttype == CX_JSON_TOKEN_STRING |
| 174 && json->uncompleted.content.ptr[json->uncompleted.content.length-1] == '\\'; |
174 && cx_strat(json->uncompleted_content, -1) == '\\'; |
| 175 |
175 |
| 176 for (size_t i = json->buffer.pos; i < json->buffer.size; i++) { |
176 for (size_t i = json->buffer.pos; i < json->buffer.size; i++) { |
| 177 char c = json->buffer.space[i]; |
177 char c = json->buffer.space[i]; |
| 178 if (ttype != CX_JSON_TOKEN_STRING) { |
178 if (ttype != CX_JSON_TOKEN_STRING) { |
| 179 // currently non-string token |
179 // currently non-string token |
| 187 ttype = CX_JSON_TOKEN_STRING; |
187 ttype = CX_JSON_TOKEN_STRING; |
| 188 token_part_start = i; |
188 token_part_start = i; |
| 189 } else if (ctype != CX_JSON_NO_TOKEN) { |
189 } else if (ctype != CX_JSON_NO_TOKEN) { |
| 190 // single-char token |
190 // single-char token |
| 191 json->buffer.pos = i + 1; |
191 json->buffer.pos = i + 1; |
| 192 *result = (CxJsonToken){ctype, false, {NULL, 0}}; |
192 *result = (CxJsonToken){ctype, false, CX_NULLSTR}; |
| 193 return CX_JSON_NO_ERROR; |
193 return CX_JSON_NO_ERROR; |
| 194 } else { |
194 } else { |
| 195 ttype = CX_JSON_TOKEN_LITERAL; // number or literal |
195 ttype = CX_JSON_TOKEN_LITERAL; // number or literal |
| 196 token_part_start = i; |
196 token_part_start = i; |
| 197 } |
197 } |
| 230 |
230 |
| 231 if (ttype == CX_JSON_NO_TOKEN) { |
231 if (ttype == CX_JSON_NO_TOKEN) { |
| 232 return CX_JSON_NO_DATA; |
232 return CX_JSON_NO_DATA; |
| 233 } else { |
233 } else { |
| 234 // uncompleted token |
234 // uncompleted token |
| 235 size_t uncompleted_len = json->buffer.size - token_part_start; |
235 cxstring uncompleted = cx_strn(json->buffer.space + token_part_start, json->buffer.size - token_part_start); |
| 236 if (json->uncompleted.tokentype == CX_JSON_NO_TOKEN) { |
236 if (json->uncompleted_tokentype == CX_JSON_NO_TOKEN) { |
| 237 // current token is uncompleted |
237 assert(json->uncompleted_content.ptr == NULL); |
| 238 // save current token content |
238 json->uncompleted_content = cx_strdup(uncompleted); |
| 239 CxJsonToken uncompleted = { |
239 if (json->uncompleted_content.ptr == NULL) { |
| 240 ttype, true, |
|
| 241 cx_strdup(cx_strn(json->buffer.space + token_part_start, uncompleted_len)) |
|
| 242 }; |
|
| 243 if (uncompleted.content.ptr == NULL) { |
|
| 244 return CX_JSON_BUFFER_ALLOC_FAILED; // LCOV_EXCL_LINE |
240 return CX_JSON_BUFFER_ALLOC_FAILED; // LCOV_EXCL_LINE |
| 245 } |
241 } |
| 246 json->uncompleted = uncompleted; |
242 json->uncompleted_tokentype = ttype; |
| 247 } else { |
243 } else { |
| 248 // previously we also had an uncompleted token |
244 // previously we already had an uncompleted token |
| 249 // combine the uncompleted token with the current token |
245 // combine the uncompleted token with the current token |
| 250 assert(json->uncompleted.allocated); |
246 cxmutstr s = cx_strcat(json->uncompleted_content, 1, uncompleted); |
| 251 cxmutstr str = cx_strcat_m(json->uncompleted.content, 1, |
247 if (s.ptr == NULL) { |
| 252 cx_strn(json->buffer.space + token_part_start, uncompleted_len)); |
|
| 253 if (str.ptr == NULL) { |
|
| 254 return CX_JSON_BUFFER_ALLOC_FAILED; // LCOV_EXCL_LINE |
248 return CX_JSON_BUFFER_ALLOC_FAILED; // LCOV_EXCL_LINE |
| 255 } |
249 } |
| 256 json->uncompleted.content = str; |
250 json->uncompleted_content = s; |
| 257 } |
251 } |
| 258 // advance the buffer position - we saved the stuff in the uncompleted token |
252 // advance the buffer position - we saved the stuff in the uncompleted token |
| 259 json->buffer.pos += uncompleted_len; |
253 json->buffer.pos += uncompleted.length; |
| 260 return CX_JSON_INCOMPLETE_DATA; |
254 return CX_JSON_INCOMPLETE_DATA; |
| 261 } |
255 } |
| 262 } |
256 } |
| 263 |
257 |
| 264 // converts a Unicode codepoint to utf8 |
258 // converts a Unicode codepoint to utf8 |
| 562 if (json->vbuf.data != json->vbuf_internal) { |
556 if (json->vbuf.data != json->vbuf_internal) { |
| 563 cx_array_free(json->vbuf); |
557 cx_array_free(json->vbuf); |
| 564 } |
558 } |
| 565 cxJsonValueFree(json->parsed); |
559 cxJsonValueFree(json->parsed); |
| 566 json->parsed = NULL; |
560 json->parsed = NULL; |
| 567 token_destroy(&json->uncompleted); |
561 json->uncompleted_tokentype = CX_JSON_NO_TOKEN; |
| |
562 cx_strfree(&json->uncompleted_content); |
| 568 cx_strfree_a(json->allocator, &json->uncompleted_member_name); |
563 cx_strfree_a(json->allocator, &json->uncompleted_member_name); |
| 569 } |
564 } |
| 570 |
565 |
| 571 void cxJsonReset(CxJson *json) { |
566 void cxJsonReset(CxJson *json) { |
| 572 const CxAllocator *allocator = json->allocator; |
567 const CxAllocator *allocator = json->allocator; |
| 1224 } |
1219 } |
| 1225 } |
1220 } |
| 1226 |
1221 |
| 1227 // the name |
1222 // the name |
| 1228 actual += wfunc("\"", 1, 1, target); |
1223 actual += wfunc("\"", 1, 1, target); |
| 1229 cxstring key = cx_strn(member->key->data, member->key->len); |
1224 cxstring key = cx_hash_key_as_string(member->key); |
| 1230 cxmutstr name = escape_string(key, settings->escape_slash); |
1225 cxmutstr name = escape_string(key, settings->escape_slash); |
| 1231 actual += wfunc(name.ptr, 1, name.length, target); |
1226 actual += wfunc(name.ptr, 1, name.length, target); |
| 1232 actual += wfunc("\"", 1, 1, target); |
1227 actual += wfunc("\"", 1, 1, target); |
| 1233 const char *obj_name_sep = ": "; |
1228 const char *obj_name_sep = ": "; |
| 1234 if (settings->pretty) { |
1229 if (settings->pretty) { |
| 1430 static cxmutstr cx_json_to_string(CxJsonValue *value, const CxAllocator *allocator, CxJsonWriter *writer) { |
1425 static cxmutstr cx_json_to_string(CxJsonValue *value, const CxAllocator *allocator, CxJsonWriter *writer) { |
| 1431 if (allocator == NULL) allocator = cxDefaultAllocator; |
1426 if (allocator == NULL) allocator = cxDefaultAllocator; |
| 1432 CxBuffer buffer; |
1427 CxBuffer buffer; |
| 1433 if (cxBufferInit(&buffer, allocator, NULL, 128, |
1428 if (cxBufferInit(&buffer, allocator, NULL, 128, |
| 1434 CX_BUFFER_AUTO_EXTEND | CX_BUFFER_DO_NOT_FREE)) { |
1429 CX_BUFFER_AUTO_EXTEND | CX_BUFFER_DO_NOT_FREE)) { |
| 1435 return (cxmutstr){NULL, 0}; |
1430 return CX_NULLSTR; // LCOV_EXCL_LINE |
| 1436 } |
1431 } |
| 1437 if (cx_json_write_rec(&buffer, value, cxBufferWriteFunc, writer, 0) |
1432 if (cx_json_write_rec(&buffer, value, cxBufferWriteFunc, writer, 0) |
| 1438 || cxBufferTerminate(&buffer)) { |
1433 || cxBufferTerminate(&buffer)) { |
| 1439 // LCOV_EXCL_START |
1434 // LCOV_EXCL_START |
| 1440 buffer.flags &= ~CX_BUFFER_DO_NOT_FREE; |
1435 buffer.flags &= ~CX_BUFFER_DO_NOT_FREE; |
| 1441 cxBufferDestroy(&buffer); |
1436 cxBufferDestroy(&buffer); |
| 1442 return (cxmutstr){NULL, 0}; |
1437 return CX_NULLSTR; |
| 1443 // LCOV_EXCL_STOP |
1438 // LCOV_EXCL_STOP |
| 1444 } else { |
1439 } else { |
| 1445 cxmutstr str = cx_mutstrn(buffer.space, buffer.size); |
1440 cxmutstr str = cx_bstr_m(&buffer); |
| 1446 cxBufferDestroy(&buffer); |
1441 cxBufferDestroy(&buffer); |
| 1447 return str; |
1442 return str; |
| 1448 } |
1443 } |
| 1449 |
1444 |
| 1450 } |
1445 } |
| 1502 CxJsonValue* cxJsonClone(const CxJsonValue* value, const CxAllocator* allocator) { |
1496 CxJsonValue* cxJsonClone(const CxJsonValue* value, const CxAllocator* allocator) { |
| 1503 return cx_json_clone_func(NULL, value, allocator, NULL); |
1497 return cx_json_clone_func(NULL, value, allocator, NULL); |
| 1504 } |
1498 } |
| 1505 |
1499 |
| 1506 CxJsonValue* cx_json_clone_func(CxJsonValue* target, const CxJsonValue* source, |
1500 CxJsonValue* cx_json_clone_func(CxJsonValue* target, const CxJsonValue* source, |
| 1507 const CxAllocator* allocator, cx_attr_unused void *data) { |
1501 const CxAllocator* allocator, CX_UNUSED void *data) { |
| 1508 if (source == NULL || source->type == CX_JSON_NOTHING) { |
1502 if (source == NULL || source->type == CX_JSON_NOTHING) { |
| 1509 return &cx_json_value_nothing; |
1503 return &cx_json_value_nothing; |
| 1510 } |
1504 } |
| 1511 if (allocator == NULL) allocator = cxDefaultAllocator; |
1505 if (allocator == NULL) allocator = cxDefaultAllocator; |
| 1512 |
1506 |
| 1538 CxJsonValue *arr = cxJsonCreateArr(allocator, elem_count); |
1533 CxJsonValue *arr = cxJsonCreateArr(allocator, elem_count); |
| 1539 if (arr == NULL) return NULL; // LCOV_EXCL_LINE |
1534 if (arr == NULL) return NULL; // LCOV_EXCL_LINE |
| 1540 arr->array.size = elem_count; |
1535 arr->array.size = elem_count; |
| 1541 for (size_t i = 0 ; i < elem_count ; i++) { |
1536 for (size_t i = 0 ; i < elem_count ; i++) { |
| 1542 CxJsonValue *e = cx_json_clone_func(NULL, source->array.data[i], allocator, NULL); |
1537 CxJsonValue *e = cx_json_clone_func(NULL, source->array.data[i], allocator, NULL); |
| 1543 if (e == NULL) { |
1538 if (e == NULL) { // LCOV_EXCL_START |
| 1544 // LCOV_EXCL_START |
|
| 1545 cxJsonValueFree(arr); |
1539 cxJsonValueFree(arr); |
| 1546 return NULL; |
1540 return NULL; |
| 1547 // LCOV_EXCL_STOP |
1541 // LCOV_EXCL_STOP |
| 1548 } |
1542 } |
| 1549 arr->array.data[i] = e; |
1543 arr->array.data[i] = e; |
| 1556 return_value(cxJsonCreateInteger(allocator, source->integer)); |
1550 return_value(cxJsonCreateInteger(allocator, source->integer)); |
| 1557 case CX_JSON_NUMBER: |
1551 case CX_JSON_NUMBER: |
| 1558 return_value(cxJsonCreateNumber(allocator, source->number)); |
1552 return_value(cxJsonCreateNumber(allocator, source->number)); |
| 1559 case CX_JSON_LITERAL: |
1553 case CX_JSON_LITERAL: |
| 1560 return_value(cxJsonCreateLiteral(allocator, source->literal)); |
1554 return_value(cxJsonCreateLiteral(allocator, source->literal)); |
| 1561 default: |
1555 default: // LCOV_EXCL_START |
| 1562 // LCOV_EXCL_START |
|
| 1563 // unreachable |
1556 // unreachable |
| 1564 assert(false); |
1557 assert(false); |
| 1565 return NULL; |
1558 return NULL; |
| 1566 // LCOV_EXCL_STOP |
1559 // LCOV_EXCL_STOP |
| 1567 } |
1560 } |