@@ -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 |