117 lines
3.6 KiB
C
117 lines
3.6 KiB
C
|
#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;
|
||
|
}
|