xref: /aosp_15_r20/external/pigweed/pw_allocator/public/pw_allocator/bump_allocator.h (revision 61c4878ac05f98d0ceed94b57d316916de578985)
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