1 | //===-- Interface for freetrie --------------------------------------------===// |
2 | // |
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 |
6 | // |
7 | //===----------------------------------------------------------------------===// |
8 | |
9 | #ifndef LLVM_LIBC_SRC___SUPPORT_FREETRIE_H |
10 | #define LLVM_LIBC_SRC___SUPPORT_FREETRIE_H |
11 | |
12 | #include "freelist.h" |
13 | |
14 | namespace LIBC_NAMESPACE_DECL { |
15 | |
16 | /// A trie of free lists. |
17 | /// |
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. |
24 | /// |
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. |
29 | /// |
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. |
37 | /// |
38 | /// The trie refers to, but does not own, the Nodes that comprise it. |
39 | class FreeTrie { |
40 | public: |
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. |
49 | Node *lower; |
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. |
52 | Node *upper; |
53 | /// The parent subtrie. nullptr if this is the root or not the head of the |
54 | /// list. |
55 | Node *parent; |
56 | |
57 | friend class FreeTrie; |
58 | }; |
59 | |
60 | /// Power-of-two range of sizes covered by a subtrie. |
61 | struct SizeRange { |
62 | size_t min; |
63 | size_t width; |
64 | |
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" ); |
68 | } |
69 | |
70 | /// @returns The lower half of the size range. |
71 | LIBC_INLINE SizeRange lower() const { return {min, width / 2}; } |
72 | |
73 | /// @returns The upper half of the size range. |
74 | LIBC_INLINE SizeRange upper() const { return {min + width / 2, width / 2}; } |
75 | |
76 | /// @returns The largest size in this range. |
77 | LIBC_INLINE size_t max() const { return min + (width - 1); } |
78 | |
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; |
82 | } |
83 | }; |
84 | |
85 | LIBC_INLINE constexpr FreeTrie() : FreeTrie(SizeRange{0, 0}) {} |
86 | LIBC_INLINE constexpr FreeTrie(SizeRange range) : range(range) {} |
87 | |
88 | /// Sets the range of possible block sizes. This can only be called when the |
89 | /// trie is empty. |
90 | LIBC_INLINE void set_range(FreeTrie::SizeRange range) { |
91 | LIBC_ASSERT(empty() && "cannot change the range of a preexisting trie" ); |
92 | this->range = range; |
93 | } |
94 | |
95 | /// @returns Whether the trie contains any blocks. |
96 | LIBC_INLINE bool empty() const { return !root; } |
97 | |
98 | /// Push a block to the trie. |
99 | void push(Block *block); |
100 | |
101 | /// Remove a node from this trie node's free list. |
102 | void remove(Node *node); |
103 | |
104 | /// @returns A smallest node that can allocate the given size; otherwise |
105 | /// nullptr. |
106 | Node *find_best_fit(size_t size); |
107 | |
108 | private: |
109 | /// @returns Whether a node is the head of its containing freelist. |
110 | bool is_head(Node *node) const { return node->parent || node == root; } |
111 | |
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); |
115 | |
116 | Node *root = nullptr; |
117 | SizeRange range; |
118 | }; |
119 | |
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" ); |
125 | |
126 | // Find the position in the tree to push to. |
127 | Node **cur = &root; |
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" ); |
132 | parent = *cur; |
133 | if (size <= cur_range.lower().max()) { |
134 | cur = &(*cur)->lower; |
135 | cur_range = cur_range.lower(); |
136 | } else { |
137 | cur = &(*cur)->upper; |
138 | cur_range = cur_range.upper(); |
139 | } |
140 | } |
141 | |
142 | Node *node = new (block->usable_space()) Node; |
143 | FreeList list = *cur; |
144 | if (list.empty()) { |
145 | node->parent = parent; |
146 | node->lower = node->upper = nullptr; |
147 | } else { |
148 | node->parent = nullptr; |
149 | } |
150 | list.push(node); |
151 | *cur = static_cast<Node *>(list.begin()); |
152 | } |
153 | |
154 | LIBC_INLINE FreeTrie::Node *FreeTrie::find_best_fit(size_t size) { |
155 | if (empty() || range.max() < size) |
156 | return nullptr; |
157 | |
158 | Node *cur = root; |
159 | SizeRange cur_range = range; |
160 | Node *best_fit = nullptr; |
161 | Node *deferred_upper_trie = nullptr; |
162 | FreeTrie::SizeRange deferred_upper_range{0, 0}; |
163 | |
164 | while (true) { |
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" ); |
171 | |
172 | // If the current node is an exact fit, it is a best fit. |
173 | if (cur->size() == size) |
174 | return cur; |
175 | |
176 | if (cur->size() > size && (!best_fit || cur->size() < best_fit->size())) { |
177 | // The current node is a better fit. |
178 | best_fit = cur; |
179 | |
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. |
183 | LIBC_ASSERT( |
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; |
188 | } |
189 | |
190 | // Determine which subtries might contain the best fit. |
191 | bool lower_impossible = !cur->lower || cur_range.lower().max() < size; |
192 | bool upper_impossible = |
193 | !cur->upper || |
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()); |
198 | |
199 | if (lower_impossible && upper_impossible) { |
200 | if (!deferred_upper_trie) |
201 | return best_fit; |
202 | // Scan the deferred upper subtrie and consider whether any element within |
203 | // provides a better fit. |
204 | // |
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; |
210 | continue; |
211 | } |
212 | |
213 | if (lower_impossible) { |
214 | cur = cur->upper; |
215 | cur_range = cur_range.upper(); |
216 | } else if (upper_impossible) { |
217 | cur = cur->lower; |
218 | cur_range = cur_range.lower(); |
219 | } else { |
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(); |
229 | cur = cur->lower; |
230 | cur_range = cur_range.lower(); |
231 | } |
232 | } |
233 | } |
234 | |
235 | } // namespace LIBC_NAMESPACE_DECL |
236 | |
237 | #endif // LLVM_LIBC_SRC___SUPPORT_FREETRIE_H |
238 | |