2022-08-02 16:35:50 +00:00
|
|
|
static uint32_t
|
|
|
|
murmur_hash32(const char* key, size_t len, uint32_t seed);
|
|
|
|
|
|
|
|
static uint64_t
|
|
|
|
murmur_hash64(const char* key, size_t len, uint64_t seed);
|
|
|
|
|
2022-08-01 23:11:10 +00:00
|
|
|
|
|
|
|
hash_t
|
|
|
|
hash_create(size_t capacity, size_t element_size)
|
|
|
|
{
|
|
|
|
hash_t result;
|
|
|
|
|
|
|
|
result.count = 0;
|
|
|
|
result.capacity = capacity;
|
|
|
|
result.element_size = element_size;
|
2022-08-02 16:35:50 +00:00
|
|
|
result.keys = persistent_mem_alloc(&g_memory->engine, capacity, sizeof(*result.keys));
|
|
|
|
result.data = persistent_mem_alloc(&g_memory->engine, capacity, element_size);
|
2022-08-01 23:11:10 +00:00
|
|
|
|
|
|
|
for (size_t i = 0; i < capacity; i++) {
|
|
|
|
result.keys[i] = HASH_UNUSED;
|
|
|
|
}
|
|
|
|
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
2022-08-02 16:35:50 +00:00
|
|
|
void
|
|
|
|
hash_reset(hash_t* hash)
|
|
|
|
{
|
|
|
|
hash->count = 0;
|
|
|
|
|
|
|
|
for (int i = 0; i < hash->capacity; i++) {
|
|
|
|
hash->keys[i] = HASH_UNUSED;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-08-01 23:11:10 +00:00
|
|
|
void
|
|
|
|
hash_add(hash_t* hash, uint64_t key, void* element)
|
|
|
|
{
|
|
|
|
int key_index = key % hash->capacity;
|
|
|
|
|
|
|
|
LOG_ASSERT(hash->keys[key_index] == HASH_UNUSED, "Hash table collision");
|
|
|
|
LOG_ASSERT(hash->count+1 < hash->capacity, "Hash table data array too small");
|
|
|
|
|
|
|
|
uint64_t element_index = hash->count;
|
|
|
|
hash->keys[key_index] = element_index;
|
|
|
|
hash->count++;
|
|
|
|
|
|
|
|
memcpy(&hash->data[element_index*hash->element_size], element, hash->element_size);
|
|
|
|
}
|
|
|
|
|
|
|
|
void*
|
|
|
|
hash_get(hash_t hash, uint64_t key)
|
|
|
|
{
|
|
|
|
size_t key_index = key % hash.capacity;
|
|
|
|
LOG_ASSERT(hash.keys[key_index] != HASH_UNUSED, "Key not found in hash table");
|
|
|
|
|
|
|
|
uint64_t element_index = hash.keys[key_index];
|
|
|
|
void* element = &hash.data[hash.element_size*element_index];
|
|
|
|
|
|
|
|
return element;
|
|
|
|
}
|
|
|
|
|
2022-08-02 16:35:50 +00:00
|
|
|
static uint32_t
|
2022-08-01 23:11:10 +00:00
|
|
|
murmur_hash32(const char* key, size_t len, uint32_t seed)
|
|
|
|
{
|
|
|
|
const uint32_t C1 = 0xcc9e2d51;
|
|
|
|
const uint32_t C2 = 0x1b873593;
|
|
|
|
const uint32_t N = 0xe6546b64;
|
|
|
|
const int R1 = 15;
|
|
|
|
const int R2 = 13;
|
|
|
|
const int M = 5;
|
|
|
|
|
|
|
|
uint32_t hash = seed;
|
|
|
|
|
|
|
|
const char* chunk = key;
|
|
|
|
size_t chunk_size = sizeof(uint32_t);
|
|
|
|
int chunk_count = len / chunk_size;
|
|
|
|
for (int i = 0; i < chunk_count; i++)
|
|
|
|
{
|
|
|
|
uint32_t k = *(uint32_t*)chunk;
|
|
|
|
k = k * C1;
|
|
|
|
k = (k << R1) | (k >> (32-R1));
|
|
|
|
k = k * C2;
|
|
|
|
|
|
|
|
hash = hash ^ k;
|
|
|
|
hash = (hash << R2) | (hash >> (32-R2));
|
|
|
|
hash = (hash * M) + N;
|
|
|
|
|
|
|
|
chunk += chunk_size;
|
|
|
|
}
|
|
|
|
|
|
|
|
int remainder_count = len % chunk_size;
|
|
|
|
uint32_t k = 0;
|
|
|
|
switch (remainder_count)
|
|
|
|
{
|
|
|
|
case 3: k |= chunk[2] << 16;
|
|
|
|
case 2: k |= chunk[1] << 8;
|
|
|
|
case 1:
|
|
|
|
{
|
|
|
|
k |= chunk[0];
|
|
|
|
|
|
|
|
k = k * C1;
|
|
|
|
k = (k << R1) | (k >> (32-R1));
|
|
|
|
k = k * C2;
|
|
|
|
|
|
|
|
hash = hash ^ k;
|
|
|
|
}
|
|
|
|
default: break;
|
|
|
|
}
|
|
|
|
|
|
|
|
hash = hash ^ len;
|
|
|
|
hash = hash ^ (hash >> 16);
|
|
|
|
hash = hash * 0x85ebca6b;
|
|
|
|
hash = hash ^ (hash >> 13);
|
|
|
|
hash = hash * 0xc2b2ae35;
|
|
|
|
hash = hash ^ (hash >> 16);
|
|
|
|
|
|
|
|
return hash;
|
|
|
|
}
|
|
|
|
|
2022-08-02 16:35:50 +00:00
|
|
|
static uint64_t
|
2022-08-01 23:11:10 +00:00
|
|
|
murmur_hash64(const char* key, size_t len, uint64_t seed)
|
|
|
|
{
|
|
|
|
const uint64_t M = 0xc6a4a7935bd1e995;
|
|
|
|
const int R = 47;
|
|
|
|
|
|
|
|
uint64_t hash = seed ^ (len * M);
|
|
|
|
|
|
|
|
const char* chunk = key;
|
|
|
|
size_t chunk_size = sizeof(uint64_t);
|
|
|
|
int chunk_count = len / chunk_size;
|
|
|
|
for (int i = 0; i < chunk_count; i++)
|
|
|
|
{
|
|
|
|
uint64_t k = *(uint64_t*)chunk;
|
|
|
|
k = k * M;
|
|
|
|
k = k ^ (k >> R);
|
|
|
|
k = k * M;
|
|
|
|
|
|
|
|
hash = hash ^ k;
|
|
|
|
hash = hash * M;
|
|
|
|
|
|
|
|
chunk += chunk_size;
|
|
|
|
}
|
|
|
|
|
|
|
|
int remainder_count = len % chunk_size;
|
|
|
|
switch (remainder_count)
|
|
|
|
{
|
|
|
|
case 7: hash ^= (uint64_t)chunk[6] << 48;
|
|
|
|
case 6: hash ^= (uint64_t)chunk[5] << 40;
|
|
|
|
case 5: hash ^= (uint64_t)chunk[4] << 32;
|
|
|
|
case 4: hash ^= (uint64_t)chunk[3] << 24;
|
|
|
|
case 3: hash ^= (uint64_t)chunk[2] << 16;
|
|
|
|
case 2: hash ^= (uint64_t)chunk[1] << 8;
|
|
|
|
case 1:
|
|
|
|
{
|
|
|
|
hash ^= (uint64_t)chunk[0];
|
|
|
|
hash = hash * M;
|
|
|
|
}
|
|
|
|
default: break;
|
|
|
|
}
|
|
|
|
|
|
|
|
hash = hash ^ (hash >> R);
|
|
|
|
hash = hash * M;
|
|
|
|
hash = hash ^ (hash >> R);
|
|
|
|
|
|
|
|
return hash;
|
|
|
|
}
|
|
|
|
|