about summary refs log tree commit diff
path: root/shared/nm-utils/nm-hash-utils.c
diff options
context:
space:
mode:
Diffstat (limited to 'shared/nm-utils/nm-hash-utils.c')
-rw-r--r--shared/nm-utils/nm-hash-utils.c134
1 files changed, 99 insertions, 35 deletions
diff --git a/shared/nm-utils/nm-hash-utils.c b/shared/nm-utils/nm-hash-utils.c
index c563140e..4bc12b7c 100644
--- a/shared/nm-utils/nm-hash-utils.c
+++ b/shared/nm-utils/nm-hash-utils.c
@@ -35,33 +35,77 @@
 
 G_STATIC_ASSERT (sizeof (guint) * HASH_KEY_SIZE_GUINT >= HASH_KEY_SIZE);
 
+static const guint8 *volatile global_seed = NULL;
+
 static const guint8 *
-_get_hash_key (void)
+_get_hash_key_init (void)
 {
-	static const guint8 *volatile global_seed = NULL;
+	/* the returned hash is aligned to guin64, hence, it is safe
+	 * to use it as guint* or guint64* pointer. */
+	static union {
+		guint8 v8[HASH_KEY_SIZE];
+	} g_arr _nm_alignas (guint64);
+	static gsize g_lock;
 	const guint8 *g;
+	CSipHash siph_state;
+	uint64_t h;
+	guint *p;
 
 	g = global_seed;
-	if (G_UNLIKELY (g == NULL)) {
-		/* the returned hash is aligned to guin64, hence, it is save
-		 * to use it as guint* or guint64* pointer. */
-		static union {
-			guint8 v8[HASH_KEY_SIZE];
-		} g_arr _nm_alignas (guint64);
-		static gsize g_lock;
-
-		if (g_once_init_enter (&g_lock)) {
-			nm_utils_random_bytes (g_arr.v8, sizeof (g_arr.v8));
-			g_atomic_pointer_compare_and_exchange (&global_seed, NULL, g_arr.v8);
-			g = g_arr.v8;
-			g_once_init_leave (&g_lock, 1);
-		} else {
-			g = global_seed;
-			nm_assert (g);
-		}
+	if (G_LIKELY (g != NULL)) {
+		nm_assert (g == g_arr.v8);
+		return g;
 	}
 
-	return g;
+	if (g_once_init_enter (&g_lock)) {
+
+		nm_utils_random_bytes (g_arr.v8, sizeof (g_arr.v8));
+
+		/* use siphash() of the key-size, to mangle the first guint. Otherwise,
+		 * the first guint has only the entropy that nm_utils_random_bytes()
+		 * generated for the first 4 bytes and relies on a good random generator. */
+		c_siphash_init (&siph_state, g_arr.v8);
+		c_siphash_append (&siph_state, g_arr.v8, sizeof (g_arr.v8));
+		h = c_siphash_finalize (&siph_state);
+		p = (guint *) g_arr.v8;
+		if (sizeof (guint) < sizeof (h))
+			*p = *p ^ ((guint) (h & 0xFFFFFFFFu)) ^ ((guint) (h >> 32));
+		else
+			*p = *p ^ ((guint) (h & 0xFFFFFFFFu));
+
+		g_atomic_pointer_compare_and_exchange (&global_seed, NULL, g_arr.v8);
+		g_once_init_leave (&g_lock, 1);
+	}
+
+	nm_assert (global_seed == g_arr.v8);
+	return g_arr.v8;
+}
+
+#define _get_hash_key() \
+	({ \
+		const guint8 *_g; \
+		\
+		_g = global_seed; \
+		if (G_UNLIKELY (_g == NULL)) \
+			_g = _get_hash_key_init (); \
+		_g; \
+	})
+
+guint
+nm_hash_static (guint static_seed)
+{
+	/* note that we only xor the static_seed with the key.
+	 * We don't use siphash, which would mix the bits better.
+	 * Note that this doesn't matter, because static_seed is not
+	 * supposed to be a value that you are hashing (for that, use
+	 * full siphash).
+	 * Instead, different callers may set a different static_seed
+	 * so that nm_hash_str(NULL) != nm_hash_ptr(NULL).
+	 *
+	 * Also, ensure that we don't return zero.
+	 */
+	return ((*((const guint *) _get_hash_key ())) ^ static_seed)
+	       ?: static_seed ?: 3679500967u;
 }
 
 void
@@ -75,7 +119,7 @@ nm_hash_init (NMHashState *state, guint static_seed)
 	g = _get_hash_key ();
 	memcpy (seed, g, HASH_KEY_SIZE);
 	seed[0] ^= static_seed;
-	siphash24_init (&state->_state, (const guint8 *) seed);
+	c_siphash_init (&state->_state, (const guint8 *) seed);
 }
 
 guint
@@ -83,11 +127,10 @@ nm_hash_str (const char *str)
 {
 	NMHashState h;
 
-	if (str) {
-		nm_hash_init (&h, 1867854211u);
-		nm_hash_update_str (&h, str);
-	} else
-		nm_hash_init (&h, 842995561u);
+	if (!str)
+		return nm_hash_static (1867854211u);
+	nm_hash_init (&h, 1867854211u);
+	nm_hash_update_str (&h, str);
 	return nm_hash_complete (&h);
 }
 
@@ -100,16 +143,13 @@ nm_str_hash (gconstpointer str)
 guint
 nm_hash_ptr (gconstpointer ptr)
 {
-	guint h;
-
-	h = ((const guint *) _get_hash_key ())[0];
-
-	if (sizeof (ptr) <= sizeof (guint))
-		h = h ^ ((guint) ((uintptr_t) ptr));
-	else
-		h = h ^ ((guint) (((guint64) (uintptr_t) ptr) >> 32)) ^ ((guint) ((uintptr_t) ptr));
+	NMHashState h;
 
-	return h ?: 2907677551u;
+	if (!ptr)
+		return nm_hash_static (2907677551u);
+	nm_hash_init (&h, 2907677551u);
+	nm_hash_update (&h, &ptr, sizeof (ptr));
+	return nm_hash_complete (&h);
 }
 
 guint
@@ -117,3 +157,27 @@ nm_direct_hash (gconstpointer ptr)
 {
 	return nm_hash_ptr (ptr);
 }
+
+/*****************************************************************************/
+
+guint
+nm_pstr_hash (gconstpointer p)
+{
+	const char *const*s = p;
+
+	if (!s)
+		return nm_hash_static (101061439u);
+	return nm_hash_str (*s);
+}
+
+gboolean
+nm_pstr_equal (gconstpointer a, gconstpointer b)
+{
+	const char *const*s1 = a;
+	const char *const*s2 = b;
+
+	return    (s1 == s2)
+	       || (   s1
+	           && s2
+	           && nm_streq0 (*s1, *s2));
+}