Merge pull request #490 from AllenX2018/master
add loop check in json deep copy #457
This commit is contained in:
commit
09e455275c
@ -1091,7 +1091,9 @@ These functions output UTF-8:
|
|||||||
|
|
||||||
Returns the JSON representation of *json* as a string, or *NULL* on
|
Returns the JSON representation of *json* as a string, or *NULL* on
|
||||||
error. *flags* is described above. The return value must be freed
|
error. *flags* is described above. The return value must be freed
|
||||||
by the caller using :func:`free()`.
|
by the caller using :func:`free()`. Note that if you have called
|
||||||
|
:func:`json_set_alloc_funcs()` to override :func:`free()`, you should
|
||||||
|
call your custom free function instead to free the return value.
|
||||||
|
|
||||||
.. function:: size_t json_dumpb(const json_t *json, char *buffer, size_t size, size_t flags)
|
.. function:: size_t json_dumpb(const json_t *json, char *buffer, size_t size, size_t flags)
|
||||||
|
|
||||||
|
16
src/dump.c
16
src/dump.c
@ -196,15 +196,6 @@ static int compare_keys(const void *key1, const void *key2)
|
|||||||
return strcmp(*(const char **)key1, *(const char **)key2);
|
return strcmp(*(const char **)key1, *(const char **)key2);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int loop_check(hashtable_t *parents, const json_t *json, char *key, size_t key_size)
|
|
||||||
{
|
|
||||||
snprintf(key, key_size, "%p", json);
|
|
||||||
if (hashtable_get(parents, key))
|
|
||||||
return -1;
|
|
||||||
|
|
||||||
return hashtable_set(parents, key, json_null());
|
|
||||||
}
|
|
||||||
|
|
||||||
static int do_dump(const json_t *json, size_t flags, int depth,
|
static int do_dump(const json_t *json, size_t flags, int depth,
|
||||||
hashtable_t *parents, json_dump_callback_t dump, void *data)
|
hashtable_t *parents, json_dump_callback_t dump, void *data)
|
||||||
{
|
{
|
||||||
@ -264,7 +255,7 @@ static int do_dump(const json_t *json, size_t flags, int depth,
|
|||||||
char key[2 + (sizeof(json) * 2) + 1];
|
char key[2 + (sizeof(json) * 2) + 1];
|
||||||
|
|
||||||
/* detect circular references */
|
/* detect circular references */
|
||||||
if (loop_check(parents, json, key, sizeof(key)))
|
if (jsonp_loop_check(parents, json, key, sizeof(key)))
|
||||||
return -1;
|
return -1;
|
||||||
|
|
||||||
n = json_array_size(json);
|
n = json_array_size(json);
|
||||||
@ -305,8 +296,7 @@ static int do_dump(const json_t *json, size_t flags, int depth,
|
|||||||
void *iter;
|
void *iter;
|
||||||
const char *separator;
|
const char *separator;
|
||||||
int separator_length;
|
int separator_length;
|
||||||
/* Space for "0x", double the sizeof a pointer for the hex and a terminator. */
|
char loop_key[LOOP_KEY_LEN];
|
||||||
char loop_key[2 + (sizeof(json) * 2) + 1];
|
|
||||||
|
|
||||||
if(flags & JSON_COMPACT) {
|
if(flags & JSON_COMPACT) {
|
||||||
separator = ":";
|
separator = ":";
|
||||||
@ -318,7 +308,7 @@ static int do_dump(const json_t *json, size_t flags, int depth,
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* detect circular references */
|
/* detect circular references */
|
||||||
if (loop_check(parents, json, loop_key, sizeof(loop_key)))
|
if (jsonp_loop_check(parents, json, loop_key, sizeof(loop_key)))
|
||||||
return -1;
|
return -1;
|
||||||
|
|
||||||
iter = json_object_iter((json_t *)json);
|
iter = json_object_iter((json_t *)json);
|
||||||
|
@ -90,6 +90,11 @@ char *jsonp_strndup(const char *str, size_t length) JANSSON_ATTRS(warn_unused_re
|
|||||||
char *jsonp_strdup(const char *str) JANSSON_ATTRS(warn_unused_result);
|
char *jsonp_strdup(const char *str) JANSSON_ATTRS(warn_unused_result);
|
||||||
char *jsonp_strndup(const char *str, size_t len) JANSSON_ATTRS(warn_unused_result);
|
char *jsonp_strndup(const char *str, size_t len) JANSSON_ATTRS(warn_unused_result);
|
||||||
|
|
||||||
|
/* Circular reference check*/
|
||||||
|
/* Space for "0x", double the sizeof a pointer for the hex and a terminator. */
|
||||||
|
#define LOOP_KEY_LEN (2 + (sizeof(json_t *) * 2) + 1)
|
||||||
|
int jsonp_loop_check(hashtable_t *parents, const json_t *json, char *key, size_t key_size);
|
||||||
|
|
||||||
|
|
||||||
/* Windows compatibility */
|
/* Windows compatibility */
|
||||||
#if defined(_WIN32) || defined(WIN32)
|
#if defined(_WIN32) || defined(WIN32)
|
||||||
|
57
src/value.c
57
src/value.c
@ -37,12 +37,22 @@ static JSON_INLINE int isnan(double x) { return x != x; }
|
|||||||
static JSON_INLINE int isinf(double x) { return !isnan(x) && isnan(x - x); }
|
static JSON_INLINE int isinf(double x) { return !isnan(x) && isnan(x - x); }
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
json_t *do_deep_copy(const json_t *, hashtable_t *);
|
||||||
|
|
||||||
static JSON_INLINE void json_init(json_t *json, json_type type)
|
static JSON_INLINE void json_init(json_t *json, json_type type)
|
||||||
{
|
{
|
||||||
json->type = type;
|
json->type = type;
|
||||||
json->refcount = 1;
|
json->refcount = 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int jsonp_loop_check(hashtable_t *parents, const json_t *json, char *key, size_t key_size)
|
||||||
|
{
|
||||||
|
snprintf(key, key_size, "%p", json);
|
||||||
|
if (hashtable_get(parents, key))
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
return hashtable_set(parents, key, json_null());
|
||||||
|
}
|
||||||
|
|
||||||
/*** object ***/
|
/*** object ***/
|
||||||
|
|
||||||
@ -308,10 +318,14 @@ static json_t *json_object_copy(json_t *object)
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
static json_t *json_object_deep_copy(const json_t *object)
|
static json_t *json_object_deep_copy(const json_t *object, hashtable_t *parents)
|
||||||
{
|
{
|
||||||
json_t *result;
|
json_t *result;
|
||||||
void *iter;
|
void *iter;
|
||||||
|
char loop_key[LOOP_KEY_LEN];
|
||||||
|
|
||||||
|
if (jsonp_loop_check(parents, object, loop_key, sizeof(loop_key)))
|
||||||
|
return NULL;
|
||||||
|
|
||||||
result = json_object();
|
result = json_object();
|
||||||
if(!result)
|
if(!result)
|
||||||
@ -326,9 +340,15 @@ static json_t *json_object_deep_copy(const json_t *object)
|
|||||||
key = json_object_iter_key(iter);
|
key = json_object_iter_key(iter);
|
||||||
value = json_object_iter_value(iter);
|
value = json_object_iter_value(iter);
|
||||||
|
|
||||||
json_object_set_new_nocheck(result, key, json_deep_copy(value));
|
if (json_object_set_new_nocheck(result, key, do_deep_copy(value, parents)))
|
||||||
|
{
|
||||||
|
json_decref(result);
|
||||||
|
result = NULL;
|
||||||
|
break;
|
||||||
|
}
|
||||||
iter = json_object_iter_next((json_t *)object, iter);
|
iter = json_object_iter_next((json_t *)object, iter);
|
||||||
}
|
}
|
||||||
|
hashtable_del(parents, loop_key);
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
@ -617,17 +637,29 @@ static json_t *json_array_copy(json_t *array)
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
static json_t *json_array_deep_copy(const json_t *array)
|
static json_t *json_array_deep_copy(const json_t *array, hashtable_t *parents)
|
||||||
{
|
{
|
||||||
json_t *result;
|
json_t *result;
|
||||||
size_t i;
|
size_t i;
|
||||||
|
char loop_key[LOOP_KEY_LEN];
|
||||||
|
|
||||||
|
if (jsonp_loop_check(parents, array, loop_key, sizeof(loop_key)))
|
||||||
|
return NULL;
|
||||||
|
|
||||||
result = json_array();
|
result = json_array();
|
||||||
if(!result)
|
if(!result)
|
||||||
return NULL;
|
return NULL;
|
||||||
|
|
||||||
for(i = 0; i < json_array_size(array); i++)
|
for(i = 0; i < json_array_size(array); i++)
|
||||||
json_array_append_new(result, json_deep_copy(json_array_get(array, i)));
|
{
|
||||||
|
if (json_array_append_new(result, do_deep_copy(json_array_get(array, i), parents)))
|
||||||
|
{
|
||||||
|
json_decref(result);
|
||||||
|
result = NULL;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
hashtable_del(parents, loop_key);
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
@ -1047,15 +1079,28 @@ json_t *json_copy(json_t *json)
|
|||||||
}
|
}
|
||||||
|
|
||||||
json_t *json_deep_copy(const json_t *json)
|
json_t *json_deep_copy(const json_t *json)
|
||||||
|
{
|
||||||
|
json_t *res;
|
||||||
|
hashtable_t parents_set;
|
||||||
|
|
||||||
|
if (hashtable_init(&parents_set))
|
||||||
|
return NULL;
|
||||||
|
res = do_deep_copy(json, &parents_set);
|
||||||
|
hashtable_close(&parents_set);
|
||||||
|
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
json_t *do_deep_copy(const json_t *json, hashtable_t *parents)
|
||||||
{
|
{
|
||||||
if(!json)
|
if(!json)
|
||||||
return NULL;
|
return NULL;
|
||||||
|
|
||||||
switch(json_typeof(json)) {
|
switch(json_typeof(json)) {
|
||||||
case JSON_OBJECT:
|
case JSON_OBJECT:
|
||||||
return json_object_deep_copy(json);
|
return json_object_deep_copy(json, parents);
|
||||||
case JSON_ARRAY:
|
case JSON_ARRAY:
|
||||||
return json_array_deep_copy(json);
|
return json_array_deep_copy(json, parents);
|
||||||
/* for the rest of the types, deep copying doesn't differ from
|
/* for the rest of the types, deep copying doesn't differ from
|
||||||
shallow copying */
|
shallow copying */
|
||||||
case JSON_STRING:
|
case JSON_STRING:
|
||||||
|
@ -323,6 +323,58 @@ static void test_deep_copy_object(void)
|
|||||||
json_decref(copy);
|
json_decref(copy);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void test_deep_copy_circular_references(void)
|
||||||
|
{
|
||||||
|
/* Construct a JSON object/array with a circular reference:
|
||||||
|
|
||||||
|
object: {"a": {"b": {"c": <circular reference to $.a>}}}
|
||||||
|
array: [[[<circular reference to the $[0] array>]]]
|
||||||
|
|
||||||
|
Deep copy it, remove the circular reference and deep copy again.
|
||||||
|
*/
|
||||||
|
|
||||||
|
json_t *json;
|
||||||
|
json_t *copy;
|
||||||
|
|
||||||
|
json = json_object();
|
||||||
|
json_object_set_new(json, "a", json_object());
|
||||||
|
json_object_set_new(json_object_get(json, "a"), "b", json_object());
|
||||||
|
json_object_set(json_object_get(json_object_get(json, "a"), "b"), "c",
|
||||||
|
json_object_get(json, "a"));
|
||||||
|
|
||||||
|
copy = json_deep_copy(json);
|
||||||
|
if(copy)
|
||||||
|
fail("json_deep_copy copied a circular reference!");
|
||||||
|
|
||||||
|
json_object_del(json_object_get(json_object_get(json, "a"), "b"), "c");
|
||||||
|
|
||||||
|
copy = json_deep_copy(json);
|
||||||
|
if(!copy)
|
||||||
|
fail("json_deep_copy failed!");
|
||||||
|
|
||||||
|
json_decref(copy);
|
||||||
|
json_decref(json);
|
||||||
|
|
||||||
|
json = json_array();
|
||||||
|
json_array_append_new(json, json_array());
|
||||||
|
json_array_append_new(json_array_get(json, 0), json_array());
|
||||||
|
json_array_append(json_array_get(json_array_get(json, 0), 0),
|
||||||
|
json_array_get(json, 0));
|
||||||
|
|
||||||
|
copy = json_deep_copy(json);
|
||||||
|
if(copy)
|
||||||
|
fail("json_deep_copy copied a circular reference!");
|
||||||
|
|
||||||
|
json_array_remove(json_array_get(json_array_get(json, 0), 0), 0);
|
||||||
|
|
||||||
|
copy = json_deep_copy(json);
|
||||||
|
if(!copy)
|
||||||
|
fail("json_deep_copy failed!");
|
||||||
|
|
||||||
|
json_decref(copy);
|
||||||
|
json_decref(json);
|
||||||
|
}
|
||||||
|
|
||||||
static void run_tests()
|
static void run_tests()
|
||||||
{
|
{
|
||||||
test_copy_simple();
|
test_copy_simple();
|
||||||
@ -331,4 +383,5 @@ static void run_tests()
|
|||||||
test_deep_copy_array();
|
test_deep_copy_array();
|
||||||
test_copy_object();
|
test_copy_object();
|
||||||
test_deep_copy_object();
|
test_deep_copy_object();
|
||||||
|
test_deep_copy_circular_references();
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user