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