[glib/gvariantiter] add GVariantBuilder
- From: Ryan Lortie <ryanl src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [glib/gvariantiter] add GVariantBuilder
- Date: Sun, 21 Feb 2010 20:46:43 +0000 (UTC)
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]