From: Mike D. Lowis Date: Thu, 1 Oct 2015 15:08:08 +0000 (-0400) Subject: Added tests for new hashtable implementation. Fully tested insert and retrieval functions X-Git-Url: https://git.mdlowis.com/?a=commitdiff_plain;h=0b074492a5fa2785f9f1001b8c093b280402458f;p=archive%2Fcarl.git Added tests for new hashtable implementation. Fully tested insert and retrieval functions --- diff --git a/build.ninja b/build.ninja index dc8a265..306242d 100644 --- a/build.ninja +++ b/build.ninja @@ -1,9 +1,9 @@ CC = gcc DEPSUFFIX = .d CPPFLAGS = -Isource/ -Imodules/atf/source -CFLAGS = -g -O3 -Wall -Wextra --std=c99 --pedantic +CFLAGS = -g -O3 -Wall -Wextra --std=c99 --pedantic --coverage LD = gcc -LDFLAGS = +LDFLAGS = --coverage AR = ar ARFLAGS = rcs @@ -54,8 +54,9 @@ build tests/refcount.o: cc tests/refcount.c build tests/utf/test_unicodedata.o: cc tests/utf/test_unicodedata.c build tests/data/bstree.o: cc tests/data/bstree.c build tests/data/slist.o: cc tests/data/slist.c +build tests/data/hash.o: cc tests/data/hash.c build tests/main.o: cc tests/main.c build modules/atf/source/atf.o: cc modules/atf/source/atf.c -build test_libc: ld tests/refcount.o tests/utf/test_unicodedata.o tests/data/bstree.o tests/data/slist.o tests/main.o modules/atf/source/atf.o libcarl.a -build Unit$ Tests: command test_libc +build test_libc: ld tests/refcount.o tests/utf/test_unicodedata.o tests/data/bstree.o tests/data/slist.o tests/data/hash.o tests/main.o modules/atf/source/atf.o libcarl.a +build Tests: command test_libc CMD = ./test_libc diff --git a/source/carl.h b/source/carl.h index a11621c..b5985d1 100644 --- a/source/carl.h +++ b/source/carl.h @@ -16,6 +16,8 @@ #include #include +typedef unsigned int uint; + #ifndef nil #define nil NULL #endif diff --git a/source/data/hash.c b/source/data/hash.c index 451ea83..7d613fb 100644 --- a/source/data/hash.c +++ b/source/data/hash.c @@ -34,8 +34,9 @@ static void find_entry(hash_t* hash, hash_entry_t** parent, hash_entry_t** curre static void rehash(hash_t* hash) { unsigned int oldcount = hash->bkt_count++; - hash_entry_t** oldbuckets = (hash_entry_t**)calloc(sizeof(hash_entry_t*), num_buckets(hash->bkt_count)); + hash_entry_t** oldbuckets = hash->buckets; hash->buckets = (hash_entry_t**)calloc(sizeof(hash_entry_t*), num_buckets(hash->bkt_count)); + hash->size = 0; /* Iterate over all of the old buckets */ for (unsigned int i = 0; i < num_buckets(oldcount); i++) { hash_entry_t* node = oldbuckets[i]; @@ -66,28 +67,40 @@ void hash_deinit(hash_t* hash) free(hash->buckets); } +size_t hash_size(hash_t* hash) +{ + return hash->size; +} + bool hash_set(hash_t* hash, hash_entry_t* entry) { if (hash->size >= num_buckets(hash->bkt_count)) rehash(hash); entry->hash = hash->hashfn(entry); - unsigned int index = (entry->hash % num_buckets(hash->bkt_count)); - hash_entry_t* parent = NULL; - hash_entry_t* node = hash->buckets[index]; + unsigned int index = (entry->hash % num_buckets(hash->bkt_count)); + hash_entry_t* parent = NULL; + hash_entry_t* node = hash->buckets[index]; + hash_entry_t* deadite = NULL; find_entry(hash, &parent, &node, entry); - if (node != NULL) { - hash_entry_t* deadite = node; - node = node->next; - entry->next = node; - if (parent != NULL) - parent->next = entry; - else - hash->buckets[index] = entry; - hash->delfn(deadite); - } else { + if ((parent == NULL) && (node == NULL)) { hash->buckets[index] = entry; entry->next = NULL; + hash->size++; + } else if (node == NULL) { + parent->next = entry; + entry->next = NULL; + hash->size++; + } else if (parent == NULL) { + deadite = node; + entry->next = deadite->next; + hash->buckets[index] = entry; + } else { + deadite = node; + entry->next = deadite->next; + parent->next = entry; } + if (deadite != NULL) + hash->delfn(deadite); return true; } diff --git a/source/data/hash.h b/source/data/hash.h index 147c0e0..3a22bab 100644 --- a/source/data/hash.h +++ b/source/data/hash.h @@ -32,6 +32,8 @@ void hash_init(hash_t* hash, hash_hashfn_t hashfn, hash_cmpfn_t cmpfn, hash_free void hash_deinit(hash_t* hash); +size_t hash_size(hash_t* hash); + bool hash_set(hash_t* hash, hash_entry_t* entry); hash_entry_t* hash_get(hash_t* hash, hash_entry_t* entry); diff --git a/tests/data/hash.c b/tests/data/hash.c new file mode 100644 index 0000000..fb0883e --- /dev/null +++ b/tests/data/hash.c @@ -0,0 +1,109 @@ + +// Unit Test Framework Includes +#include "atf.h" + +// File To Test +#include +#include + +typedef struct { + hash_entry_t link; + uint val; +} int_node_t; + +static unsigned int hash_func(const hash_entry_t* entry) +{ + int_node_t* node = container_of(entry, int_node_t, link); + return node->val; +} + +static int compare_func(const hash_entry_t* entry1, const hash_entry_t* entry2) +{ + int_node_t* node1 = container_of(entry1, int_node_t, link); + int_node_t* node2 = container_of(entry2, int_node_t, link); + return node1->val - node2->val; +} + +static void delete_func(hash_entry_t* entry) +{ + free(container_of(entry, int_node_t, link)); +} + +//----------------------------------------------------------------------------- +// Begin Unit Tests +//----------------------------------------------------------------------------- +TEST_SUITE(Hash) { + TEST(Verify sequential hash inserts and lookups) + { + uint maxval = 1000000; + hash_t hash; + hash_init(&hash, hash_func, compare_func, delete_func); + for (uint i = 0; i < maxval; i++) + { + int_node_t* entry = (int_node_t*)malloc(sizeof(int_node_t)); + entry->val = i; + hash_set(&hash, &(entry->link)); + CHECK(i+1 == hash_size(&hash)); + CHECK(&(entry->link) == hash_get(&hash, &(entry->link))); + } + for (uint i = 0; i < maxval; i++) + { + int_node_t search = { .val = i }; + hash_entry_t* entry = hash_get(&hash, &(search.link)); + int_node_t* ientry = container_of(entry, int_node_t, link); + CHECK(entry != NULL); + CHECK(ientry != NULL); + CHECK(search.val == ientry->val); + } + hash_deinit(&hash); + } + + TEST(Verify ping pong hash inserts and lookups) + { + uint maxval = 1000000; + hash_t hash; + hash_init(&hash, hash_func, compare_func, delete_func); + for (uint i = 0; i < (maxval/2); i++) { + /* Insert the lower number */ + int_node_t* entry = (int_node_t*)malloc(sizeof(int_node_t)); + entry->val = i; + hash_set(&hash, &(entry->link)); + CHECK(&(entry->link) == hash_get(&hash, &(entry->link))); + /* Insert the higher number */ + entry = (int_node_t*)malloc(sizeof(int_node_t)); + entry->val = maxval - i; + hash_set(&hash, &(entry->link)); + CHECK(&(entry->link) == hash_get(&hash, &(entry->link))); + } + for (uint i = 0; i < (maxval/2); i++) + { + /* Find the lower number */ + int_node_t search = { .val = i }; + hash_entry_t* entry = hash_get(&hash, &(search.link)); + int_node_t* ientry = container_of(entry, int_node_t, link); + CHECK(entry != NULL); + CHECK(search.val == ientry->val); + /* Find the higher number */ + search.val = maxval-i; + entry = hash_get(&hash, &(search.link)); + ientry = container_of(entry, int_node_t, link); + CHECK(entry != NULL); + CHECK(search.val == ientry->val); + } + hash_deinit(&hash); + } + + TEST(Verify random hash inserts and lookups) + { + hash_t hash; + hash_init(&hash, hash_func, compare_func, delete_func); + for (uint i = 0; i < 1000000; i++) + { + int_node_t* entry = (int_node_t*)malloc(sizeof(int_node_t)); + entry->val = (uint)rand(); + hash_set(&hash, &(entry->link)); + CHECK(&(entry->link) == hash_get(&hash, &(entry->link))); + } + hash_deinit(&hash); + } +} diff --git a/tests/main.c b/tests/main.c index 483ec68..b600219 100644 --- a/tests/main.c +++ b/tests/main.c @@ -1,13 +1,18 @@ #include "atf.h" #include +#include int main(int argc, char** argv) { (void)argc; (void)argv; + uint seed = (uint)time(NULL); + srand(seed); + printf("Random Number Generation Seed: %u\n", seed); RUN_EXTERN_TEST_SUITE(RefCount); RUN_EXTERN_TEST_SUITE(SList); RUN_EXTERN_TEST_SUITE(BSTree); + RUN_EXTERN_TEST_SUITE(Hash); RUN_EXTERN_TEST_SUITE(UnicodeData); return (PRINT_TEST_RESULTS()); }