[glib/gvariantiter] add GVariantBuilder



commit 4b6c08b47657117ab564172a7ccc492057eb012e
Author: Ryan Lortie <desrt desrt ca>
Date:   Sun Feb 21 15:45:12 2010 -0500

    add GVariantBuilder

 glib/gvariant.c |  440 +++++++++++++++++++++++++++++++++++++++++++++++++++++++
 glib/gvariant.h |   16 ++
 2 files changed, 456 insertions(+), 0 deletions(-)
---
diff --git a/glib/gvariant.c b/glib/gvariant.c
index f658488..038af08 100644
--- a/glib/gvariant.c
+++ b/glib/gvariant.c
@@ -2251,6 +2251,446 @@ g_variant_iter_next_nofree (GVariantIter *iter,
   return FALSE;
 }
 
+/* GVariantBuilder {{{1 */
+
+/**
+ * GVariantBuilder:
+ *
+ * A utility class for constructing container-type #GVariant instances.
+ *
+ * This is an opaque structure and may only be accessed using the
+ * following functions.
+ *
+ * #GVariantBuilder is not threadsafe in any way.  Do not attempt to
+ * access it from more than one thread.
+ **/
+struct _GVariantBuilder
+{
+  GVariantBuilder *parent;
+  GVariantType *type;
+
+  /* type constraint explicitly specified by 'type'.
+   * for tuple types, this moves along as we add more items.
+   */
+  const GVariantType *expected_type;
+
+  /* type constraint implied by previous array item.
+   */
+  const GVariantType *prev_item_type;
+
+  /* constraints on the number of children.  max = -1 for unlimited. */
+  gsize min_items;
+  gsize max_items;
+
+  /* dynamically-growing pointer array */
+  GVariant **children;
+  gsize allocated_children;
+  gsize offset;
+
+  /* set to '1' if all items in the container will have the same type
+   * (ie: maybe, array, variant) '0' if not (ie: tuple, dict entry)
+   */
+  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;
+
+  gint ref_count;
+};
+
+/**
+ * g_variant_builder_new:
+ * @type: a container type
+ * @returns: a #GVariantBuilder
+ *
+ * Creates 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.
+ *
+ * After the builder is created, 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 builder and returns the #GVariant that was created.
+ **/
+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->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;
+
+    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;
+
+    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;
+
+    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;
+
+    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;
+
+    default:
+      g_assert_not_reached ();
+   }
+
+  builder->children = g_new (GVariant *, builder->allocated_children);
+
+  return builder;
+}
+
+/**
+ * g_variant_builder_unref:
+ * @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).
+ **/
+void
+g_variant_builder_unref (GVariantBuilder *builder)
+{
+  GVariantBuilder *parent;
+  gsize i;
+
+  if (--builder->ref_count)
+    return;
+
+  for (i = 0; i < builder->offset; i++)
+    g_variant_unref (builder->children[i]);
+
+  g_free (builder->children);
+
+  parent = builder->parent;
+  g_slice_free (GVariantBuilder, builder);
+
+  g_variant_builder_unref (parent);
+}
+
+/**
+ * g_variant_builder_ref;
+ * @builder: a #GVariantBuilder
+ * @returns: the same #GVariantBuilder
+ *
+ * Increases the reference count on @builder by 1.
+ **/
+GVariantBuilder *
+g_variant_builder_ref (GVariantBuilder *builder)
+{
+  builder->ref_count++;
+
+  return builder;
+}
+
+static void
+g_variant_builder_make_room (GVariantBuilder *builder)
+{
+  if (builder->offset == builder->allocated_children)
+    {
+      builder->allocated_children *= 2;
+      builder->children = g_renew (GVariant *, builder->children,
+                                   builder->allocated_children);
+    }
+}
+
+/**
+ * g_variant_builder_add_value:
+ * @builder: a #GVariantBuilder
+ * @value: a #GVariant
+ *
+ * Adds @value to @builder.
+ *
+ * It is an error to call this function in any way that would create an
+ * inconsistent value to be constructed.  Some examples of this are
+ * 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.
+ **/
+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)
+    {
+      /* advance our expected type pointers */
+      if (builder->expected_type)
+        builder->expected_type =
+          g_variant_type_next (builder->expected_type);
+
+      if (builder->prev_item_type)
+        builder->prev_item_type =
+          g_variant_type_next (builder->prev_item_type);
+    }
+  else
+    builder->prev_item_type = g_variant_get_type (value);
+
+  g_variant_builder_make_room (builder);
+
+  builder->children[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 @parent.
+ *
+ * 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 create an
+ * inconsistent value to be constructed.
+ **/
+GVariantBuilder *
+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;
+
+  /* push the prev_item_type down into the subcontainer */
+  if (builder->prev_item_type)
+    {
+      if (!child->uniform_item_types)
+        /* tuples and dict entries */
+        child->prev_item_type =
+          g_variant_type_first (builder->prev_item_type);
+
+      else if (!g_variant_type_is_variant (child->type))
+        /* maybes and arrays */
+        child->prev_item_type =
+          g_variant_type_element (builder->prev_item_type);
+    }
+
+  return child;
+}
+
+/**
+ * g_variant_builder_close:
+ * @builder: a #GVariantBuilder
+ * @returns: the original parent of @child
+ *
+ * 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.
+ *
+ * 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.
+ **/
+GVariantBuilder *
+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);
+
+  /* steal reference so _end() doesn't free it. */
+  parent = builder->parent;
+  builder->parent = NULL;
+
+  parent->has_child = FALSE;
+
+  g_variant_builder_add_value (parent, g_variant_builder_end (builder));
+
+  return parent;
+}
+
+/*< private >
+ * g_variant_make_maybe_type:
+ * @element: a #GVariant
+ *
+ * Return the type of a maybe containing @element.
+ */
+static GVariantType *
+g_variant_make_maybe_type (GVariant *element)
+{
+  return g_variant_type_new_maybe (g_variant_get_type (element));
+}
+
+/*< private >
+ * g_variant_make_array_type:
+ * @element: a #GVariant
+ *
+ * Return the type of an array containing @element.
+ */
+static GVariantType *
+g_variant_make_array_type (GVariant *element)
+{
+  return g_variant_type_new_array (g_variant_get_type (element));
+}
+
+/**
+ * g_variant_builder_end:
+ * @builder: a #GVariantBuilder
+ * @returns: a new, floating, #GVariant
+ *
+ * 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.
+ **/
+GVariant *
+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);
+
+  if (g_variant_type_is_definite (builder->type))
+    my_type = g_variant_type_copy (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_array (builder->type))
+    my_type = g_variant_make_array_type (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_dict_entry (builder->type))
+    my_type = g_variant_make_dict_entry_type (builder->children[0],
+                                              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);
+  g_variant_type_free (my_type);
+
+  return value;
+}
+
 /* Epilogue {{{1 */
 #define __G_VARIANT_C__
 #include "galiasdef.c"
diff --git a/glib/gvariant.h b/glib/gvariant.h
index 442082f..7753647 100644
--- a/glib/gvariant.h
+++ b/glib/gvariant.h
@@ -155,6 +155,22 @@ gboolean                        g_variant_iter_next_nofree              (GVarian
                                                                          const gchar          *format_string,
                                                                          ...);
 
+
+typedef struct _GVariantBuilder GVariantBuilder;
+
+void                            g_variant_builder_unref                 (GVariantBuilder      *builder);
+GVariantBuilder *               g_variant_builder_ref                   (GVariantBuilder      *builder);
+GVariantBuilder *               g_variant_builder_new                   (const GVariantType   *type);
+GVariant *                      g_variant_builder_end                   (GVariantBuilder      *builder);
+GVariantBuilder *               g_variant_builder_open                  (GVariantBuilder      *builder,
+                                                                         const GVariantType   *type);
+GVariantBuilder *               g_variant_builder_close                 (GVariantBuilder      *builder);
+void                            g_variant_builder_add_value             (GVariantBuilder      *builder,
+                                                                         GVariant             *value);
+void                            g_variant_builder_add                   (GVariantBuilder      *builder,
+                                                                         const gchar          *format_string,
+                                                                         ...);
+
 G_END_DECLS
 
 #endif /* __G_VARIANT_H__ */



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