1 // Copyright 2023 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/allocator.h" 19 #include "pw_allocator/capability.h" 20 #include "pw_bytes/span.h" 21 22 namespace pw::allocator { 23 namespace internal { 24 25 /// Type-erased base class to allow destroying a generic instance of ``Owned``. 26 class GenericOwned { 27 public: 28 virtual ~GenericOwned() = default; set_next(GenericOwned * next)29 void set_next(GenericOwned* next) { next_ = next; } Destroy()30 void Destroy() { DoDestroy(); } 31 32 private: 33 virtual void DoDestroy() = 0; 34 35 GenericOwned* next_ = nullptr; 36 }; 37 38 /// Wrapper class that invokes an object's destructor when a BumpAllocator is 39 /// destroyed. 40 template <typename T> 41 class Owned : public GenericOwned { 42 public: set_object(T * object)43 void set_object(T* object) { object_ = object; } 44 45 private: DoDestroy()46 void DoDestroy() override { 47 if (object_ != nullptr) { 48 std::destroy_at(object_); 49 object_ = nullptr; 50 } 51 } 52 53 T* object_ = nullptr; 54 }; 55 56 } // namespace internal 57 58 /// Allocator that does not automatically delete. 59 /// 60 /// A "bump" or "arena" allocator provides memory by simply incrementing a 61 /// pointer to a memory region in order to "allocate" memory for requests, and 62 /// doing nothing on deallocation. All memory is freed only when the allocator 63 /// itself is destroyed. As a result, this allocator is extremely fast. 64 /// 65 /// Bump allocators are useful when short-lived to allocate objects from small 66 /// buffers with almost zero overhead. Since memory is not deallocated, a bump 67 /// allocator with a longer lifespan than any of its allocations will end up 68 /// holding unusable memory. An example of a good use case might be decoding 69 /// RPC messages that may require a variable amount of space only for as long as 70 /// it takes to decode a single message. 71 /// 72 /// On destruction, the destructors for any objects allocated using `New` or 73 /// `MakeUnique` are NOT called. To have these destructors invoked, you can 74 /// allocate "owned" objects using `NewOwned` and `MakeUniqueOwned`. This adds a 75 /// small amount of overhead to the allocation. 76 class BumpAllocator : public Allocator { 77 public: 78 static constexpr Capabilities kCapabilities = kSkipsDestroy; 79 80 /// Constructs a BumpAllocator without initializing it. BumpAllocator()81 constexpr BumpAllocator() : Allocator(kCapabilities) {} 82 83 /// Constructs a BumpAllocator and initializes it. BumpAllocator(ByteSpan region)84 explicit BumpAllocator(ByteSpan region) : BumpAllocator() { Init(region); } 85 ~BumpAllocator()86 ~BumpAllocator() override { Reset(); } 87 88 /// Sets the memory region to be used by the allocator. 89 void Init(ByteSpan region); 90 91 /// Constructs an "owned" object of type `T` from the given `args` 92 /// 93 /// Owned objects will have their destructors invoked when the allocator goes 94 /// out of scope. 95 /// 96 /// The return value is nullable, as allocating memory for the object may 97 /// fail. Callers must check for this error before using the resulting 98 /// pointer. 99 /// 100 /// @param[in] args... Arguments passed to the object constructor. 101 template <typename T, int&... ExplicitGuard, typename... Args> NewOwned(Args &&...args)102 T* NewOwned(Args&&... args) { 103 internal::Owned<T>* owned = New<internal::Owned<T>>(); 104 T* ptr = owned != nullptr ? New<T>(std::forward<Args>(args)...) : nullptr; 105 if (ptr != nullptr) { 106 owned->set_object(ptr); 107 owned->set_next(owned_); 108 owned_ = owned; 109 } 110 return ptr; 111 } 112 113 /// Constructs and object of type `T` from the given `args`, and wraps it in a 114 /// `UniquePtr` 115 /// 116 /// Owned objects will have their destructors invoked when the allocator goes 117 /// out of scope. 118 /// 119 /// The returned value may contain null if allocating memory for the object 120 /// fails. Callers must check for null before using the `UniquePtr`. 121 /// 122 /// @param[in] args... Arguments passed to the object constructor. 123 template <typename T, int&... ExplicitGuard, typename... Args> MakeUniqueOwned(Args &&...args)124 [[nodiscard]] UniquePtr<T> MakeUniqueOwned(Args&&... args) { 125 return WrapUnique<T>(NewOwned<T>(std::forward<Args>(args)...)); 126 } 127 128 private: 129 /// @copydoc Allocator::Allocate 130 void* DoAllocate(Layout layout) override; 131 132 /// @copydoc Allocator::Deallocate 133 void DoDeallocate(void*) override; 134 135 /// @copydoc Allocator::GetAllocated DoGetAllocated()136 size_t DoGetAllocated() const override { return allocated_; } 137 138 /// Frees any owned objects and discards remaining memory. 139 void Reset(); 140 141 size_t allocated_ = 0; 142 ByteSpan remaining_; 143 internal::GenericOwned* owned_ = nullptr; 144 }; 145 146 } // namespace pw::allocator 147