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 11756 : ScopedMemoryBufferBase() {}
52 : ScopedMemoryBufferBase(const ScopedMemoryBufferBase &) = delete;
53 : ScopedMemoryBufferBase & operator=(const ScopedMemoryBufferBase & other) = delete;
54 :
55 : ScopedMemoryBufferBase(ScopedMemoryBufferBase && other) { *this = std::move(other); }
56 :
57 6977 : ScopedMemoryBufferBase & operator=(ScopedMemoryBufferBase && other)
58 : {
59 6977 : if (this != &other)
60 : {
61 6977 : mBuffer = other.mBuffer;
62 6977 : other.mBuffer = nullptr;
63 : }
64 6977 : return *this;
65 : }
66 :
67 11756 : ~ScopedMemoryBufferBase() { Free(); }
68 :
69 : /** Check if a buffer is valid */
70 1 : explicit operator bool() const { return mBuffer != nullptr; }
71 4962 : bool operator!() const { return mBuffer == nullptr; }
72 :
73 : /** Release memory used */
74 22655 : void Free()
75 : {
76 22655 : if (mBuffer == nullptr)
77 : {
78 15959 : return;
79 : }
80 6696 : Impl::MemoryFree(mBuffer);
81 6696 : mBuffer = nullptr;
82 : }
83 :
84 : protected:
85 14947 : void * Ptr() { return mBuffer; }
86 6965 : 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 : CHECK_RETURN_VALUE void * Release()
94 : {
95 : void * buffer = mBuffer;
96 : mBuffer = nullptr;
97 : return buffer;
98 : }
99 :
100 1341 : void Alloc(size_t size)
101 : {
102 1341 : Free();
103 1341 : mBuffer = Impl::MemoryAlloc(size);
104 1341 : }
105 :
106 4447 : void Calloc(size_t elementCount, size_t elementSize)
107 : {
108 4447 : Free();
109 4447 : mBuffer = Impl::MemoryCalloc(elementCount, elementSize);
110 4447 : }
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 8042 : static void MemoryFree(void * p) { chip::Platform::MemoryFree(p); }
123 1341 : static void * MemoryAlloc(size_t size) { return chip::Platform::MemoryAlloc(size); }
124 4447 : 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 14651 : T * Get() { return static_cast<T *>(Base::Ptr()); }
146 183 : T & operator[](size_t index) { return Get()[index]; }
147 :
148 6965 : const T * Get() const { return static_cast<const T *>(Base::Ptr()); }
149 0 : 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 : CHECK_RETURN_VALUE T * Release() { return static_cast<T *>(Base::Release()); }
157 :
158 4412 : ScopedMemoryBuffer & Calloc(size_t elementCount)
159 : {
160 4412 : Base::Calloc(elementCount, sizeof(T));
161 4412 : ExecuteConstructors(elementCount);
162 4412 : return *this;
163 : }
164 :
165 1336 : ScopedMemoryBuffer & Alloc(size_t elementCount)
166 : {
167 1336 : Base::Alloc(elementCount * sizeof(T));
168 1336 : ExecuteConstructors(elementCount);
169 1336 : return *this;
170 : }
171 :
172 : private:
173 : template <typename U = T, std::enable_if_t<std::is_trivial<U>::value, int> = 0>
174 9719 : 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 9719 : }
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 5 : ScopedMemoryBufferWithSize() {}
209 208 : ScopedMemoryBufferWithSize(ScopedMemoryBufferWithSize && other) { *this = std::move(other); }
210 :
211 2435 : ScopedMemoryBufferWithSize & operator=(ScopedMemoryBufferWithSize && other)
212 : {
213 2435 : if (this != &other)
214 : {
215 2435 : mCount = other.mCount;
216 2435 : other.mCount = 0;
217 : }
218 2435 : ScopedMemoryBuffer<T>::operator=(std::move(other));
219 2435 : return *this;
220 : }
221 :
222 : // return the size as count of elements
223 4388 : inline size_t AllocatedSize() const { return mCount; }
224 :
225 0 : 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 698 : void Free()
229 : {
230 698 : mCount = 0;
231 698 : ScopedMemoryBuffer<T>::Free();
232 698 : }
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 193 : ScopedMemoryBufferWithSize & Calloc(size_t elementCount)
247 : {
248 193 : ScopedMemoryBuffer<T>::Calloc(elementCount);
249 193 : if (this->Get() != nullptr)
250 : {
251 193 : mCount = elementCount;
252 : }
253 193 : return *this;
254 : }
255 :
256 1839 : ScopedMemoryBufferWithSize & Alloc(size_t elementCount)
257 : {
258 1839 : ScopedMemoryBuffer<T>::Alloc(elementCount);
259 1839 : if (this->Get() != nullptr)
260 : {
261 1839 : mCount = elementCount;
262 : }
263 1839 : return *this;
264 : }
265 :
266 : private:
267 : size_t mCount = 0;
268 : };
269 :
270 : } // namespace Platform
271 : } // namespace chip
|