Matter SDK Coverage Report
Current view: top level - lib/support - Span.h (source / functions) Coverage Total Hit
Test: SHA:09f6fdf93a7e847a42518c076e487f336877a722 Lines: 95.4 % 65 62
Test Date: 2025-06-07 07:10:33 Functions: 79.1 % 258 204

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

Generated by: LCOV version 2.0-1