add loop check for json_object_update_recursive function

This commit is contained in:
allen 2019-10-12 15:36:05 +08:00
parent cb4727c4a9
commit 00d2d274bc
2 changed files with 75 additions and 8 deletions

View File

@ -37,7 +37,7 @@ 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 *); json_t *do_deep_copy(const json_t *json, hashtable_t *parents);
static JSON_INLINE void json_init(json_t *json, json_type type) static JSON_INLINE void json_init(json_t *json, json_type type)
{ {
@ -214,24 +214,56 @@ int json_object_update_missing(json_t *object, json_t *other)
return 0; return 0;
} }
int json_object_update_recursive(json_t *object, json_t *other) int do_object_update_recursive(json_t *object, json_t *other, hashtable_t *parents)
{ {
const char *key; const char *key;
json_t *value; json_t *value;
char loop_key[LOOP_KEY_LEN];
int res = 0;
if(!json_is_object(object) || !json_is_object(other)) if(!json_is_object(object) || !json_is_object(other))
return -1; return -1;
if(jsonp_loop_check(parents, other, loop_key, sizeof(loop_key)))
return -1;
json_object_foreach(other, key, value) { json_object_foreach(other, key, value) {
json_t *v = json_object_get(object, key); json_t *v = json_object_get(object, key);
if(json_is_object(v) && json_is_object(value)) if(json_is_object(v) && json_is_object(value))
json_object_update_recursive(v, value); {
if(do_object_update_recursive(v, value, parents))
{
res = -1;
break;
}
}
else else
json_object_set_nocheck(object, key, value); {
if(json_object_set_nocheck(object, key, value))
{
res = -1;
break;
}
}
} }
return 0; hashtable_del(parents, loop_key);
return res;
}
int json_object_update_recursive(json_t *object, json_t *other)
{
int res;
hashtable_t parents_set;
if (hashtable_init(&parents_set))
return -1;
res = do_object_update_recursive(object, other, &parents_set);
hashtable_close(&parents_set);
return res;
} }
void *json_object_iter(json_t *json) void *json_object_iter(json_t *json)
@ -349,7 +381,7 @@ static json_t *json_object_deep_copy(const json_t *object, hashtable_t *parents)
result = json_object(); result = json_object();
if(!result) if(!result)
return NULL; goto out;
/* Cannot use json_object_foreach because object has to be cast /* Cannot use json_object_foreach because object has to be cast
non-const */ non-const */
@ -368,6 +400,8 @@ static json_t *json_object_deep_copy(const json_t *object, hashtable_t *parents)
} }
iter = json_object_iter_next((json_t *)object, iter); iter = json_object_iter_next((json_t *)object, iter);
} }
out:
hashtable_del(parents, loop_key); hashtable_del(parents, loop_key);
return result; return result;
@ -668,7 +702,7 @@ static json_t *json_array_deep_copy(const json_t *array, hashtable_t *parents)
result = json_array(); result = json_array();
if(!result) if(!result)
return NULL; goto out;
for(i = 0; i < json_array_size(array); i++) for(i = 0; i < json_array_size(array); i++)
{ {
@ -679,6 +713,8 @@ static json_t *json_array_deep_copy(const json_t *array, hashtable_t *parents)
break; break;
} }
} }
out:
hashtable_del(parents, loop_key); hashtable_del(parents, loop_key);
return result; return result;

View File

@ -209,7 +209,7 @@ static void test_conditional_updates()
static void test_recursive_updates() static void test_recursive_updates()
{ {
json_t *invalid, *object, *other; json_t *invalid, *object, *other, *barBefore, *barAfter;
invalid = json_integer(42); invalid = json_integer(42);
@ -241,6 +241,10 @@ static void test_recursive_updates()
object = json_pack("{sis{si}}", "foo", 1, "bar", "baz", 2); object = json_pack("{sis{si}}", "foo", 1, "bar", "baz", 2);
other = json_pack("{s{si}}", "bar", "baz", 3); other = json_pack("{s{si}}", "bar", "baz", 3);
barBefore = json_object_get(object, "bar");
if(!barBefore)
fail("can't get bar object before json_object_update_recursive");
if(json_object_update_recursive(object, other)) if(json_object_update_recursive(object, other))
fail("json_object_update_recursive failed"); fail("json_object_update_recursive failed");
@ -254,6 +258,33 @@ static void test_recursive_updates()
if(json_integer_value(json_object_get(json_object_get(object, "bar"), "baz")) != 3) if(json_integer_value(json_object_get(json_object_get(object, "bar"), "baz")) != 3)
fail("json_object_update_recursive failed to update nested value"); fail("json_object_update_recursive failed to update nested value");
barAfter = json_object_get(object, "bar");
if(!barAfter)
fail("can't get bar object after json_object_update_recursive");
if(barBefore != barAfter)
fail("bar object reference changed after json_object_update_recursive");
json_decref(object);
json_decref(other);
/* check circular reference */
object = json_pack("{s{s{si}}}", "foo", "bar", "baz", 2);
other = json_pack("{s{s{si}}}", "foo", "bar", "baz", 2);
json_object_set(json_object_get(json_object_get(object, "foo"), "bar"), "baz",
json_object_get(other, "foo"));
json_object_set(json_object_get(json_object_get(other, "foo"), "bar"), "baz",
json_object_get(other, "foo"));
if(!json_object_update_recursive(object, other))
fail("json_object_update_recursive update a circular reference!");
json_object_set_new(json_object_get(json_object_get(other, "foo"), "bar"), "baz",
json_integer(1));
if(json_object_update_recursive(object, other))
fail("json_object_update_recursive failed!");
json_decref(object); json_decref(object);
json_decref(other); json_decref(other);
} }