radixtree.c revision 1.16 1 /* $NetBSD: radixtree.c,v 1.16 2011/10/25 14:11:27 yamt Exp $ */
2
3 /*-
4 * Copyright (c)2011 YAMAMOTO Takashi,
5 * All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 * 1. Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
17 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
20 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
22 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26 * SUCH DAMAGE.
27 */
28
29 /*
30 * radix tree
31 *
32 * it's designed to work efficiently with dense index distribution.
33 * the memory consumption (number of necessary intermediate nodes)
34 * heavily depends on index distribution. basically, more dense index
35 * distribution consumes less nodes per item.
36 * approximately,
37 * the best case: about RADIX_TREE_PTR_PER_NODE items per node.
38 * the worst case: RADIX_TREE_MAX_HEIGHT nodes per item.
39 */
40
41 #include <sys/cdefs.h>
42
43 #if defined(_KERNEL) || defined(_STANDALONE)
44 __KERNEL_RCSID(0, "$NetBSD: radixtree.c,v 1.16 2011/10/25 14:11:27 yamt Exp $");
45 #include <sys/param.h>
46 #include <sys/errno.h>
47 #include <sys/pool.h>
48 #include <sys/radixtree.h>
49 #include <lib/libkern/libkern.h>
50 #if defined(_STANDALONE)
51 #include <lib/libsa/stand.h>
52 #endif /* defined(_STANDALONE) */
53 #else /* defined(_KERNEL) || defined(_STANDALONE) */
54 __RCSID("$NetBSD: radixtree.c,v 1.16 2011/10/25 14:11:27 yamt Exp $");
55 #include <assert.h>
56 #include <errno.h>
57 #include <stdbool.h>
58 #include <stdlib.h>
59 #include <string.h>
60 #if 1
61 #define KASSERT assert
62 #else
63 #define KASSERT(a) /* nothing */
64 #endif
65 #endif /* defined(_KERNEL) || defined(_STANDALONE) */
66
67 #include <sys/radixtree.h>
68
69 #define RADIX_TREE_BITS_PER_HEIGHT 4 /* XXX tune */
70 #define RADIX_TREE_PTR_PER_NODE (1 << RADIX_TREE_BITS_PER_HEIGHT)
71 #define RADIX_TREE_MAX_HEIGHT (64 / RADIX_TREE_BITS_PER_HEIGHT)
72 #define RADIX_TREE_INVALID_HEIGHT (RADIX_TREE_MAX_HEIGHT + 1)
73 __CTASSERT((64 % RADIX_TREE_BITS_PER_HEIGHT) == 0);
74
75 __CTASSERT(((1 << RADIX_TREE_TAG_ID_MAX) & (sizeof(int) - 1)) == 0);
76 #define RADIX_TREE_TAG_MASK ((1 << RADIX_TREE_TAG_ID_MAX) - 1)
77
78 static inline void *
79 entry_ptr(void *p)
80 {
81
82 return (void *)((uintptr_t)p & ~RADIX_TREE_TAG_MASK);
83 }
84
85 static inline unsigned int
86 entry_tagmask(void *p)
87 {
88
89 return (uintptr_t)p & RADIX_TREE_TAG_MASK;
90 }
91
92 static inline void *
93 entry_compose(void *p, unsigned int tagmask)
94 {
95
96 return (void *)((uintptr_t)p | tagmask);
97 }
98
99 static inline bool
100 entry_match_p(void *p, unsigned int tagmask)
101 {
102
103 KASSERT(entry_ptr(p) != NULL || entry_tagmask(p) == 0);
104 if (p == NULL) {
105 return false;
106 }
107 if (tagmask == 0) {
108 return true;
109 }
110 return (entry_tagmask(p) & tagmask) != 0;
111 }
112
113 static inline unsigned int
114 tagid_to_mask(radix_tree_tagid_t id)
115 {
116
117 KASSERT(id >= 0);
118 KASSERT(id < RADIX_TREE_TAG_ID_MAX);
119 return 1U << id;
120 }
121
122 /*
123 * radix_tree_node: an intermediate node
124 *
125 * we don't care the type of leaf nodes. they are just void *.
126 */
127
128 struct radix_tree_node {
129 void *n_ptrs[RADIX_TREE_PTR_PER_NODE];
130 unsigned int n_nptrs; /* # of non-NULL pointers in n_ptrs */
131 };
132
133 /*
134 * any_children_tagmask:
135 *
136 * return OR'ed tagmask of the given node's children.
137 */
138
139 static unsigned int
140 any_children_tagmask(const struct radix_tree_node *n)
141 {
142 unsigned int mask;
143 int i;
144
145 mask = 0;
146 for (i = 0; i < RADIX_TREE_PTR_PER_NODE; i++) {
147 mask |= (unsigned int)(uintptr_t)n->n_ptrs[i];
148 }
149 return mask & RADIX_TREE_TAG_MASK;
150 }
151
152 /*
153 * p_refs[0].pptr == &t->t_root
154 * :
155 * p_refs[n].pptr == &(*p_refs[n-1])->n_ptrs[x]
156 * :
157 * :
158 * p_refs[t->t_height].pptr == &leaf_pointer
159 */
160
161 struct radix_tree_path {
162 struct radix_tree_node_ref {
163 void **pptr;
164 } p_refs[RADIX_TREE_MAX_HEIGHT + 1]; /* +1 for the root ptr */
165 /*
166 * p_lastidx is either the index of the last valid element of p_refs[]
167 * or RADIX_TREE_INVALID_HEIGHT.
168 * RADIX_TREE_INVALID_HEIGHT means that radix_tree_lookup_ptr found
169 * that the height of the tree is not enough to cover the given index.
170 */
171 unsigned int p_lastidx;
172 };
173
174 static inline void **
175 path_pptr(const struct radix_tree *t, const struct radix_tree_path *p,
176 unsigned int height)
177 {
178
179 KASSERT(height <= t->t_height);
180 return p->p_refs[height].pptr;
181 }
182
183 static inline struct radix_tree_node *
184 path_node(const struct radix_tree * t, const struct radix_tree_path *p,
185 unsigned int height)
186 {
187
188 KASSERT(height <= t->t_height);
189 return entry_ptr(*path_pptr(t, p, height));
190 }
191
192 /*
193 * radix_tree_init_tree:
194 *
195 * initialize a tree.
196 */
197
198 void
199 radix_tree_init_tree(struct radix_tree *t)
200 {
201
202 t->t_height = 0;
203 t->t_root = NULL;
204 }
205
206 /*
207 * radix_tree_init_tree:
208 *
209 * clean up a tree.
210 */
211
212 void
213 radix_tree_fini_tree(struct radix_tree *t)
214 {
215
216 KASSERT(t->t_root == NULL);
217 KASSERT(t->t_height == 0);
218 }
219
220 bool
221 radix_tree_empty_tree_p(struct radix_tree *t)
222 {
223
224 return t->t_root == NULL;
225 }
226
227 bool
228 radix_tree_empty_tagged_tree_p(struct radix_tree *t, radix_tree_tagid_t tagid)
229 {
230 const unsigned int tagmask = tagid_to_mask(tagid);
231
232 return (entry_tagmask(t->t_root) & tagmask) == 0;
233 }
234
235 static void
236 radix_tree_node_init(struct radix_tree_node *n)
237 {
238
239 memset(n, 0, sizeof(*n));
240 }
241
242 #if defined(_KERNEL)
243 pool_cache_t radix_tree_node_cache __read_mostly;
244
245 static int
246 radix_tree_node_ctor(void *dummy, void *item, int flags)
247 {
248 struct radix_tree_node *n = item;
249
250 KASSERT(dummy == NULL);
251 radix_tree_node_init(n);
252 return 0;
253 }
254
255 /*
256 * radix_tree_init:
257 *
258 * initialize the subsystem.
259 */
260
261 void
262 radix_tree_init(void)
263 {
264
265 radix_tree_node_cache = pool_cache_init(sizeof(struct radix_tree_node),
266 0, 0, 0, "radix_tree_node", NULL, IPL_NONE, radix_tree_node_ctor,
267 NULL, NULL);
268 KASSERT(radix_tree_node_cache != NULL);
269 }
270 #endif /* defined(_KERNEL) */
271
272 static bool __unused
273 radix_tree_node_clean_p(const struct radix_tree_node *n)
274 {
275 unsigned int i;
276
277 if (n->n_nptrs != 0) {
278 return false;
279 }
280 for (i = 0; i < RADIX_TREE_PTR_PER_NODE; i++) {
281 if (n->n_ptrs[i] != NULL) {
282 return false;
283 }
284 }
285 return true;
286 }
287
288 static struct radix_tree_node *
289 radix_tree_alloc_node(void)
290 {
291 struct radix_tree_node *n;
292
293 #if defined(_KERNEL)
294 n = pool_cache_get(radix_tree_node_cache, PR_NOWAIT);
295 #else /* defined(_KERNEL) */
296 #if defined(_STANDALONE)
297 n = alloc(sizeof(*n));
298 #else /* defined(_STANDALONE) */
299 n = malloc(sizeof(*n));
300 #endif /* defined(_STANDALONE) */
301 if (n != NULL) {
302 radix_tree_node_init(n);
303 }
304 #endif /* defined(_KERNEL) */
305 KASSERT(n == NULL || radix_tree_node_clean_p(n));
306 return n;
307 }
308
309 static void
310 radix_tree_free_node(struct radix_tree_node *n)
311 {
312
313 KASSERT(radix_tree_node_clean_p(n));
314 #if defined(_KERNEL)
315 pool_cache_put(radix_tree_node_cache, n);
316 #elif defined(_STANDALONE)
317 dealloc(n, sizeof(*n));
318 #else
319 free(n);
320 #endif
321 }
322
323 static int
324 radix_tree_grow(struct radix_tree *t, unsigned int newheight)
325 {
326 const unsigned int tagmask = entry_tagmask(t->t_root);
327
328 KASSERT(newheight <= 64 / RADIX_TREE_BITS_PER_HEIGHT);
329 if (t->t_root == NULL) {
330 t->t_height = newheight;
331 return 0;
332 }
333 while (t->t_height < newheight) {
334 struct radix_tree_node *n;
335
336 n = radix_tree_alloc_node();
337 if (n == NULL) {
338 /*
339 * don't bother to revert our changes.
340 * the caller will likely retry.
341 */
342 return ENOMEM;
343 }
344 n->n_nptrs = 1;
345 n->n_ptrs[0] = t->t_root;
346 t->t_root = entry_compose(n, tagmask);
347 t->t_height++;
348 }
349 return 0;
350 }
351
352 /*
353 * radix_tree_lookup_ptr:
354 *
355 * an internal helper function used for various exported functions.
356 *
357 * return the pointer to store the node for the given index.
358 *
359 * if alloc is true, try to allocate the storage. (note for _KERNEL:
360 * in that case, this function can block.) if the allocation failed or
361 * alloc is false, return NULL.
362 *
363 * if path is not NULL, fill it for the caller's investigation.
364 *
365 * if tagmask is not zero, search only for nodes with the tag set.
366 * note that, however, this function doesn't check the tagmask for the leaf
367 * pointer. it's a caller's responsibility to investigate the value which
368 * is pointed by the returned pointer if necessary.
369 *
370 * while this function is a bit large, as it's called with some constant
371 * arguments, inlining might have benefits. anyway, a compiler will decide.
372 */
373
374 static inline void **
375 radix_tree_lookup_ptr(struct radix_tree *t, uint64_t idx,
376 struct radix_tree_path *path, bool alloc, const unsigned int tagmask)
377 {
378 struct radix_tree_node *n;
379 int hshift = RADIX_TREE_BITS_PER_HEIGHT * t->t_height;
380 int shift;
381 void **vpp;
382 const uint64_t mask = (UINT64_C(1) << RADIX_TREE_BITS_PER_HEIGHT) - 1;
383 struct radix_tree_node_ref *refs = NULL;
384
385 /*
386 * check unsupported combinations
387 */
388 KASSERT(tagmask == 0 || !alloc);
389 KASSERT(path == NULL || !alloc);
390 vpp = &t->t_root;
391 if (path != NULL) {
392 refs = path->p_refs;
393 refs->pptr = vpp;
394 }
395 n = NULL;
396 for (shift = 64 - RADIX_TREE_BITS_PER_HEIGHT; shift >= 0;) {
397 struct radix_tree_node *c;
398 void *entry;
399 const uint64_t i = (idx >> shift) & mask;
400
401 if (shift >= hshift) {
402 unsigned int newheight;
403
404 KASSERT(vpp == &t->t_root);
405 if (i == 0) {
406 shift -= RADIX_TREE_BITS_PER_HEIGHT;
407 continue;
408 }
409 if (!alloc) {
410 if (path != NULL) {
411 KASSERT((refs - path->p_refs) == 0);
412 path->p_lastidx =
413 RADIX_TREE_INVALID_HEIGHT;
414 }
415 return NULL;
416 }
417 newheight = shift / RADIX_TREE_BITS_PER_HEIGHT + 1;
418 if (radix_tree_grow(t, newheight)) {
419 return NULL;
420 }
421 hshift = RADIX_TREE_BITS_PER_HEIGHT * t->t_height;
422 }
423 entry = *vpp;
424 c = entry_ptr(entry);
425 if (c == NULL ||
426 (tagmask != 0 &&
427 (entry_tagmask(entry) & tagmask) == 0)) {
428 if (!alloc) {
429 if (path != NULL) {
430 path->p_lastidx = refs - path->p_refs;
431 }
432 return NULL;
433 }
434 c = radix_tree_alloc_node();
435 if (c == NULL) {
436 return NULL;
437 }
438 *vpp = c;
439 if (n != NULL) {
440 KASSERT(n->n_nptrs < RADIX_TREE_PTR_PER_NODE);
441 n->n_nptrs++;
442 }
443 }
444 n = c;
445 vpp = &n->n_ptrs[i];
446 if (path != NULL) {
447 refs++;
448 refs->pptr = vpp;
449 }
450 shift -= RADIX_TREE_BITS_PER_HEIGHT;
451 }
452 if (alloc) {
453 KASSERT(*vpp == NULL);
454 if (n != NULL) {
455 KASSERT(n->n_nptrs < RADIX_TREE_PTR_PER_NODE);
456 n->n_nptrs++;
457 }
458 }
459 if (path != NULL) {
460 path->p_lastidx = refs - path->p_refs;
461 }
462 return vpp;
463 }
464
465 /*
466 * radix_tree_insert_node:
467 *
468 * insert the node at idx.
469 * it's illegal to insert NULL.
470 * it's illegal to insert a non-aligned pointer.
471 *
472 * this function returns ENOMEM if necessary memory allocation failed.
473 * otherwise, this function returns 0.
474 *
475 * note that inserting a node can involves memory allocation for intermediate
476 * nodes. if _KERNEL, it's done with non-blocking IPL_NONE memory allocation.
477 *
478 * for the newly inserted node, all tags are cleared.
479 */
480
481 int
482 radix_tree_insert_node(struct radix_tree *t, uint64_t idx, void *p)
483 {
484 void **vpp;
485
486 KASSERT(p != NULL);
487 KASSERT(entry_compose(p, 0) == p);
488 vpp = radix_tree_lookup_ptr(t, idx, NULL, true, 0);
489 if (vpp == NULL) {
490 return ENOMEM;
491 }
492 KASSERT(*vpp == NULL);
493 *vpp = p;
494 return 0;
495 }
496
497 /*
498 * radix_tree_replace_node:
499 *
500 * replace a node at the given index with the given node.
501 * return the old node.
502 * it's illegal to try to replace a node which has not been inserted.
503 *
504 * this function doesn't change tags.
505 */
506
507 void *
508 radix_tree_replace_node(struct radix_tree *t, uint64_t idx, void *p)
509 {
510 void **vpp;
511 void *oldp;
512
513 KASSERT(p != NULL);
514 KASSERT(entry_compose(p, 0) == p);
515 vpp = radix_tree_lookup_ptr(t, idx, NULL, false, 0);
516 KASSERT(vpp != NULL);
517 oldp = *vpp;
518 KASSERT(oldp != NULL);
519 *vpp = entry_compose(p, entry_tagmask(*vpp));
520 return entry_ptr(oldp);
521 }
522
523 /*
524 * radix_tree_remove_node:
525 *
526 * remove the node at idx.
527 * it's illegal to try to remove a node which has not been inserted.
528 */
529
530 void *
531 radix_tree_remove_node(struct radix_tree *t, uint64_t idx)
532 {
533 struct radix_tree_path path;
534 void **vpp;
535 void *oldp;
536 int i;
537
538 vpp = radix_tree_lookup_ptr(t, idx, &path, false, 0);
539 KASSERT(vpp != NULL);
540 oldp = *vpp;
541 KASSERT(oldp != NULL);
542 KASSERT(path.p_lastidx == t->t_height);
543 KASSERT(vpp == path_pptr(t, &path, path.p_lastidx));
544 *vpp = NULL;
545 for (i = t->t_height - 1; i >= 0; i--) {
546 void *entry;
547 struct radix_tree_node ** const pptr =
548 (struct radix_tree_node **)path_pptr(t, &path, i);
549 struct radix_tree_node *n;
550
551 KASSERT(pptr != NULL);
552 entry = *pptr;
553 n = entry_ptr(entry);
554 KASSERT(n != NULL);
555 KASSERT(n->n_nptrs > 0);
556 n->n_nptrs--;
557 if (n->n_nptrs > 0) {
558 break;
559 }
560 radix_tree_free_node(n);
561 *pptr = NULL;
562 }
563 /*
564 * fix up height
565 */
566 if (i < 0) {
567 KASSERT(t->t_root == NULL);
568 t->t_height = 0;
569 }
570 /*
571 * update tags
572 */
573 for (; i >= 0; i--) {
574 void *entry;
575 struct radix_tree_node ** const pptr =
576 (struct radix_tree_node **)path_pptr(t, &path, i);
577 struct radix_tree_node *n;
578 unsigned int newmask;
579
580 KASSERT(pptr != NULL);
581 entry = *pptr;
582 n = entry_ptr(entry);
583 KASSERT(n != NULL);
584 KASSERT(n->n_nptrs > 0);
585 newmask = any_children_tagmask(n);
586 if (newmask == entry_tagmask(entry)) {
587 break;
588 }
589 *pptr = entry_compose(n, newmask);
590 }
591 /*
592 * XXX is it worth to try to reduce height?
593 * if we do that, make radix_tree_grow rollback its change as well.
594 */
595 return entry_ptr(oldp);
596 }
597
598 /*
599 * radix_tree_lookup_node:
600 *
601 * returns the node at idx.
602 * returns NULL if nothing is found at idx.
603 */
604
605 void *
606 radix_tree_lookup_node(struct radix_tree *t, uint64_t idx)
607 {
608 void **vpp;
609
610 vpp = radix_tree_lookup_ptr(t, idx, NULL, false, 0);
611 if (vpp == NULL) {
612 return NULL;
613 }
614 return entry_ptr(*vpp);
615 }
616
617 static inline void
618 gang_lookup_init(struct radix_tree *t, uint64_t idx,
619 struct radix_tree_path *path, const unsigned int tagmask)
620 {
621 void **vpp;
622
623 vpp = radix_tree_lookup_ptr(t, idx, path, false, tagmask);
624 KASSERT(vpp == NULL ||
625 vpp == path_pptr(t, path, path->p_lastidx));
626 KASSERT(&t->t_root == path_pptr(t, path, 0));
627 KASSERT(path->p_lastidx == RADIX_TREE_INVALID_HEIGHT ||
628 path->p_lastidx == t->t_height ||
629 !entry_match_p(*path_pptr(t, path, path->p_lastidx), tagmask));
630 }
631
632 /*
633 * gang_lookup_scan:
634 *
635 * a helper routine for radix_tree_gang_lookup_node and its variants.
636 */
637
638 static inline unsigned int
639 __attribute__((__always_inline__))
640 gang_lookup_scan(struct radix_tree *t, struct radix_tree_path *path,
641 void **results, unsigned int maxresults, const unsigned int tagmask,
642 bool reverse)
643 {
644
645 /*
646 * we keep the path updated only for lastidx-1.
647 * vpp is what path_pptr(t, path, lastidx) would be.
648 */
649 void **vpp;
650 unsigned int nfound;
651 unsigned int lastidx;
652 /*
653 * set up scan direction dependant constants so that we can iterate
654 * n_ptrs as the following.
655 *
656 * for (i = first; i != guard; i += step)
657 * visit n->n_ptrs[i];
658 */
659 const int step = reverse ? -1 : 1;
660 const unsigned int first = reverse ? RADIX_TREE_PTR_PER_NODE - 1 : 0;
661 const unsigned int last = reverse ? 0 : RADIX_TREE_PTR_PER_NODE - 1;
662 const unsigned int guard = last + step;
663
664 KASSERT(maxresults > 0);
665 KASSERT(&t->t_root == path_pptr(t, path, 0));
666 lastidx = path->p_lastidx;
667 KASSERT(lastidx == RADIX_TREE_INVALID_HEIGHT ||
668 lastidx == t->t_height ||
669 !entry_match_p(*path_pptr(t, path, lastidx), tagmask));
670 nfound = 0;
671 if (lastidx == RADIX_TREE_INVALID_HEIGHT) {
672 if (reverse) {
673 lastidx = 0;
674 vpp = path_pptr(t, path, lastidx);
675 goto descend;
676 }
677 return 0;
678 }
679 vpp = path_pptr(t, path, lastidx);
680 while (/*CONSTCOND*/true) {
681 struct radix_tree_node *n;
682 unsigned int i;
683
684 if (entry_match_p(*vpp, tagmask)) {
685 KASSERT(lastidx == t->t_height);
686 /*
687 * record the matching non-NULL leaf.
688 */
689 results[nfound] = entry_ptr(*vpp);
690 nfound++;
691 if (nfound == maxresults) {
692 return nfound;
693 }
694 }
695 scan_siblings:
696 /*
697 * try to find the next matching non-NULL sibling.
698 */
699 if (lastidx == 0) {
700 /*
701 * the root has no siblings.
702 * we've done.
703 */
704 KASSERT(vpp == &t->t_root);
705 break;
706 }
707 n = path_node(t, path, lastidx - 1);
708 if (*vpp != NULL && n->n_nptrs == 1) {
709 /*
710 * optimization; if the node has only a single pointer
711 * and we've already visited it, there's no point to
712 * keep scanning in this node.
713 */
714 goto no_siblings;
715 }
716 for (i = vpp - n->n_ptrs + step; i != guard; i += step) {
717 KASSERT(i < RADIX_TREE_PTR_PER_NODE);
718 if (entry_match_p(n->n_ptrs[i], tagmask)) {
719 vpp = &n->n_ptrs[i];
720 break;
721 }
722 }
723 if (i == guard) {
724 no_siblings:
725 /*
726 * not found. go to parent.
727 */
728 lastidx--;
729 vpp = path_pptr(t, path, lastidx);
730 goto scan_siblings;
731 }
732 descend:
733 /*
734 * following the left-most (or right-most in the case of
735 * reverse scan) child node, decend until reaching the leaf or
736 * an non-matching entry.
737 */
738 while (entry_match_p(*vpp, tagmask) && lastidx < t->t_height) {
739 /*
740 * save vpp in the path so that we can come back to this
741 * node after finishing visiting children.
742 */
743 path->p_refs[lastidx].pptr = vpp;
744 n = entry_ptr(*vpp);
745 vpp = &n->n_ptrs[first];
746 lastidx++;
747 }
748 }
749 return nfound;
750 }
751
752 /*
753 * radix_tree_gang_lookup_node:
754 *
755 * search nodes starting from idx in the ascending order.
756 * results should be an array large enough to hold maxresults pointers.
757 * returns the number of nodes found, up to maxresults.
758 * returning less than maxresults means there are no more nodes.
759 *
760 * the result of this function is semantically equivalent to what could be
761 * obtained by repeated calls of radix_tree_lookup_node with increasing index.
762 * but this function is much faster when node indexes are distributed sparsely.
763 *
764 * note that this function doesn't return exact values of node indexes of
765 * found nodes. if they are important for a caller, it's the caller's
766 * responsibility to check them, typically by examinining the returned nodes
767 * using some caller-specific knowledge about them.
768 */
769
770 unsigned int
771 radix_tree_gang_lookup_node(struct radix_tree *t, uint64_t idx,
772 void **results, unsigned int maxresults)
773 {
774 struct radix_tree_path path;
775
776 gang_lookup_init(t, idx, &path, 0);
777 return gang_lookup_scan(t, &path, results, maxresults, 0, false);
778 }
779
780 /*
781 * radix_tree_gang_lookup_node_reverse:
782 *
783 * same as radix_tree_gang_lookup_node except that this one scans the
784 * tree in the reverse order. ie. descending index values.
785 */
786
787 unsigned int
788 radix_tree_gang_lookup_node_reverse(struct radix_tree *t, uint64_t idx,
789 void **results, unsigned int maxresults)
790 {
791 struct radix_tree_path path;
792
793 gang_lookup_init(t, idx, &path, 0);
794 return gang_lookup_scan(t, &path, results, maxresults, 0, true);
795 }
796
797 /*
798 * radix_tree_gang_lookup_tagged_node:
799 *
800 * same as radix_tree_gang_lookup_node except that this one only returns
801 * nodes tagged with tagid.
802 */
803
804 unsigned int
805 radix_tree_gang_lookup_tagged_node(struct radix_tree *t, uint64_t idx,
806 void **results, unsigned int maxresults, radix_tree_tagid_t tagid)
807 {
808 struct radix_tree_path path;
809 const unsigned int tagmask = tagid_to_mask(tagid);
810
811 gang_lookup_init(t, idx, &path, tagmask);
812 return gang_lookup_scan(t, &path, results, maxresults, tagmask, false);
813 }
814
815 /*
816 * radix_tree_gang_lookup_tagged_node_reverse:
817 *
818 * same as radix_tree_gang_lookup_tagged_node except that this one scans the
819 * tree in the reverse order. ie. descending index values.
820 */
821
822 unsigned int
823 radix_tree_gang_lookup_tagged_node_reverse(struct radix_tree *t, uint64_t idx,
824 void **results, unsigned int maxresults, radix_tree_tagid_t tagid)
825 {
826 struct radix_tree_path path;
827 const unsigned int tagmask = tagid_to_mask(tagid);
828
829 gang_lookup_init(t, idx, &path, tagmask);
830 return gang_lookup_scan(t, &path, results, maxresults, tagmask, true);
831 }
832
833 /*
834 * radix_tree_get_tag:
835 *
836 * return if the tag is set for the node at the given index. (true if set)
837 * it's illegal to call this function for a node which has not been inserted.
838 */
839
840 bool
841 radix_tree_get_tag(struct radix_tree *t, uint64_t idx,
842 radix_tree_tagid_t tagid)
843 {
844 #if 1
845 const unsigned int tagmask = tagid_to_mask(tagid);
846 void **vpp;
847
848 vpp = radix_tree_lookup_ptr(t, idx, NULL, false, tagmask);
849 if (vpp == NULL) {
850 return false;
851 }
852 KASSERT(*vpp != NULL);
853 return (entry_tagmask(*vpp) & tagmask) != 0;
854 #else
855 const unsigned int tagmask = tagid_to_mask(tagid);
856 void **vpp;
857
858 vpp = radix_tree_lookup_ptr(t, idx, NULL, false, 0);
859 KASSERT(vpp != NULL);
860 return (entry_tagmask(*vpp) & tagmask) != 0;
861 #endif
862 }
863
864 /*
865 * radix_tree_set_tag:
866 *
867 * set the tag for the node at the given index.
868 * it's illegal to call this function for a node which has not been inserted.
869 */
870
871 void
872 radix_tree_set_tag(struct radix_tree *t, uint64_t idx,
873 radix_tree_tagid_t tagid)
874 {
875 struct radix_tree_path path;
876 const unsigned int tagmask = tagid_to_mask(tagid);
877 void **vpp;
878 int i;
879
880 vpp = radix_tree_lookup_ptr(t, idx, &path, false, 0);
881 KASSERT(vpp != NULL);
882 KASSERT(*vpp != NULL);
883 KASSERT(path.p_lastidx == t->t_height);
884 KASSERT(vpp == path_pptr(t, &path, path.p_lastidx));
885 for (i = t->t_height; i >= 0; i--) {
886 void ** const pptr = (void **)path_pptr(t, &path, i);
887 void *entry;
888
889 KASSERT(pptr != NULL);
890 entry = *pptr;
891 if ((entry_tagmask(entry) & tagmask) != 0) {
892 break;
893 }
894 *pptr = (void *)((uintptr_t)entry | tagmask);
895 }
896 }
897
898 /*
899 * radix_tree_clear_tag:
900 *
901 * clear the tag for the node at the given index.
902 * it's illegal to call this function for a node which has not been inserted.
903 */
904
905 void
906 radix_tree_clear_tag(struct radix_tree *t, uint64_t idx,
907 radix_tree_tagid_t tagid)
908 {
909 struct radix_tree_path path;
910 const unsigned int tagmask = tagid_to_mask(tagid);
911 void **vpp;
912 int i;
913
914 vpp = radix_tree_lookup_ptr(t, idx, &path, false, 0);
915 KASSERT(vpp != NULL);
916 KASSERT(*vpp != NULL);
917 KASSERT(path.p_lastidx == t->t_height);
918 KASSERT(vpp == path_pptr(t, &path, path.p_lastidx));
919 /*
920 * if already cleared, nothing to do
921 */
922 if ((entry_tagmask(*vpp) & tagmask) == 0) {
923 return;
924 }
925 /*
926 * clear the tag only if no children have the tag.
927 */
928 for (i = t->t_height; i >= 0; i--) {
929 void ** const pptr = (void **)path_pptr(t, &path, i);
930 void *entry;
931
932 KASSERT(pptr != NULL);
933 entry = *pptr;
934 KASSERT((entry_tagmask(entry) & tagmask) != 0);
935 *pptr = entry_compose(entry_ptr(entry),
936 entry_tagmask(entry) & ~tagmask);
937 /*
938 * check if we should proceed to process the next level.
939 */
940 if (0 < i) {
941 struct radix_tree_node *n = path_node(t, &path, i - 1);
942
943 if ((any_children_tagmask(n) & tagmask) != 0) {
944 break;
945 }
946 }
947 }
948 }
949
950 #if defined(UNITTEST)
951
952 #include <inttypes.h>
953 #include <stdio.h>
954
955 static void
956 radix_tree_dump_node(const struct radix_tree *t, void *vp,
957 uint64_t offset, unsigned int height)
958 {
959 struct radix_tree_node *n;
960 unsigned int i;
961
962 for (i = 0; i < t->t_height - height; i++) {
963 printf(" ");
964 }
965 if (entry_tagmask(vp) == 0) {
966 printf("[%" PRIu64 "] %p", offset, entry_ptr(vp));
967 } else {
968 printf("[%" PRIu64 "] %p (tagmask=0x%x)", offset, entry_ptr(vp),
969 entry_tagmask(vp));
970 }
971 if (height == 0) {
972 printf(" (leaf)\n");
973 return;
974 }
975 n = entry_ptr(vp);
976 assert(any_children_tagmask(n) == entry_tagmask(vp));
977 printf(" (%u children)\n", n->n_nptrs);
978 for (i = 0; i < __arraycount(n->n_ptrs); i++) {
979 void *c;
980
981 c = n->n_ptrs[i];
982 if (c == NULL) {
983 continue;
984 }
985 radix_tree_dump_node(t, c,
986 offset + i * (UINT64_C(1) <<
987 (RADIX_TREE_BITS_PER_HEIGHT * (height - 1))), height - 1);
988 }
989 }
990
991 void radix_tree_dump(const struct radix_tree *);
992
993 void
994 radix_tree_dump(const struct radix_tree *t)
995 {
996
997 printf("tree %p height=%u\n", t, t->t_height);
998 radix_tree_dump_node(t, t->t_root, 0, t->t_height);
999 }
1000
1001 static void
1002 test1(void)
1003 {
1004 struct radix_tree s;
1005 struct radix_tree *t = &s;
1006 void *results[3];
1007
1008 radix_tree_init_tree(t);
1009 radix_tree_dump(t);
1010 assert(radix_tree_lookup_node(t, 0) == NULL);
1011 assert(radix_tree_lookup_node(t, 1000) == NULL);
1012 assert(radix_tree_gang_lookup_node(t, 0, results, 3) == 0);
1013 assert(radix_tree_gang_lookup_node(t, 1000, results, 3) == 0);
1014 assert(radix_tree_gang_lookup_node_reverse(t, 0, results, 3) == 0);
1015 assert(radix_tree_gang_lookup_node_reverse(t, 1000, results, 3) == 0);
1016 assert(radix_tree_gang_lookup_tagged_node(t, 0, results, 3, 0) == 0);
1017 assert(radix_tree_gang_lookup_tagged_node(t, 1000, results, 3, 0) == 0);
1018 assert(radix_tree_gang_lookup_tagged_node_reverse(t, 0, results, 3, 0)
1019 == 0);
1020 assert(radix_tree_gang_lookup_tagged_node_reverse(t, 1000, results, 3,
1021 0) == 0);
1022 assert(radix_tree_empty_tree_p(t));
1023 assert(radix_tree_empty_tagged_tree_p(t, 0));
1024 assert(radix_tree_empty_tagged_tree_p(t, 1));
1025 assert(radix_tree_insert_node(t, 0, (void *)0xdeadbea0) == 0);
1026 assert(!radix_tree_empty_tree_p(t));
1027 assert(radix_tree_empty_tagged_tree_p(t, 0));
1028 assert(radix_tree_empty_tagged_tree_p(t, 1));
1029 assert(radix_tree_lookup_node(t, 0) == (void *)0xdeadbea0);
1030 assert(radix_tree_lookup_node(t, 1000) == NULL);
1031 memset(results, 0, sizeof(results));
1032 assert(radix_tree_gang_lookup_node(t, 0, results, 3) == 1);
1033 assert(results[0] == (void *)0xdeadbea0);
1034 assert(radix_tree_gang_lookup_node(t, 1000, results, 3) == 0);
1035 memset(results, 0, sizeof(results));
1036 assert(radix_tree_gang_lookup_node_reverse(t, 0, results, 3) == 1);
1037 assert(results[0] == (void *)0xdeadbea0);
1038 memset(results, 0, sizeof(results));
1039 assert(radix_tree_gang_lookup_node_reverse(t, 1000, results, 3) == 1);
1040 assert(results[0] == (void *)0xdeadbea0);
1041 assert(radix_tree_gang_lookup_tagged_node(t, 0, results, 3, 0)
1042 == 0);
1043 assert(radix_tree_gang_lookup_tagged_node_reverse(t, 0, results, 3, 0)
1044 == 0);
1045 assert(radix_tree_insert_node(t, 1000, (void *)0xdeadbea0) == 0);
1046 assert(radix_tree_remove_node(t, 0) == (void *)0xdeadbea0);
1047 assert(!radix_tree_empty_tree_p(t));
1048 radix_tree_dump(t);
1049 assert(radix_tree_lookup_node(t, 0) == NULL);
1050 assert(radix_tree_lookup_node(t, 1000) == (void *)0xdeadbea0);
1051 memset(results, 0, sizeof(results));
1052 assert(radix_tree_gang_lookup_node(t, 0, results, 3) == 1);
1053 assert(results[0] == (void *)0xdeadbea0);
1054 memset(results, 0, sizeof(results));
1055 assert(radix_tree_gang_lookup_node(t, 1000, results, 3) == 1);
1056 assert(results[0] == (void *)0xdeadbea0);
1057 assert(radix_tree_gang_lookup_node_reverse(t, 0, results, 3) == 0);
1058 memset(results, 0, sizeof(results));
1059 assert(radix_tree_gang_lookup_node_reverse(t, 1000, results, 3) == 1);
1060 assert(results[0] == (void *)0xdeadbea0);
1061 assert(radix_tree_gang_lookup_tagged_node(t, 0, results, 3, 0)
1062 == 0);
1063 assert(radix_tree_gang_lookup_tagged_node_reverse(t, 0, results, 3, 0)
1064 == 0);
1065 assert(!radix_tree_get_tag(t, 1000, 0));
1066 assert(!radix_tree_get_tag(t, 1000, 1));
1067 assert(radix_tree_empty_tagged_tree_p(t, 0));
1068 assert(radix_tree_empty_tagged_tree_p(t, 1));
1069 radix_tree_set_tag(t, 1000, 1);
1070 assert(!radix_tree_get_tag(t, 1000, 0));
1071 assert(radix_tree_get_tag(t, 1000, 1));
1072 assert(radix_tree_empty_tagged_tree_p(t, 0));
1073 assert(!radix_tree_empty_tagged_tree_p(t, 1));
1074 radix_tree_dump(t);
1075 assert(radix_tree_lookup_node(t, 1000) == (void *)0xdeadbea0);
1076 assert(radix_tree_insert_node(t, 0, (void *)0xbea0) == 0);
1077 radix_tree_dump(t);
1078 assert(radix_tree_lookup_node(t, 0) == (void *)0xbea0);
1079 assert(radix_tree_lookup_node(t, 1000) == (void *)0xdeadbea0);
1080 assert(radix_tree_insert_node(t, UINT64_C(10000000000), (void *)0xdea0)
1081 == 0);
1082 radix_tree_dump(t);
1083 assert(radix_tree_lookup_node(t, 0) == (void *)0xbea0);
1084 assert(radix_tree_lookup_node(t, 1000) == (void *)0xdeadbea0);
1085 assert(radix_tree_lookup_node(t, UINT64_C(10000000000)) ==
1086 (void *)0xdea0);
1087 radix_tree_dump(t);
1088 assert(!radix_tree_get_tag(t, 0, 1));
1089 assert(radix_tree_get_tag(t, 1000, 1));
1090 assert(!radix_tree_get_tag(t, UINT64_C(10000000000), 1));
1091 radix_tree_set_tag(t, 0, 1);;
1092 radix_tree_set_tag(t, UINT64_C(10000000000), 1);
1093 radix_tree_dump(t);
1094 assert(radix_tree_get_tag(t, 0, 1));
1095 assert(radix_tree_get_tag(t, 1000, 1));
1096 assert(radix_tree_get_tag(t, UINT64_C(10000000000), 1));
1097 radix_tree_clear_tag(t, 0, 1);;
1098 radix_tree_clear_tag(t, UINT64_C(10000000000), 1);
1099 radix_tree_dump(t);
1100 assert(!radix_tree_get_tag(t, 0, 1));
1101 assert(radix_tree_get_tag(t, 1000, 1));
1102 assert(!radix_tree_get_tag(t, UINT64_C(10000000000), 1));
1103 radix_tree_dump(t);
1104 assert(radix_tree_replace_node(t, 1000, (void *)0x12345678) ==
1105 (void *)0xdeadbea0);
1106 assert(!radix_tree_get_tag(t, 1000, 0));
1107 assert(radix_tree_get_tag(t, 1000, 1));
1108 assert(radix_tree_gang_lookup_node(t, 0, results, 3) == 3);
1109 assert(results[0] == (void *)0xbea0);
1110 assert(results[1] == (void *)0x12345678);
1111 assert(results[2] == (void *)0xdea0);
1112 assert(radix_tree_gang_lookup_node(t, 1, results, 3) == 2);
1113 assert(results[0] == (void *)0x12345678);
1114 assert(results[1] == (void *)0xdea0);
1115 assert(radix_tree_gang_lookup_node(t, 1001, results, 3) == 1);
1116 assert(results[0] == (void *)0xdea0);
1117 assert(radix_tree_gang_lookup_node(t, UINT64_C(10000000001), results, 3)
1118 == 0);
1119 assert(radix_tree_gang_lookup_node(t, UINT64_C(1000000000000), results,
1120 3) == 0);
1121 assert(radix_tree_gang_lookup_tagged_node(t, 0, results, 100, 1) == 1);
1122 assert(results[0] == (void *)0x12345678);
1123 assert(entry_tagmask(t->t_root) != 0);
1124 assert(radix_tree_remove_node(t, 1000) == (void *)0x12345678);
1125 assert(entry_tagmask(t->t_root) == 0);
1126 radix_tree_dump(t);
1127 assert(radix_tree_remove_node(t, UINT64_C(10000000000)) ==
1128 (void *)0xdea0);
1129 radix_tree_dump(t);
1130 assert(radix_tree_remove_node(t, 0) == (void *)0xbea0);
1131 radix_tree_dump(t);
1132 radix_tree_fini_tree(t);
1133 }
1134
1135 #include <sys/time.h>
1136
1137 struct testnode {
1138 uint64_t idx;
1139 bool tagged[RADIX_TREE_TAG_ID_MAX];
1140 };
1141
1142 static void
1143 printops(const char *title, const char *name, int tag, unsigned int n,
1144 const struct timeval *stv, const struct timeval *etv)
1145 {
1146 uint64_t s = stv->tv_sec * 1000000 + stv->tv_usec;
1147 uint64_t e = etv->tv_sec * 1000000 + etv->tv_usec;
1148
1149 printf("RESULT %s %s %d %lf op/s\n", title, name, tag,
1150 (double)n / (e - s) * 1000000);
1151 }
1152
1153 #define TEST2_GANG_LOOKUP_NODES 16
1154
1155 static bool
1156 test2_should_tag(unsigned int i, radix_tree_tagid_t tagid)
1157 {
1158
1159 if (tagid == 0) {
1160 return (i & 0x3) == 0; /* 25% */
1161 } else {
1162 return (i % 7) == 0; /* 14% */
1163 }
1164 }
1165
1166 static void
1167 test2(const char *title, bool dense)
1168 {
1169 struct radix_tree s;
1170 struct radix_tree *t = &s;
1171 struct testnode *n;
1172 unsigned int i;
1173 unsigned int nnodes = 100000;
1174 unsigned int removed;
1175 radix_tree_tagid_t tag;
1176 unsigned int ntagged[RADIX_TREE_TAG_ID_MAX];
1177 struct testnode *nodes;
1178 struct timeval stv;
1179 struct timeval etv;
1180
1181 nodes = malloc(nnodes * sizeof(*nodes));
1182 for (tag = 0; tag < RADIX_TREE_TAG_ID_MAX; tag++) {
1183 ntagged[tag] = 0;
1184 }
1185 radix_tree_init_tree(t);
1186 for (i = 0; i < nnodes; i++) {
1187 n = &nodes[i];
1188 n->idx = random();
1189 if (sizeof(long) == 4) {
1190 n->idx <<= 32;
1191 n->idx |= (uint32_t)random();
1192 }
1193 if (dense) {
1194 n->idx %= nnodes * 2;
1195 }
1196 while (radix_tree_lookup_node(t, n->idx) != NULL) {
1197 n->idx++;
1198 }
1199 radix_tree_insert_node(t, n->idx, n);
1200 for (tag = 0; tag < RADIX_TREE_TAG_ID_MAX; tag++) {
1201 n->tagged[tag] = test2_should_tag(i, tag);
1202 if (n->tagged[tag]) {
1203 radix_tree_set_tag(t, n->idx, tag);
1204 ntagged[tag]++;
1205 }
1206 assert(n->tagged[tag] ==
1207 radix_tree_get_tag(t, n->idx, tag));
1208 }
1209 }
1210
1211 gettimeofday(&stv, NULL);
1212 for (i = 0; i < nnodes; i++) {
1213 n = &nodes[i];
1214 assert(radix_tree_lookup_node(t, n->idx) == n);
1215 }
1216 gettimeofday(&etv, NULL);
1217 printops(title, "lookup", 0, nnodes, &stv, &etv);
1218
1219 for (tag = 0; tag < RADIX_TREE_TAG_ID_MAX; tag++) {
1220 unsigned int count = 0;
1221
1222 gettimeofday(&stv, NULL);
1223 for (i = 0; i < nnodes; i++) {
1224 bool tagged;
1225
1226 n = &nodes[i];
1227 tagged = radix_tree_get_tag(t, n->idx, tag);
1228 assert(n->tagged[tag] == tagged);
1229 if (tagged) {
1230 count++;
1231 }
1232 }
1233 gettimeofday(&etv, NULL);
1234 assert(ntagged[tag] == count);
1235 printops(title, "get_tag", tag, nnodes, &stv, &etv);
1236 }
1237
1238 gettimeofday(&stv, NULL);
1239 for (i = 0; i < nnodes; i++) {
1240 n = &nodes[i];
1241 radix_tree_remove_node(t, n->idx);
1242 }
1243 gettimeofday(&etv, NULL);
1244 printops(title, "remove", 0, nnodes, &stv, &etv);
1245
1246 gettimeofday(&stv, NULL);
1247 for (i = 0; i < nnodes; i++) {
1248 n = &nodes[i];
1249 radix_tree_insert_node(t, n->idx, n);
1250 }
1251 gettimeofday(&etv, NULL);
1252 printops(title, "insert", 0, nnodes, &stv, &etv);
1253
1254 for (tag = 0; tag < RADIX_TREE_TAG_ID_MAX; tag++) {
1255 ntagged[tag] = 0;
1256 gettimeofday(&stv, NULL);
1257 for (i = 0; i < nnodes; i++) {
1258 n = &nodes[i];
1259 if (n->tagged[tag]) {
1260 radix_tree_set_tag(t, n->idx, tag);
1261 ntagged[tag]++;
1262 }
1263 }
1264 gettimeofday(&etv, NULL);
1265 printops(title, "set_tag", tag, ntagged[tag], &stv, &etv);
1266 }
1267
1268 gettimeofday(&stv, NULL);
1269 {
1270 struct testnode *results[TEST2_GANG_LOOKUP_NODES];
1271 uint64_t nextidx;
1272 unsigned int nfound;
1273 unsigned int total;
1274
1275 nextidx = 0;
1276 total = 0;
1277 while ((nfound = radix_tree_gang_lookup_node(t, nextidx,
1278 (void *)results, __arraycount(results))) > 0) {
1279 nextidx = results[nfound - 1]->idx + 1;
1280 total += nfound;
1281 if (nextidx == 0) {
1282 break;
1283 }
1284 }
1285 assert(total == nnodes);
1286 }
1287 gettimeofday(&etv, NULL);
1288 printops(title, "ganglookup", 0, nnodes, &stv, &etv);
1289
1290 gettimeofday(&stv, NULL);
1291 {
1292 struct testnode *results[TEST2_GANG_LOOKUP_NODES];
1293 uint64_t nextidx;
1294 unsigned int nfound;
1295 unsigned int total;
1296
1297 nextidx = UINT64_MAX;
1298 total = 0;
1299 while ((nfound = radix_tree_gang_lookup_node_reverse(t, nextidx,
1300 (void *)results, __arraycount(results))) > 0) {
1301 nextidx = results[nfound - 1]->idx - 1;
1302 total += nfound;
1303 if (nextidx == UINT64_MAX) {
1304 break;
1305 }
1306 }
1307 assert(total == nnodes);
1308 }
1309 gettimeofday(&etv, NULL);
1310 printops(title, "ganglookup_reverse", 0, nnodes, &stv, &etv);
1311
1312 for (tag = 0; tag < RADIX_TREE_TAG_ID_MAX; tag++) {
1313 gettimeofday(&stv, NULL);
1314 {
1315 struct testnode *results[TEST2_GANG_LOOKUP_NODES];
1316 uint64_t nextidx;
1317 unsigned int nfound;
1318 unsigned int total;
1319
1320 nextidx = 0;
1321 total = 0;
1322 while ((nfound = radix_tree_gang_lookup_tagged_node(t,
1323 nextidx, (void *)results, __arraycount(results),
1324 tag)) > 0) {
1325 nextidx = results[nfound - 1]->idx + 1;
1326 total += nfound;
1327 }
1328 assert(total == ntagged[tag]);
1329 }
1330 gettimeofday(&etv, NULL);
1331 printops(title, "ganglookup_tag", tag, ntagged[tag], &stv,
1332 &etv);
1333 }
1334
1335 for (tag = 0; tag < RADIX_TREE_TAG_ID_MAX; tag++) {
1336 gettimeofday(&stv, NULL);
1337 {
1338 struct testnode *results[TEST2_GANG_LOOKUP_NODES];
1339 uint64_t nextidx;
1340 unsigned int nfound;
1341 unsigned int total;
1342
1343 nextidx = UINT64_MAX;
1344 total = 0;
1345 while ((nfound =
1346 radix_tree_gang_lookup_tagged_node_reverse(t,
1347 nextidx, (void *)results, __arraycount(results),
1348 tag)) > 0) {
1349 nextidx = results[nfound - 1]->idx - 1;
1350 total += nfound;
1351 if (nextidx == UINT64_MAX) {
1352 break;
1353 }
1354 }
1355 assert(total == ntagged[tag]);
1356 }
1357 gettimeofday(&etv, NULL);
1358 printops(title, "ganglookup_tag_reverse", tag, ntagged[tag],
1359 &stv, &etv);
1360 }
1361
1362 removed = 0;
1363 for (tag = 0; tag < RADIX_TREE_TAG_ID_MAX; tag++) {
1364 unsigned int total;
1365
1366 total = 0;
1367 gettimeofday(&stv, NULL);
1368 {
1369 struct testnode *results[TEST2_GANG_LOOKUP_NODES];
1370 uint64_t nextidx;
1371 unsigned int nfound;
1372
1373 nextidx = 0;
1374 while ((nfound = radix_tree_gang_lookup_tagged_node(t,
1375 nextidx, (void *)results, __arraycount(results),
1376 tag)) > 0) {
1377 for (i = 0; i < nfound; i++) {
1378 radix_tree_remove_node(t,
1379 results[i]->idx);
1380 }
1381 nextidx = results[nfound - 1]->idx + 1;
1382 total += nfound;
1383 if (nextidx == 0) {
1384 break;
1385 }
1386 }
1387 assert(tag != 0 || total == ntagged[tag]);
1388 assert(total <= ntagged[tag]);
1389 }
1390 gettimeofday(&etv, NULL);
1391 printops(title, "ganglookup_tag+remove", tag, total, &stv,
1392 &etv);
1393 removed += total;
1394 }
1395
1396 gettimeofday(&stv, NULL);
1397 {
1398 struct testnode *results[TEST2_GANG_LOOKUP_NODES];
1399 uint64_t nextidx;
1400 unsigned int nfound;
1401 unsigned int total;
1402
1403 nextidx = 0;
1404 total = 0;
1405 while ((nfound = radix_tree_gang_lookup_node(t, nextidx,
1406 (void *)results, __arraycount(results))) > 0) {
1407 for (i = 0; i < nfound; i++) {
1408 assert(results[i] == radix_tree_remove_node(t,
1409 results[i]->idx));
1410 }
1411 nextidx = results[nfound - 1]->idx + 1;
1412 total += nfound;
1413 if (nextidx == 0) {
1414 break;
1415 }
1416 }
1417 assert(total == nnodes - removed);
1418 }
1419 gettimeofday(&etv, NULL);
1420 printops(title, "ganglookup+remove", 0, nnodes - removed, &stv, &etv);
1421
1422 assert(radix_tree_empty_tree_p(t));
1423 assert(radix_tree_empty_tagged_tree_p(t, 0));
1424 assert(radix_tree_empty_tagged_tree_p(t, 1));
1425 radix_tree_fini_tree(t);
1426 free(nodes);
1427 }
1428
1429 int
1430 main(int argc, char *argv[])
1431 {
1432
1433 test1();
1434 test2("dense", true);
1435 test2("sparse", false);
1436 return 0;
1437 }
1438
1439 #endif /* defined(UNITTEST) */
1440