After an evaluation, GNOME has moved from Bugzilla to GitLab. Learn more about GitLab.
No new issues can be reported in GNOME Bugzilla anymore.
To report an issue in a GNOME project, go to GNOME GitLab.
Do not go to GNOME Gitlab for: Bluefish, Doxygen, GnuCash, GStreamer, java-gnome, LDTP, NetworkManager, Tomboy.
Bug 773504 - Avoid extra allocations
Avoid extra allocations
Status: RESOLVED FIXED
Product: json-glib
Classification: Core
Component: Generator
git master
Other Linux
: Normal normal
: ---
Assigned To: json-glib-maint
json-glib-maint
Depends on:
Blocks:
 
 
Reported: 2016-10-26 00:09 UTC by Garrett Regier
Modified: 2017-07-10 14:21 UTC
See Also:
GNOME target: ---
GNOME version: ---


Attachments
Avoid multiple buffer allocations (11.30 KB, patch)
2016-10-26 00:11 UTC, Garrett Regier
committed Details | Review
object: Use a GQueue for members_ordered (4.45 KB, patch)
2016-10-26 00:11 UTC, Garrett Regier
none Details | Review
core: Avoid json_object_get_members() (9.47 KB, patch)
2016-10-26 00:12 UTC, Garrett Regier
none Details | Review
generator: Add an GString-based method (4.27 KB, patch)
2016-10-26 00:12 UTC, Garrett Regier
none Details | Review
object: Use a GQueue for members_ordered v2 (4.45 KB, patch)
2017-04-14 10:25 UTC, Garrett Regier
committed Details | Review
core: Avoid json_object_get_members() v2 (11.16 KB, patch)
2017-04-14 10:26 UTC, Garrett Regier
none Details | Review
generator: Add an GString-based method (3.33 KB, patch)
2017-04-14 10:26 UTC, Garrett Regier
none Details | Review
core: Avoid json_object_get_members() v3 (11.11 KB, patch)
2017-06-17 00:17 UTC, Garrett Regier
committed Details | Review
generator: Add an GString-based method v3 (3.16 KB, patch)
2017-06-17 00:17 UTC, Garrett Regier
committed Details | Review

Description Garrett Regier 2016-10-26 00:09:16 UTC
During generation quite a few GStrings are allocated as is the list of members in an object. These can all be avoided and when testing a large JSON object the number of allocations was reduced in half.

It would also be nice to allow dumping the JSON to a GString when other data is required, i.e. a header for JSON-RPC.
Comment 1 Garrett Regier 2016-10-26 00:11:29 UTC
Created attachment 338466 [details] [review]
Avoid multiple buffer allocations

Instead share a single GString in all dump functions.
Comment 2 Garrett Regier 2016-10-26 00:11:52 UTC
Created attachment 338467 [details] [review]
object: Use a GQueue for members_ordered

This makes the list always ordered and removes the g_list_reverse() in json_object_get_members().
Comment 3 Garrett Regier 2016-10-26 00:12:12 UTC
Created attachment 338468 [details] [review]
core: Avoid json_object_get_members()

Use JsonObject's private members_ordered GQueue instead. This avoids a g_list_copy().
Comment 4 Garrett Regier 2016-10-26 00:12:44 UTC
Created attachment 338469 [details] [review]
generator: Add an GString-based method

This allows callers to avoid extra allocations when dumping to a preexisting string.
Comment 5 Ole André Vadla Ravnås 2016-11-29 23:07:07 UTC
Review of attachment 338467 [details] [review]:

I'm not a maintainer, but just noticed this minor detail in json_object_foreach_member():

for (l = object->members_ordered.tail; l != NULL; l = l->prev)

Should presumably be:

for (l = object->members_ordered.head; l != NULL; l = l->next)

As the old code was walking the list in the reverse direction
because the list was stored in reverse, but the new code no longer
stores it in reverse, it should walk front to back.
Comment 6 Emmanuele Bassi (:ebassi) 2017-03-11 15:53:17 UTC
Review of attachment 338466 [details] [review]:

Looks nice, thanks.
Comment 7 Emmanuele Bassi (:ebassi) 2017-03-11 15:59:19 UTC
Review of attachment 338467 [details] [review]:

Indeed, Ole André is correct.

::: json-glib/json-object.c
@@ +889,3 @@
   g_return_if_fail (func != NULL);
 
+  for (l = object->members_ordered.tail; l != NULL; l = l->prev)

Now that `members_ordered` is stored in the right order, there's no need to traverse the list in reverse.
Comment 8 Emmanuele Bassi (:ebassi) 2017-03-11 16:01:16 UTC
Review of attachment 338468 [details] [review]:

Mmh, I don't particularly like accessing the contents of JsonObject directly.

Maybe have a `json_object_get_members_internal()` function that returned a pointer to the GQueue?
Comment 9 Emmanuele Bassi (:ebassi) 2017-03-11 16:02:32 UTC
Review of attachment 338469 [details] [review]:

::: json-glib/json-generator.h
@@ +104,3 @@
 
+JSON_AVAILABLE_IN_1_4
+GString        *json_generator_to_string        (JsonGenerator  *generator,

I'd call it `to_gstring()` or `to_buffer()` instead. "String" is a bit of an overloaded term.
Comment 10 Emmanuele Bassi (:ebassi) 2017-03-11 16:03:20 UTC
Review of attachment 338469 [details] [review]:

Could you split the chunk that added the new version macros into its own commit?
Comment 11 Emmanuele Bassi (:ebassi) 2017-03-18 18:30:22 UTC
Comment on attachment 338466 [details] [review]
Avoid multiple buffer allocations

Pushed attachment 338466 [details] [review] with a slight change to avoid compiler warnings about unused variables
Comment 12 Garrett Regier 2017-04-14 10:25:20 UTC
Created attachment 349862 [details] [review]
object: Use a GQueue for members_ordered v2

(In reply to Emmanuele Bassi (:ebassi) from comment #7)
> Review of attachment 338467 [details] [review] [review]:
> 
> Indeed, Ole André is correct.
> 
> ::: json-glib/json-object.c
> @@ +889,3 @@
>    g_return_if_fail (func != NULL);
>  
> +  for (l = object->members_ordered.tail; l != NULL; l = l->prev)
> 
> Now that `members_ordered` is stored in the right order, there's no need to
> traverse the list in reverse.

Fixed.
Comment 13 Garrett Regier 2017-04-14 10:26:08 UTC
Created attachment 349863 [details] [review]
core: Avoid json_object_get_members() v2

(In reply to Emmanuele Bassi (:ebassi) from comment #8)
> Review of attachment 338468 [details] [review] [review]:
> 
> Mmh, I don't particularly like accessing the contents of JsonObject directly.
> 
> Maybe have a `json_object_get_members_internal()` function that returned a
> pointer to the GQueue?

Done.
Comment 14 Garrett Regier 2017-04-14 10:26:44 UTC
Created attachment 349864 [details] [review]
generator: Add an GString-based method

(In reply to Emmanuele Bassi (:ebassi) from comment #9)
> Review of attachment 338469 [details] [review] [review]:
> 
> ::: json-glib/json-generator.h
> @@ +104,3 @@
>  
> +JSON_AVAILABLE_IN_1_4
> +GString        *json_generator_to_string        (JsonGenerator  *generator,
> 
> I'd call it `to_gstring()` or `to_buffer()` instead. "String" is a bit of an
> overloaded term.

Done.
Comment 15 Emmanuele Bassi (:ebassi) 2017-04-14 11:01:15 UTC
Review of attachment 349862 [details] [review]:

Looks good.
Comment 16 Emmanuele Bassi (:ebassi) 2017-04-14 11:03:39 UTC
Review of attachment 349863 [details] [review]:

CR pass. Minor style nitpick, though; could you please fix it before pushing?

::: json-glib/json-types-private.h
@@ +125,3 @@
 
+G_GNUC_INTERNAL
+GQueue *        json_object_get_members_internal

No real need to go to a new line for the arguments; it's a private header, so you don't even need to re-align everything.
Comment 17 Emmanuele Bassi (:ebassi) 2017-04-14 11:12:21 UTC
Review of attachment 349864 [details] [review]:

::: json-glib/json-generator.c
@@ +481,3 @@
+ * json_generator_to_gstring: (skip)
+ * @generator: a #JsonGenerator
+ * @string: (allow-none): a #GString, or %NULL

The `allow-none` annotation is deprecated. In this case, it would be `nullable`.

The memory management semantics of this function are a bit weird, though; I'm not entirely sure it's going to be usable by language bindings.

@@ +489,3 @@
+ * is allocated and used.
+ *
+ * Generates a JSON data stream from @generator

This is what I'm worried about. This is a `transfer full` value if string is NULL, but a `transfer none` if string is not NULL.

I don't think we want to be extra-clever, here. I'd always require a GString instance, and make this a `transfer none`. This way you can still write compact lines like:

  GString *buf = json_generator_to_gstring (generator, g_string_new (""));

without weird memory management semantics.

It's, of course, entirely possible to say that this function is not meant for language bindings — in which case, we should use the `skip` annotation; but I'd rather avoid that.

@@ +507,3 @@
+
+  root = generator->priv->root;
+ * Since: 1.4

Coding style nitpick: pointer comparisons should be against NULL — like you did above for `string`.
Comment 18 Garrett Regier 2017-06-17 00:17:38 UTC
Created attachment 353927 [details] [review]
core: Avoid json_object_get_members() v3
Comment 19 Garrett Regier 2017-06-17 00:17:57 UTC
Created attachment 353928 [details] [review]
generator: Add an GString-based method v3
Comment 20 Emmanuele Bassi (:ebassi) 2017-07-10 09:17:07 UTC
Review of attachment 353927 [details] [review]:

Looks good, thanks!
Comment 21 Emmanuele Bassi (:ebassi) 2017-07-10 09:17:54 UTC
Review of attachment 353928 [details] [review]:

Looks good, thanks!