xref: /netbsd-src/external/bsd/jemalloc.old/dist/test/unit/rb.c (revision 8e33eff89e26cf71871ead62f0d5063e1313c33a)
1*8e33eff8Schristos #include "test/jemalloc_test.h"
2*8e33eff8Schristos 
3*8e33eff8Schristos #include "jemalloc/internal/rb.h"
4*8e33eff8Schristos 
5*8e33eff8Schristos #define rbtn_black_height(a_type, a_field, a_rbt, r_height) do {	\
6*8e33eff8Schristos 	a_type *rbp_bh_t;						\
7*8e33eff8Schristos 	for (rbp_bh_t = (a_rbt)->rbt_root, (r_height) = 0; rbp_bh_t !=	\
8*8e33eff8Schristos 	    NULL; rbp_bh_t = rbtn_left_get(a_type, a_field,		\
9*8e33eff8Schristos 	    rbp_bh_t)) {						\
10*8e33eff8Schristos 		if (!rbtn_red_get(a_type, a_field, rbp_bh_t)) {		\
11*8e33eff8Schristos 		(r_height)++;						\
12*8e33eff8Schristos 		}							\
13*8e33eff8Schristos 	}								\
14*8e33eff8Schristos } while (0)
15*8e33eff8Schristos 
16*8e33eff8Schristos typedef struct node_s node_t;
17*8e33eff8Schristos 
18*8e33eff8Schristos struct node_s {
19*8e33eff8Schristos #define NODE_MAGIC 0x9823af7e
20*8e33eff8Schristos 	uint32_t magic;
21*8e33eff8Schristos 	rb_node(node_t) link;
22*8e33eff8Schristos 	uint64_t key;
23*8e33eff8Schristos };
24*8e33eff8Schristos 
25*8e33eff8Schristos static int
26*8e33eff8Schristos node_cmp(const node_t *a, const node_t *b) {
27*8e33eff8Schristos 	int ret;
28*8e33eff8Schristos 
29*8e33eff8Schristos 	assert_u32_eq(a->magic, NODE_MAGIC, "Bad magic");
30*8e33eff8Schristos 	assert_u32_eq(b->magic, NODE_MAGIC, "Bad magic");
31*8e33eff8Schristos 
32*8e33eff8Schristos 	ret = (a->key > b->key) - (a->key < b->key);
33*8e33eff8Schristos 	if (ret == 0) {
34*8e33eff8Schristos 		/*
35*8e33eff8Schristos 		 * Duplicates are not allowed in the tree, so force an
36*8e33eff8Schristos 		 * arbitrary ordering for non-identical items with equal keys.
37*8e33eff8Schristos 		 */
38*8e33eff8Schristos 		ret = (((uintptr_t)a) > ((uintptr_t)b))
39*8e33eff8Schristos 		    - (((uintptr_t)a) < ((uintptr_t)b));
40*8e33eff8Schristos 	}
41*8e33eff8Schristos 	return ret;
42*8e33eff8Schristos }
43*8e33eff8Schristos 
44*8e33eff8Schristos typedef rb_tree(node_t) tree_t;
45*8e33eff8Schristos rb_gen(static, tree_, tree_t, node_t, link, node_cmp);
46*8e33eff8Schristos 
47*8e33eff8Schristos TEST_BEGIN(test_rb_empty) {
48*8e33eff8Schristos 	tree_t tree;
49*8e33eff8Schristos 	node_t key;
50*8e33eff8Schristos 
51*8e33eff8Schristos 	tree_new(&tree);
52*8e33eff8Schristos 
53*8e33eff8Schristos 	assert_true(tree_empty(&tree), "Tree should be empty");
54*8e33eff8Schristos 	assert_ptr_null(tree_first(&tree), "Unexpected node");
55*8e33eff8Schristos 	assert_ptr_null(tree_last(&tree), "Unexpected node");
56*8e33eff8Schristos 
57*8e33eff8Schristos 	key.key = 0;
58*8e33eff8Schristos 	key.magic = NODE_MAGIC;
59*8e33eff8Schristos 	assert_ptr_null(tree_search(&tree, &key), "Unexpected node");
60*8e33eff8Schristos 
61*8e33eff8Schristos 	key.key = 0;
62*8e33eff8Schristos 	key.magic = NODE_MAGIC;
63*8e33eff8Schristos 	assert_ptr_null(tree_nsearch(&tree, &key), "Unexpected node");
64*8e33eff8Schristos 
65*8e33eff8Schristos 	key.key = 0;
66*8e33eff8Schristos 	key.magic = NODE_MAGIC;
67*8e33eff8Schristos 	assert_ptr_null(tree_psearch(&tree, &key), "Unexpected node");
68*8e33eff8Schristos }
69*8e33eff8Schristos TEST_END
70*8e33eff8Schristos 
71*8e33eff8Schristos static unsigned
72*8e33eff8Schristos tree_recurse(node_t *node, unsigned black_height, unsigned black_depth) {
73*8e33eff8Schristos 	unsigned ret = 0;
74*8e33eff8Schristos 	node_t *left_node;
75*8e33eff8Schristos 	node_t *right_node;
76*8e33eff8Schristos 
77*8e33eff8Schristos 	if (node == NULL) {
78*8e33eff8Schristos 		return ret;
79*8e33eff8Schristos 	}
80*8e33eff8Schristos 
81*8e33eff8Schristos 	left_node = rbtn_left_get(node_t, link, node);
82*8e33eff8Schristos 	right_node = rbtn_right_get(node_t, link, node);
83*8e33eff8Schristos 
84*8e33eff8Schristos 	if (!rbtn_red_get(node_t, link, node)) {
85*8e33eff8Schristos 		black_depth++;
86*8e33eff8Schristos 	}
87*8e33eff8Schristos 
88*8e33eff8Schristos 	/* Red nodes must be interleaved with black nodes. */
89*8e33eff8Schristos 	if (rbtn_red_get(node_t, link, node)) {
90*8e33eff8Schristos 		if (left_node != NULL) {
91*8e33eff8Schristos 			assert_false(rbtn_red_get(node_t, link, left_node),
92*8e33eff8Schristos 				"Node should be black");
93*8e33eff8Schristos 		}
94*8e33eff8Schristos 		if (right_node != NULL) {
95*8e33eff8Schristos 			assert_false(rbtn_red_get(node_t, link, right_node),
96*8e33eff8Schristos 			    "Node should be black");
97*8e33eff8Schristos 		}
98*8e33eff8Schristos 	}
99*8e33eff8Schristos 
100*8e33eff8Schristos 	/* Self. */
101*8e33eff8Schristos 	assert_u32_eq(node->magic, NODE_MAGIC, "Bad magic");
102*8e33eff8Schristos 
103*8e33eff8Schristos 	/* Left subtree. */
104*8e33eff8Schristos 	if (left_node != NULL) {
105*8e33eff8Schristos 		ret += tree_recurse(left_node, black_height, black_depth);
106*8e33eff8Schristos 	} else {
107*8e33eff8Schristos 		ret += (black_depth != black_height);
108*8e33eff8Schristos 	}
109*8e33eff8Schristos 
110*8e33eff8Schristos 	/* Right subtree. */
111*8e33eff8Schristos 	if (right_node != NULL) {
112*8e33eff8Schristos 		ret += tree_recurse(right_node, black_height, black_depth);
113*8e33eff8Schristos 	} else {
114*8e33eff8Schristos 		ret += (black_depth != black_height);
115*8e33eff8Schristos 	}
116*8e33eff8Schristos 
117*8e33eff8Schristos 	return ret;
118*8e33eff8Schristos }
119*8e33eff8Schristos 
120*8e33eff8Schristos static node_t *
121*8e33eff8Schristos tree_iterate_cb(tree_t *tree, node_t *node, void *data) {
122*8e33eff8Schristos 	unsigned *i = (unsigned *)data;
123*8e33eff8Schristos 	node_t *search_node;
124*8e33eff8Schristos 
125*8e33eff8Schristos 	assert_u32_eq(node->magic, NODE_MAGIC, "Bad magic");
126*8e33eff8Schristos 
127*8e33eff8Schristos 	/* Test rb_search(). */
128*8e33eff8Schristos 	search_node = tree_search(tree, node);
129*8e33eff8Schristos 	assert_ptr_eq(search_node, node,
130*8e33eff8Schristos 	    "tree_search() returned unexpected node");
131*8e33eff8Schristos 
132*8e33eff8Schristos 	/* Test rb_nsearch(). */
133*8e33eff8Schristos 	search_node = tree_nsearch(tree, node);
134*8e33eff8Schristos 	assert_ptr_eq(search_node, node,
135*8e33eff8Schristos 	    "tree_nsearch() returned unexpected node");
136*8e33eff8Schristos 
137*8e33eff8Schristos 	/* Test rb_psearch(). */
138*8e33eff8Schristos 	search_node = tree_psearch(tree, node);
139*8e33eff8Schristos 	assert_ptr_eq(search_node, node,
140*8e33eff8Schristos 	    "tree_psearch() returned unexpected node");
141*8e33eff8Schristos 
142*8e33eff8Schristos 	(*i)++;
143*8e33eff8Schristos 
144*8e33eff8Schristos 	return NULL;
145*8e33eff8Schristos }
146*8e33eff8Schristos 
147*8e33eff8Schristos static unsigned
148*8e33eff8Schristos tree_iterate(tree_t *tree) {
149*8e33eff8Schristos 	unsigned i;
150*8e33eff8Schristos 
151*8e33eff8Schristos 	i = 0;
152*8e33eff8Schristos 	tree_iter(tree, NULL, tree_iterate_cb, (void *)&i);
153*8e33eff8Schristos 
154*8e33eff8Schristos 	return i;
155*8e33eff8Schristos }
156*8e33eff8Schristos 
157*8e33eff8Schristos static unsigned
158*8e33eff8Schristos tree_iterate_reverse(tree_t *tree) {
159*8e33eff8Schristos 	unsigned i;
160*8e33eff8Schristos 
161*8e33eff8Schristos 	i = 0;
162*8e33eff8Schristos 	tree_reverse_iter(tree, NULL, tree_iterate_cb, (void *)&i);
163*8e33eff8Schristos 
164*8e33eff8Schristos 	return i;
165*8e33eff8Schristos }
166*8e33eff8Schristos 
167*8e33eff8Schristos static void
168*8e33eff8Schristos node_remove(tree_t *tree, node_t *node, unsigned nnodes) {
169*8e33eff8Schristos 	node_t *search_node;
170*8e33eff8Schristos 	unsigned black_height, imbalances;
171*8e33eff8Schristos 
172*8e33eff8Schristos 	tree_remove(tree, node);
173*8e33eff8Schristos 
174*8e33eff8Schristos 	/* Test rb_nsearch(). */
175*8e33eff8Schristos 	search_node = tree_nsearch(tree, node);
176*8e33eff8Schristos 	if (search_node != NULL) {
177*8e33eff8Schristos 		assert_u64_ge(search_node->key, node->key,
178*8e33eff8Schristos 		    "Key ordering error");
179*8e33eff8Schristos 	}
180*8e33eff8Schristos 
181*8e33eff8Schristos 	/* Test rb_psearch(). */
182*8e33eff8Schristos 	search_node = tree_psearch(tree, node);
183*8e33eff8Schristos 	if (search_node != NULL) {
184*8e33eff8Schristos 		assert_u64_le(search_node->key, node->key,
185*8e33eff8Schristos 		    "Key ordering error");
186*8e33eff8Schristos 	}
187*8e33eff8Schristos 
188*8e33eff8Schristos 	node->magic = 0;
189*8e33eff8Schristos 
190*8e33eff8Schristos 	rbtn_black_height(node_t, link, tree, black_height);
191*8e33eff8Schristos 	imbalances = tree_recurse(tree->rbt_root, black_height, 0);
192*8e33eff8Schristos 	assert_u_eq(imbalances, 0, "Tree is unbalanced");
193*8e33eff8Schristos 	assert_u_eq(tree_iterate(tree), nnodes-1,
194*8e33eff8Schristos 	    "Unexpected node iteration count");
195*8e33eff8Schristos 	assert_u_eq(tree_iterate_reverse(tree), nnodes-1,
196*8e33eff8Schristos 	    "Unexpected node iteration count");
197*8e33eff8Schristos }
198*8e33eff8Schristos 
199*8e33eff8Schristos static node_t *
200*8e33eff8Schristos remove_iterate_cb(tree_t *tree, node_t *node, void *data) {
201*8e33eff8Schristos 	unsigned *nnodes = (unsigned *)data;
202*8e33eff8Schristos 	node_t *ret = tree_next(tree, node);
203*8e33eff8Schristos 
204*8e33eff8Schristos 	node_remove(tree, node, *nnodes);
205*8e33eff8Schristos 
206*8e33eff8Schristos 	return ret;
207*8e33eff8Schristos }
208*8e33eff8Schristos 
209*8e33eff8Schristos static node_t *
210*8e33eff8Schristos remove_reverse_iterate_cb(tree_t *tree, node_t *node, void *data) {
211*8e33eff8Schristos 	unsigned *nnodes = (unsigned *)data;
212*8e33eff8Schristos 	node_t *ret = tree_prev(tree, node);
213*8e33eff8Schristos 
214*8e33eff8Schristos 	node_remove(tree, node, *nnodes);
215*8e33eff8Schristos 
216*8e33eff8Schristos 	return ret;
217*8e33eff8Schristos }
218*8e33eff8Schristos 
219*8e33eff8Schristos static void
220*8e33eff8Schristos destroy_cb(node_t *node, void *data) {
221*8e33eff8Schristos 	unsigned *nnodes = (unsigned *)data;
222*8e33eff8Schristos 
223*8e33eff8Schristos 	assert_u_gt(*nnodes, 0, "Destruction removed too many nodes");
224*8e33eff8Schristos 	(*nnodes)--;
225*8e33eff8Schristos }
226*8e33eff8Schristos 
227*8e33eff8Schristos TEST_BEGIN(test_rb_random) {
228*8e33eff8Schristos #define NNODES 25
229*8e33eff8Schristos #define NBAGS 250
230*8e33eff8Schristos #define SEED 42
231*8e33eff8Schristos 	sfmt_t *sfmt;
232*8e33eff8Schristos 	uint64_t bag[NNODES];
233*8e33eff8Schristos 	tree_t tree;
234*8e33eff8Schristos 	node_t nodes[NNODES];
235*8e33eff8Schristos 	unsigned i, j, k, black_height, imbalances;
236*8e33eff8Schristos 
237*8e33eff8Schristos 	sfmt = init_gen_rand(SEED);
238*8e33eff8Schristos 	for (i = 0; i < NBAGS; i++) {
239*8e33eff8Schristos 		switch (i) {
240*8e33eff8Schristos 		case 0:
241*8e33eff8Schristos 			/* Insert in order. */
242*8e33eff8Schristos 			for (j = 0; j < NNODES; j++) {
243*8e33eff8Schristos 				bag[j] = j;
244*8e33eff8Schristos 			}
245*8e33eff8Schristos 			break;
246*8e33eff8Schristos 		case 1:
247*8e33eff8Schristos 			/* Insert in reverse order. */
248*8e33eff8Schristos 			for (j = 0; j < NNODES; j++) {
249*8e33eff8Schristos 				bag[j] = NNODES - j - 1;
250*8e33eff8Schristos 			}
251*8e33eff8Schristos 			break;
252*8e33eff8Schristos 		default:
253*8e33eff8Schristos 			for (j = 0; j < NNODES; j++) {
254*8e33eff8Schristos 				bag[j] = gen_rand64_range(sfmt, NNODES);
255*8e33eff8Schristos 			}
256*8e33eff8Schristos 		}
257*8e33eff8Schristos 
258*8e33eff8Schristos 		for (j = 1; j <= NNODES; j++) {
259*8e33eff8Schristos 			/* Initialize tree and nodes. */
260*8e33eff8Schristos 			tree_new(&tree);
261*8e33eff8Schristos 			for (k = 0; k < j; k++) {
262*8e33eff8Schristos 				nodes[k].magic = NODE_MAGIC;
263*8e33eff8Schristos 				nodes[k].key = bag[k];
264*8e33eff8Schristos 			}
265*8e33eff8Schristos 
266*8e33eff8Schristos 			/* Insert nodes. */
267*8e33eff8Schristos 			for (k = 0; k < j; k++) {
268*8e33eff8Schristos 				tree_insert(&tree, &nodes[k]);
269*8e33eff8Schristos 
270*8e33eff8Schristos 				rbtn_black_height(node_t, link, &tree,
271*8e33eff8Schristos 				    black_height);
272*8e33eff8Schristos 				imbalances = tree_recurse(tree.rbt_root,
273*8e33eff8Schristos 				    black_height, 0);
274*8e33eff8Schristos 				assert_u_eq(imbalances, 0,
275*8e33eff8Schristos 				    "Tree is unbalanced");
276*8e33eff8Schristos 
277*8e33eff8Schristos 				assert_u_eq(tree_iterate(&tree), k+1,
278*8e33eff8Schristos 				    "Unexpected node iteration count");
279*8e33eff8Schristos 				assert_u_eq(tree_iterate_reverse(&tree), k+1,
280*8e33eff8Schristos 				    "Unexpected node iteration count");
281*8e33eff8Schristos 
282*8e33eff8Schristos 				assert_false(tree_empty(&tree),
283*8e33eff8Schristos 				    "Tree should not be empty");
284*8e33eff8Schristos 				assert_ptr_not_null(tree_first(&tree),
285*8e33eff8Schristos 				    "Tree should not be empty");
286*8e33eff8Schristos 				assert_ptr_not_null(tree_last(&tree),
287*8e33eff8Schristos 				    "Tree should not be empty");
288*8e33eff8Schristos 
289*8e33eff8Schristos 				tree_next(&tree, &nodes[k]);
290*8e33eff8Schristos 				tree_prev(&tree, &nodes[k]);
291*8e33eff8Schristos 			}
292*8e33eff8Schristos 
293*8e33eff8Schristos 			/* Remove nodes. */
294*8e33eff8Schristos 			switch (i % 5) {
295*8e33eff8Schristos 			case 0:
296*8e33eff8Schristos 				for (k = 0; k < j; k++) {
297*8e33eff8Schristos 					node_remove(&tree, &nodes[k], j - k);
298*8e33eff8Schristos 				}
299*8e33eff8Schristos 				break;
300*8e33eff8Schristos 			case 1:
301*8e33eff8Schristos 				for (k = j; k > 0; k--) {
302*8e33eff8Schristos 					node_remove(&tree, &nodes[k-1], k);
303*8e33eff8Schristos 				}
304*8e33eff8Schristos 				break;
305*8e33eff8Schristos 			case 2: {
306*8e33eff8Schristos 				node_t *start;
307*8e33eff8Schristos 				unsigned nnodes = j;
308*8e33eff8Schristos 
309*8e33eff8Schristos 				start = NULL;
310*8e33eff8Schristos 				do {
311*8e33eff8Schristos 					start = tree_iter(&tree, start,
312*8e33eff8Schristos 					    remove_iterate_cb, (void *)&nnodes);
313*8e33eff8Schristos 					nnodes--;
314*8e33eff8Schristos 				} while (start != NULL);
315*8e33eff8Schristos 				assert_u_eq(nnodes, 0,
316*8e33eff8Schristos 				    "Removal terminated early");
317*8e33eff8Schristos 				break;
318*8e33eff8Schristos 			} case 3: {
319*8e33eff8Schristos 				node_t *start;
320*8e33eff8Schristos 				unsigned nnodes = j;
321*8e33eff8Schristos 
322*8e33eff8Schristos 				start = NULL;
323*8e33eff8Schristos 				do {
324*8e33eff8Schristos 					start = tree_reverse_iter(&tree, start,
325*8e33eff8Schristos 					    remove_reverse_iterate_cb,
326*8e33eff8Schristos 					    (void *)&nnodes);
327*8e33eff8Schristos 					nnodes--;
328*8e33eff8Schristos 				} while (start != NULL);
329*8e33eff8Schristos 				assert_u_eq(nnodes, 0,
330*8e33eff8Schristos 				    "Removal terminated early");
331*8e33eff8Schristos 				break;
332*8e33eff8Schristos 			} case 4: {
333*8e33eff8Schristos 				unsigned nnodes = j;
334*8e33eff8Schristos 				tree_destroy(&tree, destroy_cb, &nnodes);
335*8e33eff8Schristos 				assert_u_eq(nnodes, 0,
336*8e33eff8Schristos 				    "Destruction terminated early");
337*8e33eff8Schristos 				break;
338*8e33eff8Schristos 			} default:
339*8e33eff8Schristos 				not_reached();
340*8e33eff8Schristos 			}
341*8e33eff8Schristos 		}
342*8e33eff8Schristos 	}
343*8e33eff8Schristos 	fini_gen_rand(sfmt);
344*8e33eff8Schristos #undef NNODES
345*8e33eff8Schristos #undef NBAGS
346*8e33eff8Schristos #undef SEED
347*8e33eff8Schristos }
348*8e33eff8Schristos TEST_END
349*8e33eff8Schristos 
350*8e33eff8Schristos int
351*8e33eff8Schristos main(void) {
352*8e33eff8Schristos 	return test(
353*8e33eff8Schristos 	    test_rb_empty,
354*8e33eff8Schristos 	    test_rb_random);
355*8e33eff8Schristos }
356