| 12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019 |
- #include "test/jemalloc_test.h"
- #include <stdlib.h>
- #include "jemalloc/internal/rb.h"
- #define rbtn_black_height(a_type, a_field, a_rbt, r_height) do { \
- a_type *rbp_bh_t; \
- for (rbp_bh_t = (a_rbt)->rbt_root, (r_height) = 0; rbp_bh_t != \
- NULL; rbp_bh_t = rbtn_left_get(a_type, a_field, \
- rbp_bh_t)) { \
- if (!rbtn_red_get(a_type, a_field, rbp_bh_t)) { \
- (r_height)++; \
- } \
- } \
- } while (0)
- static bool summarize_always_returns_true = false;
- typedef struct node_s node_t;
- struct node_s {
- #define NODE_MAGIC 0x9823af7e
- uint32_t magic;
- rb_node(node_t) link;
- /* Order used by nodes. */
- uint64_t key;
- /*
- * Our made-up summary property is "specialness", with summarization
- * taking the max.
- */
- uint64_t specialness;
- /*
- * Used by some of the test randomization to avoid double-removing
- * nodes.
- */
- bool mid_remove;
- /*
- * To test searching functionality, we want to temporarily weaken the
- * ordering to allow non-equal nodes that nevertheless compare equal.
- */
- bool allow_duplicates;
- /*
- * In check_consistency, it's handy to know a node's rank in the tree;
- * this tracks it (but only there; not all tests use this).
- */
- int rank;
- int filtered_rank;
- /*
- * Replicate the internal structure of the tree, to make sure the
- * implementation doesn't miss any updates.
- */
- const node_t *summary_lchild;
- const node_t *summary_rchild;
- uint64_t summary_max_specialness;
- };
- static int
- node_cmp(const node_t *a, const node_t *b) {
- int ret;
- expect_u32_eq(a->magic, NODE_MAGIC, "Bad magic");
- expect_u32_eq(b->magic, NODE_MAGIC, "Bad magic");
- ret = (a->key > b->key) - (a->key < b->key);
- if (ret == 0 && !a->allow_duplicates) {
- /*
- * Duplicates are not allowed in the tree, so force an
- * arbitrary ordering for non-identical items with equal keys,
- * unless the user is searching and wants to allow the
- * duplicate.
- */
- ret = (((uintptr_t)a) > ((uintptr_t)b))
- - (((uintptr_t)a) < ((uintptr_t)b));
- }
- return ret;
- }
- static uint64_t
- node_subtree_specialness(node_t *n, const node_t *lchild,
- const node_t *rchild) {
- uint64_t subtree_specialness = n->specialness;
- if (lchild != NULL
- && lchild->summary_max_specialness > subtree_specialness) {
- subtree_specialness = lchild->summary_max_specialness;
- }
- if (rchild != NULL
- && rchild->summary_max_specialness > subtree_specialness) {
- subtree_specialness = rchild->summary_max_specialness;
- }
- return subtree_specialness;
- }
- static bool
- node_summarize(node_t *a, const node_t *lchild, const node_t *rchild) {
- uint64_t new_summary_max_specialness = node_subtree_specialness(
- a, lchild, rchild);
- bool changed = (a->summary_lchild != lchild)
- || (a->summary_rchild != rchild)
- || (new_summary_max_specialness != a->summary_max_specialness);
- a->summary_max_specialness = new_summary_max_specialness;
- a->summary_lchild = lchild;
- a->summary_rchild = rchild;
- return changed || summarize_always_returns_true;
- }
- typedef rb_tree(node_t) tree_t;
- rb_summarized_proto(static, tree_, tree_t, node_t);
- rb_summarized_gen(static, tree_, tree_t, node_t, link, node_cmp,
- node_summarize);
- static bool
- specialness_filter_node(void *ctx, node_t *node) {
- uint64_t specialness = *(uint64_t *)ctx;
- return node->specialness >= specialness;
- }
- static bool
- specialness_filter_subtree(void *ctx, node_t *node) {
- uint64_t specialness = *(uint64_t *)ctx;
- return node->summary_max_specialness >= specialness;
- }
- static node_t *
- tree_iterate_cb(tree_t *tree, node_t *node, void *data) {
- unsigned *i = (unsigned *)data;
- node_t *search_node;
- expect_u32_eq(node->magic, NODE_MAGIC, "Bad magic");
- /* Test rb_search(). */
- search_node = tree_search(tree, node);
- expect_ptr_eq(search_node, node,
- "tree_search() returned unexpected node");
- /* Test rb_nsearch(). */
- search_node = tree_nsearch(tree, node);
- expect_ptr_eq(search_node, node,
- "tree_nsearch() returned unexpected node");
- /* Test rb_psearch(). */
- search_node = tree_psearch(tree, node);
- expect_ptr_eq(search_node, node,
- "tree_psearch() returned unexpected node");
- (*i)++;
- return NULL;
- }
- TEST_BEGIN(test_rb_empty) {
- tree_t tree;
- node_t key;
- tree_new(&tree);
- expect_true(tree_empty(&tree), "Tree should be empty");
- expect_ptr_null(tree_first(&tree), "Unexpected node");
- expect_ptr_null(tree_last(&tree), "Unexpected node");
- key.key = 0;
- key.magic = NODE_MAGIC;
- expect_ptr_null(tree_search(&tree, &key), "Unexpected node");
- key.key = 0;
- key.magic = NODE_MAGIC;
- expect_ptr_null(tree_nsearch(&tree, &key), "Unexpected node");
- key.key = 0;
- key.magic = NODE_MAGIC;
- expect_ptr_null(tree_psearch(&tree, &key), "Unexpected node");
- unsigned nodes = 0;
- tree_iter_filtered(&tree, NULL, &tree_iterate_cb,
- &nodes, &specialness_filter_node, &specialness_filter_subtree,
- NULL);
- expect_u_eq(0, nodes, "");
- nodes = 0;
- tree_reverse_iter_filtered(&tree, NULL, &tree_iterate_cb,
- &nodes, &specialness_filter_node, &specialness_filter_subtree,
- NULL);
- expect_u_eq(0, nodes, "");
- expect_ptr_null(tree_first_filtered(&tree, &specialness_filter_node,
- &specialness_filter_subtree, NULL), "");
- expect_ptr_null(tree_last_filtered(&tree, &specialness_filter_node,
- &specialness_filter_subtree, NULL), "");
- key.key = 0;
- key.magic = NODE_MAGIC;
- expect_ptr_null(tree_search_filtered(&tree, &key,
- &specialness_filter_node, &specialness_filter_subtree, NULL), "");
- expect_ptr_null(tree_nsearch_filtered(&tree, &key,
- &specialness_filter_node, &specialness_filter_subtree, NULL), "");
- expect_ptr_null(tree_psearch_filtered(&tree, &key,
- &specialness_filter_node, &specialness_filter_subtree, NULL), "");
- }
- TEST_END
- static unsigned
- tree_recurse(node_t *node, unsigned black_height, unsigned black_depth) {
- unsigned ret = 0;
- node_t *left_node;
- node_t *right_node;
- if (node == NULL) {
- return ret;
- }
- left_node = rbtn_left_get(node_t, link, node);
- right_node = rbtn_right_get(node_t, link, node);
- expect_ptr_eq(left_node, node->summary_lchild,
- "summary missed a tree update");
- expect_ptr_eq(right_node, node->summary_rchild,
- "summary missed a tree update");
- uint64_t expected_subtree_specialness = node_subtree_specialness(node,
- left_node, right_node);
- expect_u64_eq(expected_subtree_specialness,
- node->summary_max_specialness, "Incorrect summary");
- if (!rbtn_red_get(node_t, link, node)) {
- black_depth++;
- }
- /* Red nodes must be interleaved with black nodes. */
- if (rbtn_red_get(node_t, link, node)) {
- if (left_node != NULL) {
- expect_false(rbtn_red_get(node_t, link, left_node),
- "Node should be black");
- }
- if (right_node != NULL) {
- expect_false(rbtn_red_get(node_t, link, right_node),
- "Node should be black");
- }
- }
- /* Self. */
- expect_u32_eq(node->magic, NODE_MAGIC, "Bad magic");
- /* Left subtree. */
- if (left_node != NULL) {
- ret += tree_recurse(left_node, black_height, black_depth);
- } else {
- ret += (black_depth != black_height);
- }
- /* Right subtree. */
- if (right_node != NULL) {
- ret += tree_recurse(right_node, black_height, black_depth);
- } else {
- ret += (black_depth != black_height);
- }
- return ret;
- }
- static unsigned
- tree_iterate(tree_t *tree) {
- unsigned i;
- i = 0;
- tree_iter(tree, NULL, tree_iterate_cb, (void *)&i);
- return i;
- }
- static unsigned
- tree_iterate_reverse(tree_t *tree) {
- unsigned i;
- i = 0;
- tree_reverse_iter(tree, NULL, tree_iterate_cb, (void *)&i);
- return i;
- }
- static void
- node_remove(tree_t *tree, node_t *node, unsigned nnodes) {
- node_t *search_node;
- unsigned black_height, imbalances;
- tree_remove(tree, node);
- /* Test rb_nsearch(). */
- search_node = tree_nsearch(tree, node);
- if (search_node != NULL) {
- expect_u64_ge(search_node->key, node->key,
- "Key ordering error");
- }
- /* Test rb_psearch(). */
- search_node = tree_psearch(tree, node);
- if (search_node != NULL) {
- expect_u64_le(search_node->key, node->key,
- "Key ordering error");
- }
- node->magic = 0;
- rbtn_black_height(node_t, link, tree, black_height);
- imbalances = tree_recurse(tree->rbt_root, black_height, 0);
- expect_u_eq(imbalances, 0, "Tree is unbalanced");
- expect_u_eq(tree_iterate(tree), nnodes-1,
- "Unexpected node iteration count");
- expect_u_eq(tree_iterate_reverse(tree), nnodes-1,
- "Unexpected node iteration count");
- }
- static node_t *
- remove_iterate_cb(tree_t *tree, node_t *node, void *data) {
- unsigned *nnodes = (unsigned *)data;
- node_t *ret = tree_next(tree, node);
- node_remove(tree, node, *nnodes);
- return ret;
- }
- static node_t *
- remove_reverse_iterate_cb(tree_t *tree, node_t *node, void *data) {
- unsigned *nnodes = (unsigned *)data;
- node_t *ret = tree_prev(tree, node);
- node_remove(tree, node, *nnodes);
- return ret;
- }
- static void
- destroy_cb(node_t *node, void *data) {
- unsigned *nnodes = (unsigned *)data;
- expect_u_gt(*nnodes, 0, "Destruction removed too many nodes");
- (*nnodes)--;
- }
- TEST_BEGIN(test_rb_random) {
- enum {
- NNODES = 25,
- NBAGS = 500,
- SEED = 42
- };
- sfmt_t *sfmt;
- uint64_t bag[NNODES];
- tree_t tree;
- node_t nodes[NNODES];
- unsigned i, j, k, black_height, imbalances;
- sfmt = init_gen_rand(SEED);
- for (i = 0; i < NBAGS; i++) {
- switch (i) {
- case 0:
- /* Insert in order. */
- for (j = 0; j < NNODES; j++) {
- bag[j] = j;
- }
- break;
- case 1:
- /* Insert in reverse order. */
- for (j = 0; j < NNODES; j++) {
- bag[j] = NNODES - j - 1;
- }
- break;
- default:
- for (j = 0; j < NNODES; j++) {
- bag[j] = gen_rand64_range(sfmt, NNODES);
- }
- }
- /*
- * We alternate test behavior with a period of 2 here, and a
- * period of 5 down below, so there's no cycle in which certain
- * combinations get omitted.
- */
- summarize_always_returns_true = (i % 2 == 0);
- for (j = 1; j <= NNODES; j++) {
- /* Initialize tree and nodes. */
- tree_new(&tree);
- for (k = 0; k < j; k++) {
- nodes[k].magic = NODE_MAGIC;
- nodes[k].key = bag[k];
- nodes[k].specialness = gen_rand64_range(sfmt,
- NNODES);
- nodes[k].mid_remove = false;
- nodes[k].allow_duplicates = false;
- nodes[k].summary_lchild = NULL;
- nodes[k].summary_rchild = NULL;
- nodes[k].summary_max_specialness = 0;
- }
- /* Insert nodes. */
- for (k = 0; k < j; k++) {
- tree_insert(&tree, &nodes[k]);
- rbtn_black_height(node_t, link, &tree,
- black_height);
- imbalances = tree_recurse(tree.rbt_root,
- black_height, 0);
- expect_u_eq(imbalances, 0,
- "Tree is unbalanced");
- expect_u_eq(tree_iterate(&tree), k+1,
- "Unexpected node iteration count");
- expect_u_eq(tree_iterate_reverse(&tree), k+1,
- "Unexpected node iteration count");
- expect_false(tree_empty(&tree),
- "Tree should not be empty");
- expect_ptr_not_null(tree_first(&tree),
- "Tree should not be empty");
- expect_ptr_not_null(tree_last(&tree),
- "Tree should not be empty");
- tree_next(&tree, &nodes[k]);
- tree_prev(&tree, &nodes[k]);
- }
- /* Remove nodes. */
- switch (i % 5) {
- case 0:
- for (k = 0; k < j; k++) {
- node_remove(&tree, &nodes[k], j - k);
- }
- break;
- case 1:
- for (k = j; k > 0; k--) {
- node_remove(&tree, &nodes[k-1], k);
- }
- break;
- case 2: {
- node_t *start;
- unsigned nnodes = j;
- start = NULL;
- do {
- start = tree_iter(&tree, start,
- remove_iterate_cb, (void *)&nnodes);
- nnodes--;
- } while (start != NULL);
- expect_u_eq(nnodes, 0,
- "Removal terminated early");
- break;
- } case 3: {
- node_t *start;
- unsigned nnodes = j;
- start = NULL;
- do {
- start = tree_reverse_iter(&tree, start,
- remove_reverse_iterate_cb,
- (void *)&nnodes);
- nnodes--;
- } while (start != NULL);
- expect_u_eq(nnodes, 0,
- "Removal terminated early");
- break;
- } case 4: {
- unsigned nnodes = j;
- tree_destroy(&tree, destroy_cb, &nnodes);
- expect_u_eq(nnodes, 0,
- "Destruction terminated early");
- break;
- } default:
- not_reached();
- }
- }
- }
- fini_gen_rand(sfmt);
- }
- TEST_END
- static void
- expect_simple_consistency(tree_t *tree, uint64_t specialness,
- bool expected_empty, node_t *expected_first, node_t *expected_last) {
- bool empty;
- node_t *first;
- node_t *last;
- empty = tree_empty_filtered(tree, &specialness_filter_node,
- &specialness_filter_subtree, &specialness);
- expect_b_eq(expected_empty, empty, "");
- first = tree_first_filtered(tree,
- &specialness_filter_node, &specialness_filter_subtree,
- (void *)&specialness);
- expect_ptr_eq(expected_first, first, "");
- last = tree_last_filtered(tree,
- &specialness_filter_node, &specialness_filter_subtree,
- (void *)&specialness);
- expect_ptr_eq(expected_last, last, "");
- }
- TEST_BEGIN(test_rb_filter_simple) {
- enum {FILTER_NODES = 10};
- node_t nodes[FILTER_NODES];
- for (unsigned i = 0; i < FILTER_NODES; i++) {
- nodes[i].magic = NODE_MAGIC;
- nodes[i].key = i;
- if (i == 0) {
- nodes[i].specialness = 0;
- } else {
- nodes[i].specialness = ffs_u(i);
- }
- nodes[i].mid_remove = false;
- nodes[i].allow_duplicates = false;
- nodes[i].summary_lchild = NULL;
- nodes[i].summary_rchild = NULL;
- nodes[i].summary_max_specialness = 0;
- }
- summarize_always_returns_true = false;
- tree_t tree;
- tree_new(&tree);
- /* Should be empty */
- expect_simple_consistency(&tree, /* specialness */ 0, /* empty */ true,
- /* first */ NULL, /* last */ NULL);
- /* Fill in just the odd nodes. */
- for (int i = 1; i < FILTER_NODES; i += 2) {
- tree_insert(&tree, &nodes[i]);
- }
- /* A search for an odd node should succeed. */
- expect_simple_consistency(&tree, /* specialness */ 0, /* empty */ false,
- /* first */ &nodes[1], /* last */ &nodes[9]);
- /* But a search for an even one should fail. */
- expect_simple_consistency(&tree, /* specialness */ 1, /* empty */ true,
- /* first */ NULL, /* last */ NULL);
- /* Now we add an even. */
- tree_insert(&tree, &nodes[4]);
- expect_simple_consistency(&tree, /* specialness */ 1, /* empty */ false,
- /* first */ &nodes[4], /* last */ &nodes[4]);
- /* A smaller even, and a larger even. */
- tree_insert(&tree, &nodes[2]);
- tree_insert(&tree, &nodes[8]);
- /*
- * A first-search (resp. last-search) for an even should switch to the
- * lower (higher) one, now that it's been added.
- */
- expect_simple_consistency(&tree, /* specialness */ 1, /* empty */ false,
- /* first */ &nodes[2], /* last */ &nodes[8]);
- /*
- * If we remove 2, a first-search we should go back to 4, while a
- * last-search should remain unchanged.
- */
- tree_remove(&tree, &nodes[2]);
- expect_simple_consistency(&tree, /* specialness */ 1, /* empty */ false,
- /* first */ &nodes[4], /* last */ &nodes[8]);
- /* Reinsert 2, then find it again. */
- tree_insert(&tree, &nodes[2]);
- expect_simple_consistency(&tree, /* specialness */ 1, /* empty */ false,
- /* first */ &nodes[2], /* last */ &nodes[8]);
- /* Searching for a multiple of 4 should not have changed. */
- expect_simple_consistency(&tree, /* specialness */ 2, /* empty */ false,
- /* first */ &nodes[4], /* last */ &nodes[8]);
- /* And a multiple of 8 */
- expect_simple_consistency(&tree, /* specialness */ 3, /* empty */ false,
- /* first */ &nodes[8], /* last */ &nodes[8]);
- /* But not a multiple of 16 */
- expect_simple_consistency(&tree, /* specialness */ 4, /* empty */ true,
- /* first */ NULL, /* last */ NULL);
- }
- TEST_END
- typedef struct iter_ctx_s iter_ctx_t;
- struct iter_ctx_s {
- int ncalls;
- node_t *last_node;
- int ncalls_max;
- bool forward;
- };
- static node_t *
- tree_iterate_filtered_cb(tree_t *tree, node_t *node, void *arg) {
- iter_ctx_t *ctx = (iter_ctx_t *)arg;
- ctx->ncalls++;
- expect_u64_ge(node->specialness, 1,
- "Should only invoke cb on nodes that pass the filter");
- if (ctx->last_node != NULL) {
- if (ctx->forward) {
- expect_d_lt(node_cmp(ctx->last_node, node), 0,
- "Incorrect iteration order");
- } else {
- expect_d_gt(node_cmp(ctx->last_node, node), 0,
- "Incorrect iteration order");
- }
- }
- ctx->last_node = node;
- if (ctx->ncalls == ctx->ncalls_max) {
- return node;
- }
- return NULL;
- }
- static int
- qsort_node_cmp(const void *ap, const void *bp) {
- node_t *a = *(node_t **)ap;
- node_t *b = *(node_t **)bp;
- return node_cmp(a, b);
- }
- #define UPDATE_TEST_MAX 100
- static void
- check_consistency(tree_t *tree, node_t nodes[UPDATE_TEST_MAX], int nnodes) {
- uint64_t specialness = 1;
- bool empty;
- bool real_empty = true;
- node_t *first;
- node_t *real_first = NULL;
- node_t *last;
- node_t *real_last = NULL;
- for (int i = 0; i < nnodes; i++) {
- if (nodes[i].specialness >= specialness) {
- real_empty = false;
- if (real_first == NULL
- || node_cmp(&nodes[i], real_first) < 0) {
- real_first = &nodes[i];
- }
- if (real_last == NULL
- || node_cmp(&nodes[i], real_last) > 0) {
- real_last = &nodes[i];
- }
- }
- }
- empty = tree_empty_filtered(tree, &specialness_filter_node,
- &specialness_filter_subtree, &specialness);
- expect_b_eq(real_empty, empty, "");
- first = tree_first_filtered(tree, &specialness_filter_node,
- &specialness_filter_subtree, &specialness);
- expect_ptr_eq(real_first, first, "");
- last = tree_last_filtered(tree, &specialness_filter_node,
- &specialness_filter_subtree, &specialness);
- expect_ptr_eq(real_last, last, "");
- for (int i = 0; i < nnodes; i++) {
- node_t *next_filtered;
- node_t *real_next_filtered = NULL;
- node_t *prev_filtered;
- node_t *real_prev_filtered = NULL;
- for (int j = 0; j < nnodes; j++) {
- if (nodes[j].specialness < specialness) {
- continue;
- }
- if (node_cmp(&nodes[j], &nodes[i]) < 0
- && (real_prev_filtered == NULL
- || node_cmp(&nodes[j], real_prev_filtered) > 0)) {
- real_prev_filtered = &nodes[j];
- }
- if (node_cmp(&nodes[j], &nodes[i]) > 0
- && (real_next_filtered == NULL
- || node_cmp(&nodes[j], real_next_filtered) < 0)) {
- real_next_filtered = &nodes[j];
- }
- }
- next_filtered = tree_next_filtered(tree, &nodes[i],
- &specialness_filter_node, &specialness_filter_subtree,
- &specialness);
- expect_ptr_eq(real_next_filtered, next_filtered, "");
- prev_filtered = tree_prev_filtered(tree, &nodes[i],
- &specialness_filter_node, &specialness_filter_subtree,
- &specialness);
- expect_ptr_eq(real_prev_filtered, prev_filtered, "");
- node_t *search_filtered;
- node_t *real_search_filtered;
- node_t *nsearch_filtered;
- node_t *real_nsearch_filtered;
- node_t *psearch_filtered;
- node_t *real_psearch_filtered;
- /*
- * search, nsearch, psearch from a node before nodes[i] in the
- * ordering.
- */
- node_t before;
- before.magic = NODE_MAGIC;
- before.key = nodes[i].key - 1;
- before.allow_duplicates = false;
- real_search_filtered = NULL;
- search_filtered = tree_search_filtered(tree, &before,
- &specialness_filter_node, &specialness_filter_subtree,
- &specialness);
- expect_ptr_eq(real_search_filtered, search_filtered, "");
- real_nsearch_filtered = (nodes[i].specialness >= specialness ?
- &nodes[i] : real_next_filtered);
- nsearch_filtered = tree_nsearch_filtered(tree, &before,
- &specialness_filter_node, &specialness_filter_subtree,
- &specialness);
- expect_ptr_eq(real_nsearch_filtered, nsearch_filtered, "");
- real_psearch_filtered = real_prev_filtered;
- psearch_filtered = tree_psearch_filtered(tree, &before,
- &specialness_filter_node, &specialness_filter_subtree,
- &specialness);
- expect_ptr_eq(real_psearch_filtered, psearch_filtered, "");
- /* search, nsearch, psearch from nodes[i] */
- real_search_filtered = (nodes[i].specialness >= specialness ?
- &nodes[i] : NULL);
- search_filtered = tree_search_filtered(tree, &nodes[i],
- &specialness_filter_node, &specialness_filter_subtree,
- &specialness);
- expect_ptr_eq(real_search_filtered, search_filtered, "");
- real_nsearch_filtered = (nodes[i].specialness >= specialness ?
- &nodes[i] : real_next_filtered);
- nsearch_filtered = tree_nsearch_filtered(tree, &nodes[i],
- &specialness_filter_node, &specialness_filter_subtree,
- &specialness);
- expect_ptr_eq(real_nsearch_filtered, nsearch_filtered, "");
- real_psearch_filtered = (nodes[i].specialness >= specialness ?
- &nodes[i] : real_prev_filtered);
- psearch_filtered = tree_psearch_filtered(tree, &nodes[i],
- &specialness_filter_node, &specialness_filter_subtree,
- &specialness);
- expect_ptr_eq(real_psearch_filtered, psearch_filtered, "");
- /*
- * search, nsearch, psearch from a node equivalent to but
- * distinct from nodes[i].
- */
- node_t equiv;
- equiv.magic = NODE_MAGIC;
- equiv.key = nodes[i].key;
- equiv.allow_duplicates = true;
- real_search_filtered = (nodes[i].specialness >= specialness ?
- &nodes[i] : NULL);
- search_filtered = tree_search_filtered(tree, &equiv,
- &specialness_filter_node, &specialness_filter_subtree,
- &specialness);
- expect_ptr_eq(real_search_filtered, search_filtered, "");
- real_nsearch_filtered = (nodes[i].specialness >= specialness ?
- &nodes[i] : real_next_filtered);
- nsearch_filtered = tree_nsearch_filtered(tree, &equiv,
- &specialness_filter_node, &specialness_filter_subtree,
- &specialness);
- expect_ptr_eq(real_nsearch_filtered, nsearch_filtered, "");
- real_psearch_filtered = (nodes[i].specialness >= specialness ?
- &nodes[i] : real_prev_filtered);
- psearch_filtered = tree_psearch_filtered(tree, &equiv,
- &specialness_filter_node, &specialness_filter_subtree,
- &specialness);
- expect_ptr_eq(real_psearch_filtered, psearch_filtered, "");
- /*
- * search, nsearch, psearch from a node after nodes[i] in the
- * ordering.
- */
- node_t after;
- after.magic = NODE_MAGIC;
- after.key = nodes[i].key + 1;
- after.allow_duplicates = false;
- real_search_filtered = NULL;
- search_filtered = tree_search_filtered(tree, &after,
- &specialness_filter_node, &specialness_filter_subtree,
- &specialness);
- expect_ptr_eq(real_search_filtered, search_filtered, "");
- real_nsearch_filtered = real_next_filtered;
- nsearch_filtered = tree_nsearch_filtered(tree, &after,
- &specialness_filter_node, &specialness_filter_subtree,
- &specialness);
- expect_ptr_eq(real_nsearch_filtered, nsearch_filtered, "");
- real_psearch_filtered = (nodes[i].specialness >= specialness ?
- &nodes[i] : real_prev_filtered);
- psearch_filtered = tree_psearch_filtered(tree, &after,
- &specialness_filter_node, &specialness_filter_subtree,
- &specialness);
- expect_ptr_eq(real_psearch_filtered, psearch_filtered, "");
- }
- /* Filtered iteration test setup. */
- int nspecial = 0;
- node_t *sorted_nodes[UPDATE_TEST_MAX];
- node_t *sorted_filtered_nodes[UPDATE_TEST_MAX];
- for (int i = 0; i < nnodes; i++) {
- sorted_nodes[i] = &nodes[i];
- }
- qsort(sorted_nodes, nnodes, sizeof(node_t *), &qsort_node_cmp);
- for (int i = 0; i < nnodes; i++) {
- sorted_nodes[i]->rank = i;
- sorted_nodes[i]->filtered_rank = nspecial;
- if (sorted_nodes[i]->specialness >= 1) {
- sorted_filtered_nodes[nspecial] = sorted_nodes[i];
- nspecial++;
- }
- }
- node_t *iter_result;
- iter_ctx_t ctx;
- ctx.ncalls = 0;
- ctx.last_node = NULL;
- ctx.ncalls_max = INT_MAX;
- ctx.forward = true;
- /* Filtered forward iteration from the beginning. */
- iter_result = tree_iter_filtered(tree, NULL, &tree_iterate_filtered_cb,
- &ctx, &specialness_filter_node, &specialness_filter_subtree,
- &specialness);
- expect_ptr_null(iter_result, "");
- expect_d_eq(nspecial, ctx.ncalls, "");
- /* Filtered forward iteration from a starting point. */
- for (int i = 0; i < nnodes; i++) {
- ctx.ncalls = 0;
- ctx.last_node = NULL;
- iter_result = tree_iter_filtered(tree, &nodes[i],
- &tree_iterate_filtered_cb, &ctx, &specialness_filter_node,
- &specialness_filter_subtree, &specialness);
- expect_ptr_null(iter_result, "");
- expect_d_eq(nspecial - nodes[i].filtered_rank, ctx.ncalls, "");
- }
- /* Filtered forward iteration from the beginning, with stopping */
- for (int i = 0; i < nspecial; i++) {
- ctx.ncalls = 0;
- ctx.last_node = NULL;
- ctx.ncalls_max = i + 1;
- iter_result = tree_iter_filtered(tree, NULL,
- &tree_iterate_filtered_cb, &ctx, &specialness_filter_node,
- &specialness_filter_subtree, &specialness);
- expect_ptr_eq(sorted_filtered_nodes[i], iter_result, "");
- expect_d_eq(ctx.ncalls, i + 1, "");
- }
- /* Filtered forward iteration from a starting point, with stopping. */
- for (int i = 0; i < nnodes; i++) {
- for (int j = 0; j < nspecial - nodes[i].filtered_rank; j++) {
- ctx.ncalls = 0;
- ctx.last_node = NULL;
- ctx.ncalls_max = j + 1;
- iter_result = tree_iter_filtered(tree, &nodes[i],
- &tree_iterate_filtered_cb, &ctx,
- &specialness_filter_node,
- &specialness_filter_subtree, &specialness);
- expect_d_eq(j + 1, ctx.ncalls, "");
- expect_ptr_eq(sorted_filtered_nodes[
- nodes[i].filtered_rank + j], iter_result, "");
- }
- }
- /* Backwards iteration. */
- ctx.ncalls = 0;
- ctx.last_node = NULL;
- ctx.ncalls_max = INT_MAX;
- ctx.forward = false;
- /* Filtered backward iteration from the end. */
- iter_result = tree_reverse_iter_filtered(tree, NULL,
- &tree_iterate_filtered_cb, &ctx, &specialness_filter_node,
- &specialness_filter_subtree, &specialness);
- expect_ptr_null(iter_result, "");
- expect_d_eq(nspecial, ctx.ncalls, "");
- /* Filtered backward iteration from a starting point. */
- for (int i = 0; i < nnodes; i++) {
- ctx.ncalls = 0;
- ctx.last_node = NULL;
- iter_result = tree_reverse_iter_filtered(tree, &nodes[i],
- &tree_iterate_filtered_cb, &ctx, &specialness_filter_node,
- &specialness_filter_subtree, &specialness);
- expect_ptr_null(iter_result, "");
- int surplus_rank = (nodes[i].specialness >= 1 ? 1 : 0);
- expect_d_eq(nodes[i].filtered_rank + surplus_rank, ctx.ncalls,
- "");
- }
- /* Filtered backward iteration from the end, with stopping */
- for (int i = 0; i < nspecial; i++) {
- ctx.ncalls = 0;
- ctx.last_node = NULL;
- ctx.ncalls_max = i + 1;
- iter_result = tree_reverse_iter_filtered(tree, NULL,
- &tree_iterate_filtered_cb, &ctx, &specialness_filter_node,
- &specialness_filter_subtree, &specialness);
- expect_ptr_eq(sorted_filtered_nodes[nspecial - i - 1],
- iter_result, "");
- expect_d_eq(ctx.ncalls, i + 1, "");
- }
- /* Filtered backward iteration from a starting point, with stopping. */
- for (int i = 0; i < nnodes; i++) {
- int surplus_rank = (nodes[i].specialness >= 1 ? 1 : 0);
- for (int j = 0; j < nodes[i].filtered_rank + surplus_rank;
- j++) {
- ctx.ncalls = 0;
- ctx.last_node = NULL;
- ctx.ncalls_max = j + 1;
- iter_result = tree_reverse_iter_filtered(tree,
- &nodes[i], &tree_iterate_filtered_cb, &ctx,
- &specialness_filter_node,
- &specialness_filter_subtree, &specialness);
- expect_d_eq(j + 1, ctx.ncalls, "");
- expect_ptr_eq(sorted_filtered_nodes[
- nodes[i].filtered_rank - j - 1 + surplus_rank],
- iter_result, "");
- }
- }
- }
- static void
- do_update_search_test(int nnodes, int ntrees, int nremovals,
- int nupdates) {
- node_t nodes[UPDATE_TEST_MAX];
- assert(nnodes <= UPDATE_TEST_MAX);
- sfmt_t *sfmt = init_gen_rand(12345);
- for (int i = 0; i < ntrees; i++) {
- tree_t tree;
- tree_new(&tree);
- for (int j = 0; j < nnodes; j++) {
- nodes[j].magic = NODE_MAGIC;
- /*
- * In consistency checking, we increment or decrement a
- * key and assume that the result is not a key in the
- * tree. This isn't a *real* concern with 64-bit keys
- * and a good PRNG, but why not be correct anyways?
- */
- nodes[j].key = 2 * gen_rand64(sfmt);
- nodes[j].specialness = 0;
- nodes[j].mid_remove = false;
- nodes[j].allow_duplicates = false;
- nodes[j].summary_lchild = NULL;
- nodes[j].summary_rchild = NULL;
- nodes[j].summary_max_specialness = 0;
- tree_insert(&tree, &nodes[j]);
- }
- for (int j = 0; j < nremovals; j++) {
- int victim = (int)gen_rand64_range(sfmt, nnodes);
- if (!nodes[victim].mid_remove) {
- tree_remove(&tree, &nodes[victim]);
- nodes[victim].mid_remove = true;
- }
- }
- for (int j = 0; j < nnodes; j++) {
- if (nodes[j].mid_remove) {
- nodes[j].mid_remove = false;
- nodes[j].key = 2 * gen_rand64(sfmt);
- tree_insert(&tree, &nodes[j]);
- }
- }
- for (int j = 0; j < nupdates; j++) {
- uint32_t ind = gen_rand32_range(sfmt, nnodes);
- nodes[ind].specialness = 1 - nodes[ind].specialness;
- tree_update_summaries(&tree, &nodes[ind]);
- check_consistency(&tree, nodes, nnodes);
- }
- }
- }
- TEST_BEGIN(test_rb_update_search) {
- summarize_always_returns_true = false;
- do_update_search_test(2, 100, 3, 50);
- do_update_search_test(5, 100, 3, 50);
- do_update_search_test(12, 100, 5, 1000);
- do_update_search_test(100, 1, 50, 500);
- }
- TEST_END
- typedef rb_tree(node_t) unsummarized_tree_t;
- rb_gen(static UNUSED, unsummarized_tree_, unsummarized_tree_t, node_t, link,
- node_cmp);
- static node_t *
- unsummarized_tree_iterate_cb(unsummarized_tree_t *tree, node_t *node,
- void *data) {
- unsigned *i = (unsigned *)data;
- (*i)++;
- return NULL;
- }
- /*
- * The unsummarized and summarized funtionality is implemented via the same
- * functions; we don't really need to do much more than test that we can exclude
- * the filtered functionality without anything breaking.
- */
- TEST_BEGIN(test_rb_unsummarized) {
- unsummarized_tree_t tree;
- unsummarized_tree_new(&tree);
- unsigned nnodes = 0;
- unsummarized_tree_iter(&tree, NULL, &unsummarized_tree_iterate_cb,
- &nnodes);
- expect_u_eq(0, nnodes, "");
- }
- TEST_END
- int
- main(void) {
- return test_no_reentrancy(
- test_rb_empty,
- test_rb_random,
- test_rb_filter_simple,
- test_rb_update_search,
- test_rb_unsummarized);
- }
|