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 245043 : 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 1854661 : Span(pointer databuf, size_t datalen) : mDataBuf(databuf), mDataLen(datalen)
53 : {
54 1854661 : VerifyOrDie(databuf != nullptr || datalen == 0); // not constexpr on some platforms
55 1854661 : }
56 :
57 : // The only valid length for a span pointing to null is 0 (i.e. it's an empty span). Disallow
58 : // construction of spans from compile-time-known null. The default constructor should be used
59 : // to construct empty Spans. All other cases involving null are invalid.
60 : Span(std::nullptr_t null, size_t size) = delete;
61 :
62 : // Creates a Span view of a plain array.
63 : //
64 : // Note that this constructor template explicitly excludes `const char[]`, see below.
65 : template <
66 : class U, size_t N,
67 : std::enable_if_t<!std::is_same_v<U, const char> && sizeof(U) == sizeof(T) && std::is_convertible_v<U *, T *>, bool> = true>
68 16471 : constexpr explicit Span(U (&databuf)[N]) : mDataBuf(databuf), mDataLen(N)
69 16471 : {}
70 :
71 : // Explicitly disallow the creation of a CharSpan from a `const char[]` to prevent the
72 : // creation of spans from string literals that incorrectly include the trailing '\0' byte.
73 : // If CharSpan("Hi!") were allowed, it would be a span of length 4, not 3 as intended.
74 : //
75 : // To create a CharSpan literal, use the `_span` operator instead, e.g. "Hi!"_span.
76 : template <
77 : class U, size_t N,
78 : std::enable_if_t<std::is_same_v<U, const char> && 1 == sizeof(T) && std::is_convertible_v<const char *, T *>, bool> = true>
79 : constexpr explicit Span(U (&databuf)[N]) = delete;
80 :
81 : // Creates a (potentially mutable) Span view of an std::array
82 : template <class U, size_t N, typename = std::enable_if_t<sizeof(U) == sizeof(T) && std::is_convertible<U *, T *>::value>>
83 818 : constexpr Span(std::array<U, N> & arr) : mDataBuf(arr.data()), mDataLen(N)
84 818 : {}
85 :
86 : template <class U, size_t N, typename = std::enable_if_t<sizeof(U) == sizeof(T) && std::is_convertible<U *, T *>::value>>
87 : constexpr Span(std::array<U, N> && arr) = delete; // would be a view of an rvalue
88 :
89 : // Creates a Span view of an std::array
90 : template <class U, size_t N, typename = std::enable_if_t<sizeof(U) == sizeof(T) && std::is_convertible<const U *, T *>::value>>
91 28 : constexpr Span(const std::array<U, N> & arr) : mDataBuf(arr.data()), mDataLen(N)
92 28 : {}
93 :
94 : template <size_t N>
95 865 : constexpr Span & operator=(T (&databuf)[N])
96 : {
97 865 : mDataBuf = databuf;
98 865 : mDataLen = N;
99 865 : return (*this);
100 : }
101 :
102 : // Allow implicit construction from a Span over a type that matches our
103 : // type's size, if a pointer to the other type can be treated as a pointer
104 : // to our type (e.g. other type is same as ours, or is a same-size
105 : // subclass). The size check is really important to make sure we don't get
106 : // confused about where our object boundaries are.
107 : template <class U, typename = std::enable_if_t<sizeof(U) == sizeof(T) && std::is_convertible<U *, T *>::value>>
108 5745 : constexpr Span(const Span<U> & other) : Span(other.data(), other.size())
109 5745 : {}
110 :
111 : // Allow implicit construction from a FixedSpan over a type that matches our
112 : // type's size, if a pointer to the other type can be treated as a pointer
113 : // to our type (e.g. other type is same as ours, or is a same-size
114 : // subclass). The size check is really important to make sure we don't get
115 : // confused about where our object boundaries are.
116 : template <class U, size_t N, typename = std::enable_if_t<sizeof(U) == sizeof(T) && std::is_convertible<U *, T *>::value>>
117 : constexpr inline Span(const FixedSpan<U, N> & other);
118 :
119 3700862 : constexpr pointer data() const { return mDataBuf; }
120 2055714 : constexpr size_t size() const { return mDataLen; }
121 26247 : constexpr bool empty() const { return size() == 0; }
122 1777609 : constexpr pointer begin() const { return data(); }
123 1775185 : constexpr pointer end() const { return data() + size(); }
124 :
125 : // Element accessors, matching the std::span API.
126 28667 : reference operator[](size_t index) const
127 : {
128 28667 : VerifyOrDie(index < size()); // not constexpr on some platforms
129 28667 : return data()[index];
130 : }
131 2157 : reference front() const { return (*this)[0]; }
132 1 : reference back() const { return (*this)[size() - 1]; }
133 :
134 2032 : bool data_equal(const Span<const T> & other) const
135 : {
136 2032 : return (size() == other.size()) && (empty() || (memcmp(data(), other.data(), size() * sizeof(T)) == 0));
137 : }
138 :
139 6129 : Span SubSpan(size_t offset, size_t length) const
140 : {
141 6129 : VerifyOrDie(offset <= mDataLen);
142 6129 : VerifyOrDie(length <= mDataLen - offset);
143 6129 : return Span(mDataBuf + offset, length);
144 : }
145 :
146 2337 : Span SubSpan(size_t offset) const
147 : {
148 2337 : VerifyOrDie(offset <= mDataLen);
149 2337 : return Span(mDataBuf + offset, mDataLen - offset);
150 : }
151 :
152 : // Allow reducing the size of a span.
153 8598 : void reduce_size(size_t new_size)
154 : {
155 8598 : VerifyOrDie(new_size <= size());
156 8598 : mDataLen = new_size;
157 8598 : }
158 :
159 : // Allow creating ByteSpans and CharSpans from ZCL octet strings, so we
160 : // don't have to reinvent it various places.
161 : template <class U,
162 : typename = std::enable_if_t<std::is_same<uint8_t, std::remove_const_t<U>>::value &&
163 : (std::is_same<const uint8_t, T>::value || std::is_same<const char, T>::value)>>
164 2 : static Span fromZclString(U * bytes)
165 : {
166 2 : size_t length = bytes[0];
167 : // Treat 0xFF (aka "null string") as zero-length.
168 2 : if (length == 0xFF)
169 : {
170 0 : length = 0;
171 : }
172 : // Need reinterpret_cast if we're a CharSpan.
173 2 : return Span(reinterpret_cast<T *>(&bytes[1]), length);
174 : }
175 :
176 : // Creates a CharSpan from a null-terminated C character string.
177 : //
178 : // Note that for string literals, the user-defined `_span` string
179 : // literal operator should be used instead, e.g. `"Hello"_span`.
180 : template <class U, typename = std::enable_if_t<std::is_same<T, const U>::value && std::is_same<const char, T>::value>>
181 1577 : static Span fromCharString(U * chars)
182 : {
183 1577 : return Span(chars, strlen(chars));
184 : }
185 :
186 : // operator== explicitly not implemented on Span, because its meaning
187 : // (equality of data, or pointing to the same buffer and same length) is
188 : // ambiguous. Use data_equal if testing for equality of data.
189 : template <typename U>
190 : bool operator==(const Span<U> & other) const = delete;
191 :
192 : // Creates a Span without checking whether databuf is a null pointer.
193 : //
194 : // Note: The normal (checked) constructor should be used for general use;
195 : // this overload exists for special use cases where databuf is guaranteed
196 : // to be valid (not null) and a constexpr constructor is required.
197 : //
198 : // https://gcc.gnu.org/bugzilla/show_bug.cgi?id=61648 prevents making
199 : // operator""_span a friend (and this constructor private).
200 :
201 188 : constexpr Span(UncheckedType tag, pointer databuf, size_t datalen) : mDataBuf(databuf), mDataLen(datalen) {}
202 :
203 : private:
204 : pointer mDataBuf;
205 : size_t mDataLen;
206 : };
207 :
208 : // Template deduction guides to allow construction of Span from a pointer or
209 : // array without having to specify the type of the entries explicitly.
210 : template <class T>
211 : Span(T * data, size_t size) -> Span<T>;
212 : template <class T, size_t N>
213 : Span(T (&databuf)[N]) -> Span<T>;
214 : template <class T, size_t N>
215 : Span(std::array<T, N> & data) -> Span<T>;
216 : template <class T, size_t N>
217 : Span(const std::array<T, N> & data) -> Span<const T>;
218 :
219 : inline namespace literals {
220 :
221 188 : inline constexpr Span<const char> operator""_span(const char * literal, size_t size)
222 : {
223 188 : return Span<const char>(Unchecked, literal, size);
224 : }
225 :
226 : } // namespace literals
227 :
228 : namespace detail {
229 :
230 : // To make FixedSpan (specifically various FixedByteSpan types) default constructible
231 : // without creating a weird "empty() == true but size() != 0" state, we need an
232 : // appropriate sized array of zeroes. With a naive definition like
233 : // template <class T, size_t N> constexpr T kZero[N] {};
234 : // we would end up with separate zero arrays for each size, and might also accidentally
235 : // increase the read-only data size of the binary by a large amount. Instead, we define
236 : // a per-type limit for the zero array, FixedSpan won't be default constructible for
237 : // T / N combinations that exceed the limit. The default limit is 0.
238 : template <class T>
239 : struct zero_limit : std::integral_constant<size_t, 0>
240 : {
241 : };
242 :
243 : // FixedByteSpan types up to N=65 currently need to be default-constructible.
244 : template <>
245 : struct zero_limit<uint8_t> : std::integral_constant<size_t, 65>
246 : {
247 : };
248 :
249 : template <class T>
250 : inline constexpr T kZeroes[zero_limit<T>::value]{};
251 :
252 : template <class T, size_t N>
253 48644 : constexpr T const * shared_zeroes()
254 : {
255 : static_assert(N <= zero_limit<typename std::remove_const<T>::type>::value, "N exceeds zero_limit<T>");
256 48644 : return kZeroes<typename std::remove_const<T>::type>;
257 : }
258 :
259 : } // namespace detail
260 :
261 : /**
262 : * Similar to a Span but with a fixed size.
263 : */
264 : template <class T, size_t N>
265 : class FixedSpan
266 : {
267 : public:
268 : using pointer = T *;
269 : using reference = T &;
270 :
271 : // Creates a FixedSpan pointing to a sequence of zeroes.
272 48644 : constexpr FixedSpan() : mDataBuf(detail::shared_zeroes<T, N>()) {}
273 :
274 : // We want to allow construction from things that look like T*, but we want
275 : // to make construction from an array use the constructor that asserts the
276 : // array is big enough. This requires that both constructors be templates
277 : // (because otherwise the non-template would be favored by overload
278 : // resolution, since due to decay to pointer it matches just as well as the
279 : // template).
280 : //
281 : // To do that we have a template constructor enabled only when the type
282 : // passed to it is a pointer type, and that pointer is to a type that
283 : // matches T's size and can convert to T*.
284 : template <class U,
285 : typename = std::enable_if_t<std::is_pointer<U>::value && sizeof(std::remove_pointer_t<U>) == sizeof(T) &&
286 : std::is_convertible<U, T *>::value>>
287 12495 : explicit FixedSpan(U databuf) : mDataBuf(databuf)
288 : {
289 12495 : VerifyOrDie(databuf != nullptr || N == 0); // not constexpr on some platforms
290 12495 : }
291 :
292 : // FixedSpan does not support an empty / null state.
293 : FixedSpan(std::nullptr_t null) = delete;
294 :
295 : template <class U, size_t M, typename = std::enable_if_t<sizeof(U) == sizeof(T) && std::is_convertible<U *, T *>::value>>
296 75 : constexpr explicit FixedSpan(U (&databuf)[M]) : mDataBuf(databuf)
297 : {
298 : static_assert(M >= N, "Passed-in buffer too small for FixedSpan");
299 75 : }
300 :
301 : // Creates a (potentially mutable) FixedSpan view of an std::array
302 : template <class U, typename = std::enable_if_t<sizeof(U) == sizeof(T) && std::is_convertible<U *, T *>::value>>
303 60343 : constexpr FixedSpan(std::array<U, N> & arr) : mDataBuf(arr.data())
304 60343 : {}
305 :
306 : template <class U, typename = std::enable_if_t<sizeof(U) == sizeof(T) && std::is_convertible<U *, T *>::value>>
307 : constexpr FixedSpan(std::array<U, N> && arr) = delete; // would be a view of an rvalue
308 :
309 : // Creates a FixedSpan view of an std::array
310 : template <class U, typename = std::enable_if_t<sizeof(U) == sizeof(T) && std::is_convertible<const U *, T *>::value>>
311 5 : constexpr FixedSpan(const std::array<U, N> & arr) : mDataBuf(arr.data())
312 5 : {}
313 :
314 : // Allow implicit construction from a FixedSpan of sufficient size over a
315 : // type that has the same size as ours, as long as the pointers are convertible.
316 : template <class U, size_t M, typename = std::enable_if_t<sizeof(U) == sizeof(T) && std::is_convertible<U *, T *>::value>>
317 3 : constexpr FixedSpan(FixedSpan<U, M> const & other) : mDataBuf(other.data())
318 : {
319 : static_assert(M >= N, "Passed-in FixedSpan is smaller than we are");
320 3 : }
321 :
322 80943 : constexpr pointer data() const { return mDataBuf; }
323 78 : constexpr pointer begin() const { return mDataBuf; }
324 78 : constexpr pointer end() const { return mDataBuf + N; }
325 :
326 : // The size of a FixedSpan is always N. There is intentially no empty() method.
327 77854 : static constexpr size_t size() { return N; }
328 :
329 : // Element accessors, matching the std::span API.
330 : // VerifyOrDie cannot be used inside a constexpr function, because it uses
331 : // "static" on some platforms (e.g. when CHIP_PW_TOKENIZER_LOGGING is true)
332 : // and that's not allowed in constexpr functions.
333 10 : reference operator[](size_t index) const
334 : {
335 10 : VerifyOrDie(index < N);
336 10 : return data()[index];
337 : }
338 1 : reference front() const { return (*this)[0]; }
339 1 : reference back() const { return (*this)[size() - 1]; }
340 :
341 2669 : bool data_equal(const Span<const T> & other) const
342 : {
343 2669 : return (N == other.size() && memcmp(data(), other.data(), N * sizeof(T)) == 0);
344 : }
345 :
346 : // operator== explicitly not implemented on FixedSpan, because its meaning
347 : // (equality of data, or pointing to the same buffer and same length) is
348 : // ambiguous. Use data_equal if testing for equality of data.
349 : template <typename U>
350 : bool operator==(const Span<U> & other) const = delete;
351 : template <typename U, size_t M>
352 : bool operator==(const FixedSpan<U, M> & other) const = delete;
353 :
354 : private:
355 : pointer mDataBuf;
356 : };
357 :
358 : template <class T>
359 : template <class U, size_t N, typename>
360 3233 : constexpr Span<T>::Span(const FixedSpan<U, N> & other) : mDataBuf(other.data()), mDataLen(other.size())
361 3233 : {}
362 :
363 : template <typename T>
364 0 : [[deprecated("Use !empty()")]] inline bool IsSpanUsable(const Span<T> & span)
365 : {
366 0 : return !span.empty();
367 : }
368 :
369 : template <typename T, size_t N>
370 : [[deprecated("FixedSpan is always usable / non-empty if N > 0")]] inline bool IsSpanUsable(const FixedSpan<T, N> & span)
371 : {
372 : return N > 0;
373 : }
374 :
375 : using ByteSpan = Span<const uint8_t>;
376 : using MutableByteSpan = Span<uint8_t>;
377 : template <size_t N>
378 : using FixedByteSpan = FixedSpan<const uint8_t, N>;
379 : template <size_t N>
380 : using MutableFixedByteSpan = FixedSpan<uint8_t, N>;
381 :
382 : using CharSpan = Span<const char>;
383 : using MutableCharSpan = Span<char>;
384 :
385 3256 : inline CHIP_ERROR CopySpanToMutableSpan(ByteSpan span_to_copy, MutableByteSpan & out_buf)
386 : {
387 3256 : VerifyOrReturnError(out_buf.size() >= span_to_copy.size(), CHIP_ERROR_BUFFER_TOO_SMALL);
388 :
389 : // There is no guarantee that span_to_copy and out_buf don't overlap, so use memmove()
390 3255 : memmove(out_buf.data(), span_to_copy.data(), span_to_copy.size());
391 3255 : out_buf.reduce_size(span_to_copy.size());
392 :
393 3255 : return CHIP_NO_ERROR;
394 : }
395 :
396 14 : inline CHIP_ERROR CopyCharSpanToMutableCharSpan(CharSpan cspan_to_copy, MutableCharSpan & out_buf)
397 : {
398 14 : VerifyOrReturnError(out_buf.size() >= cspan_to_copy.size(), CHIP_ERROR_BUFFER_TOO_SMALL);
399 :
400 : // There is no guarantee that cspan_to_copy and out_buf don't overlap, so use memmove()
401 11 : memmove(out_buf.data(), cspan_to_copy.data(), cspan_to_copy.size());
402 11 : out_buf.reduce_size(cspan_to_copy.size());
403 :
404 11 : return CHIP_NO_ERROR;
405 : }
406 :
407 : /**
408 : * Copies a CharSpan into a MutableCharSpan.
409 : * If the span_to_copy does not fit in out_span, span_to_copy is truncated to fit in out_span.
410 : * @param span_to_copy The CharSpan to copy.
411 : * @param out_span The MutableCharSpan in which span_to_copy is to be copied.
412 : */
413 45 : inline void CopyCharSpanToMutableCharSpanWithTruncation(CharSpan span_to_copy, MutableCharSpan & out_span)
414 : {
415 45 : size_t size_to_copy = std::min(span_to_copy.size(), out_span.size());
416 :
417 : // There is no guarantee that span_to_copy and out_buf don't overlap, so use memmove()
418 45 : memmove(out_span.data(), span_to_copy.data(), size_to_copy);
419 45 : out_span.reduce_size(size_to_copy);
420 45 : }
421 :
422 : } // namespace chip
|