From 7fc6f8a1596b18a23e1570fc6716b34a137b76c6 Mon Sep 17 00:00:00 2001 From: Ryan Lortie Date: Fri, 5 Nov 2010 21:33:06 -0400 Subject: [PATCH] Add g_variant_lookup() and tests Convenience API for doing lookups in dictionaries where the key is a string or object path. --- docs/reference/glib/glib-sections.txt | 2 + glib/glib.symbols | 2 + glib/gvariant.c | 144 ++++++++++++++++++++++++++++++++++ glib/gvariant.h | 7 ++ glib/tests/gvariant.c | 74 +++++++++++++++++ 5 files changed, 229 insertions(+) diff --git a/docs/reference/glib/glib-sections.txt b/docs/reference/glib/glib-sections.txt index d2f7422..dca0a98 100644 --- a/docs/reference/glib/glib-sections.txt +++ b/docs/reference/glib/glib-sections.txt @@ -3015,6 +3015,8 @@ g_variant_get_maybe g_variant_n_children g_variant_get_child_value g_variant_get_child +g_variant_lookup_value +g_variant_lookup g_variant_get_fixed_array diff --git a/glib/glib.symbols b/glib/glib.symbols index 232e12a..73ec856 100644 --- a/glib/glib.symbols +++ b/glib/glib.symbols @@ -1880,6 +1880,8 @@ g_variant_get g_variant_builder_add g_variant_get_child +g_variant_lookup_value +g_variant_lookup g_variant_iter_next g_variant_iter_loop diff --git a/glib/gvariant.c b/glib/gvariant.c index e333597..5c8737f 100644 --- a/glib/gvariant.c +++ b/glib/gvariant.c @@ -899,6 +899,150 @@ g_variant_new_dict_entry (GVariant *key, } /** + * g_variant_lookup: + * @dictionary: a dictionary #GVariant + * @key: the key to lookup in the dictionary + * @format_string: a GVariant format string + * @...: the arguments to unpack the value into + * + * Looks up a value in a dictionary #GVariant. + * + * This function is a wrapper around g_variant_lookup_value() and + * g_variant_get(). In the case that %NULL would have been returned, + * this function returns %FALSE. Otherwise, it unpacks the returned + * value and returns %TRUE. + * + * See g_variant_get() for information about @format_string. + * + * Returns: %TRUE if a value was unpacked + * + * Since: 2.28 + */ +gboolean +g_variant_lookup (GVariant *dictionary, + const gchar *key, + const gchar *format_string, + ...) +{ + GVariantType *type; + GVariant *value; + + /* flatten */ + g_variant_get_data (dictionary); + + type = g_variant_format_string_scan_type (format_string, NULL, NULL); + value = g_variant_lookup_value (dictionary, key, type); + g_variant_type_free (type); + + if (value) + { + va_list ap; + + va_start (ap, format_string); + g_variant_get_va (value, format_string, NULL, &ap); + g_variant_unref (value); + va_end (ap); + + return TRUE; + } + + else + return FALSE; +} + +/** + * g_variant_lookup: + * @dictionary: a dictionary #GVariant + * @key: the key to lookup in the dictionary + * @expected_type: a #GVariantType, or %NULL + * + * Looks up a value in a dictionary #GVariant. + * + * This function works with dictionaries of the type + * a{s*} (and equally well with type + * a{o*}, but we only further discuss the string case + * for sake of clarity). + * + * In the event that @dictionary has the type a{sv}, + * the @expected_type string specifies what type of value is expected to + * be inside of the variant. If the value inside the variant has a + * different type then %NULL is returned. In the event that @dictionary + * has a value type other than v then @expected_type + * must directly match the key type and it is used to unpack the value + * directly or an error occurs. + * + * In either case, if @key is not found in @dictionary, %NULL is + * returned. + * + * If the key is found and the value has the correct type, it is + * returned. If @expected_type was specified then any non-%NULL return + * value will have this type. + * + * Returns: the value of the dictionary key, or %NULL + * + * Since: 2.28 + */ +GVariant * +g_variant_lookup_value (GVariant *dictionary, + const gchar *key, + const GVariantType *expected_type) +{ + GVariantIter iter; + GVariant *entry; + GVariant *value; + + g_return_val_if_fail (g_variant_is_of_type (dictionary, + G_VARIANT_TYPE ("a{s*}")) || + g_variant_is_of_type (dictionary, + G_VARIANT_TYPE ("a{o*}")), + NULL); + + g_variant_iter_init (&iter, dictionary); + + while ((entry = g_variant_iter_next_value (&iter))) + { + GVariant *entry_key; + gboolean matches; + + entry_key = g_variant_get_child_value (entry, 0); + matches = strcmp (g_variant_get_string (entry_key, NULL), key) == 0; + g_variant_unref (entry_key); + + if (matches) + break; + + g_variant_unref (entry); + } + + if (entry == NULL) + return NULL; + + value = g_variant_get_child_value (entry, 1); + g_variant_unref (entry); + + if (g_variant_is_of_type (value, G_VARIANT_TYPE_VARIANT)) + { + GVariant *tmp; + + tmp = g_variant_get_variant (value); + g_variant_unref (value); + + if (expected_type && !g_variant_is_of_type (tmp, expected_type)) + { + g_variant_unref (tmp); + tmp = NULL; + } + + value = tmp; + } + + g_return_val_if_fail (expected_type == NULL || value == NULL || + g_variant_is_of_type (value, expected_type), NULL); + + return value; +} + +/** * g_variant_get_fixed_array: * @value: a #GVariant array with fixed-sized elements * @n_elements: a pointer to the location to store the number of items diff --git a/glib/gvariant.h b/glib/gvariant.h index 528492b..9491c99 100644 --- a/glib/gvariant.h +++ b/glib/gvariant.h @@ -134,6 +134,13 @@ void g_variant_get_child (GVarian ...); GVariant * g_variant_get_child_value (GVariant *value, gsize index_); +gboolean g_variant_lookup (GVariant *value, + const gchar *key, + const gchar *format_string, + ...); +GVariant * g_variant_lookup_value (GVariant *value, + const gchar *key, + const GVariantType *type); gconstpointer g_variant_get_fixed_array (GVariant *value, gsize *n_elements, gsize element_size); diff --git a/glib/tests/gvariant.c b/glib/tests/gvariant.c index 2c3c048..afbafd4 100644 --- a/glib/tests/gvariant.c +++ b/glib/tests/gvariant.c @@ -3897,6 +3897,78 @@ test_bytestring (void) g_variant_unref (untrusted_empty); } +static void +test_lookup_value (void) +{ + struct { + const gchar *dict, *key, *value; + } cases[] = { + { "@a{ss} {'x': 'y'}", "x", "'y'" }, + { "@a{ss} {'x': 'y'}", "y" }, + { "@a{os} {'/x': 'y'}", "/x", "'y'" }, + { "@a{os} {'/x': 'y'}", "/y" }, + { "@a{sv} {'x': <'y'>}", "x", "'y'" }, + { "@a{sv} {'x': <5>}", "x", "5" }, + { "@a{sv} {'x': <'y'>}", "y" } + }; + gint i; + + for (i = 0; i < G_N_ELEMENTS (cases); i++) + { + GVariant *dictionary; + GVariant *value; + gchar *p; + + dictionary = g_variant_parse (NULL, cases[i].dict, NULL, NULL, NULL); + value = g_variant_lookup_value (dictionary, cases[i].key, NULL); + g_variant_unref (dictionary); + + if (value == NULL && cases[i].value == NULL) + continue; + + g_assert (value && cases[i].value); + p = g_variant_print (value, FALSE); + g_assert_cmpstr (cases[i].value, ==, p); + g_variant_unref (value); + g_free (p); + } +} + +static void +test_lookup (void) +{ + const gchar *str; + GVariant *dict; + gboolean ok; + gint num; + + dict = g_variant_parse (NULL, + "{'a': <5>, 'b': <'c'>}", + NULL, NULL, NULL); + + ok = g_variant_lookup (dict, "a", "i", &num); + g_assert (ok); + g_assert_cmpint (num, ==, 5); + + ok = g_variant_lookup (dict, "a", "&s", &str); + g_assert (!ok); + + ok = g_variant_lookup (dict, "q", "&s", &str); + g_assert (!ok); + + ok = g_variant_lookup (dict, "b", "i", &num); + g_assert (!ok); + + ok = g_variant_lookup (dict, "b", "&s", &str); + g_assert (ok); + g_assert_cmpstr (str, ==, "c"); + + ok = g_variant_lookup (dict, "q", "&s", &str); + g_assert (!ok); + + g_variant_unref (dict); +} + int main (int argc, char **argv) { @@ -3937,6 +4009,8 @@ main (int argc, char **argv) g_test_add_func ("/gvariant/parse-positional", test_parse_positional); g_test_add_func ("/gvariant/floating", test_floating); g_test_add_func ("/gvariant/bytestring", test_bytestring); + g_test_add_func ("/gvariant/lookup-value", test_lookup_value); + g_test_add_func ("/gvariant/lookup", test_lookup); return g_test_run (); } -- 2.7.4