| @@ -25,6 +25,7 @@ | |||
| /tests/test_cast | |||
| /tests/test_charcase | |||
| /tests/test_compare | |||
| /tests/test_deep_copy | |||
| /tests/test_double_serializer | |||
| /tests/test_float | |||
| /tests/test_int_add | |||
| @@ -1299,3 +1299,106 @@ int json_object_equal(struct json_object* jso1, struct json_object* jso2) | |||
| return 0; | |||
| } | |||
| static int json_object_copy_serializer_data(struct json_object *src, struct json_object *dst) | |||
| { | |||
| /* FIXME: this is shared between copies ; maybe add a `_user_copy` cb here */ | |||
| dst->_userdata = src->_userdata; | |||
| dst->_to_json_string = src->_to_json_string; | |||
| dst->_user_delete = src->_user_delete; | |||
| return 0; | |||
| } | |||
| static int json_object_deep_copy_recursive(struct json_object *src, struct json_object **dst) | |||
| { | |||
| struct json_object *jso = NULL; | |||
| struct json_object_iter iter; | |||
| size_t i; | |||
| if (!src || !dst) { | |||
| errno = EINVAL; | |||
| return -1; | |||
| } | |||
| switch (src->o_type) { | |||
| case json_type_boolean: | |||
| *dst = json_object_new_boolean(src->o.c_boolean); | |||
| break; | |||
| case json_type_double: | |||
| *dst = json_object_new_double(src->o.c_double); | |||
| break; | |||
| case json_type_int: | |||
| *dst = json_object_new_int64(src->o.c_int64); | |||
| break; | |||
| case json_type_string: | |||
| *dst = json_object_new_string(get_string_component(src)); | |||
| break; | |||
| case json_type_object: | |||
| *dst = json_object_new_object(); | |||
| if (!*dst) { | |||
| errno = ENOMEM; | |||
| return -1; | |||
| } | |||
| json_object_object_foreachC(src, iter) { | |||
| /* This handles the `json_type_null` case */ | |||
| if (!iter.val) | |||
| jso = NULL; | |||
| else if (json_object_deep_copy_recursive(iter.val, &jso) < 0) | |||
| return -1; | |||
| if (json_object_object_add(*dst, iter.key, jso) < 0) | |||
| return -1; | |||
| } | |||
| break; | |||
| case json_type_array: | |||
| *dst = json_object_new_array(); | |||
| if (!*dst) { | |||
| errno = ENOMEM; | |||
| return -1; | |||
| } | |||
| for (i = 0; i < json_object_array_length(src); i++) { | |||
| struct json_object *jso1 = json_object_array_get_idx(src, i); | |||
| /* This handles the `json_type_null` case */ | |||
| if (!jso1) | |||
| jso = NULL; | |||
| else if (json_object_deep_copy_recursive(jso1, &jso) < 0) | |||
| return -1; | |||
| if (json_object_array_add(*dst, jso) < 0) | |||
| return -1; | |||
| } | |||
| break; | |||
| default: | |||
| errno = EINVAL; | |||
| return -1; | |||
| }; | |||
| /* errno should be set by the function that faulted */ | |||
| if (!*dst) | |||
| return -1; | |||
| return json_object_copy_serializer_data(src, *dst); | |||
| } | |||
| int json_object_deep_copy(struct json_object *src, struct json_object **dst) | |||
| { | |||
| int rc; | |||
| /* Check if arguments are sane ; *dst must not point to a non-NULL object */ | |||
| if (!src || !dst || *dst) { | |||
| errno = EINVAL; | |||
| return -1; | |||
| } | |||
| rc = json_object_deep_copy_recursive(src, dst); | |||
| if (rc < 0) { | |||
| json_object_put(*dst); | |||
| *dst = NULL; | |||
| } | |||
| return rc; | |||
| } | |||
| @@ -963,6 +963,24 @@ JSON_EXPORT int json_object_set_string_len(json_object* obj, const char* new_val | |||
| JSON_EXPORT int json_object_equal(struct json_object *obj1, | |||
| struct json_object *obj2); | |||
| /** | |||
| * Copy the contents of the JSON object. | |||
| * The destination object must be initialized to NULL, | |||
| * to make sure this function won't overwrite an existing JSON object. | |||
| * | |||
| * This does roughly the same thing as | |||
| * `json_tokener_parse(json_object_get_string(src))`. | |||
| * | |||
| * @param src source JSON object whose contents will be copied | |||
| * @param dst pointer to the destination object where the contents of `src`; | |||
| * make sure this pointer is initialized to NULL | |||
| * | |||
| * @returns 0 if the copy went well, -1 if an error occured during copy | |||
| * or if the destination pointer is non-NULL | |||
| */ | |||
| extern int json_object_deep_copy(struct json_object *src, struct json_object **dst); | |||
| #ifdef __cplusplus | |||
| } | |||
| #endif | |||
| @@ -13,6 +13,7 @@ TESTS+= test2.test | |||
| TESTS+= test4.test | |||
| TESTS+= testReplaceExisting.test | |||
| TESTS+= test_parse_int64.test | |||
| TESTS+= test_deep_copy.test | |||
| TESTS+= test_null.test | |||
| TESTS+= test_cast.test | |||
| TESTS+= test_double_serializer.test | |||
| @@ -0,0 +1,175 @@ | |||
| #include <stdio.h> | |||
| #include <stdlib.h> | |||
| #include <stddef.h> | |||
| #include <string.h> | |||
| #include <assert.h> | |||
| #include <errno.h> | |||
| #include <time.h> | |||
| #include "json.h" | |||
| static const char *json_str1 = | |||
| "{" | |||
| " \"glossary\": {" | |||
| " \"title\": \"example glossary\"," | |||
| " \"GlossDiv\": {" | |||
| " \"title\": \"S\"," | |||
| " \"null_obj\": null, " | |||
| " \"GlossList\": {" | |||
| " \"GlossEntry\": {" | |||
| " \"ID\": \"SGML\"," | |||
| " \"SortAs\": \"SGML\"," | |||
| " \"GlossTerm\": \"Standard Generalized Markup Language\"," | |||
| " \"Acronym\": \"SGML\"," | |||
| " \"Abbrev\": \"ISO 8879:1986\"," | |||
| " \"GlossDef\": {" | |||
| " \"para\": \"A meta-markup language, used to create markup languages such as DocBook.\"," | |||
| " \"GlossSeeAlso\": [\"GML\", \"XML\"]" | |||
| " }," | |||
| " \"GlossSee\": \"markup\"" | |||
| " }" | |||
| " }" | |||
| " }" | |||
| " }" | |||
| "}"; | |||
| static const char *json_str2 = | |||
| "{\"menu\": {" | |||
| " \"header\": \"SVG Viewer\"," | |||
| " \"items\": [" | |||
| " {\"id\": \"Open\"}," | |||
| " {\"id\": \"OpenNew\", \"label\": \"Open New\"}," | |||
| " null," | |||
| " {\"id\": \"ZoomIn\", \"label\": \"Zoom In\"}," | |||
| " {\"id\": \"ZoomOut\", \"label\": \"Zoom Out\"}," | |||
| " {\"id\": \"OriginalView\", \"label\": \"Original View\"}," | |||
| " null," | |||
| " {\"id\": \"Quality\", \"another_null\": null}," | |||
| " {\"id\": \"Pause\"}," | |||
| " {\"id\": \"Mute\"}," | |||
| " null," | |||
| " {\"id\": \"Find\", \"label\": \"Find...\"}," | |||
| " {\"id\": \"FindAgain\", \"label\": \"Find Again\"}," | |||
| " {\"id\": \"Copy\"}," | |||
| " {\"id\": \"CopyAgain\", \"label\": \"Copy Again\"}," | |||
| " {\"id\": \"CopySVG\", \"label\": \"Copy SVG\"}," | |||
| " {\"id\": \"ViewSVG\", \"label\": \"View SVG\"}," | |||
| " {\"id\": \"ViewSource\", \"label\": \"View Source\"}," | |||
| " {\"id\": \"SaveAs\", \"label\": \"Save As\"}," | |||
| " null," | |||
| " {\"id\": \"Help\"}," | |||
| " {\"id\": \"About\", \"label\": \"About Adobe CVG Viewer...\"}" | |||
| " ]" | |||
| "}}"; | |||
| static const char *json_str3 = | |||
| "{\"menu\": {" | |||
| " \"id\": \"file\"," | |||
| " \"value\": \"File\"," | |||
| " \"popup\": {" | |||
| " \"menuitem\": [" | |||
| " {\"value\": \"New\", \"onclick\": \"CreateNewDoc()\"}," | |||
| " {\"value\": \"Open\", \"onclick\": \"OpenDoc()\"}," | |||
| " {\"value\": \"Close\", \"onclick\": \"CloseDoc()\"}" | |||
| " ]" | |||
| " }" | |||
| "}}"; | |||
| int main(int argc, char **argv) | |||
| { | |||
| struct json_object *src1, *src2, *src3; | |||
| struct json_object *dst1 = NULL, *dst2 = NULL, *dst3 = NULL; | |||
| src1 = json_tokener_parse(json_str1); | |||
| src2 = json_tokener_parse(json_str2); | |||
| src3 = json_tokener_parse(json_str3); | |||
| assert(src1 != NULL); | |||
| assert(src1 != NULL); | |||
| assert(src3 != NULL); | |||
| printf("PASSED - loaded input data\n"); | |||
| /* do this 3 times to make sure overwriting it works */ | |||
| assert(0 == json_object_deep_copy(src1, &dst1)); | |||
| assert(0 == json_object_deep_copy(src2, &dst2)); | |||
| assert(0 == json_object_deep_copy(src3, &dst3)); | |||
| printf("PASSED - all json_object_deep_copy() returned succesful\n"); | |||
| assert(-1 == json_object_deep_copy(src1, &dst1)); | |||
| assert(errno == EINVAL); | |||
| assert(-1 == json_object_deep_copy(src2, &dst2)); | |||
| assert(errno == EINVAL); | |||
| assert(-1 == json_object_deep_copy(src3, &dst3)); | |||
| assert(errno == EINVAL); | |||
| printf("PASSED - all json_object_deep_copy() returned EINVAL for non-null pointer\n"); | |||
| assert(1 == json_object_equal(src1, dst1)); | |||
| assert(1 == json_object_equal(src2, dst2)); | |||
| assert(1 == json_object_equal(src3, dst3)); | |||
| printf("PASSED - all json_object_equal() tests returned succesful\n"); | |||
| assert(0 == strcmp(json_object_to_json_string_ext(src1, JSON_C_TO_STRING_PRETTY), | |||
| json_object_to_json_string_ext(dst1, JSON_C_TO_STRING_PRETTY))); | |||
| assert(0 == strcmp(json_object_to_json_string_ext(src2, JSON_C_TO_STRING_PRETTY), | |||
| json_object_to_json_string_ext(dst2, JSON_C_TO_STRING_PRETTY))); | |||
| assert(0 == strcmp(json_object_to_json_string_ext(src3, JSON_C_TO_STRING_PRETTY), | |||
| json_object_to_json_string_ext(dst3, JSON_C_TO_STRING_PRETTY))); | |||
| printf("PASSED - comparison of string output\n"); | |||
| json_object_get(dst1); | |||
| assert(-1 == json_object_deep_copy(src1, &dst1)); | |||
| assert(errno == EINVAL); | |||
| printf("PASSED - trying to overrwrite an object that has refcount > 1"); | |||
| printf("\nPrinting JSON objects for visual inspection\n"); | |||
| printf("------------------------------------------------\n"); | |||
| printf(" JSON1\n"); | |||
| printf("%s", json_object_to_json_string_ext(dst1, JSON_C_TO_STRING_PRETTY)); | |||
| printf("------------------------------------------------\n"); | |||
| printf("------------------------------------------------\n"); | |||
| printf(" JSON2\n"); | |||
| printf("%s", json_object_to_json_string_ext(dst2, JSON_C_TO_STRING_PRETTY)); | |||
| printf("------------------------------------------------\n"); | |||
| printf("------------------------------------------------\n"); | |||
| printf(" JSON3\n"); | |||
| printf("------------------------------------------------\n"); | |||
| printf("%s", json_object_to_json_string_ext(dst3, JSON_C_TO_STRING_PRETTY)); | |||
| printf("------------------------------------------------\n"); | |||
| json_object_put(dst1); | |||
| json_object_put(dst2); | |||
| json_object_put(dst3); | |||
| #if BENCHMARK | |||
| /** | |||
| * The numbers that I got are: | |||
| * BENCHMARK - 1000000 iterations of 'dst2 = json_tokener_parse(json_object_get_string(src2))' took 20 seconds | |||
| * BENCHMARK - 1000000 iterations of 'dst2 = json_tokener_parse(json_object_get_string(src2))' took 7 seconds | |||
| */ | |||
| int iterations = 1000000; | |||
| time_t start = time(NULL); | |||
| for (i = 0; i < iterations; i++) { | |||
| dst2 = json_tokener_parse(json_object_get_string(src2)); | |||
| json_object_put(dst2); | |||
| } | |||
| printf("BENCHMARK - %d iterations of 'dst2 = json_tokener_parse(json_object_get_string(src2))' took %d seconds\n", iterations, (int)(time(NULL) - start)); | |||
| start = time(NULL); | |||
| for (i = 0; i < iterations; i++) { | |||
| json_object_deep_copy(src2, &dst2); | |||
| } | |||
| json_object_put(dst2); | |||
| printf("BENCHMARK - %d iterations of 'dst2 = json_tokener_parse(json_object_get_string(src2))' took %d seconds\n", iterations, (int)(time(NULL) - start)); | |||
| #endif | |||
| return 0; | |||
| } | |||
| @@ -0,0 +1,140 @@ | |||
| PASSED - loaded input data | |||
| PASSED - all json_object_deep_copy() returned succesful | |||
| PASSED - all json_object_deep_copy() returned EINVAL for non-null pointer | |||
| PASSED - all json_object_equal() tests returned succesful | |||
| PASSED - comparison of string output | |||
| PASSED - trying to overrwrite an object that has refcount > 1 | |||
| Printing JSON objects for visual inspection | |||
| ------------------------------------------------ | |||
| JSON1 | |||
| { | |||
| "glossary":{ | |||
| "title":"example glossary", | |||
| "GlossDiv":{ | |||
| "title":"S", | |||
| "null_obj":null, | |||
| "GlossList":{ | |||
| "GlossEntry":{ | |||
| "ID":"SGML", | |||
| "SortAs":"SGML", | |||
| "GlossTerm":"Standard Generalized Markup Language", | |||
| "Acronym":"SGML", | |||
| "Abbrev":"ISO 8879:1986", | |||
| "GlossDef":{ | |||
| "para":"A meta-markup language, used to create markup languages such as DocBook.", | |||
| "GlossSeeAlso":[ | |||
| "GML", | |||
| "XML" | |||
| ] | |||
| }, | |||
| "GlossSee":"markup" | |||
| } | |||
| } | |||
| } | |||
| } | |||
| }------------------------------------------------ | |||
| ------------------------------------------------ | |||
| JSON2 | |||
| { | |||
| "menu":{ | |||
| "header":"SVG Viewer", | |||
| "items":[ | |||
| { | |||
| "id":"Open" | |||
| }, | |||
| { | |||
| "id":"OpenNew", | |||
| "label":"Open New" | |||
| }, | |||
| null, | |||
| { | |||
| "id":"ZoomIn", | |||
| "label":"Zoom In" | |||
| }, | |||
| { | |||
| "id":"ZoomOut", | |||
| "label":"Zoom Out" | |||
| }, | |||
| { | |||
| "id":"OriginalView", | |||
| "label":"Original View" | |||
| }, | |||
| null, | |||
| { | |||
| "id":"Quality", | |||
| "another_null":null | |||
| }, | |||
| { | |||
| "id":"Pause" | |||
| }, | |||
| { | |||
| "id":"Mute" | |||
| }, | |||
| null, | |||
| { | |||
| "id":"Find", | |||
| "label":"Find..." | |||
| }, | |||
| { | |||
| "id":"FindAgain", | |||
| "label":"Find Again" | |||
| }, | |||
| { | |||
| "id":"Copy" | |||
| }, | |||
| { | |||
| "id":"CopyAgain", | |||
| "label":"Copy Again" | |||
| }, | |||
| { | |||
| "id":"CopySVG", | |||
| "label":"Copy SVG" | |||
| }, | |||
| { | |||
| "id":"ViewSVG", | |||
| "label":"View SVG" | |||
| }, | |||
| { | |||
| "id":"ViewSource", | |||
| "label":"View Source" | |||
| }, | |||
| { | |||
| "id":"SaveAs", | |||
| "label":"Save As" | |||
| }, | |||
| null, | |||
| { | |||
| "id":"Help" | |||
| }, | |||
| { | |||
| "id":"About", | |||
| "label":"About Adobe CVG Viewer..." | |||
| } | |||
| ] | |||
| } | |||
| }------------------------------------------------ | |||
| ------------------------------------------------ | |||
| JSON3 | |||
| ------------------------------------------------ | |||
| { | |||
| "menu":{ | |||
| "id":"file", | |||
| "value":"File", | |||
| "popup":{ | |||
| "menuitem":[ | |||
| { | |||
| "value":"New", | |||
| "onclick":"CreateNewDoc()" | |||
| }, | |||
| { | |||
| "value":"Open", | |||
| "onclick":"OpenDoc()" | |||
| }, | |||
| { | |||
| "value":"Close", | |||
| "onclick":"CloseDoc()" | |||
| } | |||
| ] | |||
| } | |||
| } | |||
| }------------------------------------------------ | |||
| @@ -0,0 +1 @@ | |||
| test_basic.test | |||