• 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 <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_numeric/checked_arithmetic.h"
23 #include "pw_result/result.h"
24 
25 namespace pw {
26 
27 /// Abstract interface for variable-layout memory allocation.
28 ///
29 /// The interface makes no guarantees about its implementation. Consumers of the
30 /// generic interface must not make any assumptions around allocator behavior,
31 /// thread safety, or performance.
32 class Allocator : public Deallocator {
33  public:
34   /// Allocates a block of memory with the specified size and alignment.
35   ///
36   /// Returns `nullptr` if the allocation cannot be made, or the `layout` has a
37   /// size of 0.
38   ///
39   /// @param[in]  layout      Describes the memory to be allocated.
Allocate(Layout layout)40   void* Allocate(Layout layout) {
41     return layout.size() != 0 ? DoAllocate(layout) : nullptr;
42   }
43 
44   /// Constructs an object of type `T` from the given `args`
45   ///
46   /// The return value is nullable, as allocating memory for the object may
47   /// fail. Callers must check for this error before using the resulting
48   /// pointer.
49   ///
50   /// @param[in]  args...     Arguments passed to the object constructor.
51   template <typename T, int&... kExplicitGuard, typename... Args>
New(Args &&...args)52   [[nodiscard]] std::enable_if_t<!std::is_array_v<T>, T*> New(Args&&... args) {
53     void* ptr = Allocate(Layout::Of<T>());
54     if (ptr == nullptr) {
55       return nullptr;
56     }
57     return new (ptr) T(std::forward<Args>(args)...);
58   }
59 
60   /// Constructs an array of `count` objects of type `T`
61   ///
62   /// The return value is nullable, as allocating memory for the object may
63   /// fail. Callers must check for this error before using the resulting
64   /// pointer.
65   ///
66   /// @param[in]  count        Number of objects to allocate.
67   template <typename T,
68             int&... kExplicitGuard,
69             typename ElementType = std::remove_extent_t<T>,
70             std::enable_if_t<is_unbounded_array_v<T>, int> = 0>
New(size_t count)71   [[nodiscard]] ElementType* New(size_t count) {
72     return New<T>(count, alignof(ElementType));
73   }
74 
75   /// Constructs an `alignment`-byte aligned array of `count` objects of type
76   /// `T`
77   ///
78   /// The return value is nullable, as allocating memory for the object may
79   /// fail. Callers must check for this error before using the resulting
80   /// pointer.
81   ///
82   /// @param[in]  count        Number of objects to allocate.
83   /// @param[in]  alignment    Alignment to use for the start of the array.
84   template <typename T,
85             int&... kExplicitGuard,
86             typename ElementType = std::remove_extent_t<T>,
87             std::enable_if_t<is_unbounded_array_v<T>, int> = 0>
New(size_t count,size_t alignment)88   [[nodiscard]] ElementType* New(size_t count, size_t alignment) {
89     void* ptr = Allocate(Layout::Of<T>(count).Align(alignment));
90     return ptr != nullptr ? new (ptr) ElementType[count] : nullptr;
91   }
92 
93   /// Deprecated version of `New` with a different name and templated on
94   /// the object type instead of the array type.
95   /// Do not use this method. It will be removed.
96   /// TODO(b/326509341): Remove when downstream consumers migrate.
97   template <typename T>
NewArray(size_t count)98   T* NewArray(size_t count) {
99     return New<T[]>(count, alignof(T));
100   }
101 
102   /// Deprecated version of `New` with a different name and templated on
103   /// the object type instead of the array type.
104   /// Do not use this method. It will be removed.
105   /// TODO(b/326509341): Remove when downstream consumers migrate.
106   template <typename T>
NewArray(size_t count,size_t alignment)107   T* NewArray(size_t count, size_t alignment) {
108     return New<T[]>(count, alignment);
109   }
110 
111   /// Constructs and object of type `T` from the given `args`, and wraps it in a
112   /// `UniquePtr`
113   ///
114   /// The returned value may contain null if allocating memory for the object
115   /// fails. Callers must check for null before using the `UniquePtr`.
116   ///
117   /// @param[in]  args...     Arguments passed to the object constructor.
118   template <typename T,
119             int&... kExplicitGuard,
120             std::enable_if_t<!std::is_array_v<T>, int> = 0,
121             typename... Args>
MakeUnique(Args &&...args)122   [[nodiscard]] UniquePtr<T> MakeUnique(Args&&... args) {
123     return Deallocator::WrapUnique<T>(New<T>(std::forward<Args>(args)...));
124   }
125 
126   /// Constructs an `alignment`-byte aligned array of `count` objects, and wraps
127   /// it in a `UniquePtr`
128   ///
129   /// The returned value may contain null if allocating memory for the object
130   /// fails. Callers must check for null before using the `UniquePtr`.
131   ///
132   /// @tparam     T            An array type.
133   /// @param[in]  count        Number of objects to allocate.
134   template <typename T,
135             int&... kExplicitGuard,
136             std::enable_if_t<is_unbounded_array_v<T>, int> = 0>
MakeUnique(size_t size)137   [[nodiscard]] UniquePtr<T> MakeUnique(size_t size) {
138     return MakeUnique<T>(size, alignof(std::remove_extent_t<T>));
139   }
140 
141   /// Constructs an `alignment`-byte aligned array of `count` objects of type
142   /// `T`, and wraps it in a `UniquePtr`
143   ///
144   /// The returned value may contain null if allocating memory for the object
145   /// fails. Callers must check for null before using the `UniquePtr`.
146   ///
147   /// @tparam     T            An array type.
148   /// @param[in]  count        Number of objects to allocate.
149   /// @param[in]  alignment    Object alignment.
150   template <typename T,
151             int&... kExplicitGuard,
152             std::enable_if_t<is_unbounded_array_v<T>, int> = 0>
MakeUnique(size_t size,size_t alignment)153   [[nodiscard]] UniquePtr<T> MakeUnique(size_t size, size_t alignment) {
154     return Deallocator::WrapUnique<T>(New<T>(size, alignment), size);
155   }
156 
157   /// Deprecated version of `MakeUnique` with a different name and templated on
158   /// the object type instead of the array type.
159   /// Do not use this method. It will be removed.
160   /// TODO(b/326509341): Remove when downstream consumers migrate.
161   template <typename T>
MakeUniqueArray(size_t size)162   [[nodiscard]] UniquePtr<T[]> MakeUniqueArray(size_t size) {
163     return MakeUnique<T[]>(size, alignof(std::remove_extent_t<T>));
164   }
165 
166   /// Deprecated version of `MakeUnique` with a different name and templated on
167   /// the object type instead of the array type.
168   /// Do not use this method. It will be removed.
169   /// TODO(b/326509341): Remove when downstream consumers migrate.
170   template <typename T>
MakeUniqueArray(size_t size,size_t alignment)171   [[nodiscard]] UniquePtr<T[]> MakeUniqueArray(size_t size, size_t alignment) {
172     return MakeUnique<T[]>(size, alignment);
173   }
174 
175   template <typename T,
176             int&... kExplicitGuard,
177             std::enable_if_t<is_bounded_array_v<T>, int> = 0,
178             typename... Args>
179   void MakeUnique(Args&&...) = delete;
180 
181   /// Modifies the size of an previously-allocated block of memory without
182   /// copying any data.
183   ///
184   /// Returns true if its size was changed without copying data to a new
185   /// allocation; otherwise returns false.
186   ///
187   /// In particular, it always returns true if the `old_layout.size()` equals
188   /// `new_size`, and always returns false if the given pointer is null, the
189   /// `old_layout.size()` is 0, or the `new_size` is 0.
190   ///
191   /// @param[in]  ptr           Pointer to previously-allocated memory.
192   /// @param[in]  new_size      Requested new size for the memory allocation.
Resize(void * ptr,size_t new_size)193   bool Resize(void* ptr, size_t new_size) {
194     return ptr != nullptr && new_size != 0 && DoResize(ptr, new_size);
195   }
196 
197   /// Deprecated version of `Resize` that takes a `Layout`.
198   /// Do not use this method. It will be removed.
199   /// TODO(b/326509341): Remove when downstream consumers migrate.
Resize(void * ptr,Layout layout,size_t new_size)200   bool Resize(void* ptr, Layout layout, size_t new_size) {
201     return ptr != nullptr && new_size != 0 && DoResize(ptr, layout, new_size);
202   }
203 
204   /// Modifies the size of a previously-allocated block of memory.
205   ///
206   /// Returns pointer to the modified block of memory, or `nullptr` if the
207   /// memory could not be modified.
208   ///
209   /// The data stored by the memory being modified must be trivially
210   /// copyable. If it is not, callers should themselves attempt to `Resize`,
211   /// then `Allocate`, move the data, and `Deallocate` as needed.
212   ///
213   /// If `nullptr` is returned, the block of memory is unchanged. In particular,
214   /// if the `new_layout` has a size of 0, the given pointer will NOT be
215   /// deallocated.
216   ///
217   /// TODO(b/331290408): This error condition needs to be better communicated to
218   /// module users, who may assume the pointer is freed.
219   ///
220   /// Unlike `Resize`, providing a null pointer will return a new allocation.
221   ///
222   /// If the request can be satisfied using `Resize`, the `alignment` parameter
223   /// may be ignored.
224   ///
225   /// @param[in]  ptr         Pointer to previously-allocated memory.
226   /// @param[in]  new_layout  Describes the memory to be allocated.
Reallocate(void * ptr,Layout new_layout)227   void* Reallocate(void* ptr, Layout new_layout) {
228     if (new_layout.size() == 0) {
229       return nullptr;
230     }
231     if (ptr == nullptr) {
232       return Allocate(new_layout);
233     }
234     return DoReallocate(ptr, new_layout);
235   }
236 
237   /// Deprecated version of `Reallocate` that takes a `Layout`.
238   /// Do not use this method. It will be removed.
239   /// TODO(b/326509341): Remove when downstream consumers migrate.
Reallocate(void * ptr,Layout old_layout,size_t new_size)240   void* Reallocate(void* ptr, Layout old_layout, size_t new_size) {
241     if (new_size == 0) {
242       return nullptr;
243     }
244     if (ptr == nullptr) {
245       return Allocate(Layout(new_size, old_layout.alignment()));
246     }
247     return DoReallocate(ptr, old_layout, new_size);
248   }
249 
250   /// Returns the total bytes that have been allocated by this allocator, or
251   /// `size_t(-1)` if this allocator does not track its total allocated bytes.
GetAllocated()252   size_t GetAllocated() const { return DoGetAllocated(); }
253 
254  protected:
255   /// TODO(b/326509341): Remove when downstream consumers migrate.
256   constexpr Allocator() = default;
257 
Allocator(const Capabilities & capabilities)258   explicit constexpr Allocator(const Capabilities& capabilities)
259       : Deallocator(capabilities) {}
260 
261  private:
262   /// Virtual `Allocate` function implemented by derived classes.
263   ///
264   /// @param[in]  layout        Describes the memory to be allocated. Guaranteed
265   ///                           to have a non-zero size.
266   virtual void* DoAllocate(Layout layout) = 0;
267 
268   /// Virtual `Resize` function implemented by derived classes.
269   ///
270   /// The default implementation simply returns `false`, indicating that
271   /// resizing is not supported.
272   ///
273   /// @param[in]  ptr           Pointer to memory, guaranteed to not be null.
274   /// @param[in]  new_size      Requested size, guaranteed to be non-zero..
DoResize(void *,size_t)275   virtual bool DoResize(void* /*ptr*/, size_t /*new_size*/) { return false; }
276 
277   /// Deprecated version of `DoResize` that takes a `Layout`.
278   /// Do not use this method. It will be removed.
279   /// TODO(b/326509341): Remove when downstream consumers migrate.
DoResize(void *,Layout,size_t)280   virtual bool DoResize(void*, Layout, size_t) { return false; }
281 
282   /// Virtual `Reallocate` function that can be overridden by derived classes.
283   ///
284   /// The default implementation will first try to `Resize` the data. If that is
285   /// unsuccessful, it will allocate an entirely new block, copy existing data,
286   /// and deallocate the given block.
287   ///
288   /// @param[in]  ptr           Pointer to memory, guaranteed to not be null.
289   /// @param[in]  new_layout    Describes the memory to be allocated. Guaranteed
290   ///                           to have a non-zero size.
291   virtual void* DoReallocate(void* ptr, Layout new_layout);
292 
293   /// Deprecated version of `DoReallocate` that takes a `Layout`.
294   /// Do not use this method. It will be removed.
295   /// TODO(b/326509341): Remove when downstream consumers migrate.
296   virtual void* DoReallocate(void* ptr, Layout old_layout, size_t new_size);
297 
298   /// Virtual `GetAllocated` function that can be overridden by derived classes.
299   ///
300   /// The default implementation simply returns `size_t(-1)`, indicating that
301   /// tracking total allocated bytes is not supported.
DoGetAllocated()302   virtual size_t DoGetAllocated() const { return size_t(-1); }
303 };
304 
305 namespace allocator {
306 
307 // Alias for module consumers using the older name for the above type.
308 using Allocator = ::pw::Allocator;
309 
310 }  // namespace allocator
311 }  // namespace pw
312