Matter SDK Coverage Report
Current view: top level - lib/support - Span.h (source / functions) Coverage Total Hit
Test: SHA:da00cfb13e586707ec8d44ae49325a696d4a8855 Lines: 96.8 % 93 90
Test Date: 2025-08-16 07:11:35 Functions: 94.5 % 752 711

            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
        

Generated by: LCOV version 2.0-1