You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
363 lines
12 KiB
363 lines
12 KiB
#include "hashmap.h"
|
|
#include "variant.h"
|
|
#include "log.h"
|
|
#include "string_utils.h"
|
|
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <assert.h>
|
|
|
|
struct Hashmap_Entry
|
|
{
|
|
char* key;
|
|
struct Variant value;
|
|
};
|
|
|
|
struct Hashmap
|
|
{
|
|
struct Hashmap_Entry* buckets[HASH_MAP_NUM_BUCKETS];
|
|
int iter_bucket, iter_index;
|
|
};
|
|
|
|
static unsigned int hashmap_generate_hash(const char* key);
|
|
static struct Hashmap_Entry* hashmap_entry_new(struct Hashmap* hashmap, const char* key);
|
|
|
|
static struct Hashmap_Entry* hashmap_entry_new(struct Hashmap* hashmap, const char* key)
|
|
{
|
|
unsigned int index = hashmap_generate_hash(key);
|
|
struct Hashmap_Entry* new_entry = NULL;
|
|
for(int i = 0; i < array_len(hashmap->buckets[index]); i++) /* Look for duplicates and over-write if found */
|
|
{
|
|
if(strncmp(key, hashmap->buckets[index][i].key, HASH_MAX_KEY_LEN) == 0)
|
|
{
|
|
new_entry = &hashmap->buckets[index][i];
|
|
if(new_entry->key) free(new_entry->key);
|
|
break;
|
|
}
|
|
}
|
|
if(!new_entry) new_entry = array_grow(hashmap->buckets[index], struct Hashmap_Entry);
|
|
new_entry->key = str_new(key);
|
|
new_entry->value.type = VT_NONE;
|
|
variant_free(&new_entry->value);
|
|
return new_entry;
|
|
}
|
|
|
|
unsigned int hashmap_generate_hash(const char* key)
|
|
{
|
|
unsigned int index = 0;
|
|
const int multiplier = 51;
|
|
for(int i = 0; i < (int)strlen(key); i++)
|
|
index = index * multiplier + key[i];
|
|
return index % HASH_MAP_NUM_BUCKETS;
|
|
}
|
|
|
|
struct Hashmap* hashmap_create(void)
|
|
{
|
|
struct Hashmap* hashmap = malloc(sizeof(*hashmap));
|
|
if(!hashmap)
|
|
return NULL;
|
|
for(int i = 0; i < HASH_MAP_NUM_BUCKETS; i++)
|
|
hashmap->buckets[i] = array_new(struct Hashmap_Entry);
|
|
hashmap->iter_bucket = 0;
|
|
hashmap->iter_index = -1;
|
|
return hashmap;
|
|
}
|
|
|
|
void hashmap_free(struct Hashmap* hashmap)
|
|
{
|
|
if(!hashmap) return;
|
|
for(int i = 0; i < HASH_MAP_NUM_BUCKETS; i++)
|
|
{
|
|
for(int j = 0; j < array_len(hashmap->buckets[i]); j++)
|
|
{
|
|
struct Hashmap_Entry* entry = &hashmap->buckets[i][j];
|
|
if(entry->key)
|
|
{
|
|
free(entry->key);
|
|
entry->key = NULL;
|
|
}
|
|
variant_free(&entry->value);
|
|
}
|
|
array_free(hashmap->buckets[i]);
|
|
hashmap->buckets[i] = NULL;
|
|
}
|
|
free(hashmap);
|
|
hashmap = NULL;
|
|
}
|
|
|
|
void hashmap_value_set(struct Hashmap* hashmap, const char* key, const struct Variant* value)
|
|
{
|
|
if(!hashmap || !key || !value) return;
|
|
struct Hashmap_Entry* new_entry = hashmap_entry_new(hashmap, key);
|
|
variant_copy(&new_entry->value, value);
|
|
}
|
|
|
|
struct Variant* hashmap_value_get(const struct Hashmap* hashmap, const char* key)
|
|
{
|
|
if(!hashmap || !key) return NULL;
|
|
struct Variant* value = NULL;
|
|
unsigned int index = hashmap_generate_hash(key);
|
|
int key_len = (int)strlen(key);
|
|
int compare_len = key_len < HASH_MAX_KEY_LEN ? key_len : HASH_MAX_KEY_LEN;
|
|
for(int i = 0; i < array_len(hashmap->buckets[index]); i++)
|
|
{
|
|
// Check for the length of the key to avoid partial matches. We might be looking for
|
|
// "Diffuse" and we'll get matched to "Diffuse_Color" if we're relying on the length
|
|
// of "Diffuse" only
|
|
if(strnlen(hashmap->buckets[index][i].key, HASH_MAX_KEY_LEN) == compare_len && strncmp(key, hashmap->buckets[index][i].key, compare_len) == 0)
|
|
{
|
|
value = &hashmap->buckets[index][i].value;
|
|
break;
|
|
}
|
|
}
|
|
return value;
|
|
}
|
|
|
|
void hashmap_value_remove(struct Hashmap* hashmap, const char* key)
|
|
{
|
|
if(!hashmap || !key) return;
|
|
unsigned int index = hashmap_generate_hash(key);
|
|
int index_to_remove = -1;
|
|
for(int i = 0; i < array_len(hashmap->buckets[index]); i++)
|
|
{
|
|
if(strncmp(key, hashmap->buckets[index][i].key, HASH_MAX_KEY_LEN) == 0)
|
|
{
|
|
index_to_remove = i;
|
|
break;
|
|
}
|
|
}
|
|
if(index_to_remove != -1) array_remove_at(hashmap->buckets[index], index_to_remove);
|
|
}
|
|
|
|
bool hashmap_value_exists(struct Hashmap * hashmap, const char * key)
|
|
{
|
|
return hashmap_value_get(hashmap, key);
|
|
}
|
|
|
|
|
|
void hashmap_float_set(struct Hashmap* hashmap, const char* key, const float value)
|
|
{
|
|
struct Hashmap_Entry* new_entry = hashmap_entry_new(hashmap, key);
|
|
variant_assign_float(&new_entry->value, value);
|
|
}
|
|
|
|
void hashmap_int_set(struct Hashmap* hashmap, const char* key, const int value)
|
|
{
|
|
struct Hashmap_Entry* new_entry = hashmap_entry_new(hashmap, key);
|
|
variant_assign_int(&new_entry->value, value);
|
|
}
|
|
|
|
void hashmap_double_set(struct Hashmap* hashmap, const char* key, const double value)
|
|
{
|
|
struct Hashmap_Entry* new_entry = hashmap_entry_new(hashmap, key);
|
|
variant_assign_double(&new_entry->value, value);
|
|
}
|
|
|
|
void hashmap_bool_set(struct Hashmap* hashmap, const char* key, const bool value)
|
|
{
|
|
struct Hashmap_Entry* new_entry = hashmap_entry_new(hashmap, key);
|
|
variant_assign_bool(&new_entry->value, value);
|
|
}
|
|
|
|
void hashmap_vec2_set(struct Hashmap* hashmap, const char* key, const vec2* value)
|
|
{
|
|
struct Hashmap_Entry* new_entry = hashmap_entry_new(hashmap, key);
|
|
variant_assign_vec2(&new_entry->value, value);
|
|
}
|
|
|
|
void hashmap_vec3_set(struct Hashmap* hashmap, const char* key, const vec3* value)
|
|
{
|
|
struct Hashmap_Entry* new_entry = hashmap_entry_new(hashmap, key);
|
|
variant_assign_vec3(&new_entry->value, value);
|
|
}
|
|
|
|
void hashmap_vec4_set(struct Hashmap* hashmap, const char* key, const vec4* value)
|
|
{
|
|
struct Hashmap_Entry* new_entry = hashmap_entry_new(hashmap, key);
|
|
variant_assign_vec4(&new_entry->value, value);
|
|
}
|
|
|
|
void hashmap_quat_set(struct Hashmap* hashmap, const char* key, const quat* value)
|
|
{
|
|
struct Hashmap_Entry* new_entry = hashmap_entry_new(hashmap, key);
|
|
variant_assign_quat(&new_entry->value, value);
|
|
}
|
|
|
|
void hashmap_vec2_setf(struct Hashmap* hashmap, const char* key, const float x, const float y)
|
|
{
|
|
struct Hashmap_Entry* new_entry = hashmap_entry_new(hashmap, key);
|
|
variant_assign_vec2f(&new_entry->value, x, y);
|
|
}
|
|
|
|
void hashmap_vec3_setf(struct Hashmap* hashmap, const char* key, const float x, const float y, const float z)
|
|
{
|
|
struct Hashmap_Entry* new_entry = hashmap_entry_new(hashmap, key);
|
|
variant_assign_vec3f(&new_entry->value, x, y, z);
|
|
}
|
|
|
|
void hashmap_vec4_setf(struct Hashmap* hashmap, const char* key, const float x, const float y, const float z, const float w)
|
|
{
|
|
struct Hashmap_Entry* new_entry = hashmap_entry_new(hashmap, key);
|
|
variant_assign_vec4f(&new_entry->value, x, y, z, w);
|
|
}
|
|
|
|
void hashmap_quat_setf(struct Hashmap* hashmap, const char* key, const float x, const float y, const float z, const float w)
|
|
{
|
|
struct Hashmap_Entry* new_entry = hashmap_entry_new(hashmap, key);
|
|
variant_assign_quatf(&new_entry->value, x, y, z, w);
|
|
}
|
|
|
|
void hashmap_mat4_set(struct Hashmap* hashmap, const char* key, const mat4* value)
|
|
{
|
|
struct Hashmap_Entry* new_entry = hashmap_entry_new(hashmap, key);
|
|
variant_assign_mat4(&new_entry->value, value);
|
|
}
|
|
|
|
void hashmap_str_set(struct Hashmap* hashmap, const char* key, const char* value)
|
|
{
|
|
struct Hashmap_Entry* new_entry = hashmap_entry_new(hashmap, key);
|
|
variant_assign_str(&new_entry->value, value);
|
|
}
|
|
|
|
void hashmap_ptr_set(struct Hashmap* hashmap, const char* key, void* value)
|
|
{
|
|
struct Hashmap_Entry* new_entry = hashmap_entry_new(hashmap, key);
|
|
variant_assign_ptr(&new_entry->value, value);
|
|
}
|
|
|
|
float hashmap_float_get(const struct Hashmap* hashmap, const char* key)
|
|
{
|
|
struct Variant* variant = hashmap_value_get(hashmap, key);
|
|
if(variant->type == VT_STR) variant_from_str(variant, variant->val_str, VT_FLOAT);
|
|
return variant->val_float;
|
|
}
|
|
|
|
int hashmap_int_get(const struct Hashmap* hashmap, const char* key)
|
|
{
|
|
struct Variant* variant = hashmap_value_get(hashmap, key);
|
|
if(variant->type == VT_STR) variant_from_str(variant, variant->val_str, VT_INT);
|
|
return variant->val_int;
|
|
}
|
|
|
|
double hashmap_double_get(const struct Hashmap* hashmap, const char* key)
|
|
{
|
|
struct Variant* variant = hashmap_value_get(hashmap, key);
|
|
if(variant->type == VT_STR) variant_from_str(variant, variant->val_str, VT_DOUBLE);
|
|
return variant->val_double;
|
|
}
|
|
|
|
bool hashmap_bool_get(const struct Hashmap* hashmap, const char* key)
|
|
{
|
|
struct Variant* variant = hashmap_value_get(hashmap, key);
|
|
if(variant->type == VT_STR) variant_from_str(variant, variant->val_str, VT_BOOL);
|
|
return variant->val_bool;
|
|
}
|
|
|
|
vec2 hashmap_vec2_get(const struct Hashmap* hashmap, const char* key)
|
|
{
|
|
struct Variant* variant = hashmap_value_get(hashmap, key);
|
|
if(variant->type == VT_STR) variant_from_str(variant, variant->val_str, VT_VEC2);
|
|
return variant->val_vec2;
|
|
}
|
|
|
|
vec3 hashmap_vec3_get(const struct Hashmap* hashmap, const char* key)
|
|
{
|
|
struct Variant* variant = hashmap_value_get(hashmap, key);
|
|
if(variant->type == VT_STR) variant_from_str(variant, variant->val_str, VT_VEC3);
|
|
return variant->val_vec3;
|
|
}
|
|
|
|
vec4 hashmap_vec4_get(const struct Hashmap* hashmap, const char* key)
|
|
{
|
|
struct Variant* variant = hashmap_value_get(hashmap, key);
|
|
if(variant->type == VT_STR) variant_from_str(variant, variant->val_str, VT_VEC4);
|
|
return variant->val_vec4;
|
|
}
|
|
|
|
quat hashmap_quat_get(const struct Hashmap* hashmap, const char* key)
|
|
{
|
|
struct Variant* variant = hashmap_value_get(hashmap, key);
|
|
if(variant->type == VT_STR) variant_from_str(variant, variant->val_str, VT_QUAT);
|
|
return variant->val_quat;
|
|
}
|
|
|
|
const mat4* hashmap_mat4_get(const struct Hashmap* hashmap, const char* key)
|
|
{
|
|
struct Variant* variant = hashmap_value_get(hashmap, key);
|
|
if(variant->type == VT_STR) variant_from_str(variant, variant->val_str, VT_MAT4);
|
|
return variant->val_mat4;
|
|
}
|
|
|
|
const char* hashmap_str_get(const struct Hashmap* hashmap, const char* key)
|
|
{
|
|
const struct Variant* variant = hashmap_value_get(hashmap, key);
|
|
return variant->val_str;
|
|
}
|
|
|
|
void* hashmap_ptr_get(const struct Hashmap* hashmap, const char* key)
|
|
{
|
|
const struct Variant* variant = hashmap_value_get(hashmap, key);
|
|
return variant->val_voidptr;
|
|
}
|
|
|
|
void hashmap_debug_print(const struct Hashmap* hashmap)
|
|
{
|
|
if(!hashmap) return;
|
|
static char str[128];
|
|
memset(str, '\0', 128);
|
|
for(int i = 0; i < HASH_MAP_NUM_BUCKETS; i++)
|
|
{
|
|
log_message("Bucket : %d", i);
|
|
log_message("Bucket len : %d", array_len(hashmap->buckets[i]));
|
|
for(int j = 0; j < array_len(hashmap->buckets[i]); j++)
|
|
{
|
|
struct Hashmap_Entry* entry = &hashmap->buckets[i][j];
|
|
const struct Variant* value = &entry->value;
|
|
const char* key = entry->key;
|
|
log_message("Key : %s", key);
|
|
variant_to_str(value, str, 128);
|
|
log_message("Value : %s", str);
|
|
memset(str, '\0', 128);
|
|
}
|
|
}
|
|
}
|
|
|
|
void hashmap_iter_begin(struct Hashmap* hashmap)
|
|
{
|
|
assert(hashmap);
|
|
hashmap->iter_bucket = 0;
|
|
hashmap->iter_index = -1;
|
|
}
|
|
|
|
int hashmap_iter_next(struct Hashmap* hashmap, char** key, struct Variant** value)
|
|
{
|
|
assert(hashmap);
|
|
for(; hashmap->iter_bucket < HASH_MAP_NUM_BUCKETS; hashmap->iter_bucket++)
|
|
{
|
|
if(hashmap->buckets[hashmap->iter_bucket])
|
|
{
|
|
if(++hashmap->iter_index < array_len(hashmap->buckets[hashmap->iter_bucket]))
|
|
{
|
|
*key = hashmap->buckets[hashmap->iter_bucket][hashmap->iter_index].key;
|
|
*value = &hashmap->buckets[hashmap->iter_bucket][hashmap->iter_index].value;
|
|
return 1;
|
|
}
|
|
else
|
|
{
|
|
hashmap->iter_index = -1;
|
|
}
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
void hashmap_copy(struct Hashmap* from, struct Hashmap* to)
|
|
{
|
|
struct Variant* from_val = NULL;
|
|
char* from_key = NULL;
|
|
|
|
HASHMAP_FOREACH(from, from_key, from_val)
|
|
{
|
|
hashmap_value_set(to, from_key, from_val);
|
|
}
|
|
}
|
|
|