From f01b559319c685e50783b90efbb7aa9fa8089f30 Mon Sep 17 00:00:00 2001 From: Andrew McDermott Date: Fri, 1 Jan 2010 19:05:27 +0000 Subject: [PATCH] changed implementation to support LRU/MRU iteration --- Makefile | 40 +-- hashtbl.c | 407 ++++++++++++++------------ hashtbl.h | 156 +++++----- hashtbl_test.c | 764 ++++++++++++++++++++++++++++++++++++------------- 4 files changed, 889 insertions(+), 478 deletions(-) diff --git a/Makefile b/Makefile index 196c761..30cac44 100644 --- a/Makefile +++ b/Makefile @@ -1,4 +1,4 @@ -# Copyright (c) 2009 +# Copyright (c) 2009 # # Source can be cloned from: # @@ -23,51 +23,51 @@ # THE SOFTWARE. PROFILE_FLAGS = -fprofile-arcs -ftest-coverage -COMMON_CFLAGS += -Wall -Wextra +COMMON_CFLAGS += -Wall COMMON_CFLAGS += -Wformat -Wmissing-prototypes -Wpointer-arith -COMMON_CFLAGS += -Wuninitialized -O -COMMON_CFLAGS += -Wsign-compare -Wshadow -COMMON_CFLAGS += -pedantic +COMMON_CFLAGS += -Wstrict-aliasing +#COMMON_CFLAGS += -Wuninitialized -O +COMMON_CFLAGS += -Wshadow CFLAGS += $(COMMON_CFLAGS) CFLAGS += -g -fno-inline -#CFLAGS += -O3 -DNDEBUG +CFLAGS += -O3 -DNDEBUG VALGRIND = valgrind --quiet --leak-check=full all : hashtbl_test ifeq ($(shell uname -s),Linux) - @echo $(VALGRIND) ./hashtbl_test - @$(VALGRIND) ./hashtbl_test + $(VALGRIND) ./hashtbl_test else ./hashtbl_test endif -hashtbl_test: hashtbl_test.c hashtbl.c - $(CC) $(CFLAGS) -DHASHTBL_MAX_TABLE_SIZE='((1<<8))' -o $@ hashtbl.c hashtbl_test.c +hashtbl_test: hashtbl_test.c hashtbl.c hashtbl.h + $(CC) $(CFLAGS) -DHASHTBL_MAX_TABLE_SIZE='(1<<8)' -o $@ hashtbl.c hashtbl_test.c .PHONY: hashtbl_test.gcov hashtbl_test.gcov: hashtbl_test.c hashtbl.c - $(CC) $(CFLAGS) $(PROFILE_FLAGS) -g -o $@ hashtbl_test.c hashtbl.c + $(CC) $(CFLAGS) $(PROFILE_FLAGS) -DHASHTBL_MAX_TABLE_SIZE='(1<<8)' -g -o $@ hashtbl_test.c hashtbl.c ./$@ - gcov $^ + gcov -a $^ .PHONY : hashtbl_test.pg hashtbl_test.pg: hashtbl_test.c hashtbl.c $(CC) $(CFLAGS) $(PROFILE_FLAGS) \ - -fwhole-program -combine -pg \ - -O3 -g \ + -DHASHTBL_MAX_TABLE_SIZE='(1<<8)' \ + -pg -g \ -o $@ hashtbl_test.c hashtbl.c + ./$@ + gprof -s clean: $(RM) -r *.o *.a *.d hashtbl_test.pg hashtbl_test.gcov hashtbl_test *.gcda *.gcov *.pg *.gcno *.o : Makefile -hashtbl_test : Makefile -hashtbl.c : hashtbl.h -hashtbl_test.c : hashtbl.c -hashtbl_test.c : hashtbl.h +hashtbl_test: hashtbl_test.c CUnitTest.h hashtbl.h Makefile + +.PHONY: TAGS -hashtbl.o : hashtbl.c hashtbl.h -hashtbl_test.o: hashtbl_test.c CUnitTest.h hashtbl.h +TAGS: + etags *.c *.h diff --git a/hashtbl.c b/hashtbl.c index 093f911..6448e96 100644 --- a/hashtbl.c +++ b/hashtbl.c @@ -49,8 +49,14 @@ #include #include "hashtbl.h" +#define UNUSED_PARAMETER(X) (void) (X) + #ifndef HASHTBL_MAX_TABLE_SIZE -#define HASHTBL_MAX_TABLE_SIZE (1 << 30) +#define HASHTBL_MAX_TABLE_SIZE (1 << 30) +#endif + +#ifndef HASHTBL_DEFAULT_LOAD_FACTOR +#define HASHTBL_DEFAULT_LOAD_FACTOR 0.75f #endif #if defined(_MSC_VER) @@ -60,7 +66,7 @@ #endif #define DLLIST_CONTAINER_OF(PTR, TYPE, FIELD) \ - ((TYPE *)((char *)PTR - offsetof(TYPE, FIELD))) + (TYPE *)(((char *)PTR) - offsetof(TYPE, FIELD)) #define DLLIST_ENTRY(PTR, TYPE, FIELD) \ DLLIST_CONTAINER_OF(PTR, TYPE, FIELD) @@ -69,29 +75,30 @@ struct dllist { struct dllist *next, *prev; }; +struct hashtbl { + struct dllist all_entries; + float max_load_factor; + HASHTBL_HASH_FN hash_fn; + HASHTBL_EQUALS_FN equals_fn; + unsigned long nentries; + int table_size; + int resize_threshold; + int auto_resize; + int access_order; + HASHTBL_KEY_FREE_FN key_free; + HASHTBL_VAL_FREE_FN val_free; + HASHTBL_MALLOC_FN malloc_fn; + HASHTBL_FREE_FN free_fn; + HASHTBL_EVICTOR_FN evictor_fn; + struct hashtbl_entry **table; +}; + struct hashtbl_entry { struct dllist list; /* runs through all entries */ struct hashtbl_entry *next; /* [single] linked list head */ void *key; void *val; - unsigned int hash; /* hash of key */ -}; - -struct hashtbl { - struct dllist all_entries; /* list head */ - float max_load_factor; /* before a resize */ - HASHTBL_HASH_FUNC hashfun; - HASHTBL_EQUALS_FUNC equalsfun; - unsigned int count; /* number of entries */ - int table_size; /* absolute table capacity */ - int resize_threshold; - hashtbl_resize_policy resize_policy; - hashtbl_iteration_order iteration_order; - HASHTBL_KEY_FREE_FUNC kfreefunc; - HASHTBL_VAL_FREE_FUNC vfreefunc; - HASHTBL_MALLOC_FUNC mallocfunc; - HASHTBL_FREE_FUNC freefunc; - struct hashtbl_entry **table; + hashtbl_hash_t hash; /* hash of key */ }; static INLINE int dllist_is_empty(const struct dllist *list) @@ -101,12 +108,13 @@ static INLINE int dllist_is_empty(const struct dllist *list) static INLINE void dllist_init(struct dllist *list) { - list->next = list->prev = list; + list->next = list; + list->prev = list; } -static INLINE void dllist_add_between(struct dllist *node, - struct dllist *prev, - struct dllist *next) +static INLINE void dllist_add_node(struct dllist *node, + struct dllist *prev, + struct dllist *next) { node->next = next; node->prev = prev; @@ -118,14 +126,14 @@ static INLINE void dllist_add_between(struct dllist *node, static INLINE void dllist_add_before(struct dllist *node, struct dllist *head) { - dllist_add_between(node, head, head->next); + dllist_add_node(node, head, head->next); } /* Add node before head. */ static INLINE void dllist_add_after(struct dllist *node, struct dllist *head) { - dllist_add_between(node, head->prev, head); + dllist_add_node(node, head->prev, head); } static INLINE void dllist_remove(struct dllist *node) @@ -136,7 +144,7 @@ static INLINE void dllist_remove(struct dllist *node) static INLINE int resize_threshold(int capacity, float max_load_factor) { - return (int)((capacity * max_load_factor) + 0.5); + return (int)((capacity * max_load_factor) + 0.5f); } /* @@ -149,7 +157,8 @@ static INLINE int resize_threshold(int capacity, float max_load_factor) * original number was a power of 2, then the decrement will reduce it * to one less, so that we round up to the same original value. */ -static unsigned int roundup_to_next_power_of_2(unsigned int x) +#if 0 +static unsigned int roundup_to_next_power_of_2_oldx(unsigned int x) { x--; x |= x >> 1; /* handle 2 bit numbers */ @@ -160,6 +169,14 @@ static unsigned int roundup_to_next_power_of_2(unsigned int x) x++; return x; } +#endif + +static int roundup_to_next_power_of_2(int x) +{ + int n = 1; + while (n < x) n <<=1; + return n; +} #ifndef NDEBUG static int is_power_of_2(unsigned int x) @@ -172,7 +189,7 @@ static int is_power_of_2(unsigned int x) * hash helper - Spread the lower order bits. * Magic numbers from Java 1.4. */ -static INLINE unsigned int hash_helper(unsigned int k) +static INLINE unsigned int hash_spreader(unsigned int k) { unsigned int h = k; h ^= (h >> 20) ^ (h >> 12); @@ -184,9 +201,9 @@ static INLINE unsigned int hash_helper(unsigned int k) * This algorithm was first reported by Dan Bernstein many years ago * in comp.lang.c. */ -static INLINE unsigned long djb2_hash(const unsigned char *str) +static INLINE unsigned int djb2_hash(const unsigned char *str) { - unsigned long hash = 5381; + unsigned int hash = 5381; if (str != NULL) { int c; @@ -194,9 +211,11 @@ static INLINE unsigned long djb2_hash(const unsigned char *str) hash = ((hash << 5) + hash) + c; } } + return hash; } +#if 0 static INLINE unsigned int djb_hash(void *key, size_t len) { unsigned char *p = key; @@ -213,7 +232,7 @@ static INLINE unsigned int djb_hash(void *key, size_t len) static INLINE unsigned int fnv_hash(void *key, size_t len) { unsigned char *p = key; - unsigned h = 2166136261; + unsigned int h = 0x811c9dc5; /* 2166136261; */ size_t i; for (i = 0; i < len; i++) @@ -223,7 +242,7 @@ static INLINE unsigned int fnv_hash(void *key, size_t len) } /* Robert Jenkins' 32 bit integer hash function. */ -#if 0 + static INLINE unsigned int int32hash(unsigned int a) { a = (a + 0x7ed55d16) + (a << 12); @@ -236,38 +255,10 @@ static INLINE unsigned int int32hash(unsigned int a) } #endif -/* Create a new table with NEW_CAPACITY slots. The capacity is - * automatically rounded up to the next power of 2, but not beyond the - * maximum table size (HASHTBL_MAX_TABLE_SIZE). *NEW_CAPACITY is set - * to the rounded value. - */ -static INLINE struct hashtbl_entry ** create_table(const struct hashtbl *h, - int *new_capacity) -{ - struct hashtbl_entry **table; - int capacity = *new_capacity; - - if (capacity < 1) { - capacity = 1; - } else if (capacity > HASHTBL_MAX_TABLE_SIZE) { - capacity = HASHTBL_MAX_TABLE_SIZE; - } - - capacity = roundup_to_next_power_of_2(capacity); - assert(is_power_of_2(capacity)); - assert(capacity > 0 && capacity <= HASHTBL_MAX_TABLE_SIZE); - - if ((table = h->mallocfunc(capacity * sizeof(*table))) == NULL) - return NULL; - - *new_capacity = capacity; /* output value is normalized capacity */ - memset(table, 0, capacity * sizeof(*table)); - return table; -} - static INLINE void record_access(struct hashtbl *h, struct hashtbl_entry *entry) { - if (h->iteration_order == HASHTBL_MRU_ORDER) { + if (h->access_order) { + /* move to head of all_entries */ dllist_remove(&entry->list); dllist_add_before(&entry->list, &h->all_entries); } @@ -278,14 +269,21 @@ static INLINE int slot_num(unsigned int hashval, int table_size) return hashval & (table_size - 1); } +static INLINE int remove_eldest(const struct hashtbl *h, unsigned long nentries) +{ + UNUSED_PARAMETER(h); + UNUSED_PARAMETER(nentries); + return 0; +} + static INLINE struct hashtbl_entry *find_entry(struct hashtbl *h, - unsigned int hv, + hashtbl_hash_t hv, const void *k) { struct hashtbl_entry *entry = h->table[slot_num(hv, h->table_size)]; while (entry != NULL) { - if (entry->hash == hv && h->equalsfun(entry->key, k)) + if (entry->hash == hv && h->equals_fn(entry->key, k)) break; entry = entry->next; } @@ -293,77 +291,75 @@ static INLINE struct hashtbl_entry *find_entry(struct hashtbl *h, return entry; } +/* + * Remove an entry from the hash table without deleting the underlying + * instance. Returns the entry, or NULL if not found. + */ static struct hashtbl_entry * remove_key(struct hashtbl *h, const void *k) { - struct hashtbl_entry **chain_ref, *entry = NULL; - unsigned int hash, slot; - - if (k == NULL) - return entry; + struct hashtbl_entry **slot_ref, *entry; + hashtbl_hash_t hash = h->hash_fn(k); - hash = h->hashfun(k); - slot = slot_num(hash, h->table_size); - entry = h->table[slot]; - chain_ref = &h->table[slot]; + slot_ref = &h->table[slot_num(hash, h->table_size)]; + entry = *slot_ref; while (entry != NULL) { - if (entry->hash == hash && h->equalsfun(entry->key, k)) { + if (entry->hash == hash && h->equals_fn(entry->key, k)) { /* advance previous node to next entry. */ - *chain_ref = entry->next; - h->count--; + *slot_ref = entry->next; + h->nentries--; dllist_remove(&entry->list); - entry->next = NULL; - dllist_init(&entry->list); break; } - chain_ref = &entry->next; + slot_ref = &entry->next; entry = entry->next; } + return entry; } int hashtbl_insert(struct hashtbl *h, void *k, void *v) { struct hashtbl_entry *entry, **slot_ref; - unsigned int hv; - - if (k == NULL) - return 1; + hashtbl_hash_t hv; - hv = h->hashfun(k); + hv = h->hash_fn(k); if ((entry = find_entry(h, hv, k)) != NULL) { - if (entry->val != NULL && h->vfreefunc != NULL) - h->vfreefunc(entry->val); - entry->val = v; /* replace current value */ - record_access(h, entry); + /* Replace the current value. This should not affect + * the iteration order as the key already exists. */ + if (h->val_free != NULL) + h->val_free(entry->val); + entry->val = v; return 0; } - if ((entry = h->mallocfunc(sizeof(*entry))) == NULL) + if ((entry = h->malloc_fn(sizeof(*entry))) == NULL) return 1; - - entry->key = k; - entry->val = v; - entry->hash = hv; - entry->next = NULL; - dllist_init(&entry->list); /* Link new entry at the head. */ slot_ref = &h->table[slot_num(hv, h->table_size)]; + entry->key = k; + entry->val = v; + entry->hash = hv; entry->next = *slot_ref; - *slot_ref = entry; - /* Move entry to the head of all entries. */ + *slot_ref = entry; + /* Move new entry to the head of all entries. */ dllist_add_before(&entry->list, &h->all_entries); - h->count++; + h->nentries++; - if (h->resize_policy == HASHTBL_AUTO_RESIZE) { - if (h->count >= (unsigned int)h->resize_threshold) { - /* auto resize failures are benign. */ - (void) hashtbl_resize(h, 2 * h->table_size); - } + if (h->auto_resize && h->nentries >= h->resize_threshold) { + /* auto resize failures are benign. */ + (void) hashtbl_resize(h, 2 * h->table_size); + } + + /* Evict oldest entry. */ + if (h->evictor_fn(h, h->nentries)) { + struct dllist *node = h->all_entries.prev; + entry = DLLIST_ENTRY(node, struct hashtbl_entry, list); + hashtbl_remove(h, entry->key); } return 0; @@ -371,15 +367,14 @@ int hashtbl_insert(struct hashtbl *h, void *k, void *v) void * hashtbl_lookup(struct hashtbl *h, const void *k) { - if (k != NULL) { - unsigned int hv = h->hashfun(k); - struct hashtbl_entry *entry = find_entry(h, hv, k); + hashtbl_hash_t hv = h->hash_fn(k); + struct hashtbl_entry *entry = find_entry(h, hv, k); - if (entry != NULL) { - record_access(h, entry); - return entry->val; - } + if (entry != NULL) { + record_access(h, entry); + return entry->val; } + return NULL; } @@ -388,13 +383,14 @@ int hashtbl_remove(struct hashtbl *h, const void *k) struct hashtbl_entry *entry = remove_key(h, k); if (entry != NULL) { - if (h->kfreefunc != NULL) - h->kfreefunc(entry->key); - if (h->vfreefunc != NULL) - h->vfreefunc(entry->val); - h->freefunc(entry); + if (h->key_free != NULL) + h->key_free(entry->key); + if (h->val_free != NULL && entry->val != NULL) + h->val_free(entry->val); + h->free_fn(entry); return 0; } + return 1; } @@ -407,80 +403,86 @@ void hashtbl_clear(struct hashtbl *h) node != head; node = tmp, tmp = node->next) { entry = DLLIST_ENTRY(node, struct hashtbl_entry, list); - if (h->kfreefunc != NULL) - h->kfreefunc(entry->key); - if (h->vfreefunc != NULL) - h->vfreefunc(entry->val); - h->table[entry->hash & (h->table_size -1)] = NULL; + if (h->key_free != NULL) + h->key_free(entry->key); + if (h->val_free != NULL) + h->val_free(entry->val); dllist_remove(&entry->list); - h->freefunc(entry); - --h->count; + h->free_fn(entry); + h->nentries--; } + memset(h->table, 0, h->table_size * sizeof(*h->table)); dllist_init(&h->all_entries); - assert(h->count == 0); - assert(dllist_is_empty(&h->all_entries)); + assert(h->nentries == 0); } void hashtbl_delete(struct hashtbl *h) { - if (h != NULL) { - hashtbl_clear(h); - h->freefunc(h->table); - h->freefunc(h); - } + hashtbl_clear(h); + h->free_fn(h->table); + h->free_fn(h); } -unsigned int hashtbl_count(const struct hashtbl *h) +unsigned long hashtbl_count(const struct hashtbl *h) { - return (h != NULL) ? h->count : 0; + return h->nentries; } int hashtbl_capacity(const struct hashtbl *h) { - return (h != NULL) ? h->table_size : 0; + return h->table_size; } -struct hashtbl *hashtbl_new(int capacity, - float max_load_factor, - hashtbl_resize_policy resize_policy, - hashtbl_iteration_order iteration_order, - HASHTBL_HASH_FUNC hashfun, - HASHTBL_EQUALS_FUNC equalsfun, - HASHTBL_KEY_FREE_FUNC kfreefunc, - HASHTBL_VAL_FREE_FUNC vfreefunc, - HASHTBL_MALLOC_FUNC mallocfunc, - HASHTBL_FREE_FUNC freefunc) +struct hashtbl *hashtbl_create(int capacity, + float max_load_factor, + int auto_resize, + int access_order, + HASHTBL_HASH_FN hash_fn, + HASHTBL_EQUALS_FN equals_fn, + HASHTBL_KEY_FREE_FN key_free, + HASHTBL_VAL_FREE_FN val_free, + HASHTBL_MALLOC_FN malloc_fn, + HASHTBL_FREE_FN free_fn, + HASHTBL_EVICTOR_FN evictor_fn) { struct hashtbl *h; - mallocfunc = mallocfunc ? mallocfunc : malloc; - freefunc = freefunc ? freefunc : free; + malloc_fn = (malloc_fn != NULL) ? malloc_fn : malloc; + free_fn = (free_fn != NULL) ? free_fn : free; + hash_fn = (hash_fn != NULL) ? hash_fn : hashtbl_direct_hash; + equals_fn = (equals_fn != NULL) ? equals_fn : hashtbl_direct_equals; + evictor_fn = (evictor_fn != NULL) ? evictor_fn : remove_eldest; - if ((h = (*mallocfunc)(sizeof(*h))) == NULL) + if ((h = malloc_fn(sizeof(*h))) == NULL) return NULL; - if (max_load_factor < 0.0) - max_load_factor = 0.75; + if (max_load_factor < 0.0) { + max_load_factor = HASHTBL_DEFAULT_LOAD_FACTOR; + } else if (max_load_factor > 1.0) { + max_load_factor = 1.0; + } h->max_load_factor = max_load_factor; - h->hashfun = hashfun ? hashfun : hashtbl_direct_hash; - h->equalsfun = equalsfun ? equalsfun : hashtbl_direct_equals; - h->count = 0; - h->table_size = capacity; - h->resize_threshold = resize_threshold(h->table_size, max_load_factor); - h->resize_policy = resize_policy; - h->iteration_order = iteration_order; - h->kfreefunc = kfreefunc; - h->vfreefunc = vfreefunc; - h->mallocfunc = mallocfunc ? mallocfunc : malloc; - h->freefunc = freefunc ? freefunc : free; + h->hash_fn = hash_fn; + h->equals_fn = equals_fn; + h->nentries = 0; + h->table_size = 0; /* must be 0 for resize() to work */ + h->resize_threshold = 0; + h->auto_resize = auto_resize; + h->access_order = access_order; + h->key_free = key_free; + h->val_free = val_free; + h->malloc_fn = malloc_fn; + h->free_fn = free_fn; + h->evictor_fn = evictor_fn; + h->table = NULL; dllist_init(&h->all_entries); - if ((h->table = create_table(h, &h->table_size)) == NULL) { - h->freefunc(h); - return NULL; + if (hashtbl_resize(h, capacity) != 0) { + h->free_fn(h); + h = NULL; } return h; @@ -490,18 +492,31 @@ int hashtbl_resize(struct hashtbl *h, int capacity) { struct dllist *node, *head = &h->all_entries; struct hashtbl_entry *entry, **new_table; + + if (capacity < 1) { + capacity = 1; + } else if (capacity >= HASHTBL_MAX_TABLE_SIZE) { + capacity = HASHTBL_MAX_TABLE_SIZE; + } else { + capacity = roundup_to_next_power_of_2(capacity); + } - if (capacity > HASHTBL_MAX_TABLE_SIZE) - return 0; + assert(is_power_of_2(capacity)); + assert(capacity > 0 && capacity <= HASHTBL_MAX_TABLE_SIZE); + + /* Don't grow if there is no change to the current size. */ - /* Don't grow if there's no increase in size. */ if (capacity < h->table_size || capacity == h->table_size) return 0; - if ((new_table = create_table(h, &capacity)) == NULL) + new_table = h->malloc_fn(capacity * sizeof(*new_table)); + + if (new_table == NULL) return 1; - /* Transfer all entries from old table to new_table. */ + memset(new_table, 0, capacity * sizeof(*new_table)); + + /* Transfer all entries from old table to new table. */ for (node = head->next; node != head; node = node->next) { struct hashtbl_entry **slot_ref; @@ -511,7 +526,7 @@ int hashtbl_resize(struct hashtbl *h, int capacity) *slot_ref = entry; } - h->freefunc(h->table); /* free old table */ + if (h->table != NULL) h->free_fn(h->table); h->table_size = capacity; h->table = new_table; h->resize_threshold = resize_threshold(capacity, h->max_load_factor); @@ -519,25 +534,25 @@ int hashtbl_resize(struct hashtbl *h, int capacity) return 0; } -unsigned int hashtbl_apply(const struct hashtbl *h, - HASHTBL_APPLY_FUNC apply, void *user_arg) +unsigned long hashtbl_apply(const struct hashtbl *h, + HASHTBL_APPLY_FN apply, void *client_data) { - unsigned int count = 0; + unsigned long nentries = 0; struct dllist *node; const struct dllist *head = &h->all_entries; for (node = head->next; node != head; node = node->next) { struct hashtbl_entry *entry; entry = DLLIST_ENTRY(node, struct hashtbl_entry, list); - count++; - if (apply(entry->key, entry->val, user_arg) != 1) - return count; + nentries++; + if (apply(entry->key, entry->val, client_data) != 1) + return nentries; } - return count; + return nentries; } -unsigned int hashtbl_string_hash(const void *k) +hashtbl_hash_t hashtbl_string_hash(const void *k) { return djb2_hash((const unsigned char *)k); } @@ -547,9 +562,9 @@ int hashtbl_string_equals(const void *a, const void *b) return strcmp(((const char *)a), (const char *)b) == 0; } -unsigned int hashtbl_int_hash(const void *k) +hashtbl_hash_t hashtbl_int_hash(const void *k) { - return hash_helper(*(unsigned int *)k); + return *(unsigned int *)k; } int hashtbl_int_equals(const void *a, const void *b) @@ -557,9 +572,9 @@ int hashtbl_int_equals(const void *a, const void *b) return *((const int *)a) == *((const int *)b); } -unsigned int hashtbl_direct_hash(const void *k) +hashtbl_hash_t hashtbl_direct_hash(const void *k) { - return hash_helper((intptr_t)k); + return hash_spreader((uintptr_t)k); } int hashtbl_direct_equals(const void *a, const void *b) @@ -567,27 +582,41 @@ int hashtbl_direct_equals(const void *a, const void *b) return a == b; } -int hashtbl_load_factor(const struct hashtbl *h) +float hashtbl_load_factor(const struct hashtbl *h) { - return (int)(((h->count / (float)h->table_size) * 100.0) + 0.5); + return h->nentries / (float)h->table_size; } -void hashtbl_iter_init(struct hashtbl *h, struct hashtbl_iter *iter) +void hashtbl_iter_init(struct hashtbl *h, struct hashtbl_iter *iter, + hashtbl_iter_direction direction) { iter->key = iter->val = NULL; - iter->private = h->all_entries.next; + iter->private.direction = direction; + + if (direction == HASHTBL_FORWARD_ITERATOR) { + iter->private.p = h->all_entries.next; + } else { + iter->private.p = h->all_entries.prev; + } } int hashtbl_iter_next(struct hashtbl *h, struct hashtbl_iter *iter) { - struct dllist *node; struct hashtbl_entry *entry; - assert(iter->private); - node = (struct dllist *)iter->private; - if (node == &h->all_entries) return 0; + struct dllist *node = (struct dllist *)iter->private.p; + + if (node == &h->all_entries) + return 0; + + if (iter->private.direction == HASHTBL_FORWARD_ITERATOR) { + iter->private.p = node->next; + } else { + iter->private.p = node->prev; + } + entry = DLLIST_ENTRY(node, struct hashtbl_entry, list); - iter->private = entry->list.next; iter->key = entry->key; iter->val = entry->val; + return 1; } diff --git a/hashtbl.h b/hashtbl.h index be656aa..77198ed 100644 --- a/hashtbl.h +++ b/hashtbl.h @@ -30,125 +30,130 @@ * * SYNOPSIS * - * 1. A hash table is created with hashtbl_new(). + * 1. A hash table is created with hashtbl_create(). * 2. To insert an entry use hashtbl_insert(). * 3. To lookup a key use hashtbl_lookup(). * 4. To remove a key use hashtbl_remove(). * 5. To apply a function over all entries use hashtbl_apply(). * 5. To clear all keys use hashtbl_clear(). * 6. To delete a hash table instance use hashtbl_delete(). - * 7. To iterate over all entries use hashtbl_first(), hashtbl_next(). + * 7. To iterate over all entries use hashtbl_iter_init(), hashtbl_iter_next(). * * Note: neither the keys or the values are copied so their lifetime * must match that of the hash table. NULL keys are not permitted. * Inserting, removing or lookup up NULL keys is therefore undefined. - * - * */ #ifndef HASHTBL_H #define HASHTBL_H #ifdef __cplusplus -# define __BEGIN_DECLS extern "C" { -# define __END_DECLS } +# define __HASHTBL_BEGIN_DECLS extern "C" { +# define __HASHTBL_END_DECLS } #else -# define __BEGIN_DECLS -# define __END_DECLS +# define __HASHTBL_BEGIN_DECLS +# define __HASHTBL_END_DECLS #endif -__BEGIN_DECLS +__HASHTBL_BEGIN_DECLS #include /* size_t */ /* Opaque types. */ - struct hashtbl; -typedef unsigned long hashtbl_hashval_t; +/* Hash value type. */ +typedef unsigned int hashtbl_hash_t; /* Hash function. */ -typedef unsigned int (*HASHTBL_HASH_FUNC)(const void *k); +typedef hashtbl_hash_t (*HASHTBL_HASH_FN)(const void *k); -/* Equals function. */ -typedef int (*HASHTBL_EQUALS_FUNC)(const void *a, const void *b); +/* Value equality function. */ +typedef int (*HASHTBL_EQUALS_FN)(const void *a, const void *b); /* Apply function. */ -typedef int (*HASHTBL_APPLY_FUNC)(const void *k, const void *v, const void *u); +typedef int (*HASHTBL_APPLY_FN)(const void *k, const void *v, + const void *client_data); /* Functions for deleting keys and values. */ -typedef void (*HASHTBL_KEY_FREE_FUNC)(void *k); -typedef void (*HASHTBL_VAL_FREE_FUNC)(void *v); +typedef void (*HASHTBL_KEY_FREE_FN)(void *k); +typedef void (*HASHTBL_VAL_FREE_FN)(void *v); -/* Functions for allocting an freeing memory. */ -typedef void * (*HASHTBL_MALLOC_FUNC)(size_t n); -typedef void (*HASHTBL_FREE_FUNC)(void *ptr); +/* Functions for allocating and freeing memory. */ +typedef void * (*HASHTBL_MALLOC_FN)(size_t n); +typedef void (*HASHTBL_FREE_FN)(void *ptr); -typedef enum { - HASHTBL_LRU_ORDER = 1, - HASHTBL_MRU_ORDER = 2 -} hashtbl_iteration_order; +/* Function for removing entries. */ +typedef int (*HASHTBL_EVICTOR_FN)(const struct hashtbl *h, + unsigned long count); typedef enum { - HASHTBL_AUTO_RESIZE = 1, - HASHTBL_NO_RESIZE = 2 -} hashtbl_resize_policy; + HASHTBL_FORWARD_ITERATOR = 1, + HASHTBL_REVERSE_ITERATOR = 2 +} hashtbl_iter_direction; struct hashtbl_iter { void *key, *val; - const void *private; /* clients shouldn't modify this */ + /* private: clients should not touch these fields. */ + struct { + hashtbl_iter_direction direction; + const void *p; + } private; }; /* * [Default] Hash function. */ -unsigned int hashtbl_direct_hash(const void *k); +hashtbl_hash_t hashtbl_direct_hash(const void *k); /* * [Default] Key equals function. * - * Returns 1 if key "a" equals key "b". + * Returns 1 if pointer "a" equals key pointer "b". */ int hashtbl_direct_equals(const void *a, const void *b); /* Hash functions for integer keys/values. */ -unsigned int hashtbl_int_hash(const void *k); +hashtbl_hash_t hashtbl_int_hash(const void *k); int hashtbl_int_equals(const void *a, const void *b); /* Hash functions for nul-terminated string keys/values. */ +hashtbl_hash_t hashtbl_string_hash(const void *k); int hashtbl_string_equals(const void *a, const void *b); -unsigned int hashtbl_string_hash(const void *k); /* * Creates a new hash table. * * @param initial_capacity - initial size of the table - * @param resize_policy - HASHTBL_{AUTO_RESIZE, NO_RESIZE} - * @param iteration_order - either MRU or LRU - * @param hash_fun - function that computes a hash value from a key - * @param equals_fun - function that checks keys for equality - * @param kfreefunc - function to delete "key" instances - * @param kfreefunc - function to delete "value" instances - * @param mallocfunc - function to allocate memory - * @param freefunc - function to free memory - * - * Returns non-null if the table was created succesfully. - */ -struct hashtbl *hashtbl_new(int initial_capacity, - float max_load_factor, - hashtbl_resize_policy resize_policy, - hashtbl_iteration_order iteration_order, - HASHTBL_HASH_FUNC hash_fun, - HASHTBL_EQUALS_FUNC equals_fun, - HASHTBL_KEY_FREE_FUNC kfreefunc, - HASHTBL_VAL_FREE_FUNC vfreefunc, - HASHTBL_MALLOC_FUNC mallocfunc, - HASHTBL_FREE_FUNC freefunc); + * @param max_load_factor - before resizing (0.0 uses a default value) + * @param auto_resize - if true, table grows (N*2) as new keys are added + * @param access_order - if true, iteration order is most recently accessed + * @param hash_func - function that computes a hash value from a key + * @param equals_func - function that checks keys for equality + * @param key_free_func - function to delete keys + * @param val_free_func - function to delete values + * @param malloc_func - function to allocate memory (e.g., malloc) + * @param free_func - function to free memory (e.g., free) + * @param evictor_func - function to evict entries as new keys are added + * + * Returns non-null if the table was created successfully. + */ +struct hashtbl *hashtbl_create(int initial_capacity, + float max_load_factor, + int auto_resize, + int access_order, + HASHTBL_HASH_FN hash_fun, + HASHTBL_EQUALS_FN equals_fun, + HASHTBL_KEY_FREE_FN key_free_func, + HASHTBL_VAL_FREE_FN val_free_func, + HASHTBL_MALLOC_FN malloc_func, + HASHTBL_FREE_FN free_func, + HASHTBL_EVICTOR_FN evictor_func); /* * Deletes the hash table instance. * - * All the keys are removed via hashtbl_clear(). + * All the entries are removed via hashtbl_clear(). * * @param h - hash table */ @@ -172,18 +177,18 @@ void hashtbl_clear(struct hashtbl *h); /* * Inserts a new key with associated value. * - * @param h - hashtable instance + * @param h - hash table instance * @param k - key to insert * @param v - value associated with key * - * Returns 0 on succees, or 1 if a new entry cannot be created. + * Returns 0 on success, or 1 if a new entry cannot be created. */ int hashtbl_insert(struct hashtbl *h, void *k, void *v); /* * Lookup an existing key. * - * @param h - hashtable instance + * @param h - hash table instance * @param k - the search key * * Returns the value associated with key, or NULL if key is not present. @@ -192,11 +197,15 @@ void * hashtbl_lookup(struct hashtbl *h, const void *k); /* * Returns the number of entries in the table. + * + * @param h - hash table instance */ -unsigned int hashtbl_count(const struct hashtbl *h); +unsigned long hashtbl_count(const struct hashtbl *h); /* * Returns the table's capacity. + * + * @param h - hash table instance */ int hashtbl_capacity(const struct hashtbl *h); @@ -206,43 +215,52 @@ int hashtbl_capacity(const struct hashtbl *h); * The apply function should return 0 to terminate the enumeration * early. * + * @h - hash table instance + * @fn - function to apply to each table entry + * @client_data - arbitrary user data + * * Returns the number of entries the function was applied to. */ -unsigned int hashtbl_apply(const struct hashtbl *h, - HASHTBL_APPLY_FUNC f, void *user_arg); +unsigned long hashtbl_apply(const struct hashtbl *h, + HASHTBL_APPLY_FN fn, + void *client_data); /* - * Returns the load factor of the hash table (as a percentage). + * Returns the load factor of the hash table. * * @param h - hash table instance * * The load factor is a ratio and is calculated as: * * hashtbl_count() / hashtbl_capacity() - * - * and expressed as a percentage. */ -int hashtbl_load_factor(const struct hashtbl *h); +float hashtbl_load_factor(const struct hashtbl *h); /* * Resize the hash table. * - * Returns 0 on succees, or 1 if no memory could be allocated. + * Returns 0 on success, or 1 if no memory could be allocated. */ int hashtbl_resize(struct hashtbl *h, int new_capacity); /* - * Initialize an iterator. + * Initialize a forward iterator. + * + * @param h - hash table instance + * @param iter - iterator to initialize + * @param direction - either FORWARD or REVERSE */ -void hashtbl_iter_init(struct hashtbl *h, struct hashtbl_iter *iter); +void hashtbl_iter_init(struct hashtbl *h, struct hashtbl_iter *iter, + hashtbl_iter_direction direction); /* * Advances the iterator. * - * Returns 1 while there more entries, otherwise 0. + * Returns 1 while there more entries, otherwise 0. The key and value + * can be accessed through the iterator structure. */ int hashtbl_iter_next(struct hashtbl *h, struct hashtbl_iter *iter); -__END_DECLS +__HASHTBL_END_DECLS #endif /* HASHTBL_H */ diff --git a/hashtbl_test.c b/hashtbl_test.c index fee0523..9ae524a 100644 --- a/hashtbl_test.c +++ b/hashtbl_test.c @@ -34,13 +34,18 @@ #include "hashtbl.h" #ifndef HASHTBL_MAX_LOAD_FACTOR -#define HASHTBL_MAX_LOAD_FACTOR 0.75 +#define HASHTBL_MAX_LOAD_FACTOR 0.75f #endif -#define UNUSED_PARAMETER(x) (void)(x) +#ifndef HASHTBL_MAX_TABLE_SIZE +#define HASHTBL_MAX_TABLE_SIZE (1 << 14) +#endif + +#define UNUSED_PARAMETER(X) (void)(X) #define NELEMENTS(X) (sizeof((X)) / sizeof((X)[0])) +#define STREQ(A,B) strcmp((A), (B)) == 0 -static int ht_size = 1; +static int ht_size = 0; struct test_val { int x[13]; @@ -56,7 +61,7 @@ struct test_key { int k; }; -static unsigned int key_hash(const void *k) +static hashtbl_hash_t key_hash(const void *k) { return ((struct test_key *)k)->k; } @@ -94,20 +99,21 @@ static int test12_apply_fn1(const void *k, const void *v, const void *u) static int test1(void) { - struct hashtbl *h = NULL; + struct hashtbl *h; struct hashtbl_iter iter; - h = hashtbl_new(ht_size, HASHTBL_MAX_LOAD_FACTOR, - HASHTBL_AUTO_RESIZE, - HASHTBL_LRU_ORDER, - key_hash, key_equals, - NULL, NULL, - NULL, NULL); + h = hashtbl_create(ht_size, + HASHTBL_MAX_LOAD_FACTOR, + 1, + 0, + key_hash, key_equals, + NULL, NULL, + NULL, NULL, NULL); CUT_ASSERT_NOT_NULL(h); CUT_ASSERT_EQUAL(0, hashtbl_count(h)); hashtbl_clear(h); CUT_ASSERT_EQUAL(0, hashtbl_count(h)); - hashtbl_iter_init(h, &iter); + hashtbl_iter_init(h, &iter, HASHTBL_FORWARD_ITERATOR); CUT_ASSERT_FALSE(hashtbl_iter_next(h, &iter)); hashtbl_delete(h); @@ -119,13 +125,14 @@ static int test1(void) static int test2(void) { struct test_key k; - struct hashtbl *h = NULL; - h = hashtbl_new(ht_size, HASHTBL_MAX_LOAD_FACTOR, - HASHTBL_AUTO_RESIZE, - HASHTBL_LRU_ORDER, - key_hash, key_equals, - NULL, NULL, - NULL, NULL); + struct hashtbl *h; + h = hashtbl_create(ht_size, + HASHTBL_MAX_LOAD_FACTOR, + 1, + 0, + key_hash, key_equals, + NULL, NULL, + NULL, NULL, NULL); CUT_ASSERT_NOT_NULL(h); memset(&k, 0, sizeof(k)); k.k = 2; @@ -143,13 +150,14 @@ static int test3(void) { struct test_key k; struct test_val v; - struct hashtbl *h = NULL; - h = hashtbl_new(ht_size, HASHTBL_MAX_LOAD_FACTOR, - HASHTBL_AUTO_RESIZE, - HASHTBL_LRU_ORDER, - key_hash, key_equals, - NULL, NULL, - NULL, NULL); + struct hashtbl *h; + h = hashtbl_create(ht_size, + HASHTBL_MAX_LOAD_FACTOR, + 1, + 0, + key_hash, key_equals, + NULL, NULL, + NULL, NULL, NULL); CUT_ASSERT_NOT_NULL(h); memset(&k, 0, sizeof(k)); memset(&v, 0, sizeof(v)); @@ -164,7 +172,7 @@ static int test3(void) CUT_ASSERT_EQUAL(300, ((struct test_val *)hashtbl_lookup(h, &k))->v); CUT_ASSERT_TRUE(hashtbl_load_factor(h) > 0); hashtbl_clear(h); - CUT_ASSERT_TRUE(hashtbl_load_factor(h) == 0); + CUT_ASSERT_TRUE(hashtbl_load_factor(h) == 0.0f); CUT_ASSERT_EQUAL(0, hashtbl_count(h)); hashtbl_delete(h); return 0; @@ -176,13 +184,14 @@ static int test4(void) { struct test_key k1, k2; struct test_val v1, v2; - struct hashtbl *h = NULL; - h = hashtbl_new(ht_size, HASHTBL_MAX_LOAD_FACTOR, - HASHTBL_AUTO_RESIZE, - HASHTBL_LRU_ORDER, - key_hash, key_equals, - NULL, NULL, - NULL, NULL); + struct hashtbl *h; + h = hashtbl_create(ht_size, + HASHTBL_MAX_LOAD_FACTOR, + 1, + 0, + key_hash, key_equals, + NULL, NULL, + NULL, NULL, NULL); CUT_ASSERT_NOT_NULL(h); memset(&k1, 0, sizeof(k1)); memset(&k2, 0, sizeof(k2)); @@ -221,13 +230,14 @@ static int test5(void) { struct test_key k1, k2; struct test_val v1, v2; - struct hashtbl *h = NULL; - h = hashtbl_new(ht_size, HASHTBL_MAX_LOAD_FACTOR, - HASHTBL_AUTO_RESIZE, - HASHTBL_LRU_ORDER, - key_hash, key_equals, - NULL, NULL, - NULL, NULL); + struct hashtbl *h; + h = hashtbl_create(ht_size, + HASHTBL_MAX_LOAD_FACTOR, + 1, + 0, + key_hash, key_equals, + NULL, NULL, + NULL, NULL, NULL); CUT_ASSERT_NOT_NULL(h); memset(&k1, 0, sizeof(k1)); @@ -268,14 +278,15 @@ static int test6(void) int accumulator = 0; struct test_key k1, k2; struct test_val v1, v2; - struct hashtbl *h = NULL; + struct hashtbl *h; - h = hashtbl_new(ht_size, HASHTBL_MAX_LOAD_FACTOR, - HASHTBL_AUTO_RESIZE, - HASHTBL_LRU_ORDER, - key_hash, key_equals, - NULL, NULL, - NULL, NULL); + h = hashtbl_create(ht_size, + HASHTBL_MAX_LOAD_FACTOR, + 1, + 0, + key_hash, key_equals, + NULL, NULL, + NULL, NULL, NULL); CUT_ASSERT_NOT_NULL(h); @@ -320,14 +331,15 @@ static int test7(void) { struct test_key k; struct test_val v; - struct hashtbl *h = NULL; + struct hashtbl *h; - h = hashtbl_new(ht_size, HASHTBL_MAX_LOAD_FACTOR, - HASHTBL_AUTO_RESIZE, - HASHTBL_LRU_ORDER, - key_hash, key_equals, - NULL, NULL, - NULL, NULL); + h = hashtbl_create(ht_size, + HASHTBL_MAX_LOAD_FACTOR, + 1, + 0, + key_hash, key_equals, + NULL, NULL, + NULL, NULL, NULL); CUT_ASSERT_NOT_NULL(h); @@ -356,17 +368,18 @@ static int test7(void) static int test8(void) { - struct hashtbl *h = NULL; + struct hashtbl *h; struct test_key *k = malloc(sizeof(struct test_key)); struct test_val *v1 = malloc(sizeof(struct test_val)); struct test_val *v2 = malloc(sizeof(struct test_val)); - h = hashtbl_new(ht_size, HASHTBL_MAX_LOAD_FACTOR, - HASHTBL_AUTO_RESIZE, - HASHTBL_LRU_ORDER, - key_hash, key_equals, - free, free, - NULL, NULL); + h = hashtbl_create(ht_size, + HASHTBL_MAX_LOAD_FACTOR, + 1, + 0, + key_hash, key_equals, + free, free, + NULL, NULL, NULL); CUT_ASSERT_NOT_NULL(h); CUT_ASSERT_NOT_NULL(k); @@ -403,15 +416,17 @@ static int test8(void) /* Test null key insertion. */ +#if 0 static int test9(void) { - struct hashtbl *h = NULL; - h = hashtbl_new(ht_size, HASHTBL_MAX_LOAD_FACTOR, - HASHTBL_AUTO_RESIZE, - HASHTBL_LRU_ORDER, - key_hash, key_equals, - NULL, NULL, - NULL, NULL); + struct hashtbl *h; + h = hashtbl_create(ht_size, + HASHTBL_MAX_LOAD_FACTOR, + 1, + 0, + key_hash, key_equals, + NULL, NULL, + NULL, NULL, NULL); CUT_ASSERT_NOT_NULL(h); CUT_ASSERT_EQUAL(0, !hashtbl_insert(h, NULL, NULL)); hashtbl_delete(h); @@ -422,13 +437,14 @@ static int test9(void) static int test10(void) { - struct hashtbl *h = NULL; - h = hashtbl_new(ht_size, HASHTBL_MAX_LOAD_FACTOR, - HASHTBL_AUTO_RESIZE, - HASHTBL_LRU_ORDER, - key_hash, key_equals, - NULL, NULL, - NULL, NULL); + struct hashtbl *h; + h = hashtbl_create(ht_size, + HASHTBL_MAX_LOAD_FACTOR, + 1, + 0, + key_hash, key_equals, + NULL, NULL, + NULL, NULL, NULL); CUT_ASSERT_NOT_NULL(h); CUT_ASSERT_NULL(hashtbl_lookup(h, NULL)); hashtbl_delete(h); @@ -439,30 +455,33 @@ static int test10(void) static int test11(void) { - struct hashtbl *h = NULL; - h = hashtbl_new(ht_size, HASHTBL_MAX_LOAD_FACTOR, - HASHTBL_AUTO_RESIZE, - HASHTBL_LRU_ORDER, - key_hash, key_equals, - free, free, - NULL, NULL); + struct hashtbl *h; + h = hashtbl_create(ht_size, + HASHTBL_MAX_LOAD_FACTOR, + 1, + 0, + key_hash, key_equals, + free, free, + NULL, NULL, NULL); CUT_ASSERT_NOT_NULL(h); CUT_ASSERT_EQUAL(1, hashtbl_remove(h, NULL)); hashtbl_delete(h); return 0; } +#endif static int test12(void) { int test12_max = 100; - struct hashtbl *h = NULL; + struct hashtbl *h; int i; - h = hashtbl_new(ht_size, HASHTBL_MAX_LOAD_FACTOR, - HASHTBL_AUTO_RESIZE, - HASHTBL_LRU_ORDER, - key_hash, key_equals, - free, free, - NULL, NULL); + h = hashtbl_create(ht_size, + HASHTBL_MAX_LOAD_FACTOR, + 1, + 0, + key_hash, key_equals, + free, free, + NULL, NULL, NULL); CUT_ASSERT_NOT_NULL(h); CUT_ASSERT_EQUAL(0, hashtbl_count(h)); @@ -517,13 +536,14 @@ static int test13(void) int i; int keys[] = { 100, 200, 300 }; int values[] = { 1000, 2000, 3000 }; - struct hashtbl *h = NULL; - h = hashtbl_new(ht_size, HASHTBL_MAX_LOAD_FACTOR, - HASHTBL_AUTO_RESIZE, - HASHTBL_LRU_ORDER, - hashtbl_direct_hash, hashtbl_direct_equals, - NULL, NULL, - NULL, NULL); + struct hashtbl *h; + h = hashtbl_create(ht_size, + HASHTBL_MAX_LOAD_FACTOR, + 1, + 0, + hashtbl_direct_hash, hashtbl_direct_equals, + NULL, NULL, + NULL, NULL, NULL); CUT_ASSERT_NOT_NULL(h); CUT_ASSERT_EQUAL(0, hashtbl_count(h)); for (i = 0; i < (int)NELEMENTS(keys); i++) { @@ -549,13 +569,14 @@ static int test14(void) unsigned int i; int keys[] = { 100, 200, 300 }; int values[] = { 1000, 2000, 3000 }; - struct hashtbl *h = NULL; - h = hashtbl_new(ht_size, HASHTBL_MAX_LOAD_FACTOR, - HASHTBL_AUTO_RESIZE, - HASHTBL_LRU_ORDER, - hashtbl_int_hash, hashtbl_int_equals, - NULL, NULL, - NULL, NULL); + struct hashtbl *h; + h = hashtbl_create(ht_size, + HASHTBL_MAX_LOAD_FACTOR, + 1, + 0, + hashtbl_int_hash, hashtbl_int_equals, + NULL, NULL, + NULL, NULL, NULL); CUT_ASSERT_NOT_NULL(h); CUT_ASSERT_EQUAL(0, hashtbl_count(h)); for (i = 0; i < NELEMENTS(keys); i++) { @@ -580,25 +601,29 @@ static int test14(void) static int test15(void) { unsigned int i; - char *keys[] = { "100", "200", "300" }; - char *values[] = { "1000", "2000", "3000" }; - struct hashtbl *h = NULL; - h = hashtbl_new(ht_size, HASHTBL_MAX_LOAD_FACTOR, - HASHTBL_AUTO_RESIZE, - HASHTBL_LRU_ORDER, - hashtbl_string_hash, hashtbl_string_equals, - NULL, NULL, - NULL, NULL); + char *keys[] = { "100", "200", "300" }; + char *values[] = { "100", "200", "300" }; + struct hashtbl *h; + + h = hashtbl_create(ht_size, + HASHTBL_MAX_LOAD_FACTOR, + 1, + 0, + hashtbl_string_hash, hashtbl_string_equals, + NULL, NULL, + NULL, NULL, NULL); + CUT_ASSERT_NOT_NULL(h); CUT_ASSERT_EQUAL(0, hashtbl_count(h)); + for (i = 0; i < NELEMENTS(keys); i++) { - int ss; /* same string */ CUT_ASSERT_EQUAL(0, hashtbl_insert(h, keys[i], values[i])); CUT_ASSERT_NOT_NULL(hashtbl_lookup(h, keys[i])); - ss = strcmp(keys[i], - (char *)hashtbl_lookup(h, keys[i])) == 0; - CUT_ASSERT_EQUAL(0, ss); + CUT_ASSERT_EQUAL(values[i], hashtbl_lookup(h, keys[i])); + CUT_ASSERT_TRUE(STREQ(keys[i], (char *)hashtbl_lookup(h, keys[i]))); + CUT_ASSERT_TRUE(STREQ(values[i], (char *)hashtbl_lookup(h, keys[i]))); } + hashtbl_delete(h); return 0; } @@ -609,55 +634,57 @@ static int test16(void) { struct hashtbl *h; - h = hashtbl_new(-1, HASHTBL_MAX_LOAD_FACTOR, - HASHTBL_AUTO_RESIZE, - HASHTBL_LRU_ORDER, - hashtbl_direct_hash, hashtbl_direct_equals, - NULL, NULL, - NULL, NULL); + h = hashtbl_create(-1, + HASHTBL_MAX_LOAD_FACTOR, + 1, + 0, + hashtbl_direct_hash, hashtbl_direct_equals, + NULL, NULL, + NULL, NULL, NULL); CUT_ASSERT_NOT_NULL(h); CUT_ASSERT_EQUAL(1, hashtbl_capacity(h)); hashtbl_delete(h); - h = hashtbl_new(0, HASHTBL_MAX_LOAD_FACTOR, - HASHTBL_AUTO_RESIZE, - HASHTBL_LRU_ORDER, - hashtbl_direct_hash, hashtbl_direct_equals, - NULL, NULL, - NULL, NULL); + h = hashtbl_create(0, + HASHTBL_MAX_LOAD_FACTOR, + 1, + 0, + hashtbl_direct_hash, hashtbl_direct_equals, + NULL, NULL, + NULL, NULL, NULL); CUT_ASSERT_EQUAL(1, hashtbl_capacity(h)); CUT_ASSERT_NOT_NULL(h); hashtbl_delete(h); - h = hashtbl_new(HASHTBL_MAX_TABLE_SIZE +1, - HASHTBL_MAX_LOAD_FACTOR, - HASHTBL_AUTO_RESIZE, - HASHTBL_LRU_ORDER, - hashtbl_direct_hash, hashtbl_direct_equals, - NULL, NULL, - NULL, NULL); + h = hashtbl_create(HASHTBL_MAX_TABLE_SIZE +1, + HASHTBL_MAX_LOAD_FACTOR, + 1, + 0, + hashtbl_direct_hash, hashtbl_direct_equals, + NULL, NULL, + NULL, NULL, NULL); CUT_ASSERT_EQUAL(HASHTBL_MAX_TABLE_SIZE, hashtbl_capacity(h)); CUT_ASSERT_NOT_NULL(h); hashtbl_delete(h); - h = hashtbl_new(127, - HASHTBL_MAX_LOAD_FACTOR, - HASHTBL_AUTO_RESIZE, - HASHTBL_LRU_ORDER, - hashtbl_direct_hash, hashtbl_direct_equals, - NULL, NULL, - NULL, NULL); + h = hashtbl_create(127, + HASHTBL_MAX_LOAD_FACTOR, + 1, + 0, + hashtbl_direct_hash, hashtbl_direct_equals, + NULL, NULL, + NULL, NULL, NULL); CUT_ASSERT_NOT_NULL(h); CUT_ASSERT_EQUAL(128, hashtbl_capacity(h)); hashtbl_delete(h); - h = hashtbl_new(128, - HASHTBL_MAX_LOAD_FACTOR, - HASHTBL_AUTO_RESIZE, - HASHTBL_LRU_ORDER, - hashtbl_direct_hash, hashtbl_direct_equals, - NULL, NULL, - NULL, NULL); + h = hashtbl_create(128, + HASHTBL_MAX_LOAD_FACTOR, + 1, + 0, + hashtbl_direct_hash, hashtbl_direct_equals, + NULL, NULL, + NULL, NULL, NULL); CUT_ASSERT_NOT_NULL(h); hashtbl_resize(h, 128); CUT_ASSERT_EQUAL(128, hashtbl_capacity(h)); @@ -671,9 +698,28 @@ static int test16(void) CUT_ASSERT_EQUAL(HASHTBL_MAX_TABLE_SIZE, hashtbl_capacity(h)); hashtbl_resize(h, 1+HASHTBL_MAX_TABLE_SIZE); CUT_ASSERT_EQUAL(HASHTBL_MAX_TABLE_SIZE, hashtbl_capacity(h)); - hashtbl_delete(h); + h = hashtbl_create(ht_size, + -1.0f, + 1, + 0, + hashtbl_direct_hash, hashtbl_direct_equals, + NULL, NULL, + NULL, NULL, NULL); + CUT_ASSERT_NOT_NULL(h); + hashtbl_delete(h); + + h = hashtbl_create(ht_size, + 1.1f, + 1, + 0, + NULL, NULL, + NULL, NULL, + NULL, NULL, NULL); + CUT_ASSERT_NOT_NULL(h); + hashtbl_delete(h); + return 0; } @@ -684,38 +730,48 @@ static int test17(void) unsigned int i; char *keys[] = { "100", "200", "300" }; char *vals[] = { "1000", "2000", "3000" }; - struct hashtbl *h = NULL; + struct hashtbl *h; struct hashtbl_iter iter; - h = hashtbl_new(ht_size, HASHTBL_MAX_LOAD_FACTOR, - HASHTBL_AUTO_RESIZE, - HASHTBL_LRU_ORDER, - hashtbl_string_hash, hashtbl_string_equals, - NULL, NULL, - NULL, NULL); + h = hashtbl_create(ht_size, + HASHTBL_MAX_LOAD_FACTOR, + 1, + 0, + hashtbl_string_hash, hashtbl_string_equals, + NULL, NULL, + NULL, NULL, NULL); + CUT_ASSERT_NOT_NULL(h); CUT_ASSERT_EQUAL(0, hashtbl_count(h)); + for (i = 0; i < NELEMENTS(keys); i++) { - int ss; /* same string */ CUT_ASSERT_EQUAL(0, hashtbl_insert(h, keys[i], vals[i])); CUT_ASSERT_EQUAL(vals[i], hashtbl_lookup(h, keys[i])); - ss = strcmp(vals[i], - (char *)hashtbl_lookup(h, keys[i])) == 0; - CUT_ASSERT_TRUE(ss); + CUT_ASSERT_TRUE(STREQ(vals[i], (char *)hashtbl_lookup(h, keys[i]))); } /* Iteration order should reflect insertion order. */ i = 3; - hashtbl_iter_init(h, &iter); + hashtbl_iter_init(h, &iter, HASHTBL_FORWARD_ITERATOR); while (hashtbl_iter_next(h, &iter)) { CUT_ASSERT_EQUAL(keys[i-1], iter.key); CUT_ASSERT_EQUAL(vals[i-1], iter.val); i--; } - CUT_ASSERT_EQUAL(0, i); + /* Reverse iteration. */ + + i = 0; + hashtbl_iter_init(h, &iter, HASHTBL_REVERSE_ITERATOR); + while (hashtbl_iter_next(h, &iter)) { + CUT_ASSERT_EQUAL(keys[i], iter.key); + CUT_ASSERT_EQUAL(vals[i], iter.val); + i++; + } + CUT_ASSERT_EQUAL(3, i); + hashtbl_clear(h); hashtbl_delete(h); return 0; @@ -734,12 +790,13 @@ static int test18(void) int i; struct hashtbl *h; - h = hashtbl_new(ht_size, HASHTBL_MAX_LOAD_FACTOR, - HASHTBL_AUTO_RESIZE, - HASHTBL_LRU_ORDER, - hashtbl_direct_hash, hashtbl_direct_equals, - NULL, NULL, - NULL, NULL); + h = hashtbl_create(ht_size, + HASHTBL_MAX_LOAD_FACTOR, + 1, + 0, + hashtbl_direct_hash, hashtbl_direct_equals, + NULL, NULL, + NULL, NULL, NULL); CUT_ASSERT_NOT_NULL(h); CUT_ASSERT_EQUAL(0, hashtbl_count(h)); @@ -770,12 +827,13 @@ static int test19(void) static int keys[] = { 100, 200, 300 }; struct hashtbl_iter iter; - h = hashtbl_new(ht_size, HASHTBL_MAX_LOAD_FACTOR, - HASHTBL_AUTO_RESIZE, - HASHTBL_LRU_ORDER, - hashtbl_direct_hash, hashtbl_direct_equals, - NULL, NULL, - NULL, NULL); + h = hashtbl_create(ht_size, + HASHTBL_MAX_LOAD_FACTOR, + 1, + 0, + hashtbl_direct_hash, hashtbl_direct_equals, + NULL, NULL, + NULL, NULL, NULL); CUT_ASSERT_NOT_NULL(h); CUT_ASSERT_EQUAL(0, hashtbl_count(h)); @@ -786,7 +844,7 @@ static int test19(void) CUT_ASSERT_EQUAL(3, hashtbl_count(h)); - hashtbl_iter_init(h, &iter); + hashtbl_iter_init(h, &iter, HASHTBL_FORWARD_ITERATOR); CUT_ASSERT_TRUE(hashtbl_iter_next(h, &iter)); CUT_ASSERT_EQUAL(keys[2], *(int *) iter.key); CUT_ASSERT_TRUE(hashtbl_iter_next(h, &iter)); @@ -797,7 +855,7 @@ static int test19(void) CUT_ASSERT_EQUAL(0, hashtbl_remove(h, &keys[0])); CUT_ASSERT_EQUAL(2, hashtbl_count(h)); - hashtbl_iter_init(h, &iter); + hashtbl_iter_init(h, &iter, HASHTBL_FORWARD_ITERATOR); CUT_ASSERT_TRUE(hashtbl_iter_next(h, &iter)); CUT_ASSERT_EQUAL(keys[2], *(int *) iter.key); CUT_ASSERT_TRUE(hashtbl_iter_next(h, &iter)); @@ -806,14 +864,14 @@ static int test19(void) CUT_ASSERT_EQUAL(0, hashtbl_remove(h, &keys[2])); CUT_ASSERT_EQUAL(1, hashtbl_count(h)); - hashtbl_iter_init(h, &iter); + hashtbl_iter_init(h, &iter, HASHTBL_FORWARD_ITERATOR); CUT_ASSERT_TRUE(hashtbl_iter_next(h, &iter)); CUT_ASSERT_EQUAL(keys[1], *(int *) iter.key); CUT_ASSERT_FALSE(hashtbl_iter_next(h, &iter)); CUT_ASSERT_EQUAL(0, hashtbl_remove(h, &keys[1])); CUT_ASSERT_EQUAL(0, hashtbl_count(h)); - hashtbl_iter_init(h, &iter); + hashtbl_iter_init(h, &iter, HASHTBL_FORWARD_ITERATOR); CUT_ASSERT_FALSE(hashtbl_iter_next(h, &iter)); for (i = 0; i < (int)NELEMENTS(keys); i++) { @@ -823,7 +881,7 @@ static int test19(void) CUT_ASSERT_EQUAL(0, hashtbl_remove(h, &keys[0])); CUT_ASSERT_EQUAL(0, hashtbl_insert(h, &keys[0], NULL)); - hashtbl_iter_init(h, &iter); + hashtbl_iter_init(h, &iter, HASHTBL_FORWARD_ITERATOR); CUT_ASSERT_TRUE(hashtbl_iter_next(h, &iter)); CUT_ASSERT_EQUAL(keys[0], *(int *) iter.key); CUT_ASSERT_TRUE(hashtbl_iter_next(h, &iter)); @@ -835,7 +893,7 @@ static int test19(void) CUT_ASSERT_EQUAL(0, hashtbl_remove(h, &keys[1])); CUT_ASSERT_EQUAL(0, hashtbl_insert(h, &keys[1], NULL)); - hashtbl_iter_init(h, &iter); + hashtbl_iter_init(h, &iter, HASHTBL_FORWARD_ITERATOR); CUT_ASSERT_TRUE(hashtbl_iter_next(h, &iter)); CUT_ASSERT_EQUAL(keys[1], *(int *) iter.key); CUT_ASSERT_TRUE(hashtbl_iter_next(h, &iter)); @@ -858,12 +916,13 @@ static int test20(void) static int keys[] = { 100, 200, 300 }; struct hashtbl_iter iter; - h = hashtbl_new(ht_size, HASHTBL_MAX_LOAD_FACTOR, - HASHTBL_AUTO_RESIZE, - HASHTBL_MRU_ORDER, - hashtbl_direct_hash, hashtbl_direct_equals, - NULL, NULL, - NULL, NULL); + h = hashtbl_create(ht_size, + HASHTBL_MAX_LOAD_FACTOR, + 1, + 1, + hashtbl_direct_hash, hashtbl_direct_equals, + NULL, NULL, + NULL, NULL, NULL); CUT_ASSERT_NOT_NULL(h); CUT_ASSERT_EQUAL(0, hashtbl_count(h)); @@ -877,7 +936,7 @@ static int test20(void) CUT_ASSERT_NOT_NULL(hashtbl_lookup(h, &keys[1])); CUT_ASSERT_NOT_NULL(hashtbl_lookup(h, &keys[0])); - hashtbl_iter_init(h, &iter); + hashtbl_iter_init(h, &iter, HASHTBL_FORWARD_ITERATOR); CUT_ASSERT_TRUE(hashtbl_iter_next(h, &iter)); CUT_ASSERT_EQUAL(keys[0], *(int *) iter.key); CUT_ASSERT_TRUE(hashtbl_iter_next(h, &iter)); @@ -887,7 +946,7 @@ static int test20(void) CUT_ASSERT_FALSE(hashtbl_iter_next(h, &iter)); CUT_ASSERT_NOT_NULL(hashtbl_lookup(h, &keys[1])); - hashtbl_iter_init(h, &iter); + hashtbl_iter_init(h, &iter, HASHTBL_FORWARD_ITERATOR); CUT_ASSERT_TRUE(hashtbl_iter_next(h, &iter)); CUT_ASSERT_EQUAL(keys[1], *(int *) iter.key); CUT_ASSERT_TRUE(hashtbl_iter_next(h, &iter)); @@ -897,12 +956,12 @@ static int test20(void) CUT_ASSERT_FALSE(hashtbl_iter_next(h, &iter)); CUT_ASSERT_NOT_NULL(hashtbl_lookup(h, &keys[1])); - hashtbl_iter_init(h, &iter); + hashtbl_iter_init(h, &iter, HASHTBL_FORWARD_ITERATOR); CUT_ASSERT_TRUE(hashtbl_iter_next(h, &iter)); CUT_ASSERT_EQUAL(keys[1], *(int *) iter.key); CUT_ASSERT_NOT_NULL(hashtbl_lookup(h, &keys[0])); - hashtbl_iter_init(h, &iter); + hashtbl_iter_init(h, &iter, HASHTBL_FORWARD_ITERATOR); CUT_ASSERT_TRUE(hashtbl_iter_next(h, &iter)); CUT_ASSERT_EQUAL(keys[0], *(int *) iter.key); CUT_ASSERT_TRUE(hashtbl_iter_next(h, &iter)); @@ -912,17 +971,17 @@ static int test20(void) CUT_ASSERT_FALSE(hashtbl_iter_next(h, &iter)); CUT_ASSERT_EQUAL(0, hashtbl_insert(h, &keys[2], NULL)); - hashtbl_iter_init(h, &iter); - CUT_ASSERT_TRUE(hashtbl_iter_next(h, &iter)); - CUT_ASSERT_EQUAL(keys[2], *(int *) iter.key); + hashtbl_iter_init(h, &iter, HASHTBL_FORWARD_ITERATOR); CUT_ASSERT_TRUE(hashtbl_iter_next(h, &iter)); CUT_ASSERT_EQUAL(keys[0], *(int *) iter.key); CUT_ASSERT_TRUE(hashtbl_iter_next(h, &iter)); CUT_ASSERT_EQUAL(keys[1], *(int *) iter.key); + CUT_ASSERT_TRUE(hashtbl_iter_next(h, &iter)); + CUT_ASSERT_EQUAL(keys[2], *(int *) iter.key); CUT_ASSERT_FALSE(hashtbl_iter_next(h, &iter)); CUT_ASSERT_EQUAL(0, hashtbl_remove(h, &keys[2])); - hashtbl_iter_init(h, &iter); + hashtbl_iter_init(h, &iter, HASHTBL_FORWARD_ITERATOR); CUT_ASSERT_TRUE(hashtbl_iter_next(h, &iter)); CUT_ASSERT_EQUAL(keys[0], *(int *) iter.key); CUT_ASSERT_TRUE(hashtbl_iter_next(h, &iter)); @@ -930,7 +989,7 @@ static int test20(void) CUT_ASSERT_FALSE(hashtbl_iter_next(h, &iter)); CUT_ASSERT_NOT_NULL(hashtbl_lookup(h, &keys[1])); - hashtbl_iter_init(h, &iter); + hashtbl_iter_init(h, &iter, HASHTBL_FORWARD_ITERATOR); CUT_ASSERT_TRUE(hashtbl_iter_next(h, &iter)); CUT_ASSERT_EQUAL(keys[1], *(int *) iter.key); CUT_ASSERT_TRUE(hashtbl_iter_next(h, &iter)); @@ -941,6 +1000,307 @@ static int test20(void) return 0; } +/* Test LRU eviction behaviour. */ + +static int test21_remove_eldest_1(const struct hashtbl *h, unsigned long count) +{ + UNUSED_PARAMETER(h); + CUT_ASSERT_EQUAL(1, count); + return 1; +} + +static int test21(void) +{ + struct hashtbl *h; + static int keys[] = { 100, 200, 300 }; + + h = hashtbl_create(ht_size, + HASHTBL_MAX_LOAD_FACTOR, + 1, + 0, + hashtbl_direct_hash, hashtbl_direct_equals, + NULL, NULL, + NULL, NULL, + test21_remove_eldest_1); + + CUT_ASSERT_NOT_NULL(h); + CUT_ASSERT_EQUAL(0, hashtbl_insert(h, &keys[0], &keys[0])); + CUT_ASSERT_EQUAL(0, hashtbl_count(h)); + CUT_ASSERT_EQUAL(0, hashtbl_insert(h, &keys[1], &keys[1])); + CUT_ASSERT_EQUAL(0, hashtbl_count(h)); + CUT_ASSERT_EQUAL(0, hashtbl_insert(h, &keys[2], &keys[2])); + CUT_ASSERT_EQUAL(0, hashtbl_count(h)); + + hashtbl_delete(h); + return 0; +} + +static int test22_remove_eldest_1(const struct hashtbl *h, unsigned long count) +{ + UNUSED_PARAMETER(h); + UNUSED_PARAMETER(count); + if (count > 3) + return 1; + else + return 0; +} + +static int test22(void) +{ + struct hashtbl *h; + static int keys[] = { 100, 200, 300, 400, 500, 600 }; + struct hashtbl_iter iter; + + h = hashtbl_create(ht_size, + HASHTBL_MAX_LOAD_FACTOR, + 1, + 0, + hashtbl_direct_hash, hashtbl_direct_equals, + NULL, NULL, + NULL, NULL, + test22_remove_eldest_1); + + CUT_ASSERT_NOT_NULL(h); + CUT_ASSERT_EQUAL(0, hashtbl_insert(h, &keys[0], &keys[0])); + CUT_ASSERT_EQUAL(0, hashtbl_insert(h, &keys[1], &keys[1])); + CUT_ASSERT_EQUAL(0, hashtbl_insert(h, &keys[2], &keys[2])); + CUT_ASSERT_EQUAL(0, hashtbl_insert(h, &keys[3], &keys[3])); + CUT_ASSERT_EQUAL(0, hashtbl_insert(h, &keys[4], &keys[4])); + CUT_ASSERT_EQUAL(0, hashtbl_insert(h, &keys[5], &keys[5])); + CUT_ASSERT_EQUAL(3, hashtbl_count(h)); + + hashtbl_iter_init(h, &iter, HASHTBL_FORWARD_ITERATOR); + CUT_ASSERT_TRUE(hashtbl_iter_next(h, &iter)); + CUT_ASSERT_EQUAL(keys[5], *(int *) iter.key); + CUT_ASSERT_TRUE(hashtbl_iter_next(h, &iter)); + CUT_ASSERT_EQUAL(keys[4], *(int *) iter.key); + CUT_ASSERT_TRUE(hashtbl_iter_next(h, &iter)); + CUT_ASSERT_EQUAL(keys[3], *(int *) iter.key); + CUT_ASSERT_FALSE(hashtbl_iter_next(h, &iter)); + + hashtbl_delete(h); + return 0; +} + +static int test23_remove_eldest_1(const struct hashtbl *h, unsigned long count) +{ + UNUSED_PARAMETER(h); + UNUSED_PARAMETER(count); + if (count > 3) + return 1; + else + return 0; +} + +/* Test MRU eviction behaviour. */ + +static int test23(void) +{ + struct hashtbl *h; + static int keys[] = { 100, 200, 300, 400, 500, 600 }; + struct hashtbl_iter iter; + + h = hashtbl_create(ht_size, + HASHTBL_MAX_LOAD_FACTOR, + 1, + 1, + hashtbl_direct_hash, hashtbl_direct_equals, + NULL, NULL, + NULL, NULL, + test23_remove_eldest_1); + + CUT_ASSERT_NOT_NULL(h); + CUT_ASSERT_EQUAL(0, hashtbl_insert(h, &keys[0], &keys[0])); + CUT_ASSERT_EQUAL(0, hashtbl_insert(h, &keys[1], &keys[1])); + CUT_ASSERT_EQUAL(0, hashtbl_insert(h, &keys[2], &keys[2])); + CUT_ASSERT_EQUAL(0, hashtbl_insert(h, &keys[3], &keys[3])); + CUT_ASSERT_EQUAL(0, hashtbl_insert(h, &keys[4], &keys[4])); + CUT_ASSERT_EQUAL(0, hashtbl_insert(h, &keys[5], &keys[5])); + CUT_ASSERT_EQUAL(3, hashtbl_count(h)); + + CUT_ASSERT_NULL(hashtbl_lookup(h, &keys[0])); + CUT_ASSERT_NULL(hashtbl_lookup(h, &keys[1])); + CUT_ASSERT_NULL(hashtbl_lookup(h, &keys[2])); + CUT_ASSERT_NOT_NULL(hashtbl_lookup(h, &keys[3])); + CUT_ASSERT_NOT_NULL(hashtbl_lookup(h, &keys[4])); + CUT_ASSERT_NOT_NULL(hashtbl_lookup(h, &keys[5])); + + hashtbl_iter_init(h, &iter, HASHTBL_FORWARD_ITERATOR); + CUT_ASSERT_TRUE(hashtbl_iter_next(h, &iter)); + CUT_ASSERT_EQUAL(keys[5], *(int *) iter.key); + CUT_ASSERT_TRUE(hashtbl_iter_next(h, &iter)); + CUT_ASSERT_EQUAL(keys[4], *(int *) iter.key); + CUT_ASSERT_TRUE(hashtbl_iter_next(h, &iter)); + CUT_ASSERT_EQUAL(keys[3], *(int *) iter.key); + CUT_ASSERT_FALSE(hashtbl_iter_next(h, &iter)); + + CUT_ASSERT_NOT_NULL(hashtbl_lookup(h, &keys[4])); + hashtbl_iter_init(h, &iter, HASHTBL_FORWARD_ITERATOR); + CUT_ASSERT_TRUE(hashtbl_iter_next(h, &iter)); + CUT_ASSERT_EQUAL(keys[4], *(int *) iter.key); + CUT_ASSERT_TRUE(hashtbl_iter_next(h, &iter)); + CUT_ASSERT_EQUAL(keys[5], *(int *) iter.key); + CUT_ASSERT_TRUE(hashtbl_iter_next(h, &iter)); + CUT_ASSERT_EQUAL(keys[3], *(int *) iter.key); + CUT_ASSERT_FALSE(hashtbl_iter_next(h, &iter)); + + hashtbl_delete(h); + return 0; +} + +static void * test24_malloc(size_t n) +{ + UNUSED_PARAMETER(n); + return 0; +} + +/* Test that creation fails. */ + +static int test24(void) +{ + struct hashtbl *h; + + h = hashtbl_create(ht_size, + HASHTBL_MAX_LOAD_FACTOR, + 1, + 0, + hashtbl_direct_hash, hashtbl_direct_equals, + NULL, NULL, + test24_malloc, NULL, + NULL); + + CUT_ASSERT_NULL(h); + + return 0; +} + +static void * test25_malloc(size_t n) +{ + static int invoke_count = 0; + switch (++invoke_count) { + case 1: + return malloc(n); + default: + return 0; + } +} + +static void test25_free(void *p) +{ + free(p); +} + +/* Test that table allocation in hashtbl_create fails. */ + +static int test25(void) +{ + struct hashtbl *h; + + h = hashtbl_create(ht_size, + HASHTBL_MAX_LOAD_FACTOR, + 1, + 0, + hashtbl_direct_hash, hashtbl_direct_equals, + NULL, NULL, + test25_malloc, test25_free, + NULL); + + CUT_ASSERT_NULL(h); + + return 0; +} + +static void * test26_malloc(size_t n) +{ + static int invoke_count = 0; + switch (++invoke_count) { + case 1: + return malloc(n); + case 2: + return malloc(n); + default: + return 0; + } +} + +static void test26_free(void *p) +{ + free(p); +} + +/* Test that insertion allocation fails. */ + +static int test26(void) +{ + struct hashtbl *h; + static int keys[] = { 100, 200, 300, 400, 500, 600 }; + + h = hashtbl_create(ht_size, + HASHTBL_MAX_LOAD_FACTOR, + 1, + 0, + hashtbl_direct_hash, hashtbl_direct_equals, + NULL, NULL, + test26_malloc, test26_free, + NULL); + + CUT_ASSERT_NOT_NULL(h); + + CUT_ASSERT_EQUAL(1, hashtbl_insert(h, &keys[0], &keys[0])); + CUT_ASSERT_EQUAL(1, hashtbl_insert(h, &keys[1], &keys[1])); + CUT_ASSERT_EQUAL(1, hashtbl_insert(h, &keys[2], &keys[2])); + CUT_ASSERT_EQUAL(1, hashtbl_insert(h, &keys[3], &keys[3])); + CUT_ASSERT_EQUAL(1, hashtbl_insert(h, &keys[4], &keys[4])); + CUT_ASSERT_EQUAL(1, hashtbl_insert(h, &keys[5], &keys[5])); + CUT_ASSERT_EQUAL(0, hashtbl_count(h)); + + hashtbl_delete(h); + return 0; +} + +static void * test27_malloc(size_t n) +{ + static int invoke_count = 0; + + if (++invoke_count >= 5) { + return 0; + } else { + return malloc(n); + } +} + +static void test27_free(void *p) +{ + free(p); +} + +/* Test that resize allocation fails. */ + +static int test27(void) +{ + struct hashtbl *h; + static int keys[] = { 100, 200, 300, 400, 500, 600 }; + + h = hashtbl_create(4, + HASHTBL_MAX_LOAD_FACTOR, + 1, + 0, + hashtbl_direct_hash, hashtbl_direct_equals, + NULL, NULL, + test27_malloc, test27_free, + NULL); + + CUT_ASSERT_NOT_NULL(h); + + CUT_ASSERT_EQUAL(0, hashtbl_insert(h, &keys[0], &keys[0])); + CUT_ASSERT_EQUAL(0, hashtbl_insert(h, &keys[1], &keys[1])); + CUT_ASSERT_EQUAL(1, hashtbl_resize(h, 8)); + CUT_ASSERT_EQUAL(2, hashtbl_count(h)); + + hashtbl_delete(h); + return 0; +} + CUT_BEGIN_TEST_HARNESS CUT_RUN_TEST(test1); CUT_RUN_TEST(test2); @@ -950,9 +1310,6 @@ CUT_RUN_TEST(test5); CUT_RUN_TEST(test6); CUT_RUN_TEST(test7); CUT_RUN_TEST(test8); -CUT_RUN_TEST(test9); -CUT_RUN_TEST(test10); -CUT_RUN_TEST(test11); CUT_RUN_TEST(test12); CUT_RUN_TEST(test13); CUT_RUN_TEST(test14); @@ -962,4 +1319,11 @@ CUT_RUN_TEST(test17); CUT_RUN_TEST(test18); CUT_RUN_TEST(test19); CUT_RUN_TEST(test20); +CUT_RUN_TEST(test21); +CUT_RUN_TEST(test22); +CUT_RUN_TEST(test23); +CUT_RUN_TEST(test24); +CUT_RUN_TEST(test25); +CUT_RUN_TEST(test26); +CUT_RUN_TEST(test27); CUT_END_TEST_HARNESS