LCOV - code coverage report
Current view: top level - src - span.h (source / functions) Hit Total Coverage
Test: coverage.lcov Lines: 66 74 89.2 %
Date: 2022-04-21 14:51:19 Functions: 232 236 98.3 %
Legend: Modified by patch:
Lines: hit not hit | Branches: + taken - not taken # not executed

Not modified by patch:
Lines: hit not hit | Branches: + taken - not taken # not executed
Branches: 7 8 87.5 %

           Branch data     Line data    Source code
#       1                 :            : // Copyright (c) 2018-2021 The Bitcoin Core developers
#       2                 :            : // Distributed under the MIT software license, see the accompanying
#       3                 :            : // file COPYING or http://www.opensource.org/licenses/mit-license.php.
#       4                 :            : 
#       5                 :            : #ifndef BITCOIN_SPAN_H
#       6                 :            : #define BITCOIN_SPAN_H
#       7                 :            : 
#       8                 :            : #include <type_traits>
#       9                 :            : #include <cstddef>
#      10                 :            : #include <algorithm>
#      11                 :            : #include <assert.h>
#      12                 :            : 
#      13                 :            : #ifdef DEBUG
#      14                 :            : #define CONSTEXPR_IF_NOT_DEBUG
#      15                 :   44459926 : #define ASSERT_IF_DEBUG(x) assert((x))
#      16                 :            : #else
#      17                 :            : #define CONSTEXPR_IF_NOT_DEBUG constexpr
#      18                 :            : #define ASSERT_IF_DEBUG(x)
#      19                 :            : #endif
#      20                 :            : 
#      21                 :            : #if defined(__clang__)
#      22                 :            : #if __has_attribute(lifetimebound)
#      23                 :            : #define SPAN_ATTR_LIFETIMEBOUND [[clang::lifetimebound]]
#      24                 :            : #else
#      25                 :            : #define SPAN_ATTR_LIFETIMEBOUND
#      26                 :            : #endif
#      27                 :            : #else
#      28                 :            : #define SPAN_ATTR_LIFETIMEBOUND
#      29                 :            : #endif
#      30                 :            : 
#      31                 :            : /** A Span is an object that can refer to a contiguous sequence of objects.
#      32                 :            :  *
#      33                 :            :  * This file implements a subset of C++20's std::span.  It can be considered
#      34                 :            :  * temporary compatibility code until C++20 and is designed to be a
#      35                 :            :  * self-contained abstraction without depending on other project files. For this
#      36                 :            :  * reason, Clang lifetimebound is defined here instead of including
#      37                 :            :  * <attributes.h>, which also defines it.
#      38                 :            :  *
#      39                 :            :  * Things to be aware of when writing code that deals with Spans:
#      40                 :            :  *
#      41                 :            :  * - Similar to references themselves, Spans are subject to reference lifetime
#      42                 :            :  *   issues. The user is responsible for making sure the objects pointed to by
#      43                 :            :  *   a Span live as long as the Span is used. For example:
#      44                 :            :  *
#      45                 :            :  *       std::vector<int> vec{1,2,3,4};
#      46                 :            :  *       Span<int> sp(vec);
#      47                 :            :  *       vec.push_back(5);
#      48                 :            :  *       printf("%i\n", sp.front()); // UB!
#      49                 :            :  *
#      50                 :            :  *   may exhibit undefined behavior, as increasing the size of a vector may
#      51                 :            :  *   invalidate references.
#      52                 :            :  *
#      53                 :            :  * - One particular pitfall is that Spans can be constructed from temporaries,
#      54                 :            :  *   but this is unsafe when the Span is stored in a variable, outliving the
#      55                 :            :  *   temporary. For example, this will compile, but exhibits undefined behavior:
#      56                 :            :  *
#      57                 :            :  *       Span<const int> sp(std::vector<int>{1, 2, 3});
#      58                 :            :  *       printf("%i\n", sp.front()); // UB!
#      59                 :            :  *
#      60                 :            :  *   The lifetime of the vector ends when the statement it is created in ends.
#      61                 :            :  *   Thus the Span is left with a dangling reference, and using it is undefined.
#      62                 :            :  *
#      63                 :            :  * - Due to Span's automatic creation from range-like objects (arrays, and data
#      64                 :            :  *   types that expose a data() and size() member function), functions that
#      65                 :            :  *   accept a Span as input parameter can be called with any compatible
#      66                 :            :  *   range-like object. For example, this works:
#      67                 :            :  *
#      68                 :            :  *       void Foo(Span<const int> arg);
#      69                 :            :  *
#      70                 :            :  *       Foo(std::vector<int>{1, 2, 3}); // Works
#      71                 :            :  *
#      72                 :            :  *   This is very useful in cases where a function truly does not care about the
#      73                 :            :  *   container, and only about having exactly a range of elements. However it
#      74                 :            :  *   may also be surprising to see automatic conversions in this case.
#      75                 :            :  *
#      76                 :            :  *   When a function accepts a Span with a mutable element type, it will not
#      77                 :            :  *   accept temporaries; only variables or other references. For example:
#      78                 :            :  *
#      79                 :            :  *       void FooMut(Span<int> arg);
#      80                 :            :  *
#      81                 :            :  *       FooMut(std::vector<int>{1, 2, 3}); // Does not compile
#      82                 :            :  *       std::vector<int> baz{1, 2, 3};
#      83                 :            :  *       FooMut(baz); // Works
#      84                 :            :  *
#      85                 :            :  *   This is similar to how functions that take (non-const) lvalue references
#      86                 :            :  *   as input cannot accept temporaries. This does not work either:
#      87                 :            :  *
#      88                 :            :  *       void FooVec(std::vector<int>& arg);
#      89                 :            :  *       FooVec(std::vector<int>{1, 2, 3}); // Does not compile
#      90                 :            :  *
#      91                 :            :  *   The idea is that if a function accepts a mutable reference, a meaningful
#      92                 :            :  *   result will be present in that variable after the call. Passing a temporary
#      93                 :            :  *   is useless in that context.
#      94                 :            :  */
#      95                 :            : template<typename C>
#      96                 :            : class Span
#      97                 :            : {
#      98                 :            :     C* m_data;
#      99                 :            :     std::size_t m_size;
#     100                 :            : 
#     101                 :            :     template <class T>
#     102                 :            :     struct is_Span_int : public std::false_type {};
#     103                 :            :     template <class T>
#     104                 :            :     struct is_Span_int<Span<T>> : public std::true_type {};
#     105                 :            :     template <class T>
#     106                 :            :     struct is_Span : public is_Span_int<typename std::remove_cv<T>::type>{};
#     107                 :            : 
#     108                 :            : 
#     109                 :            : public:
#     110                 :          2 :     constexpr Span() noexcept : m_data(nullptr), m_size(0) {}
#     111                 :            : 
#     112                 :            :     /** Construct a span from a begin pointer and a size.
#     113                 :            :      *
#     114                 :            :      * This implements a subset of the iterator-based std::span constructor in C++20,
#     115                 :            :      * which is hard to implement without std::address_of.
#     116                 :            :      */
#     117                 :            :     template <typename T, typename std::enable_if<std::is_convertible<T (*)[], C (*)[]>::value, int>::type = 0>
#     118                 :  873555876 :     constexpr Span(T* begin, std::size_t size) noexcept : m_data(begin), m_size(size) {}
#     119                 :            : 
#     120                 :            :     /** Construct a span from a begin and end pointer.
#     121                 :            :      *
#     122                 :            :      * This implements a subset of the iterator-based std::span constructor in C++20,
#     123                 :            :      * which is hard to implement without std::address_of.
#     124                 :            :      */
#     125                 :            :     template <typename T, typename std::enable_if<std::is_convertible<T (*)[], C (*)[]>::value, int>::type = 0>
#     126                 :            :     CONSTEXPR_IF_NOT_DEBUG Span(T* begin, T* end) noexcept : m_data(begin), m_size(end - begin)
#     127                 :      84540 :     {
#     128                 :      84540 :         ASSERT_IF_DEBUG(end >= begin);
#     129                 :      84540 :     }
#     130                 :            : 
#     131                 :            :     /** Implicit conversion of spans between compatible types.
#     132                 :            :      *
#     133                 :            :      *  Specifically, if a pointer to an array of type O can be implicitly converted to a pointer to an array of type
#     134                 :            :      *  C, then permit implicit conversion of Span<O> to Span<C>. This matches the behavior of the corresponding
#     135                 :            :      *  C++20 std::span constructor.
#     136                 :            :      *
#     137                 :            :      *  For example this means that a Span<T> can be converted into a Span<const T>.
#     138                 :            :      */
#     139                 :            :     template <typename O, typename std::enable_if<std::is_convertible<O (*)[], C (*)[]>::value, int>::type = 0>
#     140                 :    1359223 :     constexpr Span(const Span<O>& other) noexcept : m_data(other.m_data), m_size(other.m_size) {}
#     141                 :            : 
#     142                 :            :     /** Default copy constructor. */
#     143                 :            :     constexpr Span(const Span&) noexcept = default;
#     144                 :            : 
#     145                 :            :     /** Default assignment operator. */
#     146                 :            :     Span& operator=(const Span& other) noexcept = default;
#     147                 :            : 
#     148                 :            :     /** Construct a Span from an array. This matches the corresponding C++20 std::span constructor. */
#     149                 :            :     template <int N>
#     150                 :  100417104 :     constexpr Span(C (&a)[N]) noexcept : m_data(a), m_size(N) {}
#     151                 :            : 
#     152                 :            :     /** Construct a Span for objects with .data() and .size() (std::string, std::array, std::vector, ...).
#     153                 :            :      *
#     154                 :            :      * This implements a subset of the functionality provided by the C++20 std::span range-based constructor.
#     155                 :            :      *
#     156                 :            :      * To prevent surprises, only Spans for constant value types are supported when passing in temporaries.
#     157                 :            :      * Note that this restriction does not exist when converting arrays or other Spans (see above).
#     158                 :            :      */
#     159                 :            :     template <typename V>
#     160                 :            :     constexpr Span(V& other SPAN_ATTR_LIFETIMEBOUND,
#     161                 :            :         typename std::enable_if<!is_Span<V>::value &&
#     162                 :            :                                 std::is_convertible<typename std::remove_pointer<decltype(std::declval<V&>().data())>::type (*)[], C (*)[]>::value &&
#     163                 :            :                                 std::is_convertible<decltype(std::declval<V&>().size()), std::size_t>::value, std::nullptr_t>::type = nullptr)
#     164                 :   16355157 :         : m_data(other.data()), m_size(other.size()){}
#     165                 :            : 
#     166                 :            :     template <typename V>
#     167                 :            :     constexpr Span(const V& other SPAN_ATTR_LIFETIMEBOUND,
#     168                 :            :         typename std::enable_if<!is_Span<V>::value &&
#     169                 :            :                                 std::is_convertible<typename std::remove_pointer<decltype(std::declval<const V&>().data())>::type (*)[], C (*)[]>::value &&
#     170                 :            :                                 std::is_convertible<decltype(std::declval<const V&>().size()), std::size_t>::value, std::nullptr_t>::type = nullptr)
#     171                 :   56064092 :         : m_data(other.data()), m_size(other.size()){}
#     172                 :            : 
#     173                 :  929051117 :     constexpr C* data() const noexcept { return m_data; }
#     174                 :   72993668 :     constexpr C* begin() const noexcept { return m_data; }
#     175                 :   96490050 :     constexpr C* end() const noexcept { return m_data + m_size; }
#     176                 :            :     CONSTEXPR_IF_NOT_DEBUG C& front() const noexcept
#     177                 :       7426 :     {
#     178                 :       7426 :         ASSERT_IF_DEBUG(size() > 0);
#     179                 :          0 :         return m_data[0];
#     180                 :       7426 :     }
#     181                 :            :     CONSTEXPR_IF_NOT_DEBUG C& back() const noexcept
#     182                 :     146113 :     {
#     183                 :     146113 :         ASSERT_IF_DEBUG(size() > 0);
#     184                 :          0 :         return m_data[m_size - 1];
#     185                 :     146113 :     }
#     186                 :  636885108 :     constexpr std::size_t size() const noexcept { return m_size; }
#     187                 :  490202059 :     constexpr std::size_t size_bytes() const noexcept { return sizeof(C) * m_size; }
#     188                 :       5693 :     constexpr bool empty() const noexcept { return size() == 0; }
#     189                 :            :     CONSTEXPR_IF_NOT_DEBUG C& operator[](std::size_t pos) const noexcept
#     190                 :   19101003 :     {
#     191                 :   19101003 :         ASSERT_IF_DEBUG(size() > pos);
#     192                 :          0 :         return m_data[pos];
#     193                 :   19101003 :     }
#     194                 :            :     CONSTEXPR_IF_NOT_DEBUG Span<C> subspan(std::size_t offset) const noexcept
#     195                 :   17221501 :     {
#     196                 :   17221501 :         ASSERT_IF_DEBUG(size() >= offset);
#     197                 :          0 :         return Span<C>(m_data + offset, m_size - offset);
#     198                 :   17221501 :     }
#     199                 :            :     CONSTEXPR_IF_NOT_DEBUG Span<C> subspan(std::size_t offset, std::size_t count) const noexcept
#     200                 :    1278180 :     {
#     201                 :    1278180 :         ASSERT_IF_DEBUG(size() >= offset + count);
#     202                 :          0 :         return Span<C>(m_data + offset, count);
#     203                 :    1278180 :     }
#     204                 :            :     CONSTEXPR_IF_NOT_DEBUG Span<C> first(std::size_t count) const noexcept
#     205                 :    6441492 :     {
#     206                 :    6441492 :         ASSERT_IF_DEBUG(size() >= count);
#     207                 :          0 :         return Span<C>(m_data, count);
#     208                 :    6441492 :     }
#     209                 :            :     CONSTEXPR_IF_NOT_DEBUG Span<C> last(std::size_t count) const noexcept
#     210                 :       1914 :     {
#     211                 :       1914 :          ASSERT_IF_DEBUG(size() >= count);
#     212                 :          0 :          return Span<C>(m_data + m_size - count, count);
#     213                 :       1914 :     }
#     214                 :            : 
#     215 [ +  - ][ +  + ]:       4152 :     friend constexpr bool operator==(const Span& a, const Span& b) noexcept { return a.size() == b.size() && std::equal(a.begin(), a.end(), b.begin()); }
#         [ +  + ][ +  + ]
#     216                 :         56 :     friend constexpr bool operator!=(const Span& a, const Span& b) noexcept { return !(a == b); }
#     217                 :            :     friend constexpr bool operator<(const Span& a, const Span& b) noexcept { return std::lexicographical_compare(a.begin(), a.end(), b.begin(), b.end()); }
#     218                 :            :     friend constexpr bool operator<=(const Span& a, const Span& b) noexcept { return !(b < a); }
#     219                 :            :     friend constexpr bool operator>(const Span& a, const Span& b) noexcept { return (b < a); }
#     220                 :            :     friend constexpr bool operator>=(const Span& a, const Span& b) noexcept { return !(a < b); }
#     221                 :            : 
#     222                 :            :     template <typename O> friend class Span;
#     223                 :            : };
#     224                 :            : 
#     225                 :            : // Deduction guides for Span
#     226                 :            : // For the pointer/size based and iterator based constructor:
#     227                 :            : template <typename T, typename EndOrSize> Span(T*, EndOrSize) -> Span<T>;
#     228                 :            : // For the array constructor:
#     229                 :            : template <typename T, std::size_t N> Span(T (&)[N]) -> Span<T>;
#     230                 :            : // For the temporaries/rvalue references constructor, only supporting const output.
#     231                 :            : template <typename T> Span(T&&) -> Span<std::enable_if_t<!std::is_lvalue_reference_v<T>, const std::remove_pointer_t<decltype(std::declval<T&&>().data())>>>;
#     232                 :            : // For (lvalue) references, supporting mutable output.
#     233                 :            : template <typename T> Span(T&) -> Span<std::remove_pointer_t<decltype(std::declval<T&>().data())>>;
#     234                 :            : 
#     235                 :            : /** Pop the last element off a span, and return a reference to that element. */
#     236                 :            : template <typename T>
#     237                 :            : T& SpanPopBack(Span<T>& span)
#     238                 :     177757 : {
#     239                 :     177757 :     size_t size = span.size();
#     240                 :     177757 :     ASSERT_IF_DEBUG(size > 0);
#     241                 :          0 :     T& back = span[size - 1];
#     242                 :     177757 :     span = Span<T>(span.data(), size - 1);
#     243                 :     177757 :     return back;
#     244                 :     177757 : }
#     245                 :            : 
#     246                 :            : //! Convert a data pointer to a std::byte data pointer.
#     247                 :            : //! Where possible, please use the safer AsBytes helpers.
#     248                 :  139690280 : inline const std::byte* BytePtr(const void* data) { return reinterpret_cast<const std::byte*>(data); }
#     249                 :  350645930 : inline std::byte* BytePtr(void* data) { return reinterpret_cast<std::byte*>(data); }
#     250                 :            : 
#     251                 :            : // From C++20 as_bytes and as_writeable_bytes
#     252                 :            : template <typename T>
#     253                 :            : Span<const std::byte> AsBytes(Span<T> s) noexcept
#     254                 :  460394712 : {
#     255                 :  460394712 :     return {BytePtr(s.data()), s.size_bytes()};
#     256                 :  460394712 : }
#     257                 :            : template <typename T>
#     258                 :            : Span<std::byte> AsWritableBytes(Span<T> s) noexcept
#     259                 :   29672864 : {
#     260                 :   29672864 :     return {BytePtr(s.data()), s.size_bytes()};
#     261                 :   29672864 : }
#     262                 :            : 
#     263                 :            : template <typename V>
#     264                 :            : Span<const std::byte> MakeByteSpan(V&& v) noexcept
#     265                 :  138782118 : {
#     266                 :  138782118 :     return AsBytes(Span{std::forward<V>(v)});
#     267                 :  138782118 : }
#     268                 :            : template <typename V>
#     269                 :            : Span<std::byte> MakeWritableByteSpan(V&& v) noexcept
#     270                 :    1970211 : {
#     271                 :    1970211 :     return AsWritableBytes(Span{std::forward<V>(v)});
#     272                 :    1970211 : }
#     273                 :            : 
#     274                 :            : // Helper functions to safely cast to unsigned char pointers.
#     275                 :      87536 : inline unsigned char* UCharCast(char* c) { return (unsigned char*)c; }
#     276                 :      16755 : inline unsigned char* UCharCast(unsigned char* c) { return c; }
#     277                 :       2551 : inline const unsigned char* UCharCast(const char* c) { return (unsigned char*)c; }
#     278                 :    9165136 : inline const unsigned char* UCharCast(const unsigned char* c) { return c; }
#     279                 :  343547776 : inline const unsigned char* UCharCast(const std::byte* c) { return reinterpret_cast<const unsigned char*>(c); }
#     280                 :            : 
#     281                 :            : // Helper function to safely convert a Span to a Span<[const] unsigned char>.
#     282                 :    9280768 : template <typename T> constexpr auto UCharSpanCast(Span<T> s) -> Span<typename std::remove_pointer<decltype(UCharCast(s.data()))>::type> { return {UCharCast(s.data()), s.size()}; }
#     283                 :            : 
#     284                 :            : /** Like the Span constructor, but for (const) unsigned char member types only. Only works for (un)signed char containers. */
#     285                 :    9280774 : template <typename V> constexpr auto MakeUCharSpan(V&& v) -> decltype(UCharSpanCast(Span{std::forward<V>(v)})) { return UCharSpanCast(Span{std::forward<V>(v)}); }
#     286                 :            : 
#     287                 :            : #endif // BITCOIN_SPAN_H

Generated by: LCOV version 0-eol-96201-ge66f56f4af6a