Matter SDK Coverage Report
Current view: top level - lib/support - Span.h (source / functions) Coverage Total Hit
Test: SHA:5853f10e345717417494f970a7d13b422d94af51 Lines: 95.4 % 65 62
Test Date: 2025-06-30 07:09:23 Functions: 80.1 % 271 217

            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
        

Generated by: LCOV version 2.0-1