Line data Source code
1 : /* 2 : * 3 : * Copyright (c) 2020 Project CHIP Authors 4 : * All rights reserved. 5 : * 6 : * Licensed under the Apache License, Version 2.0 (the "License"); 7 : * you may not use this file except in compliance with the License. 8 : * You may obtain a copy of the License at 9 : * 10 : * http://www.apache.org/licenses/LICENSE-2.0 11 : * 12 : * Unless required by applicable law or agreed to in writing, software 13 : * distributed under the License is distributed on an "AS IS" BASIS, 14 : * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 : * See the License for the specific language governing permissions and 16 : * limitations under the License. 17 : */ 18 : 19 : /** 20 : * @file 21 : * Defines scoped auto-free buffers for CHIP. 22 : * 23 : */ 24 : 25 : #pragma once 26 : 27 : #include <lib/support/CHIPMem.h> 28 : #include <lib/support/CodeUtils.h> 29 : 30 : #include <type_traits> 31 : #include <utility> 32 : 33 : namespace chip { 34 : namespace Platform { 35 : 36 : namespace Impl { 37 : 38 : /** 39 : * Represents a memory buffer that is auto-freed in the destructor. 40 : * 41 : * This class uses void* underneath on purpose (rather than a unique_ptr like 42 : * 'Type') and uses templated type on Ptr(). This is to avoid template explosion 43 : * when the buffers are used for different types - only one implementation of 44 : * the class will be stored in flash. 45 : */ 46 : template <class Impl> 47 : class ScopedMemoryBufferBase 48 : { 49 : public: 50 9609 : ScopedMemoryBufferBase() {} 51 : ScopedMemoryBufferBase(const ScopedMemoryBufferBase &) = delete; 52 : ScopedMemoryBufferBase & operator=(const ScopedMemoryBufferBase & other) = delete; 53 : 54 : ScopedMemoryBufferBase(ScopedMemoryBufferBase && other) { *this = std::move(other); } 55 : 56 6039 : ScopedMemoryBufferBase & operator=(ScopedMemoryBufferBase && other) 57 : { 58 6039 : if (this != &other) 59 : { 60 6039 : mBuffer = other.mBuffer; 61 6039 : other.mBuffer = nullptr; 62 : } 63 6039 : return *this; 64 : } 65 : 66 9609 : ~ScopedMemoryBufferBase() { Free(); } 67 : 68 : /** Check if a buffer is valid */ 69 140 : explicit operator bool() const { return mBuffer != nullptr; } 70 5243 : bool operator!() const { return mBuffer == nullptr; } 71 : 72 : /** Release memory used */ 73 19184 : void Free() 74 : { 75 19184 : if (mBuffer == nullptr) 76 : { 77 13399 : return; 78 : } 79 5785 : Impl::MemoryFree(mBuffer); 80 5785 : mBuffer = nullptr; 81 : } 82 : 83 : protected: 84 12983 : void * Ptr() { return mBuffer; } 85 6232 : const void * Ptr() const { return mBuffer; } 86 : 87 : /** 88 : * Releases the underlying buffer. 89 : * 90 : * The buffer stops being managed and will not be auto-freed. 91 : */ 92 : CHECK_RETURN_VALUE void * Release() 93 : { 94 : void * buffer = mBuffer; 95 : mBuffer = nullptr; 96 : return buffer; 97 : } 98 : 99 4148 : void Alloc(size_t size) 100 : { 101 4148 : Free(); 102 4148 : mBuffer = Impl::MemoryAlloc(size); 103 4148 : } 104 : 105 3753 : void Calloc(size_t elementCount, size_t elementSize) 106 : { 107 3753 : Free(); 108 3753 : mBuffer = Impl::MemoryCalloc(elementCount, elementSize); 109 3753 : } 110 : 111 : private: 112 : void * mBuffer = nullptr; 113 : }; 114 : 115 : /** 116 : * Helper class that forwards memory management tasks to Platform::Memory* calls. 117 : */ 118 : class PlatformMemoryManagement 119 : { 120 : public: 121 5785 : static void MemoryFree(void * p) { chip::Platform::MemoryFree(p); } 122 4148 : static void * MemoryAlloc(size_t size) { return chip::Platform::MemoryAlloc(size); } 123 3753 : static void * MemoryCalloc(size_t num, size_t size) { return chip::Platform::MemoryCalloc(num, size); } 124 : }; 125 : 126 : } // namespace Impl 127 : 128 : /** 129 : * Represents a memory buffer allocated using chip::Platform::Memory*Alloc 130 : * methods. 131 : * 132 : * Use for RAII to auto-free after use. 133 : */ 134 : template <typename T, class MemoryManagement = Impl::PlatformMemoryManagement> 135 : class ScopedMemoryBuffer : public Impl::ScopedMemoryBufferBase<MemoryManagement> 136 : { 137 : friend class Impl::ScopedMemoryBufferBase<MemoryManagement>; 138 : 139 : public: 140 : using Base = Impl::ScopedMemoryBufferBase<MemoryManagement>; 141 : 142 : static_assert(std::is_trivially_destructible<T>::value, "Destructors won't get run"); 143 : 144 12687 : T * Get() { return static_cast<T *>(Base::Ptr()); } 145 183 : T & operator[](size_t index) { return Get()[index]; } 146 : 147 6232 : const T * Get() const { return static_cast<const T *>(Base::Ptr()); } 148 0 : const T & operator[](size_t index) const { return Get()[index]; } 149 : 150 : /** 151 : * Releases the underlying buffer. 152 : * 153 : * The buffer stops being managed and will not be auto-freed. 154 : */ 155 : CHECK_RETURN_VALUE T * Release() { return static_cast<T *>(Base::Release()); } 156 : 157 3718 : ScopedMemoryBuffer & Calloc(size_t elementCount) 158 : { 159 3718 : Base::Calloc(elementCount, sizeof(T)); 160 3718 : ExecuteConstructors(elementCount); 161 3718 : return *this; 162 : } 163 : 164 4143 : ScopedMemoryBuffer & Alloc(size_t elementCount) 165 : { 166 4143 : Base::Alloc(elementCount * sizeof(T)); 167 4143 : ExecuteConstructors(elementCount); 168 4143 : return *this; 169 : } 170 : 171 : private: 172 : template <typename U = T, std::enable_if_t<std::is_trivial<U>::value, int> = 0> 173 7546 : void ExecuteConstructors(size_t elementCount) 174 : { 175 : // Do nothing if our type is trivial. In particular, if we are a buffer 176 : // of integers, we should not go zero-initializing them here: either 177 : // caller wants that and called Calloc(), or it doesn't and we shouldn't 178 : // do it. 179 7546 : } 180 : 181 : template <typename U = T, std::enable_if_t<!std::is_trivial<U>::value, int> = 0> 182 : void ExecuteConstructors(size_t elementCount) 183 : { 184 : T * elementPtr = Get(); 185 : if (elementPtr == nullptr) 186 : { 187 : // Alloc failed, don't bother. 188 : return; 189 : } 190 : for (size_t i = 0; i < elementCount; ++i) 191 : { 192 : new (&elementPtr[i]) T(); 193 : } 194 : } 195 : }; 196 : 197 : /** 198 : * Represents a memory buffer with buffer size allocated using chip::Platform::Memory*Alloc 199 : * methods. 200 : * 201 : * Use for RAII to auto-free after use. 202 : */ 203 : template <typename T> 204 : class ScopedMemoryBufferWithSize : public ScopedMemoryBuffer<T> 205 : { 206 : public: 207 5 : ScopedMemoryBufferWithSize() {} 208 148 : ScopedMemoryBufferWithSize(ScopedMemoryBufferWithSize && other) { *this = std::move(other); } 209 : 210 2106 : ScopedMemoryBufferWithSize & operator=(ScopedMemoryBufferWithSize && other) 211 : { 212 2106 : if (this != &other) 213 : { 214 2106 : mCount = other.mCount; 215 2106 : other.mCount = 0; 216 : } 217 2106 : ScopedMemoryBuffer<T>::operator=(std::move(other)); 218 2106 : return *this; 219 : } 220 : 221 5 : ~ScopedMemoryBufferWithSize() { mCount = 0; } 222 : 223 : // return the size as count of elements 224 3943 : inline size_t AllocatedSize() const { return mCount; } 225 : 226 676 : void Free() 227 : { 228 676 : mCount = 0; 229 676 : ScopedMemoryBuffer<T>::Free(); 230 676 : } 231 : 232 : /** 233 : * Releases the underlying buffer. 234 : * 235 : * The buffer stops being managed and will not be auto-freed. 236 : */ 237 : CHECK_RETURN_VALUE T * Release() 238 : { 239 : T * buffer = ScopedMemoryBuffer<T>::Release(); 240 : mCount = 0; 241 : return buffer; 242 : } 243 : 244 130 : ScopedMemoryBufferWithSize & Calloc(size_t elementCount) 245 : { 246 130 : ScopedMemoryBuffer<T>::Calloc(elementCount); 247 130 : if (this->Get() != nullptr) 248 : { 249 130 : mCount = elementCount; 250 : } 251 130 : return *this; 252 : } 253 : 254 1611 : ScopedMemoryBufferWithSize & Alloc(size_t elementCount) 255 : { 256 1611 : ScopedMemoryBuffer<T>::Alloc(elementCount); 257 1611 : if (this->Get() != nullptr) 258 : { 259 1611 : mCount = elementCount; 260 : } 261 1611 : return *this; 262 : } 263 : 264 : private: 265 : size_t mCount = 0; 266 : }; 267 : 268 : } // namespace Platform 269 : } // namespace chip