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