| 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 |
| 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->value.object.values, |
|
| 56 obj->value.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->value.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; |
|
| 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 |
|
| 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); |
| |
48 token->allocated = false; |
| 128 } |
49 } |
| 129 } |
50 } |
| 130 |
51 |
| 131 static int num_isexp(const char *content, size_t length, size_t pos) { |
52 static int num_isexp(const char *content, size_t length, size_t pos) { |
| 132 if (pos >= length) { |
53 if (pos >= length) { |
| 517 } |
439 } |
| 518 } else if (!all_printable) { |
440 } else if (!all_printable) { |
| 519 cxBufferPut(&buf, c); |
441 cxBufferPut(&buf, c); |
| 520 } |
442 } |
| 521 } |
443 } |
| 522 if (!all_printable) { |
444 cxmutstr ret; |
| 523 str = cx_mutstrn(buf.space, buf.size); |
445 if (all_printable) { |
| |
446 // don't copy the string when we don't need to escape anything |
| |
447 ret = cx_mutstrn((char*)str.ptr, str.length); |
| |
448 } else { |
| |
449 ret = cx_mutstrn(buf.space, buf.size); |
| 524 } |
450 } |
| 525 cxBufferDestroy(&buf); |
451 cxBufferDestroy(&buf); |
| 526 return str; |
452 return ret; |
| |
453 } |
| |
454 |
| |
455 static CxJsonObject json_create_object_map(const CxAllocator *allocator) { |
| |
456 CxMap *map = cxKvListCreateAsMap(allocator, CX_STORE_POINTERS); |
| |
457 if (map == NULL) return NULL; // LCOV_EXCL_LINE |
| |
458 cxSetCompareFunc(map, cxJsonCompare); |
| |
459 cxSetDestructor(map, cxJsonValueFree); |
| |
460 return map; |
| |
461 } |
| |
462 |
| |
463 static void json_free_object_map(CxJsonObject obj) { |
| |
464 cxMapFree(obj); |
| 527 } |
465 } |
| 528 |
466 |
| 529 static CxJsonValue* json_create_value(CxJson *json, CxJsonValueType type) { |
467 static CxJsonValue* json_create_value(CxJson *json, CxJsonValueType type) { |
| 530 CxJsonValue *v = cxCalloc(json->allocator, 1, sizeof(CxJsonValue)); |
468 CxJsonValue *v = cxCalloc(json->allocator, 1, sizeof(CxJsonValue)); |
| 531 if (v == NULL) return NULL; // LCOV_EXCL_LINE |
469 if (v == NULL) return NULL; // LCOV_EXCL_LINE |
| 532 |
470 |
| 533 // initialize the value |
471 // initialize the value |
| 534 v->type = type; |
472 v->type = type; |
| 535 v->allocator = json->allocator; |
473 v->allocator = json->allocator; |
| 536 if (type == CX_JSON_ARRAY) { |
474 if (type == CX_JSON_ARRAY) { |
| 537 cx_array_initialize_a(json->allocator, v->value.array.array, 16); |
475 if (cx_array_init_a(json->allocator, v->array, 16)) { |
| 538 if (v->value.array.array == NULL) goto create_json_value_exit_error; // LCOV_EXCL_LINE |
476 goto create_json_value_exit_error; // LCOV_EXCL_LINE |
| |
477 } |
| 539 } else if (type == CX_JSON_OBJECT) { |
478 } else if (type == CX_JSON_OBJECT) { |
| 540 cx_array_initialize_a(json->allocator, v->value.object.values, 16); |
479 v->object = json_create_object_map(json->allocator); |
| 541 v->value.object.indices = cxCalloc(json->allocator, 16, sizeof(size_t)); |
480 if (v->object == NULL) goto create_json_value_exit_error; // LCOV_EXCL_LINE |
| 542 if (v->value.object.values == NULL || |
|
| 543 v->value.object.indices == NULL) |
|
| 544 goto create_json_value_exit_error; // LCOV_EXCL_LINE |
|
| 545 } |
481 } |
| 546 |
482 |
| 547 // add the new value to a possible parent |
483 // add the new value to a possible parent |
| 548 if (json->vbuf_size > 0) { |
484 if (json->vbuf.size > 0) { |
| 549 CxJsonValue *parent = json->vbuf[json->vbuf_size - 1]; |
485 CxJsonValue *parent = json->vbuf.data[json->vbuf.size - 1]; |
| 550 assert(parent != NULL); |
486 assert(parent != NULL); |
| 551 if (parent->type == CX_JSON_ARRAY) { |
487 if (parent->type == CX_JSON_ARRAY) { |
| 552 CxArrayReallocator value_realloc = cx_array_reallocator(json->allocator, NULL); |
488 if (cx_array_add_a(json->allocator, parent->array, v)) { |
| 553 if (cx_array_simple_add_a(&value_realloc, parent->value.array.array, v)) { |
|
| 554 goto create_json_value_exit_error; // LCOV_EXCL_LINE |
489 goto create_json_value_exit_error; // LCOV_EXCL_LINE |
| 555 } |
490 } |
| 556 } else if (parent->type == CX_JSON_OBJECT) { |
491 } else if (parent->type == CX_JSON_OBJECT) { |
| 557 // the member was already created after parsing the name |
492 // the member was already created after parsing the name |
| 558 assert(json->uncompleted_member.name.ptr != NULL); |
493 // store the pointer of the uncompleted value in the map |
| 559 json->uncompleted_member.value = v; |
494 assert(json->uncompleted_member_name.ptr != NULL); |
| 560 if (json_add_objvalue(parent, json->uncompleted_member)) { |
495 if (cxMapPut(parent->object, json->uncompleted_member_name, v)) { |
| 561 goto create_json_value_exit_error; // LCOV_EXCL_LINE |
496 goto create_json_value_exit_error; // LCOV_EXCL_LINE |
| 562 } |
497 } |
| 563 json->uncompleted_member.name = (cxmutstr) {NULL, 0}; |
498 cx_strfree_a(json->allocator, &json->uncompleted_member_name); |
| 564 } else { |
499 } else { |
| 565 assert(false); // LCOV_EXCL_LINE |
500 assert(false); // LCOV_EXCL_LINE |
| 566 } |
501 } |
| 567 } |
502 } |
| 568 |
503 |
| 569 // add the new value to the stack, if it is an array or object |
504 // add the new value to the stack, if it is an array or object |
| 570 if (type == CX_JSON_ARRAY || type == CX_JSON_OBJECT) { |
505 if (type == CX_JSON_ARRAY || type == CX_JSON_OBJECT) { |
| 571 CxArrayReallocator vbuf_realloc = cx_array_reallocator(NULL, json->vbuf_internal); |
506 if (json->vbuf.size >= json->vbuf.capacity) { |
| 572 if (cx_array_simple_add_a(&vbuf_realloc, json->vbuf, v)) { |
507 int alloc_error; |
| 573 goto create_json_value_exit_error; // LCOV_EXCL_LINE |
508 if (json->vbuf.data == json->vbuf_internal) { |
| 574 } |
509 alloc_error = cx_array_copy_to_new(json->vbuf, json->vbuf.size+1); |
| |
510 } else { |
| |
511 alloc_error = cx_array_reserve(json->vbuf, json->vbuf.size+1); |
| |
512 } |
| |
513 if (alloc_error) { |
| |
514 goto create_json_value_exit_error; // LCOV_EXCL_LINE |
| |
515 } |
| |
516 } |
| |
517 json->vbuf.data[json->vbuf.size] = v; |
| |
518 json->vbuf.size++; |
| 575 } |
519 } |
| 576 |
520 |
| 577 // if currently no value is parsed, this is now the value of interest |
521 // if currently no value is parsed, this is now the value of interest |
| 578 if (json->parsed == NULL) { |
522 if (json->parsed == NULL) { |
| 579 json->parsed = v; |
523 json->parsed = v; |
| 603 } |
547 } |
| 604 |
548 |
| 605 memset(json, 0, sizeof(CxJson)); |
549 memset(json, 0, sizeof(CxJson)); |
| 606 json->allocator = allocator; |
550 json->allocator = allocator; |
| 607 |
551 |
| 608 json->states = json->states_internal; |
552 cx_array_init_fixed(json->states, json->states_internal, 1); |
| 609 json->states_capacity = cx_nmemb(json->states_internal); |
553 json->states.data[0] = JP_STATE_VALUE_BEGIN; |
| 610 json->states[0] = JP_STATE_VALUE_BEGIN; |
554 cx_array_init_fixed(json->vbuf, json->vbuf_internal, 0); |
| 611 json->states_size = 1; |
|
| 612 |
|
| 613 json->vbuf = json->vbuf_internal; |
|
| 614 json->vbuf_capacity = cx_nmemb(json->vbuf_internal); |
|
| 615 } |
555 } |
| 616 |
556 |
| 617 void cxJsonDestroy(CxJson *json) { |
557 void cxJsonDestroy(CxJson *json) { |
| 618 cxBufferDestroy(&json->buffer); |
558 cxBufferDestroy(&json->buffer); |
| 619 if (json->states != json->states_internal) { |
559 if (json->states.data != json->states_internal) { |
| 620 cxFreeDefault(json->states); |
560 cx_array_free(json->states); |
| 621 } |
561 } |
| 622 if (json->vbuf != json->vbuf_internal) { |
562 if (json->vbuf.data != json->vbuf_internal) { |
| 623 cxFreeDefault(json->vbuf); |
563 cx_array_free(json->vbuf); |
| 624 } |
564 } |
| 625 cxJsonValueFree(json->parsed); |
565 cxJsonValueFree(json->parsed); |
| 626 json->parsed = NULL; |
566 json->parsed = NULL; |
| 627 if (json->uncompleted_member.name.ptr != NULL) { |
567 token_destroy(&json->uncompleted); |
| 628 cx_strfree_a(json->allocator, &json->uncompleted_member.name); |
568 cx_strfree_a(json->allocator, &json->uncompleted_member_name); |
| 629 json->uncompleted_member = (CxJsonObjValue){{NULL, 0}, NULL}; |
|
| 630 } |
|
| 631 } |
569 } |
| 632 |
570 |
| 633 void cxJsonReset(CxJson *json) { |
571 void cxJsonReset(CxJson *json) { |
| 634 const CxAllocator *allocator = json->allocator; |
572 const CxAllocator *allocator = json->allocator; |
| 635 cxJsonDestroy(json); |
573 cxJsonDestroy(json); |
| 709 return_rec(CX_JSON_VALUE_ALLOC_FAILED); // LCOV_EXCL_LINE |
656 return_rec(CX_JSON_VALUE_ALLOC_FAILED); // LCOV_EXCL_LINE |
| 710 } |
657 } |
| 711 json_add_state(json, JP_STATE_OBJ_NAME_OR_CLOSE); |
658 json_add_state(json, JP_STATE_OBJ_NAME_OR_CLOSE); |
| 712 return_rec(CX_JSON_NO_ERROR); |
659 return_rec(CX_JSON_NO_ERROR); |
| 713 } |
660 } |
| |
661 case CX_JSON_TOKEN_END_ARRAY: { |
| |
662 if (state == JP_STATE_VALUE_BEGIN_AR) { |
| |
663 // discard the array from the value buffer |
| |
664 json->vbuf.size--; |
| |
665 json->states.size--; |
| |
666 return_rec(CX_JSON_NO_ERROR); |
| |
667 } else { |
| |
668 return_rec(CX_JSON_FORMAT_ERROR_UNEXPECTED_TOKEN); |
| |
669 } |
| |
670 } |
| 714 case CX_JSON_TOKEN_STRING: { |
671 case CX_JSON_TOKEN_STRING: { |
| 715 if ((vbuf = json_create_value(json, CX_JSON_STRING)) == NULL) { |
672 if ((vbuf = json_create_value(json, CX_JSON_STRING)) == NULL) { |
| 716 return_rec(CX_JSON_VALUE_ALLOC_FAILED); // LCOV_EXCL_LINE |
673 return_rec(CX_JSON_VALUE_ALLOC_FAILED); // LCOV_EXCL_LINE |
| 717 } |
674 } |
| 718 cxmutstr str = unescape_string(json->allocator, token.content); |
675 cxmutstr str = unescape_string(json->allocator, token.content); |
| 719 if (str.ptr == NULL) { |
676 if (str.ptr == NULL) { |
| 720 return_rec(CX_JSON_VALUE_ALLOC_FAILED); // LCOV_EXCL_LINE |
677 return_rec(CX_JSON_VALUE_ALLOC_FAILED); // LCOV_EXCL_LINE |
| 721 } |
678 } |
| 722 vbuf->value.string = str; |
679 vbuf->string = str; |
| 723 return_rec(CX_JSON_NO_ERROR); |
680 return_rec(CX_JSON_NO_ERROR); |
| 724 } |
681 } |
| 725 case CX_JSON_TOKEN_INTEGER: |
682 case CX_JSON_TOKEN_INTEGER: |
| 726 case CX_JSON_TOKEN_NUMBER: { |
683 case CX_JSON_TOKEN_NUMBER: { |
| 727 int type = token.tokentype == CX_JSON_TOKEN_INTEGER ? CX_JSON_INTEGER : CX_JSON_NUMBER; |
684 int type = token.tokentype == CX_JSON_TOKEN_INTEGER ? CX_JSON_INTEGER : CX_JSON_NUMBER; |
| 728 if (NULL == (vbuf = json_create_value(json, type))) { |
685 if (NULL == (vbuf = json_create_value(json, type))) { |
| 729 return_rec(CX_JSON_VALUE_ALLOC_FAILED); // LCOV_EXCL_LINE |
686 return_rec(CX_JSON_VALUE_ALLOC_FAILED); // LCOV_EXCL_LINE |
| 730 } |
687 } |
| 731 if (type == CX_JSON_INTEGER) { |
688 if (type == CX_JSON_INTEGER) { |
| 732 if (cx_strtoi64(token.content, &vbuf->value.integer, 10)) { |
689 if (cx_strtoi64(token.content, &vbuf->integer, 10)) { |
| 733 return_rec(CX_JSON_FORMAT_ERROR_NUMBER); |
690 return_rec(CX_JSON_FORMAT_ERROR_NUMBER); |
| 734 } |
691 } |
| 735 } else { |
692 } else { |
| 736 if (cx_strtod(token.content, &vbuf->value.number)) { |
693 if (cx_strtod(token.content, &vbuf->number)) { |
| 737 return_rec(CX_JSON_FORMAT_ERROR_NUMBER); |
694 // TODO: at the moment this is unreachable, because the tokenizer is already stricter than cx_strtod() |
| |
695 return_rec(CX_JSON_FORMAT_ERROR_NUMBER); // LCOV_EXCL_LINE |
| 738 } |
696 } |
| 739 } |
697 } |
| 740 return_rec(CX_JSON_NO_ERROR); |
698 return_rec(CX_JSON_NO_ERROR); |
| 741 } |
699 } |
| 742 case CX_JSON_TOKEN_LITERAL: { |
700 case CX_JSON_TOKEN_LITERAL: { |
| 743 if ((vbuf = json_create_value(json, CX_JSON_LITERAL)) == NULL) { |
701 if ((vbuf = json_create_value(json, CX_JSON_LITERAL)) == NULL) { |
| 744 return_rec(CX_JSON_VALUE_ALLOC_FAILED); // LCOV_EXCL_LINE |
702 return_rec(CX_JSON_VALUE_ALLOC_FAILED); // LCOV_EXCL_LINE |
| 745 } |
703 } |
| 746 if (0 == cx_strcmp(cx_strcast(token.content), cx_str("true"))) { |
704 if (0 == cx_strcmp(token.content, "true")) { |
| 747 vbuf->value.literal = CX_JSON_TRUE; |
705 vbuf->literal = CX_JSON_TRUE; |
| 748 } else if (0 == cx_strcmp(cx_strcast(token.content), cx_str("false"))) { |
706 } else if (0 == cx_strcmp(token.content, "false")) { |
| 749 vbuf->value.literal = CX_JSON_FALSE; |
707 vbuf->literal = CX_JSON_FALSE; |
| 750 } else { |
708 } else { |
| 751 vbuf->value.literal = CX_JSON_NULL; |
709 vbuf->literal = CX_JSON_NULL; |
| 752 } |
710 } |
| 753 return_rec(CX_JSON_NO_ERROR); |
711 return_rec(CX_JSON_NO_ERROR); |
| 754 } |
712 } |
| 755 default: { |
713 default: { |
| 756 return_rec(CX_JSON_FORMAT_ERROR_UNEXPECTED_TOKEN); |
714 return_rec(CX_JSON_FORMAT_ERROR_UNEXPECTED_TOKEN); |
| 805 if (token.tokentype == CX_JSON_TOKEN_VALUE_SEPARATOR) { |
763 if (token.tokentype == CX_JSON_TOKEN_VALUE_SEPARATOR) { |
| 806 json_add_state(json, JP_STATE_OBJ_NAME); |
764 json_add_state(json, JP_STATE_OBJ_NAME); |
| 807 return_rec(CX_JSON_NO_ERROR); |
765 return_rec(CX_JSON_NO_ERROR); |
| 808 } else if (token.tokentype == CX_JSON_TOKEN_END_OBJECT) { |
766 } else if (token.tokentype == CX_JSON_TOKEN_END_OBJECT) { |
| 809 // discard the obj from the value buffer |
767 // discard the obj from the value buffer |
| 810 json->vbuf_size--; |
768 json->vbuf.size--; |
| 811 return_rec(CX_JSON_NO_ERROR); |
769 return_rec(CX_JSON_NO_ERROR); |
| 812 } else { |
770 } else { |
| 813 return_rec(CX_JSON_FORMAT_ERROR_UNEXPECTED_TOKEN); |
771 return_rec(CX_JSON_FORMAT_ERROR_UNEXPECTED_TOKEN); |
| 814 } |
772 } |
| 815 } else { |
773 } else { |
| 816 // should be unreachable |
774 // should be unreachable |
| 817 assert(false); |
775 assert(false); |
| 818 return_rec(-1); |
776 return_rec(-1); // LCOV_EXCL_LINE |
| 819 } |
777 } |
| 820 } |
778 } |
| 821 |
779 |
| 822 CxJsonStatus cxJsonNext(CxJson *json, CxJsonValue **value) { |
780 CxJsonStatus cxJsonNext(CxJson *json, CxJsonValue **value) { |
| 823 // check if buffer has been filled |
781 // initialize output value |
| |
782 *value = &cx_json_value_nothing; |
| |
783 |
| |
784 // check if the buffer has been filled |
| 824 if (json->buffer.space == NULL) { |
785 if (json->buffer.space == NULL) { |
| 825 return CX_JSON_NULL_DATA; |
786 return CX_JSON_NULL_DATA; |
| 826 } |
787 } |
| 827 |
|
| 828 // initialize output value |
|
| 829 *value = &cx_json_value_nothing; |
|
| 830 |
788 |
| 831 // parse data |
789 // parse data |
| 832 CxJsonStatus result; |
790 CxJsonStatus result; |
| 833 do { |
791 do { |
| 834 result = json_parse(json); |
792 result = json_parse(json); |
| 835 if (result == CX_JSON_NO_ERROR && json->states_size == 1) { |
793 if (result == CX_JSON_NO_ERROR && json->states.size == 1) { |
| 836 // final state reached |
794 // final state reached |
| 837 assert(json->states[0] == JP_STATE_VALUE_END); |
795 assert(json->states.data[0] == JP_STATE_VALUE_END); |
| 838 assert(json->vbuf_size == 0); |
796 assert(json->vbuf.size == 0); |
| 839 |
797 |
| 840 // write output value |
798 // write output value |
| 841 *value = json->parsed; |
799 *value = json->parsed; |
| 842 json->parsed = NULL; |
800 json->parsed = NULL; |
| 843 |
801 |
| 844 // re-initialize state machine |
802 // re-initialize state machine |
| 845 json->states[0] = JP_STATE_VALUE_BEGIN; |
803 json->states.data[0] = JP_STATE_VALUE_BEGIN; |
| 846 |
804 |
| 847 return CX_JSON_NO_ERROR; |
805 return CX_JSON_NO_ERROR; |
| 848 } |
806 } |
| 849 } while (result == CX_JSON_NO_ERROR); |
807 } while (result == CX_JSON_NO_ERROR); |
| 850 |
808 |
| 851 // the parser might think there is no data |
809 // the parser might think there is no data |
| 852 // but when we did not reach the final state, |
810 // but when we did not reach the final state, |
| 853 // we know that there must be more to come |
811 // we know that there must be more to come |
| 854 if (result == CX_JSON_NO_DATA && json->states_size > 1) { |
812 if (result == CX_JSON_NO_DATA && json->states.size > 1) { |
| 855 return CX_JSON_INCOMPLETE_DATA; |
813 return CX_JSON_INCOMPLETE_DATA; |
| 856 } |
814 } |
| 857 |
815 |
| 858 return result; |
816 return result; |
| |
817 } |
| |
818 |
| |
819 CxJsonStatus cx_json_from_string(const CxAllocator *allocator, |
| |
820 cxstring str, CxJsonValue **value) { |
| |
821 *value = &cx_json_value_nothing; |
| |
822 CxJson parser; |
| |
823 cxJsonInit(&parser, allocator); |
| |
824 if (cxJsonFill(&parser, str)) { |
| |
825 // LCOV_EXCL_START |
| |
826 cxJsonDestroy(&parser); |
| |
827 return CX_JSON_BUFFER_ALLOC_FAILED; |
| |
828 // LCOV_EXCL_STOP |
| |
829 } |
| |
830 CxJsonStatus status = cxJsonNext(&parser, value); |
| |
831 // check if we consume the total string |
| |
832 CxJsonValue *chk_value = NULL; |
| |
833 CxJsonStatus chk_status = CX_JSON_NO_DATA; |
| |
834 if (status == CX_JSON_NO_ERROR) { |
| |
835 chk_status = cxJsonNext(&parser, &chk_value); |
| |
836 } |
| |
837 cxJsonDestroy(&parser); |
| |
838 if (chk_status == CX_JSON_NO_DATA) { |
| |
839 return status; |
| |
840 } else { |
| |
841 cxJsonValueFree(*value); |
| |
842 // if chk_value is nothing, the free is harmless |
| |
843 cxJsonValueFree(chk_value); |
| |
844 *value = &cx_json_value_nothing; |
| |
845 return CX_JSON_FORMAT_ERROR_UNEXPECTED_TOKEN; |
| |
846 } |
| |
847 |
| 859 } |
848 } |
| 860 |
849 |
| 861 void cxJsonValueFree(CxJsonValue *value) { |
850 void cxJsonValueFree(CxJsonValue *value) { |
| 862 if (value == NULL || value->type == CX_JSON_NOTHING) return; |
851 if (value == NULL || value->type == CX_JSON_NOTHING) return; |
| 863 switch (value->type) { |
852 switch (value->type) { |
| 864 case CX_JSON_OBJECT: { |
853 case CX_JSON_OBJECT: { |
| 865 CxJsonObject obj = value->value.object; |
854 json_free_object_map(value->object); |
| 866 for (size_t i = 0; i < obj.values_size; i++) { |
|
| 867 cxJsonValueFree(obj.values[i].value); |
|
| 868 cx_strfree_a(value->allocator, &obj.values[i].name); |
|
| 869 } |
|
| 870 cxFree(value->allocator, obj.values); |
|
| 871 cxFree(value->allocator, obj.indices); |
|
| 872 break; |
855 break; |
| 873 } |
856 } |
| 874 case CX_JSON_ARRAY: { |
857 case CX_JSON_ARRAY: { |
| 875 CxJsonArray array = value->value.array; |
858 for (size_t i = 0; i < value->array.size; i++) { |
| 876 for (size_t i = 0; i < array.array_size; i++) { |
859 cxJsonValueFree(value->array.data[i]); |
| 877 cxJsonValueFree(array.array[i]); |
860 } |
| 878 } |
861 cx_array_free_a(value->allocator, value->array); |
| 879 cxFree(value->allocator, array.array); |
|
| 880 break; |
862 break; |
| 881 } |
863 } |
| 882 case CX_JSON_STRING: { |
864 case CX_JSON_STRING: { |
| 883 cxFree(value->allocator, value->value.string.ptr); |
865 cxFree(value->allocator, value->string.ptr); |
| 884 break; |
866 break; |
| 885 } |
867 } |
| 886 default: { |
868 default: { |
| 887 break; |
869 break; |
| 888 } |
870 } |
| 894 if (allocator == NULL) allocator = cxDefaultAllocator; |
876 if (allocator == NULL) allocator = cxDefaultAllocator; |
| 895 CxJsonValue* v = cxMalloc(allocator, sizeof(CxJsonValue)); |
877 CxJsonValue* v = cxMalloc(allocator, sizeof(CxJsonValue)); |
| 896 if (v == NULL) return NULL; |
878 if (v == NULL) return NULL; |
| 897 v->allocator = allocator; |
879 v->allocator = allocator; |
| 898 v->type = CX_JSON_OBJECT; |
880 v->type = CX_JSON_OBJECT; |
| 899 cx_array_initialize_a(allocator, v->value.object.values, 16); |
881 v->object = json_create_object_map(allocator); |
| 900 if (v->value.object.values == NULL) { // LCOV_EXCL_START |
882 if (v->object == NULL) { // LCOV_EXCL_START |
| 901 cxFree(allocator, v); |
883 cxFree(allocator, v); |
| 902 return NULL; |
884 return NULL; |
| 903 // LCOV_EXCL_STOP |
885 // LCOV_EXCL_STOP |
| 904 } |
886 } |
| 905 v->value.object.indices = cxCalloc(allocator, 16, sizeof(size_t)); |
|
| 906 if (v->value.object.indices == NULL) { // LCOV_EXCL_START |
|
| 907 cxFree(allocator, v->value.object.values); |
|
| 908 cxFree(allocator, v); |
|
| 909 return NULL; |
|
| 910 // LCOV_EXCL_STOP |
|
| 911 } |
|
| 912 return v; |
887 return v; |
| 913 } |
888 } |
| 914 |
889 |
| 915 CxJsonValue* cxJsonCreateArr(const CxAllocator* allocator) { |
890 CxJsonValue* cxJsonCreateArr(const CxAllocator* allocator, size_t capacity) { |
| 916 if (allocator == NULL) allocator = cxDefaultAllocator; |
891 if (allocator == NULL) allocator = cxDefaultAllocator; |
| 917 CxJsonValue* v = cxMalloc(allocator, sizeof(CxJsonValue)); |
892 CxJsonValue* v = cxMalloc(allocator, sizeof(CxJsonValue)); |
| 918 if (v == NULL) return NULL; |
893 if (v == NULL) return NULL; |
| 919 v->allocator = allocator; |
894 v->allocator = allocator; |
| 920 v->type = CX_JSON_ARRAY; |
895 v->type = CX_JSON_ARRAY; |
| 921 cx_array_initialize_a(allocator, v->value.array.array, 16); |
896 if (capacity > 0) { |
| 922 if (v->value.array.array == NULL) { cxFree(allocator, v); return NULL; } |
897 if (cx_array_init_a(allocator, v->array, capacity)) { |
| |
898 // LCOV_EXCL_START |
| |
899 cxFree(allocator, v); |
| |
900 return NULL; |
| |
901 // LCOV_EXCL_STOP |
| |
902 } |
| |
903 } else { |
| |
904 v->array.data = NULL; |
| |
905 v->array.size = v->array.capacity = 0; |
| |
906 } |
| 923 return v; |
907 return v; |
| 924 } |
908 } |
| 925 |
909 |
| 926 CxJsonValue* cxJsonCreateNumber(const CxAllocator* allocator, double num) { |
910 CxJsonValue* cxJsonCreateNumber(const CxAllocator* allocator, double num) { |
| 927 if (allocator == NULL) allocator = cxDefaultAllocator; |
911 if (allocator == NULL) allocator = cxDefaultAllocator; |
| 928 CxJsonValue* v = cxMalloc(allocator, sizeof(CxJsonValue)); |
912 CxJsonValue* v = cxMalloc(allocator, sizeof(CxJsonValue)); |
| 929 if (v == NULL) return NULL; |
913 if (v == NULL) return NULL; |
| 930 v->allocator = allocator; |
914 v->allocator = allocator; |
| 931 v->type = CX_JSON_NUMBER; |
915 v->type = CX_JSON_NUMBER; |
| 932 v->value.number = num; |
916 v->number = num; |
| 933 return v; |
917 return v; |
| 934 } |
918 } |
| 935 |
919 |
| 936 CxJsonValue* cxJsonCreateInteger(const CxAllocator* allocator, int64_t num) { |
920 CxJsonValue* cxJsonCreateInteger(const CxAllocator* allocator, int64_t num) { |
| 937 if (allocator == NULL) allocator = cxDefaultAllocator; |
921 if (allocator == NULL) allocator = cxDefaultAllocator; |
| 938 CxJsonValue* v = cxMalloc(allocator, sizeof(CxJsonValue)); |
922 CxJsonValue* v = cxMalloc(allocator, sizeof(CxJsonValue)); |
| 939 if (v == NULL) return NULL; |
923 if (v == NULL) return NULL; |
| 940 v->allocator = allocator; |
924 v->allocator = allocator; |
| 941 v->type = CX_JSON_INTEGER; |
925 v->type = CX_JSON_INTEGER; |
| 942 v->value.integer = num; |
926 v->integer = num; |
| 943 return v; |
927 return v; |
| 944 } |
928 } |
| 945 |
929 |
| 946 CxJsonValue* cxJsonCreateString(const CxAllocator* allocator, const char* str) { |
930 CxJsonValue* cx_json_create_string(const CxAllocator* allocator, cxstring str) { |
| 947 return cxJsonCreateCxString(allocator, cx_str(str)); |
|
| 948 } |
|
| 949 |
|
| 950 CxJsonValue* cxJsonCreateCxString(const CxAllocator* allocator, cxstring str) { |
|
| 951 if (allocator == NULL) allocator = cxDefaultAllocator; |
931 if (allocator == NULL) allocator = cxDefaultAllocator; |
| 952 CxJsonValue* v = cxMalloc(allocator, sizeof(CxJsonValue)); |
932 CxJsonValue* v = cxMalloc(allocator, sizeof(CxJsonValue)); |
| 953 if (v == NULL) return NULL; |
933 if (v == NULL) return NULL; |
| 954 v->allocator = allocator; |
934 v->allocator = allocator; |
| 955 v->type = CX_JSON_STRING; |
935 v->type = CX_JSON_STRING; |
| 956 cxmutstr s = cx_strdup_a(allocator, str); |
936 cxmutstr s = cx_strdup_a(allocator, str); |
| 957 if (s.ptr == NULL) { cxFree(allocator, v); return NULL; } |
937 if (s.ptr == NULL) { cxFree(allocator, v); return NULL; } |
| 958 v->value.string = s; |
938 v->string = s; |
| 959 return v; |
939 return v; |
| 960 } |
940 } |
| 961 |
941 |
| 962 CxJsonValue* cxJsonCreateLiteral(const CxAllocator* allocator, CxJsonLiteral lit) { |
942 CxJsonValue* cxJsonCreateLiteral(const CxAllocator* allocator, CxJsonLiteral lit) { |
| 963 if (allocator == NULL) allocator = cxDefaultAllocator; |
943 if (allocator == NULL) allocator = cxDefaultAllocator; |
| 964 CxJsonValue* v = cxMalloc(allocator, sizeof(CxJsonValue)); |
944 CxJsonValue* v = cxMalloc(allocator, sizeof(CxJsonValue)); |
| 965 if (v == NULL) return NULL; |
945 if (v == NULL) return NULL; |
| 966 v->allocator = allocator; |
946 v->allocator = allocator; |
| 967 v->type = CX_JSON_LITERAL; |
947 v->type = CX_JSON_LITERAL; |
| 968 v->value.literal = lit; |
948 v->literal = lit; |
| 969 return v; |
949 return v; |
| 970 } |
950 } |
| 971 |
951 |
| 972 // LCOV_EXCL_START |
952 // LCOV_EXCL_START |
| 973 // never called as long as malloc() does not return NULL |
953 // never called as long as malloc() does not return NULL |
| 1039 cxFreeDefault(values); |
1019 cxFreeDefault(values); |
| 1040 return ret; |
1020 return ret; |
| 1041 } |
1021 } |
| 1042 |
1022 |
| 1043 int cxJsonArrAddValues(CxJsonValue* arr, CxJsonValue* const* val, size_t count) { |
1023 int cxJsonArrAddValues(CxJsonValue* arr, CxJsonValue* const* val, size_t count) { |
| 1044 CxArrayReallocator value_realloc = cx_array_reallocator(arr->allocator, NULL); |
|
| 1045 assert(arr->type == CX_JSON_ARRAY); |
1024 assert(arr->type == CX_JSON_ARRAY); |
| 1046 return cx_array_simple_copy_a(&value_realloc, |
1025 return cx_array_add_array_a(arr->allocator, arr->array, val, count); |
| 1047 arr->value.array.array, |
1026 } |
| 1048 arr->value.array.array_size, |
1027 |
| 1049 val, count |
1028 int cx_json_obj_put(CxJsonValue* obj, cxstring name, CxJsonValue* child) { |
| 1050 ); |
1029 return cxMapPut(obj->object, name, child); |
| 1051 } |
1030 } |
| 1052 |
1031 |
| 1053 int cxJsonObjPut(CxJsonValue* obj, cxstring name, CxJsonValue* child) { |
1032 CxJsonValue* cx_json_obj_put_obj(CxJsonValue* obj, cxstring name) { |
| 1054 cxmutstr k = cx_strdup_a(obj->allocator, name); |
|
| 1055 if (k.ptr == NULL) return -1; |
|
| 1056 CxJsonObjValue kv = {k, child}; |
|
| 1057 if (json_add_objvalue(obj, kv)) { |
|
| 1058 cx_strfree_a(obj->allocator, &k); |
|
| 1059 return 1; |
|
| 1060 } else { |
|
| 1061 return 0; |
|
| 1062 } |
|
| 1063 } |
|
| 1064 |
|
| 1065 CxJsonValue* cxJsonObjPutObj(CxJsonValue* obj, cxstring name) { |
|
| 1066 CxJsonValue* v = cxJsonCreateObj(obj->allocator); |
1033 CxJsonValue* v = cxJsonCreateObj(obj->allocator); |
| 1067 if (v == NULL) return NULL; |
1034 if (v == NULL) return NULL; |
| 1068 if (cxJsonObjPut(obj, name, v)) { cxJsonValueFree(v); return NULL; } |
1035 if (cxJsonObjPut(obj, name, v)) { cxJsonValueFree(v); return NULL; } |
| 1069 return v; |
1036 return v; |
| 1070 } |
1037 } |
| 1071 |
1038 |
| 1072 CxJsonValue* cxJsonObjPutArr(CxJsonValue* obj, cxstring name) { |
1039 CxJsonValue* cx_json_obj_put_arr(CxJsonValue* obj, cxstring name, size_t capacity) { |
| 1073 CxJsonValue* v = cxJsonCreateArr(obj->allocator); |
1040 CxJsonValue* v = cxJsonCreateArr(obj->allocator, capacity); |
| 1074 if (v == NULL) return NULL; |
1041 if (v == NULL) return NULL; |
| 1075 if (cxJsonObjPut(obj, name, v)) { cxJsonValueFree(v); return NULL; } |
1042 if (cxJsonObjPut(obj, name, v)) { cxJsonValueFree(v); return NULL; } |
| 1076 return v; |
1043 return v; |
| 1077 } |
1044 } |
| 1078 |
1045 |
| 1079 CxJsonValue* cxJsonObjPutNumber(CxJsonValue* obj, cxstring name, double num) { |
1046 CxJsonValue* cx_json_obj_put_number(CxJsonValue* obj, cxstring name, double num) { |
| 1080 CxJsonValue* v = cxJsonCreateNumber(obj->allocator, num); |
1047 CxJsonValue* v = cxJsonCreateNumber(obj->allocator, num); |
| 1081 if (v == NULL) return NULL; |
1048 if (v == NULL) return NULL; |
| 1082 if (cxJsonObjPut(obj, name, v)) { cxJsonValueFree(v); return NULL; } |
1049 if (cxJsonObjPut(obj, name, v)) { cxJsonValueFree(v); return NULL; } |
| 1083 return v; |
1050 return v; |
| 1084 } |
1051 } |
| 1085 |
1052 |
| 1086 CxJsonValue* cxJsonObjPutInteger(CxJsonValue* obj, cxstring name, int64_t num) { |
1053 CxJsonValue* cx_json_obj_put_integer(CxJsonValue* obj, cxstring name, int64_t num) { |
| 1087 CxJsonValue* v = cxJsonCreateInteger(obj->allocator, num); |
1054 CxJsonValue* v = cxJsonCreateInteger(obj->allocator, num); |
| 1088 if (v == NULL) return NULL; |
1055 if (v == NULL) return NULL; |
| 1089 if (cxJsonObjPut(obj, name, v)) { cxJsonValueFree(v); return NULL; } |
1056 if (cxJsonObjPut(obj, name, v)) { cxJsonValueFree(v); return NULL; } |
| 1090 return v; |
1057 return v; |
| 1091 } |
1058 } |
| 1092 |
1059 |
| 1093 CxJsonValue* cxJsonObjPutString(CxJsonValue* obj, cxstring name, const char* str) { |
1060 CxJsonValue* cx_json_obj_put_string(CxJsonValue* obj, cxstring name, cxstring str) { |
| 1094 CxJsonValue* v = cxJsonCreateString(obj->allocator, str); |
1061 CxJsonValue* v = cxJsonCreateString(obj->allocator, str); |
| 1095 if (v == NULL) return NULL; |
1062 if (v == NULL) return NULL; |
| 1096 if (cxJsonObjPut(obj, name, v)) { cxJsonValueFree(v); return NULL; } |
1063 if (cxJsonObjPut(obj, name, v)) { cxJsonValueFree(v); return NULL; } |
| 1097 return v; |
1064 return v; |
| 1098 } |
1065 } |
| 1099 |
1066 |
| 1100 CxJsonValue* cxJsonObjPutCxString(CxJsonValue* obj, cxstring name, cxstring str) { |
1067 CxJsonValue* cx_json_obj_put_literal(CxJsonValue* obj, cxstring name, CxJsonLiteral lit) { |
| 1101 CxJsonValue* v = cxJsonCreateCxString(obj->allocator, str); |
|
| 1102 if (v == NULL) return NULL; |
|
| 1103 if (cxJsonObjPut(obj, name, v)) { cxJsonValueFree(v); return NULL; } |
|
| 1104 return v; |
|
| 1105 } |
|
| 1106 |
|
| 1107 CxJsonValue* cxJsonObjPutLiteral(CxJsonValue* obj, cxstring name, CxJsonLiteral lit) { |
|
| 1108 CxJsonValue* v = cxJsonCreateLiteral(obj->allocator, lit); |
1068 CxJsonValue* v = cxJsonCreateLiteral(obj->allocator, lit); |
| 1109 if (v == NULL) return NULL; |
1069 if (v == NULL) return NULL; |
| 1110 if (cxJsonObjPut(obj, name, v)) { cxJsonValueFree(v); return NULL;} |
1070 if (cxJsonObjPut(obj, name, v)) { cxJsonValueFree(v); return NULL;} |
| 1111 return v; |
1071 return v; |
| 1112 } |
1072 } |
| 1113 |
1073 |
| 1114 CxJsonValue *cxJsonArrGet(const CxJsonValue *value, size_t index) { |
1074 CxJsonValue *cxJsonArrGet(const CxJsonValue *value, size_t index) { |
| 1115 if (index >= value->value.array.array_size) { |
1075 if (index >= value->array.size) { |
| 1116 return &cx_json_value_nothing; |
1076 return &cx_json_value_nothing; |
| 1117 } |
1077 } |
| 1118 return value->value.array.array[index]; |
1078 return value->array.data[index]; |
| 1119 } |
1079 } |
| 1120 |
1080 |
| 1121 CxJsonValue *cxJsonArrRemove(CxJsonValue *value, size_t index) { |
1081 CxJsonValue *cxJsonArrRemove(CxJsonValue *value, size_t index) { |
| 1122 if (index >= value->value.array.array_size) { |
1082 if (index >= value->array.size) { |
| 1123 return NULL; |
1083 return NULL; |
| 1124 } |
1084 } |
| 1125 CxJsonValue *ret = value->value.array.array[index]; |
1085 CxJsonValue *ret = value->array.data[index]; |
| 1126 // TODO: replace with a low level cx_array_remove() |
1086 cx_array_remove(value->array, index); |
| 1127 size_t count = value->value.array.array_size - index - 1; |
|
| 1128 if (count > 0) { |
|
| 1129 memmove(value->value.array.array + index, value->value.array.array + index + 1, count * sizeof(CxJsonValue*)); |
|
| 1130 } |
|
| 1131 value->value.array.array_size--; |
|
| 1132 return ret; |
1087 return ret; |
| 1133 } |
1088 } |
| 1134 |
1089 |
| 1135 char *cxJsonAsString(const CxJsonValue *value) { |
1090 char *cxJsonAsString(const CxJsonValue *value) { |
| 1136 return value->value.string.ptr; |
1091 return value->string.ptr; |
| 1137 } |
1092 } |
| 1138 |
1093 |
| 1139 cxstring cxJsonAsCxString(const CxJsonValue *value) { |
1094 cxstring cxJsonAsCxString(const CxJsonValue *value) { |
| 1140 return cx_strcast(value->value.string); |
1095 return cx_strcast(value->string); |
| 1141 } |
1096 } |
| 1142 |
1097 |
| 1143 cxmutstr cxJsonAsCxMutStr(const CxJsonValue *value) { |
1098 cxmutstr cxJsonAsCxMutStr(const CxJsonValue *value) { |
| 1144 return value->value.string; |
1099 return value->string; |
| 1145 } |
1100 } |
| 1146 |
1101 |
| 1147 double cxJsonAsDouble(const CxJsonValue *value) { |
1102 double cxJsonAsDouble(const CxJsonValue *value) { |
| 1148 if (value->type == CX_JSON_INTEGER) { |
1103 if (value->type == CX_JSON_INTEGER) { |
| 1149 return (double) value->value.integer; |
1104 return (double) value->integer; |
| 1150 } else { |
1105 } else { |
| 1151 return value->value.number; |
1106 return value->number; |
| 1152 } |
1107 } |
| 1153 } |
1108 } |
| 1154 |
1109 |
| 1155 int64_t cxJsonAsInteger(const CxJsonValue *value) { |
1110 int64_t cxJsonAsInteger(const CxJsonValue *value) { |
| 1156 if (value->type == CX_JSON_INTEGER) { |
1111 if (value->type == CX_JSON_INTEGER) { |
| 1157 return value->value.integer; |
1112 return value->integer; |
| 1158 } else { |
1113 } else { |
| 1159 return (int64_t) value->value.number; |
1114 return (int64_t) value->number; |
| 1160 } |
1115 } |
| 1161 } |
1116 } |
| 1162 |
1117 |
| 1163 CxIterator cxJsonArrIter(const CxJsonValue *value) { |
1118 CxIterator cxJsonArrIter(const CxJsonValue *value) { |
| 1164 return cxIteratorPtr( |
1119 return cx_array_iterator_ptr(value->array); |
| 1165 value->value.array.array, |
1120 } |
| 1166 value->value.array.array_size, |
1121 |
| 1167 true // arrays need to keep order |
1122 CxMapIterator cxJsonObjIter(const CxJsonValue *value) { |
| 1168 ); |
1123 return cxMapIterator(value->object); |
| 1169 } |
|
| 1170 |
|
| 1171 CxIterator cxJsonObjIter(const CxJsonValue *value) { |
|
| 1172 return cxIterator( |
|
| 1173 value->value.object.values, |
|
| 1174 sizeof(CxJsonObjValue), |
|
| 1175 value->value.object.values_size, |
|
| 1176 true // TODO: objects do not always need to keep order |
|
| 1177 ); |
|
| 1178 } |
1124 } |
| 1179 |
1125 |
| 1180 CxJsonValue *cx_json_obj_get(const CxJsonValue *value, cxstring name) { |
1126 CxJsonValue *cx_json_obj_get(const CxJsonValue *value, cxstring name) { |
| 1181 size_t index = json_find_objvalue(value, name); |
1127 CxJsonValue *v = cxMapGet(value->object, name); |
| 1182 if (index >= value->value.object.values_size) { |
1128 if (v == NULL) { |
| 1183 return &cx_json_value_nothing; |
1129 return &cx_json_value_nothing; |
| 1184 } else { |
1130 } else { |
| 1185 return value->value.object.values[index].value; |
1131 return v; |
| 1186 } |
1132 } |
| 1187 } |
1133 } |
| 1188 |
1134 |
| 1189 CxJsonValue *cx_json_obj_remove(CxJsonValue *value, cxstring name) { |
1135 CxJsonValue *cx_json_obj_remove(CxJsonValue *value, cxstring name) { |
| 1190 size_t index = json_find_objvalue(value, name); |
1136 CxJsonValue *v = NULL; |
| 1191 if (index >= value->value.object.values_size) { |
1137 cxMapRemoveAndGet(value->object, name, &v); |
| 1192 return NULL; |
1138 return v; |
| 1193 } else { |
|
| 1194 CxJsonObjValue kv = value->value.object.values[index]; |
|
| 1195 cx_strfree_a(value->allocator, &kv.name); |
|
| 1196 // TODO: replace with cx_array_remove() / cx_array_remove_fast() |
|
| 1197 value->value.object.values_size--; |
|
| 1198 memmove(value->value.object.values + index, value->value.object.values + index + 1, (value->value.object.values_size - index) * sizeof(CxJsonObjValue)); |
|
| 1199 return kv.value; |
|
| 1200 } |
|
| 1201 } |
1139 } |
| 1202 |
1140 |
| 1203 CxJsonWriter cxJsonWriterCompact(void) { |
1141 CxJsonWriter cxJsonWriterCompact(void) { |
| 1204 return (CxJsonWriter) { |
1142 return (CxJsonWriter) { |
| 1205 false, |
1143 false, |
| 1206 true, |
|
| 1207 6, |
1144 6, |
| 1208 false, |
1145 false, |
| 1209 4, |
1146 4, |
| 1210 false |
1147 false |
| 1211 }; |
1148 }; |
| 1212 } |
1149 } |
| 1213 |
1150 |
| 1214 CxJsonWriter cxJsonWriterPretty(bool use_spaces) { |
1151 CxJsonWriter cxJsonWriterPretty(bool use_spaces) { |
| 1215 return (CxJsonWriter) { |
1152 return (CxJsonWriter) { |
| 1216 true, |
|
| 1217 true, |
1153 true, |
| 1218 6, |
1154 6, |
| 1219 use_spaces, |
1155 use_spaces, |
| 1220 4, |
1156 4, |
| 1221 false |
1157 false |
| 1277 } else { |
1213 } else { |
| 1278 actual += wfunc(begin_obj, 1, 1, target); |
1214 actual += wfunc(begin_obj, 1, 1, target); |
| 1279 expected++; |
1215 expected++; |
| 1280 } |
1216 } |
| 1281 depth++; |
1217 depth++; |
| 1282 size_t elem_count = value->value.object.values_size; |
1218 CxMapIterator member_iter = cxJsonObjIter(value); |
| 1283 for (size_t look_idx = 0; look_idx < elem_count; look_idx++) { |
1219 cx_foreach(const CxMapEntry *, member, member_iter) { |
| 1284 // get the member either via index array or directly |
|
| 1285 size_t elem_idx = settings->sort_members |
|
| 1286 ? look_idx |
|
| 1287 : value->value.object.indices[look_idx]; |
|
| 1288 CxJsonObjValue *member = &value->value.object.values[elem_idx]; |
|
| 1289 if (settings->sort_members) { |
|
| 1290 depth++;depth--; |
|
| 1291 } |
|
| 1292 |
|
| 1293 // possible indentation |
1220 // possible indentation |
| 1294 if (settings->pretty) { |
1221 if (settings->pretty) { |
| 1295 if (cx_json_writer_indent(target, wfunc, settings, depth)) { |
1222 if (cx_json_writer_indent(target, wfunc, settings, depth)) { |
| 1296 return 1; // LCOV_EXCL_LINE |
1223 return 1; // LCOV_EXCL_LINE |
| 1297 } |
1224 } |
| 1298 } |
1225 } |
| 1299 |
1226 |
| 1300 // the name |
1227 // the name |
| 1301 actual += wfunc("\"", 1, 1, target); |
1228 actual += wfunc("\"", 1, 1, target); |
| 1302 cxmutstr name = escape_string(member->name, settings->escape_slash); |
1229 cxstring key = cx_strn(member->key->data, member->key->len); |
| |
1230 cxmutstr name = escape_string(key, settings->escape_slash); |
| 1303 actual += wfunc(name.ptr, 1, name.length, target); |
1231 actual += wfunc(name.ptr, 1, name.length, target); |
| 1304 if (name.ptr != member->name.ptr) { |
|
| 1305 cx_strfree(&name); |
|
| 1306 } |
|
| 1307 actual += wfunc("\"", 1, 1, target); |
1232 actual += wfunc("\"", 1, 1, target); |
| 1308 const char *obj_name_sep = ": "; |
1233 const char *obj_name_sep = ": "; |
| 1309 if (settings->pretty) { |
1234 if (settings->pretty) { |
| 1310 actual += wfunc(obj_name_sep, 1, 2, target); |
1235 actual += wfunc(obj_name_sep, 1, 2, target); |
| 1311 expected += 4 + member->name.length; |
1236 expected += 4 + name.length; |
| 1312 } else { |
1237 } else { |
| 1313 actual += wfunc(obj_name_sep, 1, 1, target); |
1238 actual += wfunc(obj_name_sep, 1, 1, target); |
| 1314 expected += 3 + member->name.length; |
1239 expected += 3 + name.length; |
| |
1240 } |
| |
1241 if (name.ptr != key.ptr) { |
| |
1242 cx_strfree(&name); |
| 1315 } |
1243 } |
| 1316 |
1244 |
| 1317 // the value |
1245 // the value |
| 1318 if (cx_json_write_rec(target, member->value, wfunc, settings, depth)) return 1; |
1246 if (cx_json_write_rec(target, member->value, wfunc, settings, depth)) return 1; |
| 1319 |
1247 |
| 1320 // end of object-value |
1248 // end of object-value |
| 1321 if (look_idx < elem_count - 1) { |
1249 if (member_iter.index < member_iter.elem_count - 1) { |
| 1322 const char *obj_value_sep = ",\n"; |
1250 const char *obj_value_sep = ",\n"; |
| 1323 if (settings->pretty) { |
1251 if (settings->pretty) { |
| 1324 actual += wfunc(obj_value_sep, 1, 2, target); |
1252 actual += wfunc(obj_value_sep, 1, 2, target); |
| 1325 expected += 2; |
1253 expected += 2; |
| 1326 } else { |
1254 } else { |
| 1367 expected++; |
1297 expected++; |
| 1368 break; |
1298 break; |
| 1369 } |
1299 } |
| 1370 case CX_JSON_STRING: { |
1300 case CX_JSON_STRING: { |
| 1371 actual += wfunc("\"", 1, 1, target); |
1301 actual += wfunc("\"", 1, 1, target); |
| 1372 cxmutstr str = escape_string(value->value.string, settings->escape_slash); |
1302 cxmutstr str = escape_string(cx_strcast(value->string), |
| |
1303 settings->escape_slash); |
| 1373 actual += wfunc(str.ptr, 1, str.length, target); |
1304 actual += wfunc(str.ptr, 1, str.length, target); |
| 1374 if (str.ptr != value->value.string.ptr) { |
1305 actual += wfunc("\"", 1, 1, target); |
| |
1306 expected += 2 + str.length; |
| |
1307 if (str.ptr != value->string.ptr) { |
| 1375 cx_strfree(&str); |
1308 cx_strfree(&str); |
| 1376 } |
1309 } |
| 1377 actual += wfunc("\"", 1, 1, target); |
|
| 1378 expected += 2 + value->value.string.length; |
|
| 1379 break; |
1310 break; |
| 1380 } |
1311 } |
| 1381 case CX_JSON_NUMBER: { |
1312 case CX_JSON_NUMBER: { |
| 1382 int precision = settings->frac_max_digits; |
1313 int precision = settings->frac_max_digits; |
| 1383 // because of the way how %g is defined, we need to |
1314 // because of the way how %g is defined, we need to |
| 1384 // double the precision and truncate ourselves |
1315 // double the precision and truncate ourselves |
| 1385 precision = 1 + (precision > 15 ? 30 : 2 * precision); |
1316 precision = 1 + (precision > 15 ? 30 : 2 * precision); |
| 1386 snprintf(numbuf, 40, "%.*g", precision, value->value.number); |
1317 snprintf(numbuf, 40, "%.*g", precision, value->number); |
| 1387 char *dot, *exp; |
1318 char *dot, *exp; |
| 1388 unsigned char max_digits; |
1319 unsigned char max_digits; |
| 1389 // find the decimal separator and hope that it's one of . or , |
1320 // find the decimal separator and hope that it's one of . or , |
| 1390 dot = strchr(numbuf, '.'); |
1321 dot = strchr(numbuf, '.'); |
| 1391 if (dot == NULL) { |
1322 if (dot == NULL) { |
| 1493 if (settings == NULL) { |
1424 if (settings == NULL) { |
| 1494 settings = &writer_default; |
1425 settings = &writer_default; |
| 1495 } |
1426 } |
| 1496 return cx_json_write_rec(target, value, wfunc, settings, 0); |
1427 return cx_json_write_rec(target, value, wfunc, settings, 0); |
| 1497 } |
1428 } |
| |
1429 |
| |
1430 static cxmutstr cx_json_to_string(CxJsonValue *value, const CxAllocator *allocator, CxJsonWriter *writer) { |
| |
1431 if (allocator == NULL) allocator = cxDefaultAllocator; |
| |
1432 CxBuffer buffer; |
| |
1433 if (cxBufferInit(&buffer, allocator, NULL, 128, |
| |
1434 CX_BUFFER_AUTO_EXTEND | CX_BUFFER_DO_NOT_FREE)) { |
| |
1435 return (cxmutstr){NULL, 0}; |
| |
1436 } |
| |
1437 if (cx_json_write_rec(&buffer, value, cxBufferWriteFunc, writer, 0) |
| |
1438 || cxBufferTerminate(&buffer)) { |
| |
1439 // LCOV_EXCL_START |
| |
1440 buffer.flags &= ~CX_BUFFER_DO_NOT_FREE; |
| |
1441 cxBufferDestroy(&buffer); |
| |
1442 return (cxmutstr){NULL, 0}; |
| |
1443 // LCOV_EXCL_STOP |
| |
1444 } else { |
| |
1445 cxmutstr str = cx_mutstrn(buffer.space, buffer.size); |
| |
1446 cxBufferDestroy(&buffer); |
| |
1447 return str; |
| |
1448 } |
| |
1449 |
| |
1450 } |
| |
1451 |
| |
1452 cxmutstr cxJsonToString(const CxAllocator *allocator, CxJsonValue *value) { |
| |
1453 CxJsonWriter writer = cxJsonWriterCompact(); |
| |
1454 return cx_json_to_string(value, allocator, &writer); |
| |
1455 } |
| |
1456 |
| |
1457 cxmutstr cxJsonToPrettyString(const CxAllocator *allocator, CxJsonValue *value) { |
| |
1458 CxJsonWriter writer = cxJsonWriterPretty(true); |
| |
1459 return cx_json_to_string(value, allocator, &writer); |
| |
1460 } |
| |
1461 |
| |
1462 int cxJsonCompare(const CxJsonValue *json, const CxJsonValue *other) { |
| |
1463 if (json == other) return 0; |
| |
1464 if (json == NULL || other == NULL) return -1; |
| |
1465 if (json->type != other->type) { |
| |
1466 if (!cxJsonIsNumber(json)) return -1; |
| |
1467 if (!cxJsonIsNumber(other)) return -1; |
| |
1468 } |
| |
1469 switch (json->type) { |
| |
1470 case CX_JSON_NOTHING: |
| |
1471 return 0; |
| |
1472 case CX_JSON_OBJECT: |
| |
1473 return cxMapCompare(json->object, other->object); |
| |
1474 case CX_JSON_ARRAY: |
| |
1475 if (json->array.size != other->array.size) return -1; |
| |
1476 for (size_t i = 0; i < json->array.size; i++) { |
| |
1477 const int d = cxJsonCompare(json->array.data[i], other->array.data[i]); |
| |
1478 if (d != 0) return d; |
| |
1479 } |
| |
1480 return 0; |
| |
1481 case CX_JSON_STRING: |
| |
1482 return cx_strcmp(json->string, other->string); |
| |
1483 case CX_JSON_INTEGER: |
| |
1484 if (other->type == CX_JSON_INTEGER) { |
| |
1485 return cx_vcmp_int64(json->integer, other->integer); |
| |
1486 } else { |
| |
1487 return cx_vcmp_double(cxJsonAsDouble(json), other->number); |
| |
1488 } |
| |
1489 case CX_JSON_NUMBER: |
| |
1490 return cx_vcmp_double(json->number, cxJsonAsDouble(other)); |
| |
1491 case CX_JSON_LITERAL: |
| |
1492 return json->literal == other->literal ? 0 : -1; |
| |
1493 default: |
| |
1494 // LCOV_EXCL_START |
| |
1495 // unreachable |
| |
1496 assert(false); |
| |
1497 return -1; |
| |
1498 // LCOV_EXCL_STOP |
| |
1499 } |
| |
1500 } |
| |
1501 |
| |
1502 CxJsonValue* cxJsonClone(const CxJsonValue* value, const CxAllocator* allocator) { |
| |
1503 return cx_json_clone_func(NULL, value, allocator, NULL); |
| |
1504 } |
| |
1505 |
| |
1506 CxJsonValue* cx_json_clone_func(CxJsonValue* target, const CxJsonValue* source, |
| |
1507 const CxAllocator* allocator, cx_attr_unused void *data) { |
| |
1508 if (source == NULL || source->type == CX_JSON_NOTHING) { |
| |
1509 return &cx_json_value_nothing; |
| |
1510 } |
| |
1511 if (allocator == NULL) allocator = cxDefaultAllocator; |
| |
1512 |
| |
1513 #define return_value(v) { \ |
| |
1514 CxJsonValue *ret = v; \ |
| |
1515 if (target == NULL) { \ |
| |
1516 return ret; \ |
| |
1517 } else { \ |
| |
1518 *target = *ret; \ |
| |
1519 cxFree(allocator, ret); \ |
| |
1520 return target; \ |
| |
1521 } \ |
| |
1522 } |
| |
1523 |
| |
1524 switch (source->type) { |
| |
1525 case CX_JSON_OBJECT: { |
| |
1526 CxJsonValue *obj = cxJsonCreateObj(allocator); |
| |
1527 if (obj == NULL) return NULL; // LCOV_EXCL_LINE |
| |
1528 if (cxMapClone(obj->object, source->object, cxJsonCloneFunc, allocator, NULL)) { |
| |
1529 // LCOV_EXCL_START |
| |
1530 cxJsonValueFree(obj); |
| |
1531 return NULL; |
| |
1532 // LCOV_EXCL_STOP |
| |
1533 } |
| |
1534 return_value(obj); |
| |
1535 } |
| |
1536 case CX_JSON_ARRAY: { |
| |
1537 const size_t elem_count = source->array.size; |
| |
1538 CxJsonValue *arr = cxJsonCreateArr(allocator, elem_count); |
| |
1539 if (arr == NULL) return NULL; // LCOV_EXCL_LINE |
| |
1540 arr->array.size = elem_count; |
| |
1541 for (size_t i = 0 ; i < elem_count ; i++) { |
| |
1542 CxJsonValue *e = cx_json_clone_func(NULL, source->array.data[i], allocator, NULL); |
| |
1543 if (e == NULL) { |
| |
1544 // LCOV_EXCL_START |
| |
1545 cxJsonValueFree(arr); |
| |
1546 return NULL; |
| |
1547 // LCOV_EXCL_STOP |
| |
1548 } |
| |
1549 arr->array.data[i] = e; |
| |
1550 } |
| |
1551 return_value(arr); |
| |
1552 } |
| |
1553 case CX_JSON_STRING: |
| |
1554 return_value(cxJsonCreateString(allocator, source->string)); |
| |
1555 case CX_JSON_INTEGER: |
| |
1556 return_value(cxJsonCreateInteger(allocator, source->integer)); |
| |
1557 case CX_JSON_NUMBER: |
| |
1558 return_value(cxJsonCreateNumber(allocator, source->number)); |
| |
1559 case CX_JSON_LITERAL: |
| |
1560 return_value(cxJsonCreateLiteral(allocator, source->literal)); |
| |
1561 default: |
| |
1562 // LCOV_EXCL_START |
| |
1563 // unreachable |
| |
1564 assert(false); |
| |
1565 return NULL; |
| |
1566 // LCOV_EXCL_STOP |
| |
1567 } |
| |
1568 #undef return_value |
| |
1569 } |