• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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   /// Indicates what kind of information to retrieve using `GetInfo`.
116   ///
117   /// Note that this enum is considered open, and may be extended in the future.
118   /// As a result, implementers of `DoGetInfo` should include a default case
119   /// that handles unrecognized info types. If building with `-Wswitch-enum`,
120   /// you will also want to locally disable that diagnostic and build with
121   /// `-Wswitch-default` instead, e.g.:
122   ///
123   /// @code{.cpp}
124   /// class MyAllocator : public Allocator {
125   ///  private:
126   ///   Layout MyGetLayoutFromPointer(const void* ptr) { /* ... */ }
127   ///
128   ///   Result<Layout> DoGetInfo(InfoType info_type, const void* ptr) override {
129   ///     PW_MODIFY_DIAGNOSTICS_PUSH();
130   ///     PW_MODIFY_DIAGNOSTIC(ignored, "-Wswitch-enum");
131   ///     switch(info_type) {
132   ///       case InfoType::kAllocatedLayoutOf:
133   ///         return MyGetLayoutFromPointer(ptr);
134   ///       default:
135   ///         return Status::Unimplmented();
136   ///     }
137   ///     PW_MODIFY_DIAGNOSTICS_POP();
138   ///   }
139   /// };
140   /// @endcode
141   ///
142   /// See also `GetInfo`.
143   enum class InfoType {
144     /// If supported, `GetInfo` will return `OK` with the `Layout` of the
145     /// requested memory associated with the given pointer, or `NOT_FOUND` if
146     /// the pointer is not recognized.
147     ///
148     /// The requested layout may differ from either the layout of usable memory,
149     /// the layout of memory used to fulfill the request, or both.
150     ///
151     /// For example, it may have a smaller size than the usable memory if the
152     /// latter was padded to an alignment boundary, or may have a less strict
153     /// alignment than the actual memory.
154     kRequestedLayoutOf,
155 
156     /// If supported, `GetInfo` will return `OK` with the `Layout` of the
157     /// usable memory associated with the given pointer, or `NOT_FOUND` if
158     /// the pointer is not recognized.
159 
160     /// The usable layout may from either the requested layout, the layout of
161     /// memory used to fulfill the request, or both.
162     ///
163     /// For example, it may have a larger size than the requested layout if it
164     /// was padded to an alignment boundary, but may be less than the acutal
165     /// memory if the object includes some overhead for metadata.
166     kUsableLayoutOf,
167 
168     /// If supported, `GetInfo` will return `OK` with the `Layout` of the
169     /// allocated memory associated with the given pointer, or `NOT_FOUND` if
170     /// the pointer is not recognized.
171     ///
172     /// The layout of memory used to fulfill a request may differ from either
173     /// the requested layout, the layout of the usable memory, or both.
174     ///
175     /// For example, it may have a larger size than the requested layout or the
176     /// layout of usable memory if the object includes some overhead for
177     /// metadata.
178     kAllocatedLayoutOf,
179 
180     /// If supported, `GetInfo` will return `OK` with a `Layout` whose size
181     /// is the total number of bytes that can be allocated by this object, and
182     /// whose alignment is the minimum alignment of any allocation.
183     ///
184     /// The given pointer is ignored.
185     kCapacity,
186 
187     /// If supported, `GetInfo` will return `OK` with a default `Layout` if the
188     /// given pointer was provided by this object, or `NOT_FOUND`.
189     ///
190     /// This MUST only be used to dispatch between two or more objects
191     /// associated with non-overlapping regions of memory. Do NOT use it to
192     /// determine if this object can deallocate pointers. Callers MUST only
193     /// deallocate memory using the same ``Deallocator`` that provided it.
194     kRecognizes,
195   };
196 
197   /// Returns deallocator-specific information about allocations.
198   ///
199   /// Deallocators may support any number of `InfoType`s. See that type for what
200   /// each supported type returns. For unsupported types, this method returns
201   /// `UNIMPLEMENTED`.
GetInfo(InfoType info_type,const void * ptr)202   Result<Layout> GetInfo(InfoType info_type, const void* ptr) const {
203     return DoGetInfo(info_type, ptr);
204   }
205 
206   /// @copydoc GetInfo
207   ///
208   /// This method is protected in order to restrict it to object
209   /// implementations. It is static and takes an ``deallocator`` parameter in
210   /// order to allow forwarding allocators to call it on wrapped allocators.
GetInfo(const Deallocator & deallocator,InfoType info_type,const void * ptr)211   static Result<Layout> GetInfo(const Deallocator& deallocator,
212                                 InfoType info_type,
213                                 const void* ptr) {
214     return deallocator.DoGetInfo(info_type, ptr);
215   }
216 
217   /// Convenience wrapper of `DoGetInfo` for getting the requested layout
218   /// associated with a pointer.
GetRequestedLayout(const void * ptr)219   Result<Layout> GetRequestedLayout(const void* ptr) const {
220     return DoGetInfo(InfoType::kRequestedLayoutOf, ptr);
221   }
222 
223   /// Static version of `GetRequestedLayout` that allows forwarding allocators
224   /// to call it on wrapped allocators.
GetRequestedLayout(const Deallocator & deallocator,const void * ptr)225   static Result<Layout> GetRequestedLayout(const Deallocator& deallocator,
226                                            const void* ptr) {
227     return deallocator.GetRequestedLayout(ptr);
228   }
229 
230   /// Convenience wrapper of `DoGetInfo` for getting the usable layout
231   /// associated with a pointer.
GetUsableLayout(const void * ptr)232   Result<Layout> GetUsableLayout(const void* ptr) const {
233     return DoGetInfo(InfoType::kUsableLayoutOf, ptr);
234   }
235 
236   /// Static version of `GetUsableLayout` that allows forwarding allocators to
237   /// call it on wrapped allocators.
GetUsableLayout(const Deallocator & deallocator,const void * ptr)238   static Result<Layout> GetUsableLayout(const Deallocator& deallocator,
239                                         const void* ptr) {
240     return deallocator.GetUsableLayout(ptr);
241   }
242 
243   /// Convenience wrapper of `DoGetInfo` for getting the allocated layout
244   /// associated with a pointer.
GetAllocatedLayout(const void * ptr)245   Result<Layout> GetAllocatedLayout(const void* ptr) const {
246     return DoGetInfo(InfoType::kAllocatedLayoutOf, ptr);
247   }
248 
249   /// Static version of `GetAllocatedLayout` that allows forwarding allocators
250   /// to call it on wrapped allocators.
GetAllocatedLayout(const Deallocator & deallocator,const void * ptr)251   static Result<Layout> GetAllocatedLayout(const Deallocator& deallocator,
252                                            const void* ptr) {
253     return deallocator.GetAllocatedLayout(ptr);
254   }
255 
256   /// Convenience wrapper of `DoGetInfo` for getting whether the allocator
257   /// recognizes a pointer.
Recognizes(const void * ptr)258   bool Recognizes(const void* ptr) const {
259     return DoGetInfo(InfoType::kRecognizes, ptr).ok();
260   }
261 
262   /// Static version of `Recognizes` that allows forwarding allocators to call
263   /// it on wrapped allocators.
Recognizes(const Deallocator & deallocator,const void * ptr)264   static bool Recognizes(const Deallocator& deallocator, const void* ptr) {
265     return deallocator.Recognizes(ptr);
266   }
267 
268  private:
269   /// Virtual `Deallocate` function implemented by derived classes.
270   ///
271   /// @param[in]  ptr           Pointer to memory, guaranteed to not be null.
DoDeallocate(void *)272   virtual void DoDeallocate(void*) {
273     // This method will be pure virtual once consumer migrate from the deprected
274     // version that takes a `Layout` parameter. In the meantime, the check that
275     // this method is implemented is deferred to run-time.
276     PW_ASSERT(false);
277   }
278 
279   /// Deprecated version of `DoDeallocate` that takes a `Layout`.
280   /// Do not use this method. It will be removed.
DoDeallocate(void * ptr,Layout)281   virtual void DoDeallocate(void* ptr, Layout) { DoDeallocate(ptr); }
282 
283   /// Virtual `GetInfo` function that can be overridden by derived classes.
DoGetInfo(InfoType,const void *)284   virtual Result<Layout> DoGetInfo(InfoType, const void*) const {
285     return Status::Unimplemented();
286   }
287 
288   const Capabilities capabilities_;
289 };
290 
291 namespace allocator {
292 
293 // Alias for module consumers using the older name for the above type.
294 using Deallocator = ::pw::Deallocator;
295 
296 }  // namespace allocator
297 }  // namespace pw
298