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