json_dump: Fix thread safety issue.
Circular reference detection in json_dump was not thread safe. Replace visited flag with a hashtable_t. Issue #387
This commit is contained in:
parent
dc3b313e91
commit
37e0ee4d48
96
src/dump.c
96
src/dump.c
@ -196,8 +196,17 @@ 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,
|
||||||
json_dump_callback_t dump, void *data)
|
hashtable_t *parents, json_dump_callback_t dump, void *data)
|
||||||
{
|
{
|
||||||
int embed = flags & JSON_EMBED;
|
int embed = flags & JSON_EMBED;
|
||||||
|
|
||||||
@ -251,58 +260,53 @@ static int do_dump(const json_t *json, size_t flags, int depth,
|
|||||||
{
|
{
|
||||||
size_t n;
|
size_t n;
|
||||||
size_t i;
|
size_t i;
|
||||||
|
/* Space for "0x", double the sizeof a pointer for the hex and a terminator. */
|
||||||
json_array_t *array;
|
char key[2 + (sizeof(json) * 2) + 1];
|
||||||
|
|
||||||
/* detect circular references */
|
/* detect circular references */
|
||||||
array = json_to_array(json);
|
if (loop_check(parents, json, key, sizeof(key)))
|
||||||
if(array->visited)
|
return -1;
|
||||||
goto array_error;
|
|
||||||
array->visited = 1;
|
|
||||||
|
|
||||||
n = json_array_size(json);
|
n = json_array_size(json);
|
||||||
|
|
||||||
if(!embed && dump("[", 1, data))
|
if(!embed && dump("[", 1, data))
|
||||||
goto array_error;
|
return -1;
|
||||||
if(n == 0) {
|
if(n == 0) {
|
||||||
array->visited = 0;
|
hashtable_del(parents, key);
|
||||||
return embed ? 0 : dump("]", 1, data);
|
return embed ? 0 : dump("]", 1, data);
|
||||||
}
|
}
|
||||||
if(dump_indent(flags, depth + 1, 0, dump, data))
|
if(dump_indent(flags, depth + 1, 0, dump, data))
|
||||||
goto array_error;
|
return -1;
|
||||||
|
|
||||||
for(i = 0; i < n; ++i) {
|
for(i = 0; i < n; ++i) {
|
||||||
if(do_dump(json_array_get(json, i), flags, depth + 1,
|
if(do_dump(json_array_get(json, i), flags, depth + 1,
|
||||||
dump, data))
|
parents, dump, data))
|
||||||
goto array_error;
|
return -1;
|
||||||
|
|
||||||
if(i < n - 1)
|
if(i < n - 1)
|
||||||
{
|
{
|
||||||
if(dump(",", 1, data) ||
|
if(dump(",", 1, data) ||
|
||||||
dump_indent(flags, depth + 1, 1, dump, data))
|
dump_indent(flags, depth + 1, 1, dump, data))
|
||||||
goto array_error;
|
return -1;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
if(dump_indent(flags, depth, 0, dump, data))
|
if(dump_indent(flags, depth, 0, dump, data))
|
||||||
goto array_error;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
array->visited = 0;
|
|
||||||
return embed ? 0 : dump("]", 1, data);
|
|
||||||
|
|
||||||
array_error:
|
|
||||||
array->visited = 0;
|
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
hashtable_del(parents, key);
|
||||||
|
return embed ? 0 : dump("]", 1, data);
|
||||||
|
}
|
||||||
|
|
||||||
case JSON_OBJECT:
|
case JSON_OBJECT:
|
||||||
{
|
{
|
||||||
json_object_t *object;
|
|
||||||
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 key[2 + (sizeof(json) * 2) + 1];
|
||||||
|
|
||||||
if(flags & JSON_COMPACT) {
|
if(flags & JSON_COMPACT) {
|
||||||
separator = ":";
|
separator = ":";
|
||||||
@ -314,21 +318,19 @@ static int do_dump(const json_t *json, size_t flags, int depth,
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* detect circular references */
|
/* detect circular references */
|
||||||
object = json_to_object(json);
|
if (loop_check(parents, json, key, sizeof(key)))
|
||||||
if(object->visited)
|
return -1;
|
||||||
goto object_error;
|
|
||||||
object->visited = 1;
|
|
||||||
|
|
||||||
iter = json_object_iter((json_t *)json);
|
iter = json_object_iter((json_t *)json);
|
||||||
|
|
||||||
if(!embed && dump("{", 1, data))
|
if(!embed && dump("{", 1, data))
|
||||||
goto object_error;
|
return -1;
|
||||||
if(!iter) {
|
if(!iter) {
|
||||||
object->visited = 0;
|
hashtable_del(parents, key);
|
||||||
return embed ? 0 : dump("}", 1, data);
|
return embed ? 0 : dump("}", 1, data);
|
||||||
}
|
}
|
||||||
if(dump_indent(flags, depth + 1, 0, dump, data))
|
if(dump_indent(flags, depth + 1, 0, dump, data))
|
||||||
goto object_error;
|
return -1;
|
||||||
|
|
||||||
if(flags & JSON_SORT_KEYS)
|
if(flags & JSON_SORT_KEYS)
|
||||||
{
|
{
|
||||||
@ -338,7 +340,7 @@ static int do_dump(const json_t *json, size_t flags, int depth,
|
|||||||
size = json_object_size(json);
|
size = json_object_size(json);
|
||||||
keys = jsonp_malloc(size * sizeof(const char *));
|
keys = jsonp_malloc(size * sizeof(const char *));
|
||||||
if(!keys)
|
if(!keys)
|
||||||
goto object_error;
|
return -1;
|
||||||
|
|
||||||
i = 0;
|
i = 0;
|
||||||
while(iter)
|
while(iter)
|
||||||
@ -362,10 +364,10 @@ static int do_dump(const json_t *json, size_t flags, int depth,
|
|||||||
|
|
||||||
dump_string(key, strlen(key), dump, data, flags);
|
dump_string(key, strlen(key), dump, data, flags);
|
||||||
if(dump(separator, separator_length, data) ||
|
if(dump(separator, separator_length, data) ||
|
||||||
do_dump(value, flags, depth + 1, dump, data))
|
do_dump(value, flags, depth + 1, parents, dump, data))
|
||||||
{
|
{
|
||||||
jsonp_free(keys);
|
jsonp_free(keys);
|
||||||
goto object_error;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
if(i < size - 1)
|
if(i < size - 1)
|
||||||
@ -374,7 +376,7 @@ static int do_dump(const json_t *json, size_t flags, int depth,
|
|||||||
dump_indent(flags, depth + 1, 1, dump, data))
|
dump_indent(flags, depth + 1, 1, dump, data))
|
||||||
{
|
{
|
||||||
jsonp_free(keys);
|
jsonp_free(keys);
|
||||||
goto object_error;
|
return -1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@ -382,7 +384,7 @@ static int do_dump(const json_t *json, size_t flags, int depth,
|
|||||||
if(dump_indent(flags, depth, 0, dump, data))
|
if(dump_indent(flags, depth, 0, dump, data))
|
||||||
{
|
{
|
||||||
jsonp_free(keys);
|
jsonp_free(keys);
|
||||||
goto object_error;
|
return -1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -401,31 +403,27 @@ static int do_dump(const json_t *json, size_t flags, int depth,
|
|||||||
dump_string(key, strlen(key), dump, data, flags);
|
dump_string(key, strlen(key), dump, data, flags);
|
||||||
if(dump(separator, separator_length, data) ||
|
if(dump(separator, separator_length, data) ||
|
||||||
do_dump(json_object_iter_value(iter), flags, depth + 1,
|
do_dump(json_object_iter_value(iter), flags, depth + 1,
|
||||||
dump, data))
|
parents, dump, data))
|
||||||
goto object_error;
|
return -1;
|
||||||
|
|
||||||
if(next)
|
if(next)
|
||||||
{
|
{
|
||||||
if(dump(",", 1, data) ||
|
if(dump(",", 1, data) ||
|
||||||
dump_indent(flags, depth + 1, 1, dump, data))
|
dump_indent(flags, depth + 1, 1, dump, data))
|
||||||
goto object_error;
|
return -1;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
if(dump_indent(flags, depth, 0, dump, data))
|
if(dump_indent(flags, depth, 0, dump, data))
|
||||||
goto object_error;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
iter = next;
|
iter = next;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
object->visited = 0;
|
hashtable_del(parents, key);
|
||||||
return embed ? 0 : dump("}", 1, data);
|
return embed ? 0 : dump("}", 1, data);
|
||||||
|
|
||||||
object_error:
|
|
||||||
object->visited = 0;
|
|
||||||
return -1;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
default:
|
default:
|
||||||
@ -489,10 +487,18 @@ int json_dump_file(const json_t *json, const char *path, size_t flags)
|
|||||||
|
|
||||||
int json_dump_callback(const json_t *json, json_dump_callback_t callback, void *data, size_t flags)
|
int json_dump_callback(const json_t *json, json_dump_callback_t callback, void *data, size_t flags)
|
||||||
{
|
{
|
||||||
|
int res;
|
||||||
|
hashtable_t parents_set;
|
||||||
|
|
||||||
if(!(flags & JSON_ENCODE_ANY)) {
|
if(!(flags & JSON_ENCODE_ANY)) {
|
||||||
if(!json_is_array(json) && !json_is_object(json))
|
if(!json_is_array(json) && !json_is_object(json))
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
return do_dump(json, flags, 0, callback, data);
|
if (hashtable_init(&parents_set))
|
||||||
|
return -1;
|
||||||
|
res = do_dump(json, flags, 0, &parents_set, callback, data);
|
||||||
|
hashtable_close(&parents_set);
|
||||||
|
|
||||||
|
return res;
|
||||||
}
|
}
|
||||||
|
@ -35,7 +35,6 @@
|
|||||||
typedef struct {
|
typedef struct {
|
||||||
json_t json;
|
json_t json;
|
||||||
hashtable_t hashtable;
|
hashtable_t hashtable;
|
||||||
int visited;
|
|
||||||
} json_object_t;
|
} json_object_t;
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
@ -43,7 +42,6 @@ typedef struct {
|
|||||||
size_t size;
|
size_t size;
|
||||||
size_t entries;
|
size_t entries;
|
||||||
json_t **table;
|
json_t **table;
|
||||||
int visited;
|
|
||||||
} json_array_t;
|
} json_array_t;
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
|
@ -67,8 +67,6 @@ json_t *json_object(void)
|
|||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
object->visited = 0;
|
|
||||||
|
|
||||||
return &object->json;
|
return &object->json;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -354,8 +352,6 @@ json_t *json_array(void)
|
|||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
array->visited = 0;
|
|
||||||
|
|
||||||
return &array->json;
|
return &array->json;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user