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/as_pmr_allocator.h" 19 #include "pw_allocator/capability.h" 20 #include "pw_allocator/deallocator.h" 21 #include "pw_allocator/layout.h" 22 #include "pw_allocator/unique_ptr.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 and 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&... ExplicitGuard, typename... Args> New(Args &&...args)52 T* New(Args&&... args) { 53 void* ptr = Allocate(Layout::Of<T>()); 54 return ptr != nullptr ? new (ptr) T(std::forward<Args>(args)...) : nullptr; 55 } 56 57 /// Constructs and object of type `T` from the given `args`, and wraps it in a 58 /// `UniquePtr` 59 /// 60 /// The returned value may contain null if allocating memory for the object 61 /// fails. Callers must check for null before using the `UniquePtr`. 62 /// 63 /// @param[in] args... Arguments passed to the object constructor. 64 template <typename T, int&... ExplicitGuard, typename... Args> MakeUnique(Args &&...args)65 [[nodiscard]] UniquePtr<T> MakeUnique(Args&&... args) { 66 return Deallocator::WrapUnique<T>(New<T>(std::forward<Args>(args)...)); 67 } 68 69 /// Modifies the size of an previously-allocated block of memory without 70 /// copying any data. 71 /// 72 /// Returns true if its size was changed without copying data to a new 73 /// allocation; otherwise returns false. 74 /// 75 /// In particular, it always returns true if the `old_layout.size()` equals 76 /// `new_size`, and always returns false if the given pointer is null, the 77 /// `old_layout.size()` is 0, or the `new_size` is 0. 78 /// 79 /// @param[in] ptr Pointer to previously-allocated memory. 80 /// @param[in] new_size Requested new size for the memory allocation. Resize(void * ptr,size_t new_size)81 bool Resize(void* ptr, size_t new_size) { 82 return ptr != nullptr && new_size != 0 && DoResize(ptr, new_size); 83 } 84 85 /// Deprecated version of `Resize` that takes a `Layout`. 86 /// Do not use this method. It will be removed. 87 /// TODO(b/326509341): Remove when downstream consumers migrate. Resize(void * ptr,Layout layout,size_t new_size)88 bool Resize(void* ptr, Layout layout, size_t new_size) { 89 return ptr != nullptr && new_size != 0 && DoResize(ptr, layout, new_size); 90 } 91 92 /// Modifies the size of a previously-allocated block of memory. 93 /// 94 /// Returns pointer to the modified block of memory, or `nullptr` if the 95 /// memory could not be modified. 96 /// 97 /// The data stored by the memory being modified must be trivially 98 /// copyable. If it is not, callers should themselves attempt to `Resize`, 99 /// then `Allocate`, move the data, and `Deallocate` as needed. 100 /// 101 /// If `nullptr` is returned, the block of memory is unchanged. In particular, 102 /// if the `new_layout` has a size of 0, the given pointer will NOT be 103 /// deallocated. 104 /// 105 /// TODO(b/331290408): This error condition needs to be better communicated to 106 /// module users, who may assume the pointer is freed. 107 /// 108 /// Unlike `Resize`, providing a null pointer will return a new allocation. 109 /// 110 /// If the request can be satisfied using `Resize`, the `alignment` parameter 111 /// may be ignored. 112 /// 113 /// @param[in] ptr Pointer to previously-allocated memory. 114 /// @param[in] new_layout Describes the memory to be allocated. Reallocate(void * ptr,Layout new_layout)115 void* Reallocate(void* ptr, Layout new_layout) { 116 if (new_layout.size() == 0) { 117 return nullptr; 118 } 119 if (ptr == nullptr) { 120 return Allocate(new_layout); 121 } 122 return DoReallocate(ptr, new_layout); 123 } 124 125 /// Deprecated version of `Reallocate` that takes a `Layout`. 126 /// Do not use this method. It will be removed. 127 /// TODO(b/326509341): Remove when downstream consumers migrate. Reallocate(void * ptr,Layout old_layout,size_t new_size)128 void* Reallocate(void* ptr, Layout old_layout, size_t new_size) { 129 if (new_size == 0) { 130 return nullptr; 131 } 132 if (ptr == nullptr) { 133 return Allocate(Layout(new_size, old_layout.alignment())); 134 } 135 return DoReallocate(ptr, old_layout, new_size); 136 } 137 138 /// Returns an std::pmr::polymorphic_allocator that wraps this object. 139 /// 140 /// The returned object can be used with the PMR versions of standard library 141 /// containers, e.g. `std::pmr::vector`, `std::pmr::string`, etc. 142 /// 143 /// @rst 144 /// See also :ref:`module-pw_allocator-use-standard-library-containers` 145 /// @endrst as_pmr()146 allocator::AsPmrAllocator as_pmr() { 147 return allocator::AsPmrAllocator(*this); 148 } 149 150 protected: 151 /// TODO(b/326509341): Remove when downstream consumers migrate. 152 constexpr Allocator() = default; 153 Allocator(const Capabilities & capabilities)154 explicit constexpr Allocator(const Capabilities& capabilities) 155 : Deallocator(capabilities) {} 156 157 private: 158 /// Virtual `Allocate` function implemented by derived classes. 159 /// 160 /// @param[in] layout Describes the memory to be allocated. Guaranteed 161 /// to have a non-zero size. 162 virtual void* DoAllocate(Layout layout) = 0; 163 164 /// Virtual `Resize` function implemented by derived classes. 165 /// 166 /// The default implementation simply returns `false`, indicating that 167 /// resizing is not supported. 168 /// 169 /// @param[in] ptr Pointer to memory, guaranteed to not be null. 170 /// @param[in] new_size Requested size, guaranteed to be non-zero.. DoResize(void *,size_t)171 virtual bool DoResize(void* /*ptr*/, size_t /*new_size*/) { return false; } 172 173 /// Deprecated version of `DoResize` that takes a `Layout`. 174 /// Do not use this method. It will be removed. 175 /// TODO(b/326509341): Remove when downstream consumers migrate. DoResize(void *,Layout,size_t)176 virtual bool DoResize(void*, Layout, size_t) { return false; } 177 178 /// Virtual `Reallocate` function that can be overridden by derived classes. 179 /// 180 /// The default implementation will first try to `Resize` the data. If that is 181 /// unsuccessful, it will allocate an entirely new block, copy existing data, 182 /// and deallocate the given block. 183 /// 184 /// @param[in] ptr Pointer to memory, guaranteed to not be null. 185 /// @param[in] new_layout Describes the memory to be allocated. Guaranteed 186 /// to have a non-zero size. 187 virtual void* DoReallocate(void* ptr, Layout new_layout); 188 189 /// Deprecated version of `DoReallocate` that takes a `Layout`. 190 /// Do not use this method. It will be removed. 191 /// TODO(b/326509341): Remove when downstream consumers migrate. 192 virtual void* DoReallocate(void* ptr, Layout old_layout, size_t new_size); 193 }; 194 195 namespace allocator { 196 197 // Alias for module consumers using the older name for the above type. 198 using Allocator = ::pw::Allocator; 199 200 } // namespace allocator 201 } // namespace pw 202