GVariant: Add a G_VARIANT_DICT_INIT macro

The macro could be used at initialization time to avoid having an
unitialized dict, especially with g_auto variables.

The macro tries to be a bit more type-safe by making sure that the asv
parameter is actually "GVariant *".

https://bugzilla.gnome.org/show_bug.cgi?id=766370
This commit is contained in:
Krzesimir Nowak 2016-06-04 15:01:31 +02:00 committed by Matthias Clasen
parent e1c640f819
commit 992ded39bf
3 changed files with 117 additions and 9 deletions

View File

@ -3796,11 +3796,36 @@ struct heap_dict
#define GVSD(d) ((struct stack_dict *) (d))
#define GVHD(d) ((struct heap_dict *) (d))
#define GVSD_MAGIC ((gsize) 2579507750u)
#define GVSD_MAGIC_PARTIAL ((gsize) 3488698669u)
#define GVHD_MAGIC ((gsize) 2450270775u)
#define is_valid_dict(d) (d != NULL && \
GVSD(d)->magic == GVSD_MAGIC)
#define is_valid_heap_dict(d) (GVHD(d)->magic == GVHD_MAGIC)
/* Just to make sure that by adding a union to GVariantDict, we didn't
* accidentally change ABI. */
G_STATIC_ASSERT (sizeof (GVariantDict) == sizeof (gsize[16]));
static gboolean
ensure_valid_dict (GVariantDict *dict)
{
if (is_valid_dict (dict))
return TRUE;
if (dict->partial_magic == GVSD_MAGIC_PARTIAL)
{
static GVariantDict cleared_dict;
/* 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_dict.y, dict->y, sizeof cleared_dict.y))
return FALSE;
g_variant_dict_init (dict, dict->asv);
}
return is_valid_dict (dict);
}
/**
* g_variant_dict_new:
* @from_asv: (allow-none): the #GVariant with which to initialise the
@ -3908,7 +3933,7 @@ g_variant_dict_lookup (GVariantDict *dict,
GVariant *value;
va_list ap;
g_return_val_if_fail (is_valid_dict (dict), FALSE);
g_return_val_if_fail (ensure_valid_dict (dict), FALSE);
g_return_val_if_fail (key != NULL, FALSE);
g_return_val_if_fail (format_string != NULL, FALSE);
@ -3953,7 +3978,7 @@ g_variant_dict_lookup_value (GVariantDict *dict,
{
GVariant *result;
g_return_val_if_fail (is_valid_dict (dict), NULL);
g_return_val_if_fail (ensure_valid_dict (dict), NULL);
g_return_val_if_fail (key != NULL, NULL);
result = g_hash_table_lookup (GVSD(dict)->values, key);
@ -3979,7 +4004,7 @@ gboolean
g_variant_dict_contains (GVariantDict *dict,
const gchar *key)
{
g_return_val_if_fail (is_valid_dict (dict), FALSE);
g_return_val_if_fail (ensure_valid_dict (dict), FALSE);
g_return_val_if_fail (key != NULL, FALSE);
return g_hash_table_contains (GVSD(dict)->values, key);
@ -4007,7 +4032,7 @@ g_variant_dict_insert (GVariantDict *dict,
{
va_list ap;
g_return_if_fail (is_valid_dict (dict));
g_return_if_fail (ensure_valid_dict (dict));
g_return_if_fail (key != NULL);
g_return_if_fail (format_string != NULL);
@ -4033,7 +4058,7 @@ g_variant_dict_insert_value (GVariantDict *dict,
const gchar *key,
GVariant *value)
{
g_return_if_fail (is_valid_dict (dict));
g_return_if_fail (ensure_valid_dict (dict));
g_return_if_fail (key != NULL);
g_return_if_fail (value != NULL);
@ -4055,7 +4080,7 @@ gboolean
g_variant_dict_remove (GVariantDict *dict,
const gchar *key)
{
g_return_val_if_fail (is_valid_dict (dict), FALSE);
g_return_val_if_fail (ensure_valid_dict (dict), FALSE);
g_return_val_if_fail (key != NULL, FALSE);
return g_hash_table_remove (GVSD(dict)->values, key);
@ -4089,7 +4114,7 @@ g_variant_dict_clear (GVariantDict *dict)
/* all-zeros case */
return;
g_return_if_fail (is_valid_dict (dict));
g_return_if_fail (ensure_valid_dict (dict));
g_hash_table_unref (GVSD(dict)->values);
GVSD(dict)->values = NULL;
@ -4120,7 +4145,7 @@ g_variant_dict_end (GVariantDict *dict)
GHashTableIter iter;
gpointer key, value;
g_return_val_if_fail (is_valid_dict (dict), NULL);
g_return_val_if_fail (ensure_valid_dict (dict), NULL);
g_variant_builder_init (&builder, G_VARIANT_TYPE_VARDICT);

View File

@ -436,9 +436,46 @@ gint g_variant_compare (gconstp
typedef struct _GVariantDict GVariantDict;
struct _GVariantDict {
/*< private >*/
gsize x[16];
union
{
struct {
GVariant *asv;
gsize partial_magic;
gsize y[14];
};
gsize x[16];
};
};
/**
* G_VARIANT_DICT_INIT:
* @asv: (nullable): a GVariant*
*
* A stack-allocated #GVariantDict must be initialized if it is used
* together with g_auto() to avoid warnings or crashes if function
* returns before g_variant_dict_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_dict_init(), but it cannot be assigned to a variable.
*
* The passed @asv has to live long enough for #GVariantDict to gather
* the entries from, as the gathering does not happen in the
* G_VARIANT_DICT_INIT() call, but rather in functions that make sure
* that #GVariantDict is valid. In context where the initialization
* value has to be a constant expression, the only possible value of
* @asv is %NULL. It is still possible to call g_variant_dict_init()
* safely with a different @asv right after the variable was
* initialized with G_VARIANT_DICT_INIT().
*
* |[
* g_autoptr(GVariant) variant = get_asv_variant ();
* g_auto(GVariantDict) dict = G_VARIANT_DICT_INIT (variant);
* ]|
*
* Since: 2.50
*/
#define G_VARIANT_DICT_INIT(asv) { { { asv, 3488698669u, { 0, } } } }
GLIB_AVAILABLE_IN_2_40
GVariantDict * g_variant_dict_new (GVariant *from_asv);

View File

@ -4554,6 +4554,51 @@ test_stack_builder_init (void)
g_variant_unref (variant);
}
static GVariant *
get_asv (void)
{
GVariantBuilder builder = G_VARIANT_BUILDER_INIT (G_VARIANT_TYPE_VARDICT);
g_variant_builder_add (&builder, "{s@v}", "foo", g_variant_new_variant (g_variant_new_string ("FOO")));
g_variant_builder_add (&builder, "{s@v}", "bar", g_variant_new_variant (g_variant_new_string ("BAR")));
return g_variant_ref_sink (g_variant_builder_end (&builder));
}
static void
test_stack_dict_init (void)
{
GVariant *asv = get_asv ();
GVariantDict dict = G_VARIANT_DICT_INIT (asv);
GVariant *variant;
GVariantIter iter;
gchar *key;
GVariant *value;
g_variant_dict_insert_value (&dict, "baz", g_variant_new_string ("BAZ"));
g_variant_dict_insert_value (&dict, "quux", g_variant_new_string ("QUUX"));
variant = g_variant_ref_sink (g_variant_dict_end (&dict));
g_assert_nonnull (variant);
g_assert (g_variant_type_equal (g_variant_get_type (variant),
G_VARIANT_TYPE_VARDICT));
g_assert_cmpuint (g_variant_n_children (variant), ==, 4);
g_variant_iter_init (&iter, variant);
while (g_variant_iter_next (&iter, "{sv}", &key, &value))
{
gchar *strup = g_ascii_strup (key, -1);
g_assert_cmpstr (strup, ==, g_variant_get_string (value, NULL));
g_free (key);
g_free (strup);
g_variant_unref (value);
}
g_variant_unref (asv);
g_variant_unref (variant);
}
int
main (int argc, char **argv)
{
@ -4614,5 +4659,6 @@ main (int argc, char **argv)
g_test_add_func ("/gvariant/error-quark", test_error_quark);
g_test_add_func ("/gvariant/stack-builder-init", test_stack_builder_init);
g_test_add_func ("/gvariant/stack-dict-init", test_stack_dict_init);
return g_test_run ();
}