mirror of
https://gitlab.freedesktop.org/mesa/mesa.git
synced 2026-05-07 11:28:05 +02:00
util: allow any key for hash tables
We sometimes use this with non-pointer keys. This removes a footgun at the cost of a larger entry size on 32-bit. Signed-off-by: Rhys Perry <pendingchaos02@gmail.com> Acked-by: Marek Olšák <marek.olsak@amd.com> Acked-by: Emma Anholt <emma@anholt.net> Reviewed-by: Alyssa Rosenzweig <alyssa.rosenzweig@intel.com> Part-of: <https://gitlab.freedesktop.org/mesa/mesa/-/merge_requests/40318>
This commit is contained in:
parent
d348654a75
commit
213470b477
3 changed files with 33 additions and 86 deletions
|
|
@ -2143,7 +2143,6 @@ nir_unsigned_upper_bound(nir_shader *shader, struct hash_table *range_ht,
|
|||
|
||||
push_scalar_query(&state, scalar);
|
||||
|
||||
_mesa_hash_table_set_deleted_key(range_ht, (void *)(uintptr_t)UINT32_MAX);
|
||||
return perform_analysis(&state);
|
||||
}
|
||||
|
||||
|
|
@ -2547,6 +2546,5 @@ nir_def_num_lsb_zero(struct hash_table *numlsb_ht, nir_scalar def)
|
|||
|
||||
push_scalar_query(&state, def);
|
||||
|
||||
_mesa_hash_table_set_deleted_key(numlsb_ht, (void *)(uintptr_t)UINT32_MAX);
|
||||
return perform_analysis(&state);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -73,8 +73,6 @@ uint_key(unsigned id)
|
|||
return (void *)(uintptr_t) id;
|
||||
}
|
||||
|
||||
static const uint32_t deleted_key_value;
|
||||
|
||||
/**
|
||||
* From Knuth -- a good choice for hash/rehash values is p, p-2 where
|
||||
* p and p-2 are both prime. These tables are sized to have an extra 10%
|
||||
|
|
@ -136,30 +134,6 @@ 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)
|
||||
{
|
||||
return entry->key == NULL;
|
||||
}
|
||||
|
||||
static int
|
||||
entry_is_deleted(const struct hash_table *ht, struct hash_entry *entry)
|
||||
{
|
||||
return entry->key == ht->deleted_key;
|
||||
}
|
||||
|
||||
static int
|
||||
entry_is_present(const struct hash_table *ht, struct hash_entry *entry)
|
||||
{
|
||||
return entry->key != NULL && entry->key != ht->deleted_key;
|
||||
}
|
||||
|
||||
void
|
||||
_mesa_hash_table_init(struct hash_table *ht,
|
||||
void *mem_ctx,
|
||||
|
|
@ -182,7 +156,6 @@ _mesa_hash_table_init(struct hash_table *ht,
|
|||
memset(ht->table, 0, sizeof(ht->_initial_storage));
|
||||
ht->entries = 0;
|
||||
ht->deleted_entries = 0;
|
||||
ht->deleted_key = &deleted_key_value;
|
||||
}
|
||||
|
||||
static void
|
||||
|
|
@ -253,16 +226,13 @@ key_u32_equals(const void *a, const void *b)
|
|||
struct hash_table *
|
||||
_mesa_hash_table_create_u32_keys(void *mem_ctx)
|
||||
{
|
||||
struct hash_table *ht = _mesa_hash_table_create(mem_ctx, key_u32_hash, key_u32_equals);
|
||||
_mesa_hash_table_set_deleted_key(ht, (void *)(uintptr_t)UINT32_MAX);
|
||||
return ht;
|
||||
return _mesa_hash_table_create(mem_ctx, key_u32_hash, key_u32_equals);
|
||||
}
|
||||
|
||||
void
|
||||
_mesa_hash_table_init_u32_keys(struct hash_table *ht, void *mem_ctx)
|
||||
{
|
||||
_mesa_hash_table_init(ht, mem_ctx, key_u32_hash, key_u32_equals);
|
||||
_mesa_hash_table_set_deleted_key(ht, (void *)(uintptr_t)UINT32_MAX);
|
||||
}
|
||||
|
||||
/* Copy the hash table from src to dst. */
|
||||
|
|
@ -369,10 +339,11 @@ _mesa_hash_table_clear(struct hash_table *ht,
|
|||
|
||||
if (delete_function) {
|
||||
for (entry = ht->table; entry != ht->table + ht->size; entry++) {
|
||||
if (entry_is_present(ht, entry))
|
||||
if (entry->present)
|
||||
delete_function(entry);
|
||||
|
||||
entry->key = NULL;
|
||||
entry->present = false;
|
||||
entry->deleted = false;
|
||||
}
|
||||
ht->entries = 0;
|
||||
ht->deleted_entries = 0;
|
||||
|
|
@ -380,27 +351,9 @@ _mesa_hash_table_clear(struct hash_table *ht,
|
|||
hash_table_clear_fast(ht);
|
||||
}
|
||||
|
||||
/** Sets the value of the key pointer used for deleted entries in the table.
|
||||
*
|
||||
* The assumption is that usually keys are actual pointers, so we use a
|
||||
* default value of a pointer to an arbitrary piece of storage in the library.
|
||||
* But in some cases a consumer wants to store some other sort of value in the
|
||||
* table, like a uint32_t, in which case that pointer may conflict with one of
|
||||
* their valid keys. This lets that user select a safe value.
|
||||
*
|
||||
* This must be called before any keys are actually deleted from the table.
|
||||
*/
|
||||
void
|
||||
_mesa_hash_table_set_deleted_key(struct hash_table *ht, const void *deleted_key)
|
||||
{
|
||||
ht->deleted_key = deleted_key;
|
||||
}
|
||||
|
||||
static struct hash_entry *
|
||||
hash_table_search(const 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,
|
||||
|
|
@ -410,12 +363,11 @@ hash_table_search(const struct hash_table *ht, uint32_t hash, const void *key)
|
|||
do {
|
||||
struct hash_entry *entry = ht->table + hash_address;
|
||||
|
||||
if (entry_is_free(entry)) {
|
||||
if (!entry->present && !entry->deleted) {
|
||||
return NULL;
|
||||
} else if (entry_is_present(ht, entry) && entry->hash == hash) {
|
||||
if (ht->key_equals_function(key, entry->key)) {
|
||||
return entry;
|
||||
}
|
||||
} else if (entry->present && entry->hash == hash &&
|
||||
ht->key_equals_function(key, entry->key)) {
|
||||
return entry;
|
||||
}
|
||||
|
||||
hash_address += double_hash;
|
||||
|
|
@ -463,10 +415,11 @@ hash_table_insert_rehash(struct hash_table *ht, uint32_t hash,
|
|||
do {
|
||||
struct hash_entry *entry = ht->table + hash_address;
|
||||
|
||||
if (likely(entry->key == NULL)) {
|
||||
if (likely(!entry->present)) {
|
||||
entry->hash = hash;
|
||||
entry->key = key;
|
||||
entry->data = data;
|
||||
entry->present = true;
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -536,8 +489,6 @@ hash_table_get_entry(struct hash_table *ht, uint32_t hash, const void *key)
|
|||
{
|
||||
struct hash_entry *available_entry = NULL;
|
||||
|
||||
assert(!key_pointer_is_reserved(ht, key));
|
||||
|
||||
if (ht->entries >= ht->max_entries) {
|
||||
_mesa_hash_table_rehash(ht, ht->size_index + 1);
|
||||
} else if (ht->deleted_entries + ht->entries >= ht->max_entries) {
|
||||
|
|
@ -552,11 +503,11 @@ hash_table_get_entry(struct hash_table *ht, uint32_t hash, const void *key)
|
|||
do {
|
||||
struct hash_entry *entry = ht->table + hash_address;
|
||||
|
||||
if (!entry_is_present(ht, entry)) {
|
||||
if (!entry->present) {
|
||||
/* Stash the first available entry we find */
|
||||
if (available_entry == NULL)
|
||||
available_entry = entry;
|
||||
if (entry_is_free(entry))
|
||||
if (!entry->deleted)
|
||||
break;
|
||||
}
|
||||
|
||||
|
|
@ -571,8 +522,7 @@ hash_table_get_entry(struct hash_table *ht, uint32_t hash, const void *key)
|
|||
* required to avoid memory leaks, perform a search
|
||||
* before inserting.
|
||||
*/
|
||||
if (!entry_is_deleted(ht, entry) &&
|
||||
entry->hash == hash &&
|
||||
if (entry->present && entry->hash == hash &&
|
||||
ht->key_equals_function(key, entry->key))
|
||||
return entry;
|
||||
|
||||
|
|
@ -582,7 +532,7 @@ hash_table_get_entry(struct hash_table *ht, uint32_t hash, const void *key)
|
|||
} while (hash_address != start_hash_address);
|
||||
|
||||
if (available_entry) {
|
||||
if (entry_is_deleted(ht, available_entry))
|
||||
if (available_entry->deleted)
|
||||
ht->deleted_entries--;
|
||||
available_entry->hash = hash;
|
||||
ht->entries++;
|
||||
|
|
@ -604,6 +554,8 @@ hash_table_insert(struct hash_table *ht, uint32_t hash,
|
|||
if (entry) {
|
||||
entry->key = key;
|
||||
entry->data = data;
|
||||
entry->present = true;
|
||||
entry->deleted = false;
|
||||
}
|
||||
|
||||
return entry;
|
||||
|
|
@ -643,7 +595,8 @@ _mesa_hash_table_remove(struct hash_table *ht,
|
|||
if (!entry)
|
||||
return;
|
||||
|
||||
entry->key = ht->deleted_key;
|
||||
entry->present = false;
|
||||
entry->deleted = true;
|
||||
ht->entries--;
|
||||
ht->deleted_entries++;
|
||||
}
|
||||
|
|
@ -673,7 +626,7 @@ _mesa_hash_table_next_entry_unsafe(const struct hash_table *ht, struct hash_entr
|
|||
else
|
||||
entry = entry + 1;
|
||||
if (entry != ht->table + ht->size)
|
||||
return entry->key ? entry : _mesa_hash_table_next_entry_unsafe(ht, entry);
|
||||
return entry->present ? entry : _mesa_hash_table_next_entry_unsafe(ht, entry);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
|
@ -694,9 +647,8 @@ _mesa_hash_table_next_entry(struct hash_table *ht,
|
|||
entry = entry + 1;
|
||||
|
||||
for (; entry != ht->table + ht->size; entry++) {
|
||||
if (entry_is_present(ht, entry)) {
|
||||
if (entry->present)
|
||||
return entry;
|
||||
}
|
||||
}
|
||||
|
||||
return NULL;
|
||||
|
|
@ -721,15 +673,13 @@ _mesa_hash_table_random_entry(struct hash_table *ht,
|
|||
return NULL;
|
||||
|
||||
for (entry = ht->table + i; entry != ht->table + ht->size; entry++) {
|
||||
if (entry_is_present(ht, entry) &&
|
||||
(!predicate || predicate(entry))) {
|
||||
if (entry->present && (!predicate || predicate(entry))) {
|
||||
return entry;
|
||||
}
|
||||
}
|
||||
|
||||
for (entry = ht->table; entry != ht->table + i; entry++) {
|
||||
if (entry_is_present(ht, entry) &&
|
||||
(!predicate || predicate(entry))) {
|
||||
if (entry->present && (!predicate || predicate(entry))) {
|
||||
return entry;
|
||||
}
|
||||
}
|
||||
|
|
@ -943,8 +893,6 @@ _mesa_hash_table_u64_create(void *mem_ctx)
|
|||
_mesa_hash_table_set_destructor(&ht->table, _mesa_hash_table_u64_delete_keys);
|
||||
}
|
||||
|
||||
_mesa_hash_table_set_deleted_key(&ht->table, uint_key(DELETED_KEY_VALUE));
|
||||
|
||||
return ht;
|
||||
}
|
||||
|
||||
|
|
@ -1008,10 +956,13 @@ _mesa_hash_table_u64_insert(struct hash_table_u64 *ht, uint64_t key,
|
|||
}
|
||||
|
||||
entry->data = data;
|
||||
if (!entry_is_present(&ht->table, entry))
|
||||
if (!entry->present) {
|
||||
entry->key = _key;
|
||||
else
|
||||
entry->present = true;
|
||||
entry->deleted = false;
|
||||
} else {
|
||||
FREE(_key);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -39,6 +39,8 @@ extern "C" {
|
|||
|
||||
struct hash_entry {
|
||||
uint32_t hash;
|
||||
bool present;
|
||||
bool deleted;
|
||||
const void *key;
|
||||
void *data;
|
||||
};
|
||||
|
|
@ -49,7 +51,6 @@ struct hash_table {
|
|||
uint32_t (*key_hash_function)(const void *key);
|
||||
bool (*key_equals_function)(const void *a, const void *b);
|
||||
void (*table_destructor)(void *data);
|
||||
const void *deleted_key;
|
||||
uint32_t size;
|
||||
uint32_t rehash;
|
||||
uint64_t size_magic;
|
||||
|
|
@ -86,7 +87,6 @@ void
|
|||
_mesa_hash_table_fini(struct hash_table *ht,
|
||||
void (*delete_function)(struct hash_entry *entry));
|
||||
|
||||
/* key == 0 and key == UINT32_MAX are not allowed */
|
||||
/* It's preferred to use _mesa_hash_table_init_u32_keys instead of this to skip ralloc. */
|
||||
struct hash_table *
|
||||
_mesa_hash_table_create_u32_keys(void *mem_ctx);
|
||||
|
|
@ -103,8 +103,6 @@ void _mesa_hash_table_destroy(struct hash_table *ht,
|
|||
void (*delete_function)(struct hash_entry *entry));
|
||||
void _mesa_hash_table_clear(struct hash_table *ht,
|
||||
void (*delete_function)(struct hash_entry *entry));
|
||||
void _mesa_hash_table_set_deleted_key(struct hash_table *ht,
|
||||
const void *deleted_key);
|
||||
|
||||
static inline uint32_t _mesa_hash_table_num_entries(const struct hash_table *ht)
|
||||
{
|
||||
|
|
@ -179,10 +177,10 @@ _mesa_hash_table_reserve(struct hash_table *ht, unsigned size);
|
|||
* This foreach function destroys the table as it iterates.
|
||||
* It is not safe to use when inserting or removing entries.
|
||||
*/
|
||||
#define hash_table_foreach_remove(ht, entry) \
|
||||
for (struct hash_entry *entry = _mesa_hash_table_next_entry_unsafe(ht, NULL); \
|
||||
(ht)->entries; \
|
||||
entry->hash = 0, entry->key = (void*)NULL, entry->data = NULL, \
|
||||
#define hash_table_foreach_remove(ht, entry) \
|
||||
for (struct hash_entry *entry = _mesa_hash_table_next_entry_unsafe(ht, NULL); \
|
||||
(ht)->entries; \
|
||||
entry->hash = 0, entry->present = false, entry->deleted = false, entry->data = NULL, \
|
||||
(ht)->entries--, entry = _mesa_hash_table_next_entry_unsafe(ht, entry))
|
||||
|
||||
static inline void
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue