diff --git a/benchmark.c b/benchmark.c index b6cf32f..eed2cb5 100644 --- a/benchmark.c +++ b/benchmark.c @@ -66,6 +66,7 @@ main(int argc, char **argv) fprintf(stderr, " w: weight-balanced tree\n"); fprintf(stderr, " S: skiplist\n"); fprintf(stderr, " H: hashtable\n"); + fprintf(stderr, " 2: hashtable 2\n"); fprintf(stderr, "input: text file consisting of newline-separated keys\n"); exit(EXIT_FAILURE); } @@ -129,7 +130,7 @@ main(int argc, char **argv) comp_count, hash_count); total_comp += comp_count; comp_count = 0; total_hash += hash_count; hash_count = 0; - if (type != 'H' && type != 'S') { + if (type != 'H' && type != '2' && type != 'S') { tree_base *tree = dict_private(dct); printf("insert rotations: %zu\n", tree->rotation_count); total_rotations += tree->rotation_count; @@ -193,7 +194,7 @@ main(int argc, char **argv) comp_count, hash_count); total_comp += comp_count; comp_count = 0; total_hash += hash_count; hash_count = 0; - if (type != 'H' && type != 'S') { + if (type != 'H' && type != '2' && type != 'S') { tree_base *tree = dict_private(dct); printf("search rotations: %zu\n", tree->rotation_count); total_rotations += tree->rotation_count; @@ -229,7 +230,7 @@ main(int argc, char **argv) comp_count, hash_count); total_comp += comp_count; comp_count = 0; total_hash += hash_count; hash_count = 0; - if (type != 'H' && type != 'S') { + if (type != 'H' && type != '2' && type != 'S') { tree_base *tree = dict_private(dct); printf("remove rotations: %zu\n", tree->rotation_count); total_rotations += tree->rotation_count; @@ -248,7 +249,7 @@ main(int argc, char **argv) (total.tv_sec * 1000000 + total.tv_usec) * 1e-6, total_comp, total_hash); - if (type != 'H' && type != 'S') { + if (type != 'H' && type != '2' && type != 'S') { printf(" total rotations: %zu\n", total_rotations); } @@ -296,6 +297,10 @@ create_dictionary(char type, const char **container_name) *container_name = "ht"; return hashtable_dict_new(cmp_func, hash_func, key_str_free, HASHTABLE_SIZE); + case '2': + *container_name = "h2"; + return hashtable2_dict_new(cmp_func, hash_func, key_str_free, HASHTABLE_SIZE); + default: quit("type must be one of h, p, r, t, s, w or H"); } diff --git a/demo.c b/demo.c index 151ce17..ca3f289 100644 --- a/demo.c +++ b/demo.c @@ -72,6 +72,11 @@ main(int argc, char **argv) dict_str_hash, key_val_free, HSIZE); break; + case '2': + dct = hashtable2_dict_new((dict_compare_func)strcmp, + dict_str_hash, + key_val_free, HSIZE); + break; default: quit("type must be one of h, p, r, t, s, w, or H"); } diff --git a/include/dict.h b/include/dict.h index 3c30d2a..8a12c17 100644 --- a/include/dict.h +++ b/include/dict.h @@ -197,6 +197,7 @@ unsigned dict_str_hash(const void* str); END_DECL #include "hashtable.h" +#include "hashtable2.h" #include "hb_tree.h" #include "pr_tree.h" #include "rb_tree.h" diff --git a/include/hashtable2.h b/include/hashtable2.h new file mode 100644 index 0000000..d4def60 --- /dev/null +++ b/include/hashtable2.h @@ -0,0 +1,79 @@ +/* + * libdict -- open-addressing hash-table interface. + * + * Copyright (c) 2001-2014, Farooq Mela + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef _HASHTABLE2_H_ +#define _HASHTABLE2_H_ + +#include "dict.h" + +BEGIN_DECL + +typedef struct hashtable2 hashtable2; + +hashtable2* hashtable2_new(dict_compare_func cmp_func, + dict_hash_func hash_func, + dict_delete_func del_func, unsigned size); +dict* hashtable2_dict_new(dict_compare_func cmp_func, + dict_hash_func hash_func, + dict_delete_func del_func, unsigned size); +size_t hashtable2_free(hashtable2* table); +hashtable2* hashtable2_clone(hashtable2* table, + dict_key_datum_clone_func clone_func); + +void** hashtable2_insert(hashtable2* table, void* key, bool* inserted); +void* hashtable2_search(hashtable2* table, const void* key); +bool hashtable2_remove(hashtable2* table, const void* key); +size_t hashtable2_clear(hashtable2* table); +size_t hashtable2_traverse(hashtable2* table, dict_visit_func visit); +size_t hashtable2_count(const hashtable2* table); +size_t hashtable2_size(const hashtable2* table); +size_t hashtable2_slots_used(const hashtable2* table); +bool hashtable2_verify(const hashtable2* table); +bool hashtable2_resize(hashtable2* table, unsigned size); + +typedef struct hashtable2_itor hashtable2_itor; + +hashtable2_itor* hashtable2_itor_new(hashtable2* table); +dict_itor* hashtable2_dict_itor_new(hashtable2* table); +void hashtable2_itor_free(hashtable2_itor* itor); + +bool hashtable2_itor_valid(const hashtable2_itor* itor); +void hashtable2_itor_invalidate(hashtable2_itor* itor); +bool hashtable2_itor_next(hashtable2_itor* itor); +bool hashtable2_itor_prev(hashtable2_itor* itor); +bool hashtable2_itor_nextn(hashtable2_itor* itor, size_t count); +bool hashtable2_itor_prevn(hashtable2_itor* itor, size_t count); +bool hashtable2_itor_first(hashtable2_itor* itor); +bool hashtable2_itor_last(hashtable2_itor* itor); +bool hashtable2_itor_search(hashtable2_itor* itor, const void* key); +const void* hashtable2_itor_key(const hashtable2_itor* itor); +void** hashtable2_itor_data(hashtable2_itor* itor); +bool hashtable2_itor_remove(hashtable2_itor* itor); + +END_DECL + +#endif /* !_HASHTABLE2_H_ */ diff --git a/src/hashtable2.c b/src/hashtable2.c new file mode 100644 index 0000000..2ea8ed9 --- /dev/null +++ b/src/hashtable2.c @@ -0,0 +1,628 @@ +/* + * libdict -- chained hash-table, with chains sorted by hash, implementation. + * + * Copyright (c) 2001-2014, Farooq Mela + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/* + * cf. [Gonnet 1984], [Knuth 1998] + */ + +#include "hashtable2.h" + +#include +#include /* For memset() */ +#include "dict_private.h" +#include "hashtable_common.h" + +typedef struct hash_node hash_node; + +struct hash_node { + void* key; + void* datum; + unsigned hash; /* Untruncated hash value. */ + bool occupied; +}; + +struct hashtable2 { + unsigned size; + size_t count; + dict_compare_func cmp_func; + dict_hash_func hash_func; + dict_delete_func del_func; + hash_node* table; +}; + +struct hashtable2_itor { + hashtable2* table; + int slot; +}; + +static dict_vtable hashtable2_vtable = { + (dict_inew_func) hashtable2_dict_itor_new, + (dict_dfree_func) hashtable2_free, + (dict_insert_func) hashtable2_insert, + (dict_search_func) hashtable2_search, + (dict_search_func) NULL,/* search_le: not implemented */ + (dict_search_func) NULL,/* search_lt: not implemented */ + (dict_search_func) NULL,/* search_ge: not implemented */ + (dict_search_func) NULL,/* search_gt: not implemented */ + (dict_remove_func) hashtable2_remove, + (dict_clear_func) hashtable2_clear, + (dict_traverse_func) hashtable2_traverse, + (dict_count_func) hashtable2_count, + (dict_verify_func) hashtable2_verify, + (dict_clone_func) hashtable2_clone, +}; + +static itor_vtable hashtable2_itor_vtable = { + (dict_ifree_func) hashtable2_itor_free, + (dict_valid_func) hashtable2_itor_valid, + (dict_invalidate_func) hashtable2_itor_invalidate, + (dict_next_func) hashtable2_itor_next, + (dict_prev_func) hashtable2_itor_prev, + (dict_nextn_func) hashtable2_itor_nextn, + (dict_prevn_func) hashtable2_itor_prevn, + (dict_first_func) hashtable2_itor_first, + (dict_last_func) hashtable2_itor_last, + (dict_key_func) hashtable2_itor_key, + (dict_data_func) hashtable2_itor_data, + (dict_isearch_func) hashtable2_itor_search, + (dict_isearch_func) NULL,/* itor_search_le: not implemented */ + (dict_isearch_func) NULL,/* itor_search_lt: not implemented */ + (dict_isearch_func) NULL,/* itor_search_ge: not implemented */ + (dict_isearch_func) NULL,/* itor_search_gt: not implemented */ + (dict_iremove_func) NULL,/* hashtable2_itor_remove not implemented yet */ + (dict_icompare_func) NULL,/* hashtable2_itor_compare not implemented yet */ +}; + +hashtable2* +hashtable2_new(dict_compare_func cmp_func, dict_hash_func hash_func, + dict_delete_func del_func, unsigned initial_size) +{ + ASSERT(hash_func != NULL); + ASSERT(initial_size != 0); + + hashtable2* table = MALLOC(sizeof(*table)); + if (table) { + table->size = dict_prime_geq(initial_size); + table->table = MALLOC(table->size * sizeof(hash_node)); + if (!table->table) { + FREE(table); + return NULL; + } + memset(table->table, 0, table->size * sizeof(hash_node)); + table->cmp_func = cmp_func ? cmp_func : dict_ptr_cmp; + table->hash_func = hash_func; + table->del_func = del_func; + table->count = 0; + } + return table; +} + +hashtable2* +hashtable2_clone(hashtable2* table, dict_key_datum_clone_func clone_func) +{ + ASSERT(table != NULL); + + hashtable2* clone = hashtable2_new(table->cmp_func, table->hash_func, + table->del_func, table->size); + if (!clone) { + return NULL; + } + memcpy(clone->table, table->table, sizeof(hash_node) * table->size); + clone->count = table->count; + if (clone_func) { + for (hash_node *node = clone->table, *end = clone->table + table->size; node != end; ++node) { + if (node->occupied) { + clone_func(&node->key, &node->datum); + } + } + } + return clone; +} + +dict* +hashtable2_dict_new(dict_compare_func cmp_func, dict_hash_func hash_func, + dict_delete_func del_func, unsigned size) +{ + ASSERT(hash_func != NULL); + ASSERT(size != 0); + + dict* dct = MALLOC(sizeof(*dct)); + if (dct) { + dct->_object = hashtable2_new(cmp_func, hash_func, del_func, size); + if (!dct->_object) { + FREE(dct); + return NULL; + } + dct->_vtable = &hashtable2_vtable; + } + return dct; +} + +size_t +hashtable2_free(hashtable2* table) +{ + ASSERT(table != NULL); + + size_t count = hashtable2_clear(table); + FREE(table->table); + FREE(table); + return count; +} + +static void** +insert(hashtable2* table, void *key, unsigned hash, bool* inserted) +{ + const unsigned truncated_hash = hash % table->size; + unsigned index = truncated_hash; + do { + hash_node *node = &table->table[index]; + if (!node->occupied) { + node->occupied = true; + node->key = key; + node->datum = NULL; + node->hash = hash; + table->count++; + if (inserted) + *inserted = true; + return &node->datum; + } + if (node->hash == hash && table->cmp_func(key, node->key) == 0) { + if (inserted) + *inserted = false; + return &node->datum; + } + if (++index == table->size) { + index = 0; + } + } while (index != truncated_hash); + /* No room for new element! */ + if (inserted) + *inserted = false; + return NULL; +} + +void** +hashtable2_insert(hashtable2* table, void* key, bool* inserted) +{ + ASSERT(table != NULL); + + if (3*table->count >= 2*table->size) { + /* Load factor too high: resize the table up to the next prime. */ + if (!hashtable2_resize(table, table->size + 1)) { + /* Out of memory, or other error? */ + if (inserted) + *inserted = false; + return NULL; + } + } + void **datum_location = insert(table, key, table->hash_func(key), inserted); + ASSERT(datum_location != NULL); + return datum_location; +} + +void* +hashtable2_search(hashtable2* table, const void* key) +{ + ASSERT(table != NULL); + + const unsigned hash = table->hash_func(key); + const unsigned truncated_hash = hash % table->size; + unsigned index = truncated_hash; + do { + hash_node *node = &table->table[index]; + if (!node->occupied) { + return NULL; + } + if (node->hash == hash && table->cmp_func(key, node->key) == 0) { + return node->datum; + } + if (++index == table->size) { + index = 0; + } + } while (index != truncated_hash); + return NULL; +} + +#if 0 +static int +index_of_node_to_shift(hashtable2* table, unsigned truncated_hash, unsigned index) +{ + int last_index = -1; + do { + hash_node* node = &table->table[index]; + if (!node->occupied) { + break; + } + if (node->hash % table->size == truncated_hash) { + last_index = index; + } + if (++index == table->size) { + index = 0; + } + } while (index != truncated_hash); + return last_index; +} +#endif + +static void +remove_cleanup(hashtable2* table, unsigned truncated_hash, unsigned index) +{ + do { + hash_node* node = &table->table[index]; + if (!node->occupied) { + break; + } + void *datum = node->datum; + node->occupied = false; + table->count--; + + bool inserted = false; + void **datum_location = insert(table, node->key, node->hash, &inserted); + ASSERT(inserted); + ASSERT(datum_location != NULL); + *datum_location = datum; + + if (++index == table->size) { + index = 0; + } + } while (index != truncated_hash); +} + +bool +hashtable2_remove(hashtable2* table, const void* key) +{ + ASSERT(table != NULL); + + const unsigned hash = table->hash_func(key); + const unsigned truncated_hash = hash % table->size; + unsigned index = truncated_hash; + do { + hash_node *node = &table->table[index]; + if (!node->occupied) { + return NULL; + } + if (node->hash == hash && table->cmp_func(key, node->key) == 0) { + if (table->del_func) + table->del_func(node->key, node->datum); + node->occupied = false; + table->count--; + remove_cleanup(table, truncated_hash, (index + 1) % table->size); + return true; + } + if (++index == table->size) { + index = 0; + } + } while (index != truncated_hash); + return false; +} + +size_t +hashtable2_clear(hashtable2* table) +{ + ASSERT(table != NULL); + + hash_node *node = table->table; + hash_node *end = table->table + table->size; + for (; node != end; ++node) { + if (node->occupied) { + if (table->del_func) + table->del_func(node->key, node->datum); + node->occupied = false; + } + } + + const size_t count = table->count; + table->count = 0; + return count; +} + +size_t +hashtable2_traverse(hashtable2* table, dict_visit_func visit) +{ + ASSERT(table != NULL); + ASSERT(visit != NULL); + + size_t count = 0; + hash_node *node = table->table; + hash_node *end = table->table + table->size; + for (; node != end; ++node) { + if (node->occupied) { + ++count; + if (!visit(node->key, node->datum)) + break; + } + } + return count; +} + +size_t +hashtable2_count(const hashtable2* table) +{ + ASSERT(table != NULL); + + return table->count; +} + +size_t +hashtable2_size(const hashtable2* table) +{ + ASSERT(table != NULL); + + return table->size; +} + +size_t +hashtable2_slots_used(const hashtable2* table) +{ + ASSERT(table != NULL); + + return table->count; +} + +bool +hashtable2_resize(hashtable2* table, unsigned new_size) +{ + ASSERT(table != NULL); + ASSERT(new_size != 0); + + new_size = dict_prime_geq(new_size); + if (table->size == new_size) + return true; + + if (table->count > new_size) { + /* The number of records already in hashtable will not fit (must be a reduction in size). */ + return false; + } + + const unsigned old_size = table->size; + const size_t old_count = table->count; + hash_node *const old_table = table->table; + + table->table = MALLOC(new_size * sizeof(hash_node)); + if (!table->table) { + table->table = old_table; + return false; + } + memset(table->table, 0, new_size * sizeof(hash_node)); + table->size = new_size; + table->count = 0; + + for (unsigned i = 0; i < old_size; i++) { + if (old_table[i].occupied) { + bool inserted = false; + void **datum_location = insert(table, old_table[i].key, old_table[i].hash, &inserted); + if (!inserted || !datum_location) { + FREE(table->table); + table->table = old_table; + table->size = old_size; + table->count = old_count; + return false; + } + *datum_location = old_table[i].datum; + } + } + ASSERT(table->count == old_count); + FREE(old_table); + return true; +} + +bool +hashtable2_verify(const hashtable2* table) +{ + ASSERT(table != NULL); + + size_t count = 0; + const hash_node *node = table->table; + const hash_node *end = table->table + table->size; + for (; node != end; ++node) { + if (node->occupied) { + ++count; + /* TODO(farooq): additional validation here? */ + } + } + VERIFY(table->count == count); + return true; +} + +hashtable2_itor* +hashtable2_itor_new(hashtable2* table) +{ + ASSERT(table != NULL); + + hashtable2_itor* itor = MALLOC(sizeof(*itor)); + if (itor) { + itor->table = table; + itor->slot = -1; + } + return itor; +} + +dict_itor* +hashtable2_dict_itor_new(hashtable2* table) +{ + ASSERT(table != NULL); + + dict_itor* itor = MALLOC(sizeof(*itor)); + if (itor) { + if (!(itor->_itor = hashtable2_itor_new(table))) { + FREE(itor); + return NULL; + } + itor->_vtable = &hashtable2_itor_vtable; + } + return itor; +} + +void +hashtable2_itor_free(hashtable2_itor* itor) +{ + ASSERT(itor != NULL); + + FREE(itor); +} + +bool +hashtable2_itor_valid(const hashtable2_itor* itor) +{ + ASSERT(itor != NULL); + + return itor->slot >= 0; +} + +void +hashtable2_itor_invalidate(hashtable2_itor* itor) +{ + ASSERT(itor != NULL); + + itor->slot = -1; +} + +bool +hashtable2_itor_next(hashtable2_itor* itor) +{ + ASSERT(itor != NULL); + + if (itor->slot < 0) + return hashtable2_itor_first(itor); + + while (++itor->slot < (int) itor->table->size) { + if (itor->table->table[itor->slot].occupied) { + return true; + } + } + itor->slot = -1; + return false; +} + +bool +hashtable2_itor_prev(hashtable2_itor* itor) +{ + ASSERT(itor != NULL); + + if (itor->slot < 0) + return hashtable2_itor_last(itor); + + while (itor->slot-- > 0) { + if (itor->table->table[itor->slot].occupied) { + return true; + } + } + ASSERT(itor->slot == -1); + return false; +} + +bool +hashtable2_itor_nextn(hashtable2_itor* itor, size_t count) +{ + ASSERT(itor != NULL); + + while (count--) + if (!hashtable2_itor_next(itor)) + return false; + return itor->slot >= 0; +} + +bool +hashtable2_itor_prevn(hashtable2_itor* itor, size_t count) +{ + ASSERT(itor != NULL); + + while (count--) + if (!hashtable2_itor_prev(itor)) + return false; + return itor->slot >= 0; +} + +bool +hashtable2_itor_first(hashtable2_itor* itor) +{ + ASSERT(itor != NULL); + + for (unsigned slot = 0; slot < itor->table->size; ++slot) { + if (itor->table->table[slot].occupied) { + itor->slot = slot; + return true; + } + } + itor->slot = -1; + return false; +} + +bool +hashtable2_itor_last(hashtable2_itor* itor) +{ + ASSERT(itor != NULL); + + for (unsigned slot = itor->table->size; slot > 0;) { + if (itor->table->table[--slot].occupied) { + itor->slot = slot; + return true; + } + } + itor->slot = -1; + return false; +} + +bool +hashtable2_itor_search(hashtable2_itor* itor, const void* key) +{ + const unsigned hash = itor->table->hash_func(key); + const unsigned truncated_hash = hash % itor->table->size; + unsigned index = truncated_hash; + do { + hash_node *node = &itor->table->table[index]; + if (!node->occupied) { + break; + } + if (node->hash == hash && itor->table->cmp_func(key, node->key) == 0) { + itor->slot = index; + return true; + } + if (++index == itor->table->size) { + index = 0; + } + } while (index != truncated_hash); + itor->slot = -1; + return NULL; +} + +const void* +hashtable2_itor_key(const hashtable2_itor* itor) +{ + ASSERT(itor != NULL); + + return (itor->slot >= 0) ? itor->table->table[itor->slot].key : NULL; +} + +void** +hashtable2_itor_data(hashtable2_itor* itor) +{ + ASSERT(itor != NULL); + + return (itor->slot >= 0) ? &itor->table->table[itor->slot].datum : NULL; +} + diff --git a/unit_tests.c b/unit_tests.c index dbc7e66..b40da66 100644 --- a/unit_tests.c +++ b/unit_tests.c @@ -32,7 +32,9 @@ void test_basic(dict *dct, const struct key_info *keys, const unsigned nkeys, const struct closest_lookup_info *cl_infos, unsigned n_cl_infos); void test_basic_hashtable_1bucket(); +void test_basic_hashtable2_1bucket(); void test_basic_hashtable_nbuckets(); +void test_basic_hashtable2_nbuckets(); void test_basic_height_balanced_tree(); void test_basic_path_reduction_tree(); void test_basic_red_black_tree(); @@ -44,7 +46,9 @@ void test_version_string(); CU_TestInfo basic_tests[] = { TEST_FUNC(test_basic_hashtable_1bucket), + TEST_FUNC(test_basic_hashtable2_1bucket), TEST_FUNC(test_basic_hashtable_nbuckets), + TEST_FUNC(test_basic_hashtable2_nbuckets), TEST_FUNC(test_basic_height_balanced_tree), TEST_FUNC(test_basic_path_reduction_tree), TEST_FUNC(test_basic_red_black_tree), @@ -306,11 +310,16 @@ void test_basic(dict *dct, const struct key_info *keys, const unsigned nkeys, } CU_ASSERT_EQUAL(dict_count(dct), nkeys); - if (dct->_vtable->insert == (dict_insert_func)hashtable_insert) { + if (dct->_vtable->insert == (dict_insert_func)hashtable_insert || + dct->_vtable->insert == (dict_insert_func)hashtable2_insert) { /* Verify that hashtable_resize works as expected. */ dict *clone = dict_clone(dct, NULL); CU_ASSERT_TRUE(dict_verify(dct)); - CU_ASSERT_TRUE(hashtable_resize(dict_private(clone), 3)); + if (dct->_vtable->insert == (dict_insert_func)hashtable_insert) { + CU_ASSERT_TRUE(hashtable_resize(dict_private(clone), 3)); + } else { + CU_ASSERT_TRUE(hashtable2_resize(dict_private(clone), 3)); + } CU_ASSERT_TRUE(dict_verify(dct)); for (unsigned j = 0; j < nkeys; ++j) test_search(clone, NULL, keys[j].key, keys[j].value); @@ -364,7 +373,8 @@ void test_basic(dict *dct, const struct key_info *keys, const unsigned nkeys, } CU_ASSERT_TRUE(key_matched); - if (dct->_vtable->insert != (dict_insert_func)hashtable_insert) { + if (dct->_vtable->insert != (dict_insert_func)hashtable_insert && + dct->_vtable->insert != (dict_insert_func)hashtable2_insert) { if (last_key) { CU_ASSERT_TRUE(strcmp(last_key, dict_itor_key(itor)) < 0); } @@ -392,7 +402,8 @@ void test_basic(dict *dct, const struct key_info *keys, const unsigned nkeys, } CU_ASSERT_TRUE(key_matched); - if (dct->_vtable->insert != (dict_insert_func)hashtable_insert) { + if (dct->_vtable->insert != (dict_insert_func)hashtable_insert && + dct->_vtable->insert != (dict_insert_func)hashtable2_insert) { if (last_key) { CU_ASSERT_TRUE(strcmp(last_key, dict_itor_key(itor)) > 0); } @@ -479,6 +490,14 @@ void test_basic_hashtable_1bucket() keys2, NKEYS2, closest_lookup_infos, NUM_CLOSEST_LOOKUP_INFOS); } +void test_basic_hashtable2_1bucket() +{ + test_basic(hashtable2_dict_new(dict_str_cmp, strhash, NULL, 1), + keys1, NKEYS1, closest_lookup_infos, NUM_CLOSEST_LOOKUP_INFOS); + test_basic(hashtable2_dict_new(dict_str_cmp, strhash, NULL, 1), + keys2, NKEYS2, closest_lookup_infos, NUM_CLOSEST_LOOKUP_INFOS); +} + void test_basic_hashtable_nbuckets() { test_basic(hashtable_dict_new(dict_str_cmp, strhash, NULL, 7), @@ -487,6 +506,14 @@ void test_basic_hashtable_nbuckets() keys2, NKEYS2, closest_lookup_infos, NUM_CLOSEST_LOOKUP_INFOS); } +void test_basic_hashtable2_nbuckets() +{ + test_basic(hashtable2_dict_new(dict_str_cmp, strhash, NULL, 7), + keys1, NKEYS1, closest_lookup_infos, NUM_CLOSEST_LOOKUP_INFOS); + test_basic(hashtable2_dict_new(dict_str_cmp, strhash, NULL, 7), + keys2, NKEYS2, closest_lookup_infos, NUM_CLOSEST_LOOKUP_INFOS); +} + void test_basic_height_balanced_tree() { test_basic(hb_dict_new(dict_str_cmp, NULL), keys1, NKEYS1,