• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
18 #include "pw_allocator/bucket/base.h"
19 #include "pw_containers/intrusive_multimap.h"
20 #include "pw_function/function.h"
21 
22 namespace pw::allocator {
23 
24 /// Intrusive item type corresponding to a `FastSortedBucket`.
25 ///
26 /// When free blocks are added to a bucket, their usable space is used to store
27 /// an intrusive item that can be added to the bucket's intrusive container.
28 ///
29 /// This particular item is derived from pw_container's `AATreeItem`, which
30 /// allows O(log(n)) insertion and lookup and is thus "fast".
31 template <typename BlockType>
32 class FastSortedItem
33     : public IntrusiveMultiMap<size_t, FastSortedItem<BlockType>>::Item {
34  public:
key()35   size_t key() const {
36     const auto* block = BlockType::FromUsableSpace(this);
37     return block->InnerSize();
38   }
39 };
40 
41 /// Generic type with the same layout as a `FastSortedItem<BlockType>`.
42 ///
43 /// `FastSortedItem` depends on a `BlockType`, in order to return the block's
44 /// inner size as a sorting key. `BlockType` definitions like `DetailedBlock`
45 /// take a `WhenFree` parameter that describes the layout of memoryused to track
46 /// the block when free. The `WhenFree` parameter then _should_ be
47 /// `FastSortedItem`, but cannot be due to the circular dependency. Instead,
48 /// this type provides same layout without depending on `BlockType`, and thus
49 /// can be used when defining the block.
50 struct GenericFastSortedItem
51     : public IntrusiveMultiMap<size_t, GenericFastSortedItem>::Item {};
52 
53 /// Container of size-sorted free blocks.
54 ///
55 /// The container used to hold the blocks is a multimap. Insertion and removal
56 /// are O(log(n)) operations. However, the multimap nodes require more space
57 /// than the "compact" items. As such, this bucket type is a good general
58 /// purpose container for items above a minimum size.
59 template <typename BlockType>
60 class FastSortedBucket
61     : public internal::BucketBase<FastSortedBucket<BlockType>,
62                                   BlockType,
63                                   FastSortedItem<BlockType>> {
64  private:
65   using Base = internal::BucketBase<FastSortedBucket<BlockType>,
66                                     BlockType,
67                                     FastSortedItem<BlockType>>;
68   friend Base;
69 
70   template <typename>
71   friend class ReverseFastSortedBucket;
72 
73   using Compare = Function<bool(size_t, size_t)>;
74 
75  public:
76   constexpr FastSortedBucket() = default;
~FastSortedBucket()77   ~FastSortedBucket() { Base::Clear(); }
78 
79  private:
80   // Constructor used by `ReverseFastSortedBucket`.
FastSortedBucket(Compare && compare)81   constexpr explicit FastSortedBucket(Compare&& compare)
82       : items_(std::move(compare)) {}
83 
84   /// @copydoc `BucketBase::Add`
DoAdd(BlockType & block)85   void DoAdd(BlockType& block) {
86     auto* item = new (block.UsableSpace()) FastSortedItem<BlockType>();
87     items_.insert(*item);
88   }
89 
90   /// @copydoc `BucketBase::RemoveAny`
DoRemoveAny()91   BlockType* DoRemoveAny() {
92     auto iter = items_.begin();
93     FastSortedItem<BlockType>& item = *iter;
94     items_.erase(iter);
95     return BlockType::FromUsableSpace(&item);
96   }
97 
98   /// @copydoc `BucketBase::Remove`
DoRemove(BlockType & block)99   bool DoRemove(BlockType& block) {
100     FastSortedItem<BlockType>& item_to_remove = Base::GetItemFrom(block);
101     auto iters = items_.equal_range(block.InnerSize());
102     auto iter =
103         std::find_if(iters.first,
104                      iters.second,
105                      [&item_to_remove](FastSortedItem<BlockType>& item) {
106                        return &item_to_remove == &item;
107                      });
108     if (iter == items_.end()) {
109       return false;
110     }
111     items_.erase(iter);
112     return true;
113   }
114 
115   /// @copydoc `BucketBase::Remove`
DoRemoveCompatible(Layout layout)116   BlockType* DoRemoveCompatible(Layout layout) {
117     auto iter = items_.lower_bound(layout.size());
118     return RemoveImpl(iter, layout);
119   }
120 
121   template <typename Iterator>
RemoveImpl(Iterator iter,Layout layout)122   BlockType* RemoveImpl(Iterator iter, Layout layout) {
123     iter =
124         std::find_if(iter, items_.end(), Base::MakeCanAllocPredicate(layout));
125     auto* block = Base::GetBlockFromIterator(iter, items_.end());
126     if (block != nullptr) {
127       items_.erase(iter);
128     }
129     return block;
130   }
131 
132   IntrusiveMultiMap<size_t, FastSortedItem<BlockType>> items_;
133 };
134 
135 /// Like `FastSortedBucket`, but ordered largest to smallest.
136 ///
137 /// In particular, `Remove()` will return the largest free block.
138 template <typename BlockType>
139 class ReverseFastSortedBucket
140     : public internal::BucketBase<ReverseFastSortedBucket<BlockType>,
141                                   BlockType,
142                                   FastSortedItem<BlockType>> {
143  private:
144   using Base = internal::BucketBase<ReverseFastSortedBucket<BlockType>,
145                                     BlockType,
146                                     FastSortedItem<BlockType>>;
147   friend Base;
148 
149  public:
ReverseFastSortedBucket()150   constexpr ReverseFastSortedBucket()
151       : impl_(std::greater<>()), items_(impl_.items_) {}
152 
153  private:
154   /// @copydoc `BucketBase::Add`
DoAdd(BlockType & block)155   void DoAdd(BlockType& block) { impl_.DoAdd(block); }
156 
157   /// @copydoc `BucketBase::RemoveAny`
DoRemoveAny()158   BlockType* DoRemoveAny() {
159     auto iter = items_.begin();
160     FastSortedItem<BlockType>& item = *iter;
161     items_.erase(iter);
162     return BlockType::FromUsableSpace(&item);
163   }
164 
165   /// @copydoc `BucketBase::Remove`
DoRemove(BlockType & block)166   bool DoRemove(BlockType& block) { return impl_.DoRemove(block); }
167 
168   /// @copydoc `BucketBase::Remove`
DoRemoveCompatible(Layout layout)169   BlockType* DoRemoveCompatible(Layout layout) {
170     return impl_.RemoveImpl(impl_.items_.begin(), layout);
171   }
172 
173   FastSortedBucket<BlockType> impl_;
174   IntrusiveMultiMap<size_t, FastSortedItem<BlockType>>& items_;
175 };
176 
177 }  // namespace pw::allocator
178