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