rb_tree: test rank balance
With _RB_DIAGNOSTIC defined, provide an RB_RANK method to compute the rank of a node in an rb-tree, if the subtree rooted at that node is rank-balanced, and -1 otherwise. In rb_test, rewrite a bit to avoid malloc/free and nondeterministic running times because of randomness. Allocate all the nodes on the stack, and shuffle a set of keys to get randomness for the testing. Add a rank-balance check for the completed tree. Reviewed by: markj MFC after: 3 weeks Differential Revision: https://reviews.freebsd.org/D36484
This commit is contained in:
parent
0eb736c0f6
commit
2c545cf3b0
@ -410,12 +410,17 @@ struct { \
|
|||||||
RB_SET_PARENT(elm, tmp, field); \
|
RB_SET_PARENT(elm, tmp, field); \
|
||||||
} while (/*CONSTCOND*/ 0)
|
} while (/*CONSTCOND*/ 0)
|
||||||
|
|
||||||
|
#if defined(_KERNEL) && defined(DIAGNOSTIC) && !defined(_RB_DIAGNOSTIC)
|
||||||
|
#define _RB_DIAGNOSTIC 1
|
||||||
|
#endif
|
||||||
|
|
||||||
/* Generates prototypes and inline functions */
|
/* Generates prototypes and inline functions */
|
||||||
#define RB_PROTOTYPE(name, type, field, cmp) \
|
#define RB_PROTOTYPE(name, type, field, cmp) \
|
||||||
RB_PROTOTYPE_INTERNAL(name, type, field, cmp,)
|
RB_PROTOTYPE_INTERNAL(name, type, field, cmp,)
|
||||||
#define RB_PROTOTYPE_STATIC(name, type, field, cmp) \
|
#define RB_PROTOTYPE_STATIC(name, type, field, cmp) \
|
||||||
RB_PROTOTYPE_INTERNAL(name, type, field, cmp, __unused static)
|
RB_PROTOTYPE_INTERNAL(name, type, field, cmp, __unused static)
|
||||||
#define RB_PROTOTYPE_INTERNAL(name, type, field, cmp, attr) \
|
#define RB_PROTOTYPE_INTERNAL(name, type, field, cmp, attr) \
|
||||||
|
RB_PROTOTYPE_RANK(name, type, attr) \
|
||||||
RB_PROTOTYPE_INSERT_COLOR(name, type, attr); \
|
RB_PROTOTYPE_INSERT_COLOR(name, type, attr); \
|
||||||
RB_PROTOTYPE_REMOVE_COLOR(name, type, attr); \
|
RB_PROTOTYPE_REMOVE_COLOR(name, type, attr); \
|
||||||
RB_PROTOTYPE_INSERT(name, type, attr); \
|
RB_PROTOTYPE_INSERT(name, type, attr); \
|
||||||
@ -426,6 +431,12 @@ struct { \
|
|||||||
RB_PROTOTYPE_PREV(name, type, attr); \
|
RB_PROTOTYPE_PREV(name, type, attr); \
|
||||||
RB_PROTOTYPE_MINMAX(name, type, attr); \
|
RB_PROTOTYPE_MINMAX(name, type, attr); \
|
||||||
RB_PROTOTYPE_REINSERT(name, type, attr);
|
RB_PROTOTYPE_REINSERT(name, type, attr);
|
||||||
|
#ifdef _RB_DIAGNOSTIC
|
||||||
|
#define RB_PROTOTYPE_RANK(name, type, attr) \
|
||||||
|
attr int name##_RB_RANK(struct type *);
|
||||||
|
#else
|
||||||
|
#define RB_PROTOTYPE_RANK(name, type, attr)
|
||||||
|
#endif
|
||||||
#define RB_PROTOTYPE_INSERT_COLOR(name, type, attr) \
|
#define RB_PROTOTYPE_INSERT_COLOR(name, type, attr) \
|
||||||
attr void name##_RB_INSERT_COLOR(struct name *, struct type *)
|
attr void name##_RB_INSERT_COLOR(struct name *, struct type *)
|
||||||
#define RB_PROTOTYPE_REMOVE_COLOR(name, type, attr) \
|
#define RB_PROTOTYPE_REMOVE_COLOR(name, type, attr) \
|
||||||
@ -456,6 +467,7 @@ struct { \
|
|||||||
#define RB_GENERATE_STATIC(name, type, field, cmp) \
|
#define RB_GENERATE_STATIC(name, type, field, cmp) \
|
||||||
RB_GENERATE_INTERNAL(name, type, field, cmp, __unused static)
|
RB_GENERATE_INTERNAL(name, type, field, cmp, __unused static)
|
||||||
#define RB_GENERATE_INTERNAL(name, type, field, cmp, attr) \
|
#define RB_GENERATE_INTERNAL(name, type, field, cmp, attr) \
|
||||||
|
RB_GENERATE_RANK(name, type, field, attr) \
|
||||||
RB_GENERATE_INSERT_COLOR(name, type, field, attr) \
|
RB_GENERATE_INSERT_COLOR(name, type, field, attr) \
|
||||||
RB_GENERATE_REMOVE_COLOR(name, type, field, attr) \
|
RB_GENERATE_REMOVE_COLOR(name, type, field, attr) \
|
||||||
RB_GENERATE_INSERT(name, type, field, cmp, attr) \
|
RB_GENERATE_INSERT(name, type, field, cmp, attr) \
|
||||||
@ -467,6 +479,31 @@ struct { \
|
|||||||
RB_GENERATE_MINMAX(name, type, field, attr) \
|
RB_GENERATE_MINMAX(name, type, field, attr) \
|
||||||
RB_GENERATE_REINSERT(name, type, field, cmp, attr)
|
RB_GENERATE_REINSERT(name, type, field, cmp, attr)
|
||||||
|
|
||||||
|
#ifdef _RB_DIAGNOSTIC
|
||||||
|
#define RB_GENERATE_RANK(name, type, field, attr) \
|
||||||
|
attr int \
|
||||||
|
name##_RB_RANK(struct type *elm) \
|
||||||
|
{ \
|
||||||
|
struct type *left, *right; \
|
||||||
|
int left_rank, right_rank; \
|
||||||
|
__uintptr_t bits; \
|
||||||
|
\
|
||||||
|
if (elm == NULL) \
|
||||||
|
return (0); \
|
||||||
|
bits = RB_BITS(elm, field); \
|
||||||
|
left = RB_LEFT(elm, field); \
|
||||||
|
left_rank = ((bits & RB_RED_L) ? 2 : 1) + name##_RB_RANK(left); \
|
||||||
|
right = RB_RIGHT(elm, field); \
|
||||||
|
right_rank = ((bits & RB_RED_R) ? 2 : 1) + name##_RB_RANK(right); \
|
||||||
|
if (left_rank != right_rank || \
|
||||||
|
(left_rank == 2 && left == NULL && right == NULL)) \
|
||||||
|
return (-1); \
|
||||||
|
return (left_rank); \
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
#define RB_GENERATE_RANK(name, type, field, attr)
|
||||||
|
#endif
|
||||||
|
|
||||||
#define RB_GENERATE_INSERT_COLOR(name, type, field, attr) \
|
#define RB_GENERATE_INSERT_COLOR(name, type, field, attr) \
|
||||||
attr void \
|
attr void \
|
||||||
name##_RB_INSERT_COLOR(struct name *head, struct type *elm) \
|
name##_RB_INSERT_COLOR(struct name *head, struct type *elm) \
|
||||||
|
@ -29,6 +29,7 @@
|
|||||||
*/
|
*/
|
||||||
#include <sys/types.h>
|
#include <sys/types.h>
|
||||||
|
|
||||||
|
#define _RB_DIAGNOSTIC 1
|
||||||
#include <sys/tree.h>
|
#include <sys/tree.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
|
|
||||||
@ -54,52 +55,54 @@ RB_PROTOTYPE(tree, node, node, compare);
|
|||||||
RB_GENERATE(tree, node, node, compare);
|
RB_GENERATE(tree, node, node, compare);
|
||||||
|
|
||||||
#define ITER 150
|
#define ITER 150
|
||||||
#define MIN 5
|
|
||||||
#define MAX 5000
|
|
||||||
|
|
||||||
ATF_TC_WITHOUT_HEAD(rb_test);
|
ATF_TC_WITHOUT_HEAD(rb_test);
|
||||||
ATF_TC_BODY(rb_test, tc)
|
ATF_TC_BODY(rb_test, tc)
|
||||||
{
|
{
|
||||||
struct node *tmp, *ins;
|
struct node *tmp, *ins, store[ITER];
|
||||||
int i, max, min;
|
int i, j, k, max, min;
|
||||||
|
|
||||||
max = min = 42; /* pacify gcc */
|
min = ITER;
|
||||||
|
max = -1;
|
||||||
|
|
||||||
RB_INIT(&root);
|
RB_INIT(&root);
|
||||||
|
|
||||||
|
/* Initialize keys */
|
||||||
|
for (i = 0; i < ITER; i++)
|
||||||
|
store[i].key = i;
|
||||||
|
|
||||||
|
/* Randomly shuffle keys */
|
||||||
for (i = 0; i < ITER; i++) {
|
for (i = 0; i < ITER; i++) {
|
||||||
tmp = malloc(sizeof(struct node));
|
j = i + arc4random_uniform(ITER - i);
|
||||||
ATF_REQUIRE_MSG(tmp != NULL, "malloc failed");
|
k = store[j].key;
|
||||||
do {
|
store[j].key = store[i].key;
|
||||||
tmp->key = arc4random_uniform(MAX-MIN);
|
store[i].key = k;
|
||||||
tmp->key += MIN;
|
}
|
||||||
} while (RB_FIND(tree, &root, tmp) != NULL);
|
|
||||||
if (i == 0)
|
for (i = 0; i < ITER; i++) {
|
||||||
max = min = tmp->key;
|
for (j = 0; j < i; ++j) {
|
||||||
else {
|
tmp = &store[j];
|
||||||
|
ATF_REQUIRE_EQ(tmp, RB_FIND(tree, &root, tmp));
|
||||||
|
}
|
||||||
|
tmp = &store[i];
|
||||||
if (tmp->key > max)
|
if (tmp->key > max)
|
||||||
max = tmp->key;
|
max = tmp->key;
|
||||||
if (tmp->key < min)
|
if (tmp->key < min)
|
||||||
min = tmp->key;
|
min = tmp->key;
|
||||||
}
|
|
||||||
ATF_REQUIRE_EQ(NULL, RB_INSERT(tree, &root, tmp));
|
ATF_REQUIRE_EQ(NULL, RB_INSERT(tree, &root, tmp));
|
||||||
}
|
|
||||||
|
|
||||||
ins = RB_MIN(tree, &root);
|
ins = RB_MIN(tree, &root);
|
||||||
ATF_REQUIRE_MSG(ins != NULL, "RB_MIN error");
|
ATF_REQUIRE_MSG(ins != NULL, "RB_MIN error");
|
||||||
ATF_CHECK_EQ(min, ins->key);
|
ATF_CHECK_EQ(min, ins->key);
|
||||||
tmp = ins;
|
|
||||||
ins = RB_MAX(tree, &root);
|
ins = RB_MAX(tree, &root);
|
||||||
ATF_REQUIRE_MSG(ins != NULL, "RB_MAX error");
|
ATF_REQUIRE_MSG(ins != NULL, "RB_MAX error");
|
||||||
ATF_CHECK_EQ(max, ins->key);
|
ATF_CHECK_EQ(max, ins->key);
|
||||||
|
}
|
||||||
ATF_CHECK_EQ(tmp, RB_REMOVE(tree, &root, tmp));
|
tmp = RB_ROOT(&root);
|
||||||
|
ATF_REQUIRE_MSG(tree_RB_RANK(tmp) >= 0, "RB rank balance error");
|
||||||
for (i = 0; i < ITER - 1; i++) {
|
for (i = 0; i < ITER; i++) {
|
||||||
tmp = RB_ROOT(&root);
|
tmp = RB_ROOT(&root);
|
||||||
ATF_REQUIRE_MSG(tmp != NULL, "RB_ROOT error");
|
ATF_REQUIRE_MSG(tmp != NULL, "RB_ROOT error");
|
||||||
ATF_CHECK_EQ(tmp, RB_REMOVE(tree, &root, tmp));
|
ATF_CHECK_EQ(tmp, RB_REMOVE(tree, &root, tmp));
|
||||||
free(tmp);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user