diff --git a/libnm-core/nm-core-internal.h b/libnm-core/nm-core-internal.h index 2ff9e5348e..ac61737e47 100644 --- a/libnm-core/nm-core-internal.h +++ b/libnm-core/nm-core-internal.h @@ -202,4 +202,14 @@ int _nm_utils_dns_option_find_idx (GPtrArray *array, const char *option) /***********************************************************/ +typedef struct _NMUtilsStrStrDictKey NMUtilsStrStrDictKey; +guint _nm_utils_strstrdictkey_hash (gconstpointer a); +gboolean _nm_utils_strstrdictkey_equal (gconstpointer a, gconstpointer b); +NMUtilsStrStrDictKey *_nm_utils_strstrdictkey_create (const char *v1, const char *v2); + +#define _nm_utils_strstrdictkey_static(v1, v2) \ + ( (NMUtilsStrStrDictKey *) ("\03" v1 "\0" v2 "") ) + +/***********************************************************/ + #endif diff --git a/libnm-core/nm-utils.c b/libnm-core/nm-utils.c index f6a9e5a05a..fec3999bce 100644 --- a/libnm-core/nm-utils.c +++ b/libnm-core/nm-utils.c @@ -3488,6 +3488,105 @@ nm_utils_bond_mode_string_to_int (const char *mode) /**********************************************************************************************/ +#define STRSTRDICTKEY_V1_SET 0x01 +#define STRSTRDICTKEY_V2_SET 0x02 +#define STRSTRDICTKEY_ALL_SET 0x03 + +struct _NMUtilsStrStrDictKey { + char type; + char data[1]; +}; + +guint +_nm_utils_strstrdictkey_hash (gconstpointer a) +{ + const NMUtilsStrStrDictKey *k = a; + const signed char *p; + guint32 h = 5381; + + if (k) { + if (((int) k->type) & ~STRSTRDICTKEY_ALL_SET) + g_return_val_if_reached (0); + + h = (h << 5) + h + k->type; + if (k->type & STRSTRDICTKEY_ALL_SET) { + p = (void *) k->data; + for (; *p != '\0'; p++) + h = (h << 5) + h + *p; + if (k->type == STRSTRDICTKEY_ALL_SET) { + /* the key contains two strings. Continue... */ + h = (h << 5) + h + '\0'; + for (p++; *p != '\0'; p++) + h = (h << 5) + h + *p; + } + } + } + + return h; +} + +gboolean +_nm_utils_strstrdictkey_equal (gconstpointer a, gconstpointer b) +{ + const NMUtilsStrStrDictKey *k1 = a; + const NMUtilsStrStrDictKey *k2 = b; + + if (k1 == k2) + return TRUE; + if (!k1 || !k2) + return FALSE; + + if (k1->type != k2->type) + return FALSE; + + if (k1->type & STRSTRDICTKEY_ALL_SET) { + if (strcmp (k1->data, k2->data) != 0) + return FALSE; + + if (k1->type == STRSTRDICTKEY_ALL_SET) { + gsize l = strlen (k1->data) + 1; + + return strcmp (&k1->data[l], &k2->data[l]) == 0; + } + } + + return TRUE; +} + +NMUtilsStrStrDictKey * +_nm_utils_strstrdictkey_create (const char *v1, const char *v2) +{ + char type = 0; + gsize l1 = 0, l2 = 0; + NMUtilsStrStrDictKey *k; + + if (!v1 && !v2) + return g_malloc0 (1); + + /* we need to distinguish between ("",NULL) and (NULL,""). + * Thus, in @type we encode which strings we have present + * as not-NULL. */ + if (v1) { + type |= STRSTRDICTKEY_V1_SET; + l1 = strlen (v1) + 1; + } + if (v2) { + type |= STRSTRDICTKEY_V2_SET; + l2 = strlen (v2) + 1; + } + + k = g_malloc (G_STRUCT_OFFSET (NMUtilsStrStrDictKey, data) + l1 + l2); + k->type = type; + if (v1) + memcpy (&k->data[0], v1, l1); + if (v2) + memcpy (&k->data[l1], v2, l2); + + return k; +} + +/**********************************************************************************************/ + /* _nm_utils_ascii_str_to_int64: * * A wrapper for g_ascii_strtoll, that checks whether the whole string diff --git a/libnm-core/tests/test-general.c b/libnm-core/tests/test-general.c index e8e96bb756..dfe591298e 100644 --- a/libnm-core/tests/test-general.c +++ b/libnm-core/tests/test-general.c @@ -4281,6 +4281,54 @@ test_nm_utils_ascii_str_to_int64 (void) /******************************************************************************/ +static void +test_nm_utils_strstrdictkey () +{ +#define _VALUES_STATIC(_v1, _v2) { .v1 = _v1, .v2 = _v2, .v_static = _nm_utils_strstrdictkey_static (_v1, _v2), } + const struct { + const char *v1; + const char *v2; + NMUtilsStrStrDictKey *v_static; + } *val1, *val2, values[] = { + { NULL, NULL }, + { "", NULL }, + { NULL, "" }, + { "a", NULL }, + { NULL, "a" }, + _VALUES_STATIC ("", ""), + _VALUES_STATIC ("a", ""), + _VALUES_STATIC ("", "a"), + _VALUES_STATIC ("a", "b"), + }; + guint i, j; + + for (i = 0; i < G_N_ELEMENTS (values); i++) { + gs_free NMUtilsStrStrDictKey *key1 = NULL; + + val1 = &values[i]; + + key1 = _nm_utils_strstrdictkey_create (val1->v1, val1->v2); + if (val1->v_static) { + g_assert (_nm_utils_strstrdictkey_equal (key1, val1->v_static)); + g_assert (_nm_utils_strstrdictkey_equal (val1->v_static, key1)); + g_assert_cmpint (_nm_utils_strstrdictkey_hash (key1), ==, _nm_utils_strstrdictkey_hash (val1->v_static)); + } + + for (j = 0; j < G_N_ELEMENTS (values); j++) { + gs_free NMUtilsStrStrDictKey *key2 = NULL; + + val2 = &values[j]; + key2 = _nm_utils_strstrdictkey_create (val2->v1, val2->v2); + if (i != j) { + g_assert (!_nm_utils_strstrdictkey_equal (key1, key2)); + g_assert (!_nm_utils_strstrdictkey_equal (key2, key1)); + } + } + } +} + +/******************************************************************************/ + static void test_nm_utils_dns_option_validate_do (char *option, gboolean ipv6, const NMUtilsDNSOptionDesc *descs, gboolean exp_result, char *exp_name, gboolean exp_value) @@ -4764,6 +4812,7 @@ int main (int argc, char **argv) g_test_add_func ("/core/general/nm_utils_is_power_of_two", test_nm_utils_is_power_of_two); g_test_add_func ("/core/general/_glib_compat_g_ptr_array_insert", test_g_ptr_array_insert); g_test_add_func ("/core/general/_nm_utils_ptrarray_find_binary_search", test_nm_utils_ptrarray_find_binary_search); + g_test_add_func ("/core/general/_nm_utils_strstrdictkey", test_nm_utils_strstrdictkey); g_test_add_func ("/core/general/_nm_utils_dns_option_validate", test_nm_utils_dns_option_validate); g_test_add_func ("/core/general/_nm_utils_dns_option_find_idx", test_nm_utils_dns_option_find_idx);