[glib] GVariant: Add a G_VARIANT_BUILDER_INIT macro
- From: Matthias Clasen <matthiasc src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [glib] GVariant: Add a G_VARIANT_BUILDER_INIT macro
- Date: Sun, 17 Jul 2016 02:14:02 +0000 (UTC)
commit e1c640f81934ad3b908354ace1fda7344c368c85
Author: Krzesimir Nowak <qdlacz gmail com>
Date: Fri May 13 17:55:30 2016 +0200
GVariant: Add a G_VARIANT_BUILDER_INIT macro
The macro could be used at initialization time to avoid having an
unitialized builder, especially with g_auto variables.
The macro tries to be a bit more type-safe by making sure that the
variant_type parameter is actually "const GVariantType
*". Unfortunately I have no idea how to make it possible to also pass
a "const gchar *" parameter without warning.
https://bugzilla.gnome.org/show_bug.cgi?id=766370
docs/reference/glib/glib-sections.txt | 1 +
glib/gvariant.c | 37 +++++++++++++++++++++++++++-----
glib/gvariant.h | 34 +++++++++++++++++++++++++++++-
glib/tests/gvariant.c | 22 +++++++++++++++++++
4 files changed, 87 insertions(+), 7 deletions(-)
---
diff --git a/docs/reference/glib/glib-sections.txt b/docs/reference/glib/glib-sections.txt
index 4636036..577118a 100644
--- a/docs/reference/glib/glib-sections.txt
+++ b/docs/reference/glib/glib-sections.txt
@@ -3329,6 +3329,7 @@ g_variant_iter_next
g_variant_iter_loop
<SUBSECTION>
+G_VARIANT_BUILDER_INIT
GVariantBuilder
g_variant_builder_unref
g_variant_builder_ref
diff --git a/glib/gvariant.c b/glib/gvariant.c
index ba34ff8..9dca358 100644
--- a/glib/gvariant.c
+++ b/glib/gvariant.c
@@ -3169,11 +3169,36 @@ struct heap_builder
#define GVSB(b) ((struct stack_builder *) (b))
#define GVHB(b) ((struct heap_builder *) (b))
#define GVSB_MAGIC ((gsize) 1033660112u)
+#define GVSB_MAGIC_PARTIAL ((gsize) 2942751021u)
#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)
+/* Just to make sure that by adding a union to GVariantBuilder, we
+ * didn't accidentally change ABI. */
+G_STATIC_ASSERT (sizeof (GVariantBuilder) == sizeof (gsize[16]));
+
+static gboolean
+ensure_valid_builder (GVariantBuilder *builder)
+{
+ if (is_valid_builder (builder))
+ return TRUE;
+ if (builder->partial_magic == GVSB_MAGIC_PARTIAL)
+ {
+ static GVariantBuilder cleared_builder;
+
+ /* Make sure that only first two fields were set and the rest is
+ * zeroed to avoid messing up the builder that had parent
+ * address equal to GVSB_MAGIC_PARTIAL. */
+ if (memcmp (cleared_builder.y, builder->y, sizeof cleared_builder.y))
+ return FALSE;
+
+ g_variant_builder_init (builder, builder->type);
+ }
+ return is_valid_builder (builder);
+}
+
/**
* g_variant_builder_new:
* @type: a container type
@@ -3283,10 +3308,10 @@ g_variant_builder_clear (GVariantBuilder *builder)
gsize i;
if (GVSB(builder)->magic == 0)
- /* all-zeros case */
+ /* all-zeros or partial case */
return;
- g_return_if_fail (is_valid_builder (builder));
+ g_return_if_fail (ensure_valid_builder (builder));
g_variant_type_free (GVSB(builder)->type);
@@ -3449,7 +3474,7 @@ void
g_variant_builder_add_value (GVariantBuilder *builder,
GVariant *value)
{
- g_return_if_fail (is_valid_builder (builder));
+ g_return_if_fail (ensure_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,
@@ -3500,7 +3525,7 @@ g_variant_builder_open (GVariantBuilder *builder,
{
GVariantBuilder *parent;
- g_return_if_fail (is_valid_builder (builder));
+ g_return_if_fail (ensure_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,
@@ -3546,7 +3571,7 @@ g_variant_builder_close (GVariantBuilder *builder)
{
GVariantBuilder *parent;
- g_return_if_fail (is_valid_builder (builder));
+ g_return_if_fail (ensure_valid_builder (builder));
g_return_if_fail (GVSB(builder)->parent != NULL);
parent = GVSB(builder)->parent;
@@ -3614,7 +3639,7 @@ g_variant_builder_end (GVariantBuilder *builder)
GVariantType *my_type;
GVariant *value;
- g_return_val_if_fail (is_valid_builder (builder), NULL);
+ g_return_val_if_fail (ensure_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 ||
diff --git a/glib/gvariant.h b/glib/gvariant.h
index fa0fee1..7d59213 100644
--- a/glib/gvariant.h
+++ b/glib/gvariant.h
@@ -297,7 +297,15 @@ gboolean g_variant_iter_loop (GVarian
typedef struct _GVariantBuilder GVariantBuilder;
struct _GVariantBuilder {
/*< private >*/
- gsize x[16];
+ union
+ {
+ struct {
+ gsize partial_magic;
+ const GVariantType *type;
+ gsize y[14];
+ };
+ gsize x[16];
+ };
};
typedef enum
@@ -329,6 +337,30 @@ GQuark g_variant_parser_get_error_quark (void);
GLIB_AVAILABLE_IN_ALL
GQuark g_variant_parse_error_quark (void);
+/**
+ * G_VARIANT_BUILDER_INIT:
+ * @variant_type: a const GVariantType*
+ *
+ * A stack-allocated #GVariantBuilder must be initialized if it is
+ * used together with g_auto() to avoid warnings or crashes if
+ * function returns before g_variant_builder_init() is called on the
+ * builder. This macro can be used as initializer instead of an
+ * explicit zeroing a variable when declaring it and a following
+ * g_variant_builder_init(), but it cannot be assigned to a variable.
+ *
+ * The passed @variant_type should be a static GVariantType to avoid
+ * lifetime issues, as copying the @variant_type does not happen in
+ * the G_VARIANT_BUILDER_INIT() call, but rather in functions that
+ * make sure that #GVariantBuilder is valid.
+ *
+ * |[
+ * g_auto(GVariantBuilder) builder = G_VARIANT_BUILDER_INIT (G_VARIANT_TYPE_BYTESTRING);
+ * ]|
+ *
+ * Since: 2.50
+ */
+#define G_VARIANT_BUILDER_INIT(variant_type) { { { 2942751021u, variant_type, { 0, } } } }
+
GLIB_AVAILABLE_IN_ALL
GVariantBuilder * g_variant_builder_new (const GVariantType *type);
GLIB_AVAILABLE_IN_ALL
diff --git a/glib/tests/gvariant.c b/glib/tests/gvariant.c
index 889b28b..5489733 100644
--- a/glib/tests/gvariant.c
+++ b/glib/tests/gvariant.c
@@ -4533,6 +4533,27 @@ G_GNUC_BEGIN_IGNORE_DEPRECATIONS
G_GNUC_END_IGNORE_DEPRECATIONS
}
+static void
+test_stack_builder_init (void)
+{
+ GVariantBuilder builder = G_VARIANT_BUILDER_INIT (G_VARIANT_TYPE_BYTESTRING);
+ GVariant *variant;
+
+ g_variant_builder_add_value (&builder, g_variant_new_byte ('g'));
+ g_variant_builder_add_value (&builder, g_variant_new_byte ('l'));
+ g_variant_builder_add_value (&builder, g_variant_new_byte ('i'));
+ g_variant_builder_add_value (&builder, g_variant_new_byte ('b'));
+ g_variant_builder_add_value (&builder, g_variant_new_byte ('\0'));
+
+ variant = g_variant_ref_sink (g_variant_builder_end (&builder));
+ g_assert_nonnull (variant);
+ g_assert (g_variant_type_equal (g_variant_get_type (variant),
+ G_VARIANT_TYPE_BYTESTRING));
+ g_assert_cmpuint (g_variant_n_children (variant), ==, 5);
+ g_assert_cmpstr (g_variant_get_bytestring (variant), ==, "glib");
+ g_variant_unref (variant);
+}
+
int
main (int argc, char **argv)
{
@@ -4592,5 +4613,6 @@ main (int argc, char **argv)
g_test_add_func ("/gvariant/print-context", test_print_context);
g_test_add_func ("/gvariant/error-quark", test_error_quark);
+ g_test_add_func ("/gvariant/stack-builder-init", test_stack_builder_init);
return g_test_run ();
}
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]