#include <stdlib.h>
#include <assert.h>
#include <string.h>
#include "hash_table.h"
#include "macros.h"
#include "ralloc.h"
#include "set.h"
#include "fast_urem_by_const.h"
static const uint32_t deleted_key_value;
static const void *deleted_key = &deleted_key_value;
static const struct {
uint32_t max_entries, size, rehash;
uint64_t size_magic, rehash_magic;
} hash_sizes[] = {
#define ENTRY(max_entries, size, rehash) \
{ max_entries, size, rehash, \
REMAINDER_MAGIC(size), REMAINDER_MAGIC(rehash) }
ENTRY(2, 5, 3 ),
ENTRY(4, 7, 5 ),
ENTRY(8, 13, 11 ),
ENTRY(16, 19, 17 ),
ENTRY(32, 43, 41 ),
ENTRY(64, 73, 71 ),
ENTRY(128, 151, 149 ),
ENTRY(256, 283, 281 ),
ENTRY(512, 571, 569 ),
ENTRY(1024, 1153, 1151 ),
ENTRY(2048, 2269, 2267 ),
ENTRY(4096, 4519, 4517 ),
ENTRY(8192, 9013, 9011 ),
ENTRY(16384, 18043, 18041 ),
ENTRY(32768, 36109, 36107 ),
ENTRY(65536, 72091, 72089 ),
ENTRY(131072, 144409, 144407 ),
ENTRY(262144, 288361, 288359 ),
ENTRY(524288, 576883, 576881 ),
ENTRY(1048576, 1153459, 1153457 ),
ENTRY(2097152, 2307163, 2307161 ),
ENTRY(4194304, 4613893, 4613891 ),
ENTRY(8388608, 9227641, 9227639 ),
ENTRY(16777216, 18455029, 18455027 ),
ENTRY(33554432, 36911011, 36911009 ),
ENTRY(67108864, 73819861, 73819859 ),
ENTRY(134217728, 147639589, 147639587 ),
ENTRY(268435456, 295279081, 295279079 ),
ENTRY(536870912, 590559793, 590559791 ),
ENTRY(1073741824, 1181116273, 1181116271 ),
ENTRY(2147483648ul, 2362232233ul, 2362232231ul )
};
ASSERTED static inline bool
key_pointer_is_reserved(const void *key)
{
return key == NULL || key == deleted_key;
}
static int
entry_is_free(struct set_entry *entry)
{
return entry->key == NULL;
}
static int
entry_is_deleted(struct set_entry *entry)
{
return entry->key == deleted_key;
}
static int
entry_is_present(struct set_entry *entry)
{
return entry->key != NULL && entry->key != deleted_key;
}
struct set *
_mesa_set_create(void *mem_ctx,
uint32_t (*key_hash_function)(const void *key),
bool (*key_equals_function)(const void *a,
const void *b))
{
struct set *ht;
ht = ralloc(mem_ctx, struct set);
if (ht == NULL)
return NULL;
ht->size_index = 0;
ht->size = hash_sizes[ht->size_index].size;
ht->rehash = hash_sizes[ht->size_index].rehash;
ht->size_magic = hash_sizes[ht->size_index].size_magic;
ht->rehash_magic = hash_sizes[ht->size_index].rehash_magic;
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 set_entry, ht->size);
ht->entries = 0;
ht->deleted_entries = 0;
if (ht->table == NULL) {
ralloc_free(ht);
return NULL;
}
return ht;
}
struct set *
_mesa_set_clone(struct set *set, void *dst_mem_ctx)
{
struct set *clone;
clone = ralloc(dst_mem_ctx, struct set);
if (clone == NULL)
return NULL;
memcpy(clone, set, sizeof(struct set));
clone->table = ralloc_array(clone, struct set_entry, clone->size);
if (clone->table == NULL) {
ralloc_free(clone);
return NULL;
}
memcpy(clone->table, set->table, clone->size * sizeof(struct set_entry));
return clone;
}
void
_mesa_set_destroy(struct set *ht, void (*delete_function)(struct set_entry *entry))
{
if (!ht)
return;
if (delete_function) {
set_foreach (ht, entry) {
delete_function(entry);
}
}
ralloc_free(ht->table);
ralloc_free(ht);
}
void
_mesa_set_clear(struct set *set, void (*delete_function)(struct set_entry *entry))
{
if (!set)
return;
set_foreach (set, entry) {
if (delete_function)
delete_function(entry);
entry->key = deleted_key;
}
set->entries = set->deleted_entries = 0;
}
static struct set_entry *
set_search(const struct set *ht, uint32_t hash, const void *key)
{
assert(!key_pointer_is_reserved(key));
uint32_t size = ht->size;
uint32_t start_address = util_fast_urem32(hash, size, ht->size_magic);
uint32_t double_hash = util_fast_urem32(hash, ht->rehash,
ht->rehash_magic) + 1;
uint32_t hash_address = start_address;
do {
struct set_entry *entry = ht->table + hash_address;
if (entry_is_free(entry)) {
return NULL;
} else if (entry_is_present(entry) && entry->hash == hash) {
if (ht->key_equals_function(key, entry->key)) {
return entry;
}
}
hash_address += double_hash;
if (hash_address >= size)
hash_address -= size;
} while (hash_address != start_address);
return NULL;
}
struct set_entry *
_mesa_set_search(const struct set *set, const void *key)
{
assert(set->key_hash_function);
return set_search(set, set->key_hash_function(key), key);
}
struct set_entry *
_mesa_set_search_pre_hashed(const struct set *set, uint32_t hash,
const void *key)
{
assert(set->key_hash_function == NULL ||
hash == set->key_hash_function(key));
return set_search(set, hash, key);
}
static void
set_add_rehash(struct set *ht, uint32_t hash, const void *key)
{
uint32_t size = ht->size;
uint32_t start_address = util_fast_urem32(hash, size, ht->size_magic);
uint32_t double_hash = util_fast_urem32(hash, ht->rehash,
ht->rehash_magic) + 1;
uint32_t hash_address = start_address;
do {
struct set_entry *entry = ht->table + hash_address;
if (likely(entry->key == NULL)) {
entry->hash = hash;
entry->key = key;
return;
}
hash_address = hash_address + double_hash;
if (hash_address >= size)
hash_address -= size;
} while (true);
}
static void
set_rehash(struct set *ht, unsigned new_size_index)
{
struct set old_ht;
struct set_entry *table;
if (new_size_index >= ARRAY_SIZE(hash_sizes))
return;
table = rzalloc_array(ht, struct set_entry,
hash_sizes[new_size_index].size);
if (table == NULL)
return;
old_ht = *ht;
ht->table = table;
ht->size_index = new_size_index;
ht->size = hash_sizes[ht->size_index].size;
ht->rehash = hash_sizes[ht->size_index].rehash;
ht->size_magic = hash_sizes[ht->size_index].size_magic;
ht->rehash_magic = hash_sizes[ht->size_index].rehash_magic;
ht->max_entries = hash_sizes[ht->size_index].max_entries;
ht->entries = 0;
ht->deleted_entries = 0;
set_foreach(&old_ht, entry) {
set_add_rehash(ht, entry->hash, entry->key);
}
ht->entries = old_ht.entries;
ralloc_free(old_ht.table);
}
void
_mesa_set_resize(struct set *set, uint32_t entries)
{
if (set->entries > entries)
entries = set->entries;
unsigned size_index = 0;
while (hash_sizes[size_index].max_entries < entries)
size_index++;
set_rehash(set, size_index);
}
static struct set_entry *
set_search_or_add(struct set *ht, uint32_t hash, const void *key, bool *found)
{
struct set_entry *available_entry = NULL;
assert(!key_pointer_is_reserved(key));
if (ht->entries >= ht->max_entries) {
set_rehash(ht, ht->size_index + 1);
} else if (ht->deleted_entries + ht->entries >= ht->max_entries) {
set_rehash(ht, ht->size_index);
}
uint32_t size = ht->size;
uint32_t start_address = util_fast_urem32(hash, size, ht->size_magic);
uint32_t double_hash = util_fast_urem32(hash, ht->rehash,
ht->rehash_magic) + 1;
uint32_t hash_address = start_address;
do {
struct set_entry *entry = ht->table + hash_address;
if (!entry_is_present(entry)) {
if (available_entry == NULL)
available_entry = entry;
if (entry_is_free(entry))
break;
}
if (!entry_is_deleted(entry) &&
entry->hash == hash &&
ht->key_equals_function(key, entry->key)) {
if (found)
*found = true;
return entry;
}
hash_address = hash_address + double_hash;
if (hash_address >= size)
hash_address -= size;
} while (hash_address != start_address);
if (available_entry) {
if (entry_is_deleted(available_entry))
ht->deleted_entries--;
available_entry->hash = hash;
available_entry->key = key;
ht->entries++;
if (found)
*found = false;
return available_entry;
}
return NULL;
}
static struct set_entry *
set_add(struct set *ht, uint32_t hash, const void *key)
{
struct set_entry *entry = set_search_or_add(ht, hash, key, NULL);
if (unlikely(!entry))
return NULL;
entry->key = key;
return entry;
}
struct set_entry *
_mesa_set_add(struct set *set, const void *key)
{
assert(set->key_hash_function);
return set_add(set, set->key_hash_function(key), key);
}
struct set_entry *
_mesa_set_add_pre_hashed(struct set *set, uint32_t hash, const void *key)
{
assert(set->key_hash_function == NULL ||
hash == set->key_hash_function(key));
return set_add(set, hash, key);
}
struct set_entry *
_mesa_set_search_and_add(struct set *set, const void *key, bool *replaced)
{
assert(set->key_hash_function);
return _mesa_set_search_and_add_pre_hashed(set,
set->key_hash_function(key),
key, replaced);
}
struct set_entry *
_mesa_set_search_and_add_pre_hashed(struct set *set, uint32_t hash,
const void *key, bool *replaced)
{
assert(set->key_hash_function == NULL ||
hash == set->key_hash_function(key));
struct set_entry *entry = set_search_or_add(set, hash, key, replaced);
if (unlikely(!entry))
return NULL;
entry->key = key;
return entry;
}
struct set_entry *
_mesa_set_search_or_add(struct set *set, const void *key)
{
assert(set->key_hash_function);
return set_search_or_add(set, set->key_hash_function(key), key, NULL);
}
struct set_entry *
_mesa_set_search_or_add_pre_hashed(struct set *set, uint32_t hash,
const void *key)
{
assert(set->key_hash_function == NULL ||
hash == set->key_hash_function(key));
return set_search_or_add(set, hash, key, NULL);
}
void
_mesa_set_remove(struct set *ht, struct set_entry *entry)
{
if (!entry)
return;
entry->key = deleted_key;
ht->entries--;
ht->deleted_entries++;
}
void
_mesa_set_remove_key(struct set *set, const void *key)
{
_mesa_set_remove(set, _mesa_set_search(set, key));
}
struct set_entry *
_mesa_set_next_entry(const struct set *ht, struct set_entry *entry)
{
if (entry == NULL)
entry = ht->table;
else
entry = entry + 1;
for (; entry != ht->table + ht->size; entry++) {
if (entry_is_present(entry)) {
return entry;
}
}
return NULL;
}
struct set_entry *
_mesa_set_random_entry(struct set *ht,
int (*predicate)(struct set_entry *entry))
{
struct set_entry *entry;
uint32_t i = rand() % ht->size;
if (ht->entries == 0)
return NULL;
for (entry = ht->table + i; entry != ht->table + ht->size; entry++) {
if (entry_is_present(entry) &&
(!predicate || predicate(entry))) {
return entry;
}
}
for (entry = ht->table; entry != ht->table + i; entry++) {
if (entry_is_present(entry) &&
(!predicate || predicate(entry))) {
return entry;
}
}
return NULL;
}
struct set *
_mesa_pointer_set_create(void *mem_ctx)
{
return _mesa_set_create(mem_ctx, _mesa_hash_pointer,
_mesa_key_pointer_equal);
}