add loop check in json deep copy #457

This commit is contained in:
allen 2019-08-12 11:17:25 +08:00
parent a1f297aa83
commit 63fb81faa5
4 changed files with 109 additions and 17 deletions

View File

@ -196,15 +196,6 @@ static int compare_keys(const void *key1, const void *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,
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];
/* detect circular references */
if (loop_check(parents, json, key, sizeof(key)))
if (jsonp_loop_check(parents, json, key, sizeof(key)))
return -1;
n = json_array_size(json);
@ -318,7 +309,7 @@ static int do_dump(const json_t *json, size_t flags, int depth,
}
/* 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;
iter = json_object_iter((json_t *)json);

View File

@ -90,6 +90,9 @@ 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_strndup(const char *str, size_t len) JANSSON_ATTRS(warn_unused_result);
/* Circular reference check*/
int jsonp_loop_check(hashtable_t *parents, const json_t *json, char *key, size_t key_size);
/* Windows compatibility */
#if defined(_WIN32) || defined(WIN32)

View File

@ -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); }
#endif
json_t *do_deep_copy(const json_t *, hashtable_t *);
static JSON_INLINE void json_init(json_t *json, json_type type)
{
json->type = type;
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 ***/
@ -308,10 +318,14 @@ static json_t *json_object_copy(json_t *object)
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;
void *iter;
char loop_key[2 + (sizeof(object) * 2) + 1];
if (jsonp_loop_check(parents, object, loop_key, sizeof(loop_key)))
return NULL;
result = json_object();
if(!result)
@ -326,9 +340,15 @@ static json_t *json_object_deep_copy(const json_t *object)
key = json_object_iter_key(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);
}
hashtable_del(parents, loop_key);
return result;
}
@ -617,17 +637,29 @@ static json_t *json_array_copy(json_t *array)
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;
size_t i;
char loop_key[2 + (sizeof(array) * 2) + 1];
if (jsonp_loop_check(parents, array, loop_key, sizeof(loop_key)))
return NULL;
result = json_array();
if(!result)
return NULL;
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;
}
@ -1047,15 +1079,28 @@ json_t *json_copy(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)
return NULL;
switch(json_typeof(json)) {
case JSON_OBJECT:
return json_object_deep_copy(json);
return json_object_deep_copy(json, parents);
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
shallow copying */
case JSON_STRING:

View File

@ -323,6 +323,58 @@ static void test_deep_copy_object(void)
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()
{
test_copy_simple();
@ -331,4 +383,5 @@ static void run_tests()
test_deep_copy_array();
test_copy_object();
test_deep_copy_object();
test_deep_copy_circular_references();
}