Line data Source code
1 : /*
2 : *
3 : * Copyright (c) 2021 Project CHIP Authors
4 : *
5 : * Licensed under the Apache License, Version 2.0 (the "License");
6 : * you may not use this file except in compliance with the License.
7 : * You may obtain a copy of the License at
8 : *
9 : * http://www.apache.org/licenses/LICENSE-2.0
10 : *
11 : * Unless required by applicable law or agreed to in writing, software
12 : * distributed under the License is distributed on an "AS IS" BASIS,
13 : * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 : * See the License for the specific language governing permissions and
15 : * limitations under the License.
16 : */
17 :
18 : #pragma once
19 :
20 : #include <array>
21 : #include <cstddef>
22 : #include <cstdint>
23 : #include <cstdlib>
24 : #include <cstring>
25 : #include <type_traits>
26 :
27 : #include <lib/core/Unchecked.h>
28 : #include <lib/support/CodeUtils.h>
29 :
30 : namespace chip {
31 :
32 : template <class T, size_t N>
33 : class FixedSpan;
34 :
35 : /**
36 : * @brief A wrapper class for holding objects and its length, without the ownership of it.
37 : * We can use C++20 std::span once we support it, the data() and size() come from C++20 std::span.
38 : */
39 : template <class T>
40 : class Span
41 : {
42 : public:
43 : using pointer = T *;
44 : using reference = T &;
45 :
46 21392 : constexpr Span() : mDataBuf(nullptr), mDataLen(0) {}
47 :
48 : // Note: VerifyOrDie cannot be used inside a constexpr function, because it uses
49 : // "static" on some platforms (e.g. when CHIP_PW_TOKENIZER_LOGGING is true)
50 : // and that's not allowed in constexpr functions.
51 :
52 50583 : Span(pointer databuf, size_t datalen) : mDataBuf(databuf), mDataLen(datalen)
53 : {
54 50583 : VerifyOrDie(databuf != nullptr || datalen == 0); // not constexpr on some platforms
55 50583 : }
56 :
57 : // A Span can only point to null if it is empty (size == 0). The default constructor
58 : // should be used to construct empty Spans. All other cases involving null are invalid.
59 : Span(std::nullptr_t null, size_t size) = delete;
60 :
61 : // Creates a Span view of a plain array.
62 : //
63 : // Note that this constructor template explicitly excludes `const char[]`, see below.
64 : template <
65 : class U, size_t N,
66 : std::enable_if_t<!std::is_same_v<U, const char> && sizeof(U) == sizeof(T) && std::is_convertible_v<U *, T *>, bool> = true>
67 11668 : constexpr explicit Span(U (&databuf)[N]) : mDataBuf(databuf), mDataLen(N)
68 11668 : {}
69 :
70 : // Explicitly disallow the creation of a CharSpan from a `const char[]` to prevent the
71 : // creation of spans from string literals that incorrectly include the trailing '\0' byte.
72 : // If CharSpan("Hi!") were allowed, it would be a span of length 4, not 3 as intended.
73 : //
74 : // To create a CharSpan literal, use the `_span` operator instead, e.g. "Hi!"_span.
75 : template <
76 : class U, size_t N,
77 : std::enable_if_t<std::is_same_v<U, const char> && 1 == sizeof(T) && std::is_convertible_v<const char *, T *>, bool> = true>
78 : constexpr explicit Span(U (&databuf)[N]) = delete;
79 :
80 : // Creates a (potentially mutable) Span view of an std::array
81 : template <class U, size_t N, typename = std::enable_if_t<sizeof(U) == sizeof(T) && std::is_convertible<U *, T *>::value>>
82 683 : constexpr Span(std::array<U, N> & arr) : mDataBuf(arr.data()), mDataLen(N)
83 683 : {}
84 :
85 : template <class U, size_t N, typename = std::enable_if_t<sizeof(U) == sizeof(T) && std::is_convertible<U *, T *>::value>>
86 : constexpr Span(std::array<U, N> && arr) = delete; // would be a view of an rvalue
87 :
88 : // Creates a Span view of an std::array
89 : template <class U, size_t N, typename = std::enable_if_t<sizeof(U) == sizeof(T) && std::is_convertible<const U *, T *>::value>>
90 : constexpr Span(const std::array<U, N> & arr) : mDataBuf(arr.data()), mDataLen(N)
91 : {}
92 :
93 : template <size_t N>
94 : constexpr Span & operator=(T (&databuf)[N])
95 : {
96 : mDataBuf = databuf;
97 : mDataLen = N;
98 : return (*this);
99 : }
100 :
101 : // Allow implicit construction from a Span over a type that matches our
102 : // type's size, if a pointer to the other type can be treated as a pointer
103 : // to our type (e.g. other type is same as ours, or is a same-size
104 : // subclass). The size check is really important to make sure we don't get
105 : // confused about where our object boundaries are.
106 : template <class U, typename = std::enable_if_t<sizeof(U) == sizeof(T) && std::is_convertible<U *, T *>::value>>
107 4641 : constexpr Span(const Span<U> & other) : Span(other.data(), other.size())
108 4641 : {}
109 :
110 : // Allow implicit construction from a FixedSpan over a type that matches our
111 : // type's size, if a pointer to the other type can be treated as a pointer
112 : // to our type (e.g. other type is same as ours, or is a same-size
113 : // subclass). The size check is really important to make sure we don't get
114 : // confused about where our object boundaries are.
115 : template <class U, size_t N, typename = std::enable_if_t<sizeof(U) == sizeof(T) && std::is_convertible<U *, T *>::value>>
116 : constexpr inline Span(const FixedSpan<U, N> & other);
117 :
118 119468 : constexpr pointer data() const { return mDataBuf; }
119 203601 : constexpr size_t size() const { return mDataLen; }
120 14238 : constexpr bool empty() const { return size() == 0; }
121 3316 : constexpr pointer begin() const { return data(); }
122 3303 : constexpr pointer end() const { return data() + size(); }
123 :
124 : // Element accessors, matching the std::span API.
125 0 : reference operator[](size_t index) const
126 : {
127 0 : VerifyOrDie(index < size()); // not constexpr on some platforms
128 0 : return data()[index];
129 : }
130 : reference front() const { return (*this)[0]; }
131 : reference back() const { return (*this)[size() - 1]; }
132 :
133 1155 : bool data_equal(const Span<const T> & other) const
134 : {
135 1155 : return (size() == other.size()) && (empty() || (memcmp(data(), other.data(), size() * sizeof(T)) == 0));
136 : }
137 :
138 5749 : Span SubSpan(size_t offset, size_t length) const
139 : {
140 5749 : VerifyOrDie(offset <= mDataLen);
141 5749 : VerifyOrDie(length <= mDataLen - offset);
142 5749 : return Span(mDataBuf + offset, length);
143 : }
144 :
145 121 : Span SubSpan(size_t offset) const
146 : {
147 121 : VerifyOrDie(offset <= mDataLen);
148 121 : return Span(mDataBuf + offset, mDataLen - offset);
149 : }
150 :
151 : // Allow reducing the size of a span.
152 7076 : void reduce_size(size_t new_size)
153 : {
154 7076 : VerifyOrDie(new_size <= size());
155 7076 : mDataLen = new_size;
156 7076 : }
157 :
158 : // Allow creating ByteSpans and CharSpans from ZCL octet strings, so we
159 : // don't have to reinvent it various places.
160 : template <class U,
161 : typename = std::enable_if_t<std::is_same<uint8_t, std::remove_const_t<U>>::value &&
162 : (std::is_same<const uint8_t, T>::value || std::is_same<const char, T>::value)>>
163 : static Span fromZclString(U * bytes)
164 : {
165 : size_t length = bytes[0];
166 : // Treat 0xFF (aka "null string") as zero-length.
167 : if (length == 0xFF)
168 : {
169 : length = 0;
170 : }
171 : // Need reinterpret_cast if we're a CharSpan.
172 : return Span(reinterpret_cast<T *>(&bytes[1]), length);
173 : }
174 :
175 : // Creates a CharSpan from a null-terminated C character string.
176 : //
177 : // Note that for string literals, the user-defined `_span` string
178 : // literal operator should be used instead, e.g. `"Hello"_span`.
179 : template <class U, typename = std::enable_if_t<std::is_same<T, const U>::value && std::is_same<const char, T>::value>>
180 1240 : static Span fromCharString(U * chars)
181 : {
182 1240 : return Span(chars, strlen(chars));
183 : }
184 :
185 : // operator== explicitly not implemented on Span, because its meaning
186 : // (equality of data, or pointing to the same buffer and same length) is
187 : // ambiguous. Use data_equal if testing for equality of data.
188 : template <typename U>
189 : bool operator==(const Span<U> & other) const = delete;
190 :
191 : // Creates a Span without checking whether databuf is a null pointer.
192 : //
193 : // Note: The normal (checked) constructor should be used for general use;
194 : // this overload exists for special use cases where databuf is guaranteed
195 : // to be valid (not null) and a constexpr constructor is required.
196 : //
197 : // https://gcc.gnu.org/bugzilla/show_bug.cgi?id=61648 prevents making
198 : // operator""_span a friend (and this constructor private).
199 :
200 0 : constexpr Span(UncheckedType tag, pointer databuf, size_t datalen) : mDataBuf(databuf), mDataLen(datalen) {}
201 :
202 : private:
203 : pointer mDataBuf;
204 : size_t mDataLen;
205 : };
206 :
207 : inline namespace literals {
208 :
209 0 : inline constexpr Span<const char> operator"" _span(const char * literal, size_t size)
210 : {
211 0 : return Span<const char>(Unchecked, literal, size);
212 : }
213 :
214 : } // namespace literals
215 :
216 : namespace detail {
217 :
218 : // To make FixedSpan (specifically various FixedByteSpan types) default constructible
219 : // without creating a weird "empty() == true but size() != 0" state, we need an
220 : // appropriate sized array of zeroes. With a naive definition like
221 : // template <class T, size_t N> constexpr T kZero[N] {};
222 : // we would end up with separate zero arrays for each size, and might also accidentally
223 : // increase the read-only data size of the binary by a large amount. Instead, we define
224 : // a per-type limit for the zero array, FixedSpan won't be default constructible for
225 : // T / N combinations that exceed the limit. The default limit is 0.
226 : template <class T>
227 : struct zero_limit : std::integral_constant<size_t, 0>
228 : {
229 : };
230 :
231 : // FixedByteSpan types up to N=65 currently need to be default-constructible.
232 : template <>
233 : struct zero_limit<uint8_t> : std::integral_constant<size_t, 65>
234 : {
235 : };
236 :
237 : template <class T>
238 : inline constexpr T kZeroes[zero_limit<T>::value]{};
239 :
240 : template <class T, size_t N>
241 41032 : constexpr T const * shared_zeroes()
242 : {
243 : static_assert(N <= zero_limit<typename std::remove_const<T>::type>::value, "N exceeds zero_limit<T>");
244 41032 : return kZeroes<typename std::remove_const<T>::type>;
245 : }
246 :
247 : } // namespace detail
248 :
249 : /**
250 : * Similar to a Span but with a fixed size.
251 : */
252 : template <class T, size_t N>
253 : class FixedSpan
254 : {
255 : public:
256 : using pointer = T *;
257 : using reference = T &;
258 :
259 : // Creates a FixedSpan pointing to a sequence of zeroes.
260 41032 : constexpr FixedSpan() : mDataBuf(detail::shared_zeroes<T, N>()) {}
261 :
262 : // We want to allow construction from things that look like T*, but we want
263 : // to make construction from an array use the constructor that asserts the
264 : // array is big enough. This requires that both constructors be templates
265 : // (because otherwise the non-template would be favored by overload
266 : // resolution, since due to decay to pointer it matches just as well as the
267 : // template).
268 : //
269 : // To do that we have a template constructor enabled only when the type
270 : // passed to it is a pointer type, and that pointer is to a type that
271 : // matches T's size and can convert to T*.
272 : template <class U,
273 : typename = std::enable_if_t<std::is_pointer<U>::value && sizeof(std::remove_pointer_t<U>) == sizeof(T) &&
274 : std::is_convertible<U, T *>::value>>
275 10442 : explicit FixedSpan(U databuf) : mDataBuf(databuf)
276 : {
277 10442 : VerifyOrDie(databuf != nullptr || N == 0); // not constexpr on some platforms
278 10442 : }
279 :
280 : // FixedSpan does not support an empty / null state.
281 : FixedSpan(std::nullptr_t null) = delete;
282 :
283 : template <class U, size_t M, typename = std::enable_if_t<sizeof(U) == sizeof(T) && std::is_convertible<U *, T *>::value>>
284 39 : constexpr explicit FixedSpan(U (&databuf)[M]) : mDataBuf(databuf)
285 : {
286 : static_assert(M >= N, "Passed-in buffer too small for FixedSpan");
287 39 : }
288 :
289 : // Creates a (potentially mutable) FixedSpan view of an std::array
290 : template <class U, typename = std::enable_if_t<sizeof(U) == sizeof(T) && std::is_convertible<U *, T *>::value>>
291 37785 : constexpr FixedSpan(std::array<U, N> & arr) : mDataBuf(arr.data())
292 37785 : {}
293 :
294 : template <class U, typename = std::enable_if_t<sizeof(U) == sizeof(T) && std::is_convertible<U *, T *>::value>>
295 : constexpr FixedSpan(std::array<U, N> && arr) = delete; // would be a view of an rvalue
296 :
297 : // Creates a FixedSpan view of an std::array
298 : template <class U, typename = std::enable_if_t<sizeof(U) == sizeof(T) && std::is_convertible<const U *, T *>::value>>
299 : constexpr FixedSpan(const std::array<U, N> & arr) : mDataBuf(arr.data())
300 : {}
301 :
302 : // Allow implicit construction from a FixedSpan of sufficient size over a
303 : // type that has the same size as ours, as long as the pointers are convertible.
304 : template <class U, size_t M, typename = std::enable_if_t<sizeof(U) == sizeof(T) && std::is_convertible<U *, T *>::value>>
305 : constexpr FixedSpan(FixedSpan<U, M> const & other) : mDataBuf(other.data())
306 : {
307 : static_assert(M >= N, "Passed-in FixedSpan is smaller than we are");
308 : }
309 :
310 53290 : constexpr pointer data() const { return mDataBuf; }
311 67 : constexpr pointer begin() const { return mDataBuf; }
312 67 : constexpr pointer end() const { return mDataBuf + N; }
313 :
314 : // The size of a FixedSpan is always N. There is intentially no empty() method.
315 53088 : static constexpr size_t size() { return N; }
316 :
317 : // Element accessors, matching the std::span API.
318 : // VerifyOrDie cannot be used inside a constexpr function, because it uses
319 : // "static" on some platforms (e.g. when CHIP_PW_TOKENIZER_LOGGING is true)
320 : // and that's not allowed in constexpr functions.
321 : reference operator[](size_t index) const
322 : {
323 : VerifyOrDie(index < N);
324 : return data()[index];
325 : }
326 : reference front() const { return (*this)[0]; }
327 : reference back() const { return (*this)[size() - 1]; }
328 :
329 1956 : bool data_equal(const Span<const T> & other) const
330 : {
331 1956 : return (N == other.size() && memcmp(data(), other.data(), N * sizeof(T)) == 0);
332 : }
333 :
334 : // operator== explicitly not implemented on FixedSpan, because its meaning
335 : // (equality of data, or pointing to the same buffer and same length) is
336 : // ambiguous. Use data_equal if testing for equality of data.
337 : template <typename U>
338 : bool operator==(const Span<U> & other) const = delete;
339 : template <typename U, size_t M>
340 : bool operator==(const FixedSpan<U, M> & other) const = delete;
341 :
342 : private:
343 : pointer mDataBuf;
344 : };
345 :
346 : template <class T>
347 : template <class U, size_t N, typename>
348 2685 : constexpr Span<T>::Span(const FixedSpan<U, N> & other) : mDataBuf(other.data()), mDataLen(other.size())
349 2685 : {}
350 :
351 : template <typename T>
352 : [[deprecated("Use !empty()")]] inline bool IsSpanUsable(const Span<T> & span)
353 : {
354 : return !span.empty();
355 : }
356 :
357 : template <typename T, size_t N>
358 : [[deprecated("FixedSpan is always usable / non-empty if N > 0")]] inline bool IsSpanUsable(const FixedSpan<T, N> & span)
359 : {
360 : return N > 0;
361 : }
362 :
363 : using ByteSpan = Span<const uint8_t>;
364 : using MutableByteSpan = Span<uint8_t>;
365 : template <size_t N>
366 : using FixedByteSpan = FixedSpan<const uint8_t, N>;
367 :
368 : using CharSpan = Span<const char>;
369 : using MutableCharSpan = Span<char>;
370 :
371 2700 : inline CHIP_ERROR CopySpanToMutableSpan(ByteSpan span_to_copy, MutableByteSpan & out_buf)
372 : {
373 2700 : VerifyOrReturnError(out_buf.size() >= span_to_copy.size(), CHIP_ERROR_BUFFER_TOO_SMALL);
374 :
375 2699 : memcpy(out_buf.data(), span_to_copy.data(), span_to_copy.size());
376 2699 : out_buf.reduce_size(span_to_copy.size());
377 :
378 2699 : return CHIP_NO_ERROR;
379 : }
380 :
381 11 : inline CHIP_ERROR CopyCharSpanToMutableCharSpan(CharSpan cspan_to_copy, MutableCharSpan & out_buf)
382 : {
383 11 : VerifyOrReturnError(out_buf.size() >= cspan_to_copy.size(), CHIP_ERROR_BUFFER_TOO_SMALL);
384 :
385 9 : memcpy(out_buf.data(), cspan_to_copy.data(), cspan_to_copy.size());
386 9 : out_buf.reduce_size(cspan_to_copy.size());
387 :
388 9 : return CHIP_NO_ERROR;
389 : }
390 :
391 : } // namespace chip
|