]> wimlib.net Git - wimlib/blob - include/wimlib/avl_tree.h
avl_tree: Remove dependency on compiler.h
[wimlib] / include / wimlib / avl_tree.h
1 /*
2  * avl_tree.h
3  *
4  * Intrusive, nonrecursive AVL tree data structure (self-balancing binary search
5  * tree), header file.
6  *
7  * Author:  Eric Biggers
8  * Year:    2014
9  *
10  * This file is placed into the public domain.  You can do whatever you want
11  * with it.
12  */
13
14 #ifndef _AVL_TREE_H_
15 #define _AVL_TREE_H_
16
17 #include <stdbool.h>
18 #include <stddef.h>
19 #include <inttypes.h> /* for uintptr_t */
20
21 #ifdef __GNUC__
22 #  define AVL_INLINE inline __attribute__((always_inline))
23 #else
24 #  define AVL_INLINE inline
25 #endif
26
27 /* Node in an AVL tree.  Embed this in some other data structure.  */
28 struct avl_tree_node {
29
30         /* Pointer to left child or NULL  */
31         struct avl_tree_node *left;
32
33         /* Pointer to right child or NULL  */
34         struct avl_tree_node *right;
35
36         /* Pointer to parent combined with the balance factor.  This saves 4 or
37          * 8 bytes of memory depending on the CPU architecture.
38          *
39          * Low 2 bits:  One greater than the balance factor of this subtree,
40          * which is equal to height(right) - height(left).  The mapping is:
41          *
42          * 00 => -1
43          * 01 =>  0
44          * 10 => +1
45          * 11 => undefined
46          *
47          * The rest of the bits are the pointer to the parent node.  It must be
48          * 4-byte aligned, and it will be NULL if this is the root note and
49          * therefore has no parent.  */
50         uintptr_t parent_balance;
51 };
52
53 /* Cast an AVL tree node to the containing data structure.  */
54 #define avl_tree_entry(entry, type, member) \
55         ((type*) ((char *)(entry) - offsetof(type, member)))
56
57 /* Extracts the parent pointer from the specified AVL tree node.
58  * Returns the parent pointer, or NULL if @node is the root node.  */
59 static AVL_INLINE struct avl_tree_node *
60 avl_get_parent(const struct avl_tree_node *node)
61 {
62         return (struct avl_tree_node *)(node->parent_balance & ~3);
63 }
64
65 /* Mark the node as unlinked from any tree.  */
66 static AVL_INLINE void
67 avl_tree_node_set_unlinked(struct avl_tree_node *node)
68 {
69         node->parent_balance = (uintptr_t)node;
70 }
71
72 /* Returns true iff the specified node has been marked with
73  * avl_tree_node_set_unlinked() and has not subsequently been inserted into a
74  * tree.  */
75 static AVL_INLINE bool
76 avl_tree_node_is_unlinked(const struct avl_tree_node *node)
77 {
78         return node->parent_balance == (uintptr_t)node;
79 }
80
81 /* Internal use only */
82 extern void
83 avl_tree_rebalance_after_insert(struct avl_tree_node **root_ptr,
84                                 struct avl_tree_node *inserted);
85
86 /*
87  * Look up an item in an AVL tree.
88  *
89  * @root
90  *      Pointer to the root of the AVL tree.  (This can be NULL --- that just
91  *      means the tree is empty.)
92  *
93  * @cmp_ctx
94  *      First argument to pass to the comparison callback --- generally a
95  *      pointer to an object equal to the one being searched for.
96  *
97  * @cmp
98  *      Comparison callback.  Must return < 0, 0, or > 0 if the first argument
99  *      is less than, equal to, or greater than the second argument,
100  *      respectively.  The first argument will be @cmp_ctx and the second
101  *      argument will be a pointer to the AVL tree node contained in the item
102  *      inserted into the AVL tree.
103  *
104  * Returns a pointer to the AVL tree node embedded in the resulting item, or
105  * NULL if the item was not found.
106  */
107 static AVL_INLINE struct avl_tree_node *
108 avl_tree_lookup(const struct avl_tree_node *root,
109                 const void *cmp_ctx,
110                 int (*cmp)(const void *, const struct avl_tree_node *))
111 {
112         const struct avl_tree_node *cur = root;
113
114         while (cur) {
115                 int res = (*cmp)(cmp_ctx, cur);
116                 if (res < 0)
117                         cur = cur->left;
118                 else if (res > 0)
119                         cur = cur->right;
120                 else
121                         break;
122         }
123         return (struct avl_tree_node*)cur;
124 }
125
126 /* Same as avl_tree_lookup(), just uses a more specific type for the comparison
127  * function.  Specifically, the item being searched for is expected to be in the
128  * same format as those already in the tree, with an embedded 'struct
129  * avl_tree_node'.  */
130 static AVL_INLINE struct avl_tree_node *
131 avl_tree_lookup_node(const struct avl_tree_node *root,
132                      const struct avl_tree_node *node,
133                      int (*cmp)(const struct avl_tree_node *,
134                                 const struct avl_tree_node *))
135 {
136         return avl_tree_lookup(root,
137                                (const void *)node,
138                                (int (*) (const void *,
139                                          const struct avl_tree_node *))cmp);
140 }
141
142 /*
143  * Insert an item into an AVL tree.
144  *
145  * @root_ptr
146  *      Pointer to the pointer to the root of the AVL tree.  (Indirection is
147  *      needed because the root node may change.)  Initialize *root_ptr to NULL
148  *      for an empty tree.
149  *
150  * @item
151  *      Pointer to the `struct avl_tree_node' embedded in the item to insert.
152  *      No members in it need be pre-initialized, although members in the
153  *      containing structure should be pre-initialized so that @cmp can use them
154  *      in comparisons.
155  *
156  * @cmp
157  *      Comparison callback.  Must return < 0, 0, or > 0 if the first argument
158  *      is less than, equal to, or greater than the second argument,
159  *      respectively.  The first argument will be @item and the second
160  *      argument will be a pointer to an AVL tree node embedded in some
161  *      previously-inserted item that @item is being compared with.
162  *
163  * Returns NULL if the item was successfully inserted, otherwise the node of a
164  * previously-inserted item which compared equal to @item and prevented the new
165  * insertion of @item.
166  */
167 static AVL_INLINE struct avl_tree_node *
168 avl_tree_insert(struct avl_tree_node **root_ptr,
169                 struct avl_tree_node *item,
170                 int (*cmp)(const struct avl_tree_node *,
171                            const struct avl_tree_node *))
172 {
173         struct avl_tree_node **cur_ptr = root_ptr, *cur = NULL;
174         int res;
175
176         while (*cur_ptr) {
177                 cur = *cur_ptr;
178                 res = (*cmp)(item, cur);
179                 if (res < 0)
180                         cur_ptr = &cur->left;
181                 else if (res > 0)
182                         cur_ptr = &cur->right;
183                 else
184                         return cur;
185         }
186         *cur_ptr = item;
187         item->parent_balance = (uintptr_t)cur | 1;
188         avl_tree_rebalance_after_insert(root_ptr, item);
189         return NULL;
190 }
191
192 extern void
193 avl_tree_remove(struct avl_tree_node **root_ptr, struct avl_tree_node *node);
194
195 /* Nonrecursive AVL tree traversal functions  */
196
197 extern struct avl_tree_node *
198 avl_tree_first_in_order(const struct avl_tree_node *root);
199
200 extern struct avl_tree_node *
201 avl_tree_next_in_order(const struct avl_tree_node *prev);
202
203 extern struct avl_tree_node *
204 avl_tree_first_in_postorder(const struct avl_tree_node *root);
205
206 extern struct avl_tree_node *
207 avl_tree_next_in_postorder(const struct avl_tree_node *prev,
208                            const struct avl_tree_node *prev_parent);
209
210 #endif /* _AVL_TREE_H_ */