void test_basic(dict *dct, const struct key_info *keys, const unsigned nkeys, const struct closest_lookup_info *cl_infos, unsigned n_cl_infos) { dict_itor *itor = dict_itor_new(dct); CU_ASSERT_TRUE(dict_verify(dct)); for (unsigned i = 0; i < nkeys; ++i) { bool inserted = false; void **datum_location = dict_insert(dct, keys[i].key, &inserted); CU_ASSERT_TRUE(inserted); CU_ASSERT_PTR_NOT_NULL(datum_location); CU_ASSERT_PTR_NULL(*datum_location); *datum_location = keys[i].value; CU_ASSERT_TRUE(dict_verify(dct)); for (unsigned j = 0; j <= i; ++j) test_search(dct, itor, keys[j].key, keys[j].value); for (unsigned j = i + 1; j < nkeys; ++j) test_search(dct, itor, keys[j].key, NULL); } CU_ASSERT_EQUAL(dict_count(dct), nkeys); 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)); 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); dict_free(clone); } if (dct->_vtable->clone) { dict *clone = dict_clone(dct, NULL); CU_ASSERT_PTR_NOT_NULL(clone); CU_ASSERT_TRUE(dict_verify(clone)); CU_ASSERT_EQUAL(dict_count(clone), nkeys); for (unsigned i = 0; i < nkeys; ++i) { test_search(clone, itor, keys[i].key, keys[i].value); } for (unsigned i = 0; i < nkeys; ++i) { CU_ASSERT_TRUE(dict_remove(clone, keys[i].key)); } dict_free(clone); } for (unsigned i = 0; i < nkeys; ++i) test_search(dct, itor, keys[i].key, keys[i].value); for (unsigned i = 0; i < nkeys; ++i) { bool inserted = false; void **datum_location = dict_insert(dct, keys[i].key, &inserted); CU_ASSERT_FALSE(inserted); CU_ASSERT_PTR_NOT_NULL(datum_location); CU_ASSERT_EQUAL(*datum_location, keys[i].value); CU_ASSERT_TRUE(dict_verify(dct)); } CU_ASSERT_EQUAL(dict_count(dct), nkeys); CU_ASSERT_PTR_NOT_NULL(itor); char *last_key = NULL; unsigned n = 0; for (dict_itor_first(itor); dict_itor_valid(itor); dict_itor_next(itor)) { CU_ASSERT_PTR_NOT_NULL(dict_itor_key(itor)); CU_ASSERT_PTR_NOT_NULL(dict_itor_data(itor)); CU_ASSERT_PTR_NOT_NULL(*dict_itor_data(itor)); char *key = dict_itor_key(itor); bool key_matched = false; for (unsigned i = 0; i < nkeys; ++i) { if (keys[i].key == key) { CU_ASSERT_EQUAL(*dict_itor_data(itor), keys[i].value); key_matched = true; break; } } CU_ASSERT_TRUE(key_matched); 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); } last_key = dict_itor_key(itor); } ++n; } CU_ASSERT_EQUAL(n, nkeys); last_key = NULL; n = 0; for (dict_itor_last(itor); dict_itor_valid(itor); dict_itor_prev(itor)) { CU_ASSERT_PTR_NOT_NULL(dict_itor_key(itor)); CU_ASSERT_PTR_NOT_NULL(dict_itor_data(itor)); CU_ASSERT_PTR_NOT_NULL(*dict_itor_data(itor)); char *key = dict_itor_key(itor); bool key_matched = false; for (unsigned i = 0; i < nkeys; ++i) { if (keys[i].key == key) { CU_ASSERT_EQUAL(*dict_itor_data(itor), keys[i].value); key_matched = true; break; } } CU_ASSERT_TRUE(key_matched); 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); } last_key = dict_itor_key(itor); } ++n; } CU_ASSERT_EQUAL(n, nkeys); for (unsigned i = 0; i < nkeys; ++i) { bool inserted = false; void **datum_location = dict_insert(dct, keys[i].key, &inserted); CU_ASSERT_FALSE(inserted); CU_ASSERT_PTR_NOT_NULL(datum_location); CU_ASSERT_PTR_NOT_NULL(*datum_location); *datum_location = keys[i].alt; CU_ASSERT_TRUE(dict_verify(dct)); } CU_ASSERT_EQUAL(dict_count(dct), nkeys); for (unsigned i = 0; i < nkeys; ++i) test_search(dct, itor, keys[i].key, keys[i].alt); for (unsigned i = 0; i < nkeys; ++i) { test_search(dct, itor, keys[i].key, keys[i].alt); CU_ASSERT_TRUE(dict_remove(dct, keys[i].key)); CU_ASSERT_TRUE(dict_verify(dct)); CU_ASSERT_EQUAL(dict_remove(dct, keys[i].key), false); for (unsigned j = 0; j <= i; ++j) { test_search(dct, itor, keys[j].key, NULL); } for (unsigned j = i + 1; j < nkeys; ++j) { test_search(dct, itor, keys[j].key, keys[j].alt); } } for (unsigned i = 0; i < nkeys; ++i) { bool inserted = false; void **datum_location = dict_insert(dct, keys[i].key, &inserted); CU_ASSERT_TRUE(inserted); CU_ASSERT_PTR_NOT_NULL(datum_location); CU_ASSERT_PTR_NULL(*datum_location); *datum_location = keys[i].value; CU_ASSERT_TRUE(dict_verify(dct)); } CU_ASSERT_EQUAL(dict_count(dct), nkeys); CU_ASSERT_EQUAL(dict_clear(dct), nkeys); for (unsigned i = 0; i < nkeys; ++i) { bool inserted = false; void **datum_location = dict_insert(dct, keys[i].key, &inserted); CU_ASSERT_TRUE(inserted); CU_ASSERT_PTR_NOT_NULL(datum_location); CU_ASSERT_PTR_NULL(*datum_location); *datum_location = keys[i].value; CU_ASSERT_TRUE(dict_verify(dct)); } test_closest_lookup(dct, cl_infos, n_cl_infos); dict_itor_free(itor); CU_ASSERT_EQUAL(dict_count(dct), nkeys); CU_ASSERT_EQUAL(dict_free(dct), nkeys); }
void dict_insert(dict_t *dict, dnode_t *node, const void *key) { dnode_t *where = dict_root(dict), *nil = dict_nil(dict); dnode_t *parent = nil, *uncle, *grandpa; int result = -1; node->key = key; assert (!dict_isfull(dict)); assert (!dict_contains(dict, node)); assert (!dnode_is_in_a_dict(node)); /* basic binary tree insert */ while (where != nil) { parent = where; result = dict->compare(key, where->key); /* trap attempts at duplicate key insertion unless it's explicitly allowed */ assert (dict->dupes || result != 0); if (result < 0) where = where->left; else where = where->right; } assert (where == nil); if (result < 0) parent->left = node; else parent->right = node; node->parent = parent; node->left = nil; node->right = nil; dict->nodecount++; /* red black adjustments */ node->color = dnode_red; while (parent->color == dnode_red) { grandpa = parent->parent; if (parent == grandpa->left) { uncle = grandpa->right; if (uncle->color == dnode_red) { /* red parent, red uncle */ parent->color = dnode_black; uncle->color = dnode_black; grandpa->color = dnode_red; node = grandpa; parent = grandpa->parent; } else { /* red parent, black uncle */ if (node == parent->right) { rotate_left(parent); parent = node; assert (grandpa == parent->parent); /* rotation between parent and child preserves grandpa */ } parent->color = dnode_black; grandpa->color = dnode_red; rotate_right(grandpa); break; } } else { /* symmetric cases: parent == parent->parent->right */ uncle = grandpa->left; if (uncle->color == dnode_red) { parent->color = dnode_black; uncle->color = dnode_black; grandpa->color = dnode_red; node = grandpa; parent = grandpa->parent; } else { if (node == parent->left) { rotate_right(parent); parent = node; assert (grandpa == parent->parent); } parent->color = dnode_black; grandpa->color = dnode_red; rotate_left(grandpa); break; } } } dict_root(dict)->color = dnode_black; assert (dict_verify(dict)); }
dnode_t *dict_delete(dict_t *dict, dnode_t *target) { dnode_t *nil = dict_nil(dict), *child, *delparent = target->parent; /* basic deletion */ assert (!dict_isempty(dict)); assert (dict_contains(dict, target)); /* * If the node being deleted has two children, then we replace it with its * successor (i.e. the leftmost node in the right subtree.) By doing this, * we avoid the traditional algorithm under which the successor's key and * value *only* move to the deleted node and the successor is spliced out * from the tree. We cannot use this approach because the user may hold * pointers to the successor, or nodes may be inextricably tied to some * other structures by way of embedding, etc. So we must splice out the * node we are given, not some other node, and must not move contents from * one node to another behind the user's back. */ if (target->left != nil && target->right != nil) { dnode_t *next = dict_next(dict, target); dnode_t *nextparent = next->parent; dnode_color_t nextcolor = next->color; assert (next != nil); assert (next->parent != nil); assert (next->left == nil); /* * First, splice out the successor from the tree completely, by * moving up its right child into its place. */ child = next->right; child->parent = nextparent; if (nextparent->left == next) { nextparent->left = child; } else { assert (nextparent->right == next); nextparent->right = child; } /* * Now that the successor has been extricated from the tree, install it * in place of the node that we want deleted. */ next->parent = delparent; next->left = target->left; next->right = target->right; next->left->parent = next; next->right->parent = next; next->color = target->color; target->color = nextcolor; if (delparent->left == target) { delparent->left = next; } else { assert (delparent->right == target); delparent->right = next; } } else { assert (target != nil); assert (target->left == nil || target->right == nil); child = (target->left != nil) ? target->left : target->right; child->parent = delparent = target->parent; if (target == delparent->left) { delparent->left = child; } else { assert (target == delparent->right); delparent->right = child; } } target->parent = NULL; target->right = NULL; target->left = NULL; dict->nodecount--; assert (verify_bintree(dict)); /* red-black adjustments */ if (target->color == dnode_black) { dnode_t *parent, *sister; dict_root(dict)->color = dnode_red; while (child->color == dnode_black) { parent = child->parent; if (child == parent->left) { sister = parent->right; assert (sister != nil); if (sister->color == dnode_red) { sister->color = dnode_black; parent->color = dnode_red; rotate_left(parent); sister = parent->right; assert (sister != nil); } if (sister->left->color == dnode_black && sister->right->color == dnode_black) { sister->color = dnode_red; child = parent; } else { if (sister->right->color == dnode_black) { assert (sister->left->color == dnode_red); sister->left->color = dnode_black; sister->color = dnode_red; rotate_right(sister); sister = parent->right; assert (sister != nil); } sister->color = parent->color; sister->right->color = dnode_black; parent->color = dnode_black; rotate_left(parent); break; } } else { /* symmetric case: child == child->parent->right */ assert (child == parent->right); sister = parent->left; assert (sister != nil); if (sister->color == dnode_red) { sister->color = dnode_black; parent->color = dnode_red; rotate_right(parent); sister = parent->left; assert (sister != nil); } if (sister->right->color == dnode_black && sister->left->color == dnode_black) { sister->color = dnode_red; child = parent; } else { if (sister->left->color == dnode_black) { assert (sister->right->color == dnode_red); sister->right->color = dnode_black; sister->color = dnode_red; rotate_left(sister); sister = parent->left; assert (sister != nil); } sister->color = parent->color; sister->left->color = dnode_black; parent->color = dnode_black; rotate_right(parent); break; } } } child->color = dnode_black; dict_root(dict)->color = dnode_black; } assert (dict_verify(dict)); return target; }
void dict_load_end(dict_load_t *load) { dict_t *dict = load->dictptr; dnode_t *tree[DICT_DEPTH_MAX] = { 0 }; dnode_t *curr, *dictnil = dict_nil(dict), *loadnil = &load->nilnode, *next; dnode_t *complete = 0; dictcount_t fullcount = DICTCOUNT_T_MAX, nodecount = dict->nodecount; dictcount_t botrowcount; unsigned baselevel = 0, level = 0, i; assert (dnode_red == 0 && dnode_black == 1); while (fullcount >= nodecount && fullcount) fullcount >>= 1; botrowcount = nodecount - fullcount; for (curr = loadnil->left; curr != loadnil; curr = next) { next = curr->left; if (complete == NULL && botrowcount-- == 0) { assert (baselevel == 0); assert (level == 0); baselevel = level = 1; complete = tree[0]; if (complete != 0) { tree[0] = 0; complete->right = dictnil; while (tree[level] != 0) { tree[level]->right = complete; complete->parent = tree[level]; complete = tree[level]; tree[level++] = 0; } } } if (complete == NULL) { curr->left = dictnil; curr->right = dictnil; curr->color = (dnode_color_t) (level % 2); complete = curr; assert (level == baselevel); while (tree[level] != 0) { tree[level]->right = complete; complete->parent = tree[level]; complete = tree[level]; tree[level++] = 0; } } else { curr->left = complete; curr->color = (dnode_color_t) ((level + 1) % 2); complete->parent = curr; tree[level] = curr; complete = 0; level = baselevel; } } if (complete == NULL) complete = dictnil; for (i = 0; i < DICT_DEPTH_MAX; i++) { if (tree[i] != 0) { tree[i]->right = complete; complete->parent = tree[i]; complete = tree[i]; } } dictnil->color = dnode_black; dictnil->right = dictnil; complete->parent = dictnil; complete->color = dnode_black; dict_root(dict) = complete; assert (dict_verify(dict)); }
int main(int argc, char **argv) { if (argc != 3) { fprintf(stderr, "usage: %s [type] [input]\n", appname); fprintf(stderr, "type: specifies the dictionary type:\n"); fprintf(stderr, " h: height-balanced tree\n"); fprintf(stderr, " p: path-reduction tree\n"); fprintf(stderr, " r: red-black tree\n"); fprintf(stderr, " t: treap\n"); fprintf(stderr, " s: splay tree\n"); 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); } srand(0xdeadbeef); dict_malloc_func = xmalloc; const char type = argv[1][0]; const char *container_name = NULL; dict *dct = create_dictionary(type, &container_name); if (!dct) quit("can't create container"); ASSERT(dict_verify(dct)); const size_t malloced_save = malloced; FILE *fp = fopen(argv[2], "r"); if (fp == NULL) quit("cant open file '%s': %s", argv[2], strerror(errno)); unsigned nwords = 0; char buf[512]; while (fgets(buf, sizeof(buf), fp)) ++nwords; if (!nwords) quit("nothing read from file"); char **words = xmalloc(sizeof(*words) * nwords); rewind(fp); for (unsigned i = 0; i < nwords && fgets(buf, sizeof(buf), fp); i++) { strtok(buf, "\n"); words[i] = xstrdup(buf); } fclose(fp); malloced = malloced_save; size_t total_comp = 0, total_hash = 0, total_rotations = 0; struct rusage start, end; struct timeval total = { 0, 0 }; timer_start(&start); for (unsigned i = 0; i < nwords; i++) { bool inserted = false; void **datum_location = dict_insert(dct, words[i], &inserted); if (!inserted) quit("insert #%d failed for '%s'", i, words[i]); ASSERT(datum_location != NULL); ASSERT(*datum_location == NULL); *datum_location = words[i]; } timer_end(&start, &end, &total); printf(" %s container: %.02fkB\n", container_name, malloced_save * 1e-3); printf(" %s memory: %.02fkB\n", container_name, malloced * 1e-3); printf(" %s insert: %6.03f s (%9zu cmp, %9zu hash)\n", container_name, (end.ru_utime.tv_sec * 1000000 + end.ru_utime.tv_usec) * 1e-6, comp_count, hash_count); total_comp += comp_count; comp_count = 0; total_hash += hash_count; hash_count = 0; 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; tree->rotation_count = 0; } ASSERT(dict_verify(dct)); unsigned n = dict_count(dct); if (n != nwords) quit("bad count (%u - should be %u)!", n, nwords); dict_itor *itor = dict_itor_new(dct); timer_start(&start); n = 0; ASSERT(dict_itor_first(itor)); do { ASSERT(dict_itor_valid(itor)); ASSERT(dict_itor_key(itor) == *dict_itor_data(itor)); ++n; } while (dict_itor_next(itor)); timer_end(&start, &end, &total); printf(" %s fwd iterate: %6.03f s\n", container_name, (end.ru_utime.tv_sec * 1000000 + end.ru_utime.tv_usec) * 1e-6); if (n != nwords) warn("Fwd iteration returned %u items - should be %u", n, nwords); timer_start(&start); n = 0; ASSERT(dict_itor_last(itor)); do { ASSERT(dict_itor_valid(itor)); ASSERT(dict_itor_key(itor) == *dict_itor_data(itor)); ++n; } while (dict_itor_prev(itor)); timer_end(&start, &end, &total); printf(" %s rev iterate: %6.03f s\n", container_name, (end.ru_utime.tv_sec * 1000000 + end.ru_utime.tv_usec) * 1e-6); if (n != nwords) warn("Rev iteration returned %u items - should be %u", n, nwords); dict_itor_free(itor); /* shuffle(words, nwords); */ timer_start(&start); for (unsigned i = 0; i < nwords; i++) { char *p = dict_search(dct, words[i]); if (!p) quit("lookup failed for '%s'", buf); if (p != words[i]) quit("bad data for '%s', got '%s' instead", words[i], p); } timer_end(&start, &end, &total); printf(" %s good search: %6.03f s (%9zu cmp, %9zu hash)\n", container_name, (end.ru_utime.tv_sec * 1000000 + end.ru_utime.tv_usec) * 1e-6, comp_count, hash_count); total_comp += comp_count; comp_count = 0; total_hash += hash_count; hash_count = 0; 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; tree->rotation_count = 0; } timer_start(&start); for (unsigned i = 0; i < nwords; i++) { int rv = rand() % strlen(words[i]); words[i][rv]++; dict_search(dct, words[i]); words[i][rv]--; } timer_end(&start, &end, &total); printf(" %s bad search: %6.03f s (%9zu cmp, %9zu hash)\n", container_name, (end.ru_utime.tv_sec * 1000000 + end.ru_utime.tv_usec) * 1e-6, comp_count, hash_count); total_comp += comp_count; comp_count = 0; total_hash += hash_count; hash_count = 0; /* shuffle(words, nwords); */ timer_start(&start); for (unsigned i = 0; i < nwords; i++) { if (!dict_remove(dct, words[i])) quit("removing #%d '%s' failed!\n", i, words[i]); } timer_end(&start, &end, &total); printf(" %s remove: %6.03f s (%9zu cmp, %9zu hash)\n", container_name, (end.ru_utime.tv_sec * 1000000 + end.ru_utime.tv_usec) * 1e-6, comp_count, hash_count); total_comp += comp_count; comp_count = 0; total_hash += hash_count; hash_count = 0; 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; tree->rotation_count = 0; } ASSERT(dict_verify(dct)); if ((n = dict_count(dct)) != 0) quit("error - count not zero (%u)!", n); dict_free(dct); printf(" %s total: %6.03f s (%9zu cmp, %9zu hash)\n", container_name, (total.tv_sec * 1000000 + total.tv_usec) * 1e-6, total_comp, total_hash); if (type != 'H' && type != '2' && type != 'S') { printf(" total rotations: %zu\n", total_rotations); } FREE(words); exit(EXIT_SUCCESS); }
int main(int argc, char **argv) { bool shuffle_keys = true; if (argc != 3) { fprintf(stderr, "usage: %s [type] [input]\n", appname); fprintf(stderr, "type: specifies the dictionary type:\n"); fprintf(stderr, " h: height-balanced tree\n"); fprintf(stderr, " p: path-reduction tree\n"); fprintf(stderr, " r: red-black tree\n"); fprintf(stderr, " t: treap\n"); fprintf(stderr, " s: splay tree\n"); 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); } srand(0xdeadbeef); dict_malloc_func = xmalloc; const char type = argv[1][0]; const char *container_name = NULL; dict *dct = create_dictionary(type, &container_name); if (!dct) quit("can't create container"); ASSERT(dict_verify(dct)); ASSERT(comp_count == 0); ASSERT(hash_count == 0); const size_t malloced_save = malloced; FILE *fp = fopen(argv[2], "r"); if (fp == NULL) quit("cant open file '%s': %s", argv[2], strerror(errno)); size_t nwords = 0; char buf[512]; while (fgets(buf, sizeof(buf), fp)) ++nwords; if (!nwords) quit("nothing read from file"); char **words = xmalloc(sizeof(*words) * nwords); rewind(fp); size_t words_read = 0; while (words_read < nwords && fgets(buf, sizeof(buf), fp)) { strtok(buf, "\n"); words[words_read++] = xstrdup(buf); } fclose(fp); if (words_read < nwords) quit("Only read %zu/%zu words!", words_read, nwords); printf("Loaded %zu keys from %s.\n", nwords, argv[2]); malloced = malloced_save; size_t total_comp = 0, total_hash = 0, total_rotations = 0; struct rusage start, end; struct timeval total = { 0, 0 }; timer_start(&start); for (unsigned i = 0; i < nwords; i++) { dict_insert_result result = dict_insert(dct, words[i]); if (!result.inserted) quit("insert #%d failed for '%s'", i, words[i]); ASSERT(result.datum_ptr != NULL); ASSERT(*result.datum_ptr == NULL); *result.datum_ptr = words[i]; } timer_end(&start, &end, &total); printf(" %s container: %.02fkB\n", container_name, malloced_save * 1e-3); printf(" %s memory: %.02fkB\n", container_name, malloced * 1e-3); printf(" %s insert: %6.03fs %9zu cmp (%.02f/insert)", container_name, (end.ru_utime.tv_sec * 1000000 + end.ru_utime.tv_usec) * 1e-6, comp_count, comp_count / (double) nwords); if (hash_count) printf(" %9zu hash", hash_count); printf("\n"); total_comp += comp_count; comp_count = 0; total_hash += hash_count; hash_count = 0; if (dict_is_sorted(dct) && type != 'S') { tree_base *tree = dict_private(dct); printf(" min path length: %zu\n", tree_min_path_length(tree)); printf(" max path length: %zu\n", tree_max_path_length(tree)); printf(" tot path length: %zu\n", tree_total_path_length(tree)); printf("insert rotations: %zu\n", tree->rotation_count); total_rotations += tree->rotation_count; tree->rotation_count = 0; } else if (type == 'S') { size_t counts[16] = { 0 }; size_t num_counts = skiplist_link_count_histogram(dict_private(dct), counts, sizeof(counts) / sizeof(counts[0])); size_t count_sum = 0; for (size_t i = 0; i <= num_counts; ++i) { printf("skiplist %zu-node(s): %zu\n", i, counts[i]); count_sum += counts[i]; } ASSERT(count_sum == nwords); } ASSERT(dict_verify(dct)); comp_count = hash_count = 0; /* Ignore comparisons/hashes incurred by dict_verify() */ size_t n = dict_count(dct); if (n != nwords) quit("bad count (%u - should be %u)!", n, nwords); dict_itor *itor = dict_itor_new(dct); timer_start(&start); n = 0; ASSERT(dict_itor_first(itor)); do { ASSERT(dict_itor_valid(itor)); ASSERT(dict_itor_key(itor) == *dict_itor_datum(itor)); ++n; } while (dict_itor_next(itor)); timer_end(&start, &end, &total); printf(" %s fwd iterate: %6.03fs\n", container_name, (end.ru_utime.tv_sec * 1000000 + end.ru_utime.tv_usec) * 1e-6); if (n != nwords) warn("Fwd iteration returned %u items - should be %u", n, nwords); ASSERT(dict_verify(dct)); comp_count = hash_count = 0; /* Ignore comparisons/hashes incurred by dict_verify() */ timer_start(&start); n = 0; ASSERT(dict_itor_last(itor)); do { ASSERT(dict_itor_valid(itor)); ASSERT(dict_itor_key(itor) == *dict_itor_datum(itor)); ++n; } while (dict_itor_prev(itor)); timer_end(&start, &end, &total); printf(" %s rev iterate: %6.03fs\n", container_name, (end.ru_utime.tv_sec * 1000000 + end.ru_utime.tv_usec) * 1e-6); if (n != nwords) warn("Rev iteration returned %u items - should be %u", n, nwords); dict_itor_free(itor); if (shuffle_keys) shuffle(words, nwords); ASSERT(dict_verify(dct)); comp_count = hash_count = 0; /* Ignore comparisons/hashes incurred by dict_verify() */ timer_start(&start); for (unsigned i = 0; i < nwords; i++) { void **p = dict_search(dct, words[i]); if (!p) quit("lookup failed for '%s'", buf); if (*p != words[i]) quit("bad data for '%s', got '%s' instead", words[i], *(char **)p); } timer_end(&start, &end, &total); printf(" %s good search: %6.03fs %9zu cmp (%.02f/search)", container_name, (end.ru_utime.tv_sec * 1000000 + end.ru_utime.tv_usec) * 1e-6, comp_count, comp_count / (double) nwords); if (hash_count) printf(" %9zu hash", hash_count); printf("\n"); total_comp += comp_count; comp_count = 0; total_hash += hash_count; hash_count = 0; 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; tree->rotation_count = 0; } ASSERT(dict_verify(dct)); comp_count = hash_count = 0; /* Ignore comparisons/hashes incurred by dict_verify() */ timer_start(&start); for (unsigned i = 0; i < nwords; i++) { unsigned rv = dict_rand() % strlen(words[i]); words[i][rv]++; dict_search(dct, words[i]); words[i][rv]--; } timer_end(&start, &end, &total); printf(" %s bad search: %6.03fs %9zu cmp (%.02f/search)", container_name, (end.ru_utime.tv_sec * 1000000 + end.ru_utime.tv_usec) * 1e-6, comp_count, comp_count / (double) nwords); if (hash_count) printf(" %9zu hash", hash_count); printf("\n"); total_comp += comp_count; comp_count = 0; total_hash += hash_count; hash_count = 0; ASSERT(dict_verify(dct)); comp_count = hash_count = 0; /* Ignore comparisons/hashes incurred by dict_verify() */ if (shuffle_keys) shuffle(words, nwords); timer_start(&start); for (unsigned i = 0; i < nwords; i++) { dict_remove_result result = dict_remove(dct, words[i]); if (!result.removed) quit("removing #%d '%s' failed!\n", i, words[i]); ASSERT(result.key == words[i]); ASSERT(result.datum == words[i]); } timer_end(&start, &end, &total); printf(" %s remove: %6.03fs %9zu cmp (%.2f/remove)", container_name, (end.ru_utime.tv_sec * 1000000 + end.ru_utime.tv_usec) * 1e-6, comp_count, comp_count / (double)nwords); if (hash_count) printf(" %9zu hash", hash_count); printf("\n"); total_comp += comp_count; comp_count = 0; total_hash += hash_count; hash_count = 0; 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; tree->rotation_count = 0; } ASSERT(dict_verify(dct)); comp_count = hash_count = 0; /* Ignore comparisons/hashes incurred by dict_verify() */ if ((n = dict_count(dct)) != 0) quit("error - count not zero (%u)!", n); dict_free(dct, key_str_free); printf(" %s total: %6.03fs %9zu cmp", container_name, (total.tv_sec * 1000000 + total.tv_usec) * 1e-6, total_comp); if (total_hash) printf(" %9zu hash", total_hash); printf("\n"); if (type != 'H' && type != '2' && type != 'S') { printf(" total rotations: %zu\n", total_rotations); } FREE(words); exit(EXIT_SUCCESS); }
void test_basic(dict *dct, const struct key_info *keys, const unsigned nkeys) { CU_ASSERT_TRUE(dict_verify(dct)); for (unsigned i = 0; i < nkeys; ++i) { void **datum_location = NULL; CU_ASSERT_TRUE(dict_insert(dct, keys[i].key, &datum_location)); CU_ASSERT_PTR_NOT_NULL(datum_location); *datum_location = keys[i].value; CU_ASSERT_TRUE(dict_verify(dct)); for (unsigned j = 0; j <= i; ++j) CU_ASSERT_EQUAL(dict_search(dct, keys[j].key), keys[j].value); for (unsigned j = i + 1; j < nkeys; ++j) CU_ASSERT_EQUAL(dict_search(dct, keys[j].key), NULL); } CU_ASSERT_EQUAL(dict_count(dct), nkeys); if (dct->_vtable->insert == (dict_insert_func)hashtable_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)); CU_ASSERT_TRUE(dict_verify(dct)); for (unsigned j = 0; j < nkeys; ++j) CU_ASSERT_EQUAL(dict_search(clone, keys[j].key), keys[j].value); dict_free(clone); } if (dct->_vtable->clone) { dict *clone = dict_clone(dct, NULL); CU_ASSERT_PTR_NOT_NULL(clone); CU_ASSERT_TRUE(dict_verify(clone)); CU_ASSERT_EQUAL(dict_count(clone), nkeys); for (unsigned i = 0; i < nkeys; ++i) { CU_ASSERT_EQUAL(dict_search(clone, keys[i].key), keys[i].value); } for (unsigned i = 0; i < nkeys; ++i) { CU_ASSERT_TRUE(dict_remove(clone, keys[i].key)); } dict_free(clone); } for (unsigned i = 0; i < nkeys; ++i) CU_ASSERT_EQUAL(dict_search(dct, keys[i].key), keys[i].value); for (unsigned i = 0; i < nkeys; ++i) { void **datum_location = NULL; CU_ASSERT_FALSE(dict_insert(dct, keys[i].key, &datum_location)); CU_ASSERT_PTR_NOT_NULL(datum_location); CU_ASSERT_EQUAL(*datum_location, keys[i].value); CU_ASSERT_TRUE(dict_verify(dct)); } CU_ASSERT_EQUAL(dict_count(dct), nkeys); dict_itor *itor = dict_itor_new(dct); CU_ASSERT_PTR_NOT_NULL(itor); char *last_key = NULL; unsigned n = 0; for (dict_itor_first(itor); dict_itor_valid(itor); dict_itor_next(itor)) { CU_ASSERT_PTR_NOT_NULL(dict_itor_key(itor)); CU_ASSERT_PTR_NOT_NULL(dict_itor_data(itor)); ++n; if (dct->_vtable->insert != (dict_insert_func)hashtable_insert) { if (last_key) { CU_ASSERT_TRUE(strcmp(last_key, dict_itor_key(itor)) < 0); } last_key = dict_itor_key(itor); } } CU_ASSERT_EQUAL(n, nkeys); last_key = NULL; n = 0; for (dict_itor_last(itor); dict_itor_valid(itor); dict_itor_prev(itor)) { CU_ASSERT_PTR_NOT_NULL(dict_itor_key(itor)); CU_ASSERT_PTR_NOT_NULL(dict_itor_data(itor)); ++n; if (dct->_vtable->insert != (dict_insert_func)hashtable_insert) { if (last_key) { CU_ASSERT_TRUE(strcmp(last_key, dict_itor_key(itor)) > 0); } last_key = dict_itor_key(itor); } } CU_ASSERT_EQUAL(n, nkeys); dict_itor_free(itor); for (unsigned i = 0; i < nkeys; ++i) { void **datum_location = NULL; CU_ASSERT_FALSE(dict_insert(dct, keys[i].key, &datum_location)); CU_ASSERT_PTR_NOT_NULL(datum_location); *datum_location = keys[i].alt; CU_ASSERT_TRUE(dict_verify(dct)); } CU_ASSERT_EQUAL(dict_count(dct), nkeys); for (unsigned i = 0; i < nkeys; ++i) CU_ASSERT_EQUAL(dict_search(dct, keys[i].key), keys[i].alt); for (unsigned i = 0; i < nkeys; ++i) { CU_ASSERT_EQUAL(dict_search(dct, keys[i].key), keys[i].alt); CU_ASSERT_TRUE(dict_remove(dct, keys[i].key)); CU_ASSERT_TRUE(dict_verify(dct)); CU_ASSERT_EQUAL(dict_remove(dct, keys[i].key), false); for (unsigned j = 0; j <= i; ++j) { CU_ASSERT_EQUAL(dict_search(dct, keys[j].key), NULL); } for (unsigned j = i + 1; j < nkeys; ++j) { CU_ASSERT_EQUAL(dict_search(dct, keys[j].key), keys[j].alt); } } for (unsigned i = 0; i < nkeys; ++i) { void **datum_location = NULL; CU_ASSERT_TRUE(dict_insert(dct, keys[i].key, &datum_location)); CU_ASSERT_PTR_NOT_NULL(datum_location); *datum_location = keys[i].value; CU_ASSERT_TRUE(dict_verify(dct)); } CU_ASSERT_EQUAL(dict_count(dct), nkeys); CU_ASSERT_EQUAL(dict_clear(dct), nkeys); for (unsigned i = 0; i < nkeys; ++i) { void **datum_location = NULL; CU_ASSERT_TRUE(dict_insert(dct, keys[i].key, &datum_location)); CU_ASSERT_PTR_NOT_NULL(datum_location); *datum_location = keys[i].value; CU_ASSERT_TRUE(dict_verify(dct)); } CU_ASSERT_EQUAL(dict_count(dct), nkeys); CU_ASSERT_EQUAL(dict_free(dct), nkeys); }
const void * fc_solve_kaz_tree_insert(dict_t *dict, dnode_t *node, const void *key) { dnode_t *where = dict_root(dict), *nil = dict_nil(dict); dnode_t *parent = nil, *uncle, *grandpa; int result = -1; node->key = key; assert (!dict_isfull(dict)); assert (!dict_contains(dict, node)); assert (!dnode_is_in_a_dict(node)); /* basic binary tree insert */ while (where != nil) { parent = where; result = dict->compare(key, where->key, dict->context); /* We are remming it out because instead of duplicating the key * we return the existing key. -- Shlomi Fish, fc-solve. * */ #if 0 /* trap attempts at duplicate key insertion unless it's explicitly allowed */ assert (dict->dupes || result != 0); #endif if (result == 0) { return where->dict_key; } else if (result < 0) { where = where->left; } else { where = where->right; } } assert (where == nil); if (result < 0) parent->left = node; else parent->right = node; node->parent = parent; node->left = nil; node->right = nil; #ifdef NO_FC_SOLVE dict->nodecount++; #endif /* red black adjustments */ node->color = dnode_red; while (parent->color == dnode_red) { grandpa = parent->parent; if (parent == grandpa->left) { uncle = grandpa->right; if (uncle->color == dnode_red) { /* red parent, red uncle */ parent->color = dnode_black; uncle->color = dnode_black; grandpa->color = dnode_red; node = grandpa; parent = grandpa->parent; } else { /* red parent, black uncle */ if (node == parent->right) { rotate_left(parent); parent = node; assert (grandpa == parent->parent); /* rotation between parent and child preserves grandpa */ } parent->color = dnode_black; grandpa->color = dnode_red; rotate_right(grandpa); break; } } else { /* symmetric cases: parent == parent->parent->right */ uncle = grandpa->left; if (uncle->color == dnode_red) { parent->color = dnode_black; uncle->color = dnode_black; grandpa->color = dnode_red; node = grandpa; parent = grandpa->parent; } else { if (node == parent->left) { rotate_right(parent); parent = node; assert (grandpa == parent->parent); } parent->color = dnode_black; grandpa->color = dnode_red; rotate_left(grandpa); break; } } } dict_root(dict)->color = dnode_black; assert (dict_verify(dict)); return NULL; }