| 408 |
408 |
| 409 if (all_printable && escape) { |
409 if (all_printable && escape) { |
| 410 size_t capa = str.length + 32; |
410 size_t capa = str.length + 32; |
| 411 char *space = cxMallocDefault(capa); |
411 char *space = cxMallocDefault(capa); |
| 412 if (space == NULL) return cx_mutstrn(NULL, 0); |
412 if (space == NULL) return cx_mutstrn(NULL, 0); |
| 413 cxBufferInit(&buf, space, capa, NULL, CX_BUFFER_AUTO_EXTEND); |
413 cxBufferInit(&buf, NULL, space, capa, CX_BUFFER_AUTO_EXTEND); |
| 414 cxBufferWrite(str.ptr, 1, i, &buf); |
414 cxBufferWrite(str.ptr, 1, i, &buf); |
| 415 all_printable = false; |
415 all_printable = false; |
| 416 } |
416 } |
| 417 if (escape) { |
417 if (escape) { |
| 418 cxBufferPut(&buf, '\\'); |
418 cxBufferPut(&buf, '\\'); |
| 470 |
470 |
| 471 // initialize the value |
471 // initialize the value |
| 472 v->type = type; |
472 v->type = type; |
| 473 v->allocator = json->allocator; |
473 v->allocator = json->allocator; |
| 474 if (type == CX_JSON_ARRAY) { |
474 if (type == CX_JSON_ARRAY) { |
| 475 cx_array_initialize_a(json->allocator, v->array.data, 16); |
475 if (cx_array_init_a(json->allocator, v->array, 16)) { |
| 476 if (v->array.data == NULL) goto create_json_value_exit_error; // LCOV_EXCL_LINE |
476 goto create_json_value_exit_error; // LCOV_EXCL_LINE |
| |
477 } |
| 477 } else if (type == CX_JSON_OBJECT) { |
478 } else if (type == CX_JSON_OBJECT) { |
| 478 v->object = json_create_object_map(json->allocator); |
479 v->object = json_create_object_map(json->allocator); |
| 479 if (v->object == NULL) goto create_json_value_exit_error; // LCOV_EXCL_LINE |
480 if (v->object == NULL) goto create_json_value_exit_error; // LCOV_EXCL_LINE |
| 480 } |
481 } |
| 481 |
482 |
| 482 // add the new value to a possible parent |
483 // add the new value to a possible parent |
| 483 if (json->vbuf_size > 0) { |
484 if (json->vbuf.size > 0) { |
| 484 CxJsonValue *parent = json->vbuf[json->vbuf_size - 1]; |
485 CxJsonValue *parent = json->vbuf.data[json->vbuf.size - 1]; |
| 485 assert(parent != NULL); |
486 assert(parent != NULL); |
| 486 if (parent->type == CX_JSON_ARRAY) { |
487 if (parent->type == CX_JSON_ARRAY) { |
| 487 CxArrayReallocator value_realloc = cx_array_reallocator(json->allocator, NULL); |
488 if (cx_array_add_a(json->allocator, parent->array, v)) { |
| 488 if (cx_array_simple_add_a(&value_realloc, parent->array.data, v)) { |
|
| 489 goto create_json_value_exit_error; // LCOV_EXCL_LINE |
489 goto create_json_value_exit_error; // LCOV_EXCL_LINE |
| 490 } |
490 } |
| 491 } else if (parent->type == CX_JSON_OBJECT) { |
491 } else if (parent->type == CX_JSON_OBJECT) { |
| 492 // the member was already created after parsing the name |
492 // the member was already created after parsing the name |
| 493 // store the pointer of the uncompleted value in the map |
493 // store the pointer of the uncompleted value in the map |
| 501 } |
501 } |
| 502 } |
502 } |
| 503 |
503 |
| 504 // 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 |
| 505 if (type == CX_JSON_ARRAY || type == CX_JSON_OBJECT) { |
505 if (type == CX_JSON_ARRAY || type == CX_JSON_OBJECT) { |
| 506 CxArrayReallocator vbuf_realloc = cx_array_reallocator(NULL, json->vbuf_internal); |
506 if (json->vbuf.size >= json->vbuf.capacity) { |
| 507 if (cx_array_simple_add_a(&vbuf_realloc, json->vbuf, v)) { |
507 int alloc_error; |
| 508 goto create_json_value_exit_error; // LCOV_EXCL_LINE |
508 if (json->vbuf.data == json->vbuf_internal) { |
| 509 } |
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++; |
| 510 } |
519 } |
| 511 |
520 |
| 512 // 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 |
| 513 if (json->parsed == NULL) { |
522 if (json->parsed == NULL) { |
| 514 json->parsed = v; |
523 json->parsed = v; |
| 538 } |
547 } |
| 539 |
548 |
| 540 memset(json, 0, sizeof(CxJson)); |
549 memset(json, 0, sizeof(CxJson)); |
| 541 json->allocator = allocator; |
550 json->allocator = allocator; |
| 542 |
551 |
| 543 json->states = json->states_internal; |
552 cx_array_init_fixed(json->states, json->states_internal, 1); |
| 544 json->states_capacity = cx_nmemb(json->states_internal); |
553 json->states.data[0] = JP_STATE_VALUE_BEGIN; |
| 545 json->states[0] = JP_STATE_VALUE_BEGIN; |
554 cx_array_init_fixed(json->vbuf, json->vbuf_internal, 0); |
| 546 json->states_size = 1; |
|
| 547 |
|
| 548 json->vbuf = json->vbuf_internal; |
|
| 549 json->vbuf_capacity = cx_nmemb(json->vbuf_internal); |
|
| 550 } |
555 } |
| 551 |
556 |
| 552 void cxJsonDestroy(CxJson *json) { |
557 void cxJsonDestroy(CxJson *json) { |
| 553 cxBufferDestroy(&json->buffer); |
558 cxBufferDestroy(&json->buffer); |
| 554 if (json->states != json->states_internal) { |
559 if (json->states.data != json->states_internal) { |
| 555 cxFreeDefault(json->states); |
560 cx_array_free(json->states); |
| 556 } |
561 } |
| 557 if (json->vbuf != json->vbuf_internal) { |
562 if (json->vbuf.data != json->vbuf_internal) { |
| 558 cxFreeDefault(json->vbuf); |
563 cx_array_free(json->vbuf); |
| 559 } |
564 } |
| 560 cxJsonValueFree(json->parsed); |
565 cxJsonValueFree(json->parsed); |
| 561 json->parsed = NULL; |
566 json->parsed = NULL; |
| 562 token_destroy(&json->uncompleted); |
567 token_destroy(&json->uncompleted); |
| 563 cx_strfree_a(json->allocator, &json->uncompleted_member_name); |
568 cx_strfree_a(json->allocator, &json->uncompleted_member_name); |
| 572 int cxJsonFilln(CxJson *json, const char *buf, size_t size) { |
577 int cxJsonFilln(CxJson *json, const char *buf, size_t size) { |
| 573 if (cxBufferEof(&json->buffer)) { |
578 if (cxBufferEof(&json->buffer)) { |
| 574 // reinitialize the buffer |
579 // reinitialize the buffer |
| 575 cxBufferDestroy(&json->buffer); |
580 cxBufferDestroy(&json->buffer); |
| 576 if (buf == NULL) buf = ""; // buffer must not be initialized with NULL |
581 if (buf == NULL) buf = ""; // buffer must not be initialized with NULL |
| 577 cxBufferInit(&json->buffer, (char*) buf, size, |
582 cxBufferInit(&json->buffer, NULL, (char*) buf, |
| 578 NULL, CX_BUFFER_AUTO_EXTEND | CX_BUFFER_COPY_ON_WRITE); |
583 size, CX_BUFFER_AUTO_EXTEND | CX_BUFFER_COPY_ON_WRITE); |
| 579 json->buffer.size = size; |
584 json->buffer.size = size; |
| 580 return 0; |
585 return 0; |
| 581 } else { |
586 } else { |
| 582 return size != cxBufferAppend(buf, 1, size, &json->buffer); |
587 return size != cxBufferAppend(buf, 1, size, &json->buffer); |
| 583 } |
588 } |
| 584 } |
589 } |
| 585 |
590 |
| 586 static void json_add_state(CxJson *json, int state) { |
591 static void json_add_state(CxJson *json, int state) { |
| 587 // we have guaranteed the necessary space with cx_array_simple_reserve() |
592 // we have guaranteed the necessary space |
| 588 // therefore, we can safely add the state in the simplest way possible |
593 // therefore, we can safely add the state in the simplest way possible |
| 589 json->states[json->states_size++] = state; |
594 json->states.data[json->states.size++] = state; |
| 590 } |
595 } |
| 591 |
596 |
| 592 #define return_rec(code) \ |
597 #define return_rec(code) \ |
| 593 token_destroy(&token); \ |
598 token_destroy(&token); \ |
| 594 return code |
599 return code |
| 605 return ret; |
610 return ret; |
| 606 } |
611 } |
| 607 } |
612 } |
| 608 |
613 |
| 609 // pop the current state |
614 // pop the current state |
| 610 assert(json->states_size > 0); |
615 assert(json->states.size > 0); |
| 611 int state = json->states[--json->states_size]; |
616 int state = json->states.data[--json->states.size]; |
| 612 |
617 |
| 613 // guarantee that at least two more states fit on the stack |
618 // guarantee that at least two more states fit into the array |
| 614 CxArrayReallocator state_realloc = cx_array_reallocator(NULL, json->states_internal); |
619 const size_t required_states_depth = json->states.size + 2; |
| 615 if (cx_array_simple_reserve_a(&state_realloc, json->states, 2)) { |
620 if (required_states_depth >= json->states.capacity) { |
| 616 return CX_JSON_BUFFER_ALLOC_FAILED; // LCOV_EXCL_LINE |
621 int alloc_error; |
| |
622 if (json->states.data == json->states_internal) { |
| |
623 alloc_error = cx_array_copy_to_new(json->states, required_states_depth); |
| |
624 } else { |
| |
625 alloc_error = cx_array_reserve(json->states, required_states_depth); |
| |
626 } |
| |
627 if (alloc_error) { |
| |
628 return CX_JSON_BUFFER_ALLOC_FAILED; // LCOV_EXCL_LINE |
| |
629 } |
| 617 } |
630 } |
| 618 |
631 |
| 619 |
632 |
| 620 // 0 JP_STATE_VALUE_BEGIN value begin |
633 // 0 JP_STATE_VALUE_BEGIN value begin |
| 621 // 10 JP_STATE_VALUE_END expect value end |
634 // 10 JP_STATE_VALUE_END expect value end |
| 643 return_rec(CX_JSON_VALUE_ALLOC_FAILED); // LCOV_EXCL_LINE |
656 return_rec(CX_JSON_VALUE_ALLOC_FAILED); // LCOV_EXCL_LINE |
| 644 } |
657 } |
| 645 json_add_state(json, JP_STATE_OBJ_NAME_OR_CLOSE); |
658 json_add_state(json, JP_STATE_OBJ_NAME_OR_CLOSE); |
| 646 return_rec(CX_JSON_NO_ERROR); |
659 return_rec(CX_JSON_NO_ERROR); |
| 647 } |
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 } |
| 648 case CX_JSON_TOKEN_STRING: { |
671 case CX_JSON_TOKEN_STRING: { |
| 649 if ((vbuf = json_create_value(json, CX_JSON_STRING)) == NULL) { |
672 if ((vbuf = json_create_value(json, CX_JSON_STRING)) == NULL) { |
| 650 return_rec(CX_JSON_VALUE_ALLOC_FAILED); // LCOV_EXCL_LINE |
673 return_rec(CX_JSON_VALUE_ALLOC_FAILED); // LCOV_EXCL_LINE |
| 651 } |
674 } |
| 652 cxmutstr str = unescape_string(json->allocator, token.content); |
675 cxmutstr str = unescape_string(json->allocator, token.content); |
| 676 } |
699 } |
| 677 case CX_JSON_TOKEN_LITERAL: { |
700 case CX_JSON_TOKEN_LITERAL: { |
| 678 if ((vbuf = json_create_value(json, CX_JSON_LITERAL)) == NULL) { |
701 if ((vbuf = json_create_value(json, CX_JSON_LITERAL)) == NULL) { |
| 679 return_rec(CX_JSON_VALUE_ALLOC_FAILED); // LCOV_EXCL_LINE |
702 return_rec(CX_JSON_VALUE_ALLOC_FAILED); // LCOV_EXCL_LINE |
| 680 } |
703 } |
| 681 if (0 == cx_strcmp(cx_strcast(token.content), cx_str("true"))) { |
704 if (0 == cx_strcmp(token.content, "true")) { |
| 682 vbuf->literal = CX_JSON_TRUE; |
705 vbuf->literal = CX_JSON_TRUE; |
| 683 } else if (0 == cx_strcmp(cx_strcast(token.content), cx_str("false"))) { |
706 } else if (0 == cx_strcmp(token.content, "false")) { |
| 684 vbuf->literal = CX_JSON_FALSE; |
707 vbuf->literal = CX_JSON_FALSE; |
| 685 } else { |
708 } else { |
| 686 vbuf->literal = CX_JSON_NULL; |
709 vbuf->literal = CX_JSON_NULL; |
| 687 } |
710 } |
| 688 return_rec(CX_JSON_NO_ERROR); |
711 return_rec(CX_JSON_NO_ERROR); |
| 696 if (token.tokentype == CX_JSON_TOKEN_VALUE_SEPARATOR) { |
719 if (token.tokentype == CX_JSON_TOKEN_VALUE_SEPARATOR) { |
| 697 json_add_state(json, JP_STATE_VALUE_BEGIN_AR); |
720 json_add_state(json, JP_STATE_VALUE_BEGIN_AR); |
| 698 return_rec(CX_JSON_NO_ERROR); |
721 return_rec(CX_JSON_NO_ERROR); |
| 699 } else if (token.tokentype == CX_JSON_TOKEN_END_ARRAY) { |
722 } else if (token.tokentype == CX_JSON_TOKEN_END_ARRAY) { |
| 700 // discard the array from the value buffer |
723 // discard the array from the value buffer |
| 701 json->vbuf_size--; |
724 json->vbuf.size--; |
| 702 return_rec(CX_JSON_NO_ERROR); |
725 return_rec(CX_JSON_NO_ERROR); |
| 703 } else { |
726 } else { |
| 704 return_rec(CX_JSON_FORMAT_ERROR_UNEXPECTED_TOKEN); |
727 return_rec(CX_JSON_FORMAT_ERROR_UNEXPECTED_TOKEN); |
| 705 } |
728 } |
| 706 } else if (state == JP_STATE_OBJ_NAME_OR_CLOSE || state == JP_STATE_OBJ_NAME) { |
729 } else if (state == JP_STATE_OBJ_NAME_OR_CLOSE || state == JP_STATE_OBJ_NAME) { |
| 707 if (state == JP_STATE_OBJ_NAME_OR_CLOSE && token.tokentype == CX_JSON_TOKEN_END_OBJECT) { |
730 if (state == JP_STATE_OBJ_NAME_OR_CLOSE && token.tokentype == CX_JSON_TOKEN_END_OBJECT) { |
| 708 // discard the obj from the value buffer |
731 // discard the obj from the value buffer |
| 709 json->vbuf_size--; |
732 json->vbuf.size--; |
| 710 return_rec(CX_JSON_NO_ERROR); |
733 return_rec(CX_JSON_NO_ERROR); |
| 711 } else { |
734 } else { |
| 712 // expect string |
735 // expect string |
| 713 if (token.tokentype != CX_JSON_TOKEN_STRING) { |
736 if (token.tokentype != CX_JSON_TOKEN_STRING) { |
| 714 return_rec(CX_JSON_FORMAT_ERROR_UNEXPECTED_TOKEN); |
737 return_rec(CX_JSON_FORMAT_ERROR_UNEXPECTED_TOKEN); |
| 765 |
788 |
| 766 // parse data |
789 // parse data |
| 767 CxJsonStatus result; |
790 CxJsonStatus result; |
| 768 do { |
791 do { |
| 769 result = json_parse(json); |
792 result = json_parse(json); |
| 770 if (result == CX_JSON_NO_ERROR && json->states_size == 1) { |
793 if (result == CX_JSON_NO_ERROR && json->states.size == 1) { |
| 771 // final state reached |
794 // final state reached |
| 772 assert(json->states[0] == JP_STATE_VALUE_END); |
795 assert(json->states.data[0] == JP_STATE_VALUE_END); |
| 773 assert(json->vbuf_size == 0); |
796 assert(json->vbuf.size == 0); |
| 774 |
797 |
| 775 // write output value |
798 // write output value |
| 776 *value = json->parsed; |
799 *value = json->parsed; |
| 777 json->parsed = NULL; |
800 json->parsed = NULL; |
| 778 |
801 |
| 779 // re-initialize state machine |
802 // re-initialize state machine |
| 780 json->states[0] = JP_STATE_VALUE_BEGIN; |
803 json->states.data[0] = JP_STATE_VALUE_BEGIN; |
| 781 |
804 |
| 782 return CX_JSON_NO_ERROR; |
805 return CX_JSON_NO_ERROR; |
| 783 } |
806 } |
| 784 } while (result == CX_JSON_NO_ERROR); |
807 } while (result == CX_JSON_NO_ERROR); |
| 785 |
808 |
| 786 // the parser might think there is no data |
809 // the parser might think there is no data |
| 787 // but when we did not reach the final state, |
810 // but when we did not reach the final state, |
| 788 // we know that there must be more to come |
811 // we know that there must be more to come |
| 789 if (result == CX_JSON_NO_DATA && json->states_size > 1) { |
812 if (result == CX_JSON_NO_DATA && json->states.size > 1) { |
| 790 return CX_JSON_INCOMPLETE_DATA; |
813 return CX_JSON_INCOMPLETE_DATA; |
| 791 } |
814 } |
| 792 |
815 |
| 793 return result; |
816 return result; |
| 794 } |
817 } |
| 863 // LCOV_EXCL_STOP |
885 // LCOV_EXCL_STOP |
| 864 } |
886 } |
| 865 return v; |
887 return v; |
| 866 } |
888 } |
| 867 |
889 |
| 868 CxJsonValue* cxJsonCreateArr(const CxAllocator* allocator) { |
890 CxJsonValue* cxJsonCreateArr(const CxAllocator* allocator, size_t capacity) { |
| 869 if (allocator == NULL) allocator = cxDefaultAllocator; |
891 if (allocator == NULL) allocator = cxDefaultAllocator; |
| 870 CxJsonValue* v = cxMalloc(allocator, sizeof(CxJsonValue)); |
892 CxJsonValue* v = cxMalloc(allocator, sizeof(CxJsonValue)); |
| 871 if (v == NULL) return NULL; |
893 if (v == NULL) return NULL; |
| 872 v->allocator = allocator; |
894 v->allocator = allocator; |
| 873 v->type = CX_JSON_ARRAY; |
895 v->type = CX_JSON_ARRAY; |
| 874 cx_array_initialize_a(allocator, v->array.data, 16); |
896 if (capacity > 0) { |
| 875 if (v->array.data == 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 } |
| 876 return v; |
907 return v; |
| 877 } |
908 } |
| 878 |
909 |
| 879 CxJsonValue* cxJsonCreateNumber(const CxAllocator* allocator, double num) { |
910 CxJsonValue* cxJsonCreateNumber(const CxAllocator* allocator, double num) { |
| 880 if (allocator == NULL) allocator = cxDefaultAllocator; |
911 if (allocator == NULL) allocator = cxDefaultAllocator; |
| 988 cxFreeDefault(values); |
1019 cxFreeDefault(values); |
| 989 return ret; |
1020 return ret; |
| 990 } |
1021 } |
| 991 |
1022 |
| 992 int cxJsonArrAddValues(CxJsonValue* arr, CxJsonValue* const* val, size_t count) { |
1023 int cxJsonArrAddValues(CxJsonValue* arr, CxJsonValue* const* val, size_t count) { |
| 993 CxArrayReallocator value_realloc = cx_array_reallocator(arr->allocator, NULL); |
|
| 994 assert(arr->type == CX_JSON_ARRAY); |
1024 assert(arr->type == CX_JSON_ARRAY); |
| 995 return cx_array_simple_copy_a(&value_realloc, |
1025 return cx_array_add_array_a(arr->allocator, arr->array, val, count); |
| 996 arr->array.data, |
|
| 997 arr->array.data_size, |
|
| 998 val, count |
|
| 999 ); |
|
| 1000 } |
1026 } |
| 1001 |
1027 |
| 1002 int cx_json_obj_put(CxJsonValue* obj, cxstring name, CxJsonValue* child) { |
1028 int cx_json_obj_put(CxJsonValue* obj, cxstring name, CxJsonValue* child) { |
| 1003 return cxMapPut(obj->object, name, child); |
1029 return cxMapPut(obj->object, name, child); |
| 1004 } |
1030 } |
| 1044 if (cxJsonObjPut(obj, name, v)) { cxJsonValueFree(v); return NULL;} |
1070 if (cxJsonObjPut(obj, name, v)) { cxJsonValueFree(v); return NULL;} |
| 1045 return v; |
1071 return v; |
| 1046 } |
1072 } |
| 1047 |
1073 |
| 1048 CxJsonValue *cxJsonArrGet(const CxJsonValue *value, size_t index) { |
1074 CxJsonValue *cxJsonArrGet(const CxJsonValue *value, size_t index) { |
| 1049 if (index >= value->array.data_size) { |
1075 if (index >= value->array.size) { |
| 1050 return &cx_json_value_nothing; |
1076 return &cx_json_value_nothing; |
| 1051 } |
1077 } |
| 1052 return value->array.data[index]; |
1078 return value->array.data[index]; |
| 1053 } |
1079 } |
| 1054 |
1080 |
| 1055 CxJsonValue *cxJsonArrRemove(CxJsonValue *value, size_t index) { |
1081 CxJsonValue *cxJsonArrRemove(CxJsonValue *value, size_t index) { |
| 1056 if (index >= value->array.data_size) { |
1082 if (index >= value->array.size) { |
| 1057 return NULL; |
1083 return NULL; |
| 1058 } |
1084 } |
| 1059 CxJsonValue *ret = value->array.data[index]; |
1085 CxJsonValue *ret = value->array.data[index]; |
| 1060 // TODO: replace with a low level cx_array_remove() |
1086 cx_array_remove(value->array, index); |
| 1061 size_t count = value->array.data_size - index - 1; |
|
| 1062 if (count > 0) { |
|
| 1063 memmove(value->array.data + index, value->array.data + index + 1, count * sizeof(CxJsonValue*)); |
|
| 1064 } |
|
| 1065 value->array.data_size--; |
|
| 1066 return ret; |
1087 return ret; |
| 1067 } |
1088 } |
| 1068 |
1089 |
| 1069 char *cxJsonAsString(const CxJsonValue *value) { |
1090 char *cxJsonAsString(const CxJsonValue *value) { |
| 1070 return value->string.ptr; |
1091 return value->string.ptr; |
| 1407 if (settings == NULL) { |
1424 if (settings == NULL) { |
| 1408 settings = &writer_default; |
1425 settings = &writer_default; |
| 1409 } |
1426 } |
| 1410 return cx_json_write_rec(target, value, wfunc, settings, 0); |
1427 return cx_json_write_rec(target, value, wfunc, settings, 0); |
| 1411 } |
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 } |