1 //===-- Interface for freetrie --------------------------------------------===//
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
7 //===----------------------------------------------------------------------===//
9 #ifndef LLVM_LIBC_SRC___SUPPORT_FREETRIE_H
10 #define LLVM_LIBC_SRC___SUPPORT_FREETRIE_H
14 namespace LIBC_NAMESPACE_DECL
{
16 /// A trie of free lists.
18 /// This is an unusual little data structure originally from Doug Lea's malloc.
19 /// Finding the best fit from a set of differently-sized free list typically
20 /// required some kind of ordered map, and these are typically implemented using
21 /// a self-balancing binary search tree. Those are notorious for having a
22 /// relatively large number of special cases, while this trie has relatively
23 /// few, which helps with code size.
25 /// Operations on the trie are logarithmic not on the number of nodes within it,
26 /// but rather the fixed range of possible sizes that the trie can contain. This
27 /// means that the data structure would likely actually perform worse than an
28 /// e.g. red-black tree, but its implementation is still much simpler.
30 /// Each trie node's children subdivide the range of possible sizes into two
31 /// halves: a lower and an upper. The node itself holds a free list of some size
32 /// within its range. This makes it possible to summarily replace any node with
33 /// any leaf within its subtrie, which makes it very straightforward to remove a
34 /// node. Insertion is also simple; the only real complexity lies with finding
35 /// the best fit. This can still be done in logarithmic time with only a few
36 /// cases to consider.
38 /// The trie refers to, but does not own, the Nodes that comprise it.
41 /// A trie node that is also a free list. Only the head node of each list is
42 /// actually part of the trie. The subtrie contains a continous SizeRange of
43 /// free lists. The lower and upper subtrie's contain the lower and upper half
44 /// of the subtries range. There is no direct relationship between the size of
45 /// this node's free list and the contents of the lower and upper subtries.
46 class Node
: public FreeList::Node
{
47 /// The child subtrie covering the lower half of this subtrie's size range.
48 /// Undefined if this is not the head of the list.
50 /// The child subtrie covering the upper half of this subtrie's size range.
51 /// Undefined if this is not the head of the list.
53 /// The parent subtrie. nullptr if this is the root or not the head of the
57 friend class FreeTrie
;
60 /// Power-of-two range of sizes covered by a subtrie.
65 LIBC_INLINE
constexpr SizeRange(size_t min
, size_t width
)
66 : min(min
), width(width
) {
67 LIBC_ASSERT(!(width
& (width
- 1)) && "width must be a power of two");
70 /// @returns The lower half of the size range.
71 LIBC_INLINE SizeRange
lower() const { return {min
, width
/ 2}; }
73 /// @returns The upper half of the size range.
74 LIBC_INLINE SizeRange
upper() const { return {min
+ width
/ 2, width
/ 2}; }
76 /// @returns The largest size in this range.
77 LIBC_INLINE
size_t max() const { return min
+ (width
- 1); }
79 /// @returns Whether the range contains the given size.
80 LIBC_INLINE
bool contains(size_t size
) const {
81 return min
<= size
&& size
< min
+ width
;
85 LIBC_INLINE
constexpr FreeTrie() : FreeTrie(SizeRange
{0, 0}) {}
86 LIBC_INLINE
constexpr FreeTrie(SizeRange range
) : range(range
) {}
88 /// Sets the range of possible block sizes. This can only be called when the
90 LIBC_INLINE
void set_range(FreeTrie::SizeRange range
) {
91 LIBC_ASSERT(empty() && "cannot change the range of a preexisting trie");
95 /// @returns Whether the trie contains any blocks.
96 LIBC_INLINE
bool empty() const { return !root
; }
98 /// Push a block to the trie.
99 void push(Block
*block
);
101 /// Remove a node from this trie node's free list.
102 void remove(Node
*node
);
104 /// @returns A smallest node that can allocate the given size; otherwise
106 Node
*find_best_fit(size_t size
);
109 /// @returns Whether a node is the head of its containing freelist.
110 bool is_head(Node
*node
) const { return node
->parent
|| node
== root
; }
112 /// Replaces references to one node with another (or nullptr) in all adjacent
113 /// parent and child nodes.
114 void replace_node(Node
*node
, Node
*new_node
);
116 Node
*root
= nullptr;
120 LIBC_INLINE
void FreeTrie::push(Block
*block
) {
121 LIBC_ASSERT(block
->inner_size_free() >= sizeof(Node
) &&
122 "block too small to accomodate free trie node");
123 size_t size
= block
->inner_size();
124 LIBC_ASSERT(range
.contains(size
) && "requested size out of trie range");
126 // Find the position in the tree to push to.
128 Node
*parent
= nullptr;
129 SizeRange cur_range
= range
;
130 while (*cur
&& (*cur
)->size() != size
) {
131 LIBC_ASSERT(cur_range
.contains(size
) && "requested size out of trie range");
133 if (size
<= cur_range
.lower().max()) {
134 cur
= &(*cur
)->lower
;
135 cur_range
= cur_range
.lower();
137 cur
= &(*cur
)->upper
;
138 cur_range
= cur_range
.upper();
142 Node
*node
= new (block
->usable_space()) Node
;
143 FreeList list
= *cur
;
145 node
->parent
= parent
;
146 node
->lower
= node
->upper
= nullptr;
148 node
->parent
= nullptr;
151 *cur
= static_cast<Node
*>(list
.begin());
154 LIBC_INLINE
FreeTrie::Node
*FreeTrie::find_best_fit(size_t size
) {
155 if (empty() || range
.max() < size
)
159 SizeRange cur_range
= range
;
160 Node
*best_fit
= nullptr;
161 Node
*deferred_upper_trie
= nullptr;
162 FreeTrie::SizeRange deferred_upper_range
{0, 0};
165 LIBC_ASSERT(cur_range
.contains(cur
->size()) &&
166 "trie node size out of range");
167 LIBC_ASSERT(cur_range
.max() >= size
&&
168 "range could not fit requested size");
169 LIBC_ASSERT((!best_fit
|| cur_range
.min
< best_fit
->size()) &&
170 "range could not contain a best fit");
172 // If the current node is an exact fit, it is a best fit.
173 if (cur
->size() == size
)
176 if (cur
->size() > size
&& (!best_fit
|| cur
->size() < best_fit
->size())) {
177 // The current node is a better fit.
180 // If there is a deferred upper subtrie, then the current node is
181 // somewhere in its lower sibling subtrie. That means that the new best
182 // fit is better than the best fit in the deferred subtrie.
184 (!deferred_upper_trie
||
185 deferred_upper_range
.min
> best_fit
->size()) &&
186 "deferred upper subtrie should be outclassed by new best fit");
187 deferred_upper_trie
= nullptr;
190 // Determine which subtries might contain the best fit.
191 bool lower_impossible
= !cur
->lower
|| cur_range
.lower().max() < size
;
192 bool upper_impossible
=
194 // If every node in the lower trie fits
195 (!lower_impossible
&& cur_range
.min
>= size
) ||
196 // If every node in the upper trie is worse than the current best
197 (best_fit
&& cur_range
.upper().min
>= best_fit
->size());
199 if (lower_impossible
&& upper_impossible
) {
200 if (!deferred_upper_trie
)
202 // Scan the deferred upper subtrie and consider whether any element within
203 // provides a better fit.
205 // This can only ever be reached once. In a deferred upper subtrie, every
206 // node fits, so the higher of two subtries can never contain a best fit.
207 cur
= deferred_upper_trie
;
208 cur_range
= deferred_upper_range
;
209 deferred_upper_trie
= nullptr;
213 if (lower_impossible
) {
215 cur_range
= cur_range
.upper();
216 } else if (upper_impossible
) {
218 cur_range
= cur_range
.lower();
220 // Both subtries might contain a better fit. Any fit in the lower subtrie
221 // is better than the any fit in the upper subtrie, so scan the lower
222 // and return to the upper only if no better fits were found. (Any better
223 // fit found clears the deferred upper subtrie.)
224 LIBC_ASSERT((!deferred_upper_trie
||
225 cur_range
.upper().max() < deferred_upper_range
.min
) &&
226 "old deferred upper subtrie should be outclassed by new");
227 deferred_upper_trie
= cur
->upper
;
228 deferred_upper_range
= cur_range
.upper();
230 cur_range
= cur_range
.lower();
235 } // namespace LIBC_NAMESPACE_DECL
237 #endif // LLVM_LIBC_SRC___SUPPORT_FREETRIE_H