diff --git a/core/src/dictionary.c b/core/src/dictionary.c new file mode 100644 index 0000000..ed8f6fb --- /dev/null +++ b/core/src/dictionary.c @@ -0,0 +1,100 @@ +#include "dictionary.h" +#include "strutil.h" +#include "string.h" +#include "math.h" + +typedef struct Key { + char* key; + uintptr_t hash; +} Key; + +Dictionary dictionary_new(size_t element_size) { + return (Dictionary) { + .element_size = element_size, + .list = list_init(sizeof(Key) + element_size) + }; +} + +Key* internal_dictionary_get(Dictionary* self, uintptr_t keyhash, size_t* out_index) { + if(self->list.len == 0) { + *out_index = 0; + return NULL; + } + Key* element; + signed long l = 0, r = self->list.len, m; + while(l <= r) { + m = round((double)(l+r)/2.0); + if(out_index != NULL) *out_index = m; + element = list_at_unchecked(&self->list, m); + if(keyhash < element->hash) + r = m-1; + else if(keyhash > element->hash) + l = m+1; + else + return element; + } + m = m >= 0 ? (m < self->list.len ? m : self->list.len - 1) : 0; + return list_at_as(Key, &self->list, m); +} + +void* dictionary_get(Dictionary* self, const char* key) { + uintptr_t hash = strhash(key); + Key* keyblock = internal_dictionary_get(self, hash, NULL); + return strcmp(key, keyblock->key) != 0 ? NULL : ((char*)keyblock) + sizeof(Key); +} + +static inline +int dictionary_insert_kvp(Dictionary* self, Key key, void* value, size_t index) { + // allocate a block of memory to insert into the list + char* insert_data = malloc(self->list.element_size); + if(insert_data == NULL) + return -1; // OOM + // fill block with key and value + memcpy(insert_data, &key, sizeof(Key)); + memcpy(insert_data + sizeof(Key), value, self->element_size); + // decide add or insert based on current length of list + if(self->list.len == 0) + list_add(&self->list, insert_data); + else + list_insert(&self->list, insert_data, index); + // free allocated key-value block + free(insert_data); + return 0; +} + +int dictionary_set_raw(Dictionary* self, const char* key, void* data) { + const uintptr_t keyhash = strhash(key); + size_t closest_index; + Key* closest = internal_dictionary_get(self, keyhash, &closest_index); + // get index of closest hash + // prepare key data + Key keydata = { + .hash = strhash(key), + .key = malloc(strlen(key)+1) + }; + strcpy(keydata.key, key); + if(closest == NULL || keyhash < closest->hash) { // insert before + return dictionary_insert_kvp(self, keydata, data, closest_index); + } else if(strcmp(closest->key, key) == 0) { + memcpy(closest + 1, data, self->element_size); + return 0; + } else { + return dictionary_insert_kvp(self, keydata, data, closest_index + 1); + } +} + +int dictionary_erase(Dictionary* self, const char* key) { + const uintptr_t keyhash = strhash(key); + size_t index; + Key* element = internal_dictionary_get(self, keyhash, &index); + if(element->hash != keyhash || strcmp(key, element->key) != 0) + return -1; // not a match + // free the key string + free(element->key); + list_erase(&self->list, index); + return 0; +} + +void dictionary_empty(Dictionary* self) { + list_empty(&self->list); +} diff --git a/core/src/dictionary.h b/core/src/dictionary.h new file mode 100644 index 0000000..1a5361d --- /dev/null +++ b/core/src/dictionary.h @@ -0,0 +1,36 @@ +#ifndef _fencer_hash_map_h +#define _fencer_hash_map_h + +#include "list.h" +#include "stdint.h" + +typedef struct Dictionary { + List list; + size_t element_size; +} Dictionary; + +extern Dictionary dictionary_new(size_t element_size); +extern void* dictionary_get(Dictionary* self, const char* key); +extern int dictionary_set_raw(Dictionary* self, const char* key, void* data); +extern int dictionary_erase(Dictionary* self, const char* key); +extern void dictionary_empty(Dictionary* self); + +static inline int dictionary_set_int(Dictionary* self, const char* key, int data) { + return dictionary_set_raw(self, key, &data); +} +static inline int dictionary_set_unintptr(Dictionary* self, const char* key, uintptr_t data) { + return dictionary_set_raw(self, key, &data); +} +#define dictionary_set_value(Type_, Dict_, Key_, Value_)\ +{ Type_ __value = (Type_)Value_; dictionary_set_raw(Dict_, Key_, &__value); } +#define dictionary_check_size(Type_, Dict_)\ +(Dict_)->element_size == sizeof(Type_) + +#define dictionary_from_type(Type_)\ +dictionary_new(sizeof(Type_)) +#define dictionary_get_as(Type_, Dict_, Key_)\ +(Type_*)dictionary_get(Dict_, Key_) +#define dictionary_get_value(Type_, Dict_, Key_)\ +*((Type_*)dictionary_get(Dict_, Key_)) + +#endif // !_fencer_hash_map_h diff --git a/core/src/list.c b/core/src/list.c index 1977a68..3035375 100644 --- a/core/src/list.c +++ b/core/src/list.c @@ -64,7 +64,6 @@ void list_reserve(List* self, size_t at_least) { self->cap = new_cap; } -static inline void* list_at_unchecked(List* self, size_t at) { union { uint8_t* as_byte; diff --git a/core/src/list.h b/core/src/list.h index e79e206..0d34494 100644 --- a/core/src/list.h +++ b/core/src/list.h @@ -18,6 +18,7 @@ extern void list_empty(List* list); extern void list_reserve(List* self, size_t at_least); extern void* list_at(List* list, size_t at); +extern void* list_at_unchecked(List* self, size_t at); extern size_t list_add(List* self, void* item); extern void list_insert(List* self, void* item, size_t at);