xref: /aosp_15_r20/external/pigweed/pw_containers/public/pw_containers/intrusive_map.h (revision 61c4878ac05f98d0ceed94b57d316916de578985)
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 "pw_containers/internal/aa_tree.h"
17 #include "pw_containers/internal/aa_tree_item.h"
18 
19 namespace pw {
20 
21 /// A `std::map<Key, T, Compare>`-like class that uses intrusive items.
22 ///
23 /// Since the map structure is stored in the items themselves, each item must
24 /// outlive any map it is a part of and must be part of at most one map.
25 ///
26 /// This map requires unique keys. Attempting to add an item with same key as an
27 /// item already in the map will fail.
28 ///
29 /// - Since items are not allocated by this class, the following methods have
30 ///   no analogue:
31 ///   - std::map<T>::operator=
32 ///   - std::map<T>::operator[]
33 ///   - std::map<T>::get_allocator
34 ///   - std::map<T>::insert_or_assign
35 ///   - std::map<T>::emplace
36 ///   - std::map<T>::emplace_hint
37 ///   - std::map<T>::try_emplace
38 ///
39 /// - Methods corresponding to the following take initializer lists of pointer
40 ///   to items rather than the items themselves:
41 ///   - std::map<T>::(constructor)
42 ///   - std::map<T>::insert
43 ///
44 /// - There are no overloads corresponding to the following methods that take
45 ///   r-value references.:
46 ///   - std::map<T>::insert
47 ///   - std::map<T>::merge
48 ///
49 /// - Since modifying the map modifies the items themselves, methods
50 ///   corresponding to those below only take `iterator`s and not
51 ///   `const_iterator`s:
52 ///   - std::map<T>::insert
53 ///   - std::map<T>::erase
54 ///
55 /// - An additional overload of `erase` is provided that takes a direct
56 ///   reference to an item.
57 ///
58 /// - C++23 methods are not (yet) supported.
59 ///
60 /// @tparam   Key         Type to sort items on
61 /// @tparam   T           Type of values stored in the map.
62 template <typename Key, typename T>
63 class IntrusiveMap {
64  private:
65   using GenericIterator = containers::internal::GenericAATree::iterator;
66   using Tree = containers::internal::AATree<Key, T>;
67   using Compare = typename Tree::Compare;
68   using GetKey = typename Tree::GetKey;
69 
70  public:
71   /// IntrusiveMap items must derive from either `Item` or `Pair`.
72   /// Use `Pair` to automatically provide storage for a `Key`.
73   /// Use `Item` when the derived type has a `key()` accessor method or when the
74   /// map provides a custom `GetKey` function object.
75   using Item = typename Tree::Item;
76   using Pair = typename Tree::Pair;
77 
78   using key_type = Key;
79   using mapped_type = std::remove_cv_t<T>;
80   using value_type = Item;
81   using size_type = std::size_t;
82   using difference_type = std::ptrdiff_t;
83   using key_compare = Compare;
84   using reference = value_type&;
85   using const_reference = const value_type&;
86   using pointer = value_type*;
87   using const_pointer = const value_type*;
88 
89  public:
90   class iterator : public containers::internal::AATreeIterator<T> {
91    public:
92     constexpr iterator() = default;
93 
94    private:
95     friend IntrusiveMap;
iterator(GenericIterator iter)96     constexpr explicit iterator(GenericIterator iter)
97         : containers::internal::AATreeIterator<T>(iter) {}
98   };
99 
100   class const_iterator
101       : public containers::internal::AATreeIterator<std::add_const_t<T>> {
102    public:
103     constexpr const_iterator() = default;
104 
105    private:
106     friend IntrusiveMap;
const_iterator(GenericIterator iter)107     constexpr explicit const_iterator(GenericIterator iter)
108         : containers::internal::AATreeIterator<std::add_const_t<T>>(iter) {}
109   };
110 
111   using reverse_iterator = std::reverse_iterator<iterator>;
112   using const_reverse_iterator = std::reverse_iterator<const_iterator>;
113 
114   /// Constructs an empty map of items.
IntrusiveMap()115   constexpr explicit IntrusiveMap() : IntrusiveMap(std::less<Key>()) {}
116 
117   /// Constructs an empty map of items.
118   ///
119   /// SFINAE is used to disambiguate between this constructor and the one that
120   /// takes an initializer list.
121   ///
122   /// @param  compare   Function with the signature `bool(Key, Key)` that is
123   ///                   used to order items.
124   template <typename Comparator>
IntrusiveMap(Comparator && compare)125   constexpr explicit IntrusiveMap(Comparator&& compare)
126       : IntrusiveMap(std::forward<Comparator>(compare),
127                      [](const T& t) { return t.key(); }) {}
128 
129   /// Constructs an empty map of items.
130   ///
131   /// @param  compare   Function with the signature `bool(Key, Key)` that is
132   ///                   used to order items.
133   /// @param  get_key   Function with signature `Key(const T&)` that returns the
134   ///                   value that items are sorted on.
135   template <typename Comparator, typename KeyRetriever>
IntrusiveMap(Comparator && compare,KeyRetriever && get_key)136   constexpr IntrusiveMap(Comparator&& compare, KeyRetriever&& get_key)
137       : tree_(true,
138               std::forward<Comparator>(compare),
139               std::forward<KeyRetriever>(get_key)) {
140     CheckItemType();
141   }
142 
143   /// Constructs an IntrusiveMap from an iterator over Items.
144   ///
145   /// The iterator may dereference as either Item& (e.g. from std::array<Item>)
146   /// or Item* (e.g. from std::initializer_list<Item*>).
147   template <typename Iterator, typename... Functors>
IntrusiveMap(Iterator first,Iterator last,Functors &&...functors)148   IntrusiveMap(Iterator first, Iterator last, Functors&&... functors)
149       : IntrusiveMap(std::forward<Functors>(functors)...) {
150     tree_.insert(first, last);
151   }
152 
153   /// Constructs an IntrusiveMap from a std::initializer_list of pointers
154   /// to items.
155   template <typename... Functors>
IntrusiveMap(std::initializer_list<T * > items,Functors &&...functors)156   explicit IntrusiveMap(std::initializer_list<T*> items, Functors&&... functors)
157       : IntrusiveMap(
158             items.begin(), items.end(), std::forward<Functors>(functors)...) {}
159 
160   // Element access
161 
162   /// Returns a reference to the item associated with the given key.
163   ///
164   /// @pre The map must contain an item associated with the key.
at(const key_type & key)165   T& at(const key_type& key) {
166     auto iter = tree_.find(key);
167     PW_DASSERT(iter != tree_.end());
168     return static_cast<T&>(*iter);
169   }
170 
at(const key_type & key)171   const T& at(const key_type& key) const {
172     auto iter = tree_.find(key);
173     PW_DASSERT(iter != tree_.end());
174     return static_cast<const T&>(*iter);
175   }
176 
177   // Iterators
178 
begin()179   iterator begin() noexcept { return iterator(tree_.begin()); }
begin()180   const_iterator begin() const noexcept {
181     return const_iterator(tree_.begin());
182   }
cbegin()183   const_iterator cbegin() const noexcept { return begin(); }
184 
end()185   iterator end() noexcept { return iterator(tree_.end()); }
end()186   const_iterator end() const noexcept { return const_iterator(tree_.end()); }
cend()187   const_iterator cend() const noexcept { return end(); }
188 
rbegin()189   reverse_iterator rbegin() noexcept { return reverse_iterator(end()); }
rbegin()190   const_reverse_iterator rbegin() const noexcept {
191     return const_reverse_iterator(end());
192   }
crbegin()193   const_reverse_iterator crbegin() const noexcept { return rbegin(); }
194 
rend()195   reverse_iterator rend() noexcept { return reverse_iterator(begin()); }
rend()196   const_reverse_iterator rend() const noexcept {
197     return const_reverse_iterator(begin());
198   }
crend()199   const_reverse_iterator crend() const noexcept { return rend(); }
200 
201   // Capacity
202 
203   /// Returns whether the map has zero items or not.
empty()204   [[nodiscard]] bool empty() const noexcept { return tree_.empty(); }
205 
206   /// Returns the number of items in the map.
size()207   size_t size() const { return tree_.size(); }
208 
209   /// Returns how many items can be added.
210   ///
211   /// As an intrusive container, this is effectively unbounded.
max_size()212   constexpr size_t max_size() const noexcept { return tree_.max_size(); }
213 
214   // Modifiers
215 
216   /// Removes all items from the map and leaves it empty.
217   ///
218   /// The items themselves are not destructed.
clear()219   void clear() { tree_.clear(); }
220 
221   /// Attempts to add the given item to the map.
222   ///
223   /// The item will be added if the map does not already contain an item with
224   /// the given item's key.
225   ///
226   /// @returns  A pointer to the inserted item and `true`, or a pointer to the
227   ///           existing item with same key and `false`.
insert(T & item)228   std::pair<iterator, bool> insert(T& item) {
229     auto result = tree_.insert(item);
230     return std::make_pair(iterator(result.first), result.second);
231   }
232 
insert(iterator,T & item)233   iterator insert(iterator, T& item) {
234     // Disregard the hint.
235     return insert(item).first;
236   }
237 
238   template <class Iterator>
insert(Iterator first,Iterator last)239   void insert(Iterator first, Iterator last) {
240     tree_.insert(first, last);
241   }
242 
insert(std::initializer_list<T * > ilist)243   void insert(std::initializer_list<T*> ilist) {
244     tree_.insert(ilist.begin(), ilist.end());
245   }
246 
247   /// Removes an item from the map and returns an iterator to the item after
248   /// the removed item..
249   ///
250   /// The items themselves are not destructed.
erase(T & item)251   iterator erase(T& item) { return iterator(tree_.erase_one(item)); }
252 
erase(iterator pos)253   iterator erase(iterator pos) { return iterator(tree_.erase_one(*pos)); }
254 
erase(iterator first,iterator last)255   iterator erase(iterator first, iterator last) {
256     return iterator(tree_.erase_range(*first, *last));
257   }
258 
erase(const key_type & key)259   size_t erase(const key_type& key) { return tree_.erase_all(key); }
260 
261   /// Exchanges this map's items with the `other` map's items.
swap(IntrusiveMap<Key,T> & other)262   void swap(IntrusiveMap<Key, T>& other) { tree_.swap(other.tree_); }
263 
264   /// Splices items from the `other` map into this one.
265   template <typename MapType>
merge(MapType & other)266   void merge(MapType& other) {
267     tree_.merge(other.tree_);
268   }
269 
270   /// Returns the number of items in the map with the given key.
271   ///
272   /// Since the map requires unique keys, this is always 0 or 1.
count(const key_type & key)273   size_t count(const key_type& key) const { return tree_.count(key); }
274 
275   /// Returns a pointer to an item with the given key, or null if the map does
276   /// not contain such an item.
find(const key_type & key)277   iterator find(const key_type& key) { return iterator(tree_.find(key)); }
278 
find(const key_type & key)279   const_iterator find(const key_type& key) const {
280     return const_iterator(tree_.find(key));
281   }
282 
283   /// Returns a pair of iterators where the first points to the item with the
284   /// smallest key that is not less than the given key, and the second points to
285   /// the item with the smallest key that is greater than the given key.
equal_range(const key_type & key)286   std::pair<iterator, iterator> equal_range(const key_type& key) {
287     auto result = tree_.equal_range(key);
288     return std::make_pair(iterator(result.first), iterator(result.second));
289   }
equal_range(const key_type & key)290   std::pair<const_iterator, const_iterator> equal_range(
291       const key_type& key) const {
292     auto result = tree_.equal_range(key);
293     return std::make_pair(const_iterator(result.first),
294                           const_iterator(result.second));
295   }
296 
297   /// Returns an iterator to the item in the map with the smallest key that is
298   /// greater than or equal to the given key, or `end()` if the map is empty.
lower_bound(const key_type & key)299   iterator lower_bound(const key_type& key) {
300     return iterator(tree_.lower_bound(key));
301   }
lower_bound(const key_type & key)302   const_iterator lower_bound(const key_type& key) const {
303     return const_iterator(tree_.lower_bound(key));
304   }
305 
306   /// Returns an iterator to the item in the map with the smallest key that is
307   /// strictly greater than the given key, or `end()` if the map is empty.
upper_bound(const key_type & key)308   iterator upper_bound(const key_type& key) {
309     return iterator(tree_.upper_bound(key));
310   }
upper_bound(const key_type & key)311   const_iterator upper_bound(const key_type& key) const {
312     return const_iterator(tree_.upper_bound(key));
313   }
314 
315  private:
316   // Check that T is an Item in a function, since the class T will not be fully
317   // defined when the IntrusiveList<T> class is instantiated.
CheckItemType()318   static constexpr void CheckItemType() {
319     using ItemBase = containers::internal::AATreeItem;
320     using IntrusiveItemType =
321         typename containers::internal::IntrusiveItem<ItemBase, T>::Type;
322     static_assert(
323         std::is_base_of<IntrusiveItemType, T>(),
324         "IntrusiveMap items must be derived from IntrusiveMap<Key, T>::Item, "
325         "where T is the item or one of its bases.");
326   }
327 
328   // Allow multimaps to access the tree for `merge`.
329   template <typename, typename>
330   friend class IntrusiveMultiMap;
331 
332   // The AA tree that stores the map.
333   //
334   // This field is mutable so that it doesn't need const overloads.
335   mutable Tree tree_;
336 };
337 
338 }  // namespace pw
339