diff options
author | Emmanuele Bassi <ebassi@gnome.org> | 2020-08-24 11:30:01 +0100 |
---|---|---|
committer | Emmanuele Bassi <ebassi@gnome.org> | 2020-08-24 17:44:07 +0100 |
commit | 695e45c310c795a74c279e1810a19cde775edb82 (patch) | |
tree | 97ee88c0b7782c9dfac41606d3b91d2b10231a8e /json-glib/json-object.c | |
parent | e4db3ea6ea0f8e6318f0d266e2015976b97f7716 (diff) | |
download | json-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.c | 113 |
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; +} |