97 cxmutstr str = cx_mutstrn(json->buffer.space + start, end - start); |
97 cxmutstr str = cx_mutstrn(json->buffer.space + start, end - start); |
98 bool allocated = false; |
98 bool allocated = false; |
99 if (json->uncompleted.tokentype != CX_JSON_NO_TOKEN) { |
99 if (json->uncompleted.tokentype != CX_JSON_NO_TOKEN) { |
100 allocated = true; |
100 allocated = true; |
101 str = cx_strcat_m(json->uncompleted.content, 1, str); |
101 str = cx_strcat_m(json->uncompleted.content, 1, str); |
102 if (str.ptr == NULL) { |
102 if (str.ptr == NULL) { // LCOV_EXCL_START |
103 return (CxJsonToken){CX_JSON_NO_TOKEN, false, {NULL, 0}}; |
103 return (CxJsonToken){CX_JSON_NO_TOKEN, false, {NULL, 0}}; |
104 } |
104 } // LCOV_EXCL_STOP |
105 } |
105 } |
106 json->uncompleted = (CxJsonToken){0}; |
106 json->uncompleted = (CxJsonToken){0}; |
107 CxJsonTokenType ttype; |
107 CxJsonTokenType ttype; |
108 if (isstring) { |
108 if (isstring) { |
109 ttype = CX_JSON_TOKEN_STRING; |
109 ttype = CX_JSON_TOKEN_STRING; |
193 } else { |
193 } else { |
194 // finish token |
194 // finish token |
195 if (ctype != CX_JSON_NO_TOKEN) { |
195 if (ctype != CX_JSON_NO_TOKEN) { |
196 *result = token_create(json, false, token_start, i); |
196 *result = token_create(json, false, token_start, i); |
197 if (result->tokentype == CX_JSON_NO_TOKEN) { |
197 if (result->tokentype == CX_JSON_NO_TOKEN) { |
198 return CX_JSON_BUFFER_ALLOC_FAILED; |
198 return CX_JSON_BUFFER_ALLOC_FAILED; // LCOV_EXCL_LINE |
199 } |
199 } |
200 if (result->tokentype == CX_JSON_TOKEN_ERROR) { |
200 if (result->tokentype == CX_JSON_TOKEN_ERROR) { |
201 return CX_JSON_FORMAT_ERROR_NUMBER; |
201 return CX_JSON_FORMAT_ERROR_NUMBER; |
202 } |
202 } |
203 json->buffer.pos = i; |
203 json->buffer.pos = i; |
210 json->tokenizer_escape = false; |
210 json->tokenizer_escape = false; |
211 } else { |
211 } else { |
212 if (c == '"') { |
212 if (c == '"') { |
213 *result = token_create(json, true, token_start, i + 1); |
213 *result = token_create(json, true, token_start, i + 1); |
214 if (result->tokentype == CX_JSON_NO_TOKEN) { |
214 if (result->tokentype == CX_JSON_NO_TOKEN) { |
215 return CX_JSON_BUFFER_ALLOC_FAILED; |
215 return CX_JSON_BUFFER_ALLOC_FAILED; // LCOV_EXCL_LINE |
216 } |
216 } |
217 json->buffer.pos = i + 1; |
217 json->buffer.pos = i + 1; |
218 return CX_JSON_NO_ERROR; |
218 return CX_JSON_NO_ERROR; |
219 } else if (c == '\\') { |
219 } else if (c == '\\') { |
220 json->tokenizer_escape = true; |
220 json->tokenizer_escape = true; |
232 CxJsonToken uncompleted = { |
232 CxJsonToken uncompleted = { |
233 ttype, true, |
233 ttype, true, |
234 cx_strdup(cx_strn(json->buffer.space + token_start, uncompleted_len)) |
234 cx_strdup(cx_strn(json->buffer.space + token_start, uncompleted_len)) |
235 }; |
235 }; |
236 if (uncompleted.content.ptr == NULL) { |
236 if (uncompleted.content.ptr == NULL) { |
237 return CX_JSON_BUFFER_ALLOC_FAILED; |
237 return CX_JSON_BUFFER_ALLOC_FAILED; // LCOV_EXCL_LINE |
238 } |
238 } |
239 json->uncompleted = uncompleted; |
239 json->uncompleted = uncompleted; |
240 } else { |
240 } else { |
241 // previously we also had an uncompleted token |
241 // previously we also had an uncompleted token |
242 // combine the uncompleted token with the current token |
242 // combine the uncompleted token with the current token |
243 assert(json->uncompleted.allocated); |
243 assert(json->uncompleted.allocated); |
244 cxmutstr str = cx_strcat_m(json->uncompleted.content, 1, |
244 cxmutstr str = cx_strcat_m(json->uncompleted.content, 1, |
245 cx_strn(json->buffer.space + token_start, uncompleted_len)); |
245 cx_strn(json->buffer.space + token_start, uncompleted_len)); |
246 if (str.ptr == NULL) { |
246 if (str.ptr == NULL) { |
247 return CX_JSON_BUFFER_ALLOC_FAILED; |
247 return CX_JSON_BUFFER_ALLOC_FAILED; // LCOV_EXCL_LINE |
248 } |
248 } |
249 json->uncompleted.content = str; |
249 json->uncompleted.content = str; |
250 } |
250 } |
251 // advance the buffer position - we saved the stuff in the uncompleted token |
251 // advance the buffer position - we saved the stuff in the uncompleted token |
252 json->buffer.pos += uncompleted_len; |
252 json->buffer.pos += uncompleted_len; |
289 return result; |
287 return result; |
290 } |
288 } |
291 |
289 |
292 static CxJsonValue* create_json_value(CxJson *json, CxJsonValueType type) { |
290 static CxJsonValue* create_json_value(CxJson *json, CxJsonValueType type) { |
293 CxJsonValue *v = cxMalloc(json->allocator, sizeof(CxJsonValue)); |
291 CxJsonValue *v = cxMalloc(json->allocator, sizeof(CxJsonValue)); |
294 if (v == NULL) { |
292 if (v == NULL) return NULL; // LCOV_EXCL_LINE |
295 return NULL; |
|
296 } |
|
297 |
293 |
298 // initialize the value |
294 // initialize the value |
299 if (type == CX_JSON_ARRAY) { |
295 if (type == CX_JSON_ARRAY) { |
300 cx_array_initialize_a(json->allocator, v->value.array.array, 16); |
296 cx_array_initialize_a(json->allocator, v->value.array.array, 16); |
301 if (v->value.array.array == NULL) { |
297 if (v->value.array.array == NULL) { // LCOV_EXCL_START |
302 cxFree(json->allocator, v); |
298 cxFree(json->allocator, v); |
303 return NULL; |
299 return NULL; |
304 } |
300 } // LCOV_EXCL_STOP |
305 } else if (type == CX_JSON_OBJECT) { |
301 } else if (type == CX_JSON_OBJECT) { |
306 cx_array_initialize_a(json->allocator, v->value.object.values, 16); |
302 cx_array_initialize_a(json->allocator, v->value.object.values, 16); |
307 if (v->value.object.values == NULL) { |
303 if (v->value.object.values == NULL) { // LCOV_EXCL_START |
308 cxFree(json->allocator, v); |
304 cxFree(json->allocator, v); |
309 return NULL; |
305 return NULL; |
310 } |
306 } // LCOV_EXCL_STOP |
311 } else { |
307 } else { |
312 memset(v, 0, sizeof(CxJsonValue)); |
308 memset(v, 0, sizeof(CxJsonValue)); |
313 } |
309 } |
314 v->type = type; |
310 v->type = type; |
315 v->allocator = json->allocator; |
311 v->allocator = json->allocator; |
323 } else if (parent->type == CX_JSON_OBJECT) { |
319 } else if (parent->type == CX_JSON_OBJECT) { |
324 assert(parent->value.object.values_size > 0); |
320 assert(parent->value.object.values_size > 0); |
325 assert(parent->value.object.values[parent->value.object.values_size - 1].value == NULL); |
321 assert(parent->value.object.values[parent->value.object.values_size - 1].value == NULL); |
326 parent->value.object.values[parent->value.object.values_size - 1].value = v; |
322 parent->value.object.values[parent->value.object.values_size - 1].value = v; |
327 } else { |
323 } else { |
328 assert(false); |
324 assert(false); // LCOV_EXCL_LINE |
329 } |
325 } |
330 } |
326 } |
331 |
327 |
332 // add the new value to the stack, if it is an array or object |
328 // add the new value to the stack, if it is an array or object |
333 if (type == CX_JSON_ARRAY || type == CX_JSON_OBJECT) { |
329 if (type == CX_JSON_ARRAY || type == CX_JSON_OBJECT) { |
334 CxArrayReallocator vbuf_realloc = cx_array_reallocator(NULL, json->vbuf_internal); |
330 CxArrayReallocator vbuf_realloc = cx_array_reallocator(NULL, json->vbuf_internal); |
335 if (cx_array_simple_add_a(&vbuf_realloc, json->vbuf, v)) { |
331 if (cx_array_simple_add_a(&vbuf_realloc, json->vbuf, v)) { |
|
332 // LCOV_EXCL_START |
336 cxFree(json->allocator, v); |
333 cxFree(json->allocator, v); |
337 return NULL; |
334 return NULL; |
338 } |
335 } // LCOV_EXCL_STOP |
339 } |
336 } |
340 |
337 |
341 // if currently no value is parsed, this is now the value of interest |
338 // if currently no value is parsed, this is now the value of interest |
342 if (json->parsed == NULL) { |
339 if (json->parsed == NULL) { |
343 json->parsed = v; |
340 json->parsed = v; |
426 int state = json->states[--json->states_size]; |
423 int state = json->states[--json->states_size]; |
427 |
424 |
428 // guarantee that at least two more states fit on the stack |
425 // guarantee that at least two more states fit on the stack |
429 CxArrayReallocator state_realloc = cx_array_reallocator(NULL, json->states_internal); |
426 CxArrayReallocator state_realloc = cx_array_reallocator(NULL, json->states_internal); |
430 if (cx_array_simple_reserve_a(&state_realloc, json->states, 2)) { |
427 if (cx_array_simple_reserve_a(&state_realloc, json->states, 2)) { |
431 return CX_JSON_BUFFER_ALLOC_FAILED; |
428 return CX_JSON_BUFFER_ALLOC_FAILED; // LCOV_EXCL_LINE |
432 } |
429 } |
433 |
430 |
434 |
431 |
435 // 0 JP_STATE_VALUE_BEGIN value begin |
432 // 0 JP_STATE_VALUE_BEGIN value begin |
436 // 10 JP_STATE_VALUE_END expect value end |
433 // 10 JP_STATE_VALUE_END expect value end |
446 // push expected end state to the stack |
443 // push expected end state to the stack |
447 json_add_state(json, 10 + state); |
444 json_add_state(json, 10 + state); |
448 switch (token.tokentype) { |
445 switch (token.tokentype) { |
449 case CX_JSON_TOKEN_BEGIN_ARRAY: { |
446 case CX_JSON_TOKEN_BEGIN_ARRAY: { |
450 if (create_json_value(json, CX_JSON_ARRAY) == NULL) { |
447 if (create_json_value(json, CX_JSON_ARRAY) == NULL) { |
451 return_rec(CX_JSON_VALUE_ALLOC_FAILED); |
448 return_rec(CX_JSON_VALUE_ALLOC_FAILED); // LCOV_EXCL_LINE |
452 } |
449 } |
453 json_add_state(json, JP_STATE_VALUE_BEGIN_AR); |
450 json_add_state(json, JP_STATE_VALUE_BEGIN_AR); |
454 return_rec(CX_JSON_NO_ERROR); |
451 return_rec(CX_JSON_NO_ERROR); |
455 } |
452 } |
456 case CX_JSON_TOKEN_BEGIN_OBJECT: { |
453 case CX_JSON_TOKEN_BEGIN_OBJECT: { |
457 if (create_json_value(json, CX_JSON_OBJECT) == NULL) { |
454 if (create_json_value(json, CX_JSON_OBJECT) == NULL) { |
458 return_rec(CX_JSON_VALUE_ALLOC_FAILED); |
455 return_rec(CX_JSON_VALUE_ALLOC_FAILED); // LCOV_EXCL_LINE |
459 } |
456 } |
460 json_add_state(json, JP_STATE_OBJ_NAME_OR_CLOSE); |
457 json_add_state(json, JP_STATE_OBJ_NAME_OR_CLOSE); |
461 return_rec(CX_JSON_NO_ERROR); |
458 return_rec(CX_JSON_NO_ERROR); |
462 } |
459 } |
463 case CX_JSON_TOKEN_STRING: { |
460 case CX_JSON_TOKEN_STRING: { |
464 if ((vbuf = create_json_value(json, CX_JSON_STRING)) == NULL) { |
461 if ((vbuf = create_json_value(json, CX_JSON_STRING)) == NULL) { |
465 return_rec(CX_JSON_VALUE_ALLOC_FAILED); |
462 return_rec(CX_JSON_VALUE_ALLOC_FAILED); // LCOV_EXCL_LINE |
466 } |
463 } |
467 cxmutstr str = unescape_string(json->allocator, token.content); |
464 cxmutstr str = unescape_string(json->allocator, token.content); |
468 if (str.ptr == NULL) { |
465 if (str.ptr == NULL) { |
469 return_rec(CX_JSON_VALUE_ALLOC_FAILED); |
466 return_rec(CX_JSON_VALUE_ALLOC_FAILED); // LCOV_EXCL_LINE |
470 } |
467 } |
471 vbuf->value.string = str; |
468 vbuf->value.string = str; |
472 return_rec(CX_JSON_NO_ERROR); |
469 return_rec(CX_JSON_NO_ERROR); |
473 } |
470 } |
474 case CX_JSON_TOKEN_INTEGER: |
471 case CX_JSON_TOKEN_INTEGER: |
475 case CX_JSON_TOKEN_NUMBER: { |
472 case CX_JSON_TOKEN_NUMBER: { |
476 int type = token.tokentype == CX_JSON_TOKEN_INTEGER ? CX_JSON_INTEGER : CX_JSON_NUMBER; |
473 int type = token.tokentype == CX_JSON_TOKEN_INTEGER ? CX_JSON_INTEGER : CX_JSON_NUMBER; |
477 if (NULL == (vbuf = create_json_value(json, type))) { |
474 if (NULL == (vbuf = create_json_value(json, type))) { |
478 return_rec(CX_JSON_VALUE_ALLOC_FAILED); |
475 return_rec(CX_JSON_VALUE_ALLOC_FAILED); // LCOV_EXCL_LINE |
479 } |
476 } |
480 if (type == CX_JSON_INTEGER) { |
477 if (type == CX_JSON_INTEGER) { |
481 if (cx_strtoi64(token.content, &vbuf->value.integer, 10)) { |
478 if (cx_strtoi64(token.content, &vbuf->value.integer, 10)) { |
482 return_rec(CX_JSON_FORMAT_ERROR_NUMBER); |
479 return_rec(CX_JSON_FORMAT_ERROR_NUMBER); |
483 } |
480 } |
488 } |
485 } |
489 return_rec(CX_JSON_NO_ERROR); |
486 return_rec(CX_JSON_NO_ERROR); |
490 } |
487 } |
491 case CX_JSON_TOKEN_LITERAL: { |
488 case CX_JSON_TOKEN_LITERAL: { |
492 if ((vbuf = create_json_value(json, CX_JSON_LITERAL)) == NULL) { |
489 if ((vbuf = create_json_value(json, CX_JSON_LITERAL)) == NULL) { |
493 return_rec(CX_JSON_VALUE_ALLOC_FAILED); |
490 return_rec(CX_JSON_VALUE_ALLOC_FAILED); // LCOV_EXCL_LINE |
494 } |
491 } |
495 if (0 == cx_strcmp(cx_strcast(token.content), cx_str("true"))) { |
492 if (0 == cx_strcmp(cx_strcast(token.content), cx_str("true"))) { |
496 vbuf->value.literal = CX_JSON_TRUE; |
493 vbuf->value.literal = CX_JSON_TRUE; |
497 } else if (0 == cx_strcmp(cx_strcast(token.content), cx_str("false"))) { |
494 } else if (0 == cx_strcmp(cx_strcast(token.content), cx_str("false"))) { |
498 vbuf->value.literal = CX_JSON_FALSE; |
495 vbuf->value.literal = CX_JSON_FALSE; |
529 } |
526 } |
530 |
527 |
531 // add new entry |
528 // add new entry |
532 cxmutstr name = unescape_string(json->allocator, token.content); |
529 cxmutstr name = unescape_string(json->allocator, token.content); |
533 if (name.ptr == NULL) { |
530 if (name.ptr == NULL) { |
534 return_rec(CX_JSON_VALUE_ALLOC_FAILED); |
531 return_rec(CX_JSON_VALUE_ALLOC_FAILED); // LCOV_EXCL_LINE |
535 } |
532 } |
536 CxJsonObjValue kv = {name, NULL}; |
533 CxJsonObjValue kv = {name, NULL}; |
537 assert(json->vbuf_size > 0); |
534 assert(json->vbuf_size > 0); |
538 CxJsonValue *parent = json->vbuf[json->vbuf_size - 1]; |
535 CxJsonValue *parent = json->vbuf[json->vbuf_size - 1]; |
539 assert(parent != NULL); |
536 assert(parent != NULL); |
540 assert(parent->type == CX_JSON_OBJECT); |
537 assert(parent->type == CX_JSON_OBJECT); |
541 CxArrayReallocator value_realloc = cx_array_reallocator(json->allocator, NULL); |
538 CxArrayReallocator value_realloc = cx_array_reallocator(json->allocator, NULL); |
542 if (cx_array_simple_add_a(&value_realloc, parent->value.object.values, kv)) { |
539 if (cx_array_simple_add_a(&value_realloc, parent->value.object.values, kv)) { |
543 return_rec(CX_JSON_VALUE_ALLOC_FAILED); |
540 return_rec(CX_JSON_VALUE_ALLOC_FAILED); // LCOV_EXCL_LINE |
544 } |
541 } |
545 |
542 |
546 // next state |
543 // next state |
547 json_add_state(json, JP_STATE_OBJ_COLON); |
544 json_add_state(json, JP_STATE_OBJ_COLON); |
548 return_rec(CX_JSON_NO_ERROR); |
545 return_rec(CX_JSON_NO_ERROR); |
704 v->type = CX_JSON_LITERAL; |
701 v->type = CX_JSON_LITERAL; |
705 v->value.literal = lit; |
702 v->value.literal = lit; |
706 return v; |
703 return v; |
707 } |
704 } |
708 |
705 |
|
706 // LCOV_EXCL_START |
|
707 // never called as long as malloc() does not return NULL |
709 static void cx_json_arr_free_temp(CxJsonValue** values, size_t count) { |
708 static void cx_json_arr_free_temp(CxJsonValue** values, size_t count) { |
710 for (size_t i = 0; i < count; i++) { |
709 for (size_t i = 0; i < count; i++) { |
711 if (values[i] == NULL) break; |
710 if (values[i] == NULL) break; |
712 cxJsonValueFree(values[i]); |
711 cxJsonValueFree(values[i]); |
713 } |
712 } |
714 free(values); |
713 free(values); |
715 } |
714 } |
|
715 // LCOV_EXCL_STOP |
716 |
716 |
717 int cxJsonArrAddNumbers(CxJsonValue* arr, const double* num, size_t count) { |
717 int cxJsonArrAddNumbers(CxJsonValue* arr, const double* num, size_t count) { |
718 CxJsonValue** values = calloc(count, sizeof(CxJsonValue*)); |
718 CxJsonValue** values = calloc(count, sizeof(CxJsonValue*)); |
719 if (values == NULL) return -1; |
719 if (values == NULL) return -1; |
720 for (size_t i = 0; i < count; i++) { |
720 for (size_t i = 0; i < count; i++) { |