1 /*
2 * Copyright (c) 2018 Intel Corporation.
3 *
4 * SPDX-License-Identifier: Apache-2.0
5 */
6 #include <zephyr/ztest.h>
7 #include <zephyr/sys/rb.h>
8
9 #include "../../../lib/utils/rb.c"
10
11 #define _CHECK(n) \
12 zassert_true(!!(n), "Tree check failed: [ " #n " ] @%d", __LINE__)
13
14 #define MAX_NODES 256
15
16 static struct rbtree test_rbtree;
17
18 static struct rbnode nodes[MAX_NODES];
19
20 /* Bit is set if node is in the tree */
21 static unsigned int node_mask[(MAX_NODES + 31)/32];
22
23 /* Array of nodes dumped via rb_walk */
24 static struct rbnode *walked_nodes[MAX_NODES];
25
26 /* Node currently being inserted, for testing lessthan() argument order */
27 static struct rbnode *current_insertee;
28
set_node_mask(int node,int val)29 void set_node_mask(int node, int val)
30 {
31 unsigned int *p = &node_mask[node / 32];
32 unsigned int bit = 1u << (node % 32);
33
34 *p &= ~bit;
35 *p |= val ? bit : 0;
36 }
37
get_node_mask(int node)38 int get_node_mask(int node)
39 {
40 unsigned int *p = &node_mask[node / 32];
41 unsigned int bit = 1u << (node % 32);
42
43 return !!(*p & bit);
44 }
45
node_index(struct rbnode * n)46 int node_index(struct rbnode *n)
47 {
48 return (int)(n - &nodes[0]);
49 }
50
51 /* Our "lessthan" is just the location of the struct */
node_lessthan(struct rbnode * a,struct rbnode * b)52 bool node_lessthan(struct rbnode *a, struct rbnode *b)
53 {
54 if (current_insertee) {
55 _CHECK(a == current_insertee);
56 _CHECK(b != current_insertee);
57 }
58
59 return a < b;
60 }
61
62 /* Simple LCRNG (modulus is 2^64!) cribbed from:
63 * https://nuclear.llnl.gov/CNP/rng/rngman/node4.html
64 *
65 * Don't need much in the way of quality, do need repeatability across
66 * platforms.
67 */
next_rand_mod(unsigned int mod)68 static unsigned int next_rand_mod(unsigned int mod)
69 {
70 static unsigned long long state = 123456789; /* seed */
71
72 state = state * 2862933555777941757ul + 3037000493ul;
73
74 return ((unsigned int)(state >> 32)) % mod;
75 }
76
visit_node(struct rbnode * node,void * cookie)77 void visit_node(struct rbnode *node, void *cookie)
78 {
79 int *nwalked = cookie;
80
81 _CHECK(*nwalked < MAX_NODES);
82
83 walked_nodes[*nwalked] = node;
84 *nwalked += 1;
85 }
86
87 /* Stores the last-seen black height at a leaf during check_rb(), or
88 * zero if no leaves have been seen yet
89 */
90 static int last_black_height;
91
check_rbnode(struct rbnode * node,int blacks_above)92 void check_rbnode(struct rbnode *node, int blacks_above)
93 {
94 int side, bheight = blacks_above + z_rb_is_black(node);
95
96 for (side = 0; side < 2; side++) {
97 struct rbnode *ch = z_rb_child(node, side);
98
99 if (ch) {
100 /* Basic tree requirement */
101 if (side == 0) {
102 _CHECK(node_lessthan(ch, node));
103 } else {
104 _CHECK(node_lessthan(node, ch));
105 }
106
107 /* Can't have adjacent red nodes */
108 _CHECK(z_rb_is_black(node) || z_rb_is_black(ch));
109
110 /* Recurse */
111 check_rbnode(ch, bheight);
112 } else {
113 /* All leaf nodes must be at the same black height */
114 if (last_black_height) {
115 _CHECK(last_black_height == bheight);
116 }
117 last_black_height = bheight;
118 }
119 }
120 }
121
check_rb(void)122 void check_rb(void)
123 {
124 last_black_height = 0;
125
126 _CHECK(test_rbtree.root);
127 _CHECK(z_rb_is_black(test_rbtree.root));
128
129 check_rbnode(test_rbtree.root, 0);
130 }
131
132 /* First validates the external API behavior via a walk, then checks
133 * interior tree and red/black state via internal APIs.
134 */
_check_tree(int size,int use_foreach)135 void _check_tree(int size, int use_foreach)
136 {
137 int nwalked = 0, i, ni;
138 struct rbnode *n, *last = NULL;
139
140 (void)memset(walked_nodes, 0, sizeof(walked_nodes));
141
142 if (use_foreach) {
143 RB_FOR_EACH(&test_rbtree, n) {
144 visit_node(n, &nwalked);
145 }
146 } else {
147 rb_walk(&test_rbtree, visit_node, &nwalked);
148 }
149
150 /* Make sure all found nodes are in-order and marked in the tree */
151 for (i = 0; i < nwalked; i++) {
152 n = walked_nodes[i];
153 ni = node_index(n);
154
155 if (last) {
156 _CHECK(node_lessthan(last, n));
157 }
158
159 _CHECK(get_node_mask(ni));
160
161 last = n;
162 }
163
164 /* Make sure all tree bits properly reflect the set of nodes we found */
165 ni = 0;
166 for (i = 0; i < MAX_NODES; i++) {
167 _CHECK(get_node_mask(i) == rb_contains(&test_rbtree, &nodes[i]));
168
169 if (get_node_mask(i)) {
170 _CHECK(node_index(walked_nodes[ni]) == i);
171 ni++;
172 }
173 }
174
175 _CHECK(ni == nwalked);
176
177 if (test_rbtree.root) {
178 check_rb();
179 }
180 }
181
check_tree(int size)182 void check_tree(int size)
183 {
184 /* Do it with both enumeration mechanisms */
185 _check_tree(size, 0);
186 _check_tree(size, 1);
187 }
188
checked_insert(struct rbtree * tree,struct rbnode * node)189 void checked_insert(struct rbtree *tree, struct rbnode *node)
190 {
191 current_insertee = node;
192 rb_insert(tree, node);
193 current_insertee = NULL;
194 }
195
test_tree(int size)196 void test_tree(int size)
197 {
198 int i, j;
199
200 /* Small trees get checked after every op, big trees less often */
201 int small_tree = size <= 32;
202
203 (void)memset(&test_rbtree, 0, sizeof(test_rbtree));
204 test_rbtree.lessthan_fn = node_lessthan;
205 (void)memset(nodes, 0, sizeof(nodes));
206 (void)memset(node_mask, 0, sizeof(node_mask));
207
208 for (j = 0; j < 10; j++) {
209 for (i = 0; i < size; i++) {
210 int node = next_rand_mod(size);
211
212 if (!get_node_mask(node)) {
213 rb_insert(&test_rbtree, &nodes[node]);
214 set_node_mask(node, 1);
215 } else {
216 rb_remove(&test_rbtree, &nodes[node]);
217 set_node_mask(node, 0);
218 }
219
220 if (small_tree) {
221 check_tree(size);
222 }
223 }
224
225 if (!small_tree) {
226 check_tree(size);
227 }
228 }
229 }
230
ZTEST(rbtree_api,test_rbtree_spam)231 ZTEST(rbtree_api, test_rbtree_spam)
232 {
233 int size = 1;
234
235 do {
236 size += next_rand_mod(size) + 1;
237
238 if (size > MAX_NODES) {
239 size = MAX_NODES;
240 }
241
242 TC_PRINT("Checking trees built from %d nodes...\n", size);
243
244 test_tree(size);
245 } while (size < MAX_NODES);
246 }
247
248 /**
249 * @brief Test removing a node with abnormal color.
250 *
251 * @details Initialize a tree and insert it,
252 * and test APIs rb_get_min(), rb_get_max().
253 *
254 * @ingroup lib_rbtree_tests
255 *
256 * @see rb_get_min(), rb_get_max()
257 */
ZTEST(rbtree_api,test_rb_get_minmax)258 ZTEST(rbtree_api, test_rb_get_minmax)
259 {
260 struct rbnode temp = {0};
261
262 /* Initialize a tree and insert it */
263 (void)memset(&test_rbtree, 0, sizeof(test_rbtree));
264 test_rbtree.lessthan_fn = node_lessthan;
265 (void)memset(nodes, 0, sizeof(nodes));
266
267 zassert_true(rb_get_min(&test_rbtree) == NULL, "the tree is invalid");
268
269 for (int i = 0; i < 8; i++) {
270 rb_insert(&test_rbtree, &nodes[i]);
271 }
272
273 rb_remove(&test_rbtree, &temp);
274
275 /* Check if tree's max and min node are expected */
276 zassert_true(rb_get_min(&test_rbtree) == &nodes[0], "the tree is invalid");
277 zassert_true(rb_get_max(&test_rbtree) == &nodes[7], "the tree is invalid");
278 }
279
280 ZTEST_SUITE(rbtree_api, NULL, NULL, NULL, NULL, NULL);
281