]> granicus.if.org Git - json-c/commitdiff
Add alternative iterator implementation
authorKeith Derrick <keith.derrick@palm.com>
Thu, 12 Apr 2012 16:54:21 +0000 (09:54 -0700)
committerKeith Derrick <keith.derrick@palm.com>
Thu, 12 Apr 2012 18:51:23 +0000 (11:51 -0700)
json.h
json_object_iterator.c [new file with mode: 0644]
json_object_iterator.h [new file with mode: 0644]

diff --git a/json.h b/json.h
index a5a3432b2c84bcc4f8e81af17539ebebaebf645f..d49715b3b5ecc9ff95b7bd8d5efd52ae85ac0956 100644 (file)
--- a/json.h
+++ b/json.h
@@ -3,6 +3,7 @@
  *
  * Copyright (c) 2004, 2005 Metaparadigm Pte. Ltd.
  * Michael Clark <michael@metaparadigm.com>
+ * Copyright (c) 2009 Hewlett-Packard Development Company, L.P.
  *
  * This library is free software; you can redistribute it and/or modify
  * it under the terms of the MIT license. See COPYING for details.
@@ -23,6 +24,7 @@ extern "C" {
 #include "json_util.h"
 #include "json_object.h"
 #include "json_tokener.h"
+#include "json_object_iterator.h"
 
 #ifdef __cplusplus
 }
diff --git a/json_object_iterator.c b/json_object_iterator.c
new file mode 100644 (file)
index 0000000..b887eb2
--- /dev/null
@@ -0,0 +1,169 @@
+/** 
+*******************************************************************************
+* @file cjson_object_iterator.c
+* 
+* Copyright (c) 2009 Hewlett-Packard Development Company, L.P.
+*
+* This library is free software; you can redistribute it and/or modify
+* it under the terms of the MIT license. See COPYING for details.
+*
+* @brief  cjson forces clients to use its private data
+*         structures for JSON Object iteration.  This API
+*         implementation corrects that by abstracting the
+*         private cjson details.
+* 
+*******************************************************************************
+*/
+
+#include <stddef.h>
+#include <stdbool.h>
+
+#include "json.h"
+#include "json_object_private.h"
+
+#include "json_object_iterator.h"
+
+/**
+ * How It Works
+ * 
+ * For each JSON Object, cjson maintains a linked list of zero
+ * or more lh_entry (link-hash entry) structures inside the
+ * Object's link-hash table (lh_table).
+ * 
+ * Each lh_entry structure on the JSON Object's linked list
+ * represents a single name/value pair.  The "next" field of the
+ * last lh_entry in the list is set to NULL, which terminates
+ * the list.
+ * 
+ * We represent a valid iterator that refers to an actual
+ * name/value pair via a pointer to the pair's lh_entry
+ * structure set as the iterator's opaque_ field.
+ * 
+ * We follow cjson's current pair list representation by
+ * representing a valid "end" iterator (one that refers past the
+ * last pair) with a NULL value in the iterator's opaque_ field.
+ * 
+ * A JSON Object without any pairs in it will have the "head"
+ * field of its lh_table structure set to NULL.  For such an
+ * object, json_object_iter_begin will return an iterator with
+ * the opaque_ field set to NULL, which is equivalent to the
+ * "end" iterator.
+ * 
+ * When iterating, we simply update the iterator's opaque_ field
+ * to point to the next lh_entry structure in the linked list.
+ * opaque_ will become NULL once we iterate past the last pair
+ * in the list, which makes the iterator equivalent to the "end"
+ * iterator.
+ */
+
+/// Our current representation of the "end" iterator;
+/// 
+/// @note May not always be NULL
+static const void* kObjectEndIterValue = NULL;
+
+/**
+ * ****************************************************************************
+ */
+struct json_object_iterator
+json_object_iter_begin(struct json_object* obj)
+{
+    struct json_object_iterator iter;
+    struct lh_table* pTable;
+
+    /// @note json_object_get_object will return NULL if passed NULL
+    ///       or a non-json_type_object instance
+    pTable = json_object_get_object(obj);
+    JASSERT(NULL != pTable);
+
+    /// @note For a pair-less Object, head is NULL, which matches our
+    ///       definition of the "end" iterator
+    iter.opaque_ = pTable->head;
+    return iter;
+}
+
+/**
+ * ****************************************************************************
+ */
+struct json_object_iterator
+json_object_iter_end(const struct json_object* obj)
+{
+    struct json_object_iterator iter;
+
+    JASSERT(NULL != obj);
+    JASSERT(json_object_is_type(obj, json_type_object));
+
+    iter.opaque_ = kObjectEndIterValue;
+
+    return iter;
+}
+
+/**
+ * ****************************************************************************
+ */
+void
+json_object_iter_next(struct json_object_iterator* iter)
+{
+    JASSERT(NULL != iter);
+    JASSERT(kObjectEndIterValue != iter->opaque_);
+
+    iter->opaque_ = ((struct lh_entry *)iter->opaque_)->next;
+}
+
+
+/**
+ * ****************************************************************************
+ */
+const char*
+json_object_iter_peek_name(const struct json_object_iterator* iter)
+{
+    JASSERT(NULL != iter);
+    JASSERT(kObjectEndIterValue != iter->opaque_);
+
+    return (const char*)(((struct lh_entry *)iter->opaque_)->k);
+}
+
+
+/**
+ * ****************************************************************************
+ */
+struct json_object*
+json_object_iter_peek_value(const struct json_object_iterator* iter)
+{
+    JASSERT(NULL != iter);
+    JASSERT(kObjectEndIterValue != iter->opaque_);
+
+    return (struct json_object*)(((struct lh_entry *)iter->opaque_)->v);
+}
+
+
+/**
+ * ****************************************************************************
+ */
+bool
+json_object_iter_equal(const struct json_object_iterator* iter1,
+                       const struct json_object_iterator* iter2)
+{
+    JASSERT(NULL != iter1);
+    JASSERT(NULL != iter2);
+
+    return (iter1->opaque_ == iter2->opaque_);
+}
+
+
+/**
+ * ****************************************************************************
+ */
+struct json_object_iterator
+json_object_iter_init_default(void)
+{
+    struct json_object_iterator iter;
+
+    /**
+     * @note Make this a negative, invalid value, such that
+     *       accidental access to it would likely be trapped by the
+     *       hardware as an invalid address.
+     */
+    iter.opaque_ = NULL;
+
+    return iter;
+}
diff --git a/json_object_iterator.h b/json_object_iterator.h
new file mode 100644 (file)
index 0000000..21feaf5
--- /dev/null
@@ -0,0 +1,254 @@
+/** 
+*******************************************************************************
+* @file json_object_iterator.h
+* 
+* Copyright (c) 2009 Hewlett-Packard Development Company, L.P.
+*
+* This library is free software; you can redistribute it and/or modify
+* it under the terms of the MIT license. See COPYING for details.
+* 
+* @brief  cjson forces clients to use its private data
+*         structures for JSON Object iteration.  This API
+*         corrects that by abstracting the private cjson
+*         details.
+* 
+*         The intention is to add this API (and its
+*         implementation) to Palm's version of the cjson
+*         library, at which point it can be removed from the
+*         Wireless System Framework library implementation.
+* 
+* API attributes:
+*   * Thread-safe: NO
+*   * Re-entrant: NO
+* 
+*******************************************************************************
+*/
+
+
+#ifndef JSON_OBJECT_ITERATOR_H
+#define JSON_OBJECT_ITERATOR_H
+
+#include <stddef.h>
+#include <stdbool.h>
+
+#ifdef __cplusplus 
+extern "C" {
+#endif
+
+/**
+ * Forward declaration for the opaque iterator information.
+ */
+struct json_object_iter_info_;
+
+/**
+ * The opaque iterator that references a name/value pair within
+ * a JSON Object intance or the "end" iterator value.
+ */
+struct json_object_iterator {
+    const void* opaque_;
+};
+
+
+/**
+ * forward declaration of cjson's JSON value instance structure
+ */
+struct json_object;
+
+
+/**
+ * Initializes an iterator structure to a "default" value that
+ * is convenient for initializing an iterator variable to a
+ * default state (e.g., initialization list in a class'
+ * constructor).
+ * 
+ * @code 
+ * struct json_object_iterator iter = json_object_iter_init_default();
+ * MyClass() : iter_(json_object_iter_init_default())
+ * @endcode
+ * 
+ * @note The initialized value doesn't reference any specific
+ *       pair, is considered an invalid iterator, and MUST NOT
+ *       be passed to any cjson API that expects a valid
+ *       iterator.
+ * 
+ * @note User and internal code MUST NOT make any assumptions
+ *       about and dependencies on the value of the "default"
+ *       iterator value.
+ * 
+ * @return json_object_iterator
+ */
+struct json_object_iterator
+json_object_iter_init_default(void);
+
+/** Retrieves an iterator to the first pair of the JSON Object.
+ * 
+ * @note WARNING: Any modification of the underlying pair
+ *       invalidates all iterators to that pair.
+ * 
+ * @param obj JSON Object instance (MUST be of type
+ *             json_type_object)
+ *
+ * @return json_object_iterator If the JSON Object has at
+ *              least one pair, on return, the iterator refers
+ *              to the first pair. If the JSON Object doesn't
+ *              have any pairs, the returned iterator is
+ *              equivalent to the "end" iterator for the same
+ *              JSON Object instance.
+ * 
+ * @code    
+ * struct json_object_iterator it;
+ * struct json_object_iterator itEnd;
+ * struct json_object* obj = json_tokener_parse(
+ *        "{'first':'george', 'age':100}");
+ * json_object_iter_begin(obj, &it);
+ * json_object_iter_end(obj, &itEnd);
+ * while (!json_object_iter_equal(&it, &itEnd)) {
+ *     printf("%s\n",
+ *            json_object_iter_peek_name(&it));
+ *     json_object_iter_next(&it);
+ * }
+ *
+ * struct json_object* obj = json_tokener_parse(
+ *        "{'first':'george', 'age':100}");
+ * struct json_object_iterator it;
+ * bool iterable = json_object_iter_begin(&it);
+ * if (iterable) {
+ *     do  {
+ *         printf("%s\n", json_object_iter_peek_name(&it));
+ *     } while (json_object_iter_next(&it));
+ * }
+ * @endcode
+ */
+struct json_object_iterator
+json_object_iter_begin(struct json_object* obj);
+
+/** Retrieves the iterator that represents the position beyond the
+ *  last pair of the given JSON Object instance.
+ * 
+ *  @note WARNING: Do NOT write code that assumes that the "end"
+ *        iterator value is NULL, even if it is so in a
+ *        particular instance of the implementation.
+ * 
+ *  @note The reason we do not (and MUST NOT) provide
+ *        "json_object_iter_is_end(json_object_iterator* iter)"
+ *        type of API is because it would limit the underlying
+ *        representation of name/value containment (or force us
+ *        to add additional, otherwise unnecessary, fields to
+ *        the iterator structure). The "end" iterator and the
+ *        equality test method, on the other hand, permit us to
+ *        cleanly abstract pretty much any reasonable underlying
+ *        representation without burdening the iterator
+ *        structure with unnecessary data.
+ *
+ *  @note For performance reasons, memoize the "end" iterator prior
+ *        to any loop.
+ * 
+ * @param obj JSON Object instance (MUST be of type
+ *             json_type_object)
+ *
+ * @return json_object_iterator On return, the iterator refers
+ *              to the "end" of the Object instance's pairs
+ *              (i.e., NOT the last pair, but "beyond the last
+ *              pair" value)
+ */
+struct json_object_iterator
+json_object_iter_end(const struct json_object* obj);
+
+/** Returns an iterator to the next pair, if any
+ * 
+ * @note WARNING: Any modification of the underlying pair
+ *       invalidates all iterators to that pair.
+ * 
+ * @param iter Iterator that references a name/value pair; 
+ * 
+ * @param iter [IN/OUT] Pointer to iterator that references a
+ *         name/value pair; MUST be a valid, non-end iterator.
+ *         WARNING: bad things will happen if invalid or "end"
+ *         iterator is passed. Upon return will contain the
+ *         reference to the next pair if there is one; if there
+ *         are no more pairs, will contain the "end" iterator
+ *         value, which may be compared against the return value
+ *         of json_object_iter_end() for the same JSON Object
+ *         instance.
+ */
+void
+json_object_iter_next(struct json_object_iterator* iter);
+
+
+/** Returns a const pointer to the name of the pair referenced
+ *  by the given iterator.
+ * 
+ * @param iter pointer to iterator that references a name/value
+ *             pair; MUST be a valid, non-end iterator.
+ *             WARNING: bad things will happen if invalid or
+ *             "end" iterator is passed.
+ * 
+ * @return const char* Pointer to the name of the rerferenced
+ *         name/value pair.  The name memory belongs to the
+ *         name/value pair, will be freed when the pair is
+ *         deleted or modified, and MUST NOT be modified or
+ *         freed by the user.
+ */
+const char*
+json_object_iter_peek_name(const struct json_object_iterator* iter);
+
+
+/** Returns a pointer to the cjson instance representing the
+ *  value of the referenced name/value pair, without altering
+ *  the instance's reference count.
+ * 
+ * @param iter pointer to iterator that references a name/value
+ *             pair; MUST be a valid, non-end iterator.
+ *             WARNING: bad things will happen if invalid or
+ *             "end" iterator is passed.
+ * 
+ * @return struct json_object* Pointer to the cjson value
+ *         instance of the referenced name/value pair;  the
+ *         value's reference count is not changed by this
+ *         function: if you plan to hold on to this cjson node,
+ *         take a look at json_object_get() and
+ *         json_object_put(). IMPORTANT: cjson API represents
+ *         the JSON Null value as a NULL json_object instance
+ *         pointer.
+ */
+struct json_object*
+json_object_iter_peek_value(const struct json_object_iterator* iter);
+
+
+/** Tests two iterators for equality.  Typically used to test
+ *  for end of iteration by comparing an iterator to the
+ *  corresponding "end" iterator (that was derived from the same
+ *  JSON Object instance).
+ * 
+ *  @note The reason we do not (and MUST NOT) provide
+ *        "json_object_iter_is_end(json_object_iterator* iter)"
+ *        type of API is because it would limit the underlying
+ *        representation of name/value containment (or force us
+ *        to add additional, otherwise unnecessary, fields to
+ *        the iterator structure). The equality test method, on
+ *        the other hand, permits us to cleanly abstract pretty
+ *        much any reasonable underlying representation.
+ * 
+ * @param iter1 Pointer to first valid, non-NULL iterator
+ * @param iter2 POinter to second valid, non-NULL iterator
+ * 
+ * @note WARNING: if a NULL iterator pointer or an uninitialized
+ *       or invalid iterator, or iterators derived from
+ *       different JSON Object instances are passed, bad things
+ *       will happen!
+ * 
+ * @return bool non-zero if iterators are equal (i.e., both
+ *         reference the same name/value pair or are both at
+ *         "end"); zero if they are not equal.
+ */
+bool
+json_object_iter_equal(const struct json_object_iterator* iter1,
+                       const struct json_object_iterator* iter2);
+
+
+#ifdef __cplusplus
+}
+#endif
+
+
+#endif // JSON_OBJECT_ITERATOR_H