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