[glib] GVariantBuilder: allow for stack allocation



commit fe6e278a870c7b2a3e76ba9ef17a9b99a4c0c474
Author: Ryan Lortie <desrt desrt ca>
Date:   Wed Mar 3 03:37:37 2010 -0500

    GVariantBuilder: allow for stack allocation

 docs/reference/glib/glib-sections.txt |    2 +
 glib/glib.symbols                     |    6 +-
 glib/gvariant.c                       |  553 ++++++++++++++++++++-------------
 glib/gvariant.h                       |   13 +-
 glib/tests/gvariant.c                 |   13 +-
 5 files changed, 352 insertions(+), 235 deletions(-)
---
diff --git a/docs/reference/glib/glib-sections.txt b/docs/reference/glib/glib-sections.txt
index 9063458..c771dd9 100644
--- a/docs/reference/glib/glib-sections.txt
+++ b/docs/reference/glib/glib-sections.txt
@@ -2869,6 +2869,8 @@ GVariantBuilder
 g_variant_builder_unref
 g_variant_builder_ref
 g_variant_builder_new
+g_variant_builder_init
+g_variant_builder_clear
 g_variant_builder_add_value
 g_variant_builder_end
 g_variant_builder_open
diff --git a/glib/glib.symbols b/glib/glib.symbols
index 2d9786c..4baec9f 100644
--- a/glib/glib.symbols
+++ b/glib/glib.symbols
@@ -1766,12 +1766,14 @@ g_variant_iter_loop
 g_variant_iter_next_value
 
 g_variant_builder_add_value
+g_variant_builder_init
+g_variant_builder_clear
+g_variant_builder_open
 g_variant_builder_close
 g_variant_builder_end
 g_variant_builder_new
-g_variant_builder_open
-g_variant_builder_ref
 g_variant_builder_unref
+g_variant_builder_ref
 #endif
 #endif
 
diff --git a/glib/gvariant.c b/glib/gvariant.c
index b54cd89..015d46a 100644
--- a/glib/gvariant.c
+++ b/glib/gvariant.c
@@ -1619,7 +1619,7 @@ g_variant_print_string (GVariant *value,
         const gchar *str = g_variant_get_string (value, NULL);
         gchar *escaped = g_strescape (str, NULL);
 
-        g_string_append_printf (string, "\"%s\"", escaped);
+        g_string_append_printf (string, "\'%s\'", escaped);
 
         g_free (escaped);
       }
@@ -1709,14 +1709,14 @@ g_variant_print_string (GVariant *value,
     case G_VARIANT_CLASS_OBJECT_PATH:
       if (type_annotate)
         g_string_append (string, "objectpath ");
-      g_string_append_printf (string, "\"%s\"",
+      g_string_append_printf (string, "\'%s\'",
                               g_variant_get_string (value, NULL));
       break;
 
     case G_VARIANT_CLASS_SIGNATURE:
       if (type_annotate)
         g_string_append (string, "signature ");
-      g_string_append_printf (string, "\"%s\"",
+      g_string_append_printf (string, "\'%s\'",
                               g_variant_get_string (value, NULL));
       break;
 
@@ -1929,7 +1929,8 @@ struct heap_iter
 #define GVHI(i)                 ((struct heap_iter *) (i))
 #define GVSI_MAGIC              ((gsize) 3579507750u)
 #define GVHI_MAGIC              ((gsize) 1450270775u)
-#define is_valid_iter(i)        (GVSI(i)->magic == GVSI_MAGIC)
+#define is_valid_iter(i)        (i != NULL && \
+                                 GVSI(i)->magic == GVSI_MAGIC)
 #define is_valid_heap_iter(i)   (GVHI(i)->magic == GVHI_MAGIC && \
                                  is_valid_iter(i))
 
@@ -2285,11 +2286,10 @@ g_variant_iter_next (GVariantIter *iter,
 }
 
 /* GVariantBuilder {{{1 */
-
 /**
  * GVariantBuilder:
  *
- * A utility class for constructing container-type #GVariant instances.
+ * A utility type for constructing container-type #GVariant instances.
  *
  * This is an opaque structure and may only be accessed using the
  * following functions.
@@ -2297,7 +2297,8 @@ g_variant_iter_next (GVariantIter *iter,
  * #GVariantBuilder is not threadsafe in any way.  Do not attempt to
  * access it from more than one thread.
  **/
-struct _GVariantBuilder
+
+struct stack_builder
 {
   GVariantBuilder *parent;
   GVariantType *type;
@@ -2325,165 +2326,274 @@ struct _GVariantBuilder
    */
   guint uniform_item_types : 1;
 
-  /* set to '1' until _end() or _close() is called. */
-  guint is_active : 1;
-
-  /* set to '1' by _open() until _close() is called */
-  guint has_child : 1;
-
   /* set to '1' initially and changed to '0' if an untrusted value is
    * added
    */
   guint trusted : 1;
 
+  gsize magic;
+};
+
+struct heap_builder
+{
+  GVariantBuilder builder;
+  gsize magic;
+
   gint ref_count;
 };
 
+#define GVSB(b)                  ((struct stack_builder *) (b))
+#define GVHB(b)                  ((struct heap_builder *) (b))
+#define GVSB_MAGIC               ((gsize) 1033660112u)
+#define GVHB_MAGIC               ((gsize) 3087242682u)
+#define is_valid_builder(b)      (b != NULL && \
+                                  GVSB(b)->magic == GVSB_MAGIC)
+#define is_valid_heap_builder(b) (GVHB(b)->magic == GVHB_MAGIC && \
+                                  is_valid_builder(b))
+
 /**
  * g_variant_builder_new:
  * @type: a container type
  * @returns: a #GVariantBuilder
  *
- * Creates a new #GVariantBuilder.
+ * Allocates and initialises a new #GVariantBuilder.
  *
- * @type must be non-%NULL.  It specifies the type of container to
- * construct.  It can be an indefinite type such as
- * %G_VARIANT_TYPE_ARRAY or a definite type such as "as" or "(ii)".
- * Maybe, array, tuple, dictionary entry and variants may be
- * constructed.
+ * You should call g_variant_builder_unref() on the return value when it
+ * is no longer needed.  The memory will not be automatically freed by
+ * any other call.
  *
- * After the builder is created, values are added using
- * g_variant_builder_add_value() or g_variant_builder_add().
+ * In most cases it is easier to place a #GVariantBuilder directly on
+ * the stack of the calling function and initialise it with
+ * g_variant_builder_init().
  *
- * After all the child values are added, g_variant_builder_end() frees
- * the builder and returns the #GVariant that was created.
+ * Since: 2.24
  **/
 GVariantBuilder *
 g_variant_builder_new (const GVariantType *type)
 {
   GVariantBuilder *builder;
 
-  g_return_val_if_fail (type != NULL, NULL);
-  g_return_val_if_fail (g_variant_type_is_container (type), NULL);
-
-  builder = g_slice_new (GVariantBuilder);
-  builder->parent = NULL;
-  builder->offset = 0;
-
-  builder->has_child = FALSE;
-  builder->is_active = TRUE;
-  builder->trusted = TRUE;
-
-  builder->type = g_variant_type_copy (type);
-  builder->prev_item_type = NULL;
+  builder = (GVariantBuilder *) g_slice_new (struct heap_builder);
+  g_variant_builder_init (builder, type);
+  GVHB(builder)->magic = GVHB_MAGIC;
+  GVHB(builder)->ref_count = 1;
 
-  builder->ref_count = 1;
-
-  switch (*(const gchar *) type)
-    {
-    case G_VARIANT_CLASS_VARIANT:
-      builder->uniform_item_types = TRUE;
-      builder->allocated_children = 1;
-      builder->expected_type = NULL;
-      builder->min_items = 1;
-      builder->max_items = 1;
-      break;
-
-    case G_VARIANT_CLASS_ARRAY:
-      builder->uniform_item_types = TRUE;
-      builder->allocated_children = 8;
-      builder->expected_type = g_variant_type_element (builder->type);
-      builder->min_items = 0;
-      builder->max_items = -1;
-      break;
+  return builder;
+}
 
-    case G_VARIANT_CLASS_MAYBE:
-      builder->uniform_item_types = TRUE;
-      builder->allocated_children = 1;
-      builder->expected_type = g_variant_type_element (builder->type);
-      builder->min_items = 0;
-      builder->max_items = 1;
-      break;
+/**
+ * g_variant_builder_unref:
+ * @builder: a #GVariantBuilder allocated by g_variant_builder_new()
+ *
+ * Decreases the reference count on @builder.
+ *
+ * In the event that there are no more references, releases all memory
+ * associated with the #GVariantBuilder.
+ *
+ * Don't call this on stack-allocated #GVariantBuilder instances or bad
+ * things will happen.
+ *
+ * Since: 2.24
+ **/
+void
+g_variant_builder_unref (GVariantBuilder *builder)
+{
+  g_return_if_fail (is_valid_heap_builder (builder));
 
-    case G_VARIANT_CLASS_DICT_ENTRY:
-      builder->uniform_item_types = FALSE;
-      builder->allocated_children = 2;
-      builder->expected_type = g_variant_type_key (builder->type);
-      builder->min_items = 2;
-      builder->max_items = 2;
-      break;
+  if (--GVHB(builder)->ref_count)
+    return;
 
-    case 'r': /* G_VARIANT_TYPE_TUPLE was given */
-      builder->uniform_item_types = FALSE;
-      builder->allocated_children = 8;
-      builder->expected_type = NULL;
-      builder->min_items = 0;
-      builder->max_items = -1;
-      break;
+  g_variant_builder_clear (builder);
+  GVHB(builder)->magic = 0;
 
-    case G_VARIANT_CLASS_TUPLE: /* a definite tuple type was given */
-      builder->allocated_children = g_variant_type_n_items (type);
-      builder->expected_type = g_variant_type_first (builder->type);
-      builder->min_items = builder->allocated_children;
-      builder->max_items = builder->allocated_children;
-      builder->uniform_item_types = FALSE;
-      break;
+  g_slice_free (struct heap_builder, GVHB(builder));
+}
 
-    default:
-      g_assert_not_reached ();
-   }
+/**
+ * g_variant_builder_ref:
+ * @builder: a #GVariantBuilder allocated by g_variant_builder_new()
+ * @returns: a new reference to @builder
+ *
+ * Increases the reference count on @builder.
+ *
+ * Don't call this on stack-allocated #GVariantBuilder instances or bad
+ * things will happen.
+ *
+ * Since: 2.24
+ **/
+GVariantBuilder *
+g_variant_builder_ref (GVariantBuilder *builder)
+{
+  g_return_val_if_fail (is_valid_heap_builder (builder), NULL);
 
-  builder->children = g_new (GVariant *, builder->allocated_children);
+  GVHB(builder)->ref_count++;
 
   return builder;
 }
 
 /**
- * g_variant_builder_unref:
+ * g_variant_builder_clear:
  * @builder: a #GVariantBuilder
  *
- * Reduces the reference count on @builder.  If no other references are
- * held, the builder is freed.  If the builder was created using
- * g_variant_builder_open() then this may result in the destruction of
- * the parent builder too (if no other references are held on it).
+ * Releases all memory associated with a #GVariantBuilder without
+ * freeing the #GVariantBuilder structure itself.
+ *
+ * It typically only makes sense to do this on a stack-allocated
+ * #GVariantBuilder if you want to abort building the value part-way
+ * through.  This function need not be called if you call
+ * g_variant_builder_end() and it also doesn't need to be called on
+ * builders allocated with g_variant_builder_new (see
+ * g_variant_builder_free() for that).
+ *
+ * This function leaves the #GVariantBuilder structure set to all-zeros.
+ * It is valid to call this function on either an initialised
+ * #GVariantBuilder or one that is set to all-zeros but it is not valid
+ * to call this function on uninitialised memory.
+ *
+ * Since: 2.24
  **/
 void
-g_variant_builder_unref (GVariantBuilder *builder)
+g_variant_builder_clear (GVariantBuilder *builder)
 {
-  GVariantBuilder *parent;
   gsize i;
 
-  if (--builder->ref_count)
+  if (GVSB(builder)->magic == 0)
+    /* all-zeros case */
     return;
 
-  for (i = 0; i < builder->offset; i++)
-    g_variant_unref (builder->children[i]);
+  g_return_if_fail (is_valid_builder (builder));
+
+  g_variant_type_free (GVSB(builder)->type);
+
+  for (i = 0; i < GVSB(builder)->offset; i++)
+    g_variant_unref (GVSB(builder)->children[i]);
 
-  g_free (builder->children);
+  g_free (GVSB(builder)->children);
 
-  parent = builder->parent;
-  g_slice_free (GVariantBuilder, builder);
+  if (GVSB(builder)->parent)
+    {
+      g_variant_builder_clear (GVSB(builder)->parent);
+      g_slice_free (GVariantBuilder, GVSB(builder)->parent);
+    }
 
-  g_variant_builder_unref (parent);
+  memset (builder, 0, sizeof (GVariantBuilder));
 }
 
 /**
- * g_variant_builder_ref;
+ * g_variant_builder_init:
  * @builder: a #GVariantBuilder
- * @returns: the same #GVariantBuilder
+ * @type: a container type
+ *
+ * Initialises a #GVariantBuilder structure.
+ *
+ * @type must be non-%NULL.  It specifies the type of container to
+ * construct.  It can be an indefinite type such as
+ * %G_VARIANT_TYPE_ARRAY or a definite type such as "as" or "(ii)".
+ * Maybe, array, tuple, dictionary entry and variant-typed values may be
+ * constructed.
  *
- * Increases the reference count on @builder by 1.
+ * After the builder is initialised, values are added using
+ * g_variant_builder_add_value() or g_variant_builder_add().
+ *
+ * After all the child values are added, g_variant_builder_end() frees
+ * the memory associated with the builder and returns the #GVariant that
+ * was created.
+ *
+ * This function completely ignores the previous contents of @builder.
+ * On one hand this means that it is valid to pass in completely
+ * uninitialised memory.  On the other hand, this means that if you are
+ * initialising over top of an existing #GVariantBuilder you need to
+ * first call g_variant_builder_clear() in order to avoid leaking
+ * memory.
+ *
+ * You must not call g_variant_builder_ref() or
+ * g_variant_builder_unref() on a #GVariantBuilder that was initialised
+ * with this function.  If you ever pass a reference to a
+ * #GVariantBuilder outside of the control of your own code then you
+ * should assume that the person receiving that reference may try to use
+ * reference counting; you should use g_variant_builder_new() instead of
+ * this function.
+ *
+ * Since: 2.24
  **/
-GVariantBuilder *
-g_variant_builder_ref (GVariantBuilder *builder)
+void
+g_variant_builder_init (GVariantBuilder    *builder,
+                        const GVariantType *type)
 {
-  builder->ref_count++;
+  g_return_if_fail (type != NULL);
+  g_return_if_fail (g_variant_type_is_container (type));
 
-  return builder;
+  g_assert (sizeof (struct stack_builder) < sizeof (GVariantBuilder));
+  memset (builder, 0, sizeof (GVariantBuilder));
+
+  GVSB(builder)->type = g_variant_type_copy (type);
+  GVSB(builder)->magic = GVSB_MAGIC;
+  GVSB(builder)->trusted = TRUE;
+
+  switch (*(const gchar *) type)
+    {
+    case G_VARIANT_CLASS_VARIANT:
+      GVSB(builder)->uniform_item_types = TRUE;
+      GVSB(builder)->allocated_children = 1;
+      GVSB(builder)->expected_type = NULL;
+      GVSB(builder)->min_items = 1;
+      GVSB(builder)->max_items = 1;
+      break;
+
+    case G_VARIANT_CLASS_ARRAY:
+      GVSB(builder)->uniform_item_types = TRUE;
+      GVSB(builder)->allocated_children = 8;
+      GVSB(builder)->expected_type =
+        g_variant_type_element (GVSB(builder)->type);
+      GVSB(builder)->min_items = 0;
+      GVSB(builder)->max_items = -1;
+      break;
+
+    case G_VARIANT_CLASS_MAYBE:
+      GVSB(builder)->uniform_item_types = TRUE;
+      GVSB(builder)->allocated_children = 1;
+      GVSB(builder)->expected_type =
+        g_variant_type_element (GVSB(builder)->type);
+      GVSB(builder)->min_items = 0;
+      GVSB(builder)->max_items = 1;
+      break;
+
+    case G_VARIANT_CLASS_DICT_ENTRY:
+      GVSB(builder)->uniform_item_types = FALSE;
+      GVSB(builder)->allocated_children = 2;
+      GVSB(builder)->expected_type =
+        g_variant_type_key (GVSB(builder)->type);
+      GVSB(builder)->min_items = 2;
+      GVSB(builder)->max_items = 2;
+      break;
+
+    case 'r': /* G_VARIANT_TYPE_TUPLE was given */
+      GVSB(builder)->uniform_item_types = FALSE;
+      GVSB(builder)->allocated_children = 8;
+      GVSB(builder)->expected_type = NULL;
+      GVSB(builder)->min_items = 0;
+      GVSB(builder)->max_items = -1;
+      break;
+
+    case G_VARIANT_CLASS_TUPLE: /* a definite tuple type was given */
+      GVSB(builder)->allocated_children = g_variant_type_n_items (type);
+      GVSB(builder)->expected_type =
+        g_variant_type_first (GVSB(builder)->type);
+      GVSB(builder)->min_items = GVSB(builder)->allocated_children;
+      GVSB(builder)->max_items = GVSB(builder)->allocated_children;
+      GVSB(builder)->uniform_item_types = FALSE;
+      break;
+
+    default:
+      g_assert_not_reached ();
+   }
+
+  GVSB(builder)->children = g_new (GVariant *,
+                                   GVSB(builder)->allocated_children);
 }
 
 static void
-g_variant_builder_make_room (GVariantBuilder *builder)
+g_variant_builder_make_room (struct stack_builder *builder)
 {
   if (builder->offset == builder->allocated_children)
     {
@@ -2505,139 +2615,120 @@ g_variant_builder_make_room (GVariantBuilder *builder)
  * putting different types of items into an array, putting the wrong
  * types or number of items in a tuple, putting more than one value into
  * a variant, etc.
+ *
+ * Since: 2.24
  **/
 void
 g_variant_builder_add_value (GVariantBuilder *builder,
                              GVariant        *value)
 {
-  g_return_if_fail (builder != NULL && value != NULL);
-  g_return_if_fail (builder->is_active && !builder->has_child);
-  g_return_if_fail (builder->offset < builder->max_items);
-  g_return_if_fail (!builder->expected_type ||
-                    g_variant_is_of_type (value, builder->expected_type));
-  g_return_if_fail (!builder->prev_item_type ||
-                    g_variant_is_of_type (value, builder->prev_item_type));
-
-  builder->trusted &= g_variant_is_trusted (value);
-
-  if (!builder->uniform_item_types)
+  g_return_if_fail (is_valid_builder (builder));
+  g_return_if_fail (GVSB(builder)->offset < GVSB(builder)->max_items);
+  g_return_if_fail (!GVSB(builder)->expected_type ||
+                    g_variant_is_of_type (value,
+                                          GVSB(builder)->expected_type));
+  g_return_if_fail (!GVSB(builder)->prev_item_type ||
+                    g_variant_is_of_type (value,
+                                          GVSB(builder)->prev_item_type));
+
+  GVSB(builder)->trusted &= g_variant_is_trusted (value);
+
+  if (!GVSB(builder)->uniform_item_types)
     {
       /* advance our expected type pointers */
-      if (builder->expected_type)
-        builder->expected_type =
-          g_variant_type_next (builder->expected_type);
+      if (GVSB(builder)->expected_type)
+        GVSB(builder)->expected_type =
+          g_variant_type_next (GVSB(builder)->expected_type);
 
-      if (builder->prev_item_type)
-        builder->prev_item_type =
-          g_variant_type_next (builder->prev_item_type);
+      if (GVSB(builder)->prev_item_type)
+        GVSB(builder)->prev_item_type =
+          g_variant_type_next (GVSB(builder)->prev_item_type);
     }
   else
-    builder->prev_item_type = g_variant_get_type (value);
+    GVSB(builder)->prev_item_type = g_variant_get_type (value);
 
-  g_variant_builder_make_room (builder);
+  g_variant_builder_make_room (GVSB(builder));
 
-  builder->children[builder->offset++] = g_variant_ref_sink (value);
+  GVSB(builder)->children[GVSB(builder)->offset++] =
+    g_variant_ref_sink (value);
 }
 
 /**
  * g_variant_builder_open:
  * @builder: a #GVariantBuilder
  * @type: a #GVariantType
- * @returns: a new #GVariantBuilder
  *
- * Opens a subcontainer inside the given @builder.
+ * Opens a subcontainer inside the given @builder.  When done adding
+ * items to the subcontainer, g_variant_builder_close() must be called.
  *
- * This call consumes the caller's reference to @builder.
- * g_variant_builder_close() returns the reference.
- *
- * Even if additional references are held, it is not permissible to use
- * @builder in any way (except for further reference counting
- * operations) until g_variant_builder_close() is called on the return
- * value of this function.
+ * It is an error to call this function in any way that would cause an
+ * inconsistent value to be constructed (ie: adding too many values or
+ * a value of an incorrect type).
  *
- * It is an error to call this function in any way that would create an
- * inconsistent value to be constructed.
+ * Since: 2.24
  **/
-GVariantBuilder *
+void
 g_variant_builder_open (GVariantBuilder    *builder,
                         const GVariantType *type)
 {
-  GVariantBuilder *child;
-
-  g_return_val_if_fail (builder != NULL && type != NULL, NULL);
-  g_return_val_if_fail (builder->is_active && !builder->has_child, NULL);
-  g_return_val_if_fail (builder->offset < builder->max_items, NULL);
-  g_return_val_if_fail (!builder->expected_type ||
-                        g_variant_type_is_subtype_of (type,
-                                                      builder->expected_type),
-                        NULL);
-  g_return_val_if_fail (!builder->prev_item_type ||
-                        g_variant_type_is_subtype_of (builder->prev_item_type,
-                                                      type), NULL);
-  child = g_variant_builder_new (type);
-  builder->has_child = TRUE;
-  child->parent = builder;
+  GVariantBuilder *parent;
+
+  g_return_if_fail (is_valid_builder (builder));
+  g_return_if_fail (GVSB(builder)->offset < GVSB(builder)->max_items);
+  g_return_if_fail (!GVSB(builder)->expected_type ||
+                    g_variant_type_is_subtype_of (type,
+                                                  GVSB(builder)->expected_type));
+  g_return_if_fail (!GVSB(builder)->prev_item_type ||
+                    g_variant_type_is_subtype_of (GVSB(builder)->prev_item_type,
+                                                  type));
+
+  parent = g_slice_dup (GVariantBuilder, builder);
+  g_variant_builder_init (builder, type);
+  GVSB(builder)->parent = parent;
 
   /* push the prev_item_type down into the subcontainer */
-  if (builder->prev_item_type)
+  if (GVSB(parent)->prev_item_type)
     {
-      if (!child->uniform_item_types)
+      if (!GVSB(builder)->uniform_item_types)
         /* tuples and dict entries */
-        child->prev_item_type =
-          g_variant_type_first (builder->prev_item_type);
+        GVSB(builder)->prev_item_type =
+          g_variant_type_first (GVSB(parent)->prev_item_type);
 
-      else if (!g_variant_type_is_variant (child->type))
+      else if (!g_variant_type_is_variant (GVSB(builder)->type))
         /* maybes and arrays */
-        child->prev_item_type =
-          g_variant_type_element (builder->prev_item_type);
+        GVSB(builder)->prev_item_type =
+          g_variant_type_element (GVSB(parent)->prev_item_type);
     }
-
-  return child;
 }
 
 /**
  * g_variant_builder_close:
  * @builder: a #GVariantBuilder
- * @returns: the original parent of @builder
  *
- * This function closes a builder that was created with a call to
- * g_variant_builder_open().
- *
- * This function consumes the caller's reference to @builder and drops
- * it.  The return result is the reference to the parent
- * #GVariantBuilder that was originally taken from the caller by
- * g_variant_builder_open().
- *
- * Even if additional references are held, it is not permissible to use
- * @builder in any way after this call except for further reference
- * counting operations.
+ * Closes the subcontainer inside the given @builder that was opened by
+ * the most recent call to g_variant_builder_open().
  *
  * It is an error to call this function in any way that would create an
- * inconsistent value to be constructed (ie: insufficient number of
- * items added to a container with a specific number of children
- * required).  It is also an error to call this function if the builder
- * was created with an indefinite array or maybe type and no children
- * have been added; in this case it is impossible to infer the type of
- * the empty array.
+ * inconsistent value to be constructed (ie: too few values added to the
+ * subcontainer).
+ *
+ * Since: 2.24
  **/
-GVariantBuilder *
+void
 g_variant_builder_close (GVariantBuilder *builder)
 {
   GVariantBuilder *parent;
 
-  g_return_val_if_fail (builder != NULL, NULL);
-  g_return_val_if_fail (builder->parent != NULL, NULL);
-  g_assert (builder->parent->has_child);
+  g_return_if_fail (is_valid_builder (builder));
+  g_return_if_fail (GVSB(builder)->parent != NULL);
 
-  /* steal reference so _end() doesn't free it. */
-  parent = builder->parent;
-  builder->parent = NULL;
-
-  parent->has_child = FALSE;
+  parent = GVSB(builder)->parent;
+  GVSB(builder)->parent = NULL;
 
   g_variant_builder_add_value (parent, g_variant_builder_end (builder));
+  *builder = *parent;
 
-  return parent;
+  g_slice_free (GVariantBuilder, parent);
 }
 
 /*< private >
@@ -2671,11 +2762,24 @@ g_variant_make_array_type (GVariant *element)
  *
  * Ends the builder process and returns the constructed value.
  *
- * It is an error to call this function on a #GVariantBuilder created
- * by a call to g_variant_builder_open().  It is an error to call this
- * function if @builder has an outstanding child.  It is an error to
- * call this function in any case that g_variant_builder_check_end()
- * would return %FALSE.
+ * This call automatically reduces the reference count on @builder by
+ * one, unless it has previously had g_variant_builder_no_autofree()
+ * called on it.  Unless you've taken other actions, this is usually
+ * sufficient to free @builder.
+ *
+ * Even if additional references are held, it is not permissible to use
+ * @builder in any way after this call except for further reference
+ * counting operations.
+ *
+ * It is an error to call this function in any way that would create an
+ * inconsistent value to be constructed (ie: insufficient number of
+ * items added to a container with a specific number of children
+ * required).  It is also an error to call this function if the builder
+ * was created with an indefinite array or maybe type and no children
+ * have been added; in this case it is impossible to infer the type of
+ * the empty array.
+ *
+ * Since: 2.24
  **/
 GVariant *
 g_variant_builder_end (GVariantBuilder *builder)
@@ -2683,42 +2787,43 @@ g_variant_builder_end (GVariantBuilder *builder)
   GVariantType *my_type;
   GVariant *value;
 
-  g_return_val_if_fail (builder != NULL, NULL);
-  g_return_val_if_fail (builder->is_active && !builder->has_child, NULL);
-  g_return_val_if_fail (builder->offset >= builder->min_items, NULL);
-  g_return_val_if_fail (!builder->uniform_item_types ||
-                        builder->prev_item_type != NULL ||
-                        g_variant_type_is_definite (builder->type), NULL);
+  g_return_val_if_fail (is_valid_builder (builder), NULL);
+  g_return_val_if_fail (GVSB(builder)->offset >= GVSB(builder)->min_items,
+                        NULL);
+  g_return_val_if_fail (!GVSB(builder)->uniform_item_types ||
+                        GVSB(builder)->prev_item_type != NULL ||
+                        g_variant_type_is_definite (GVSB(builder)->type),
+                        NULL);
 
-  if (g_variant_type_is_definite (builder->type))
-    my_type = g_variant_type_copy (builder->type);
+  if (g_variant_type_is_definite (GVSB(builder)->type))
+    my_type = g_variant_type_copy (GVSB(builder)->type);
 
-  else if (g_variant_type_is_maybe (builder->type))
-    my_type = g_variant_make_maybe_type (builder->children[0]);
+  else if (g_variant_type_is_maybe (GVSB(builder)->type))
+    my_type = g_variant_make_maybe_type (GVSB(builder)->children[0]);
 
-  else if (g_variant_type_is_array (builder->type))
-    my_type = g_variant_make_array_type (builder->children[0]);
+  else if (g_variant_type_is_array (GVSB(builder)->type))
+    my_type = g_variant_make_array_type (GVSB(builder)->children[0]);
 
-  else if (g_variant_type_is_tuple (builder->type))
-    my_type = g_variant_make_tuple_type (builder->children, builder->offset);
+  else if (g_variant_type_is_tuple (GVSB(builder)->type))
+    my_type = g_variant_make_tuple_type (GVSB(builder)->children,
+                                         GVSB(builder)->offset);
 
-  else if (g_variant_type_is_dict_entry (builder->type))
-    my_type = g_variant_make_dict_entry_type (builder->children[0],
-                                              builder->children[1]);
+  else if (g_variant_type_is_dict_entry (GVSB(builder)->type))
+    my_type = g_variant_make_dict_entry_type (GVSB(builder)->children[0],
+                                              GVSB(builder)->children[1]);
   else
     g_assert_not_reached ();
 
   value = g_variant_new_from_children (my_type,
                                        g_renew (GVariant *,
-                                                builder->children,
-                                                builder->offset),
-                                       builder->offset,
-                                       builder->trusted);
-  builder->is_active = FALSE;
-  builder->children = NULL;
-
-  g_variant_type_free (builder->type);
-  g_slice_free (GVariantBuilder, builder);
+                                                GVSB(builder)->children,
+                                                GVSB(builder)->offset),
+                                       GVSB(builder)->offset,
+                                       GVSB(builder)->trusted);
+  GVSB(builder)->children = NULL;
+  GVSB(builder)->offset = 0;
+
+  g_variant_builder_clear (builder);
   g_variant_type_free (my_type);
 
   return value;
diff --git a/glib/gvariant.h b/glib/gvariant.h
index 2dade1f..d8ea126 100644
--- a/glib/gvariant.h
+++ b/glib/gvariant.h
@@ -157,14 +157,21 @@ gboolean                        g_variant_iter_loop                     (GVarian
 
 
 typedef struct _GVariantBuilder GVariantBuilder;
+struct _GVariantBuilder {
+  /*< private >*/
+  gsize x[16];
+};
 
+GVariantBuilder *               g_variant_builder_new                   (const GVariantType   *type);
 void                            g_variant_builder_unref                 (GVariantBuilder      *builder);
 GVariantBuilder *               g_variant_builder_ref                   (GVariantBuilder      *builder);
-GVariantBuilder *               g_variant_builder_new                   (const GVariantType   *type);
+void                            g_variant_builder_init                  (GVariantBuilder      *builder,
+                                                                         const GVariantType   *type);
 GVariant *                      g_variant_builder_end                   (GVariantBuilder      *builder);
-GVariantBuilder *               g_variant_builder_open                  (GVariantBuilder      *builder,
+void                            g_variant_builder_clear                 (GVariantBuilder      *builder);
+void                            g_variant_builder_open                  (GVariantBuilder      *builder,
                                                                          const GVariantType   *type);
-GVariantBuilder *               g_variant_builder_close                 (GVariantBuilder      *builder);
+void                            g_variant_builder_close                 (GVariantBuilder      *builder);
 void                            g_variant_builder_add_value             (GVariantBuilder      *builder,
                                                                          GVariant             *value);
 void                            g_variant_builder_add                   (GVariantBuilder      *builder,
diff --git a/glib/tests/gvariant.c b/glib/tests/gvariant.c
index 3ae7843..ea01500 100644
--- a/glib/tests/gvariant.c
+++ b/glib/tests/gvariant.c
@@ -2540,7 +2540,7 @@ tree_instance_build_gvariant (TreeInstance    *tree,
     {
       gsize i;
 
-      builder = g_variant_builder_open (builder, type);
+      g_variant_builder_open (builder, type);
 
       for (i = 0; i < tree->n_children; i++)
         tree_instance_build_gvariant (tree->children[i], builder);
@@ -2619,15 +2619,15 @@ test_container (void)
 
   if (g_variant_is_container (value))
     {
-      GVariantBuilder *builder;
+      GVariantBuilder builder;
       GVariantIter iter;
       GVariant *built;
       GVariant *val;
       gchar *s3;
 
-      builder = g_variant_builder_new (G_VARIANT_TYPE_VARIANT);
-      tree_instance_build_gvariant (tree, builder);
-      built = g_variant_builder_end (builder);
+      g_variant_builder_init (&builder, G_VARIANT_TYPE_VARIANT);
+      tree_instance_build_gvariant (tree, &builder);
+      built = g_variant_builder_end (&builder);
       g_variant_ref_sink (built);
       g_variant_get_data (built);
       val = g_variant_get_variant (built);
@@ -2676,7 +2676,6 @@ main (int argc, char **argv)
   g_test_add_func ("/gvariant/serialiser/variant", test_variants);
   g_test_add_func ("/gvariant/serialiser/strings", test_strings);
   g_test_add_func ("/gvariant/serialiser/byteswap", test_byteswaps);
-  g_test_add_func ("/gvariant/containers", test_containers);
 
   for (i = 1; i <= 20; i += 4)
     {
@@ -2688,5 +2687,7 @@ main (int argc, char **argv)
       g_free (testname);
     }
 
+  g_test_add_func ("/gvariant/containers", test_containers);
+
   return g_test_run ();
 }



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]