• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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