xref: /aosp_15_r20/external/pigweed/pw_allocator/public/pw_allocator/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/capability.h"
19 #include "pw_allocator/deallocator.h"
20 #include "pw_allocator/layout.h"
21 #include "pw_allocator/unique_ptr.h"
22 #include "pw_result/result.h"
23 
24 namespace pw {
25 
26 /// Abstract interface for variable-layout memory allocation.
27 ///
28 /// The interface makes no guarantees about its implementation. Consumers of the
29 /// generic interface must not make any assumptions around allocator behavior,
30 /// thread safety, or performance.
31 class Allocator : public Deallocator {
32  public:
33   /// Allocates a block of memory with the specified size and alignment.
34   ///
35   /// Returns `nullptr` if the allocation cannot be made, or the `layout` has a
36   /// size of 0.
37   ///
38   /// @param[in]  layout      Describes the memory to be allocated.
Allocate(Layout layout)39   void* Allocate(Layout layout) {
40     return layout.size() != 0 ? DoAllocate(layout) : nullptr;
41   }
42 
43   /// Constructs and object of type `T` from the given `args`
44   ///
45   /// The return value is nullable, as allocating memory for the object may
46   /// fail. Callers must check for this error before using the resulting
47   /// pointer.
48   ///
49   /// @param[in]  args...     Arguments passed to the object constructor.
50   template <typename T, int&... ExplicitGuard, typename... Args>
New(Args &&...args)51   T* New(Args&&... args) {
52     void* ptr = Allocate(Layout::Of<T>());
53     return ptr != nullptr ? new (ptr) T(std::forward<Args>(args)...) : nullptr;
54   }
55 
56   /// Constructs an array of type `T` from the given `args` and size
57   ///
58   /// The return value is nullable, as allocating memory for the object may
59   /// fail. Callers must check for this error before using the resulting
60   /// pointer.
61   ///
62   /// @param[in]  size        The size of the array to allocate.
63   template <typename T, int&... ExplicitGuard>
NewArray(size_t size)64   T* NewArray(size_t size) {
65     Layout layout(sizeof(T) * size, alignof(T));
66     void* ptr = Allocate(layout);
67     return ptr != nullptr ? new (ptr) T[size] : nullptr;
68   }
69 
70   /// Constructs and object of type `T` from the given `args`, and wraps it in a
71   /// `UniquePtr`
72   ///
73   /// The returned value may contain null if allocating memory for the object
74   /// fails. Callers must check for null before using the `UniquePtr`.
75   ///
76   /// @param[in]  args...     Arguments passed to the object constructor.
77   template <typename T, int&... ExplicitGuard, typename... Args>
MakeUnique(Args &&...args)78   [[nodiscard]] UniquePtr<T> MakeUnique(Args&&... args) {
79     return Deallocator::WrapUnique<T>(New<T>(std::forward<Args>(args)...));
80   }
81 
82   /// Constructs an array of type `T` from the given `args` and size, and
83   /// wraps it in a `UniquePtr`
84   ///
85   /// The returned value may contain null if allocating memory for the object
86   /// fails. Callers must check for null before using the `UniquePtr`.
87   ///
88   /// @param[in]  size        The size of the array to allocate.
89   template <typename T, int&... ExplicitGuard>
MakeUniqueArray(size_t size)90   [[nodiscard]] UniquePtr<T[]> MakeUniqueArray(size_t size) {
91     return Deallocator::WrapUniqueArray<T>(NewArray<T>(size), size);
92   }
93 
94   /// Modifies the size of an previously-allocated block of memory without
95   /// copying any data.
96   ///
97   /// Returns true if its size was changed without copying data to a new
98   /// allocation; otherwise returns false.
99   ///
100   /// In particular, it always returns true if the `old_layout.size()` equals
101   /// `new_size`, and always returns false if the given pointer is null, the
102   /// `old_layout.size()` is 0, or the `new_size` is 0.
103   ///
104   /// @param[in]  ptr           Pointer to previously-allocated memory.
105   /// @param[in]  new_size      Requested new size for the memory allocation.
Resize(void * ptr,size_t new_size)106   bool Resize(void* ptr, size_t new_size) {
107     return ptr != nullptr && new_size != 0 && DoResize(ptr, new_size);
108   }
109 
110   /// Deprecated version of `Resize` that takes a `Layout`.
111   /// Do not use this method. It will be removed.
112   /// TODO(b/326509341): Remove when downstream consumers migrate.
Resize(void * ptr,Layout layout,size_t new_size)113   bool Resize(void* ptr, Layout layout, size_t new_size) {
114     return ptr != nullptr && new_size != 0 && DoResize(ptr, layout, new_size);
115   }
116 
117   /// Modifies the size of a previously-allocated block of memory.
118   ///
119   /// Returns pointer to the modified block of memory, or `nullptr` if the
120   /// memory could not be modified.
121   ///
122   /// The data stored by the memory being modified must be trivially
123   /// copyable. If it is not, callers should themselves attempt to `Resize`,
124   /// then `Allocate`, move the data, and `Deallocate` as needed.
125   ///
126   /// If `nullptr` is returned, the block of memory is unchanged. In particular,
127   /// if the `new_layout` has a size of 0, the given pointer will NOT be
128   /// deallocated.
129   ///
130   /// TODO(b/331290408): This error condition needs to be better communicated to
131   /// module users, who may assume the pointer is freed.
132   ///
133   /// Unlike `Resize`, providing a null pointer will return a new allocation.
134   ///
135   /// If the request can be satisfied using `Resize`, the `alignment` parameter
136   /// may be ignored.
137   ///
138   /// @param[in]  ptr         Pointer to previously-allocated memory.
139   /// @param[in]  new_layout  Describes the memory to be allocated.
Reallocate(void * ptr,Layout new_layout)140   void* Reallocate(void* ptr, Layout new_layout) {
141     if (new_layout.size() == 0) {
142       return nullptr;
143     }
144     if (ptr == nullptr) {
145       return Allocate(new_layout);
146     }
147     return DoReallocate(ptr, new_layout);
148   }
149 
150   /// Deprecated version of `Reallocate` that takes a `Layout`.
151   /// Do not use this method. It will be removed.
152   /// TODO(b/326509341): Remove when downstream consumers migrate.
Reallocate(void * ptr,Layout old_layout,size_t new_size)153   void* Reallocate(void* ptr, Layout old_layout, size_t new_size) {
154     if (new_size == 0) {
155       return nullptr;
156     }
157     if (ptr == nullptr) {
158       return Allocate(Layout(new_size, old_layout.alignment()));
159     }
160     return DoReallocate(ptr, old_layout, new_size);
161   }
162 
163   /// Returns the total bytes that have been allocated by this allocator, or
164   /// `size_t(-1)` if this allocator does not track its total allocated bytes.
GetAllocated()165   size_t GetAllocated() const { return DoGetAllocated(); }
166 
167  protected:
168   /// TODO(b/326509341): Remove when downstream consumers migrate.
169   constexpr Allocator() = default;
170 
Allocator(const Capabilities & capabilities)171   explicit constexpr Allocator(const Capabilities& capabilities)
172       : Deallocator(capabilities) {}
173 
174  private:
175   /// Virtual `Allocate` function implemented by derived classes.
176   ///
177   /// @param[in]  layout        Describes the memory to be allocated. Guaranteed
178   ///                           to have a non-zero size.
179   virtual void* DoAllocate(Layout layout) = 0;
180 
181   /// Virtual `Resize` function implemented by derived classes.
182   ///
183   /// The default implementation simply returns `false`, indicating that
184   /// resizing is not supported.
185   ///
186   /// @param[in]  ptr           Pointer to memory, guaranteed to not be null.
187   /// @param[in]  new_size      Requested size, guaranteed to be non-zero..
DoResize(void *,size_t)188   virtual bool DoResize(void* /*ptr*/, size_t /*new_size*/) { return false; }
189 
190   /// Deprecated version of `DoResize` that takes a `Layout`.
191   /// Do not use this method. It will be removed.
192   /// TODO(b/326509341): Remove when downstream consumers migrate.
DoResize(void *,Layout,size_t)193   virtual bool DoResize(void*, Layout, size_t) { return false; }
194 
195   /// Virtual `Reallocate` function that can be overridden by derived classes.
196   ///
197   /// The default implementation will first try to `Resize` the data. If that is
198   /// unsuccessful, it will allocate an entirely new block, copy existing data,
199   /// and deallocate the given block.
200   ///
201   /// @param[in]  ptr           Pointer to memory, guaranteed to not be null.
202   /// @param[in]  new_layout    Describes the memory to be allocated. Guaranteed
203   ///                           to have a non-zero size.
204   virtual void* DoReallocate(void* ptr, Layout new_layout);
205 
206   /// Deprecated version of `DoReallocate` that takes a `Layout`.
207   /// Do not use this method. It will be removed.
208   /// TODO(b/326509341): Remove when downstream consumers migrate.
209   virtual void* DoReallocate(void* ptr, Layout old_layout, size_t new_size);
210 
211   /// Virtual `GetAllocated` function that can be overridden by derived classes.
212   ///
213   /// The default implementation simply returns `size_t(-1)`, indicating that
214   /// tracking total allocated bytes is not supported.
DoGetAllocated()215   virtual size_t DoGetAllocated() const { return size_t(-1); }
216 };
217 
218 namespace allocator {
219 
220 // Alias for module consumers using the older name for the above type.
221 using Allocator = ::pw::Allocator;
222 
223 }  // namespace allocator
224 }  // namespace pw
225