util/hash_table: Rework the API to know about hashing

Previously, the hash_table API required the user to do all of the hashing
of keys as it passed them in.  Since the hashing function is intrinsically
tied to the comparison function, it makes sense for the hash table to know
about it.  Also, it makes for a somewhat clumsy API as the user is
constantly calling hashing functions many of which have long names.  This
is especially bad when the standard call looks something like

_mesa_hash_table_insert(ht, _mesa_pointer_hash(key), key, data);

In the above case, there is no reason why the hash table shouldn't do the
hashing for you.  We leave the option for you to do your own hashing if
it's more efficient, but it's no longer needed.  Also, if you do do your
own hashing, the hash table will assert that your hash matches what it
expects out of the hashing function.  This should make it harder to mess up
your hashing.

v2: change to call the old entrypoint "pre_hashed" rather than
    "with_hash", like cworth's equivalent change upstream (change by
    anholt, acked-in-general by Jason).

Signed-off-by: Jason Ekstrand <jason.ekstrand@intel.com>
Signed-off-by: Eric Anholt <eric@anholt.net>
Reviewed-by: Eric Anholt <eric@anholt.net>
This commit is contained in:
Jason Ekstrand 2014-11-24 22:19:50 -08:00 committed by Eric Anholt
parent 0d7f4c8658
commit 94303a0750
16 changed files with 138 additions and 92 deletions

View File

@ -84,7 +84,7 @@ vc4_find_cse(struct vc4_compile *c, struct hash_table *ht,
uint32_t hash = _mesa_hash_data(&key, sizeof(key));
struct hash_entry *entry =
_mesa_hash_table_search(ht, hash, &key);
_mesa_hash_table_search_pre_hashed(ht, hash, &key);
if (entry) {
if (debug) {
@ -106,7 +106,7 @@ vc4_find_cse(struct vc4_compile *c, struct hash_table *ht,
if (!alloc_key)
return NULL;
memcpy(alloc_key, &key, sizeof(*alloc_key));
_mesa_hash_table_insert(ht, hash, alloc_key, inst);
_mesa_hash_table_insert_with_hash(ht, hash, alloc_key, inst);
if (debug) {
fprintf(stderr, "Added to CSE HT: ");
@ -125,7 +125,8 @@ qir_opt_cse(struct vc4_compile *c)
struct qinst *last_sf = NULL;
uint32_t sf_count = 0, r4_count = 0;
struct hash_table *ht = _mesa_hash_table_create(NULL, inst_key_equals);
struct hash_table *ht = _mesa_hash_table_create(NULL, NULL,
inst_key_equals);
if (!ht)
return false;

View File

@ -38,7 +38,8 @@
ir_variable_refcount_visitor::ir_variable_refcount_visitor()
{
this->mem_ctx = ralloc_context(NULL);
this->ht = _mesa_hash_table_create(NULL, _mesa_key_pointer_equal);
this->ht = _mesa_hash_table_create(NULL, _mesa_hash_pointer,
_mesa_key_pointer_equal);
}
static void
@ -70,15 +71,13 @@ ir_variable_refcount_visitor::get_variable_entry(ir_variable *var)
{
assert(var);
struct hash_entry *e = _mesa_hash_table_search(this->ht,
_mesa_hash_pointer(var),
var);
struct hash_entry *e = _mesa_hash_table_search(this->ht, var);
if (e)
return (ir_variable_refcount_entry *)e->data;
ir_variable_refcount_entry *entry = new ir_variable_refcount_entry(var);
assert(entry->referenced_count == 0);
_mesa_hash_table_insert(this->ht, _mesa_hash_pointer(var), var, entry);
_mesa_hash_table_insert(this->ht, var, entry);
return entry;
}

View File

@ -27,9 +27,8 @@
link_uniform_block_active *
process_block(void *mem_ctx, struct hash_table *ht, ir_variable *var)
{
const uint32_t h = _mesa_hash_string(var->get_interface_type()->name);
const hash_entry *const existing_block =
_mesa_hash_table_search(ht, h, var->get_interface_type()->name);
_mesa_hash_table_search(ht, var->get_interface_type()->name);
const glsl_type *const block_type = var->is_interface_instance()
? var->type : var->get_interface_type();
@ -54,8 +53,7 @@ process_block(void *mem_ctx, struct hash_table *ht, ir_variable *var)
b->binding = 0;
}
_mesa_hash_table_insert(ht, h, var->get_interface_type()->name,
(void *) b);
_mesa_hash_table_insert(ht, var->get_interface_type()->name, (void *) b);
return b;
} else {
link_uniform_block_active *const b =

View File

@ -182,7 +182,8 @@ link_uniform_blocks(void *mem_ctx,
* the hash is organized by block-name.
*/
struct hash_table *block_hash =
_mesa_hash_table_create(mem_ctx, _mesa_key_string_equal);
_mesa_hash_table_create(mem_ctx, _mesa_key_hash_string,
_mesa_key_string_equal);
if (block_hash == NULL) {
_mesa_error_no_memory(__func__);

View File

@ -96,6 +96,12 @@ uint_hash(GLuint id)
return id;
}
static uint32_t
uint_key_hash(const void *key)
{
return uint_hash((uintptr_t)key);
}
static void *
uint_key(GLuint id)
{
@ -114,7 +120,8 @@ _mesa_NewHashTable(void)
struct _mesa_HashTable *table = CALLOC_STRUCT(_mesa_HashTable);
if (table) {
table->ht = _mesa_hash_table_create(NULL, uint_key_compare);
table->ht = _mesa_hash_table_create(NULL, uint_key_hash,
uint_key_compare);
if (table->ht == NULL) {
free(table);
_mesa_error_no_memory(__func__);
@ -175,7 +182,7 @@ _mesa_HashLookup_unlocked(struct _mesa_HashTable *table, GLuint key)
if (key == DELETED_KEY_VALUE)
return table->deleted_key_data;
entry = _mesa_hash_table_search(table->ht, uint_hash(key), uint_key(key));
entry = _mesa_hash_table_search(table->ht, uint_key(key));
if (!entry)
return NULL;
@ -266,11 +273,11 @@ _mesa_HashInsert_unlocked(struct _mesa_HashTable *table, GLuint key, void *data)
if (key == DELETED_KEY_VALUE) {
table->deleted_key_data = data;
} else {
entry = _mesa_hash_table_search(table->ht, hash, uint_key(key));
entry = _mesa_hash_table_search_pre_hashed(table->ht, hash, uint_key(key));
if (entry) {
entry->data = data;
} else {
_mesa_hash_table_insert(table->ht, hash, uint_key(key), data);
_mesa_hash_table_insert_with_hash(table->ht, hash, uint_key(key), data);
}
}
}
@ -340,7 +347,7 @@ _mesa_HashRemove(struct _mesa_HashTable *table, GLuint key)
if (key == DELETED_KEY_VALUE) {
table->deleted_key_data = NULL;
} else {
entry = _mesa_hash_table_search(table->ht, uint_hash(key), uint_key(key));
entry = _mesa_hash_table_search(table->ht, uint_key(key));
_mesa_hash_table_remove(table->ht, entry);
}
mtx_unlock(&table->Mutex);

View File

@ -42,6 +42,7 @@
#include <stdlib.h>
#include <string.h>
#include <assert.h>
#include "hash_table.h"
#include "ralloc.h"
@ -110,6 +111,7 @@ entry_is_present(const struct hash_table *ht, struct hash_entry *entry)
struct hash_table *
_mesa_hash_table_create(void *mem_ctx,
uint32_t (*key_hash_function)(const void *key),
bool (*key_equals_function)(const void *a,
const void *b))
{
@ -123,6 +125,7 @@ _mesa_hash_table_create(void *mem_ctx,
ht->size = hash_sizes[ht->size_index].size;
ht->rehash = hash_sizes[ht->size_index].rehash;
ht->max_entries = hash_sizes[ht->size_index].max_entries;
ht->key_hash_function = key_hash_function;
ht->key_equals_function = key_equals_function;
ht->table = rzalloc_array(ht, struct hash_entry, ht->size);
ht->entries = 0;
@ -176,15 +179,8 @@ _mesa_hash_table_set_deleted_key(struct hash_table *ht, const void *deleted_key)
ht->deleted_key = deleted_key;
}
/**
* Finds a hash table entry with the given key and hash of that key.
*
* Returns NULL if no entry is found. Note that the data pointer may be
* modified by the user.
*/
struct hash_entry *
_mesa_hash_table_search(struct hash_table *ht, uint32_t hash,
const void *key)
static struct hash_entry *
hash_table_search(struct hash_table *ht, uint32_t hash, const void *key)
{
uint32_t start_hash_address = hash % ht->size;
uint32_t hash_address = start_hash_address;
@ -210,6 +206,31 @@ _mesa_hash_table_search(struct hash_table *ht, uint32_t hash,
return NULL;
}
/**
* Finds a hash table entry with the given key and hash of that key.
*
* Returns NULL if no entry is found. Note that the data pointer may be
* modified by the user.
*/
struct hash_entry *
_mesa_hash_table_search(struct hash_table *ht, const void *key)
{
assert(ht->key_hash_function);
return hash_table_search(ht, ht->key_hash_function(key), key);
}
struct hash_entry *
_mesa_hash_table_search_pre_hashed(struct hash_table *ht, uint32_t hash,
const void *key)
{
assert(ht->key_hash_function == NULL || hash == ht->key_hash_function(key));
return hash_table_search(ht, hash, key);
}
static struct hash_entry *
hash_table_insert(struct hash_table *ht, uint32_t hash,
const void *key, void *data);
static void
_mesa_hash_table_rehash(struct hash_table *ht, int new_size_index)
{
@ -235,22 +256,15 @@ _mesa_hash_table_rehash(struct hash_table *ht, int new_size_index)
ht->deleted_entries = 0;
hash_table_foreach(&old_ht, entry) {
_mesa_hash_table_insert(ht, entry->hash,
entry->key, entry->data);
hash_table_insert(ht, entry->hash, entry->key, entry->data);
}
ralloc_free(old_ht.table);
}
/**
* Inserts the key with the given hash into the table.
*
* Note that insertion may rearrange the table on a resize or rehash,
* so previously found hash_entries are no longer valid after this function.
*/
struct hash_entry *
_mesa_hash_table_insert(struct hash_table *ht, uint32_t hash,
const void *key, void *data)
static struct hash_entry *
hash_table_insert(struct hash_table *ht, uint32_t hash,
const void *key, void *data)
{
uint32_t start_hash_address, hash_address;
@ -306,6 +320,27 @@ _mesa_hash_table_insert(struct hash_table *ht, uint32_t hash,
return NULL;
}
/**
* Inserts the key with the given hash into the table.
*
* Note that insertion may rearrange the table on a resize or rehash,
* so previously found hash_entries are no longer valid after this function.
*/
struct hash_entry *
_mesa_hash_table_insert(struct hash_table *ht, const void *key, void *data)
{
assert(ht->key_hash_function);
hash_table_insert(ht, ht->key_hash_function(key), key, data);
}
struct hash_entry *
_mesa_hash_table_insert_with_hash(struct hash_table *ht, uint32_t hash,
const void *key, void *data)
{
assert(ht->key_hash_function == NULL || hash == ht->key_hash_function(key));
hash_table_insert(ht, hash, key, data);
}
/**
* This function deletes the given hash table entry.
*

View File

@ -46,6 +46,7 @@ struct hash_entry {
struct hash_table {
struct hash_entry *table;
uint32_t (*key_hash_function)(const void *key);
bool (*key_equals_function)(const void *a, const void *b);
const void *deleted_key;
uint32_t size;
@ -58,6 +59,7 @@ struct hash_table {
struct hash_table *
_mesa_hash_table_create(void *mem_ctx,
uint32_t (*key_hash_function)(const void *key),
bool (*key_equals_function)(const void *a,
const void *b));
void _mesa_hash_table_destroy(struct hash_table *ht,
@ -66,11 +68,15 @@ void _mesa_hash_table_set_deleted_key(struct hash_table *ht,
const void *deleted_key);
struct hash_entry *
_mesa_hash_table_insert(struct hash_table *ht, uint32_t hash,
const void *key, void *data);
_mesa_hash_table_insert(struct hash_table *ht, const void *key, void *data);
struct hash_entry *
_mesa_hash_table_search(struct hash_table *ht, uint32_t hash,
const void *key);
_mesa_hash_table_insert_with_hash(struct hash_table *ht, uint32_t hash,
const void *key, void *data);
struct hash_entry *
_mesa_hash_table_search(struct hash_table *ht, const void *key);
struct hash_entry *
_mesa_hash_table_search_pre_hashed(struct hash_table *ht, uint32_t hash,
const void *key);
void _mesa_hash_table_remove(struct hash_table *ht,
struct hash_entry *entry);
@ -85,6 +91,11 @@ uint32_t _mesa_hash_string(const char *key);
bool _mesa_key_string_equal(const void *a, const void *b);
bool _mesa_key_pointer_equal(const void *a, const void *b);
static inline uint32_t _mesa_key_hash_string(const void *key)
{
return _mesa_hash_string((const char *)key);
}
static inline uint32_t _mesa_hash_pointer(const void *pointer)
{
return _mesa_hash_data(&pointer, sizeof(pointer));

View File

@ -40,38 +40,38 @@ main(int argc, char **argv)
uint32_t bad_hash = 5;
int i;
ht = _mesa_hash_table_create(NULL, _mesa_key_string_equal);
ht = _mesa_hash_table_create(NULL, NULL, _mesa_key_string_equal);
_mesa_hash_table_insert(ht, bad_hash, str1, NULL);
_mesa_hash_table_insert(ht, bad_hash, str2, NULL);
_mesa_hash_table_insert_with_hash(ht, bad_hash, str1, NULL);
_mesa_hash_table_insert_with_hash(ht, bad_hash, str2, NULL);
entry1 = _mesa_hash_table_search(ht, bad_hash, str1);
entry1 = _mesa_hash_table_search_pre_hashed(ht, bad_hash, str1);
assert(entry1->key == str1);
entry2 = _mesa_hash_table_search(ht, bad_hash, str2);
entry2 = _mesa_hash_table_search_pre_hashed(ht, bad_hash, str2);
assert(entry2->key == str2);
/* Check that we can still find #1 after inserting #2 */
entry1 = _mesa_hash_table_search(ht, bad_hash, str1);
entry1 = _mesa_hash_table_search_pre_hashed(ht, bad_hash, str1);
assert(entry1->key == str1);
/* Remove the collided entry and look again. */
_mesa_hash_table_remove(ht, entry1);
entry2 = _mesa_hash_table_search(ht, bad_hash, str2);
entry2 = _mesa_hash_table_search_pre_hashed(ht, bad_hash, str2);
assert(entry2->key == str2);
/* Put str1 back, then spam junk into the table to force a
* resize and make sure we can still find them both.
*/
_mesa_hash_table_insert(ht, bad_hash, str1, NULL);
_mesa_hash_table_insert_with_hash(ht, bad_hash, str1, NULL);
for (i = 0; i < 100; i++) {
char *key = malloc(10);
sprintf(key, "spam%d", i);
_mesa_hash_table_insert(ht, _mesa_hash_string(key), key, NULL);
_mesa_hash_table_insert_with_hash(ht, _mesa_hash_string(key), key, NULL);
}
entry1 = _mesa_hash_table_search(ht, bad_hash, str1);
entry1 = _mesa_hash_table_search_pre_hashed(ht, bad_hash, str1);
assert(entry1->key == str1);
entry2 = _mesa_hash_table_search(ht, bad_hash, str2);
entry2 = _mesa_hash_table_search_pre_hashed(ht, bad_hash, str2);
assert(entry2->key == str2);
_mesa_hash_table_destroy(ht, NULL);

View File

@ -45,27 +45,25 @@ main(int argc, char **argv)
struct hash_table *ht;
const char *str1 = "test1";
const char *str2 = "test2";
uint32_t hash_str1 = badhash(str1);
uint32_t hash_str2 = badhash(str2);
struct hash_entry *entry;
ht = _mesa_hash_table_create(NULL, _mesa_key_string_equal);
ht = _mesa_hash_table_create(NULL, badhash, _mesa_key_string_equal);
_mesa_hash_table_insert(ht, hash_str1, str1, NULL);
_mesa_hash_table_insert(ht, hash_str2, str2, NULL);
_mesa_hash_table_insert(ht, str1, NULL);
_mesa_hash_table_insert(ht, str2, NULL);
entry = _mesa_hash_table_search(ht, hash_str2, str2);
entry = _mesa_hash_table_search(ht, str2);
assert(strcmp(entry->key, str2) == 0);
entry = _mesa_hash_table_search(ht, hash_str1, str1);
entry = _mesa_hash_table_search(ht, str1);
assert(strcmp(entry->key, str1) == 0);
_mesa_hash_table_remove(ht, entry);
entry = _mesa_hash_table_search(ht, hash_str1, str1);
entry = _mesa_hash_table_search(ht, str1);
assert(entry == NULL);
entry = _mesa_hash_table_search(ht, hash_str2, str2);
entry = _mesa_hash_table_search(ht, str2);
assert(strcmp(entry->key, str2) == 0);
_mesa_hash_table_destroy(ht, NULL);

View File

@ -51,24 +51,23 @@ main(int argc, char **argv)
uint32_t keys[size];
uint32_t i;
ht = _mesa_hash_table_create(NULL, uint32_t_key_equals);
ht = _mesa_hash_table_create(NULL, key_value, uint32_t_key_equals);
for (i = 0; i < size; i++) {
keys[i] = i;
_mesa_hash_table_insert(ht, i, keys + i, NULL);
_mesa_hash_table_insert(ht, keys + i, NULL);
if (i >= 100) {
uint32_t delete_value = i - 100;
entry = _mesa_hash_table_search(ht, delete_value,
&delete_value);
entry = _mesa_hash_table_search(ht, &delete_value);
_mesa_hash_table_remove(ht, entry);
}
}
/* Make sure that all our entries were present at the end. */
for (i = size - 100; i < size; i++) {
entry = _mesa_hash_table_search(ht, i, keys + i);
entry = _mesa_hash_table_search(ht, keys + i);
assert(entry);
assert(key_value(entry->key) == i);
}

View File

@ -50,13 +50,12 @@ int
main(int argc, char **argv)
{
struct hash_table *ht;
uint32_t hash_str1 = _mesa_hash_string(str1);
uint32_t hash_str2 = _mesa_hash_string(str2);
ht = _mesa_hash_table_create(NULL, _mesa_key_string_equal);
ht = _mesa_hash_table_create(NULL, _mesa_key_hash_string,
_mesa_key_string_equal);
_mesa_hash_table_insert(ht, hash_str1, str1, NULL);
_mesa_hash_table_insert(ht, hash_str2, str2, NULL);
_mesa_hash_table_insert(ht, str1, NULL);
_mesa_hash_table_insert(ht, str2, NULL);
_mesa_hash_table_destroy(ht, delete_callback);

View File

@ -36,19 +36,18 @@ main(int argc, char **argv)
struct hash_table *ht;
const char *str1 = "test1";
const char *str2 = "test2";
uint32_t hash_str1 = _mesa_hash_string(str1);
uint32_t hash_str2 = _mesa_hash_string(str2);
struct hash_entry *entry;
ht = _mesa_hash_table_create(NULL, _mesa_key_string_equal);
ht = _mesa_hash_table_create(NULL, _mesa_key_hash_string,
_mesa_key_string_equal);
_mesa_hash_table_insert(ht, hash_str1, str1, NULL);
_mesa_hash_table_insert(ht, hash_str2, str2, NULL);
_mesa_hash_table_insert(ht, str1, NULL);
_mesa_hash_table_insert(ht, str2, NULL);
entry = _mesa_hash_table_search(ht, hash_str1, str1);
entry = _mesa_hash_table_search(ht, str1);
assert(strcmp(entry->key, str1) == 0);
entry = _mesa_hash_table_search(ht, hash_str2, str2);
entry = _mesa_hash_table_search(ht, str2);
assert(strcmp(entry->key, str2) == 0);
_mesa_hash_table_destroy(ht, NULL);

View File

@ -51,16 +51,16 @@ main(int argc, char **argv)
uint32_t keys[size];
uint32_t i;
ht = _mesa_hash_table_create(NULL, uint32_t_key_equals);
ht = _mesa_hash_table_create(NULL, key_value, uint32_t_key_equals);
for (i = 0; i < size; i++) {
keys[i] = i;
_mesa_hash_table_insert(ht, i, keys + i, NULL);
_mesa_hash_table_insert(ht, keys + i, NULL);
}
for (i = 0; i < size; i++) {
entry = _mesa_hash_table_search(ht, i, keys + i);
entry = _mesa_hash_table_search(ht, keys + i);
assert(entry);
assert(key_value(entry->key) == i);
}

View File

@ -57,12 +57,12 @@ main(int argc, char **argv)
uint32_t keys[size];
uint32_t i, random_value;
ht = _mesa_hash_table_create(NULL, uint32_t_key_equals);
ht = _mesa_hash_table_create(NULL, key_value, uint32_t_key_equals);
for (i = 0; i < size; i++) {
keys[i] = i;
_mesa_hash_table_insert(ht, i, keys + i, NULL);
_mesa_hash_table_insert(ht, keys + i, NULL);
}
/* Test the no-predicate case. */

View File

@ -35,7 +35,7 @@ main(int argc, char **argv)
{
struct hash_table *ht;
ht = _mesa_hash_table_create(NULL, _mesa_key_string_equal);
ht = _mesa_hash_table_create(NULL, NULL, _mesa_key_string_equal);
_mesa_hash_table_remove(ht, NULL);

View File

@ -36,24 +36,23 @@ main(int argc, char **argv)
struct hash_table *ht;
char *str1 = strdup("test1");
char *str2 = strdup("test1");
uint32_t hash_str1 = _mesa_hash_string(str1);
uint32_t hash_str2 = _mesa_hash_string(str2);
struct hash_entry *entry;
assert(str1 != str2);
ht = _mesa_hash_table_create(NULL, _mesa_key_string_equal);
ht = _mesa_hash_table_create(NULL, _mesa_key_hash_string,
_mesa_key_string_equal);
_mesa_hash_table_insert(ht, hash_str1, str1, str1);
_mesa_hash_table_insert(ht, hash_str2, str2, str2);
_mesa_hash_table_insert(ht, str1, str1);
_mesa_hash_table_insert(ht, str2, str2);
entry = _mesa_hash_table_search(ht, hash_str1, str1);
entry = _mesa_hash_table_search(ht, str1);
assert(entry);
assert(entry->data == str2);
_mesa_hash_table_remove(ht, entry);
entry = _mesa_hash_table_search(ht, hash_str1, str1);
entry = _mesa_hash_table_search(ht, str1);
assert(!entry);
_mesa_hash_table_destroy(ht, NULL);