Commit ec92eced authored by Thomas Haller's avatar Thomas Haller

libnm: add NMUtilsStrStrDictKey utility

When having a hash-of-hashes where each hash is indexed by a name,
(such as GKeyFile), you can either implement it as a hash-of-hashes
or define your own version of indexes that pack both levels of names
into one key.

This is an implementation of such a key. Use it as:

  GHashTable *hash = g_hash_table_new_full (_nm_utils_strstrdictkey_hash,
                                            _nm_utils_strstrdictkey_equal,
                                            g_free, _destroy_value);

and create keys via:

  NMUtilsStrStrDictKey *k = _nm_utils_strstrdictkey_create (s1, s2);

For lookup you can use static strings (note that the static string
might increase the size of the binary):

  g_hash_table_contains (hash, _nm_utils_strstrdictkey_static ("outer", "inner"))
parent 4f989108
......@@ -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
......@@ -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
......
......@@ -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);
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment