xref: /aosp_15_r20/external/pigweed/pw_allocator/public/pw_allocator/deallocator.h (revision 61c4878ac05f98d0ceed94b57d316916de578985)
1 // Copyright 2024 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/capability.h"
17 #include "pw_allocator/layout.h"
18 #include "pw_allocator/unique_ptr.h"
19 #include "pw_result/result.h"
20 #include "pw_status/status.h"
21 #include "pw_status/status_with_size.h"
22 
23 namespace pw {
24 
25 /// Abstract interface for releasing memory.
26 class Deallocator {
27  public:
28   using Capabilities = allocator::Capabilities;
29   using Capability = allocator::Capability;
30   using Layout = allocator::Layout;
31 
32   virtual ~Deallocator() = default;
33 
capabilities()34   const Capabilities& capabilities() const { return capabilities_; }
35 
36   /// Returns whether a given capabilityis enabled for this object.
HasCapability(Capability capability)37   bool HasCapability(Capability capability) const {
38     return capabilities_.has(capability);
39   }
40 
41   /// Releases a previously-allocated block of memory.
42   ///
43   /// The given pointer must have been previously provided by this memory
44   /// resource; otherwise the behavior is undefined.
45   ///
46   /// @param[in]  ptr           Pointer to previously-allocated memory.
Deallocate(void * ptr)47   void Deallocate(void* ptr) {
48     if (ptr != nullptr) {
49       DoDeallocate(ptr);
50     }
51   }
52 
53   /// Deprecated version of `Deallocate` that takes a `Layout`.
54   /// Do not use this method. It will be removed.
55   /// TODO(b/326509341): Remove when downstream consumers migrate.
Deallocate(void * ptr,Layout layout)56   void Deallocate(void* ptr, Layout layout) {
57     if (ptr != nullptr) {
58       DoDeallocate(ptr, layout);
59     }
60   }
61 
62   /// Destroys the object at ``ptr`` and deallocates the associated memory.
63   ///
64   /// The given pointer must have been previously obtained from a call to
65   /// ``New`` using the same object; otherwise the behavior is undefined.
66   ///
67   /// @param[in] ptr      Pointer to previously-allocated object.
68   template <typename T>
Delete(T * ptr)69   void Delete(T* ptr) {
70     if (!capabilities_.has(Capability::kSkipsDestroy)) {
71       std::destroy_at(ptr);
72     }
73     Deallocate(ptr);
74   }
75 
76   /// Returns the total amount of memory provided by this object.
77   ///
78   /// This is an optional method. Some memory providers may not have an easily
79   /// defined capacity, e.g. the system allocator. If implemented, the returned
80   /// capacity may be less than the memory originally given to an allocator,
81   /// e.g. if the allocator must align the region of memory, its capacity may be
82   /// reduced.
GetCapacity()83   StatusWithSize GetCapacity() const {
84     auto result = DoGetInfo(InfoType::kCapacity, nullptr);
85     return StatusWithSize(result.status(), Layout::Unwrap(result).size());
86   }
87 
88   /// Returns whether the given object is the same as this one.
89   ///
90   /// This method is used instead of ``operator==`` in keeping with
91   /// ``std::pmr::memory_resource::is_equal``. There currently is no
92   /// corresponding virtual ``DoIsEqual``, as objects that would
93   /// require ``dynamic_cast`` to properly determine equality are not supported.
94   /// This method will allow the interface to remain unchanged should a future
95   /// need for such objects arise.
96   ///
97   /// @param[in]  other       Object to compare with this object.
IsEqual(const Deallocator & other)98   bool IsEqual(const Deallocator& other) const { return this == &other; }
99 
100  protected:
101   /// TODO(b/326509341): Remove when downstream consumers migrate.
102   constexpr Deallocator() = default;
103 
Deallocator(const Capabilities & capabilities)104   explicit constexpr Deallocator(const Capabilities& capabilities)
105       : capabilities_(capabilities) {}
106 
107   /// Wraps an object of type ``T`` in a ``UniquePtr``
108   ///
109   /// @param[in]  ptr         Pointer to memory provided by this object.
110   template <typename T>
WrapUnique(T * ptr)111   [[nodiscard]] UniquePtr<T> WrapUnique(T* ptr) {
112     return UniquePtr<T>(UniquePtr<T>::kPrivateConstructor, ptr, this);
113   }
114 
115   /// Wraps an array of type ``T`` in a ``UniquePtr``
116   ///
117   /// @param[in]  ptr         Pointer to memory provided by this object.
118   /// @param[in]  size        The size of the array.
119   template <typename T>
WrapUniqueArray(T * ptr,size_t size)120   [[nodiscard]] UniquePtr<T[]> WrapUniqueArray(T* ptr, size_t size) {
121     return UniquePtr<T[]>(UniquePtr<T[]>::kPrivateConstructor, ptr, this, size);
122   }
123 
124   /// Indicates what kind of information to retrieve using `GetInfo`.
125   ///
126   /// Note that this enum is considered open, and may be extended in the future.
127   /// As a result, implementers of `DoGetInfo` should include a default case
128   /// that handles unrecognized info types. If building with `-Wswitch-enum`,
129   /// you will also want to locally disable that diagnostic and build with
130   /// `-Wswitch-default` instead, e.g.:
131   ///
132   /// @code{.cpp}
133   /// class MyAllocator : public Allocator {
134   ///  private:
135   ///   Layout MyGetLayoutFromPointer(const void* ptr) { /* ... */ }
136   ///
137   ///   Result<Layout> DoGetInfo(InfoType info_type, const void* ptr) override {
138   ///     PW_MODIFY_DIAGNOSTICS_PUSH();
139   ///     PW_MODIFY_DIAGNOSTIC(ignored, "-Wswitch-enum");
140   ///     switch(info_type) {
141   ///       case InfoType::kAllocatedLayoutOf:
142   ///         return MyGetLayoutFromPointer(ptr);
143   ///       default:
144   ///         return Status::Unimplmented();
145   ///     }
146   ///     PW_MODIFY_DIAGNOSTICS_POP();
147   ///   }
148   /// };
149   /// @endcode
150   ///
151   /// See also `GetInfo`.
152   enum class InfoType {
153     /// If supported, `GetInfo` will return `OK` with the `Layout` of the
154     /// requested memory associated with the given pointer, or `NOT_FOUND` if
155     /// the pointer is not recognized.
156     ///
157     /// The requested layout may differ from either the layout of usable memory,
158     /// the layout of memory used to fulfill the request, or both.
159     ///
160     /// For example, it may have a smaller size than the usable memory if the
161     /// latter was padded to an alignment boundary, or may have a less strict
162     /// alignment than the actual memory.
163     kRequestedLayoutOf,
164 
165     /// If supported, `GetInfo` will return `OK` with the `Layout` of the
166     /// usable memory associated with the given pointer, or `NOT_FOUND` if
167     /// the pointer is not recognized.
168 
169     /// The usable layout may from either the requested layout, the layout of
170     /// memory used to fulfill the request, or both.
171     ///
172     /// For example, it may have a larger size than the requested layout if it
173     /// was padded to an alignment boundary, but may be less than the acutal
174     /// memory if the object includes some overhead for metadata.
175     kUsableLayoutOf,
176 
177     /// If supported, `GetInfo` will return `OK` with the `Layout` of the
178     /// allocated memory associated with the given pointer, or `NOT_FOUND` if
179     /// the pointer is not recognized.
180     ///
181     /// The layout of memory used to fulfill a request may differ from either
182     /// the requested layout, the layout of the usable memory, or both.
183     ///
184     /// For example, it may have a larger size than the requested layout or the
185     /// layout of usable memory if the object includes some overhead for
186     /// metadata.
187     kAllocatedLayoutOf,
188 
189     /// If supported, `GetInfo` will return `OK` with a `Layout` whose size
190     /// is the total number of bytes that can be allocated by this object, and
191     /// whose alignment is the minimum alignment of any allocation.
192     ///
193     /// The given pointer is ignored.
194     kCapacity,
195 
196     /// If supported, `GetInfo` will return `OK` with a default `Layout` if the
197     /// given pointer was provided by this object, or `NOT_FOUND`.
198     ///
199     /// This MUST only be used to dispatch between two or more objects
200     /// associated with non-overlapping regions of memory. Do NOT use it to
201     /// determine if this object can deallocate pointers. Callers MUST only
202     /// deallocate memory using the same ``Deallocator`` that provided it.
203     kRecognizes,
204   };
205 
206   /// Returns deallocator-specific information about allocations.
207   ///
208   /// Deallocators may support any number of `InfoType`s. See that type for what
209   /// each supported type returns. For unsupported types, this method returns
210   /// `UNIMPLEMENTED`.
GetInfo(InfoType info_type,const void * ptr)211   Result<Layout> GetInfo(InfoType info_type, const void* ptr) const {
212     return DoGetInfo(info_type, ptr);
213   }
214 
215   /// @copydoc GetInfo
216   ///
217   /// This method is protected in order to restrict it to object
218   /// implementations. It is static and takes an ``deallocator`` parameter in
219   /// order to allow forwarding allocators to call it on wrapped allocators.
GetInfo(const Deallocator & deallocator,InfoType info_type,const void * ptr)220   static Result<Layout> GetInfo(const Deallocator& deallocator,
221                                 InfoType info_type,
222                                 const void* ptr) {
223     return deallocator.DoGetInfo(info_type, ptr);
224   }
225 
226   /// Convenience wrapper of `DoGetInfo` for getting the requested layout
227   /// associated with a pointer.
GetRequestedLayout(const void * ptr)228   Result<Layout> GetRequestedLayout(const void* ptr) const {
229     return DoGetInfo(InfoType::kRequestedLayoutOf, ptr);
230   }
231 
232   /// Static version of `GetRequestedLayout` that allows forwarding allocators
233   /// to call it on wrapped allocators.
GetRequestedLayout(const Deallocator & deallocator,const void * ptr)234   static Result<Layout> GetRequestedLayout(const Deallocator& deallocator,
235                                            const void* ptr) {
236     return deallocator.GetRequestedLayout(ptr);
237   }
238 
239   /// Convenience wrapper of `DoGetInfo` for getting the usable layout
240   /// associated with a pointer.
GetUsableLayout(const void * ptr)241   Result<Layout> GetUsableLayout(const void* ptr) const {
242     return DoGetInfo(InfoType::kUsableLayoutOf, ptr);
243   }
244 
245   /// Static version of `GetUsableLayout` that allows forwarding allocators to
246   /// call it on wrapped allocators.
GetUsableLayout(const Deallocator & deallocator,const void * ptr)247   static Result<Layout> GetUsableLayout(const Deallocator& deallocator,
248                                         const void* ptr) {
249     return deallocator.GetUsableLayout(ptr);
250   }
251 
252   /// Convenience wrapper of `DoGetInfo` for getting the allocated layout
253   /// associated with a pointer.
GetAllocatedLayout(const void * ptr)254   Result<Layout> GetAllocatedLayout(const void* ptr) const {
255     return DoGetInfo(InfoType::kAllocatedLayoutOf, ptr);
256   }
257 
258   /// Static version of `GetAllocatedLayout` that allows forwarding allocators
259   /// to call it on wrapped allocators.
GetAllocatedLayout(const Deallocator & deallocator,const void * ptr)260   static Result<Layout> GetAllocatedLayout(const Deallocator& deallocator,
261                                            const void* ptr) {
262     return deallocator.GetAllocatedLayout(ptr);
263   }
264 
265   /// Convenience wrapper of `DoGetInfo` for getting whether the allocator
266   /// recognizes a pointer.
Recognizes(const void * ptr)267   bool Recognizes(const void* ptr) const {
268     return DoGetInfo(InfoType::kRecognizes, ptr).ok();
269   }
270 
271   /// Static version of `Recognizes` that allows forwarding allocators to call
272   /// it on wrapped allocators.
Recognizes(const Deallocator & deallocator,const void * ptr)273   static bool Recognizes(const Deallocator& deallocator, const void* ptr) {
274     return deallocator.Recognizes(ptr);
275   }
276 
277  private:
278   /// Virtual `Deallocate` function implemented by derived classes.
279   ///
280   /// @param[in]  ptr           Pointer to memory, guaranteed to not be null.
DoDeallocate(void *)281   virtual void DoDeallocate(void*) {
282     // This method will be pure virtual once consumer migrate from the deprected
283     // version that takes a `Layout` parameter. In the meantime, the check that
284     // this method is implemented is deferred to run-time.
285     PW_ASSERT(false);
286   }
287 
288   /// Deprecated version of `DoDeallocate` that takes a `Layout`.
289   /// Do not use this method. It will be removed.
DoDeallocate(void * ptr,Layout)290   virtual void DoDeallocate(void* ptr, Layout) { DoDeallocate(ptr); }
291 
292   /// Virtual `GetInfo` function that can be overridden by derived classes.
DoGetInfo(InfoType,const void *)293   virtual Result<Layout> DoGetInfo(InfoType, const void*) const {
294     return Status::Unimplemented();
295   }
296 
297   const Capabilities capabilities_;
298 };
299 
300 namespace allocator {
301 
302 // Alias for module consumers using the older name for the above type.
303 using Deallocator = ::pw::Deallocator;
304 
305 }  // namespace allocator
306 }  // namespace pw
307