xref: /netbsd-src/external/bsd/jemalloc/dist/test/unit/rb.c (revision 7bdf38e5b7a28439665f2fdeff81e36913eef7dd)
1a0698ed9Schristos #include "test/jemalloc_test.h"
2a0698ed9Schristos 
3*7bdf38e5Schristos #include <stdlib.h>
4*7bdf38e5Schristos 
5a0698ed9Schristos #include "jemalloc/internal/rb.h"
6a0698ed9Schristos 
7a0698ed9Schristos #define rbtn_black_height(a_type, a_field, a_rbt, r_height) do {	\
8a0698ed9Schristos 	a_type *rbp_bh_t;						\
9a0698ed9Schristos 	for (rbp_bh_t = (a_rbt)->rbt_root, (r_height) = 0; rbp_bh_t !=	\
10a0698ed9Schristos 	    NULL; rbp_bh_t = rbtn_left_get(a_type, a_field,		\
11a0698ed9Schristos 	    rbp_bh_t)) {						\
12a0698ed9Schristos 		if (!rbtn_red_get(a_type, a_field, rbp_bh_t)) {		\
13a0698ed9Schristos 		(r_height)++;						\
14a0698ed9Schristos 		}							\
15a0698ed9Schristos 	}								\
16a0698ed9Schristos } while (0)
17a0698ed9Schristos 
18*7bdf38e5Schristos static bool summarize_always_returns_true = false;
19a0698ed9Schristos 
20*7bdf38e5Schristos typedef struct node_s node_t;
21a0698ed9Schristos struct node_s {
22a0698ed9Schristos #define NODE_MAGIC 0x9823af7e
23a0698ed9Schristos 	uint32_t magic;
24a0698ed9Schristos 	rb_node(node_t) link;
25*7bdf38e5Schristos 	/* Order used by nodes. */
26a0698ed9Schristos 	uint64_t key;
27*7bdf38e5Schristos 	/*
28*7bdf38e5Schristos 	 * Our made-up summary property is "specialness", with summarization
29*7bdf38e5Schristos 	 * taking the max.
30*7bdf38e5Schristos 	 */
31*7bdf38e5Schristos 	uint64_t specialness;
32*7bdf38e5Schristos 
33*7bdf38e5Schristos 	/*
34*7bdf38e5Schristos 	 * Used by some of the test randomization to avoid double-removing
35*7bdf38e5Schristos 	 * nodes.
36*7bdf38e5Schristos 	 */
37*7bdf38e5Schristos 	bool mid_remove;
38*7bdf38e5Schristos 
39*7bdf38e5Schristos 	/*
40*7bdf38e5Schristos 	 * To test searching functionality, we want to temporarily weaken the
41*7bdf38e5Schristos 	 * ordering to allow non-equal nodes that nevertheless compare equal.
42*7bdf38e5Schristos 	 */
43*7bdf38e5Schristos 	bool allow_duplicates;
44*7bdf38e5Schristos 
45*7bdf38e5Schristos 	/*
46*7bdf38e5Schristos 	 * In check_consistency, it's handy to know a node's rank in the tree;
47*7bdf38e5Schristos 	 * this tracks it (but only there; not all tests use this).
48*7bdf38e5Schristos 	 */
49*7bdf38e5Schristos 	int rank;
50*7bdf38e5Schristos 	int filtered_rank;
51*7bdf38e5Schristos 
52*7bdf38e5Schristos 	/*
53*7bdf38e5Schristos 	 * Replicate the internal structure of the tree, to make sure the
54*7bdf38e5Schristos 	 * implementation doesn't miss any updates.
55*7bdf38e5Schristos 	 */
56*7bdf38e5Schristos 	const node_t *summary_lchild;
57*7bdf38e5Schristos 	const node_t *summary_rchild;
58*7bdf38e5Schristos 	uint64_t summary_max_specialness;
59a0698ed9Schristos };
60a0698ed9Schristos 
61a0698ed9Schristos static int
62a0698ed9Schristos node_cmp(const node_t *a, const node_t *b) {
63a0698ed9Schristos 	int ret;
64a0698ed9Schristos 
65*7bdf38e5Schristos 	expect_u32_eq(a->magic, NODE_MAGIC, "Bad magic");
66*7bdf38e5Schristos 	expect_u32_eq(b->magic, NODE_MAGIC, "Bad magic");
67a0698ed9Schristos 
68a0698ed9Schristos 	ret = (a->key > b->key) - (a->key < b->key);
69*7bdf38e5Schristos 	if (ret == 0 && !a->allow_duplicates) {
70a0698ed9Schristos 		/*
71a0698ed9Schristos 		 * Duplicates are not allowed in the tree, so force an
72*7bdf38e5Schristos 		 * arbitrary ordering for non-identical items with equal keys,
73*7bdf38e5Schristos 		 * unless the user is searching and wants to allow the
74*7bdf38e5Schristos 		 * duplicate.
75a0698ed9Schristos 		 */
76a0698ed9Schristos 		ret = (((uintptr_t)a) > ((uintptr_t)b))
77a0698ed9Schristos 		    - (((uintptr_t)a) < ((uintptr_t)b));
78a0698ed9Schristos 	}
79a0698ed9Schristos 	return ret;
80a0698ed9Schristos }
81a0698ed9Schristos 
82*7bdf38e5Schristos static uint64_t
83*7bdf38e5Schristos node_subtree_specialness(node_t *n, const node_t *lchild,
84*7bdf38e5Schristos     const node_t *rchild) {
85*7bdf38e5Schristos 	uint64_t subtree_specialness = n->specialness;
86*7bdf38e5Schristos 	if (lchild != NULL
87*7bdf38e5Schristos 	    && lchild->summary_max_specialness > subtree_specialness) {
88*7bdf38e5Schristos 		subtree_specialness = lchild->summary_max_specialness;
89*7bdf38e5Schristos 	}
90*7bdf38e5Schristos 	if (rchild != NULL
91*7bdf38e5Schristos 	    && rchild->summary_max_specialness > subtree_specialness) {
92*7bdf38e5Schristos 		subtree_specialness = rchild->summary_max_specialness;
93*7bdf38e5Schristos 	}
94*7bdf38e5Schristos 	return subtree_specialness;
95*7bdf38e5Schristos }
96*7bdf38e5Schristos 
97*7bdf38e5Schristos static bool
98*7bdf38e5Schristos node_summarize(node_t *a, const node_t *lchild, const node_t *rchild) {
99*7bdf38e5Schristos 	uint64_t new_summary_max_specialness = node_subtree_specialness(
100*7bdf38e5Schristos 	    a, lchild, rchild);
101*7bdf38e5Schristos 	bool changed = (a->summary_lchild != lchild)
102*7bdf38e5Schristos 	    || (a->summary_rchild != rchild)
103*7bdf38e5Schristos 	    || (new_summary_max_specialness != a->summary_max_specialness);
104*7bdf38e5Schristos 	a->summary_max_specialness = new_summary_max_specialness;
105*7bdf38e5Schristos 	a->summary_lchild = lchild;
106*7bdf38e5Schristos 	a->summary_rchild = rchild;
107*7bdf38e5Schristos 	return changed || summarize_always_returns_true;
108*7bdf38e5Schristos }
109*7bdf38e5Schristos 
110a0698ed9Schristos typedef rb_tree(node_t) tree_t;
111*7bdf38e5Schristos rb_summarized_proto(static, tree_, tree_t, node_t);
112*7bdf38e5Schristos rb_summarized_gen(static, tree_, tree_t, node_t, link, node_cmp,
113*7bdf38e5Schristos     node_summarize);
114*7bdf38e5Schristos 
115*7bdf38e5Schristos static bool
116*7bdf38e5Schristos specialness_filter_node(void *ctx, node_t *node) {
117*7bdf38e5Schristos 	uint64_t specialness = *(uint64_t *)ctx;
118*7bdf38e5Schristos 	return node->specialness >= specialness;
119*7bdf38e5Schristos }
120*7bdf38e5Schristos 
121*7bdf38e5Schristos static bool
122*7bdf38e5Schristos specialness_filter_subtree(void *ctx, node_t *node) {
123*7bdf38e5Schristos 	uint64_t specialness = *(uint64_t *)ctx;
124*7bdf38e5Schristos 	return node->summary_max_specialness >= specialness;
125*7bdf38e5Schristos }
126*7bdf38e5Schristos 
127*7bdf38e5Schristos static node_t *
128*7bdf38e5Schristos tree_iterate_cb(tree_t *tree, node_t *node, void *data) {
129*7bdf38e5Schristos 	unsigned *i = (unsigned *)data;
130*7bdf38e5Schristos 	node_t *search_node;
131*7bdf38e5Schristos 
132*7bdf38e5Schristos 	expect_u32_eq(node->magic, NODE_MAGIC, "Bad magic");
133*7bdf38e5Schristos 
134*7bdf38e5Schristos 	/* Test rb_search(). */
135*7bdf38e5Schristos 	search_node = tree_search(tree, node);
136*7bdf38e5Schristos 	expect_ptr_eq(search_node, node,
137*7bdf38e5Schristos 	    "tree_search() returned unexpected node");
138*7bdf38e5Schristos 
139*7bdf38e5Schristos 	/* Test rb_nsearch(). */
140*7bdf38e5Schristos 	search_node = tree_nsearch(tree, node);
141*7bdf38e5Schristos 	expect_ptr_eq(search_node, node,
142*7bdf38e5Schristos 	    "tree_nsearch() returned unexpected node");
143*7bdf38e5Schristos 
144*7bdf38e5Schristos 	/* Test rb_psearch(). */
145*7bdf38e5Schristos 	search_node = tree_psearch(tree, node);
146*7bdf38e5Schristos 	expect_ptr_eq(search_node, node,
147*7bdf38e5Schristos 	    "tree_psearch() returned unexpected node");
148*7bdf38e5Schristos 
149*7bdf38e5Schristos 	(*i)++;
150*7bdf38e5Schristos 
151*7bdf38e5Schristos 	return NULL;
152*7bdf38e5Schristos }
153a0698ed9Schristos 
154a0698ed9Schristos TEST_BEGIN(test_rb_empty) {
155a0698ed9Schristos 	tree_t tree;
156a0698ed9Schristos 	node_t key;
157a0698ed9Schristos 
158a0698ed9Schristos 	tree_new(&tree);
159a0698ed9Schristos 
160*7bdf38e5Schristos 	expect_true(tree_empty(&tree), "Tree should be empty");
161*7bdf38e5Schristos 	expect_ptr_null(tree_first(&tree), "Unexpected node");
162*7bdf38e5Schristos 	expect_ptr_null(tree_last(&tree), "Unexpected node");
163a0698ed9Schristos 
164a0698ed9Schristos 	key.key = 0;
165a0698ed9Schristos 	key.magic = NODE_MAGIC;
166*7bdf38e5Schristos 	expect_ptr_null(tree_search(&tree, &key), "Unexpected node");
167a0698ed9Schristos 
168a0698ed9Schristos 	key.key = 0;
169a0698ed9Schristos 	key.magic = NODE_MAGIC;
170*7bdf38e5Schristos 	expect_ptr_null(tree_nsearch(&tree, &key), "Unexpected node");
171a0698ed9Schristos 
172a0698ed9Schristos 	key.key = 0;
173a0698ed9Schristos 	key.magic = NODE_MAGIC;
174*7bdf38e5Schristos 	expect_ptr_null(tree_psearch(&tree, &key), "Unexpected node");
175*7bdf38e5Schristos 
176*7bdf38e5Schristos 	unsigned nodes = 0;
177*7bdf38e5Schristos 	tree_iter_filtered(&tree, NULL, &tree_iterate_cb,
178*7bdf38e5Schristos 	    &nodes, &specialness_filter_node, &specialness_filter_subtree,
179*7bdf38e5Schristos 	    NULL);
180*7bdf38e5Schristos 	expect_u_eq(0, nodes, "");
181*7bdf38e5Schristos 
182*7bdf38e5Schristos 	nodes = 0;
183*7bdf38e5Schristos 	tree_reverse_iter_filtered(&tree, NULL, &tree_iterate_cb,
184*7bdf38e5Schristos 	    &nodes, &specialness_filter_node, &specialness_filter_subtree,
185*7bdf38e5Schristos 	    NULL);
186*7bdf38e5Schristos 	expect_u_eq(0, nodes, "");
187*7bdf38e5Schristos 
188*7bdf38e5Schristos 	expect_ptr_null(tree_first_filtered(&tree, &specialness_filter_node,
189*7bdf38e5Schristos 	    &specialness_filter_subtree, NULL), "");
190*7bdf38e5Schristos 	expect_ptr_null(tree_last_filtered(&tree, &specialness_filter_node,
191*7bdf38e5Schristos 	    &specialness_filter_subtree, NULL), "");
192*7bdf38e5Schristos 
193*7bdf38e5Schristos 	key.key = 0;
194*7bdf38e5Schristos 	key.magic = NODE_MAGIC;
195*7bdf38e5Schristos 	expect_ptr_null(tree_search_filtered(&tree, &key,
196*7bdf38e5Schristos 	    &specialness_filter_node, &specialness_filter_subtree, NULL), "");
197*7bdf38e5Schristos 	expect_ptr_null(tree_nsearch_filtered(&tree, &key,
198*7bdf38e5Schristos 	    &specialness_filter_node, &specialness_filter_subtree, NULL), "");
199*7bdf38e5Schristos 	expect_ptr_null(tree_psearch_filtered(&tree, &key,
200*7bdf38e5Schristos 	    &specialness_filter_node, &specialness_filter_subtree, NULL), "");
201a0698ed9Schristos }
202a0698ed9Schristos TEST_END
203a0698ed9Schristos 
204a0698ed9Schristos static unsigned
205a0698ed9Schristos tree_recurse(node_t *node, unsigned black_height, unsigned black_depth) {
206a0698ed9Schristos 	unsigned ret = 0;
207a0698ed9Schristos 	node_t *left_node;
208a0698ed9Schristos 	node_t *right_node;
209a0698ed9Schristos 
210a0698ed9Schristos 	if (node == NULL) {
211a0698ed9Schristos 		return ret;
212a0698ed9Schristos 	}
213a0698ed9Schristos 
214a0698ed9Schristos 	left_node = rbtn_left_get(node_t, link, node);
215a0698ed9Schristos 	right_node = rbtn_right_get(node_t, link, node);
216a0698ed9Schristos 
217*7bdf38e5Schristos 	expect_ptr_eq(left_node, node->summary_lchild,
218*7bdf38e5Schristos 	    "summary missed a tree update");
219*7bdf38e5Schristos 	expect_ptr_eq(right_node, node->summary_rchild,
220*7bdf38e5Schristos 	    "summary missed a tree update");
221*7bdf38e5Schristos 
222*7bdf38e5Schristos 	uint64_t expected_subtree_specialness = node_subtree_specialness(node,
223*7bdf38e5Schristos 	    left_node, right_node);
224*7bdf38e5Schristos 	expect_u64_eq(expected_subtree_specialness,
225*7bdf38e5Schristos 	    node->summary_max_specialness, "Incorrect summary");
226*7bdf38e5Schristos 
227a0698ed9Schristos 	if (!rbtn_red_get(node_t, link, node)) {
228a0698ed9Schristos 		black_depth++;
229a0698ed9Schristos 	}
230a0698ed9Schristos 
231a0698ed9Schristos 	/* Red nodes must be interleaved with black nodes. */
232a0698ed9Schristos 	if (rbtn_red_get(node_t, link, node)) {
233a0698ed9Schristos 		if (left_node != NULL) {
234*7bdf38e5Schristos 			expect_false(rbtn_red_get(node_t, link, left_node),
235a0698ed9Schristos 				"Node should be black");
236a0698ed9Schristos 		}
237a0698ed9Schristos 		if (right_node != NULL) {
238*7bdf38e5Schristos 			expect_false(rbtn_red_get(node_t, link, right_node),
239a0698ed9Schristos 			    "Node should be black");
240a0698ed9Schristos 		}
241a0698ed9Schristos 	}
242a0698ed9Schristos 
243a0698ed9Schristos 	/* Self. */
244*7bdf38e5Schristos 	expect_u32_eq(node->magic, NODE_MAGIC, "Bad magic");
245a0698ed9Schristos 
246a0698ed9Schristos 	/* Left subtree. */
247a0698ed9Schristos 	if (left_node != NULL) {
248a0698ed9Schristos 		ret += tree_recurse(left_node, black_height, black_depth);
249a0698ed9Schristos 	} else {
250a0698ed9Schristos 		ret += (black_depth != black_height);
251a0698ed9Schristos 	}
252a0698ed9Schristos 
253a0698ed9Schristos 	/* Right subtree. */
254a0698ed9Schristos 	if (right_node != NULL) {
255a0698ed9Schristos 		ret += tree_recurse(right_node, black_height, black_depth);
256a0698ed9Schristos 	} else {
257a0698ed9Schristos 		ret += (black_depth != black_height);
258a0698ed9Schristos 	}
259a0698ed9Schristos 
260a0698ed9Schristos 	return ret;
261a0698ed9Schristos }
262a0698ed9Schristos 
263a0698ed9Schristos static unsigned
264a0698ed9Schristos tree_iterate(tree_t *tree) {
265a0698ed9Schristos 	unsigned i;
266a0698ed9Schristos 
267a0698ed9Schristos 	i = 0;
268a0698ed9Schristos 	tree_iter(tree, NULL, tree_iterate_cb, (void *)&i);
269a0698ed9Schristos 
270a0698ed9Schristos 	return i;
271a0698ed9Schristos }
272a0698ed9Schristos 
273a0698ed9Schristos static unsigned
274a0698ed9Schristos tree_iterate_reverse(tree_t *tree) {
275a0698ed9Schristos 	unsigned i;
276a0698ed9Schristos 
277a0698ed9Schristos 	i = 0;
278a0698ed9Schristos 	tree_reverse_iter(tree, NULL, tree_iterate_cb, (void *)&i);
279a0698ed9Schristos 
280a0698ed9Schristos 	return i;
281a0698ed9Schristos }
282a0698ed9Schristos 
283a0698ed9Schristos static void
284a0698ed9Schristos node_remove(tree_t *tree, node_t *node, unsigned nnodes) {
285a0698ed9Schristos 	node_t *search_node;
286a0698ed9Schristos 	unsigned black_height, imbalances;
287a0698ed9Schristos 
288a0698ed9Schristos 	tree_remove(tree, node);
289a0698ed9Schristos 
290a0698ed9Schristos 	/* Test rb_nsearch(). */
291a0698ed9Schristos 	search_node = tree_nsearch(tree, node);
292a0698ed9Schristos 	if (search_node != NULL) {
293*7bdf38e5Schristos 		expect_u64_ge(search_node->key, node->key,
294a0698ed9Schristos 		    "Key ordering error");
295a0698ed9Schristos 	}
296a0698ed9Schristos 
297a0698ed9Schristos 	/* Test rb_psearch(). */
298a0698ed9Schristos 	search_node = tree_psearch(tree, node);
299a0698ed9Schristos 	if (search_node != NULL) {
300*7bdf38e5Schristos 		expect_u64_le(search_node->key, node->key,
301a0698ed9Schristos 		    "Key ordering error");
302a0698ed9Schristos 	}
303a0698ed9Schristos 
304a0698ed9Schristos 	node->magic = 0;
305a0698ed9Schristos 
306a0698ed9Schristos 	rbtn_black_height(node_t, link, tree, black_height);
307a0698ed9Schristos 	imbalances = tree_recurse(tree->rbt_root, black_height, 0);
308*7bdf38e5Schristos 	expect_u_eq(imbalances, 0, "Tree is unbalanced");
309*7bdf38e5Schristos 	expect_u_eq(tree_iterate(tree), nnodes-1,
310a0698ed9Schristos 	    "Unexpected node iteration count");
311*7bdf38e5Schristos 	expect_u_eq(tree_iterate_reverse(tree), nnodes-1,
312a0698ed9Schristos 	    "Unexpected node iteration count");
313a0698ed9Schristos }
314a0698ed9Schristos 
315a0698ed9Schristos static node_t *
316a0698ed9Schristos remove_iterate_cb(tree_t *tree, node_t *node, void *data) {
317a0698ed9Schristos 	unsigned *nnodes = (unsigned *)data;
318a0698ed9Schristos 	node_t *ret = tree_next(tree, node);
319a0698ed9Schristos 
320a0698ed9Schristos 	node_remove(tree, node, *nnodes);
321a0698ed9Schristos 
322a0698ed9Schristos 	return ret;
323a0698ed9Schristos }
324a0698ed9Schristos 
325a0698ed9Schristos static node_t *
326a0698ed9Schristos remove_reverse_iterate_cb(tree_t *tree, node_t *node, void *data) {
327a0698ed9Schristos 	unsigned *nnodes = (unsigned *)data;
328a0698ed9Schristos 	node_t *ret = tree_prev(tree, node);
329a0698ed9Schristos 
330a0698ed9Schristos 	node_remove(tree, node, *nnodes);
331a0698ed9Schristos 
332a0698ed9Schristos 	return ret;
333a0698ed9Schristos }
334a0698ed9Schristos 
335a0698ed9Schristos static void
336a0698ed9Schristos destroy_cb(node_t *node, void *data) {
337a0698ed9Schristos 	unsigned *nnodes = (unsigned *)data;
338a0698ed9Schristos 
339*7bdf38e5Schristos 	expect_u_gt(*nnodes, 0, "Destruction removed too many nodes");
340a0698ed9Schristos 	(*nnodes)--;
341a0698ed9Schristos }
342a0698ed9Schristos 
343a0698ed9Schristos TEST_BEGIN(test_rb_random) {
344*7bdf38e5Schristos 	enum {
345*7bdf38e5Schristos 		NNODES = 25,
346*7bdf38e5Schristos 		NBAGS = 500,
347*7bdf38e5Schristos 		SEED = 42
348*7bdf38e5Schristos 	};
349a0698ed9Schristos 	sfmt_t *sfmt;
350a0698ed9Schristos 	uint64_t bag[NNODES];
351a0698ed9Schristos 	tree_t tree;
352a0698ed9Schristos 	node_t nodes[NNODES];
353a0698ed9Schristos 	unsigned i, j, k, black_height, imbalances;
354a0698ed9Schristos 
355a0698ed9Schristos 	sfmt = init_gen_rand(SEED);
356a0698ed9Schristos 	for (i = 0; i < NBAGS; i++) {
357a0698ed9Schristos 		switch (i) {
358a0698ed9Schristos 		case 0:
359a0698ed9Schristos 			/* Insert in order. */
360a0698ed9Schristos 			for (j = 0; j < NNODES; j++) {
361a0698ed9Schristos 				bag[j] = j;
362a0698ed9Schristos 			}
363a0698ed9Schristos 			break;
364a0698ed9Schristos 		case 1:
365a0698ed9Schristos 			/* Insert in reverse order. */
366a0698ed9Schristos 			for (j = 0; j < NNODES; j++) {
367a0698ed9Schristos 				bag[j] = NNODES - j - 1;
368a0698ed9Schristos 			}
369a0698ed9Schristos 			break;
370a0698ed9Schristos 		default:
371a0698ed9Schristos 			for (j = 0; j < NNODES; j++) {
372a0698ed9Schristos 				bag[j] = gen_rand64_range(sfmt, NNODES);
373a0698ed9Schristos 			}
374a0698ed9Schristos 		}
375a0698ed9Schristos 
376*7bdf38e5Schristos 		/*
377*7bdf38e5Schristos 		 * We alternate test behavior with a period of 2 here, and a
378*7bdf38e5Schristos 		 * period of 5 down below, so there's no cycle in which certain
379*7bdf38e5Schristos 		 * combinations get omitted.
380*7bdf38e5Schristos 		 */
381*7bdf38e5Schristos 		summarize_always_returns_true = (i % 2 == 0);
382*7bdf38e5Schristos 
383a0698ed9Schristos 		for (j = 1; j <= NNODES; j++) {
384a0698ed9Schristos 			/* Initialize tree and nodes. */
385a0698ed9Schristos 			tree_new(&tree);
386a0698ed9Schristos 			for (k = 0; k < j; k++) {
387a0698ed9Schristos 				nodes[k].magic = NODE_MAGIC;
388a0698ed9Schristos 				nodes[k].key = bag[k];
389*7bdf38e5Schristos 				nodes[k].specialness = gen_rand64_range(sfmt,
390*7bdf38e5Schristos 				    NNODES);
391*7bdf38e5Schristos 				nodes[k].mid_remove = false;
392*7bdf38e5Schristos 				nodes[k].allow_duplicates = false;
393*7bdf38e5Schristos 				nodes[k].summary_lchild = NULL;
394*7bdf38e5Schristos 				nodes[k].summary_rchild = NULL;
395*7bdf38e5Schristos 				nodes[k].summary_max_specialness = 0;
396a0698ed9Schristos 			}
397a0698ed9Schristos 
398a0698ed9Schristos 			/* Insert nodes. */
399a0698ed9Schristos 			for (k = 0; k < j; k++) {
400a0698ed9Schristos 				tree_insert(&tree, &nodes[k]);
401a0698ed9Schristos 
402a0698ed9Schristos 				rbtn_black_height(node_t, link, &tree,
403a0698ed9Schristos 				    black_height);
404a0698ed9Schristos 				imbalances = tree_recurse(tree.rbt_root,
405a0698ed9Schristos 				    black_height, 0);
406*7bdf38e5Schristos 				expect_u_eq(imbalances, 0,
407a0698ed9Schristos 				    "Tree is unbalanced");
408a0698ed9Schristos 
409*7bdf38e5Schristos 				expect_u_eq(tree_iterate(&tree), k+1,
410a0698ed9Schristos 				    "Unexpected node iteration count");
411*7bdf38e5Schristos 				expect_u_eq(tree_iterate_reverse(&tree), k+1,
412a0698ed9Schristos 				    "Unexpected node iteration count");
413a0698ed9Schristos 
414*7bdf38e5Schristos 				expect_false(tree_empty(&tree),
415a0698ed9Schristos 				    "Tree should not be empty");
416*7bdf38e5Schristos 				expect_ptr_not_null(tree_first(&tree),
417a0698ed9Schristos 				    "Tree should not be empty");
418*7bdf38e5Schristos 				expect_ptr_not_null(tree_last(&tree),
419a0698ed9Schristos 				    "Tree should not be empty");
420a0698ed9Schristos 
421a0698ed9Schristos 				tree_next(&tree, &nodes[k]);
422a0698ed9Schristos 				tree_prev(&tree, &nodes[k]);
423a0698ed9Schristos 			}
424a0698ed9Schristos 
425a0698ed9Schristos 			/* Remove nodes. */
426a0698ed9Schristos 			switch (i % 5) {
427a0698ed9Schristos 			case 0:
428a0698ed9Schristos 				for (k = 0; k < j; k++) {
429a0698ed9Schristos 					node_remove(&tree, &nodes[k], j - k);
430a0698ed9Schristos 				}
431a0698ed9Schristos 				break;
432a0698ed9Schristos 			case 1:
433a0698ed9Schristos 				for (k = j; k > 0; k--) {
434a0698ed9Schristos 					node_remove(&tree, &nodes[k-1], k);
435a0698ed9Schristos 				}
436a0698ed9Schristos 				break;
437a0698ed9Schristos 			case 2: {
438a0698ed9Schristos 				node_t *start;
439a0698ed9Schristos 				unsigned nnodes = j;
440a0698ed9Schristos 
441a0698ed9Schristos 				start = NULL;
442a0698ed9Schristos 				do {
443a0698ed9Schristos 					start = tree_iter(&tree, start,
444a0698ed9Schristos 					    remove_iterate_cb, (void *)&nnodes);
445a0698ed9Schristos 					nnodes--;
446a0698ed9Schristos 				} while (start != NULL);
447*7bdf38e5Schristos 				expect_u_eq(nnodes, 0,
448a0698ed9Schristos 				    "Removal terminated early");
449a0698ed9Schristos 				break;
450a0698ed9Schristos 			} case 3: {
451a0698ed9Schristos 				node_t *start;
452a0698ed9Schristos 				unsigned nnodes = j;
453a0698ed9Schristos 
454a0698ed9Schristos 				start = NULL;
455a0698ed9Schristos 				do {
456a0698ed9Schristos 					start = tree_reverse_iter(&tree, start,
457a0698ed9Schristos 					    remove_reverse_iterate_cb,
458a0698ed9Schristos 					    (void *)&nnodes);
459a0698ed9Schristos 					nnodes--;
460a0698ed9Schristos 				} while (start != NULL);
461*7bdf38e5Schristos 				expect_u_eq(nnodes, 0,
462a0698ed9Schristos 				    "Removal terminated early");
463a0698ed9Schristos 				break;
464a0698ed9Schristos 			} case 4: {
465a0698ed9Schristos 				unsigned nnodes = j;
466a0698ed9Schristos 				tree_destroy(&tree, destroy_cb, &nnodes);
467*7bdf38e5Schristos 				expect_u_eq(nnodes, 0,
468a0698ed9Schristos 				    "Destruction terminated early");
469a0698ed9Schristos 				break;
470a0698ed9Schristos 			} default:
471a0698ed9Schristos 				not_reached();
472a0698ed9Schristos 			}
473a0698ed9Schristos 		}
474a0698ed9Schristos 	}
475a0698ed9Schristos 	fini_gen_rand(sfmt);
476*7bdf38e5Schristos }
477*7bdf38e5Schristos TEST_END
478*7bdf38e5Schristos 
479*7bdf38e5Schristos static void
480*7bdf38e5Schristos expect_simple_consistency(tree_t *tree, uint64_t specialness,
481*7bdf38e5Schristos     bool expected_empty, node_t *expected_first, node_t *expected_last) {
482*7bdf38e5Schristos 	bool empty;
483*7bdf38e5Schristos 	node_t *first;
484*7bdf38e5Schristos 	node_t *last;
485*7bdf38e5Schristos 
486*7bdf38e5Schristos 	empty = tree_empty_filtered(tree, &specialness_filter_node,
487*7bdf38e5Schristos 	    &specialness_filter_subtree, &specialness);
488*7bdf38e5Schristos 	expect_b_eq(expected_empty, empty, "");
489*7bdf38e5Schristos 
490*7bdf38e5Schristos 	first = tree_first_filtered(tree,
491*7bdf38e5Schristos 	    &specialness_filter_node, &specialness_filter_subtree,
492*7bdf38e5Schristos 	    (void *)&specialness);
493*7bdf38e5Schristos 	expect_ptr_eq(expected_first, first, "");
494*7bdf38e5Schristos 
495*7bdf38e5Schristos 	last = tree_last_filtered(tree,
496*7bdf38e5Schristos 	    &specialness_filter_node, &specialness_filter_subtree,
497*7bdf38e5Schristos 	    (void *)&specialness);
498*7bdf38e5Schristos 	expect_ptr_eq(expected_last, last, "");
499*7bdf38e5Schristos }
500*7bdf38e5Schristos 
501*7bdf38e5Schristos TEST_BEGIN(test_rb_filter_simple) {
502*7bdf38e5Schristos 	enum {FILTER_NODES = 10};
503*7bdf38e5Schristos 	node_t nodes[FILTER_NODES];
504*7bdf38e5Schristos 	for (unsigned i = 0; i < FILTER_NODES; i++) {
505*7bdf38e5Schristos 		nodes[i].magic = NODE_MAGIC;
506*7bdf38e5Schristos 		nodes[i].key = i;
507*7bdf38e5Schristos 		if (i == 0) {
508*7bdf38e5Schristos 			nodes[i].specialness = 0;
509*7bdf38e5Schristos 		} else {
510*7bdf38e5Schristos 			nodes[i].specialness = ffs_u(i);
511*7bdf38e5Schristos 		}
512*7bdf38e5Schristos 		nodes[i].mid_remove = false;
513*7bdf38e5Schristos 		nodes[i].allow_duplicates = false;
514*7bdf38e5Schristos 		nodes[i].summary_lchild = NULL;
515*7bdf38e5Schristos 		nodes[i].summary_rchild = NULL;
516*7bdf38e5Schristos 		nodes[i].summary_max_specialness = 0;
517*7bdf38e5Schristos 	}
518*7bdf38e5Schristos 
519*7bdf38e5Schristos 	summarize_always_returns_true = false;
520*7bdf38e5Schristos 
521*7bdf38e5Schristos 	tree_t tree;
522*7bdf38e5Schristos 	tree_new(&tree);
523*7bdf38e5Schristos 
524*7bdf38e5Schristos 	/* Should be empty */
525*7bdf38e5Schristos 	expect_simple_consistency(&tree, /* specialness */ 0, /* empty */ true,
526*7bdf38e5Schristos 	    /* first */ NULL, /* last */ NULL);
527*7bdf38e5Schristos 
528*7bdf38e5Schristos 	/* Fill in just the odd nodes. */
529*7bdf38e5Schristos 	for (int i = 1; i < FILTER_NODES; i += 2) {
530*7bdf38e5Schristos 		tree_insert(&tree, &nodes[i]);
531*7bdf38e5Schristos 	}
532*7bdf38e5Schristos 
533*7bdf38e5Schristos 	/* A search for an odd node should succeed. */
534*7bdf38e5Schristos 	expect_simple_consistency(&tree, /* specialness */ 0, /* empty */ false,
535*7bdf38e5Schristos 	    /* first */ &nodes[1], /* last */ &nodes[9]);
536*7bdf38e5Schristos 
537*7bdf38e5Schristos 	/* But a search for an even one should fail. */
538*7bdf38e5Schristos 	expect_simple_consistency(&tree, /* specialness */ 1, /* empty */ true,
539*7bdf38e5Schristos 	    /* first */ NULL, /* last */ NULL);
540*7bdf38e5Schristos 
541*7bdf38e5Schristos 	/* Now we add an even. */
542*7bdf38e5Schristos 	tree_insert(&tree, &nodes[4]);
543*7bdf38e5Schristos 	expect_simple_consistency(&tree, /* specialness */ 1, /* empty */ false,
544*7bdf38e5Schristos 	    /* first */ &nodes[4], /* last */ &nodes[4]);
545*7bdf38e5Schristos 
546*7bdf38e5Schristos 	/* A smaller even, and a larger even. */
547*7bdf38e5Schristos 	tree_insert(&tree, &nodes[2]);
548*7bdf38e5Schristos 	tree_insert(&tree, &nodes[8]);
549*7bdf38e5Schristos 
550*7bdf38e5Schristos 	/*
551*7bdf38e5Schristos 	 * A first-search (resp. last-search) for an even should switch to the
552*7bdf38e5Schristos 	 * lower (higher) one, now that it's been added.
553*7bdf38e5Schristos 	 */
554*7bdf38e5Schristos 	expect_simple_consistency(&tree, /* specialness */ 1, /* empty */ false,
555*7bdf38e5Schristos 	    /* first */ &nodes[2], /* last */ &nodes[8]);
556*7bdf38e5Schristos 
557*7bdf38e5Schristos 	/*
558*7bdf38e5Schristos 	 * If we remove 2, a first-search we should go back to 4, while a
559*7bdf38e5Schristos 	 * last-search should remain unchanged.
560*7bdf38e5Schristos 	 */
561*7bdf38e5Schristos 	tree_remove(&tree, &nodes[2]);
562*7bdf38e5Schristos 	expect_simple_consistency(&tree, /* specialness */ 1, /* empty */ false,
563*7bdf38e5Schristos 	    /* first */ &nodes[4], /* last */ &nodes[8]);
564*7bdf38e5Schristos 
565*7bdf38e5Schristos 	/* Reinsert 2, then find it again. */
566*7bdf38e5Schristos 	tree_insert(&tree, &nodes[2]);
567*7bdf38e5Schristos 	expect_simple_consistency(&tree, /* specialness */ 1, /* empty */ false,
568*7bdf38e5Schristos 	    /* first */ &nodes[2], /* last */ &nodes[8]);
569*7bdf38e5Schristos 
570*7bdf38e5Schristos 	/* Searching for a multiple of 4 should not have changed. */
571*7bdf38e5Schristos 	expect_simple_consistency(&tree, /* specialness */ 2, /* empty */ false,
572*7bdf38e5Schristos 	    /* first */ &nodes[4], /* last */ &nodes[8]);
573*7bdf38e5Schristos 
574*7bdf38e5Schristos 	/* And a multiple of 8 */
575*7bdf38e5Schristos 	expect_simple_consistency(&tree, /* specialness */ 3, /* empty */ false,
576*7bdf38e5Schristos 	    /* first */ &nodes[8], /* last */ &nodes[8]);
577*7bdf38e5Schristos 
578*7bdf38e5Schristos 	/* But not a multiple of 16 */
579*7bdf38e5Schristos 	expect_simple_consistency(&tree, /* specialness */ 4, /* empty */ true,
580*7bdf38e5Schristos 	    /* first */ NULL, /* last */ NULL);
581*7bdf38e5Schristos }
582*7bdf38e5Schristos TEST_END
583*7bdf38e5Schristos 
584*7bdf38e5Schristos typedef struct iter_ctx_s iter_ctx_t;
585*7bdf38e5Schristos struct iter_ctx_s {
586*7bdf38e5Schristos 	int ncalls;
587*7bdf38e5Schristos 	node_t *last_node;
588*7bdf38e5Schristos 
589*7bdf38e5Schristos 	int ncalls_max;
590*7bdf38e5Schristos 	bool forward;
591*7bdf38e5Schristos };
592*7bdf38e5Schristos 
593*7bdf38e5Schristos static node_t *
594*7bdf38e5Schristos tree_iterate_filtered_cb(tree_t *tree, node_t *node, void *arg) {
595*7bdf38e5Schristos 	iter_ctx_t *ctx = (iter_ctx_t *)arg;
596*7bdf38e5Schristos 	ctx->ncalls++;
597*7bdf38e5Schristos 	expect_u64_ge(node->specialness, 1,
598*7bdf38e5Schristos 	    "Should only invoke cb on nodes that pass the filter");
599*7bdf38e5Schristos 	if (ctx->last_node != NULL) {
600*7bdf38e5Schristos 		if (ctx->forward) {
601*7bdf38e5Schristos 			expect_d_lt(node_cmp(ctx->last_node, node), 0,
602*7bdf38e5Schristos 			    "Incorrect iteration order");
603*7bdf38e5Schristos 		} else {
604*7bdf38e5Schristos 			expect_d_gt(node_cmp(ctx->last_node, node), 0,
605*7bdf38e5Schristos 			    "Incorrect iteration order");
606*7bdf38e5Schristos 		}
607*7bdf38e5Schristos 	}
608*7bdf38e5Schristos 	ctx->last_node = node;
609*7bdf38e5Schristos 	if (ctx->ncalls == ctx->ncalls_max) {
610*7bdf38e5Schristos 		return node;
611*7bdf38e5Schristos 	}
612*7bdf38e5Schristos 	return NULL;
613*7bdf38e5Schristos }
614*7bdf38e5Schristos 
615*7bdf38e5Schristos static int
616*7bdf38e5Schristos qsort_node_cmp(const void *ap, const void *bp) {
617*7bdf38e5Schristos 	node_t *a = *(node_t **)ap;
618*7bdf38e5Schristos 	node_t *b = *(node_t **)bp;
619*7bdf38e5Schristos 	return node_cmp(a, b);
620*7bdf38e5Schristos }
621*7bdf38e5Schristos 
622*7bdf38e5Schristos #define UPDATE_TEST_MAX 100
623*7bdf38e5Schristos static void
624*7bdf38e5Schristos check_consistency(tree_t *tree, node_t nodes[UPDATE_TEST_MAX], int nnodes) {
625*7bdf38e5Schristos 	uint64_t specialness = 1;
626*7bdf38e5Schristos 
627*7bdf38e5Schristos 	bool empty;
628*7bdf38e5Schristos 	bool real_empty = true;
629*7bdf38e5Schristos 	node_t *first;
630*7bdf38e5Schristos 	node_t *real_first = NULL;
631*7bdf38e5Schristos 	node_t *last;
632*7bdf38e5Schristos 	node_t *real_last = NULL;
633*7bdf38e5Schristos 	for (int i = 0; i < nnodes; i++) {
634*7bdf38e5Schristos 		if (nodes[i].specialness >= specialness) {
635*7bdf38e5Schristos 			real_empty = false;
636*7bdf38e5Schristos 			if (real_first == NULL
637*7bdf38e5Schristos 			    || node_cmp(&nodes[i], real_first) < 0) {
638*7bdf38e5Schristos 				real_first = &nodes[i];
639*7bdf38e5Schristos 			}
640*7bdf38e5Schristos 			if (real_last == NULL
641*7bdf38e5Schristos 			    || node_cmp(&nodes[i], real_last) > 0) {
642*7bdf38e5Schristos 				real_last = &nodes[i];
643*7bdf38e5Schristos 			}
644*7bdf38e5Schristos 		}
645*7bdf38e5Schristos 	}
646*7bdf38e5Schristos 
647*7bdf38e5Schristos 	empty = tree_empty_filtered(tree, &specialness_filter_node,
648*7bdf38e5Schristos 	    &specialness_filter_subtree, &specialness);
649*7bdf38e5Schristos 	expect_b_eq(real_empty, empty, "");
650*7bdf38e5Schristos 
651*7bdf38e5Schristos 	first = tree_first_filtered(tree, &specialness_filter_node,
652*7bdf38e5Schristos 	    &specialness_filter_subtree, &specialness);
653*7bdf38e5Schristos 	expect_ptr_eq(real_first, first, "");
654*7bdf38e5Schristos 
655*7bdf38e5Schristos 	last = tree_last_filtered(tree, &specialness_filter_node,
656*7bdf38e5Schristos 	    &specialness_filter_subtree, &specialness);
657*7bdf38e5Schristos 	expect_ptr_eq(real_last, last, "");
658*7bdf38e5Schristos 
659*7bdf38e5Schristos 	for (int i = 0; i < nnodes; i++) {
660*7bdf38e5Schristos 		node_t *next_filtered;
661*7bdf38e5Schristos 		node_t *real_next_filtered = NULL;
662*7bdf38e5Schristos 		node_t *prev_filtered;
663*7bdf38e5Schristos 		node_t *real_prev_filtered = NULL;
664*7bdf38e5Schristos 		for (int j = 0; j < nnodes; j++) {
665*7bdf38e5Schristos 			if (nodes[j].specialness < specialness) {
666*7bdf38e5Schristos 				continue;
667*7bdf38e5Schristos 			}
668*7bdf38e5Schristos 			if (node_cmp(&nodes[j], &nodes[i]) < 0
669*7bdf38e5Schristos 			    && (real_prev_filtered == NULL
670*7bdf38e5Schristos 			    || node_cmp(&nodes[j], real_prev_filtered) > 0)) {
671*7bdf38e5Schristos 				real_prev_filtered = &nodes[j];
672*7bdf38e5Schristos 			}
673*7bdf38e5Schristos 			if (node_cmp(&nodes[j], &nodes[i]) > 0
674*7bdf38e5Schristos 			    && (real_next_filtered == NULL
675*7bdf38e5Schristos 			    || node_cmp(&nodes[j], real_next_filtered) < 0)) {
676*7bdf38e5Schristos 				real_next_filtered = &nodes[j];
677*7bdf38e5Schristos 			}
678*7bdf38e5Schristos 		}
679*7bdf38e5Schristos 		next_filtered = tree_next_filtered(tree, &nodes[i],
680*7bdf38e5Schristos 		    &specialness_filter_node, &specialness_filter_subtree,
681*7bdf38e5Schristos 		    &specialness);
682*7bdf38e5Schristos 		expect_ptr_eq(real_next_filtered, next_filtered, "");
683*7bdf38e5Schristos 
684*7bdf38e5Schristos 		prev_filtered = tree_prev_filtered(tree, &nodes[i],
685*7bdf38e5Schristos 		    &specialness_filter_node, &specialness_filter_subtree,
686*7bdf38e5Schristos 		    &specialness);
687*7bdf38e5Schristos 		expect_ptr_eq(real_prev_filtered, prev_filtered, "");
688*7bdf38e5Schristos 
689*7bdf38e5Schristos 		node_t *search_filtered;
690*7bdf38e5Schristos 		node_t *real_search_filtered;
691*7bdf38e5Schristos 		node_t *nsearch_filtered;
692*7bdf38e5Schristos 		node_t *real_nsearch_filtered;
693*7bdf38e5Schristos 		node_t *psearch_filtered;
694*7bdf38e5Schristos 		node_t *real_psearch_filtered;
695*7bdf38e5Schristos 
696*7bdf38e5Schristos 		/*
697*7bdf38e5Schristos 		 * search, nsearch, psearch from a node before nodes[i] in the
698*7bdf38e5Schristos 		 * ordering.
699*7bdf38e5Schristos 		 */
700*7bdf38e5Schristos 		node_t before;
701*7bdf38e5Schristos 		before.magic = NODE_MAGIC;
702*7bdf38e5Schristos 		before.key = nodes[i].key - 1;
703*7bdf38e5Schristos 		before.allow_duplicates = false;
704*7bdf38e5Schristos 		real_search_filtered = NULL;
705*7bdf38e5Schristos 		search_filtered = tree_search_filtered(tree, &before,
706*7bdf38e5Schristos 		    &specialness_filter_node, &specialness_filter_subtree,
707*7bdf38e5Schristos 		    &specialness);
708*7bdf38e5Schristos 		expect_ptr_eq(real_search_filtered, search_filtered, "");
709*7bdf38e5Schristos 
710*7bdf38e5Schristos 		real_nsearch_filtered = (nodes[i].specialness >= specialness ?
711*7bdf38e5Schristos 		    &nodes[i] : real_next_filtered);
712*7bdf38e5Schristos 		nsearch_filtered = tree_nsearch_filtered(tree, &before,
713*7bdf38e5Schristos 		    &specialness_filter_node, &specialness_filter_subtree,
714*7bdf38e5Schristos 		    &specialness);
715*7bdf38e5Schristos 		expect_ptr_eq(real_nsearch_filtered, nsearch_filtered, "");
716*7bdf38e5Schristos 
717*7bdf38e5Schristos 		real_psearch_filtered = real_prev_filtered;
718*7bdf38e5Schristos 		psearch_filtered = tree_psearch_filtered(tree, &before,
719*7bdf38e5Schristos 		    &specialness_filter_node, &specialness_filter_subtree,
720*7bdf38e5Schristos 		    &specialness);
721*7bdf38e5Schristos 		expect_ptr_eq(real_psearch_filtered, psearch_filtered, "");
722*7bdf38e5Schristos 
723*7bdf38e5Schristos 		/* search, nsearch, psearch from nodes[i] */
724*7bdf38e5Schristos 		real_search_filtered = (nodes[i].specialness >= specialness ?
725*7bdf38e5Schristos 		    &nodes[i] : NULL);
726*7bdf38e5Schristos 		search_filtered = tree_search_filtered(tree, &nodes[i],
727*7bdf38e5Schristos 		    &specialness_filter_node, &specialness_filter_subtree,
728*7bdf38e5Schristos 		    &specialness);
729*7bdf38e5Schristos 		expect_ptr_eq(real_search_filtered, search_filtered, "");
730*7bdf38e5Schristos 
731*7bdf38e5Schristos 		real_nsearch_filtered = (nodes[i].specialness >= specialness ?
732*7bdf38e5Schristos 		    &nodes[i] : real_next_filtered);
733*7bdf38e5Schristos 		nsearch_filtered = tree_nsearch_filtered(tree, &nodes[i],
734*7bdf38e5Schristos 		    &specialness_filter_node, &specialness_filter_subtree,
735*7bdf38e5Schristos 		    &specialness);
736*7bdf38e5Schristos 		expect_ptr_eq(real_nsearch_filtered, nsearch_filtered, "");
737*7bdf38e5Schristos 
738*7bdf38e5Schristos 		real_psearch_filtered = (nodes[i].specialness >= specialness ?
739*7bdf38e5Schristos 		    &nodes[i] : real_prev_filtered);
740*7bdf38e5Schristos 		psearch_filtered = tree_psearch_filtered(tree, &nodes[i],
741*7bdf38e5Schristos 		    &specialness_filter_node, &specialness_filter_subtree,
742*7bdf38e5Schristos 		    &specialness);
743*7bdf38e5Schristos 		expect_ptr_eq(real_psearch_filtered, psearch_filtered, "");
744*7bdf38e5Schristos 
745*7bdf38e5Schristos 		/*
746*7bdf38e5Schristos 		 * search, nsearch, psearch from a node equivalent to but
747*7bdf38e5Schristos 		 * distinct from nodes[i].
748*7bdf38e5Schristos 		 */
749*7bdf38e5Schristos 		node_t equiv;
750*7bdf38e5Schristos 		equiv.magic = NODE_MAGIC;
751*7bdf38e5Schristos 		equiv.key = nodes[i].key;
752*7bdf38e5Schristos 		equiv.allow_duplicates = true;
753*7bdf38e5Schristos 		real_search_filtered = (nodes[i].specialness >= specialness ?
754*7bdf38e5Schristos 		    &nodes[i] : NULL);
755*7bdf38e5Schristos 		search_filtered = tree_search_filtered(tree, &equiv,
756*7bdf38e5Schristos 		    &specialness_filter_node, &specialness_filter_subtree,
757*7bdf38e5Schristos 		    &specialness);
758*7bdf38e5Schristos 		expect_ptr_eq(real_search_filtered, search_filtered, "");
759*7bdf38e5Schristos 
760*7bdf38e5Schristos 		real_nsearch_filtered = (nodes[i].specialness >= specialness ?
761*7bdf38e5Schristos 		    &nodes[i] : real_next_filtered);
762*7bdf38e5Schristos 		nsearch_filtered = tree_nsearch_filtered(tree, &equiv,
763*7bdf38e5Schristos 		    &specialness_filter_node, &specialness_filter_subtree,
764*7bdf38e5Schristos 		    &specialness);
765*7bdf38e5Schristos 		expect_ptr_eq(real_nsearch_filtered, nsearch_filtered, "");
766*7bdf38e5Schristos 
767*7bdf38e5Schristos 		real_psearch_filtered = (nodes[i].specialness >= specialness ?
768*7bdf38e5Schristos 		    &nodes[i] : real_prev_filtered);
769*7bdf38e5Schristos 		psearch_filtered = tree_psearch_filtered(tree, &equiv,
770*7bdf38e5Schristos 		    &specialness_filter_node, &specialness_filter_subtree,
771*7bdf38e5Schristos 		    &specialness);
772*7bdf38e5Schristos 		expect_ptr_eq(real_psearch_filtered, psearch_filtered, "");
773*7bdf38e5Schristos 
774*7bdf38e5Schristos 		/*
775*7bdf38e5Schristos 		 * search, nsearch, psearch from a node after nodes[i] in the
776*7bdf38e5Schristos 		 * ordering.
777*7bdf38e5Schristos 		 */
778*7bdf38e5Schristos 		node_t after;
779*7bdf38e5Schristos 		after.magic = NODE_MAGIC;
780*7bdf38e5Schristos 		after.key = nodes[i].key + 1;
781*7bdf38e5Schristos 		after.allow_duplicates = false;
782*7bdf38e5Schristos 		real_search_filtered = NULL;
783*7bdf38e5Schristos 		search_filtered = tree_search_filtered(tree, &after,
784*7bdf38e5Schristos 		    &specialness_filter_node, &specialness_filter_subtree,
785*7bdf38e5Schristos 		    &specialness);
786*7bdf38e5Schristos 		expect_ptr_eq(real_search_filtered, search_filtered, "");
787*7bdf38e5Schristos 
788*7bdf38e5Schristos 		real_nsearch_filtered = real_next_filtered;
789*7bdf38e5Schristos 		nsearch_filtered = tree_nsearch_filtered(tree, &after,
790*7bdf38e5Schristos 		    &specialness_filter_node, &specialness_filter_subtree,
791*7bdf38e5Schristos 		    &specialness);
792*7bdf38e5Schristos 		expect_ptr_eq(real_nsearch_filtered, nsearch_filtered, "");
793*7bdf38e5Schristos 
794*7bdf38e5Schristos 		real_psearch_filtered = (nodes[i].specialness >= specialness ?
795*7bdf38e5Schristos 		    &nodes[i] : real_prev_filtered);
796*7bdf38e5Schristos 		psearch_filtered = tree_psearch_filtered(tree, &after,
797*7bdf38e5Schristos 		    &specialness_filter_node, &specialness_filter_subtree,
798*7bdf38e5Schristos 		    &specialness);
799*7bdf38e5Schristos 		expect_ptr_eq(real_psearch_filtered, psearch_filtered, "");
800*7bdf38e5Schristos 	}
801*7bdf38e5Schristos 
802*7bdf38e5Schristos 	/* Filtered iteration test setup. */
803*7bdf38e5Schristos 	int nspecial = 0;
804*7bdf38e5Schristos 	node_t *sorted_nodes[UPDATE_TEST_MAX];
805*7bdf38e5Schristos 	node_t *sorted_filtered_nodes[UPDATE_TEST_MAX];
806*7bdf38e5Schristos 	for (int i = 0; i < nnodes; i++) {
807*7bdf38e5Schristos 		sorted_nodes[i] = &nodes[i];
808*7bdf38e5Schristos 	}
809*7bdf38e5Schristos 	qsort(sorted_nodes, nnodes, sizeof(node_t *), &qsort_node_cmp);
810*7bdf38e5Schristos 	for (int i = 0; i < nnodes; i++) {
811*7bdf38e5Schristos 		sorted_nodes[i]->rank = i;
812*7bdf38e5Schristos 		sorted_nodes[i]->filtered_rank = nspecial;
813*7bdf38e5Schristos 		if (sorted_nodes[i]->specialness >= 1) {
814*7bdf38e5Schristos 			sorted_filtered_nodes[nspecial] = sorted_nodes[i];
815*7bdf38e5Schristos 			nspecial++;
816*7bdf38e5Schristos 		}
817*7bdf38e5Schristos 	}
818*7bdf38e5Schristos 
819*7bdf38e5Schristos 	node_t *iter_result;
820*7bdf38e5Schristos 
821*7bdf38e5Schristos 	iter_ctx_t ctx;
822*7bdf38e5Schristos 	ctx.ncalls = 0;
823*7bdf38e5Schristos 	ctx.last_node = NULL;
824*7bdf38e5Schristos 	ctx.ncalls_max = INT_MAX;
825*7bdf38e5Schristos 	ctx.forward = true;
826*7bdf38e5Schristos 
827*7bdf38e5Schristos 	/* Filtered forward iteration from the beginning. */
828*7bdf38e5Schristos 	iter_result = tree_iter_filtered(tree, NULL, &tree_iterate_filtered_cb,
829*7bdf38e5Schristos 	    &ctx, &specialness_filter_node, &specialness_filter_subtree,
830*7bdf38e5Schristos 	    &specialness);
831*7bdf38e5Schristos 	expect_ptr_null(iter_result, "");
832*7bdf38e5Schristos 	expect_d_eq(nspecial, ctx.ncalls, "");
833*7bdf38e5Schristos 	/* Filtered forward iteration from a starting point. */
834*7bdf38e5Schristos 	for (int i = 0; i < nnodes; i++) {
835*7bdf38e5Schristos 		ctx.ncalls = 0;
836*7bdf38e5Schristos 		ctx.last_node = NULL;
837*7bdf38e5Schristos 		iter_result = tree_iter_filtered(tree, &nodes[i],
838*7bdf38e5Schristos 		    &tree_iterate_filtered_cb, &ctx, &specialness_filter_node,
839*7bdf38e5Schristos 		    &specialness_filter_subtree, &specialness);
840*7bdf38e5Schristos 		expect_ptr_null(iter_result, "");
841*7bdf38e5Schristos 		expect_d_eq(nspecial - nodes[i].filtered_rank, ctx.ncalls, "");
842*7bdf38e5Schristos 	}
843*7bdf38e5Schristos 	/* Filtered forward iteration from the beginning, with stopping */
844*7bdf38e5Schristos 	for (int i = 0; i < nspecial; i++) {
845*7bdf38e5Schristos 		ctx.ncalls = 0;
846*7bdf38e5Schristos 		ctx.last_node = NULL;
847*7bdf38e5Schristos 		ctx.ncalls_max = i + 1;
848*7bdf38e5Schristos 		iter_result = tree_iter_filtered(tree, NULL,
849*7bdf38e5Schristos 		    &tree_iterate_filtered_cb, &ctx, &specialness_filter_node,
850*7bdf38e5Schristos 		    &specialness_filter_subtree, &specialness);
851*7bdf38e5Schristos 		expect_ptr_eq(sorted_filtered_nodes[i], iter_result, "");
852*7bdf38e5Schristos 		expect_d_eq(ctx.ncalls, i + 1, "");
853*7bdf38e5Schristos 	}
854*7bdf38e5Schristos 	/* Filtered forward iteration from a starting point, with stopping. */
855*7bdf38e5Schristos 	for (int i = 0; i < nnodes; i++) {
856*7bdf38e5Schristos 		for (int j = 0; j < nspecial - nodes[i].filtered_rank; j++) {
857*7bdf38e5Schristos 			ctx.ncalls = 0;
858*7bdf38e5Schristos 			ctx.last_node = NULL;
859*7bdf38e5Schristos 			ctx.ncalls_max = j + 1;
860*7bdf38e5Schristos 			iter_result = tree_iter_filtered(tree, &nodes[i],
861*7bdf38e5Schristos 			    &tree_iterate_filtered_cb, &ctx,
862*7bdf38e5Schristos 			    &specialness_filter_node,
863*7bdf38e5Schristos 			    &specialness_filter_subtree, &specialness);
864*7bdf38e5Schristos 			expect_d_eq(j + 1, ctx.ncalls, "");
865*7bdf38e5Schristos 			expect_ptr_eq(sorted_filtered_nodes[
866*7bdf38e5Schristos 			    nodes[i].filtered_rank + j], iter_result, "");
867*7bdf38e5Schristos 		}
868*7bdf38e5Schristos 	}
869*7bdf38e5Schristos 
870*7bdf38e5Schristos 	/* Backwards iteration. */
871*7bdf38e5Schristos 	ctx.ncalls = 0;
872*7bdf38e5Schristos 	ctx.last_node = NULL;
873*7bdf38e5Schristos 	ctx.ncalls_max = INT_MAX;
874*7bdf38e5Schristos 	ctx.forward = false;
875*7bdf38e5Schristos 
876*7bdf38e5Schristos 	/* Filtered backward iteration from the end. */
877*7bdf38e5Schristos 	iter_result = tree_reverse_iter_filtered(tree, NULL,
878*7bdf38e5Schristos 	    &tree_iterate_filtered_cb, &ctx, &specialness_filter_node,
879*7bdf38e5Schristos 	    &specialness_filter_subtree, &specialness);
880*7bdf38e5Schristos 	expect_ptr_null(iter_result, "");
881*7bdf38e5Schristos 	expect_d_eq(nspecial, ctx.ncalls, "");
882*7bdf38e5Schristos 	/* Filtered backward iteration from a starting point. */
883*7bdf38e5Schristos 	for (int i = 0; i < nnodes; i++) {
884*7bdf38e5Schristos 		ctx.ncalls = 0;
885*7bdf38e5Schristos 		ctx.last_node = NULL;
886*7bdf38e5Schristos 		iter_result = tree_reverse_iter_filtered(tree, &nodes[i],
887*7bdf38e5Schristos 		    &tree_iterate_filtered_cb, &ctx, &specialness_filter_node,
888*7bdf38e5Schristos 		    &specialness_filter_subtree, &specialness);
889*7bdf38e5Schristos 		expect_ptr_null(iter_result, "");
890*7bdf38e5Schristos 		int surplus_rank = (nodes[i].specialness >= 1 ? 1 : 0);
891*7bdf38e5Schristos 		expect_d_eq(nodes[i].filtered_rank + surplus_rank, ctx.ncalls,
892*7bdf38e5Schristos 		    "");
893*7bdf38e5Schristos 	}
894*7bdf38e5Schristos 	/* Filtered backward iteration from the end, with stopping */
895*7bdf38e5Schristos 	for (int i = 0; i < nspecial; i++) {
896*7bdf38e5Schristos 		ctx.ncalls = 0;
897*7bdf38e5Schristos 		ctx.last_node = NULL;
898*7bdf38e5Schristos 		ctx.ncalls_max = i + 1;
899*7bdf38e5Schristos 		iter_result = tree_reverse_iter_filtered(tree, NULL,
900*7bdf38e5Schristos 		    &tree_iterate_filtered_cb, &ctx, &specialness_filter_node,
901*7bdf38e5Schristos 		    &specialness_filter_subtree, &specialness);
902*7bdf38e5Schristos 		expect_ptr_eq(sorted_filtered_nodes[nspecial - i - 1],
903*7bdf38e5Schristos 		    iter_result, "");
904*7bdf38e5Schristos 		expect_d_eq(ctx.ncalls, i + 1, "");
905*7bdf38e5Schristos 	}
906*7bdf38e5Schristos 	/* Filtered backward iteration from a starting point, with stopping. */
907*7bdf38e5Schristos 	for (int i = 0; i < nnodes; i++) {
908*7bdf38e5Schristos 		int surplus_rank = (nodes[i].specialness >= 1 ? 1 : 0);
909*7bdf38e5Schristos 		for (int j = 0; j < nodes[i].filtered_rank + surplus_rank;
910*7bdf38e5Schristos 		    j++) {
911*7bdf38e5Schristos 			ctx.ncalls = 0;
912*7bdf38e5Schristos 			ctx.last_node = NULL;
913*7bdf38e5Schristos 			ctx.ncalls_max = j + 1;
914*7bdf38e5Schristos 			iter_result = tree_reverse_iter_filtered(tree,
915*7bdf38e5Schristos 			    &nodes[i], &tree_iterate_filtered_cb, &ctx,
916*7bdf38e5Schristos 			    &specialness_filter_node,
917*7bdf38e5Schristos 			    &specialness_filter_subtree, &specialness);
918*7bdf38e5Schristos 			expect_d_eq(j + 1, ctx.ncalls, "");
919*7bdf38e5Schristos 			expect_ptr_eq(sorted_filtered_nodes[
920*7bdf38e5Schristos 			    nodes[i].filtered_rank - j - 1 + surplus_rank],
921*7bdf38e5Schristos 			    iter_result, "");
922*7bdf38e5Schristos 		}
923*7bdf38e5Schristos 	}
924*7bdf38e5Schristos }
925*7bdf38e5Schristos 
926*7bdf38e5Schristos static void
927*7bdf38e5Schristos do_update_search_test(int nnodes, int ntrees, int nremovals,
928*7bdf38e5Schristos     int nupdates) {
929*7bdf38e5Schristos 	node_t nodes[UPDATE_TEST_MAX];
930*7bdf38e5Schristos 	assert(nnodes <= UPDATE_TEST_MAX);
931*7bdf38e5Schristos 
932*7bdf38e5Schristos 	sfmt_t *sfmt = init_gen_rand(12345);
933*7bdf38e5Schristos 	for (int i = 0; i < ntrees; i++) {
934*7bdf38e5Schristos 		tree_t tree;
935*7bdf38e5Schristos 		tree_new(&tree);
936*7bdf38e5Schristos 		for (int j = 0; j < nnodes; j++) {
937*7bdf38e5Schristos 			nodes[j].magic = NODE_MAGIC;
938*7bdf38e5Schristos 			/*
939*7bdf38e5Schristos 			 * In consistency checking, we increment or decrement a
940*7bdf38e5Schristos 			 * key and assume that the result is not a key in the
941*7bdf38e5Schristos 			 * tree.  This isn't a *real* concern with 64-bit keys
942*7bdf38e5Schristos 			 * and a good PRNG, but why not be correct anyways?
943*7bdf38e5Schristos 			 */
944*7bdf38e5Schristos 			nodes[j].key = 2 * gen_rand64(sfmt);
945*7bdf38e5Schristos 			nodes[j].specialness = 0;
946*7bdf38e5Schristos 			nodes[j].mid_remove = false;
947*7bdf38e5Schristos 			nodes[j].allow_duplicates = false;
948*7bdf38e5Schristos 			nodes[j].summary_lchild = NULL;
949*7bdf38e5Schristos 			nodes[j].summary_rchild = NULL;
950*7bdf38e5Schristos 			nodes[j].summary_max_specialness = 0;
951*7bdf38e5Schristos 			tree_insert(&tree, &nodes[j]);
952*7bdf38e5Schristos 		}
953*7bdf38e5Schristos 		for (int j = 0; j < nremovals; j++) {
954*7bdf38e5Schristos 			int victim = (int)gen_rand64_range(sfmt, nnodes);
955*7bdf38e5Schristos 			if (!nodes[victim].mid_remove) {
956*7bdf38e5Schristos 				tree_remove(&tree, &nodes[victim]);
957*7bdf38e5Schristos 				nodes[victim].mid_remove = true;
958*7bdf38e5Schristos 			}
959*7bdf38e5Schristos 		}
960*7bdf38e5Schristos 		for (int j = 0; j < nnodes; j++) {
961*7bdf38e5Schristos 			if (nodes[j].mid_remove) {
962*7bdf38e5Schristos 				nodes[j].mid_remove = false;
963*7bdf38e5Schristos 				nodes[j].key = 2 * gen_rand64(sfmt);
964*7bdf38e5Schristos 				tree_insert(&tree, &nodes[j]);
965*7bdf38e5Schristos 			}
966*7bdf38e5Schristos 		}
967*7bdf38e5Schristos 		for (int j = 0; j < nupdates; j++) {
968*7bdf38e5Schristos 			uint32_t ind = gen_rand32_range(sfmt, nnodes);
969*7bdf38e5Schristos 			nodes[ind].specialness = 1 - nodes[ind].specialness;
970*7bdf38e5Schristos 			tree_update_summaries(&tree, &nodes[ind]);
971*7bdf38e5Schristos 			check_consistency(&tree, nodes, nnodes);
972*7bdf38e5Schristos 		}
973*7bdf38e5Schristos 	}
974*7bdf38e5Schristos }
975*7bdf38e5Schristos 
976*7bdf38e5Schristos TEST_BEGIN(test_rb_update_search) {
977*7bdf38e5Schristos 	summarize_always_returns_true = false;
978*7bdf38e5Schristos 	do_update_search_test(2, 100, 3, 50);
979*7bdf38e5Schristos 	do_update_search_test(5, 100, 3, 50);
980*7bdf38e5Schristos 	do_update_search_test(12, 100, 5, 1000);
981*7bdf38e5Schristos 	do_update_search_test(100, 1, 50, 500);
982*7bdf38e5Schristos }
983*7bdf38e5Schristos TEST_END
984*7bdf38e5Schristos 
985*7bdf38e5Schristos typedef rb_tree(node_t) unsummarized_tree_t;
986*7bdf38e5Schristos rb_gen(static UNUSED, unsummarized_tree_, unsummarized_tree_t, node_t, link,
987*7bdf38e5Schristos     node_cmp);
988*7bdf38e5Schristos 
989*7bdf38e5Schristos static node_t *
990*7bdf38e5Schristos unsummarized_tree_iterate_cb(unsummarized_tree_t *tree, node_t *node,
991*7bdf38e5Schristos     void *data) {
992*7bdf38e5Schristos 	unsigned *i = (unsigned *)data;
993*7bdf38e5Schristos 	(*i)++;
994*7bdf38e5Schristos 	return NULL;
995*7bdf38e5Schristos }
996*7bdf38e5Schristos /*
997*7bdf38e5Schristos  * The unsummarized and summarized funtionality is implemented via the same
998*7bdf38e5Schristos  * functions; we don't really need to do much more than test that we can exclude
999*7bdf38e5Schristos  * the filtered functionality without anything breaking.
1000*7bdf38e5Schristos  */
1001*7bdf38e5Schristos TEST_BEGIN(test_rb_unsummarized) {
1002*7bdf38e5Schristos 	unsummarized_tree_t tree;
1003*7bdf38e5Schristos 	unsummarized_tree_new(&tree);
1004*7bdf38e5Schristos 	unsigned nnodes = 0;
1005*7bdf38e5Schristos 	unsummarized_tree_iter(&tree, NULL, &unsummarized_tree_iterate_cb,
1006*7bdf38e5Schristos 	    &nnodes);
1007*7bdf38e5Schristos 	expect_u_eq(0, nnodes, "");
1008a0698ed9Schristos }
1009a0698ed9Schristos TEST_END
1010a0698ed9Schristos 
1011a0698ed9Schristos int
1012a0698ed9Schristos main(void) {
1013*7bdf38e5Schristos 	return test_no_reentrancy(
1014a0698ed9Schristos 	    test_rb_empty,
1015*7bdf38e5Schristos 	    test_rb_random,
1016*7bdf38e5Schristos 	    test_rb_filter_simple,
1017*7bdf38e5Schristos 	    test_rb_update_search,
1018*7bdf38e5Schristos 	    test_rb_unsummarized);
1019a0698ed9Schristos }
1020