#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) { if(out_index) *out_index = 0; return NULL; } Key* element; signed long l = 0, r = self->list.len, m; while(l <= r) { m = floor((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_raw(Dictionary* self, const char* key) { uintptr_t hash = strhash(key); Key* keyblock = internal_dictionary_get(self, hash, NULL); return keyblock == NULL || 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 || index >= self->list.len) 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 = keyhash, .key = malloc(strlen(key)+1) }; strcpy(keydata.key, key); if(closest == NULL) { return dictionary_insert_kvp(self, keydata, data, closest_index); } else if(closest->hash == keyhash && strcmp(closest->key, key) == 0) { memcpy(closest + 1, data, self->element_size); return 0; } else if(keyhash < closest->hash) { // insert before return dictionary_insert_kvp(self, keydata, data, closest_index); } 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); } int dictionary_has_key(Dictionary* self, const char* key) { return dictionary_get_raw(self, key) != NULL; } int dictionary_try_get(Dictionary* self, const char* key, void* out) { void* found = dictionary_get_raw(self, key); if(found == NULL) return 0; memmove(out, found, self->element_size); return 1; }