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