dict

Dictionary for sd_t's
git clone git://www.tkruger.se/dict.git
Log | Files | Refs | README

commit 681e2339e846ba645e5ff6ade641adfb30c3734e
parent b5311852537e210aa4da7463f8d5dd972d4952b6
Author: olikru <olikru@tkruger.se>
Date:   Tue,  9 Jan 2024 14:32:23 +0100

major refactor

Diffstat:
MREADME | 3++-
Mdict.c | 128++++++++++++++++++++++++++++++++++++++++++++++++-------------------------------
Mdict.h | 58++++++++++++++++++++++++++++++++++++++++++++++------------
Mtest_dict.c | 96+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++--
4 files changed, 220 insertions(+), 65 deletions(-)

diff --git a/README b/README @@ -1,4 +1,5 @@ dict ==== -Small dictionary type for sd_t's. +Small dictionary type for sd_t's. Implementation specifically not made +for being able to remove elements from the list. diff --git a/dict.c b/dict.c @@ -1,54 +1,31 @@ #include "dict.h" -static inline void stack_ptr_init_size(stack_ptr *d, const size_t alloc) { - d->nmemb = 0; - d->alloc = alloc; - - d->data = calloc(d->alloc, sizeof(*(d->data))); -} - -static inline void stack_ptr_clear(stack_ptr *d) { - d->nmemb = 0; - free(d->data); - d->data = NULL; -} - -static inline void stack_ptr_push(stack_ptr *d, void *x) { - if (d->alloc <= d->nmemb) { - d->alloc <<= 1; - d->data = realloc(d->data, d->alloc * sizeof(*(d->data))); - } - - d->data[d->nmemb] = x; - d->nmemb++; -} - void dict_init_size(dict_t *d, const size_t alloc) { d->nalloc = alloc; d->nelts = 0; - d->keys = calloc(d->nalloc, sizeof(*d->keys)); - d->values = calloc(d->nalloc, sizeof(*d->values)); - - size_t i; - for (i = 0; i < d->nalloc; i++) { - stack_sd_init_size(&d->keys[i], DICT_DEFAULT_STACK_SIZE); - stack_ptr_init_size(&d->values[i], DICT_DEFAULT_STACK_SIZE); - } + d->keys = calloc(d->nalloc, sizeof(sd_t *)); + d->values = calloc(d->nalloc, sizeof(*(d->values))); + d->nvalues = calloc(d->nalloc, sizeof(*(d->nvalues))); + d->value_alloc = calloc(d->nalloc, sizeof(*(d->value_alloc))); } void dict_init(dict_t *d) { dict_init_size(d, DICT_DEFAULT_SIZE); } void dict_clear(dict_t *d) { - size_t i; + size_t i,j; for (i = 0; i < d->nalloc; i++) { - stack_sd_clear(&d->keys[i]); - stack_ptr_clear(&d->values[i]); + for(j = 0; j < d->nvalues[i]; j++) + sd_clear(&(d->keys[i][j])); + free(d->keys[i]); + free(d->values[i]); } d->nalloc = 0; d->nelts = 0; free(d->keys); free(d->values); + free(d->nvalues); + free(d->value_alloc); d->keys = NULL; d->values = NULL; } @@ -56,27 +33,78 @@ void dict_clear(dict_t *d) { void *dict_lookup(dict_t *d, sd_t k) { uint64_t h = sd_hash(&k); size_t i = h % d->nalloc; - int stack_idx = stack_sd_lookup(&d->keys[i], k); + size_t j; - if (stack_idx == STACK_SD_LOOKUP_NOT_FOUND) { - return NULL; + // linear search for key in d->keys[i] + for (j = 0; j < d->nvalues[i]; j++) { + if(sd_cmp(&(d->keys[i][j]), &k) == 0) + return d->values[i][j]; } - return (d->values[i]).data[stack_idx]; + return NULL; } -#include <stdio.h> +static inline void dict_basic_insert(dict_t *d, const sd_t * const key, void * const val, const size_t i) { + if (d->value_alloc[i] == 0) { + // create new stacks + d->value_alloc[i] = DICT_SMALLEST_STACK_SIZE; + d->values[i] = calloc(DICT_SMALLEST_STACK_SIZE, sizeof(*(d->values[i]))); + d->keys[i] = calloc(DICT_SMALLEST_STACK_SIZE, sizeof(*(d->keys[i]))); + } else if(d->value_alloc[i] == d->nvalues[i]) { + // double size + d->value_alloc[i] <<= 1; + d->values[i] = realloc(d->values[i], d->value_alloc[i] * sizeof(*(d->values[i]))); + d->keys[i] = realloc(d->keys[i], d->value_alloc[i] * sizeof(*(d->keys[i]))); + } -void dict_insert(dict_t *d, sd_t k, void *value) { - uint64_t h = sd_hash(&k); - size_t i = h % d->nalloc; - int stack_idx = stack_sd_lookup(&d->keys[i], k); - - if (stack_idx == STACK_SD_LOOKUP_NOT_FOUND) { - stack_sd_push(&(d->keys[i]), k); - stack_ptr_push(&(d->values[i]), value); - d->nelts++; - } else { - (d->values[i]).data[stack_idx] = value; + size_t j = d->nvalues[i]; + d->keys[i][j] = *key; + d->values[i][j] = val; + d->nvalues[i]++; + d->nelts++; +} + +static inline void dict_copy_insert(dict_t *d, const sd_t key, void * const val, const size_t i) { + // copy the key + sd_t key_copy; + sd_init(&key_copy, key.size); + memcpy(key_copy.data, key.data, key.size); + + dict_basic_insert(d, &key_copy, val, i); +} + +static inline void dict_resize(dict_t *d, const size_t nalloc) { + dict_t e; + dict_init_size(&e, nalloc); + size_t i,j, new_index; + + // insert the elements into e + for (i = 0; i < d->nalloc; i++) { + for(j = 0; j < d->nvalues[i]; j++) { + new_index = sd_hash(&(d->keys[i][j])) % e.nalloc; + dict_basic_insert(&e, &(d->keys[i][j]), d->values[i][j], new_index); + } + + // free the old stack with index i + free(d->keys[i]); + free(d->values[i]); } + + // free the list of stacks + free(d->keys); + free(d->values); + + // change d to point where d pointed + *d = e; +} + +void dict_insert(dict_t *d, sd_t k, void *value) { + size_t i; + + while(DICT_FACTOR * d->nelts > d->nalloc) + dict_resize(d, d->nalloc * DICT_FACTOR); + + i = sd_hash(&k) % d->nalloc; + + dict_copy_insert(d, k, value, i); } diff --git a/dict.h b/dict.h @@ -4,30 +4,64 @@ #include "stack_sd.h" #define DICT_DEFAULT_SIZE 255 -#define DICT_DEFAULT_STACK_SIZE 2 -#define STACK_PTR_EMPTY_POP (NULL) +#define DICT_SMALLEST_STACK_SIZE 2 #define DICT_LOOKUP_NOT_FOUND (STACK_SD_LOOKUP_NOT_FOUND) // == -1 - -typedef struct { - size_t nmemb; - size_t alloc; - - void **data; -} stack_ptr; +#define DICT_FACTOR 2 typedef struct { size_t nelts; size_t nalloc; - stack_sd_t *keys; - stack_ptr *values; + size_t* nvalues; + size_t* value_alloc; + sd_t **keys; + void ***values; } dict_t; +/** + * Initialises a dictionary with a given size (allocated). The + * initialized dict_t will be "empty". This allocates memory that will + * need to be cleared (use dict_clear). + * + * @param d pointer to the dict_t to initialise + * @param alloc the size to allocate to it + */ void dict_init_size(dict_t *d, const size_t alloc); + +/** + * Initialises a dictionary with the standard allocation size (which is + * DICT_DEFAULT_SIZE == 255). + * + * @param d pointer to the dict_t to initialise + */ void dict_init(dict_t *d); + +/** + * Clears an initialised dictionary. + * + * @param d pointer to the dict_t to clear + */ void dict_clear(dict_t *d); + +/** + * Lookup an element in the dictionary, by its key. Returns the void + * pointer which is stored as its value, if the dictionary contains the + * key. Otherwise, NULL is returned. + * + * @param d pointer to the dict_t to lookup in + * @param k the key to lookup with + */ void *dict_lookup(dict_t *d, sd_t k); + +/** + * Insert an element (a key <-> value pair) into the dictionary. If the + * key is already in the dictionary the value is replaced with the + * provided value. + * + * @param d pointer to the dict_t to insert into + * @param k the key of the element to insert + * @param v the (void*) value to insert + */ void dict_insert(dict_t *t, sd_t k, void *v); -void print_dict(dict_t *d); #endif diff --git a/test_dict.c b/test_dict.c @@ -1,8 +1,100 @@ #include <stdlib.h> #include <stdio.h> #include <assert.h> -#include "fheap.h" +#include "dict.h" + +static void test_dict_init() { + size_t i; + dict_t d; + dict_init_size(&d, 20); + + assert(d.nalloc == 20); + assert(d.nelts == 0); + assert(d.keys != NULL); + assert(d.values != NULL); + assert(d.nvalues != NULL); + assert(d.value_alloc != NULL); + + for (i = 0; i < d.nalloc; i++) { + assert(d.nvalues[i] == 0); + assert(d.value_alloc[i] == 0); + assert(d.keys[i] == NULL); + assert(d.values[i] == NULL); + } + + dict_clear(&d); + + dict_init(&d); + + assert(d.nalloc == DICT_DEFAULT_SIZE); + assert(d.nelts == 0); + assert(d.keys != NULL); + assert(d.values != NULL); + assert(d.nvalues != NULL); + assert(d.value_alloc != NULL); + + for (i = 0; i < d.nalloc; i++) { + assert(d.nvalues[i] == 0); + assert(d.value_alloc[i] == 0); + assert(d.keys[i] == NULL); + assert(d.values[i] == NULL); + } + + dict_clear(&d); +} + +static void test_dict_insert() { + dict_t d; + dict_init_size(&d, 4); + + sd_t elts[7]; + uint64_t values[7] = {1,2,3,4,5,6,7}; + size_t i; + for(i = 0; i < 7; i++) { + sd_init_u64(&elts[i], 2*i+1); + dict_insert(&d, elts[i], &values[i]); + } + + assert(d.nelts == 7); + assert(d.nalloc >= 7); + + for(i = 0; i < 7; i++) + sd_clear(&elts[i]); + dict_clear(&d); +} + +static void test_dict_lookup() { + dict_t d; + dict_init_size(&d, 4); + + sd_t elts[7]; + uint64_t values[7] = {1,2,3,4,5,6,7}; + size_t i; + for(i = 0; i < 7; i++) { + sd_init_u64(&elts[i], 2*i+1); + dict_insert(&d, elts[i], &values[i]); + } + + sd_t pelt[2]; + sd_init_u64(&pelt[0], 5); + sd_init_u64(&pelt[1], 37); + + for(i = 0; i < 7; i++) + assert(* (uint64_t*) dict_lookup(&d, elts[i]) == i+1); + + assert(* (uint64_t*) dict_lookup(&d, pelt[0]) == 3); + assert(dict_lookup(&d, pelt[1]) == NULL); + + for(i = 0; i < 7; i++) + sd_clear(&elts[i]); + dict_clear(&d); +} + int main() { - printf("test unimplemented\n"); + test_dict_init(); + test_dict_insert(); + test_dict_lookup(); + + printf("test ok\n"); }