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