Matter SDK Coverage Report
Current view: top level - lib/support - Span.h (source / functions) Coverage Total Hit
Test: SHA:b879ecb8e99e175eea0a293a888bda853da2b19c Lines: 95.3 % 64 61
Test Date: 2025-01-17 19:00:11 Functions: 77.3 % 194 150

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

Generated by: LCOV version 2.0-1