summaryrefslogtreecommitdiff
path: root/json-glib/json-object.c
diff options
context:
space:
mode:
authorEmmanuele Bassi <ebassi@gnome.org>2020-08-24 11:30:01 +0100
committerEmmanuele Bassi <ebassi@gnome.org>2020-08-24 17:44:07 +0100
commit695e45c310c795a74c279e1810a19cde775edb82 (patch)
tree97ee88c0b7782c9dfac41606d3b91d2b10231a8e /json-glib/json-object.c
parente4db3ea6ea0f8e6318f0d266e2015976b97f7716 (diff)
downloadjson-glib-695e45c310c795a74c279e1810a19cde775edb82.tar.gz
Add ordered iterator for JsonObject
The current iterator API does not guarantee the iteration order, as it's based off of the hash table iterator. JsonObject, though, can guarantee the iteration order—we do that for json_object_foreach_member(). Instead of changing the behaviour of json_object_iter_next(), we can add API to initialize and iterate a JsonObjectIter in insertion order.
Diffstat (limited to 'json-glib/json-object.c')
-rw-r--r--json-glib/json-object.c113
1 files changed, 111 insertions, 2 deletions
diff --git a/json-glib/json-object.c b/json-glib/json-object.c
index 8e6b293..c67b687 100644
--- a/json-glib/json-object.c
+++ b/json-glib/json-object.c
@@ -62,7 +62,8 @@ json_object_new (void)
JsonObject *object;
object = g_slice_new0 (JsonObject);
-
+
+ object->age = 0;
object->ref_count = 1;
object->members = g_hash_table_new_full (g_str_hash, g_str_equal,
g_free,
@@ -176,7 +177,10 @@ object_set_member_internal (JsonObject *object,
gchar *name = g_strdup (member_name);
if (g_hash_table_lookup (object->members, name) == NULL)
- g_queue_push_tail (&object->members_ordered, name);
+ {
+ g_queue_push_tail (&object->members_ordered, name);
+ object->age += 1;
+ }
else
{
GList *l;
@@ -1120,6 +1124,12 @@ json_object_iter_init (JsonObjectIter *iter,
* The order in which members are returned by the iterator is undefined. The
* iterator is invalidated if its #JsonObject is modified during iteration.
*
+ * You must use this function with a #JsonObjectIter initialized with
+ * json_object_iter_init(); using this function with an iterator initialized
+ * with json_object_iter_init_ordered() yields undefined behavior.
+ *
+ * See also: json_object_iter_next_ordered()
+ *
* Returns: %TRUE if @member_name and @member_node are valid; %FALSE if the end
* of the object has been reached
*
@@ -1140,3 +1150,102 @@ json_object_iter_next (JsonObjectIter *iter,
(gpointer *) member_name,
(gpointer *) member_node);
}
+
+/**
+ * json_object_iter_init_ordered:
+ * @iter: an uninitialised #JsonObjectIter
+ * @object: the #JsonObject to iterate over
+ *
+ * Initialise the @iter and associate it with @object.
+ *
+ * |[<!-- language="C" -->
+ * JsonObjectIter iter;
+ * const gchar *member_name;
+ * JsonNode *member_node;
+ *
+ * json_object_iter_init_ordered (&iter, some_object);
+ * while (json_object_iter_next_ordered (&iter, &member_name, &member_node))
+ * {
+ * // Do something with @member_name and @member_node.
+ * }
+ * ]|
+ *
+ * See also: json_object_iter_init()
+ *
+ * Since: 1.6
+ */
+void
+json_object_iter_init_ordered (JsonObjectIter *iter,
+ JsonObject *object)
+{
+ JsonObjectOrderedIterReal *iter_real = (JsonObjectOrderedIterReal *) iter;
+
+ g_return_if_fail (iter != NULL);
+ g_return_if_fail (object != NULL);
+ g_return_if_fail (object->ref_count > 0);
+
+ iter_real->object = object;
+ iter_real->cur_member = NULL;
+ iter_real->next_member = NULL;
+ iter_real->age = iter_real->object->age;
+}
+
+/**
+ * json_object_iter_next_ordered:
+ * @iter: a #JsonObjectIter
+ * @member_name: (out callee-allocates) (transfer none) (optional): return
+ * location for the member name, or %NULL to ignore
+ * @member_node: (out callee-allocates) (transfer none) (optional): return
+ * location for the member value, or %NULL to ignore
+ *
+ * Advance @iter and retrieve the next member in the object. If the end of the
+ * object is reached, %FALSE is returned and @member_name and @member_node are
+ * set to invalid values. After that point, the @iter is invalid.
+ *
+ * The order in which members are returned by the iterator is the same order in
+ * which the members were added to the #JsonObject. The iterator is invalidated
+ * if its #JsonObject is modified during iteration.
+ *
+ * You must use this function with a #JsonObjectIter initialized with
+ * json_object_iter_init_ordered(); using this function with an iterator initialized
+ * with json_object_iter_init() yields undefined behavior.
+ *
+ * See also: json_object_iter_next()
+ *
+ * Returns: %TRUE if @member_name and @member_node are valid; %FALSE if the end
+ * of the object has been reached
+ *
+ * Since: 1.6
+ */
+gboolean
+json_object_iter_next_ordered (JsonObjectIter *iter,
+ const gchar **member_name,
+ JsonNode **member_node)
+{
+ JsonObjectOrderedIterReal *iter_real = (JsonObjectOrderedIterReal *) iter;
+ const char *name = NULL;
+
+ g_return_val_if_fail (iter != NULL, FALSE);
+ g_return_val_if_fail (iter_real->object != NULL, FALSE);
+ g_return_val_if_fail (iter_real->object->ref_count > 0, FALSE);
+ g_return_val_if_fail (iter_real->age == iter_real->object->age, FALSE);
+
+ if (iter_real->cur_member == NULL)
+ iter_real->cur_member = iter_real->object->members_ordered.head;
+ else
+ iter_real->cur_member = iter_real->cur_member->next;
+
+ name = iter_real->cur_member != NULL ? iter_real->cur_member->data : NULL;
+
+ if (member_name != NULL)
+ *member_name = name;
+ if (member_node != NULL)
+ {
+ if (name != NULL)
+ *member_node = g_hash_table_lookup (iter_real->object->members, name);
+ else
+ *member_name = NULL;
+ }
+
+ return iter_real->cur_member != NULL;
+}