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 242 : ScopedMemoryBufferBase() {}
51 : ScopedMemoryBufferBase(const ScopedMemoryBufferBase &) = delete;
52 : ScopedMemoryBufferBase & operator=(const ScopedMemoryBufferBase & other) = delete;
53 :
54 : ScopedMemoryBufferBase(ScopedMemoryBufferBase && other) { *this = std::move(other); }
55 :
56 4353 : ScopedMemoryBufferBase & operator=(ScopedMemoryBufferBase && other)
57 : {
58 4353 : if (this != &other)
59 : {
60 4353 : mBuffer = other.mBuffer;
61 4353 : other.mBuffer = nullptr;
62 : }
63 4353 : return *this;
64 : }
65 :
66 242 : ~ScopedMemoryBufferBase() { Free(); }
67 :
68 : /** Check if a buffer is valid */
69 1 : explicit operator bool() const { return mBuffer != nullptr; }
70 4814 : bool operator!() const { return mBuffer == nullptr; }
71 :
72 : /** Release memory used */
73 466 : void Free()
74 : {
75 466 : if (mBuffer == nullptr)
76 : {
77 242 : return;
78 : }
79 224 : Impl::MemoryFree(mBuffer);
80 224 : mBuffer = nullptr;
81 : }
82 :
83 : protected:
84 659 : void * Ptr() { return mBuffer; }
85 6806 : 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 1409 : void Alloc(size_t size)
100 : {
101 1409 : Free();
102 1409 : mBuffer = Impl::MemoryAlloc(size);
103 1409 : }
104 :
105 1134 : void Calloc(size_t elementCount, size_t elementSize)
106 : {
107 1134 : Free();
108 1134 : mBuffer = Impl::MemoryCalloc(elementCount, elementSize);
109 1134 : }
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 224 : static void MemoryFree(void * p) { chip::Platform::MemoryFree(p); }
122 1409 : static void * MemoryAlloc(size_t size) { return chip::Platform::MemoryAlloc(size); }
123 1134 : 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 14613 : T * Get() { return static_cast<T *>(Base::Ptr()); }
145 183 : T & operator[](size_t index) { return Get()[index]; }
146 :
147 6806 : 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 4251 : ScopedMemoryBuffer & Calloc(size_t elementCount)
158 : {
159 4251 : Base::Calloc(elementCount, sizeof(T));
160 4251 : ExecuteConstructors(elementCount);
161 4251 : return *this;
162 : }
163 :
164 1404 : ScopedMemoryBuffer & Alloc(size_t elementCount)
165 : {
166 1404 : Base::Alloc(elementCount * sizeof(T));
167 1404 : ExecuteConstructors(elementCount);
168 1404 : return *this;
169 : }
170 :
171 : private:
172 : template <typename U = T, std::enable_if_t<std::is_trivial<U>::value, int> = 0>
173 9462 : 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 9462 : }
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 2332 : ScopedMemoryBufferWithSize & operator=(ScopedMemoryBufferWithSize && other)
211 : {
212 2332 : if (this != &other)
213 : {
214 2332 : mCount = other.mCount;
215 2332 : other.mCount = 0;
216 : }
217 2332 : ScopedMemoryBuffer<T>::operator=(std::move(other));
218 2332 : return *this;
219 : }
220 :
221 5 : ~ScopedMemoryBufferWithSize() { mCount = 0; }
222 :
223 : // return the size as count of elements
224 4297 : inline size_t AllocatedSize() const { return mCount; }
225 :
226 698 : void Free()
227 : {
228 698 : mCount = 0;
229 698 : ScopedMemoryBuffer<T>::Free();
230 698 : }
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 1779 : ScopedMemoryBufferWithSize & Alloc(size_t elementCount)
255 : {
256 1779 : ScopedMemoryBuffer<T>::Alloc(elementCount);
257 1779 : if (this->Get() != nullptr)
258 : {
259 1779 : mCount = elementCount;
260 : }
261 1779 : return *this;
262 : }
263 :
264 : private:
265 : size_t mCount = 0;
266 : };
267 :
268 : } // namespace Platform
269 : } // namespace chip
|