mirror of
https://gitlab.freedesktop.org/NetworkManager/NetworkManager.git
synced 2026-02-15 08:00:37 +01:00
glib-aux: add nm_g_variant_cmp()
There is g_variant_equal(), which can handle all variant types (however that is not a compare function). There is g_variant_compare(), which is a compare function but only works for basic types. Add nm_g_variant_cmp() which works with all variant types. This is based on nm_property_compare(), with some differences: - nm_property_compare() tries (wrongly) to accept string dictionaries in any order. That functionality seems wrong, and nm_g_variant_cmp() doesn't do that. - nm_property_compare() does possibly not support all variant types. This can be a problem, if we call the function on untrusted data (and it can be hard to validate first, whether the function can be called with a particular variant). Instead, nm_g_variant_cmp() should work with all variants. The unit tests are copied from "src/libnm-core-impl/tests/test-compare.c" with some adjustments (because nm_property_compare() is not the same as nm_g_variant_cmp()). Note that the code is actually unused. It was written as replacement for nm_property_compare(), but turns out not to be used there. For now, leave it, because it might still be useful to have in the toolbox and it exists (including tests).
This commit is contained in:
parent
e415e4fc18
commit
9db8cdb64d
3 changed files with 372 additions and 0 deletions
|
|
@ -623,6 +623,149 @@ nm_g_variant_maybe_singleton_i(gint32 value)
|
|||
|
||||
/*****************************************************************************/
|
||||
|
||||
static int
|
||||
_variant_type_cmp(const GVariantType *type1, const GVariantType *type2)
|
||||
{
|
||||
const char *string1;
|
||||
const char *string2;
|
||||
gsize size;
|
||||
|
||||
NM_CMP_SELF(type1, type2);
|
||||
|
||||
size = g_variant_type_get_string_length(type1);
|
||||
|
||||
NM_CMP_DIRECT(size, g_variant_type_get_string_length(type2));
|
||||
|
||||
string1 = g_variant_type_peek_string(type1);
|
||||
string2 = g_variant_type_peek_string(type2);
|
||||
|
||||
NM_CMP_DIRECT_MEMCMP(string1, string2, size);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
nm_g_variant_type_cmp(const GVariantType *type1, const GVariantType *type2)
|
||||
{
|
||||
int r;
|
||||
|
||||
r = _variant_type_cmp(type1, type2);
|
||||
nm_assert((!!g_variant_type_equal(type1, type2)) == (r == 0));
|
||||
return r;
|
||||
}
|
||||
|
||||
/*****************************************************************************/
|
||||
|
||||
typedef enum {
|
||||
VARIANT_CMP_TYPE_VARIANT,
|
||||
VARIANT_CMP_TYPE_STRDICT,
|
||||
VARIANT_CMP_TYPE_VARDICT,
|
||||
} VariantCmpType;
|
||||
|
||||
static int
|
||||
_variant_cmp_array(GVariant *value1, GVariant *value2, VariantCmpType type)
|
||||
{
|
||||
gsize len;
|
||||
gsize i;
|
||||
|
||||
len = g_variant_n_children(value1);
|
||||
|
||||
NM_CMP_DIRECT(len, g_variant_n_children(value2));
|
||||
|
||||
for (i = 0; i < len; i++) {
|
||||
gs_unref_variant GVariant *child1 = g_variant_get_child_value(value1, i);
|
||||
gs_unref_variant GVariant *child2 = g_variant_get_child_value(value2, i);
|
||||
const char *key1;
|
||||
const char *key2;
|
||||
const char *val1_str;
|
||||
const char *val2_str;
|
||||
|
||||
nm_assert(child1);
|
||||
nm_assert(child2);
|
||||
|
||||
switch (type) {
|
||||
case VARIANT_CMP_TYPE_VARIANT:
|
||||
NM_CMP_RETURN(nm_g_variant_cmp(child1, child2));
|
||||
break;
|
||||
case VARIANT_CMP_TYPE_STRDICT:
|
||||
g_variant_get(child1, "{&s&s}", &key1, &val1_str);
|
||||
g_variant_get(child2, "{&s&s}", &key2, &val2_str);
|
||||
NM_CMP_DIRECT_STRCMP(key1, key2);
|
||||
NM_CMP_DIRECT_STRCMP(val1_str, val2_str);
|
||||
break;
|
||||
case VARIANT_CMP_TYPE_VARDICT:
|
||||
{
|
||||
gs_unref_variant GVariant *val1_var = NULL;
|
||||
gs_unref_variant GVariant *val2_var = NULL;
|
||||
|
||||
g_variant_get(child1, "{&sv}", &key1, &val1_var);
|
||||
g_variant_get(child2, "{&sv}", &key2, &val2_var);
|
||||
NM_CMP_DIRECT_STRCMP(key1, key2);
|
||||
NM_CMP_RETURN(nm_g_variant_cmp(val1_var, val2_var));
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
_variant_cmp_generic(GVariant *value1, GVariant *value2)
|
||||
{
|
||||
gs_free char *str1 = NULL;
|
||||
gs_free char *str2 = NULL;
|
||||
|
||||
/* This is like g_variant_equal(), which also resorts to pretty-printing
|
||||
* the variants for comparison.
|
||||
*
|
||||
* Note that the variant types are already checked and equal. We thus don't
|
||||
* need to include the type annotation. */
|
||||
str1 = g_variant_print(value1, FALSE);
|
||||
str2 = g_variant_print(value2, FALSE);
|
||||
|
||||
NM_CMP_DIRECT_STRCMP(str1, str2);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
_variant_cmp(GVariant *value1, GVariant *value2)
|
||||
{
|
||||
const GVariantType *type;
|
||||
|
||||
NM_CMP_SELF(value1, value2);
|
||||
|
||||
type = g_variant_get_type(value1);
|
||||
|
||||
NM_CMP_RETURN(nm_g_variant_type_cmp(type, g_variant_get_type(value2)));
|
||||
|
||||
if (g_variant_type_is_basic(type))
|
||||
NM_CMP_RETURN(g_variant_compare(value1, value2));
|
||||
else if (g_variant_type_is_subtype_of(type, G_VARIANT_TYPE("a{ss}")))
|
||||
NM_CMP_RETURN(_variant_cmp_array(value1, value2, VARIANT_CMP_TYPE_STRDICT));
|
||||
else if (g_variant_type_is_subtype_of(type, G_VARIANT_TYPE("a{sv}")))
|
||||
NM_CMP_RETURN(_variant_cmp_array(value1, value2, VARIANT_CMP_TYPE_VARDICT));
|
||||
else if (g_variant_type_is_array(type) || g_variant_type_is_tuple(type))
|
||||
NM_CMP_RETURN(_variant_cmp_array(value1, value2, VARIANT_CMP_TYPE_VARIANT));
|
||||
else
|
||||
NM_CMP_RETURN(_variant_cmp_generic(value1, value2));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
nm_g_variant_cmp(GVariant *value1, GVariant *value2)
|
||||
{
|
||||
int r;
|
||||
|
||||
r = _variant_cmp(value1, value2);
|
||||
|
||||
nm_assert((!!nm_g_variant_equal(value1, value2)) == (r == 0));
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
/*****************************************************************************/
|
||||
|
||||
GHashTable *
|
||||
nm_strdict_clone(GHashTable *src)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -1418,6 +1418,10 @@ nm_g_variant_builder_add_sv_str(GVariantBuilder *builder, const char *key, const
|
|||
nm_g_variant_builder_add_sv(builder, key, g_variant_new_string(str));
|
||||
}
|
||||
|
||||
int nm_g_variant_type_cmp(const GVariantType *type1, const GVariantType *type2);
|
||||
|
||||
int nm_g_variant_cmp(GVariant *value1, GVariant *value2);
|
||||
|
||||
static inline void
|
||||
nm_g_source_destroy_and_unref(GSource *source)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -2630,6 +2630,224 @@ test_uid_to_name(void)
|
|||
|
||||
/*****************************************************************************/
|
||||
|
||||
static void
|
||||
compare_ints(void)
|
||||
{
|
||||
GVariant *value1, *value2;
|
||||
|
||||
value1 = g_variant_new_int32(5);
|
||||
value2 = g_variant_new_int32(5);
|
||||
g_assert(nm_g_variant_cmp(value1, value2) == 0);
|
||||
|
||||
g_variant_unref(value2);
|
||||
value2 = g_variant_new_int32(10);
|
||||
g_assert(nm_g_variant_cmp(value1, value2) < 0);
|
||||
|
||||
g_variant_unref(value2);
|
||||
value2 = g_variant_new_int32(-1);
|
||||
g_assert(nm_g_variant_cmp(value1, value2) > 0);
|
||||
|
||||
g_variant_unref(value1);
|
||||
g_variant_unref(value2);
|
||||
}
|
||||
|
||||
static void
|
||||
compare_strings(void)
|
||||
{
|
||||
GVariant *value1, *value2;
|
||||
const char *str1 = "hello";
|
||||
const char *str2 = "world";
|
||||
|
||||
value1 = g_variant_new_string(str1);
|
||||
value2 = g_variant_new_string(str1);
|
||||
g_assert(nm_g_variant_cmp(value1, value2) == 0);
|
||||
|
||||
g_variant_unref(value2);
|
||||
value2 = g_variant_new_string(str2);
|
||||
g_assert(nm_g_variant_cmp(value1, value2) < 0);
|
||||
|
||||
g_assert(nm_g_variant_cmp(value2, value1) > 0);
|
||||
|
||||
g_variant_unref(value1);
|
||||
g_variant_unref(value2);
|
||||
}
|
||||
|
||||
static void
|
||||
compare_strv(void)
|
||||
{
|
||||
GVariant *value1, *value2;
|
||||
const char *const strv1[] = {"foo", "bar", "baz", NULL};
|
||||
const char *const strv2[] = {"foo", "bar", "bar", NULL};
|
||||
const char *const strv3[] = {"foo", "bar", NULL};
|
||||
const char *const strv4[] = {"foo", "bar", "baz", "bam", NULL};
|
||||
|
||||
value1 = g_variant_new_strv(strv1, -1);
|
||||
value2 = g_variant_new_strv(strv1, -1);
|
||||
g_assert(nm_g_variant_cmp(value1, value2) == 0);
|
||||
|
||||
g_variant_unref(value2);
|
||||
value2 = g_variant_new_strv(strv2, -1);
|
||||
g_assert(nm_g_variant_cmp(value1, value2) != 0);
|
||||
|
||||
g_variant_unref(value2);
|
||||
value2 = g_variant_new_strv(strv3, -1);
|
||||
g_assert(nm_g_variant_cmp(value1, value2) != 0);
|
||||
|
||||
g_variant_unref(value2);
|
||||
value2 = g_variant_new_strv(strv4, -1);
|
||||
g_assert(nm_g_variant_cmp(value1, value2) != 0);
|
||||
|
||||
g_variant_unref(value1);
|
||||
g_variant_unref(value2);
|
||||
}
|
||||
|
||||
static void
|
||||
compare_arrays(void)
|
||||
{
|
||||
GVariant *value1, *value2;
|
||||
guint32 array[] = {0, 1, 2, 3, 4};
|
||||
|
||||
value1 = g_variant_new_fixed_array(G_VARIANT_TYPE_UINT32,
|
||||
array,
|
||||
G_N_ELEMENTS(array),
|
||||
sizeof(guint32));
|
||||
value2 = g_variant_new_fixed_array(G_VARIANT_TYPE_UINT32,
|
||||
array,
|
||||
G_N_ELEMENTS(array),
|
||||
sizeof(guint32));
|
||||
|
||||
g_assert(nm_g_variant_cmp(value1, value2) == 0);
|
||||
|
||||
g_variant_unref(value2);
|
||||
value2 = g_variant_new_fixed_array(G_VARIANT_TYPE_UINT32,
|
||||
array + 1,
|
||||
G_N_ELEMENTS(array) - 1,
|
||||
sizeof(guint32));
|
||||
g_assert(nm_g_variant_cmp(value1, value2) != 0);
|
||||
|
||||
array[0] = 7;
|
||||
g_variant_unref(value2);
|
||||
value2 = g_variant_new_fixed_array(G_VARIANT_TYPE_UINT32,
|
||||
array,
|
||||
G_N_ELEMENTS(array),
|
||||
sizeof(guint32));
|
||||
g_assert(nm_g_variant_cmp(value1, value2) != 0);
|
||||
|
||||
g_variant_unref(value1);
|
||||
g_variant_unref(value2);
|
||||
}
|
||||
|
||||
static void
|
||||
compare_str_hash(void)
|
||||
{
|
||||
GVariant *value1, *value2;
|
||||
GVariantBuilder builder;
|
||||
|
||||
g_variant_builder_init(&builder, G_VARIANT_TYPE("a{ss}"));
|
||||
g_variant_builder_add(&builder, "{ss}", "key1", "hello");
|
||||
g_variant_builder_add(&builder, "{ss}", "key2", "world");
|
||||
g_variant_builder_add(&builder, "{ss}", "key3", "!");
|
||||
value1 = g_variant_builder_end(&builder);
|
||||
|
||||
g_variant_builder_init(&builder, G_VARIANT_TYPE("a{ss}"));
|
||||
g_variant_builder_add(&builder, "{ss}", "key3", "!");
|
||||
g_variant_builder_add(&builder, "{ss}", "key2", "world");
|
||||
g_variant_builder_add(&builder, "{ss}", "key1", "hello");
|
||||
value2 = g_variant_builder_end(&builder);
|
||||
|
||||
g_assert(nm_g_variant_cmp(value1, value2) != 0);
|
||||
|
||||
g_variant_unref(value1);
|
||||
g_variant_builder_init(&builder, G_VARIANT_TYPE("a{sv}"));
|
||||
g_variant_builder_add(&builder, "{sv}", "key1", g_variant_new_string("hello"));
|
||||
g_variant_builder_add(&builder, "{sv}", "key2", g_variant_new_string("world"));
|
||||
g_variant_builder_add(&builder, "{sv}", "key3", g_variant_new_string("!"));
|
||||
value1 = g_variant_builder_end(&builder);
|
||||
|
||||
g_variant_unref(value2);
|
||||
g_variant_builder_init(&builder, G_VARIANT_TYPE("a{sv}"));
|
||||
g_variant_builder_add(&builder, "{sv}", "key1", g_variant_new_string("hello"));
|
||||
g_variant_builder_add(&builder, "{sv}", "key2", g_variant_new_string("world"));
|
||||
g_variant_builder_add(&builder, "{sv}", "key3", g_variant_new_string("!"));
|
||||
value2 = g_variant_builder_end(&builder);
|
||||
|
||||
g_assert(nm_g_variant_cmp(value1, value2) == 0);
|
||||
|
||||
g_variant_unref(value2);
|
||||
g_variant_builder_init(&builder, G_VARIANT_TYPE("a{ss}"));
|
||||
g_variant_builder_add(&builder, "{ss}", "key1", "hello");
|
||||
g_variant_builder_add(&builder, "{ss}", "key3", "!");
|
||||
value2 = g_variant_builder_end(&builder);
|
||||
|
||||
g_assert(nm_g_variant_cmp(value1, value2) != 0);
|
||||
g_assert(nm_g_variant_cmp(value2, value1) != 0);
|
||||
|
||||
g_variant_unref(value2);
|
||||
g_variant_builder_init(&builder, G_VARIANT_TYPE("a{ss}"));
|
||||
g_variant_builder_add(&builder, "{ss}", "key1", "hello");
|
||||
g_variant_builder_add(&builder, "{ss}", "key2", "moon");
|
||||
g_variant_builder_add(&builder, "{ss}", "key3", "!");
|
||||
value2 = g_variant_builder_end(&builder);
|
||||
|
||||
g_assert(nm_g_variant_cmp(value1, value2) != 0);
|
||||
|
||||
g_variant_unref(value1);
|
||||
g_variant_unref(value2);
|
||||
}
|
||||
|
||||
static void
|
||||
compare_ip6_addresses(void)
|
||||
{
|
||||
GVariant *value1, *value2;
|
||||
struct in6_addr addr1;
|
||||
struct in6_addr addr2;
|
||||
struct in6_addr addr3;
|
||||
guint32 prefix1 = 64;
|
||||
guint32 prefix2 = 64;
|
||||
guint32 prefix3 = 0;
|
||||
|
||||
inet_pton(AF_INET6, "1:2:3:4:5:6:7:8", &addr1);
|
||||
inet_pton(AF_INET6, "ffff:2:3:4:5:6:7:8", &addr2);
|
||||
inet_pton(AF_INET6, "::", &addr3);
|
||||
|
||||
value1 = g_variant_new(
|
||||
"(@ayu@ay)",
|
||||
g_variant_new_fixed_array(G_VARIANT_TYPE_BYTE, (guint8 *) addr1.s6_addr, 16, 1),
|
||||
prefix1,
|
||||
g_variant_new_fixed_array(G_VARIANT_TYPE_BYTE, (guint8 *) addr3.s6_addr, 16, 1));
|
||||
|
||||
value2 = g_variant_new(
|
||||
"(@ayu@ay)",
|
||||
g_variant_new_fixed_array(G_VARIANT_TYPE_BYTE, (guint8 *) addr1.s6_addr, 16, 1),
|
||||
prefix1,
|
||||
g_variant_new_fixed_array(G_VARIANT_TYPE_BYTE, (guint8 *) addr3.s6_addr, 16, 1));
|
||||
|
||||
g_assert(nm_g_variant_cmp(value1, value2) == 0);
|
||||
|
||||
g_variant_unref(value2);
|
||||
value2 = g_variant_new(
|
||||
"(@ayu@ay)",
|
||||
g_variant_new_fixed_array(G_VARIANT_TYPE_BYTE, (guint8 *) addr2.s6_addr, 16, 1),
|
||||
prefix2,
|
||||
g_variant_new_fixed_array(G_VARIANT_TYPE_BYTE, (guint8 *) addr3.s6_addr, 16, 1));
|
||||
|
||||
g_assert(nm_g_variant_cmp(value1, value2) != 0);
|
||||
|
||||
g_variant_unref(value2);
|
||||
value2 = g_variant_new(
|
||||
"(@ayu@ay)",
|
||||
g_variant_new_fixed_array(G_VARIANT_TYPE_BYTE, (guint8 *) addr3.s6_addr, 16, 1),
|
||||
prefix3,
|
||||
g_variant_new_fixed_array(G_VARIANT_TYPE_BYTE, (guint8 *) addr3.s6_addr, 16, 1));
|
||||
|
||||
g_assert(nm_g_variant_cmp(value1, value2) != 0);
|
||||
|
||||
g_variant_unref(value1);
|
||||
g_variant_unref(value2);
|
||||
}
|
||||
|
||||
/*****************************************************************************/
|
||||
|
||||
NMTST_DEFINE();
|
||||
|
||||
int
|
||||
|
|
@ -2683,5 +2901,12 @@ main(int argc, char **argv)
|
|||
g_test_add_func("/general/test_nm_random", test_nm_random);
|
||||
g_test_add_func("/general/test_uid_to_name", test_uid_to_name);
|
||||
|
||||
g_test_add_func("/libnm/compare/ints", compare_ints);
|
||||
g_test_add_func("/libnm/compare/strings", compare_strings);
|
||||
g_test_add_func("/libnm/compare/strv", compare_strv);
|
||||
g_test_add_func("/libnm/compare/arrays", compare_arrays);
|
||||
g_test_add_func("/libnm/compare/str_hash", compare_str_hash);
|
||||
g_test_add_func("/libnm/compare/ip6_addresses", compare_ip6_addresses);
|
||||
|
||||
return g_test_run();
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue