argument.
+Circular References
+-------------------
+
+A circular reference is created when an object or an array is,
+directly or indirectly, inserted inside itself. The direct case is
+simple::
+
+ json_t *obj = json_object();
+ json_object_set(obj, "foo", obj);
+
+Jansson will refuse to do this, and :cfunc:`json_object_set()` (and
+all the other such functions for objects and arrays) will return with
+an error status. The indirect case is the dangerous one::
+
+ json_t *arr1 = json_array(), *arr2 = json_array();
+ json_array_append(arr1, arr2);
+ json_array_append(arr2, arr1);
+
+In this example, the array ``arr2`` is contained in the array
+``arr1``, and vice versa. Jansson cannot check for this kind of
+indirect circular references without a performance hit, so it's up to
+the user to avoid them.
+
+If a circular reference is created, the memory consumed by the values
+cannot be freed by :cfunc:`json_decref()`. The reference counts never
+drops to zero because the values are keeping the circular reference to
+themselves. Moreover, trying to encode the values with any of the
+encoding functions will fail. The encoder detects circular references
+and returns an error status.
+
+
True, False and Null
====================
#include <string.h>
#include <jansson.h>
+#include "jansson_private.h"
#include "strbuffer.h"
#define MAX_INTEGER_STR_LENGTH 100
case JSON_ARRAY:
{
int i;
- int n = json_array_size(json);
+ int n;
+ json_array_t *array;
+
+ /* detect circular references */
+ array = json_to_array(json);
+ if(array->visited)
+ return -1;
+ array->visited = 1;
+
+ n = json_array_size(json);
if(dump("[", 1, data))
return -1;
return -1;
}
}
+
+ array->visited = 0;
return dump("]", 1, data);
}
case JSON_OBJECT:
{
- void *iter = json_object_iter((json_t *)json);
+ json_object_t *object;
+ void *iter;
+
+ /* detect circular references */
+ object = json_to_object(json);
+ if(object->visited)
+ return -1;
+ object->visited = 1;
+
+ iter = json_object_iter((json_t *)json);
if(dump("{", 1, data))
return -1;
iter = next;
}
+
+ object->visited = 0;
return dump("}", 1, data);
}
#ifndef JANSSON_PRIVATE_H
#define JANSSON_PRIVATE_H
+#include "jansson.h"
+#include "hashtable.h"
+
+#define container_of(ptr_, type_, member_) \
+ ((type_ *)((char *)ptr_ - (size_t)&((type_ *)0)->member_))
+
+typedef struct {
+ json_t json;
+ hashtable_t hashtable;
+ int visited;
+} json_object_t;
+
+typedef struct {
+ json_t json;
+ unsigned int size;
+ unsigned int entries;
+ json_t **table;
+ int visited;
+} json_array_t;
+
+typedef struct {
+ json_t json;
+ char *value;
+} json_string_t;
+
+typedef struct {
+ json_t json;
+ double value;
+} json_real_t;
+
+typedef struct {
+ json_t json;
+ int value;
+} json_integer_t;
+
+#define json_to_object(json_) container_of(json_, json_object_t, json)
+#define json_to_array(json_) container_of(json_, json_array_t, json)
+#define json_to_string(json_) container_of(json_, json_string_t, json)
+#define json_to_real(json_) container_of(json_, json_real_t, json)
+#define json_to_integer(json_) container_of(json_, json_integer_t, json)
+
int json_object_set_nocheck(json_t *json, const char *key, json_t *value);
json_t *json_string_nocheck(const char *value);
-
#endif
#include "utf.h"
#include "util.h"
-#define container_of(ptr_, type_, member_) \
- ((type_ *)((char *)ptr_ - (size_t)&((type_ *)0)->member_))
-
-typedef struct {
- json_t json;
- hashtable_t hashtable;
-} json_object_t;
-
-typedef struct {
- json_t json;
- unsigned int size;
- unsigned int entries;
- json_t **table;
-} json_array_t;
-
-typedef struct {
- json_t json;
- char *value;
-} json_string_t;
-
-typedef struct {
- json_t json;
- double value;
-} json_real_t;
-
-typedef struct {
- json_t json;
- int value;
-} json_integer_t;
-
-#define json_to_object(json_) container_of(json_, json_object_t, json)
-#define json_to_array(json_) container_of(json_, json_array_t, json)
-#define json_to_string(json_) container_of(json_, json_string_t, json)
-#define json_to_real(json_) container_of(json_, json_real_t, json)
-#define json_to_integer(json_) container_of(json_, json_integer_t, json)
static inline void json_init(json_t *json, json_type type)
{
free(object);
return NULL;
}
+
+ object->visited = 0;
+
return &object->json;
}
if(!key || !value)
return -1;
- if(!json_is_object(json))
+ if(!json_is_object(json) || json == value)
{
json_decref(value);
return -1;
return NULL;
}
+ array->visited = 0;
+
return &array->json;
}
if(!value)
return -1;
- if(!json_is_array(json))
+ if(!json_is_array(json) || json == value)
{
json_decref(value);
return -1;
if(!value)
return -1;
- if(!json_is_array(json))
+ if(!json_is_array(json) || json == value)
{
json_decref(value);
return -1;
if(!value)
return -1;
- if(!json_is_array(json)) {
+ if(!json_is_array(json) || json == value) {
json_decref(value);
return -1;
}
json_decref(array2);
}
+static void test_circular()
+{
+ json_t *array1, *array2;
+
+ /* the simple cases are checked */
+
+ array1 = json_array();
+ if(!array1)
+ fail("unable to create array");
+
+ if(json_array_append(array1, array1) == 0)
+ fail("able to append self");
+
+ if(json_array_insert(array1, 0, array1) == 0)
+ fail("able to insert self");
+
+ if(json_array_append_new(array1, json_true()))
+ fail("failed to append true");
+
+ if(json_array_set(array1, 0, array1) == 0)
+ fail("able to set self");
+
+ json_decref(array1);
+
+
+ /* create circular references */
+
+ array1 = json_array();
+ array2 = json_array();
+ if(!array1 || !array2)
+ fail("unable to create array");
+
+ if(json_array_append(array1, array2) ||
+ json_array_append(array2, array1))
+ fail("unable to append");
+
+ /* circularity is detected when dumping */
+ if(json_dumps(array1, 0) != NULL)
+ fail("able to dump circulars");
+
+ /* decref twice to deal with the circular references */
+ json_decref(array1);
+ json_decref(array2);
+ json_decref(array1);
+}
+
int main()
{
test_remove();
test_clear();
test_extend();
+ test_circular();
return 0;
}
json_decref(object);
}
+static void test_circular()
+{
+ json_t *object1, *object2;
+
+ object1 = json_object();
+ object2 = json_object();
+ if(!object1 || !object2)
+ fail("unable to create object");
+
+ /* the simple case is checked */
+ if(json_object_set(object1, "a", object1) == 0)
+ fail("able to set self");
+
+ /* create circular references */
+ if(json_object_set(object1, "a", object2) ||
+ json_object_set(object2, "a", object1))
+ fail("unable to set value");
+
+ /* circularity is detected when dumping */
+ if(json_dumps(object1, 0) != NULL)
+ fail("able to dump circulars");
+
+ /* decref twice to deal with the circular references */
+ json_decref(object1);
+ json_decref(object2);
+ json_decref(object1);
+}
+
static void test_misc()
{
json_t *object, *string, *other_string, *value;
test_misc();
test_clear();
test_update();
+ test_circular();
return 0;
}