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 : #include <lib/support/Span.h>
30 :
31 : #include <type_traits>
32 : #include <utility>
33 :
34 : namespace chip {
35 : namespace Platform {
36 :
37 : namespace Impl {
38 :
39 : /**
40 : * Represents a memory buffer that is auto-freed in the destructor.
41 : *
42 : * This class uses void* underneath on purpose (rather than a unique_ptr like
43 : * 'Type') and uses templated type on Ptr(). This is to avoid template explosion
44 : * when the buffers are used for different types - only one implementation of
45 : * the class will be stored in flash.
46 : */
47 : template <class Impl>
48 : class ScopedMemoryBufferBase
49 : {
50 : public:
51 22885 : ScopedMemoryBufferBase() {}
52 : ScopedMemoryBufferBase(const ScopedMemoryBufferBase &) = delete;
53 : ScopedMemoryBufferBase & operator=(const ScopedMemoryBufferBase & other) = delete;
54 :
55 : ScopedMemoryBufferBase(ScopedMemoryBufferBase && other) { *this = std::move(other); }
56 :
57 7336 : ScopedMemoryBufferBase & operator=(ScopedMemoryBufferBase && other)
58 : {
59 7336 : if (this != &other)
60 : {
61 7336 : mBuffer = other.mBuffer;
62 7336 : other.mBuffer = nullptr;
63 : }
64 7336 : return *this;
65 : }
66 :
67 22885 : ~ScopedMemoryBufferBase() { Free(); }
68 :
69 : /** Check if a buffer is valid */
70 1292 : explicit operator bool() const { return mBuffer != nullptr; }
71 7175 : bool operator!() const { return mBuffer == nullptr; }
72 :
73 : /** Release memory used */
74 49879 : void Free()
75 : {
76 49879 : if (mBuffer == nullptr)
77 : {
78 36722 : return;
79 : }
80 13157 : Impl::MemoryFree(mBuffer);
81 13157 : mBuffer = nullptr;
82 : }
83 :
84 : protected:
85 29810 : void * Ptr() { return mBuffer; }
86 9197 : const void * Ptr() const { return mBuffer; }
87 :
88 : /**
89 : * Releases the underlying buffer.
90 : *
91 : * The buffer stops being managed and will not be auto-freed.
92 : */
93 1 : CHECK_RETURN_VALUE void * Release()
94 : {
95 1 : void * buffer = mBuffer;
96 1 : mBuffer = nullptr;
97 1 : return buffer;
98 : }
99 :
100 8677 : void Alloc(size_t size)
101 : {
102 8677 : Free();
103 8677 : mBuffer = Impl::MemoryAlloc(size);
104 8677 : }
105 :
106 4481 : void Calloc(size_t elementCount, size_t elementSize)
107 : {
108 4481 : Free();
109 4481 : mBuffer = Impl::MemoryCalloc(elementCount, elementSize);
110 4481 : }
111 :
112 : private:
113 : void * mBuffer = nullptr;
114 : };
115 :
116 : /**
117 : * Helper class that forwards memory management tasks to Platform::Memory* calls.
118 : */
119 : class PlatformMemoryManagement
120 : {
121 : public:
122 13153 : static void MemoryFree(void * p) { chip::Platform::MemoryFree(p); }
123 8672 : static void * MemoryAlloc(size_t size) { return chip::Platform::MemoryAlloc(size); }
124 4480 : static void * MemoryCalloc(size_t num, size_t size) { return chip::Platform::MemoryCalloc(num, size); }
125 : };
126 :
127 : } // namespace Impl
128 :
129 : /**
130 : * Represents a memory buffer allocated using chip::Platform::Memory*Alloc
131 : * methods.
132 : *
133 : * Use for RAII to auto-free after use.
134 : */
135 : template <typename T, class MemoryManagement = Impl::PlatformMemoryManagement>
136 : class ScopedMemoryBuffer : public Impl::ScopedMemoryBufferBase<MemoryManagement>
137 : {
138 : friend class Impl::ScopedMemoryBufferBase<MemoryManagement>;
139 :
140 : public:
141 : using Base = Impl::ScopedMemoryBufferBase<MemoryManagement>;
142 :
143 : static_assert(std::is_trivially_destructible<T>::value, "Destructors won't get run");
144 :
145 29810 : T * Get() { return static_cast<T *>(Base::Ptr()); }
146 712 : T & operator[](size_t index) { return Get()[index]; }
147 :
148 9197 : const T * Get() const { return static_cast<const T *>(Base::Ptr()); }
149 100 : const T & operator[](size_t index) const { return Get()[index]; }
150 :
151 : /**
152 : * Releases the underlying buffer.
153 : *
154 : * The buffer stops being managed and will not be auto-freed.
155 : */
156 1 : CHECK_RETURN_VALUE T * Release() { return static_cast<T *>(Base::Release()); }
157 :
158 4481 : ScopedMemoryBuffer & Calloc(size_t elementCount)
159 : {
160 4481 : Base::Calloc(elementCount, sizeof(T));
161 4481 : ExecuteConstructors(elementCount);
162 4481 : return *this;
163 : }
164 :
165 8677 : ScopedMemoryBuffer & Alloc(size_t elementCount)
166 : {
167 8677 : Base::Alloc(elementCount * sizeof(T));
168 8677 : ExecuteConstructors(elementCount);
169 8677 : return *this;
170 : }
171 :
172 : private:
173 : template <typename U = T, std::enable_if_t<std::is_trivial<U>::value, int> = 0>
174 13158 : void ExecuteConstructors(size_t elementCount)
175 : {
176 : // Do nothing if our type is trivial. In particular, if we are a buffer
177 : // of integers, we should not go zero-initializing them here: either
178 : // caller wants that and called Calloc(), or it doesn't and we shouldn't
179 : // do it.
180 13158 : }
181 :
182 : template <typename U = T, std::enable_if_t<!std::is_trivial<U>::value, int> = 0>
183 0 : void ExecuteConstructors(size_t elementCount)
184 : {
185 0 : T * elementPtr = Get();
186 0 : if (elementPtr == nullptr)
187 : {
188 : // Alloc failed, don't bother.
189 0 : return;
190 : }
191 0 : for (size_t i = 0; i < elementCount; ++i)
192 : {
193 0 : new (&elementPtr[i]) T();
194 : }
195 : }
196 : };
197 :
198 : /**
199 : * Represents a memory buffer with buffer size allocated using chip::Platform::Memory*Alloc
200 : * methods.
201 : *
202 : * Use for RAII to auto-free after use.
203 : */
204 : template <typename T>
205 : class ScopedMemoryBufferWithSize : public ScopedMemoryBuffer<T>
206 : {
207 : public:
208 8965 : ScopedMemoryBufferWithSize() {}
209 208 : ScopedMemoryBufferWithSize(ScopedMemoryBufferWithSize && other) { *this = std::move(other); }
210 :
211 2618 : ScopedMemoryBufferWithSize & operator=(ScopedMemoryBufferWithSize && other)
212 : {
213 2618 : if (this != &other)
214 : {
215 2618 : mCount = other.mCount;
216 2618 : other.mCount = 0;
217 : }
218 2618 : ScopedMemoryBuffer<T>::operator=(std::move(other));
219 2618 : return *this;
220 : }
221 :
222 : // return the size as count of elements
223 6155 : inline size_t AllocatedSize() const { return mCount; }
224 :
225 3 : chip::Span<T> Span() { return chip::Span<T>(this->Get(), AllocatedSize()); }
226 : chip::Span<const T> Span() const { return chip::Span<T>(this->Get(), AllocatedSize()); }
227 :
228 13759 : void Free()
229 : {
230 13759 : mCount = 0;
231 13759 : ScopedMemoryBuffer<T>::Free();
232 13759 : }
233 :
234 : /**
235 : * Releases the underlying buffer.
236 : *
237 : * The buffer stops being managed and will not be auto-freed.
238 : */
239 : CHECK_RETURN_VALUE T * Release()
240 : {
241 : T * buffer = ScopedMemoryBuffer<T>::Release();
242 : mCount = 0;
243 : return buffer;
244 : }
245 :
246 263 : ScopedMemoryBufferWithSize & Calloc(size_t elementCount)
247 : {
248 263 : ScopedMemoryBuffer<T>::Calloc(elementCount);
249 263 : if (this->Get() != nullptr)
250 : {
251 263 : mCount = elementCount;
252 : }
253 263 : return *this;
254 : }
255 :
256 2824 : ScopedMemoryBufferWithSize & Alloc(size_t elementCount)
257 : {
258 2824 : ScopedMemoryBuffer<T>::Alloc(elementCount);
259 2824 : if (this->Get() != nullptr)
260 : {
261 2824 : mCount = elementCount;
262 : }
263 2824 : return *this;
264 : }
265 :
266 : // Allow creating ScopedMemoryBufferWithSize from Span, so we
267 : // don't have to reinvent it in various places.
268 : template <class U, typename = std::enable_if_t<sizeof(U) == sizeof(T) && std::is_convertible_v<U *, T *>>>
269 0 : ScopedMemoryBufferWithSize & CopyFromSpan(const chip::Span<const U> & span)
270 : {
271 : static_assert(std::is_trivially_copyable_v<U>, "Span<const U> must be trivially copyable");
272 :
273 0 : if (span.size() == 0)
274 : {
275 0 : Free();
276 0 : return *this;
277 : }
278 :
279 0 : ScopedMemoryBufferWithSize<T>::Alloc(span.size());
280 :
281 0 : if (AllocatedSize() > 0)
282 : {
283 0 : memcpy(ScopedMemoryBuffer<T>::Get(), span.data(), AllocatedSize());
284 : }
285 :
286 0 : return *this;
287 : }
288 :
289 : private:
290 : size_t mCount = 0;
291 : };
292 :
293 : } // namespace Platform
294 : } // namespace chip
|