Implement json_object_foreach()
authorPetri Lehtinen <petri@digip.org>
Tue, 24 Jan 2012 18:37:08 +0000 (20:37 +0200)
committerPetri Lehtinen <petri@digip.org>
Tue, 24 Jan 2012 19:01:24 +0000 (21:01 +0200)
Also change many places to use it internally to replace hand-crafted
iteration.

Closes #45, #46.

doc/apiref.rst
src/hashtable.h
src/jansson.h
src/value.c
test/suites/api/check-exports
test/suites/api/test_object.c

index 5003a04..3820b3f 100644 (file)
@@ -565,6 +565,36 @@ Unicode string and the value is any JSON value.
    existing keys. Returns 0 on success or -1 on error.
 
 
+The following macro can be used to iterate through all key-value pairs
+in an object.
+
+.. function:: json_object_foreach(object, key, value)
+
+   Iterate over every key-value pair of ``object``, running the block
+   of code that follows each time with the proper values set to
+   variables ``key`` and ``value``, of types :type:`const char *` and
+   :type:`json_t *` respectively. Example::
+
+       /* obj is a JSON object */
+       const char *key;
+       json_t *value;
+
+       json_object_foreach(obj, key, value) {
+           /* block of code that uses key and value */
+       }
+
+   The items are not returned in any particular order.
+
+   This macro expands to an ordinary ``for`` statement upon
+   preprocessing, so its performance is equivalent to that of
+   hand-written iteration code using the object iteration protocol
+   (see below). The main advantage of this macro is that it abstracts
+   away the complexity behind iteration, and makes for shorter, more
+   concise code.
+
+   .. versionadded:: 2.3
+
+
 The following functions implement an iteration protocol for objects,
 allowing to iterate through all key-value pairs in an object. The
 items are not returned in any particular order, as this would require
@@ -610,11 +640,21 @@ sorting due to the internal hashtable implementation.
    *value*. This is useful when *value* is newly created and not used
    after the call.
 
+.. function:: void *json_object_key_to_iter(const char *key)
+
+   Like :func:`json_object_iter_at()`, but much faster. Only works for
+   values returned by :func:`json_object_iter_key()`. Using other keys
+   will lead to segfaults. This function is used internally to
+   implement :func:`json_object_foreach`.
+
+   .. versionadded:: 2.3
+
 The iteration protocol can be used for example as follows::
 
    /* obj is a JSON object */
    const char *key;
    json_t *value;
+
    void *iter = json_object_iter(obj);
    while(iter)
    {
index e8d5ccb..beee8ea 100644 (file)
@@ -40,6 +40,10 @@ typedef struct hashtable {
     struct hashtable_list list;
 } hashtable_t;
 
+
+#define hashtable_key_to_iter(key_) \
+    (&(container_of(key_, struct hashtable_pair, key)->list))
+
 /**
  * hashtable_init - Initialize a hashtable object
  *
index ca9ac11..274dbad 100644 (file)
@@ -132,11 +132,17 @@ int json_object_clear(json_t *object);
 int json_object_update(json_t *object, json_t *other);
 void *json_object_iter(json_t *object);
 void *json_object_iter_at(json_t *object, const char *key);
+void *json_object_key_to_iter(const char *key);
 void *json_object_iter_next(json_t *object, void *iter);
 const char *json_object_iter_key(void *iter);
 json_t *json_object_iter_value(void *iter);
 int json_object_iter_set_new(json_t *object, void *iter, json_t *value);
 
+#define json_object_foreach(object, key, value) \
+    for(key = json_object_iter_key(json_object_iter(object)); \
+        key && (value = json_object_iter_value(json_object_key_to_iter(key))); \
+        key = json_object_iter_key(json_object_iter_next(object, json_object_key_to_iter(key))))
+
 static JSON_INLINE
 int json_object_set(json_t *object, const char *key, json_t *value)
 {
index c728427..eadf596 100644 (file)
@@ -135,23 +135,15 @@ int json_object_clear(json_t *json)
 
 int json_object_update(json_t *object, json_t *other)
 {
-    void *iter;
+    const char *key;
+    json_t *value;
 
     if(!json_is_object(object) || !json_is_object(other))
         return -1;
 
-    iter = json_object_iter(other);
-    while(iter) {
-        const char *key;
-        json_t *value;
-
-        key = json_object_iter_key(iter);
-        value = json_object_iter_value(iter);
-
+    json_object_foreach(other, key, value) {
         if(json_object_set_nocheck(object, key, value))
             return -1;
-
-        iter = json_object_iter_next(other, iter);
     }
 
     return 0;
@@ -215,27 +207,27 @@ int json_object_iter_set_new(json_t *json, void *iter, json_t *value)
     return 0;
 }
 
+void *json_object_key_to_iter(const char *key)
+{
+    if(!key)
+        return NULL;
+
+    return hashtable_key_to_iter(key);
+}
+
 static int json_object_equal(json_t *object1, json_t *object2)
 {
-    void *iter;
+    const char *key;
+    json_t *value1, *value2;
 
     if(json_object_size(object1) != json_object_size(object2))
         return 0;
 
-    iter = json_object_iter(object1);
-    while(iter)
-    {
-        const char *key;
-        json_t *value1, *value2;
-
-        key = json_object_iter_key(iter);
-        value1 = json_object_iter_value(iter);
+    json_object_foreach(object1, key, value1) {
         value2 = json_object_get(object2, key);
 
         if(!json_equal(value1, value2))
             return 0;
-
-        iter = json_object_iter_next(object1, iter);
     }
 
     return 1;
@@ -244,50 +236,34 @@ static int json_object_equal(json_t *object1, json_t *object2)
 static json_t *json_object_copy(json_t *object)
 {
     json_t *result;
-    void *iter;
+
+    const char *key;
+    json_t *value;
 
     result = json_object();
     if(!result)
         return NULL;
 
-    iter = json_object_iter(object);
-    while(iter)
-    {
-        const char *key;
-        json_t *value;
-
-        key = json_object_iter_key(iter);
-        value = json_object_iter_value(iter);
+    json_object_foreach(object, key, value)
         json_object_set_nocheck(result, key, value);
 
-        iter = json_object_iter_next(object, iter);
-    }
-
     return result;
 }
 
 static json_t *json_object_deep_copy(json_t *object)
 {
     json_t *result;
-    void *iter;
+
+    const char *key;
+    json_t *value;
 
     result = json_object();
     if(!result)
         return NULL;
 
-    iter = json_object_iter(object);
-    while(iter)
-    {
-        const char *key;
-        json_t *value;
-
-        key = json_object_iter_key(iter);
-        value = json_object_iter_value(iter);
+    json_object_foreach(object, key, value)
         json_object_set_new_nocheck(result, key, json_deep_copy(value));
 
-        iter = json_object_iter_next(object, iter);
-    }
-
     return result;
 }
 
index 802d683..0af0dd0 100755 (executable)
@@ -44,6 +44,7 @@ json_object_iter_next
 json_object_iter_key
 json_object_iter_value
 json_object_iter_set_new
+json_object_key_to_iter
 json_dumps
 json_dumpf
 json_dump_file
index 59f85b9..4a19016 100644 (file)
@@ -437,6 +437,24 @@ static void test_preserve_order()
     json_decref(object);
 }
 
+static void test_foreach()
+{
+    const char *key;
+    json_t *object1, *object2, *value;
+
+    object1 = json_pack("{sisisi}", "foo", 1, "bar", 2, "baz", 3);
+    object2 = json_object();
+
+    json_object_foreach(object1, key, value)
+        json_object_set(object2, key, value);
+
+    if(!json_equal(object1, object2))
+        fail("json_object_foreach failed to iterate all key-value pairs");
+
+    json_decref(object1);
+    json_decref(object2);
+}
+
 static void run_tests()
 {
     test_misc();
@@ -446,4 +464,5 @@ static void run_tests()
     test_set_nocheck();
     test_iterators();
     test_preserve_order();
+    test_foreach();
 }