1 /* $NetBSD: radixtree.c,v 1.17 2011/11/02 13:49:43 yamt Exp $ */
4 * Copyright (c)2011 YAMAMOTO Takashi,
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
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.
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
32 * this is an implementation of radix tree, whose keys are uint64_t and leafs
33 * are user provided pointers.
35 * leaf nodes are just void * and this implementation doesn't care about
36 * what they actually point to. however, this implementation has an assumption
37 * about their alignment. specifically, this implementation assumes that their
38 * 2 LSBs are zero and uses them internally.
40 * intermediate nodes are automatically allocated and freed internally and
41 * basically users don't need to care about them. only radix_tree_insert_node
42 * function can allocate memory for intermediate nodes and thus can fail for
46 * it's designed to work efficiently with dense index distribution.
47 * the memory consumption (number of necessary intermediate nodes)
48 * heavily depends on index distribution. basically, more dense index
49 * distribution consumes less nodes per item.
51 * the best case: about RADIX_TREE_PTR_PER_NODE items per intermediate node.
52 * the worst case: RADIX_TREE_MAX_HEIGHT intermediate nodes per item.
55 * this implementation provides a way to lookup many nodes quickly via
56 * radix_tree_gang_lookup_node function and its varients.
59 * this implementation provides tagging functionality to allow quick
60 * scanning of a subset of leaf nodes. leaf nodes are untagged when
61 * inserted into the tree and can be tagged by radix_tree_set_tag function.
62 * radix_tree_gang_lookup_tagged_node function and its variants returns
63 * only leaf nodes with the given tag. to reduce amount of nodes to visit for
64 * these functions, this implementation keeps tagging information in internal
65 * intermediate nodes and quickly skips uninterested parts of a tree.
68 #include <sys/cdefs.h>
70 #if defined(_KERNEL) || defined(_STANDALONE)
71 __KERNEL_RCSID(0, "$NetBSD: radixtree.c,v 1.17 2011/11/02 13:49:43 yamt Exp $");
72 #include <sys/param.h>
73 #include <sys/errno.h>
75 #include <sys/radixtree.h>
76 #include <lib/libkern/libkern.h>
77 #if defined(_STANDALONE)
78 #include <lib/libsa/stand.h>
79 #endif /* defined(_STANDALONE) */
80 #else /* defined(_KERNEL) || defined(_STANDALONE) */
81 __RCSID("$NetBSD: radixtree.c,v 1.17 2011/11/02 13:49:43 yamt Exp $");
88 #define KASSERT assert
90 #define KASSERT(a) /* nothing */
92 #endif /* defined(_KERNEL) || defined(_STANDALONE) */
94 #include <sys/radixtree.h>
96 #define RADIX_TREE_BITS_PER_HEIGHT 4 /* XXX tune */
97 #define RADIX_TREE_PTR_PER_NODE (1 << RADIX_TREE_BITS_PER_HEIGHT)
98 #define RADIX_TREE_MAX_HEIGHT (64 / RADIX_TREE_BITS_PER_HEIGHT)
99 #define RADIX_TREE_INVALID_HEIGHT (RADIX_TREE_MAX_HEIGHT + 1)
100 __CTASSERT((64 % RADIX_TREE_BITS_PER_HEIGHT
) == 0);
102 __CTASSERT(((1 << RADIX_TREE_TAG_ID_MAX
) & (sizeof(int) - 1)) == 0);
103 #define RADIX_TREE_TAG_MASK ((1 << RADIX_TREE_TAG_ID_MAX) - 1)
109 return (void *)((uintptr_t)p
& ~RADIX_TREE_TAG_MASK
);
112 static inline unsigned int
113 entry_tagmask(void *p
)
116 return (uintptr_t)p
& RADIX_TREE_TAG_MASK
;
120 entry_compose(void *p
, unsigned int tagmask
)
123 return (void *)((uintptr_t)p
| tagmask
);
127 entry_match_p(void *p
, unsigned int tagmask
)
130 KASSERT(entry_ptr(p
) != NULL
|| entry_tagmask(p
) == 0);
137 return (entry_tagmask(p
) & tagmask
) != 0;
140 static inline unsigned int
141 tagid_to_mask(radix_tree_tagid_t id
)
145 KASSERT(id
< RADIX_TREE_TAG_ID_MAX
);
150 * radix_tree_node: an intermediate node
152 * we don't care the type of leaf nodes. they are just void *.
155 struct radix_tree_node
{
156 void *n_ptrs
[RADIX_TREE_PTR_PER_NODE
];
157 unsigned int n_nptrs
; /* # of non-NULL pointers in n_ptrs */
161 * any_children_tagmask:
163 * return OR'ed tagmask of the given node's children.
167 any_children_tagmask(const struct radix_tree_node
*n
)
173 for (i
= 0; i
< RADIX_TREE_PTR_PER_NODE
; i
++) {
174 mask
|= (unsigned int)(uintptr_t)n
->n_ptrs
[i
];
176 return mask
& RADIX_TREE_TAG_MASK
;
180 * p_refs[0].pptr == &t->t_root
182 * p_refs[n].pptr == &(*p_refs[n-1])->n_ptrs[x]
185 * p_refs[t->t_height].pptr == &leaf_pointer
188 struct radix_tree_path
{
189 struct radix_tree_node_ref
{
191 } p_refs
[RADIX_TREE_MAX_HEIGHT
+ 1]; /* +1 for the root ptr */
193 * p_lastidx is either the index of the last valid element of p_refs[]
194 * or RADIX_TREE_INVALID_HEIGHT.
195 * RADIX_TREE_INVALID_HEIGHT means that radix_tree_lookup_ptr found
196 * that the height of the tree is not enough to cover the given index.
198 unsigned int p_lastidx
;
201 static inline void **
202 path_pptr(const struct radix_tree
*t
, const struct radix_tree_path
*p
,
206 KASSERT(height
<= t
->t_height
);
207 return p
->p_refs
[height
].pptr
;
210 static inline struct radix_tree_node
*
211 path_node(const struct radix_tree
* t
, const struct radix_tree_path
*p
,
215 KASSERT(height
<= t
->t_height
);
216 return entry_ptr(*path_pptr(t
, p
, height
));
220 * radix_tree_init_tree:
226 radix_tree_init_tree(struct radix_tree
*t
)
234 * radix_tree_init_tree:
240 radix_tree_fini_tree(struct radix_tree
*t
)
243 KASSERT(t
->t_root
== NULL
);
244 KASSERT(t
->t_height
== 0);
248 radix_tree_empty_tree_p(struct radix_tree
*t
)
251 return t
->t_root
== NULL
;
255 radix_tree_empty_tagged_tree_p(struct radix_tree
*t
, radix_tree_tagid_t tagid
)
257 const unsigned int tagmask
= tagid_to_mask(tagid
);
259 return (entry_tagmask(t
->t_root
) & tagmask
) == 0;
263 radix_tree_node_init(struct radix_tree_node
*n
)
266 memset(n
, 0, sizeof(*n
));
270 pool_cache_t radix_tree_node_cache __read_mostly
;
273 radix_tree_node_ctor(void *dummy
, void *item
, int flags
)
275 struct radix_tree_node
*n
= item
;
277 KASSERT(dummy
== NULL
);
278 radix_tree_node_init(n
);
285 * initialize the subsystem.
289 radix_tree_init(void)
292 radix_tree_node_cache
= pool_cache_init(sizeof(struct radix_tree_node
),
293 0, 0, 0, "radix_tree_node", NULL
, IPL_NONE
, radix_tree_node_ctor
,
295 KASSERT(radix_tree_node_cache
!= NULL
);
297 #endif /* defined(_KERNEL) */
300 radix_tree_node_clean_p(const struct radix_tree_node
*n
)
304 if (n
->n_nptrs
!= 0) {
307 for (i
= 0; i
< RADIX_TREE_PTR_PER_NODE
; i
++) {
308 if (n
->n_ptrs
[i
] != NULL
) {
315 static struct radix_tree_node
*
316 radix_tree_alloc_node(void)
318 struct radix_tree_node
*n
;
321 n
= pool_cache_get(radix_tree_node_cache
, PR_NOWAIT
);
322 #else /* defined(_KERNEL) */
323 #if defined(_STANDALONE)
324 n
= alloc(sizeof(*n
));
325 #else /* defined(_STANDALONE) */
326 n
= malloc(sizeof(*n
));
327 #endif /* defined(_STANDALONE) */
329 radix_tree_node_init(n
);
331 #endif /* defined(_KERNEL) */
332 KASSERT(n
== NULL
|| radix_tree_node_clean_p(n
));
337 radix_tree_free_node(struct radix_tree_node
*n
)
340 KASSERT(radix_tree_node_clean_p(n
));
342 pool_cache_put(radix_tree_node_cache
, n
);
343 #elif defined(_STANDALONE)
344 dealloc(n
, sizeof(*n
));
351 radix_tree_grow(struct radix_tree
*t
, unsigned int newheight
)
353 const unsigned int tagmask
= entry_tagmask(t
->t_root
);
355 KASSERT(newheight
<= 64 / RADIX_TREE_BITS_PER_HEIGHT
);
356 if (t
->t_root
== NULL
) {
357 t
->t_height
= newheight
;
360 while (t
->t_height
< newheight
) {
361 struct radix_tree_node
*n
;
363 n
= radix_tree_alloc_node();
366 * don't bother to revert our changes.
367 * the caller will likely retry.
372 n
->n_ptrs
[0] = t
->t_root
;
373 t
->t_root
= entry_compose(n
, tagmask
);
380 * radix_tree_lookup_ptr:
382 * an internal helper function used for various exported functions.
384 * return the pointer to store the node for the given index.
386 * if alloc is true, try to allocate the storage. (note for _KERNEL:
387 * in that case, this function can block.) if the allocation failed or
388 * alloc is false, return NULL.
390 * if path is not NULL, fill it for the caller's investigation.
392 * if tagmask is not zero, search only for nodes with the tag set.
393 * note that, however, this function doesn't check the tagmask for the leaf
394 * pointer. it's a caller's responsibility to investigate the value which
395 * is pointed by the returned pointer if necessary.
397 * while this function is a bit large, as it's called with some constant
398 * arguments, inlining might have benefits. anyway, a compiler will decide.
401 static inline void **
402 radix_tree_lookup_ptr(struct radix_tree
*t
, uint64_t idx
,
403 struct radix_tree_path
*path
, bool alloc
, const unsigned int tagmask
)
405 struct radix_tree_node
*n
;
406 int hshift
= RADIX_TREE_BITS_PER_HEIGHT
* t
->t_height
;
409 const uint64_t mask
= (UINT64_C(1) << RADIX_TREE_BITS_PER_HEIGHT
) - 1;
410 struct radix_tree_node_ref
*refs
= NULL
;
413 * check unsupported combinations
415 KASSERT(tagmask
== 0 || !alloc
);
416 KASSERT(path
== NULL
|| !alloc
);
423 for (shift
= 64 - RADIX_TREE_BITS_PER_HEIGHT
; shift
>= 0;) {
424 struct radix_tree_node
*c
;
426 const uint64_t i
= (idx
>> shift
) & mask
;
428 if (shift
>= hshift
) {
429 unsigned int newheight
;
431 KASSERT(vpp
== &t
->t_root
);
433 shift
-= RADIX_TREE_BITS_PER_HEIGHT
;
438 KASSERT((refs
- path
->p_refs
) == 0);
440 RADIX_TREE_INVALID_HEIGHT
;
444 newheight
= shift
/ RADIX_TREE_BITS_PER_HEIGHT
+ 1;
445 if (radix_tree_grow(t
, newheight
)) {
448 hshift
= RADIX_TREE_BITS_PER_HEIGHT
* t
->t_height
;
451 c
= entry_ptr(entry
);
454 (entry_tagmask(entry
) & tagmask
) == 0)) {
457 path
->p_lastidx
= refs
- path
->p_refs
;
461 c
= radix_tree_alloc_node();
467 KASSERT(n
->n_nptrs
< RADIX_TREE_PTR_PER_NODE
);
477 shift
-= RADIX_TREE_BITS_PER_HEIGHT
;
480 KASSERT(*vpp
== NULL
);
482 KASSERT(n
->n_nptrs
< RADIX_TREE_PTR_PER_NODE
);
487 path
->p_lastidx
= refs
- path
->p_refs
;
493 * radix_tree_insert_node:
495 * insert the node at idx.
496 * it's illegal to insert NULL.
497 * it's illegal to insert a non-aligned pointer.
499 * this function returns ENOMEM if necessary memory allocation failed.
500 * otherwise, this function returns 0.
502 * note that inserting a node can involves memory allocation for intermediate
503 * nodes. if _KERNEL, it's done with no-sleep IPL_NONE memory allocation.
505 * for the newly inserted node, all tags are cleared.
509 radix_tree_insert_node(struct radix_tree
*t
, uint64_t idx
, void *p
)
514 KASSERT(entry_compose(p
, 0) == p
);
515 vpp
= radix_tree_lookup_ptr(t
, idx
, NULL
, true, 0);
519 KASSERT(*vpp
== NULL
);
525 * radix_tree_replace_node:
527 * replace a node at the given index with the given node.
528 * return the old node.
529 * it's illegal to try to replace a node which has not been inserted.
531 * this function doesn't change tags.
535 radix_tree_replace_node(struct radix_tree
*t
, uint64_t idx
, void *p
)
541 KASSERT(entry_compose(p
, 0) == p
);
542 vpp
= radix_tree_lookup_ptr(t
, idx
, NULL
, false, 0);
543 KASSERT(vpp
!= NULL
);
545 KASSERT(oldp
!= NULL
);
546 *vpp
= entry_compose(p
, entry_tagmask(*vpp
));
547 return entry_ptr(oldp
);
551 * radix_tree_remove_node:
553 * remove the node at idx.
554 * it's illegal to try to remove a node which has not been inserted.
558 radix_tree_remove_node(struct radix_tree
*t
, uint64_t idx
)
560 struct radix_tree_path path
;
565 vpp
= radix_tree_lookup_ptr(t
, idx
, &path
, false, 0);
566 KASSERT(vpp
!= NULL
);
568 KASSERT(oldp
!= NULL
);
569 KASSERT(path
.p_lastidx
== t
->t_height
);
570 KASSERT(vpp
== path_pptr(t
, &path
, path
.p_lastidx
));
572 for (i
= t
->t_height
- 1; i
>= 0; i
--) {
574 struct radix_tree_node
** const pptr
=
575 (struct radix_tree_node
**)path_pptr(t
, &path
, i
);
576 struct radix_tree_node
*n
;
578 KASSERT(pptr
!= NULL
);
580 n
= entry_ptr(entry
);
582 KASSERT(n
->n_nptrs
> 0);
584 if (n
->n_nptrs
> 0) {
587 radix_tree_free_node(n
);
594 KASSERT(t
->t_root
== NULL
);
600 for (; i
>= 0; i
--) {
602 struct radix_tree_node
** const pptr
=
603 (struct radix_tree_node
**)path_pptr(t
, &path
, i
);
604 struct radix_tree_node
*n
;
605 unsigned int newmask
;
607 KASSERT(pptr
!= NULL
);
609 n
= entry_ptr(entry
);
611 KASSERT(n
->n_nptrs
> 0);
612 newmask
= any_children_tagmask(n
);
613 if (newmask
== entry_tagmask(entry
)) {
616 *pptr
= entry_compose(n
, newmask
);
619 * XXX is it worth to try to reduce height?
620 * if we do that, make radix_tree_grow rollback its change as well.
622 return entry_ptr(oldp
);
626 * radix_tree_lookup_node:
628 * returns the node at idx.
629 * returns NULL if nothing is found at idx.
633 radix_tree_lookup_node(struct radix_tree
*t
, uint64_t idx
)
637 vpp
= radix_tree_lookup_ptr(t
, idx
, NULL
, false, 0);
641 return entry_ptr(*vpp
);
645 gang_lookup_init(struct radix_tree
*t
, uint64_t idx
,
646 struct radix_tree_path
*path
, const unsigned int tagmask
)
650 vpp
= radix_tree_lookup_ptr(t
, idx
, path
, false, tagmask
);
651 KASSERT(vpp
== NULL
||
652 vpp
== path_pptr(t
, path
, path
->p_lastidx
));
653 KASSERT(&t
->t_root
== path_pptr(t
, path
, 0));
654 KASSERT(path
->p_lastidx
== RADIX_TREE_INVALID_HEIGHT
||
655 path
->p_lastidx
== t
->t_height
||
656 !entry_match_p(*path_pptr(t
, path
, path
->p_lastidx
), tagmask
));
662 * a helper routine for radix_tree_gang_lookup_node and its variants.
665 static inline unsigned int
666 __attribute__((__always_inline__
))
667 gang_lookup_scan(struct radix_tree
*t
, struct radix_tree_path
*path
,
668 void **results
, unsigned int maxresults
, const unsigned int tagmask
,
673 * we keep the path updated only for lastidx-1.
674 * vpp is what path_pptr(t, path, lastidx) would be.
678 unsigned int lastidx
;
680 * set up scan direction dependant constants so that we can iterate
681 * n_ptrs as the following.
683 * for (i = first; i != guard; i += step)
684 * visit n->n_ptrs[i];
686 const int step
= reverse
? -1 : 1;
687 const unsigned int first
= reverse
? RADIX_TREE_PTR_PER_NODE
- 1 : 0;
688 const unsigned int last
= reverse
? 0 : RADIX_TREE_PTR_PER_NODE
- 1;
689 const unsigned int guard
= last
+ step
;
691 KASSERT(maxresults
> 0);
692 KASSERT(&t
->t_root
== path_pptr(t
, path
, 0));
693 lastidx
= path
->p_lastidx
;
694 KASSERT(lastidx
== RADIX_TREE_INVALID_HEIGHT
||
695 lastidx
== t
->t_height
||
696 !entry_match_p(*path_pptr(t
, path
, lastidx
), tagmask
));
698 if (lastidx
== RADIX_TREE_INVALID_HEIGHT
) {
701 vpp
= path_pptr(t
, path
, lastidx
);
706 vpp
= path_pptr(t
, path
, lastidx
);
707 while (/*CONSTCOND*/true) {
708 struct radix_tree_node
*n
;
711 if (entry_match_p(*vpp
, tagmask
)) {
712 KASSERT(lastidx
== t
->t_height
);
714 * record the matching non-NULL leaf.
716 results
[nfound
] = entry_ptr(*vpp
);
718 if (nfound
== maxresults
) {
724 * try to find the next matching non-NULL sibling.
728 * the root has no siblings.
731 KASSERT(vpp
== &t
->t_root
);
734 n
= path_node(t
, path
, lastidx
- 1);
735 if (*vpp
!= NULL
&& n
->n_nptrs
== 1) {
737 * optimization; if the node has only a single pointer
738 * and we've already visited it, there's no point to
739 * keep scanning in this node.
743 for (i
= vpp
- n
->n_ptrs
+ step
; i
!= guard
; i
+= step
) {
744 KASSERT(i
< RADIX_TREE_PTR_PER_NODE
);
745 if (entry_match_p(n
->n_ptrs
[i
], tagmask
)) {
753 * not found. go to parent.
756 vpp
= path_pptr(t
, path
, lastidx
);
761 * following the left-most (or right-most in the case of
762 * reverse scan) child node, decend until reaching the leaf or
763 * an non-matching entry.
765 while (entry_match_p(*vpp
, tagmask
) && lastidx
< t
->t_height
) {
767 * save vpp in the path so that we can come back to this
768 * node after finishing visiting children.
770 path
->p_refs
[lastidx
].pptr
= vpp
;
772 vpp
= &n
->n_ptrs
[first
];
780 * radix_tree_gang_lookup_node:
782 * search nodes starting from idx in the ascending order.
783 * results should be an array large enough to hold maxresults pointers.
784 * returns the number of nodes found, up to maxresults.
785 * returning less than maxresults means there are no more nodes.
787 * the result of this function is semantically equivalent to what could be
788 * obtained by repeated calls of radix_tree_lookup_node with increasing index.
789 * but this function is much faster when node indexes are distributed sparsely.
791 * note that this function doesn't return exact values of node indexes of
792 * found nodes. if they are important for a caller, it's the caller's
793 * responsibility to check them, typically by examinining the returned nodes
794 * using some caller-specific knowledge about them.
798 radix_tree_gang_lookup_node(struct radix_tree
*t
, uint64_t idx
,
799 void **results
, unsigned int maxresults
)
801 struct radix_tree_path path
;
803 gang_lookup_init(t
, idx
, &path
, 0);
804 return gang_lookup_scan(t
, &path
, results
, maxresults
, 0, false);
808 * radix_tree_gang_lookup_node_reverse:
810 * same as radix_tree_gang_lookup_node except that this one scans the
811 * tree in the reverse order. ie. descending index values.
815 radix_tree_gang_lookup_node_reverse(struct radix_tree
*t
, uint64_t idx
,
816 void **results
, unsigned int maxresults
)
818 struct radix_tree_path path
;
820 gang_lookup_init(t
, idx
, &path
, 0);
821 return gang_lookup_scan(t
, &path
, results
, maxresults
, 0, true);
825 * radix_tree_gang_lookup_tagged_node:
827 * same as radix_tree_gang_lookup_node except that this one only returns
828 * nodes tagged with tagid.
832 radix_tree_gang_lookup_tagged_node(struct radix_tree
*t
, uint64_t idx
,
833 void **results
, unsigned int maxresults
, radix_tree_tagid_t tagid
)
835 struct radix_tree_path path
;
836 const unsigned int tagmask
= tagid_to_mask(tagid
);
838 gang_lookup_init(t
, idx
, &path
, tagmask
);
839 return gang_lookup_scan(t
, &path
, results
, maxresults
, tagmask
, false);
843 * radix_tree_gang_lookup_tagged_node_reverse:
845 * same as radix_tree_gang_lookup_tagged_node except that this one scans the
846 * tree in the reverse order. ie. descending index values.
850 radix_tree_gang_lookup_tagged_node_reverse(struct radix_tree
*t
, uint64_t idx
,
851 void **results
, unsigned int maxresults
, radix_tree_tagid_t tagid
)
853 struct radix_tree_path path
;
854 const unsigned int tagmask
= tagid_to_mask(tagid
);
856 gang_lookup_init(t
, idx
, &path
, tagmask
);
857 return gang_lookup_scan(t
, &path
, results
, maxresults
, tagmask
, true);
861 * radix_tree_get_tag:
863 * return if the tag is set for the node at the given index. (true if set)
864 * it's illegal to call this function for a node which has not been inserted.
868 radix_tree_get_tag(struct radix_tree
*t
, uint64_t idx
,
869 radix_tree_tagid_t tagid
)
872 const unsigned int tagmask
= tagid_to_mask(tagid
);
875 vpp
= radix_tree_lookup_ptr(t
, idx
, NULL
, false, tagmask
);
879 KASSERT(*vpp
!= NULL
);
880 return (entry_tagmask(*vpp
) & tagmask
) != 0;
882 const unsigned int tagmask
= tagid_to_mask(tagid
);
885 vpp
= radix_tree_lookup_ptr(t
, idx
, NULL
, false, 0);
886 KASSERT(vpp
!= NULL
);
887 return (entry_tagmask(*vpp
) & tagmask
) != 0;
892 * radix_tree_set_tag:
894 * set the tag for the node at the given index.
895 * it's illegal to call this function for a node which has not been inserted.
899 radix_tree_set_tag(struct radix_tree
*t
, uint64_t idx
,
900 radix_tree_tagid_t tagid
)
902 struct radix_tree_path path
;
903 const unsigned int tagmask
= tagid_to_mask(tagid
);
907 vpp
= radix_tree_lookup_ptr(t
, idx
, &path
, false, 0);
908 KASSERT(vpp
!= NULL
);
909 KASSERT(*vpp
!= NULL
);
910 KASSERT(path
.p_lastidx
== t
->t_height
);
911 KASSERT(vpp
== path_pptr(t
, &path
, path
.p_lastidx
));
912 for (i
= t
->t_height
; i
>= 0; i
--) {
913 void ** const pptr
= (void **)path_pptr(t
, &path
, i
);
916 KASSERT(pptr
!= NULL
);
918 if ((entry_tagmask(entry
) & tagmask
) != 0) {
921 *pptr
= (void *)((uintptr_t)entry
| tagmask
);
926 * radix_tree_clear_tag:
928 * clear the tag for the node at the given index.
929 * it's illegal to call this function for a node which has not been inserted.
933 radix_tree_clear_tag(struct radix_tree
*t
, uint64_t idx
,
934 radix_tree_tagid_t tagid
)
936 struct radix_tree_path path
;
937 const unsigned int tagmask
= tagid_to_mask(tagid
);
941 vpp
= radix_tree_lookup_ptr(t
, idx
, &path
, false, 0);
942 KASSERT(vpp
!= NULL
);
943 KASSERT(*vpp
!= NULL
);
944 KASSERT(path
.p_lastidx
== t
->t_height
);
945 KASSERT(vpp
== path_pptr(t
, &path
, path
.p_lastidx
));
947 * if already cleared, nothing to do
949 if ((entry_tagmask(*vpp
) & tagmask
) == 0) {
953 * clear the tag only if no children have the tag.
955 for (i
= t
->t_height
; i
>= 0; i
--) {
956 void ** const pptr
= (void **)path_pptr(t
, &path
, i
);
959 KASSERT(pptr
!= NULL
);
961 KASSERT((entry_tagmask(entry
) & tagmask
) != 0);
962 *pptr
= entry_compose(entry_ptr(entry
),
963 entry_tagmask(entry
) & ~tagmask
);
965 * check if we should proceed to process the next level.
968 struct radix_tree_node
*n
= path_node(t
, &path
, i
- 1);
970 if ((any_children_tagmask(n
) & tagmask
) != 0) {
977 #if defined(UNITTEST)
979 #include <inttypes.h>
983 radix_tree_dump_node(const struct radix_tree
*t
, void *vp
,
984 uint64_t offset
, unsigned int height
)
986 struct radix_tree_node
*n
;
989 for (i
= 0; i
< t
->t_height
- height
; i
++) {
992 if (entry_tagmask(vp
) == 0) {
993 printf("[%" PRIu64
"] %p", offset
, entry_ptr(vp
));
995 printf("[%" PRIu64
"] %p (tagmask=0x%x)", offset
, entry_ptr(vp
),
1003 assert(any_children_tagmask(n
) == entry_tagmask(vp
));
1004 printf(" (%u children)\n", n
->n_nptrs
);
1005 for (i
= 0; i
< __arraycount(n
->n_ptrs
); i
++) {
1012 radix_tree_dump_node(t
, c
,
1013 offset
+ i
* (UINT64_C(1) <<
1014 (RADIX_TREE_BITS_PER_HEIGHT
* (height
- 1))), height
- 1);
1018 void radix_tree_dump(const struct radix_tree
*);
1021 radix_tree_dump(const struct radix_tree
*t
)
1024 printf("tree %p height=%u\n", t
, t
->t_height
);
1025 radix_tree_dump_node(t
, t
->t_root
, 0, t
->t_height
);
1031 struct radix_tree s
;
1032 struct radix_tree
*t
= &s
;
1035 radix_tree_init_tree(t
);
1037 assert(radix_tree_lookup_node(t
, 0) == NULL
);
1038 assert(radix_tree_lookup_node(t
, 1000) == NULL
);
1039 assert(radix_tree_gang_lookup_node(t
, 0, results
, 3) == 0);
1040 assert(radix_tree_gang_lookup_node(t
, 1000, results
, 3) == 0);
1041 assert(radix_tree_gang_lookup_node_reverse(t
, 0, results
, 3) == 0);
1042 assert(radix_tree_gang_lookup_node_reverse(t
, 1000, results
, 3) == 0);
1043 assert(radix_tree_gang_lookup_tagged_node(t
, 0, results
, 3, 0) == 0);
1044 assert(radix_tree_gang_lookup_tagged_node(t
, 1000, results
, 3, 0) == 0);
1045 assert(radix_tree_gang_lookup_tagged_node_reverse(t
, 0, results
, 3, 0)
1047 assert(radix_tree_gang_lookup_tagged_node_reverse(t
, 1000, results
, 3,
1049 assert(radix_tree_empty_tree_p(t
));
1050 assert(radix_tree_empty_tagged_tree_p(t
, 0));
1051 assert(radix_tree_empty_tagged_tree_p(t
, 1));
1052 assert(radix_tree_insert_node(t
, 0, (void *)0xdeadbea0) == 0);
1053 assert(!radix_tree_empty_tree_p(t
));
1054 assert(radix_tree_empty_tagged_tree_p(t
, 0));
1055 assert(radix_tree_empty_tagged_tree_p(t
, 1));
1056 assert(radix_tree_lookup_node(t
, 0) == (void *)0xdeadbea0);
1057 assert(radix_tree_lookup_node(t
, 1000) == NULL
);
1058 memset(results
, 0, sizeof(results
));
1059 assert(radix_tree_gang_lookup_node(t
, 0, results
, 3) == 1);
1060 assert(results
[0] == (void *)0xdeadbea0);
1061 assert(radix_tree_gang_lookup_node(t
, 1000, results
, 3) == 0);
1062 memset(results
, 0, sizeof(results
));
1063 assert(radix_tree_gang_lookup_node_reverse(t
, 0, results
, 3) == 1);
1064 assert(results
[0] == (void *)0xdeadbea0);
1065 memset(results
, 0, sizeof(results
));
1066 assert(radix_tree_gang_lookup_node_reverse(t
, 1000, results
, 3) == 1);
1067 assert(results
[0] == (void *)0xdeadbea0);
1068 assert(radix_tree_gang_lookup_tagged_node(t
, 0, results
, 3, 0)
1070 assert(radix_tree_gang_lookup_tagged_node_reverse(t
, 0, results
, 3, 0)
1072 assert(radix_tree_insert_node(t
, 1000, (void *)0xdeadbea0) == 0);
1073 assert(radix_tree_remove_node(t
, 0) == (void *)0xdeadbea0);
1074 assert(!radix_tree_empty_tree_p(t
));
1076 assert(radix_tree_lookup_node(t
, 0) == NULL
);
1077 assert(radix_tree_lookup_node(t
, 1000) == (void *)0xdeadbea0);
1078 memset(results
, 0, sizeof(results
));
1079 assert(radix_tree_gang_lookup_node(t
, 0, results
, 3) == 1);
1080 assert(results
[0] == (void *)0xdeadbea0);
1081 memset(results
, 0, sizeof(results
));
1082 assert(radix_tree_gang_lookup_node(t
, 1000, results
, 3) == 1);
1083 assert(results
[0] == (void *)0xdeadbea0);
1084 assert(radix_tree_gang_lookup_node_reverse(t
, 0, results
, 3) == 0);
1085 memset(results
, 0, sizeof(results
));
1086 assert(radix_tree_gang_lookup_node_reverse(t
, 1000, results
, 3) == 1);
1087 assert(results
[0] == (void *)0xdeadbea0);
1088 assert(radix_tree_gang_lookup_tagged_node(t
, 0, results
, 3, 0)
1090 assert(radix_tree_gang_lookup_tagged_node_reverse(t
, 0, results
, 3, 0)
1092 assert(!radix_tree_get_tag(t
, 1000, 0));
1093 assert(!radix_tree_get_tag(t
, 1000, 1));
1094 assert(radix_tree_empty_tagged_tree_p(t
, 0));
1095 assert(radix_tree_empty_tagged_tree_p(t
, 1));
1096 radix_tree_set_tag(t
, 1000, 1);
1097 assert(!radix_tree_get_tag(t
, 1000, 0));
1098 assert(radix_tree_get_tag(t
, 1000, 1));
1099 assert(radix_tree_empty_tagged_tree_p(t
, 0));
1100 assert(!radix_tree_empty_tagged_tree_p(t
, 1));
1102 assert(radix_tree_lookup_node(t
, 1000) == (void *)0xdeadbea0);
1103 assert(radix_tree_insert_node(t
, 0, (void *)0xbea0) == 0);
1105 assert(radix_tree_lookup_node(t
, 0) == (void *)0xbea0);
1106 assert(radix_tree_lookup_node(t
, 1000) == (void *)0xdeadbea0);
1107 assert(radix_tree_insert_node(t
, UINT64_C(10000000000), (void *)0xdea0)
1110 assert(radix_tree_lookup_node(t
, 0) == (void *)0xbea0);
1111 assert(radix_tree_lookup_node(t
, 1000) == (void *)0xdeadbea0);
1112 assert(radix_tree_lookup_node(t
, UINT64_C(10000000000)) ==
1115 assert(!radix_tree_get_tag(t
, 0, 1));
1116 assert(radix_tree_get_tag(t
, 1000, 1));
1117 assert(!radix_tree_get_tag(t
, UINT64_C(10000000000), 1));
1118 radix_tree_set_tag(t
, 0, 1);;
1119 radix_tree_set_tag(t
, UINT64_C(10000000000), 1);
1121 assert(radix_tree_get_tag(t
, 0, 1));
1122 assert(radix_tree_get_tag(t
, 1000, 1));
1123 assert(radix_tree_get_tag(t
, UINT64_C(10000000000), 1));
1124 radix_tree_clear_tag(t
, 0, 1);;
1125 radix_tree_clear_tag(t
, UINT64_C(10000000000), 1);
1127 assert(!radix_tree_get_tag(t
, 0, 1));
1128 assert(radix_tree_get_tag(t
, 1000, 1));
1129 assert(!radix_tree_get_tag(t
, UINT64_C(10000000000), 1));
1131 assert(radix_tree_replace_node(t
, 1000, (void *)0x12345678) ==
1132 (void *)0xdeadbea0);
1133 assert(!radix_tree_get_tag(t
, 1000, 0));
1134 assert(radix_tree_get_tag(t
, 1000, 1));
1135 assert(radix_tree_gang_lookup_node(t
, 0, results
, 3) == 3);
1136 assert(results
[0] == (void *)0xbea0);
1137 assert(results
[1] == (void *)0x12345678);
1138 assert(results
[2] == (void *)0xdea0);
1139 assert(radix_tree_gang_lookup_node(t
, 1, results
, 3) == 2);
1140 assert(results
[0] == (void *)0x12345678);
1141 assert(results
[1] == (void *)0xdea0);
1142 assert(radix_tree_gang_lookup_node(t
, 1001, results
, 3) == 1);
1143 assert(results
[0] == (void *)0xdea0);
1144 assert(radix_tree_gang_lookup_node(t
, UINT64_C(10000000001), results
, 3)
1146 assert(radix_tree_gang_lookup_node(t
, UINT64_C(1000000000000), results
,
1148 assert(radix_tree_gang_lookup_tagged_node(t
, 0, results
, 100, 1) == 1);
1149 assert(results
[0] == (void *)0x12345678);
1150 assert(entry_tagmask(t
->t_root
) != 0);
1151 assert(radix_tree_remove_node(t
, 1000) == (void *)0x12345678);
1152 assert(entry_tagmask(t
->t_root
) == 0);
1154 assert(radix_tree_remove_node(t
, UINT64_C(10000000000)) ==
1157 assert(radix_tree_remove_node(t
, 0) == (void *)0xbea0);
1159 radix_tree_fini_tree(t
);
1162 #include <sys/time.h>
1166 bool tagged
[RADIX_TREE_TAG_ID_MAX
];
1170 printops(const char *title
, const char *name
, int tag
, unsigned int n
,
1171 const struct timeval
*stv
, const struct timeval
*etv
)
1173 uint64_t s
= stv
->tv_sec
* 1000000 + stv
->tv_usec
;
1174 uint64_t e
= etv
->tv_sec
* 1000000 + etv
->tv_usec
;
1176 printf("RESULT %s %s %d %lf op/s\n", title
, name
, tag
,
1177 (double)n
/ (e
- s
) * 1000000);
1180 #define TEST2_GANG_LOOKUP_NODES 16
1183 test2_should_tag(unsigned int i
, radix_tree_tagid_t tagid
)
1187 return (i
& 0x3) == 0; /* 25% */
1189 return (i
% 7) == 0; /* 14% */
1194 test2(const char *title
, bool dense
)
1196 struct radix_tree s
;
1197 struct radix_tree
*t
= &s
;
1200 unsigned int nnodes
= 100000;
1201 unsigned int removed
;
1202 radix_tree_tagid_t tag
;
1203 unsigned int ntagged
[RADIX_TREE_TAG_ID_MAX
];
1204 struct testnode
*nodes
;
1208 nodes
= malloc(nnodes
* sizeof(*nodes
));
1209 for (tag
= 0; tag
< RADIX_TREE_TAG_ID_MAX
; tag
++) {
1212 radix_tree_init_tree(t
);
1213 for (i
= 0; i
< nnodes
; i
++) {
1216 if (sizeof(long) == 4) {
1218 n
->idx
|= (uint32_t)random();
1221 n
->idx
%= nnodes
* 2;
1223 while (radix_tree_lookup_node(t
, n
->idx
) != NULL
) {
1226 radix_tree_insert_node(t
, n
->idx
, n
);
1227 for (tag
= 0; tag
< RADIX_TREE_TAG_ID_MAX
; tag
++) {
1228 n
->tagged
[tag
] = test2_should_tag(i
, tag
);
1229 if (n
->tagged
[tag
]) {
1230 radix_tree_set_tag(t
, n
->idx
, tag
);
1233 assert(n
->tagged
[tag
] ==
1234 radix_tree_get_tag(t
, n
->idx
, tag
));
1238 gettimeofday(&stv
, NULL
);
1239 for (i
= 0; i
< nnodes
; i
++) {
1241 assert(radix_tree_lookup_node(t
, n
->idx
) == n
);
1243 gettimeofday(&etv
, NULL
);
1244 printops(title
, "lookup", 0, nnodes
, &stv
, &etv
);
1246 for (tag
= 0; tag
< RADIX_TREE_TAG_ID_MAX
; tag
++) {
1247 unsigned int count
= 0;
1249 gettimeofday(&stv
, NULL
);
1250 for (i
= 0; i
< nnodes
; i
++) {
1254 tagged
= radix_tree_get_tag(t
, n
->idx
, tag
);
1255 assert(n
->tagged
[tag
] == tagged
);
1260 gettimeofday(&etv
, NULL
);
1261 assert(ntagged
[tag
] == count
);
1262 printops(title
, "get_tag", tag
, nnodes
, &stv
, &etv
);
1265 gettimeofday(&stv
, NULL
);
1266 for (i
= 0; i
< nnodes
; i
++) {
1268 radix_tree_remove_node(t
, n
->idx
);
1270 gettimeofday(&etv
, NULL
);
1271 printops(title
, "remove", 0, nnodes
, &stv
, &etv
);
1273 gettimeofday(&stv
, NULL
);
1274 for (i
= 0; i
< nnodes
; i
++) {
1276 radix_tree_insert_node(t
, n
->idx
, n
);
1278 gettimeofday(&etv
, NULL
);
1279 printops(title
, "insert", 0, nnodes
, &stv
, &etv
);
1281 for (tag
= 0; tag
< RADIX_TREE_TAG_ID_MAX
; tag
++) {
1283 gettimeofday(&stv
, NULL
);
1284 for (i
= 0; i
< nnodes
; i
++) {
1286 if (n
->tagged
[tag
]) {
1287 radix_tree_set_tag(t
, n
->idx
, tag
);
1291 gettimeofday(&etv
, NULL
);
1292 printops(title
, "set_tag", tag
, ntagged
[tag
], &stv
, &etv
);
1295 gettimeofday(&stv
, NULL
);
1297 struct testnode
*results
[TEST2_GANG_LOOKUP_NODES
];
1299 unsigned int nfound
;
1304 while ((nfound
= radix_tree_gang_lookup_node(t
, nextidx
,
1305 (void *)results
, __arraycount(results
))) > 0) {
1306 nextidx
= results
[nfound
- 1]->idx
+ 1;
1312 assert(total
== nnodes
);
1314 gettimeofday(&etv
, NULL
);
1315 printops(title
, "ganglookup", 0, nnodes
, &stv
, &etv
);
1317 gettimeofday(&stv
, NULL
);
1319 struct testnode
*results
[TEST2_GANG_LOOKUP_NODES
];
1321 unsigned int nfound
;
1324 nextidx
= UINT64_MAX
;
1326 while ((nfound
= radix_tree_gang_lookup_node_reverse(t
, nextidx
,
1327 (void *)results
, __arraycount(results
))) > 0) {
1328 nextidx
= results
[nfound
- 1]->idx
- 1;
1330 if (nextidx
== UINT64_MAX
) {
1334 assert(total
== nnodes
);
1336 gettimeofday(&etv
, NULL
);
1337 printops(title
, "ganglookup_reverse", 0, nnodes
, &stv
, &etv
);
1339 for (tag
= 0; tag
< RADIX_TREE_TAG_ID_MAX
; tag
++) {
1340 gettimeofday(&stv
, NULL
);
1342 struct testnode
*results
[TEST2_GANG_LOOKUP_NODES
];
1344 unsigned int nfound
;
1349 while ((nfound
= radix_tree_gang_lookup_tagged_node(t
,
1350 nextidx
, (void *)results
, __arraycount(results
),
1352 nextidx
= results
[nfound
- 1]->idx
+ 1;
1355 assert(total
== ntagged
[tag
]);
1357 gettimeofday(&etv
, NULL
);
1358 printops(title
, "ganglookup_tag", tag
, ntagged
[tag
], &stv
,
1362 for (tag
= 0; tag
< RADIX_TREE_TAG_ID_MAX
; tag
++) {
1363 gettimeofday(&stv
, NULL
);
1365 struct testnode
*results
[TEST2_GANG_LOOKUP_NODES
];
1367 unsigned int nfound
;
1370 nextidx
= UINT64_MAX
;
1373 radix_tree_gang_lookup_tagged_node_reverse(t
,
1374 nextidx
, (void *)results
, __arraycount(results
),
1376 nextidx
= results
[nfound
- 1]->idx
- 1;
1378 if (nextidx
== UINT64_MAX
) {
1382 assert(total
== ntagged
[tag
]);
1384 gettimeofday(&etv
, NULL
);
1385 printops(title
, "ganglookup_tag_reverse", tag
, ntagged
[tag
],
1390 for (tag
= 0; tag
< RADIX_TREE_TAG_ID_MAX
; tag
++) {
1394 gettimeofday(&stv
, NULL
);
1396 struct testnode
*results
[TEST2_GANG_LOOKUP_NODES
];
1398 unsigned int nfound
;
1401 while ((nfound
= radix_tree_gang_lookup_tagged_node(t
,
1402 nextidx
, (void *)results
, __arraycount(results
),
1404 for (i
= 0; i
< nfound
; i
++) {
1405 radix_tree_remove_node(t
,
1408 nextidx
= results
[nfound
- 1]->idx
+ 1;
1414 assert(tag
!= 0 || total
== ntagged
[tag
]);
1415 assert(total
<= ntagged
[tag
]);
1417 gettimeofday(&etv
, NULL
);
1418 printops(title
, "ganglookup_tag+remove", tag
, total
, &stv
,
1423 gettimeofday(&stv
, NULL
);
1425 struct testnode
*results
[TEST2_GANG_LOOKUP_NODES
];
1427 unsigned int nfound
;
1432 while ((nfound
= radix_tree_gang_lookup_node(t
, nextidx
,
1433 (void *)results
, __arraycount(results
))) > 0) {
1434 for (i
= 0; i
< nfound
; i
++) {
1435 assert(results
[i
] == radix_tree_remove_node(t
,
1438 nextidx
= results
[nfound
- 1]->idx
+ 1;
1444 assert(total
== nnodes
- removed
);
1446 gettimeofday(&etv
, NULL
);
1447 printops(title
, "ganglookup+remove", 0, nnodes
- removed
, &stv
, &etv
);
1449 assert(radix_tree_empty_tree_p(t
));
1450 assert(radix_tree_empty_tagged_tree_p(t
, 0));
1451 assert(radix_tree_empty_tagged_tree_p(t
, 1));
1452 radix_tree_fini_tree(t
);
1457 main(int argc
, char *argv
[])
1461 test2("dense", true);
1462 test2("sparse", false);
1466 #endif /* defined(UNITTEST) */