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 97029 : 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 1794081 : Span(pointer databuf, size_t datalen) : mDataBuf(databuf), mDataLen(datalen)
53 : {
54 1794081 : VerifyOrDie(databuf != nullptr || datalen == 0); // not constexpr on some platforms
55 1794081 : }
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 9565 : constexpr explicit Span(U (&databuf)[N]) : mDataBuf(databuf), mDataLen(N)
68 9565 : {}
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 682 : constexpr Span(std::array<U, N> & arr) : mDataBuf(arr.data()), mDataLen(N)
83 682 : {}
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 940 : constexpr Span(const Span<U> & other) : Span(other.data(), other.size())
108 940 : {}
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 3585406 : constexpr pointer data() const { return mDataBuf; }
119 1873650 : constexpr size_t size() const { return mDataLen; }
120 15949 : constexpr bool empty() const { return size() == 0; }
121 1767287 : constexpr pointer begin() const { return data(); }
122 1764862 : constexpr pointer end() const { return data() + size(); }
123 :
124 : // Element accessors, matching the std::span API.
125 16904 : reference operator[](size_t index) const
126 : {
127 16904 : VerifyOrDie(index < size()); // not constexpr on some platforms
128 16904 : return data()[index];
129 : }
130 2295 : reference front() const { return (*this)[0]; }
131 : reference back() const { return (*this)[size() - 1]; }
132 :
133 965 : bool data_equal(const Span<const T> & other) const
134 : {
135 965 : return (size() == other.size()) && (empty() || (memcmp(data(), other.data(), size() * sizeof(T)) == 0));
136 : }
137 :
138 6015 : Span SubSpan(size_t offset, size_t length) const
139 : {
140 6015 : VerifyOrDie(offset <= mDataLen);
141 6015 : VerifyOrDie(length <= mDataLen - offset);
142 6015 : return Span(mDataBuf + offset, length);
143 : }
144 :
145 2327 : Span SubSpan(size_t offset) const
146 : {
147 2327 : VerifyOrDie(offset <= mDataLen);
148 2327 : return Span(mDataBuf + offset, mDataLen - offset);
149 : }
150 :
151 : // Allow reducing the size of a span.
152 3284 : void reduce_size(size_t new_size)
153 : {
154 3284 : VerifyOrDie(new_size <= size());
155 3284 : mDataLen = new_size;
156 3284 : }
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 1386 : static Span fromCharString(U * chars)
181 : {
182 1386 : 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 : // Template deduction guides to allow construction of Span from a pointer or
208 : // array without having to specify the type of the entries explicitly.
209 : template <class T>
210 : Span(T * data, size_t size) -> Span<T>;
211 : template <class T, size_t N>
212 : Span(T (&databuf)[N]) -> Span<T>;
213 :
214 : inline namespace literals {
215 :
216 0 : inline constexpr Span<const char> operator""_span(const char * literal, size_t size)
217 : {
218 0 : return Span<const char>(Unchecked, literal, size);
219 : }
220 :
221 : } // namespace literals
222 :
223 : namespace detail {
224 :
225 : // To make FixedSpan (specifically various FixedByteSpan types) default constructible
226 : // without creating a weird "empty() == true but size() != 0" state, we need an
227 : // appropriate sized array of zeroes. With a naive definition like
228 : // template <class T, size_t N> constexpr T kZero[N] {};
229 : // we would end up with separate zero arrays for each size, and might also accidentally
230 : // increase the read-only data size of the binary by a large amount. Instead, we define
231 : // a per-type limit for the zero array, FixedSpan won't be default constructible for
232 : // T / N combinations that exceed the limit. The default limit is 0.
233 : template <class T>
234 : struct zero_limit : std::integral_constant<size_t, 0>
235 : {
236 : };
237 :
238 : // FixedByteSpan types up to N=65 currently need to be default-constructible.
239 : template <>
240 : struct zero_limit<uint8_t> : std::integral_constant<size_t, 65>
241 : {
242 : };
243 :
244 : template <class T>
245 : inline constexpr T kZeroes[zero_limit<T>::value]{};
246 :
247 : template <class T, size_t N>
248 47684 : constexpr T const * shared_zeroes()
249 : {
250 : static_assert(N <= zero_limit<typename std::remove_const<T>::type>::value, "N exceeds zero_limit<T>");
251 47684 : return kZeroes<typename std::remove_const<T>::type>;
252 : }
253 :
254 : } // namespace detail
255 :
256 : /**
257 : * Similar to a Span but with a fixed size.
258 : */
259 : template <class T, size_t N>
260 : class FixedSpan
261 : {
262 : public:
263 : using pointer = T *;
264 : using reference = T &;
265 :
266 : // Creates a FixedSpan pointing to a sequence of zeroes.
267 47684 : constexpr FixedSpan() : mDataBuf(detail::shared_zeroes<T, N>()) {}
268 :
269 : // We want to allow construction from things that look like T*, but we want
270 : // to make construction from an array use the constructor that asserts the
271 : // array is big enough. This requires that both constructors be templates
272 : // (because otherwise the non-template would be favored by overload
273 : // resolution, since due to decay to pointer it matches just as well as the
274 : // template).
275 : //
276 : // To do that we have a template constructor enabled only when the type
277 : // passed to it is a pointer type, and that pointer is to a type that
278 : // matches T's size and can convert to T*.
279 : template <class U,
280 : typename = std::enable_if_t<std::is_pointer<U>::value && sizeof(std::remove_pointer_t<U>) == sizeof(T) &&
281 : std::is_convertible<U, T *>::value>>
282 12255 : explicit FixedSpan(U databuf) : mDataBuf(databuf)
283 : {
284 12255 : VerifyOrDie(databuf != nullptr || N == 0); // not constexpr on some platforms
285 12255 : }
286 :
287 : // FixedSpan does not support an empty / null state.
288 : FixedSpan(std::nullptr_t null) = delete;
289 :
290 : template <class U, size_t M, typename = std::enable_if_t<sizeof(U) == sizeof(T) && std::is_convertible<U *, T *>::value>>
291 64 : constexpr explicit FixedSpan(U (&databuf)[M]) : mDataBuf(databuf)
292 : {
293 : static_assert(M >= N, "Passed-in buffer too small for FixedSpan");
294 64 : }
295 :
296 : // Creates a (potentially mutable) FixedSpan view of an std::array
297 : template <class U, typename = std::enable_if_t<sizeof(U) == sizeof(T) && std::is_convertible<U *, T *>::value>>
298 59458 : constexpr FixedSpan(std::array<U, N> & arr) : mDataBuf(arr.data())
299 59458 : {}
300 :
301 : template <class U, typename = std::enable_if_t<sizeof(U) == sizeof(T) && std::is_convertible<U *, T *>::value>>
302 : constexpr FixedSpan(std::array<U, N> && arr) = delete; // would be a view of an rvalue
303 :
304 : // Creates a FixedSpan view of an std::array
305 : template <class U, typename = std::enable_if_t<sizeof(U) == sizeof(T) && std::is_convertible<const U *, T *>::value>>
306 : constexpr FixedSpan(const std::array<U, N> & arr) : mDataBuf(arr.data())
307 : {}
308 :
309 : // Allow implicit construction from a FixedSpan of sufficient size over a
310 : // type that has the same size as ours, as long as the pointers are convertible.
311 : template <class U, size_t M, typename = std::enable_if_t<sizeof(U) == sizeof(T) && std::is_convertible<U *, T *>::value>>
312 : constexpr FixedSpan(FixedSpan<U, M> const & other) : mDataBuf(other.data())
313 : {
314 : static_assert(M >= N, "Passed-in FixedSpan is smaller than we are");
315 : }
316 :
317 77865 : constexpr pointer data() const { return mDataBuf; }
318 76 : constexpr pointer begin() const { return mDataBuf; }
319 76 : constexpr pointer end() const { return mDataBuf + N; }
320 :
321 : // The size of a FixedSpan is always N. There is intentially no empty() method.
322 77289 : static constexpr size_t size() { return N; }
323 :
324 : // Element accessors, matching the std::span API.
325 : // VerifyOrDie cannot be used inside a constexpr function, because it uses
326 : // "static" on some platforms (e.g. when CHIP_PW_TOKENIZER_LOGGING is true)
327 : // and that's not allowed in constexpr functions.
328 : reference operator[](size_t index) const
329 : {
330 : VerifyOrDie(index < N);
331 : return data()[index];
332 : }
333 : reference front() const { return (*this)[0]; }
334 : reference back() const { return (*this)[size() - 1]; }
335 :
336 2349 : bool data_equal(const Span<const T> & other) const
337 : {
338 2349 : return (N == other.size() && memcmp(data(), other.data(), N * sizeof(T)) == 0);
339 : }
340 :
341 : // operator== explicitly not implemented on FixedSpan, because its meaning
342 : // (equality of data, or pointing to the same buffer and same length) is
343 : // ambiguous. Use data_equal if testing for equality of data.
344 : template <typename U>
345 : bool operator==(const Span<U> & other) const = delete;
346 : template <typename U, size_t M>
347 : bool operator==(const FixedSpan<U, M> & other) const = delete;
348 :
349 : private:
350 : pointer mDataBuf;
351 : };
352 :
353 : template <class T>
354 : template <class U, size_t N, typename>
355 3149 : constexpr Span<T>::Span(const FixedSpan<U, N> & other) : mDataBuf(other.data()), mDataLen(other.size())
356 3149 : {}
357 :
358 : template <typename T>
359 : [[deprecated("Use !empty()")]] inline bool IsSpanUsable(const Span<T> & span)
360 : {
361 : return !span.empty();
362 : }
363 :
364 : template <typename T, size_t N>
365 : [[deprecated("FixedSpan is always usable / non-empty if N > 0")]] inline bool IsSpanUsable(const FixedSpan<T, N> & span)
366 : {
367 : return N > 0;
368 : }
369 :
370 : using ByteSpan = Span<const uint8_t>;
371 : using MutableByteSpan = Span<uint8_t>;
372 : template <size_t N>
373 : using FixedByteSpan = FixedSpan<const uint8_t, N>;
374 : template <size_t N>
375 : using MutableFixedByteSpan = FixedSpan<uint8_t, N>;
376 :
377 : using CharSpan = Span<const char>;
378 : using MutableCharSpan = Span<char>;
379 :
380 2172 : inline CHIP_ERROR CopySpanToMutableSpan(ByteSpan span_to_copy, MutableByteSpan & out_buf)
381 : {
382 2172 : VerifyOrReturnError(out_buf.size() >= span_to_copy.size(), CHIP_ERROR_BUFFER_TOO_SMALL);
383 :
384 : // There is no guarantee that span_to_copy and out_buf don't overlap, so use memmove()
385 2172 : memmove(out_buf.data(), span_to_copy.data(), span_to_copy.size());
386 2172 : out_buf.reduce_size(span_to_copy.size());
387 :
388 2172 : return CHIP_NO_ERROR;
389 : }
390 :
391 11 : inline CHIP_ERROR CopyCharSpanToMutableCharSpan(CharSpan cspan_to_copy, MutableCharSpan & out_buf)
392 : {
393 11 : VerifyOrReturnError(out_buf.size() >= cspan_to_copy.size(), CHIP_ERROR_BUFFER_TOO_SMALL);
394 :
395 : // There is no guarantee that cspan_to_copy and out_buf don't overlap, so use memmove()
396 8 : memmove(out_buf.data(), cspan_to_copy.data(), cspan_to_copy.size());
397 8 : out_buf.reduce_size(cspan_to_copy.size());
398 :
399 8 : return CHIP_NO_ERROR;
400 : }
401 :
402 : /**
403 : * Copies a CharSpan into a MutableCharSpan.
404 : * If the span_to_copy does not fit in out_span, span_to_copy is truncated to fit in out_span.
405 : * @param span_to_copy The CharSpan to copy.
406 : * @param out_span The MutableCharSpan in which span_to_copy is to be copied.
407 : */
408 : inline void CopyCharSpanToMutableCharSpanWithTruncation(CharSpan span_to_copy, MutableCharSpan & out_span)
409 : {
410 : size_t size_to_copy = std::min(span_to_copy.size(), out_span.size());
411 :
412 : // There is no guarantee that span_to_copy and out_buf don't overlap, so use memmove()
413 : memmove(out_span.data(), span_to_copy.data(), size_to_copy);
414 : out_span.reduce_size(size_to_copy);
415 : }
416 :
417 : } // namespace chip
|