util/hash_table: replace fnv1a hash function with xxhash
[mesa.git] / src / util / hash_table.c
index 74d03aa60fb177d245f08ea1c490ec1110c91cd3..7b2b7eb0d6a890fa30869c78e0423db5a4b227d9 100644 (file)
 #include "hash_table.h"
 #include "ralloc.h"
 #include "macros.h"
-#include "main/hash.h"
+#include "u_memory.h"
 #include "fast_urem_by_const.h"
+#include "util/u_memory.h"
+
+#define XXH_INLINE_ALL
+#include "xxhash.h"
+
+/**
+ * Magic number that gets stored outside of the struct hash_table.
+ *
+ * The hash table needs a particular pointer to be the marker for a key that
+ * was deleted from the table, along with NULL for the "never allocated in the
+ * table" marker.  Legacy GL allows any GLuint to be used as a GL object name,
+ * and we use a 1:1 mapping from GLuints to key pointers, so we need to be
+ * able to track a GLuint that happens to match the deleted key outside of
+ * struct hash_table.  We tell the hash table to use "1" as the deleted key
+ * value, so that we test the deleted-key-in-the-table path as best we can.
+ */
+#define DELETED_KEY_VALUE 1
+
+static inline void *
+uint_key(unsigned id)
+{
+   return (void *)(uintptr_t) id;
+}
 
 static const uint32_t deleted_key_value;
 
@@ -98,6 +121,12 @@ static const struct {
    ENTRY(2147483648ul, 2362232233ul, 2362232231ul )
 };
 
+ASSERTED static inline bool
+key_pointer_is_reserved(const struct hash_table *ht, const void *key)
+{
+   return key == NULL || key == ht->deleted_key;
+}
+
 static int
 entry_is_free(const struct hash_entry *entry)
 {
@@ -250,6 +279,8 @@ _mesa_hash_table_set_deleted_key(struct hash_table *ht, const void *deleted_key)
 static struct hash_entry *
 hash_table_search(struct hash_table *ht, uint32_t hash, const void *key)
 {
+   assert(!key_pointer_is_reserved(ht, key));
+
    uint32_t size = ht->size;
    uint32_t start_hash_address = util_fast_urem32(hash, size, ht->size_magic);
    uint32_t double_hash = 1 + util_fast_urem32(hash, ht->rehash,
@@ -366,7 +397,7 @@ hash_table_insert(struct hash_table *ht, uint32_t hash,
 {
    struct hash_entry *available_entry = NULL;
 
-   assert(key != NULL);
+   assert(!key_pointer_is_reserved(ht, key));
 
    if (ht->entries >= ht->max_entries) {
       _mesa_hash_table_rehash(ht, ht->size_index + 1);
@@ -538,35 +569,69 @@ _mesa_hash_table_random_entry(struct hash_table *ht,
 }
 
 
-/**
- * Quick FNV-1a hash implementation based on:
- * http://www.isthe.com/chongo/tech/comp/fnv/
- *
- * FNV-1a is not be the best hash out there -- Jenkins's lookup3 is supposed
- * to be quite good, and it probably beats FNV.  But FNV has the advantage
- * that it involves almost no code.  For an improvement on both, see Paul
- * Hsieh's http://www.azillionmonkeys.com/qed/hash.html
- */
 uint32_t
 _mesa_hash_data(const void *data, size_t size)
 {
-   return _mesa_fnv32_1a_accumulate_block(_mesa_fnv32_1a_offset_bias,
-                                          data, size);
+   return XXH32(data, size, 0);
+}
+
+uint32_t
+_mesa_hash_int(const void *key)
+{
+   return XXH32(key, sizeof(int), 0);
+}
+
+uint32_t
+_mesa_hash_uint(const void *key)
+{
+   return XXH32(key, sizeof(unsigned), 0);
+}
+
+uint32_t
+_mesa_hash_u32(const void *key)
+{
+   return XXH32(key, 4, 0);
 }
 
 /** FNV-1a string hash implementation */
 uint32_t
 _mesa_hash_string(const void *_key)
 {
-   uint32_t hash = _mesa_fnv32_1a_offset_bias;
+   uint32_t hash = 0;
    const char *key = _key;
+   size_t len = strlen(key);
+#if defined(_WIN64) || defined(__x86_64__)
+   hash = (uint32_t)XXH64(key, len, hash);
+#else
+   hash = XXH32(key, len, hash);
+#endif
+   return hash;
+}
 
-   while (*key != 0) {
-      hash = _mesa_fnv32_1a_accumulate(hash, *key);
-      key++;
-   }
+uint32_t
+_mesa_hash_pointer(const void *pointer)
+{
+   uintptr_t num = (uintptr_t) pointer;
+   return (uint32_t) ((num >> 2) ^ (num >> 6) ^ (num >> 10) ^ (num >> 14));
+}
 
-   return hash;
+bool
+_mesa_key_int_equal(const void *a, const void *b)
+{
+   return *((const int *)a) == *((const int *)b);
+}
+
+bool
+_mesa_key_uint_equal(const void *a, const void *b)
+{
+
+   return *((const unsigned *)a) == *((const unsigned *)b);
+}
+
+bool
+_mesa_key_u32_equal(const void *a, const void *b)
+{
+   return *((const uint32_t *)a) == *((const uint32_t *)b);
 }
 
 /**
@@ -620,9 +685,12 @@ key_u64_equals(const void *a, const void *b)
    return aa->value == bb->value;
 }
 
+#define FREED_KEY_VALUE 0
+
 struct hash_table_u64 *
 _mesa_hash_table_u64_create(void *mem_ctx)
 {
+   STATIC_ASSERT(FREED_KEY_VALUE != DELETED_KEY_VALUE);
    struct hash_table_u64 *ht;
 
    ht = CALLOC_STRUCT(hash_table_u64);
@@ -644,8 +712,8 @@ _mesa_hash_table_u64_create(void *mem_ctx)
 }
 
 void
-_mesa_hash_table_u64_destroy(struct hash_table_u64 *ht,
-                             void (*delete_function)(struct hash_entry *entry))
+_mesa_hash_table_u64_clear(struct hash_table_u64 *ht,
+                           void (*delete_function)(struct hash_entry *entry))
 {
    if (!ht)
       return;
@@ -653,18 +721,54 @@ _mesa_hash_table_u64_destroy(struct hash_table_u64 *ht,
    if (ht->deleted_key_data) {
       if (delete_function) {
          struct hash_table *table = ht->table;
-         struct hash_entry deleted_entry;
+         struct hash_entry entry;
 
          /* Create a fake entry for the delete function. */
-         deleted_entry.hash = table->key_hash_function(table->deleted_key);
-         deleted_entry.key = table->deleted_key;
-         deleted_entry.data = ht->deleted_key_data;
+         if (sizeof(void *) == 8) {
+            entry.hash = table->key_hash_function(table->deleted_key);
+         } else {
+            struct hash_key_u64 _key = { .value = (uintptr_t)table->deleted_key };
+            entry.hash = table->key_hash_function(&_key);
+         }
+         entry.key = table->deleted_key;
+         entry.data = ht->deleted_key_data;
 
-         delete_function(&deleted_entry);
+         delete_function(&entry);
       }
       ht->deleted_key_data = NULL;
    }
 
+   if (ht->freed_key_data) {
+      if (delete_function) {
+         struct hash_table *table = ht->table;
+         struct hash_entry entry;
+
+         /* Create a fake entry for the delete function. */
+         if (sizeof(void *) == 8) {
+            entry.hash = table->key_hash_function(uint_key(FREED_KEY_VALUE));
+         } else {
+            struct hash_key_u64 _key = { .value = (uintptr_t)FREED_KEY_VALUE };
+            entry.hash = table->key_hash_function(&_key);
+         }
+         entry.key = uint_key(FREED_KEY_VALUE);
+         entry.data = ht->freed_key_data;
+
+         delete_function(&entry);
+      }
+      ht->freed_key_data = NULL;
+   }
+
+   _mesa_hash_table_clear(ht->table, delete_function);
+}
+
+void
+_mesa_hash_table_u64_destroy(struct hash_table_u64 *ht,
+                             void (*delete_function)(struct hash_entry *entry))
+{
+   if (!ht)
+      return;
+
+   _mesa_hash_table_u64_clear(ht, delete_function);
    _mesa_hash_table_destroy(ht->table, delete_function);
    free(ht);
 }
@@ -673,6 +777,11 @@ void
 _mesa_hash_table_u64_insert(struct hash_table_u64 *ht, uint64_t key,
                             void *data)
 {
+   if (key == FREED_KEY_VALUE) {
+      ht->freed_key_data = data;
+      return;
+   }
+
    if (key == DELETED_KEY_VALUE) {
       ht->deleted_key_data = data;
       return;
@@ -707,6 +816,9 @@ _mesa_hash_table_u64_search(struct hash_table_u64 *ht, uint64_t key)
 {
    struct hash_entry *entry;
 
+   if (key == FREED_KEY_VALUE)
+      return ht->freed_key_data;
+
    if (key == DELETED_KEY_VALUE)
       return ht->deleted_key_data;
 
@@ -722,6 +834,11 @@ _mesa_hash_table_u64_remove(struct hash_table_u64 *ht, uint64_t key)
 {
    struct hash_entry *entry;
 
+   if (key == FREED_KEY_VALUE) {
+      ht->freed_key_data = NULL;
+      return;
+   }
+
    if (key == DELETED_KEY_VALUE) {
       ht->deleted_key_data = NULL;
       return;