Line data Source code
1 : /*
2 : *
3 : * Copyright (c) 2024 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 : #pragma once
19 :
20 : #include <lib/core/CHIPError.h>
21 : #include <lib/support/CHIPMem.h>
22 : #include <lib/support/CodeUtils.h>
23 : #include <lib/support/ScopedBuffer.h>
24 : #include <lib/support/Span.h>
25 :
26 : #include <cstdint>
27 : #include <type_traits>
28 :
29 : namespace chip {
30 : namespace app {
31 : namespace DataModel {
32 :
33 : namespace detail {
34 :
35 : class GenericAppendOnlyBuffer
36 : {
37 : public:
38 25492 : GenericAppendOnlyBuffer(size_t elementSize) : mElementSize(elementSize) {}
39 : ~GenericAppendOnlyBuffer();
40 :
41 : GenericAppendOnlyBuffer(GenericAppendOnlyBuffer && other);
42 : GenericAppendOnlyBuffer & operator=(GenericAppendOnlyBuffer &&);
43 :
44 : GenericAppendOnlyBuffer() = delete;
45 : GenericAppendOnlyBuffer & operator=(const GenericAppendOnlyBuffer &) = delete;
46 :
47 : /// Ensure that at least the specified number of elements
48 : /// can be appended to the internal buffer;
49 : ///
50 : /// This will cause the internal buffer to become an allocated buffer
51 : CHIP_ERROR EnsureAppendCapacity(size_t numElements);
52 :
53 : bool IsEmpty() const { return mElementCount == 0; }
54 :
55 : /// Number of elements stored in the object.
56 13 : size_t Size() const { return mElementCount; }
57 :
58 : protected:
59 : /// Appends a single element of mElementSize size.
60 : ///
61 : /// ALWAYS COPIES the given element internally.
62 : /// Sufficient capacity MUST exist to append.
63 : CHIP_ERROR AppendSingleElementRaw(const void * buffer);
64 :
65 : /// Appends a list of elements from a raw array.
66 : ///
67 : /// This ALWAYS COPIES the elements internally.
68 : /// Additional capacity is AUTOMATICALLY ADDED.
69 : ///
70 : /// buffer MUST NOT point inside "own" buffer as mBuffer may be reallocated
71 : /// as part of the appending.
72 : CHIP_ERROR AppendElementArrayRaw(const void * __restrict__ buffer, size_t numElements);
73 :
74 : /// Appends a list of elements from a raw array.
75 : ///
76 : /// If the buffer contains no elements, this will just REFERENCE the given
77 : /// buffer, so its lifetime MUST be longer than the lifetime of this buffer and
78 : /// its usage.
79 : ///
80 : /// If the buffer already contains some elements, this will AUTOMATICALLY
81 : /// add additional capacity and COPY the elements at the end of the internal array.
82 : CHIP_ERROR ReferenceExistingElementArrayRaw(const void * buffer, size_t numElements);
83 :
84 : /// release ownership of any used buffer.
85 : ///
86 : /// Returns the current buffer details and releases ownership of it (clears internal state)
87 : void ReleaseBuffer(void *& buffer, size_t & size, bool & allocated);
88 :
89 : private:
90 : const size_t mElementSize; // size of one element in the buffer
91 : uint8_t * mBuffer = nullptr;
92 : size_t mElementCount = 0; // how many elements are stored in the class
93 : size_t mCapacity = 0; // how many elements can be stored in total in mBuffer
94 : bool mBufferIsAllocated = false; // if mBuffer is an allocated buffer
95 : };
96 :
97 : /// Represents a RAII instance owning a buffer.
98 : ///
99 : /// It auto-frees the owned buffer on destruction via `Platform::MemoryFree`.
100 : ///
101 : /// This class is designed to be a storage class for `GenericAppendOnlyBuffer` and
102 : /// its subclasses (i.e. GenericAppendOnlyBuffer uses PlatformMemory and this class
103 : /// does the same. They MUST be kept in sync.)
104 : class ScopedBuffer
105 : {
106 : public:
107 46499 : ScopedBuffer(void * buffer) : mBuffer(buffer) {}
108 : ~ScopedBuffer();
109 :
110 : ScopedBuffer(const ScopedBuffer &) = delete;
111 : ScopedBuffer & operator=(const ScopedBuffer &) = delete;
112 :
113 : ScopedBuffer(ScopedBuffer && other) : mBuffer(other.mBuffer) { other.mBuffer = nullptr; }
114 : ScopedBuffer & operator=(ScopedBuffer && other);
115 :
116 : private:
117 : void * mBuffer;
118 : };
119 :
120 : } // namespace detail
121 :
122 : template <typename T>
123 : class ReadOnlyBuffer : public Span<const T>, detail::ScopedBuffer
124 : {
125 : public:
126 21011 : ReadOnlyBuffer() : ScopedBuffer(nullptr) {}
127 25482 : ReadOnlyBuffer(const T * buffer, size_t size, bool allocated) :
128 25482 : Span<const T>(buffer, size), ScopedBuffer(allocated ? const_cast<void *>(static_cast<const void *>(buffer)) : nullptr)
129 25482 : {}
130 45646 : ~ReadOnlyBuffer() = default;
131 :
132 24266 : ReadOnlyBuffer & operator=(ReadOnlyBuffer && other)
133 : {
134 24266 : *static_cast<Span<const T> *>(this) = other;
135 24266 : *static_cast<ScopedBuffer *>(this) = std::move(other);
136 24266 : return *this;
137 : }
138 : };
139 :
140 : template <typename T>
141 : class ListBuilder : public detail::GenericAppendOnlyBuffer
142 : {
143 : public:
144 : using SpanType = Span<const T>;
145 :
146 : // we do not call destructors, just malloc things.
147 : // Note that classes should also be trivially assignable (we do NOT call the assignment operator)
148 : // This makes this class somewhat dangerous...
149 : //
150 : // Note: ideally we would want `is_trivially_copyable_v` as well however our chip::Optional
151 : // implementation does not actually report that.
152 : static_assert(std::is_trivially_destructible_v<T>);
153 :
154 25482 : ListBuilder() : GenericAppendOnlyBuffer(sizeof(T)) {}
155 :
156 : ListBuilder(const ListBuilder &) = delete;
157 : ListBuilder & operator=(const ListBuilder & other) = delete;
158 :
159 : ListBuilder(ListBuilder && other) : GenericAppendOnlyBuffer(sizeof(T)) { *this = std::move(other); }
160 :
161 : ListBuilder & operator=(ListBuilder && other)
162 : {
163 : *static_cast<GenericAppendOnlyBuffer *>(this) = std::move(other);
164 : return *this;
165 : }
166 :
167 : /// Reference methods attempt to reference the existing array IN PLACE
168 : /// so its lifetime is assumed to be longer than the usage of this list.
169 5 : CHIP_ERROR ReferenceExisting(SpanType span) { return ReferenceExistingElementArrayRaw(span.data(), span.size()); }
170 :
171 : /// Append always attempts to append/extend existing memory.
172 : ///
173 : /// Automatically attempts to allocate sufficient space to fulfill the element
174 : /// requirements.
175 : ///
176 : /// `span` MUST NOT point inside "own" buffer (and generally will not
177 : /// as this class does not expose buffer access except by releasing ownership
178 : /// via `Take`)
179 : CHIP_ERROR AppendElements(SpanType span) { return AppendElementArrayRaw(span.data(), span.size()); }
180 :
181 : /// Append a single element.
182 : /// Sufficent append capacity MUST exist.
183 115611 : CHIP_ERROR Append(const T & value) { return AppendSingleElementRaw(&value); }
184 :
185 : /// Once a list is built, the data is taken as a scoped SPAN that owns its data
186 : /// and the original list is cleared
187 25482 : ReadOnlyBuffer<T> TakeBuffer()
188 : {
189 : void * buffer;
190 : size_t size;
191 : bool allocated;
192 25482 : ReleaseBuffer(buffer, size, allocated);
193 :
194 25482 : return ReadOnlyBuffer<T>(static_cast<const T *>(buffer), size, allocated);
195 : }
196 : };
197 :
198 : } // namespace DataModel
199 : } // namespace app
200 : } // namespace chip
|