1 // Copyright 2024 The Pigweed Authors
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License"); you may not
4 // use this file except in compliance with the License. You may obtain a copy of
5 // the License at
6 //
7 // https://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
11 // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
12 // License for the specific language governing permissions and limitations under
13 // the License.
14 #pragma once
15
16 #include <cstddef>
17 #include <cstdint>
18 #include <functional>
19 #include <utility>
20
21 #include "pw_assert/assert.h"
22 #include "pw_containers/internal/aa_tree_item.h"
23 #include "pw_containers/internal/aa_tree_iterator.h"
24 #include "pw_containers/internal/intrusive_item.h"
25 #include "pw_function/function.h"
26
27 namespace pw::containers::internal {
28
29 /// Base type for an AA tree that is devoid of template parameters.
30 ///
31 /// This generic class does not implement any functionality that requires
32 /// comparing keys, and should not be used directly. Instead, see the `AATree`
33 /// class that is templated on methods to get and compare keys.
34 class GenericAATree {
35 public:
36 using iterator = AATreeIterator<>;
37
38 /// Constructs an empty AA tree.
39 ///
40 /// @param unique_keys Indicates if this tree requires unique keys or
41 /// allows duplicate keys.
GenericAATree(bool unique_keys)42 constexpr explicit GenericAATree(bool unique_keys)
43 : unique_keys_(unique_keys) {}
44
45 /// Destructor.
~GenericAATree()46 ~GenericAATree() { CheckIntrusiveContainerIsEmpty(empty()); }
47
48 // Intrusive trees cannot be copied, since each Item can only be in one tree.
49 GenericAATree(const GenericAATree&) = delete;
50 GenericAATree& operator=(const GenericAATree&) = delete;
51
unique_keys()52 [[nodiscard]] constexpr bool unique_keys() const { return unique_keys_; }
53
54 /// Sets the tree's root item.
55 void SetRoot(AATreeItem* item);
56
57 // Iterators
58
59 /// Returns a pointer to the first item, if any.
begin()60 constexpr iterator begin() noexcept {
61 return empty() ? iterator(&root_) : iterator(&root_, root_->GetLeftmost());
62 }
63
64 /// Returns a pointer to the last item, if any.
end()65 constexpr iterator end() noexcept {
66 return empty() ? iterator(&root_)
67 : ++(iterator(&root_, root_->GetRightmost()));
68 }
69
70 // Capacity
71
72 /// Returns whether the tree has zero items or not.
empty()73 [[nodiscard]] constexpr bool empty() const { return root_ == nullptr; }
74
75 /// Returns the number of items in the tree.
76 size_t size() const;
77
78 /// Returns how many items can be added.
79 ///
80 /// As an intrusive container, this is effectively unbounded.
max_size()81 constexpr size_t max_size() const noexcept {
82 return std::numeric_limits<ptrdiff_t>::max();
83 }
84
85 // Modifiers
86
87 /// Removes all items from the tree and leaves it empty.
88 ///
89 /// The items themselves are not destructed.
90 void clear();
91
92 /// Removes an item from the tree and returns an iterator to the item after
93 /// the removed item.
94 ///
95 /// The items themselves are not destructed.
96 iterator erase_one(AATreeItem& item);
97
98 /// Removes the items from first, inclusive, to last, exclusive from the tree
99 /// and returns an iterator to the item after the last removed item.
100 ///
101 /// The items themselves are not destructed.
102 iterator erase_range(AATreeItem& first, AATreeItem& last);
103
104 /// Exchanges this tree's items with the `other` tree's items.
105 void swap(GenericAATree& other);
106
107 private:
108 // The derived, templated type needs to be able to access the root element.
109 template <typename, typename>
110 friend class AATree;
111
112 /// Root of the tree. Only null if the tree is empty.
113 AATreeItem* root_ = nullptr;
114
115 /// Indicates whether the tree requires unique keys.
116 ///
117 /// This is a const member rather than a template parameter for 3 reasons:
118 /// 1. It is a tree field and not an item field, meaning the space overhead is
119 /// marginal.
120 /// 2. It is only used in a single branch, i.e. when inserting an item with a
121 /// duplicate key. Making this branch constexpr-if would only save a
122 /// minimal amount of code size.
123 /// 3. It allows the same types of ``AATree<K,C>``, ``AATree<K,C>::Item`` and
124 /// ``iterator<TT,T>`` to be used for both ``IntrusiveMap`` and
125 /// ``IntrusiveMultiMap``, thereby reducing code size more significantly.
126 const bool unique_keys_;
127 };
128
129 /// An AA tree, as described by Arne Andersson in
130 /// https://user.it.uu.se/~arneande/ps/simp.pdf. AA trees are simplified
131 /// red-black which offer almost as much performance with much simpler and
132 /// smaller code.
133 ///
134 /// The tree provides an ordered collection of keyed items, and is used to
135 /// implement ``pw::IntrusiveMap`` and ``pw::IntrusiveMultiMap``. Keys are
136 /// retrieved and compared using the functions provided via template parameters.
137 ///
138 /// @tparam Compare Function with the signature `bool(Key, Key) that is
139 /// used to order items.
140 template <typename K, typename V>
141 class AATree final : public GenericAATree {
142 public:
143 using Key = K;
144 using Compare = Function<bool(Key, Key)>;
145 using GetKey = Function<Key(const V&)>;
146 using GenericAATree::iterator;
147
148 /// Base item type for intrusive items stored in trees.
149 ///
150 /// Unlike `IntrusiveForwardList` and `IntrusiveList`, which define distinct
151 /// nested types for their items, maps and sets share the same base item type.
152 class Item : public AATreeItem {
153 public:
154 constexpr explicit Item() = default;
155
156 private:
157 // IntrusiveItem is used to ensure T inherits from this container's Item
158 // type. See also `CheckItemType`.
159 template <typename, typename, bool>
160 friend struct IntrusiveItem;
161 using ItemType = V;
162 };
163
164 /// An extension of `Item` that includes storage for a key.
165 class Pair : public Item {
166 public:
Pair(Key key)167 constexpr explicit Pair(Key key) : key_(key) {}
key()168 constexpr Key key() const { return key_; }
169
170 constexpr bool operator<(const Pair& rhs) { return key_ < rhs.key(); }
171
172 private:
173 const Key key_;
174 };
175
176 /// Constructs an empty AA tree.
177 ///
178 /// @param unique_keys Indicates if this tree requires unique keys or
179 /// allows duplicate keys.
AATree(bool unique_keys,Compare && compare,GetKey && get_key)180 constexpr AATree(bool unique_keys, Compare&& compare, GetKey&& get_key)
181 : GenericAATree(unique_keys),
182 compare_(std::move(compare)),
183 get_key_(std::move(get_key)) {}
184
185 // Modifiers
186
187 /// Attempts to add the given item to the tree.
188 ///
189 /// The item will be added if the tree does not already contain an item with
190 /// the given item's key or if the tree does not require unique keys.
191 ///
192 /// @pre The item must not be a part of any tree.
193 ///
194 /// @returns A pointer to the inserted item and `true`, or a pointer to the
195 /// existing item with same key and `false`.
196 std::pair<iterator, bool> insert(AATreeItem& item);
197
198 /// Inserts each item from `first`, inclusive, to `last`, exclusive. If the
199 /// tree does not all duplicates and an equivalent item is already in the
200 /// tree, the item is ignored.
201 template <class Iterator>
202 void insert(Iterator first, Iterator last);
203
204 using GenericAATree::erase_one;
205 using GenericAATree::erase_range;
206
207 /// Removes items that match the given `key`, and returns an iterator to
208 /// the item after the removed items and the number of items removed.
209 size_t erase_all(Key key);
210
211 /// Splices items from the `other` tree into this one.
212 ///
213 /// The receiving tree's `Compare` and `GetKey` functions are used when
214 /// inserting items.
215 void merge(AATree<K, V>& other);
216
217 // Lookup
218
219 /// Returns the number of items in the tree with the given key.
220 ///
221 /// If the tree requires unique keys, this simply 0 or 1.
222 size_t count(Key key);
223
224 /// Returns a pointer to an item with the given key, or null if the tree does
225 /// not contain such an item.
226 iterator find(Key key);
227
228 /// Returns a pair of items where the first points to the item with the
229 /// smallest key that is not less than the given key, and the second points to
230 /// the item with the smallest key that is greater than the given key.
231 std::pair<iterator, iterator> equal_range(Key key);
232
233 /// Returns the item in the tree with the smallest key that is greater than or
234 /// equal to the given key, or null if the tree is empty.
235 iterator lower_bound(Key key);
236
237 /// Returns the item in the tree with the smallest key that is strictly
238 /// greater than the given key, or null if the tree is empty.
239 iterator upper_bound(Key key);
240
241 private:
242 /// Inserts the given `child` in the subtree rooted by `parent` and returns
243 /// the resulting tree. If the tree does not allow duplicates and an
244 /// equivalent item is already in the tree, the tree is unchanged and the
245 /// existing item is returned via `duplicate`.
246 AATreeItem* InsertImpl(AATreeItem& parent,
247 AATreeItem& child,
248 AATreeItem*& duplicate);
249
250 /// Returns the item in the subtree rooted by the given `item` with the
251 /// smallest key that is greater than or equal to the given `key`, or null if
252 /// the subtree is empty.
253 AATreeItem* GetLowerBoundImpl(AATreeItem* item, Key key);
254
255 /// Returns the item in the subtree rooted by the given `item` with the
256 /// smallest key that is strictly greater than the given `key`, or null if the
257 /// subtree is empty.
258 AATreeItem* GetUpperBoundImpl(AATreeItem* item, Key key);
259
260 /// Returns the key associated with a tree item.
DoGetKey(const AATreeItem & item)261 constexpr Key DoGetKey(const AATreeItem& item) {
262 return get_key_(static_cast<const V&>(item));
263 }
264
265 Compare compare_;
266 GetKey get_key_;
267 };
268
269 // Template method implementations.
270
271 template <typename K, typename V>
insert(AATreeItem & item)272 std::pair<GenericAATree::iterator, bool> AATree<K, V>::insert(
273 AATreeItem& item) {
274 CheckIntrusiveItemIsUncontained(!item.IsMapped());
275 item.SetLevel(1);
276 if (empty()) {
277 SetRoot(&item);
278 return std::make_pair(iterator(&root_, &item), true);
279 }
280 AATreeItem* duplicate = nullptr;
281 SetRoot(InsertImpl(*root_, item, duplicate));
282 if (duplicate == nullptr) {
283 return std::make_pair(iterator(&root_, &item), true);
284 }
285 item.Reset();
286 return std::make_pair(iterator(&root_, duplicate), false);
287 }
288
289 template <typename K, typename V>
InsertImpl(AATreeItem & parent,AATreeItem & child,AATreeItem * & duplicate)290 AATreeItem* AATree<K, V>::InsertImpl(AATreeItem& parent,
291 AATreeItem& child,
292 AATreeItem*& duplicate) {
293 if (compare_(DoGetKey(child), DoGetKey(parent))) {
294 AATreeItem* left = parent.left_.get();
295 left = left == nullptr ? &child : InsertImpl(*left, child, duplicate);
296 parent.SetLeft(left);
297
298 } else if (compare_(DoGetKey(parent), DoGetKey(child)) || !unique_keys()) {
299 AATreeItem* right = parent.right_.get();
300 right = right == nullptr ? &child : InsertImpl(*right, child, duplicate);
301 parent.SetRight(right);
302
303 } else {
304 duplicate = &parent;
305 return &parent;
306 }
307
308 return parent.Skew()->Split();
309 }
310
311 template <typename K, typename V>
312 template <class Iterator>
insert(Iterator first,Iterator last)313 void AATree<K, V>::insert(Iterator first, Iterator last) {
314 for (Iterator iter = first; iter != last; ++iter) {
315 if constexpr (std::is_pointer_v<
316 typename std::iterator_traits<Iterator>::value_type>) {
317 insert(**iter);
318 } else {
319 insert(*iter);
320 }
321 }
322 }
323
324 template <typename K, typename V>
erase_all(Key key)325 size_t AATree<K, V>::erase_all(Key key) {
326 size_t removed = 0;
327 iterator iter = lower_bound(key);
328 while (iter != end() && !compare_(key, DoGetKey(*iter))) {
329 iter = erase_one(*iter);
330 ++removed;
331 }
332 return removed;
333 }
334
335 template <typename K, typename V>
merge(AATree<K,V> & other)336 void AATree<K, V>::merge(AATree<K, V>& other) {
337 auto iter = other.begin();
338 while (!other.empty()) {
339 AATreeItem& item = *iter;
340 iter = other.erase_one(item);
341 insert(item);
342 }
343 }
344
345 template <typename K, typename V>
count(Key key)346 size_t AATree<K, V>::count(Key key) {
347 return std::distance(lower_bound(key), upper_bound(key));
348 }
349
350 template <typename K, typename V>
find(Key key)351 GenericAATree::iterator AATree<K, V>::find(Key key) {
352 iterator iter = lower_bound(key);
353 if (iter == end() || compare_(key, DoGetKey(*iter))) {
354 return end();
355 }
356 return iter;
357 }
358
359 template <typename K, typename V>
360 std::pair<GenericAATree::iterator, GenericAATree::iterator>
equal_range(Key key)361 AATree<K, V>::equal_range(Key key) {
362 return std::make_pair(lower_bound(key), upper_bound(key));
363 }
364
365 template <typename K, typename V>
lower_bound(Key key)366 GenericAATree::iterator AATree<K, V>::lower_bound(Key key) {
367 AATreeItem* item = GetLowerBoundImpl(root_, key);
368 return item == nullptr ? end() : iterator(&root_, item);
369 }
370
371 template <typename K, typename V>
GetLowerBoundImpl(AATreeItem * item,Key key)372 AATreeItem* AATree<K, V>::GetLowerBoundImpl(AATreeItem* item, Key key) {
373 if (item == nullptr) {
374 return nullptr;
375 }
376 // If the item's key is less than the key, go right.
377 if (compare_(DoGetKey(*item), key)) {
378 return GetLowerBoundImpl(item->right_.get(), key);
379 }
380 // Otherwise the item's key is greater than the key. Try to go left.
381 AATreeItem* left = item->left_.get();
382 if (left == nullptr) {
383 return item;
384 }
385 AATreeItem* next = GetLowerBoundImpl(left, key);
386 return next == nullptr ? item : next;
387 }
388
389 template <typename K, typename V>
upper_bound(Key key)390 GenericAATree::iterator AATree<K, V>::upper_bound(Key key) {
391 AATreeItem* item = GetUpperBoundImpl(root_, key);
392 return item == nullptr ? end() : iterator(&root_, item);
393 }
394
395 template <typename K, typename V>
GetUpperBoundImpl(AATreeItem * item,Key key)396 AATreeItem* AATree<K, V>::GetUpperBoundImpl(AATreeItem* item, Key key) {
397 if (item == nullptr) {
398 return nullptr;
399 }
400 // If the item's key is less than or equal to the key, go right.
401 if (!compare_(key, DoGetKey(*item))) {
402 return GetUpperBoundImpl(item->right_.get(), key);
403 }
404 // Otherwise the item's key is greater than the key. Try to go left.
405 AATreeItem* left = item->left_.get();
406 if (left == nullptr) {
407 return item;
408 }
409 AATreeItem* next = GetUpperBoundImpl(left, key);
410 return next == nullptr ? item : next;
411 }
412
413 } // namespace pw::containers::internal
414