diff --git a/src/script/descriptor.cpp b/src/script/descriptor.cpp --- a/src/script/descriptor.cpp +++ b/src/script/descriptor.cpp @@ -169,7 +169,7 @@ } std::string AddChecksum(const std::string &str) { - return str + "#" + DescriptorChecksum(MakeSpan(str)); + return str + "#" + DescriptorChecksum(str); } //////////////////////////////////////////////////////////////////////////// @@ -1277,7 +1277,7 @@ std::unique_ptr Parse(const std::string &descriptor, FlatSigningProvider &out, std::string &error, bool require_checksum) { - Span sp(descriptor.data(), descriptor.size()); + Span sp{descriptor}; if (!CheckChecksum(sp, require_checksum, error)) { return nullptr; } @@ -1291,7 +1291,7 @@ std::string GetDescriptorChecksum(const std::string &descriptor) { std::string ret; std::string error; - Span sp(descriptor.data(), descriptor.size()); + Span sp{descriptor}; if (!CheckChecksum(sp, false, error, &ret)) { return ""; } diff --git a/src/span.h b/src/span.h --- a/src/span.h +++ b/src/span.h @@ -10,22 +10,54 @@ #include #include -/** A Span is an object that can refer to a contiguous sequence of objects. +#ifdef DEBUG +#define CONSTEXPR_IF_NOT_DEBUG +#define ASSERT_IF_DEBUG(x) assert((x)) +#else +#define CONSTEXPR_IF_NOT_DEBUG constexpr +#define ASSERT_IF_DEBUG(x) +#endif + +/** + * A Span is an object that can refer to a contiguous sequence of objects. * * It implements a subset of C++20's std::span. */ template class Span { C *m_data; - std::ptrdiff_t m_size; + std::size_t m_size; public: constexpr Span() noexcept : m_data(nullptr), m_size(0) {} - constexpr Span(C *data, std::ptrdiff_t size) noexcept - : m_data(data), m_size(size) {} - constexpr Span(C *data, C *end) noexcept - : m_data(data), m_size(end - data) {} - /** Implicit conversion of spans between compatible types. + /** + * Construct a span from a begin pointer and a size. + * + * This implements a subset of the iterator-based std::span constructor in + * C++20, which is hard to implement without std::address_of. + */ + template ::value, int>::type = 0> + constexpr Span(T *begin, std::size_t size) noexcept + : m_data(begin), m_size(size) {} + + /** + * Construct a span from a begin and end pointer. + * + * This implements a subset of the iterator-based std::span constructor in + * C++20, which is hard to implement without std::address_of. + */ + template ::value, int>::type = 0> + CONSTEXPR_IF_NOT_DEBUG Span(T *begin, T *end) noexcept + : m_data(begin), m_size(end - begin) { + ASSERT_IF_DEBUG(end >= begin); + } + + /** + * Implicit conversion of spans between compatible types. * * Specifically, if a pointer to an array of type O can be implicitly * converted to a pointer to an array of type C, then permit implicit @@ -47,27 +79,66 @@ /** Default assignment operator. */ Span &operator=(const Span &other) noexcept = default; + /** Construct a Span from an array. This matches the corresponding C++20 + * std::span constructor. */ + template + constexpr Span(C (&a)[N]) noexcept : m_data(a), m_size(N) {} + + /** + * Construct a Span for objects with .data() and .size() (std::string, + * std::array, std::vector, ...). + * + * This implements a subset of the functionality provided by the C++20 + * std::span range-based constructor. + * + * To prevent surprises, only Spans for constant value types are supported + * when passing in temporaries. Note that this restriction does not exist + * when converting arrays or other Spans (see above). + */ + template < + typename V, + typename std::enable_if< + (std::is_const::value || std::is_lvalue_reference::value) && + std::is_convertible< + typename std::remove_pointer< + decltype(std::declval().data())>::type (*)[], + C (*)[]>::value && + std::is_convertible().size()), + std::size_t>::value, + int>::type = 0> + constexpr Span(V &&v) noexcept : m_data(v.data()), m_size(v.size()) {} + constexpr C *data() const noexcept { return m_data; } constexpr C *begin() const noexcept { return m_data; } constexpr C *end() const noexcept { return m_data + m_size; } - constexpr C &front() const noexcept { return m_data[0]; } - constexpr C &back() const noexcept { return m_data[m_size - 1]; } - constexpr std::ptrdiff_t size() const noexcept { return m_size; } - constexpr C &operator[](std::ptrdiff_t pos) const noexcept { + CONSTEXPR_IF_NOT_DEBUG C &front() const noexcept { + ASSERT_IF_DEBUG(size() > 0); + return m_data[0]; + } + CONSTEXPR_IF_NOT_DEBUG C &back() const noexcept { + ASSERT_IF_DEBUG(size() > 0); + return m_data[m_size - 1]; + } + constexpr std::size_t size() const noexcept { return m_size; } + CONSTEXPR_IF_NOT_DEBUG C &operator[](std::size_t pos) const noexcept { + ASSERT_IF_DEBUG(size() > pos); return m_data[pos]; } - - constexpr Span subspan(std::ptrdiff_t offset) const noexcept { + CONSTEXPR_IF_NOT_DEBUG Span subspan(std::size_t offset) const noexcept { + ASSERT_IF_DEBUG(size() >= offset); return Span(m_data + offset, m_size - offset); } - constexpr Span subspan(std::ptrdiff_t offset, std::ptrdiff_t count) const - noexcept { + CONSTEXPR_IF_NOT_DEBUG Span subspan(std::size_t offset, + std::size_t count) const noexcept { + ASSERT_IF_DEBUG(size() >= offset + count); return Span(m_data + offset, count); } - constexpr Span first(std::ptrdiff_t count) const noexcept { + CONSTEXPR_IF_NOT_DEBUG Span first(std::size_t count) const noexcept { + ASSERT_IF_DEBUG(size() >= count); return Span(m_data, count); } - constexpr Span last(std::ptrdiff_t count) const noexcept { + CONSTEXPR_IF_NOT_DEBUG Span last(std::size_t count) const noexcept { + ASSERT_IF_DEBUG(size() >= count); return Span(m_data + m_size - count, count); } @@ -95,33 +166,30 @@ template friend class Span; }; -/** Create a span to a container exposing data() and size(). - * - * This correctly deals with constness: the returned Span's element type will be - * whatever data() returns a pointer to. If either the passed container is - * const, or its element type is const, the resulting span will have a const - * element type. - * - * std::span will have a constructor that implements this functionality - * directly. - */ -template constexpr Span MakeSpan(A (&a)[N]) { +// MakeSpan helps constructing a Span of the right type automatically. +/** MakeSpan for arrays: */ +template Span constexpr MakeSpan(A (&a)[N]) { return Span(a, N); } - +/** MakeSpan for temporaries / rvalue references, only supporting const output. + */ +template +constexpr auto MakeSpan(V &&v) -> typename std::enable_if< + !std::is_lvalue_reference::value, + Span::type>>::type { + return std::forward(v); +} +/** MakeSpan for (lvalue) references, supporting mutable output. */ template -constexpr Span< - typename std::remove_pointer().data())>::type> -MakeSpan(V &v) { - return Span< - typename std::remove_pointer().data())>::type>( - v.data(), v.size()); +constexpr auto MakeSpan(V &v) + -> Span::type> { + return v; } /** Pop the last element off a span, and return a reference to that element. */ template T &SpanPopBack(Span &span) { size_t size = span.size(); - assert(size > 0); + ASSERT_IF_DEBUG(size > 0); T &back = span[size - 1]; span = Span(span.data(), size - 1); return back; diff --git a/src/test/fuzz/span.cpp b/src/test/fuzz/span.cpp --- a/src/test/fuzz/span.cpp +++ b/src/test/fuzz/span.cpp @@ -18,7 +18,7 @@ FuzzedDataProvider fuzzed_data_provider(buffer.data(), buffer.size()); std::string str = fuzzed_data_provider.ConsumeBytesAsString(32); - const Span span = MakeSpan(str); + const Span span{str}; (void)span.data(); (void)span.begin(); (void)span.end(); @@ -34,7 +34,7 @@ } std::string another_str = fuzzed_data_provider.ConsumeBytesAsString(32); - const Span another_span = MakeSpan(another_str); + const Span another_span{another_str}; assert((span <= another_span) != (span > another_span)); assert((span == another_span) != (span != another_span)); assert((span >= another_span) != (span < another_span)); diff --git a/src/test/fuzz/spanparsing.cpp b/src/test/fuzz/spanparsing.cpp --- a/src/test/fuzz/spanparsing.cpp +++ b/src/test/fuzz/spanparsing.cpp @@ -13,7 +13,7 @@ std::min(query_size, 1024 * 1024)); const std::string span_str = fuzzed_data_provider.ConsumeRemainingBytesAsString(); - const Span const_span = MakeSpan(span_str); + const Span const_span{span_str}; Span mut_span = const_span; (void)spanparsing::Const(query, mut_span); diff --git a/src/test/util_tests.cpp b/src/test/util_tests.cpp --- a/src/test/util_tests.cpp +++ b/src/test/util_tests.cpp @@ -2197,7 +2197,7 @@ // Const(...): parse a constant, update span to skip it if successful input = "MilkToastHoney"; - sp = MakeSpan(input); + sp = input; success = Const("", sp); // empty BOOST_CHECK(success); BOOST_CHECK_EQUAL(SpanToStr(sp), "MilkToastHoney"); @@ -2222,7 +2222,7 @@ // Func(...): parse a function call, update span to argument if successful input = "Foo(Bar(xy,z()))"; - sp = MakeSpan(input); + sp = input; success = Func("FooBar", sp); BOOST_CHECK(!success); @@ -2246,31 +2246,31 @@ Span result; input = "(n*(n-1))/2"; - sp = MakeSpan(input); + sp = input; result = Expr(sp); BOOST_CHECK_EQUAL(SpanToStr(result), "(n*(n-1))/2"); BOOST_CHECK_EQUAL(SpanToStr(sp), ""); input = "foo,bar"; - sp = MakeSpan(input); + sp = input; result = Expr(sp); BOOST_CHECK_EQUAL(SpanToStr(result), "foo"); BOOST_CHECK_EQUAL(SpanToStr(sp), ",bar"); input = "(aaaaa,bbbbb()),c"; - sp = MakeSpan(input); + sp = input; result = Expr(sp); BOOST_CHECK_EQUAL(SpanToStr(result), "(aaaaa,bbbbb())"); BOOST_CHECK_EQUAL(SpanToStr(sp), ",c"); input = "xyz)foo"; - sp = MakeSpan(input); + sp = input; result = Expr(sp); BOOST_CHECK_EQUAL(SpanToStr(result), "xyz"); BOOST_CHECK_EQUAL(SpanToStr(sp), ")foo"); input = "((a),(b),(c)),xxx"; - sp = MakeSpan(input); + sp = input; result = Expr(sp); BOOST_CHECK_EQUAL(SpanToStr(result), "((a),(b),(c))"); BOOST_CHECK_EQUAL(SpanToStr(sp), ",xxx"); @@ -2279,7 +2279,7 @@ std::vector> results; input = "xxx"; - results = Split(MakeSpan(input), 'x'); + results = Split(input, 'x'); BOOST_CHECK_EQUAL(results.size(), 4); BOOST_CHECK_EQUAL(SpanToStr(results[0]), ""); BOOST_CHECK_EQUAL(SpanToStr(results[1]), ""); @@ -2287,19 +2287,19 @@ BOOST_CHECK_EQUAL(SpanToStr(results[3]), ""); input = "one#two#three"; - results = Split(MakeSpan(input), '-'); + results = Split(input, '-'); BOOST_CHECK_EQUAL(results.size(), 1); BOOST_CHECK_EQUAL(SpanToStr(results[0]), "one#two#three"); input = "one#two#three"; - results = Split(MakeSpan(input), '#'); + results = Split(input, '#'); BOOST_CHECK_EQUAL(results.size(), 3); BOOST_CHECK_EQUAL(SpanToStr(results[0]), "one"); BOOST_CHECK_EQUAL(SpanToStr(results[1]), "two"); BOOST_CHECK_EQUAL(SpanToStr(results[2]), "three"); input = "*foo*bar*"; - results = Split(MakeSpan(input), '*'); + results = Split(input, '*'); BOOST_CHECK_EQUAL(results.size(), 4); BOOST_CHECK_EQUAL(SpanToStr(results[0]), ""); BOOST_CHECK_EQUAL(SpanToStr(results[1]), "foo");