LCOV - code coverage report
Current view: top level - lib/support - Span.h (source / functions) Hit Total Coverage
Test: lcov_final.info Lines: 58 64 90.6 %
Date: 2024-02-15 08:20:41 Functions: 144 194 74.2 %

          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       21392 :     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       50583 :     Span(pointer databuf, size_t datalen) : mDataBuf(databuf), mDataLen(datalen)
      53             :     {
      54       50583 :         VerifyOrDie(databuf != nullptr || datalen == 0); // not constexpr on some platforms
      55       50583 :     }
      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       11668 :     constexpr explicit Span(U (&databuf)[N]) : mDataBuf(databuf), mDataLen(N)
      68       11668 :     {}
      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         683 :     constexpr Span(std::array<U, N> & arr) : mDataBuf(arr.data()), mDataLen(N)
      83         683 :     {}
      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        4641 :     constexpr Span(const Span<U> & other) : Span(other.data(), other.size())
     108        4641 :     {}
     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      119468 :     constexpr pointer data() const { return mDataBuf; }
     119      203601 :     constexpr size_t size() const { return mDataLen; }
     120       14238 :     constexpr bool empty() const { return size() == 0; }
     121        3316 :     constexpr pointer begin() const { return data(); }
     122        3303 :     constexpr pointer end() const { return data() + size(); }
     123             : 
     124             :     // Element accessors, matching the std::span API.
     125           0 :     reference operator[](size_t index) const
     126             :     {
     127           0 :         VerifyOrDie(index < size()); // not constexpr on some platforms
     128           0 :         return data()[index];
     129             :     }
     130             :     reference front() const { return (*this)[0]; }
     131             :     reference back() const { return (*this)[size() - 1]; }
     132             : 
     133        1155 :     bool data_equal(const Span<const T> & other) const
     134             :     {
     135        1155 :         return (size() == other.size()) && (empty() || (memcmp(data(), other.data(), size() * sizeof(T)) == 0));
     136             :     }
     137             : 
     138        5749 :     Span SubSpan(size_t offset, size_t length) const
     139             :     {
     140        5749 :         VerifyOrDie(offset <= mDataLen);
     141        5749 :         VerifyOrDie(length <= mDataLen - offset);
     142        5749 :         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        7076 :     void reduce_size(size_t new_size)
     153             :     {
     154        7076 :         VerifyOrDie(new_size <= size());
     155        7076 :         mDataLen = new_size;
     156        7076 :     }
     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        1240 :     static Span fromCharString(U * chars)
     181             :     {
     182        1240 :         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       41032 : 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       41032 :     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       41032 :     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       10442 :     explicit FixedSpan(U databuf) : mDataBuf(databuf)
     276             :     {
     277       10442 :         VerifyOrDie(databuf != nullptr || N == 0); // not constexpr on some platforms
     278       10442 :     }
     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          39 :     constexpr explicit FixedSpan(U (&databuf)[M]) : mDataBuf(databuf)
     285             :     {
     286             :         static_assert(M >= N, "Passed-in buffer too small for FixedSpan");
     287          39 :     }
     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       37785 :     constexpr FixedSpan(std::array<U, N> & arr) : mDataBuf(arr.data())
     292       37785 :     {}
     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       53290 :     constexpr pointer data() const { return mDataBuf; }
     311          67 :     constexpr pointer begin() const { return mDataBuf; }
     312          67 :     constexpr pointer end() const { return mDataBuf + N; }
     313             : 
     314             :     // The size of a FixedSpan is always N. There is intentially no empty() method.
     315       53088 :     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        1956 :     bool data_equal(const Span<const T> & other) const
     330             :     {
     331        1956 :         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        2685 : constexpr Span<T>::Span(const FixedSpan<U, N> & other) : mDataBuf(other.data()), mDataLen(other.size())
     349        2685 : {}
     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             : 
     368             : using CharSpan        = Span<const char>;
     369             : using MutableCharSpan = Span<char>;
     370             : 
     371        2700 : inline CHIP_ERROR CopySpanToMutableSpan(ByteSpan span_to_copy, MutableByteSpan & out_buf)
     372             : {
     373        2700 :     VerifyOrReturnError(out_buf.size() >= span_to_copy.size(), CHIP_ERROR_BUFFER_TOO_SMALL);
     374             : 
     375        2699 :     memcpy(out_buf.data(), span_to_copy.data(), span_to_copy.size());
     376        2699 :     out_buf.reduce_size(span_to_copy.size());
     377             : 
     378        2699 :     return CHIP_NO_ERROR;
     379             : }
     380             : 
     381          11 : inline CHIP_ERROR CopyCharSpanToMutableCharSpan(CharSpan cspan_to_copy, MutableCharSpan & out_buf)
     382             : {
     383          11 :     VerifyOrReturnError(out_buf.size() >= cspan_to_copy.size(), CHIP_ERROR_BUFFER_TOO_SMALL);
     384             : 
     385           9 :     memcpy(out_buf.data(), cspan_to_copy.data(), cspan_to_copy.size());
     386           9 :     out_buf.reduce_size(cspan_to_copy.size());
     387             : 
     388           9 :     return CHIP_NO_ERROR;
     389             : }
     390             : 
     391             : } // namespace chip

Generated by: LCOV version 1.14