From 21b7f1b3795cef5dcfcd5934eb9cceef67d6c4d2 Mon Sep 17 00:00:00 2001 From: Davide Pesavento Date: Fri, 28 Apr 2023 23:21:02 -0400 Subject: [PATCH] encoding+lp+net: simplify with `if constexpr` Change-Id: Ie2b45a502c59e5cb7181628e691f7aaaf7509d84 --- ndn-cxx/encoding/block-helpers.hpp | 81 ++++++++--------- ndn-cxx/encoding/tlv.hpp | 128 ++++++++++----------------- ndn-cxx/lp/field.hpp | 15 +--- ndn-cxx/lp/packet.cpp | 37 +++----- ndn-cxx/net/impl/netlink-message.hpp | 92 ++++++------------- tests/benchmarks/encoding-bench.cpp | 24 +++-- tests/unit/encoding/block.t.cpp | 16 ++-- tests/unit/encoding/tlv.t.cpp | 8 +- 8 files changed, 147 insertions(+), 254 deletions(-) diff --git a/ndn-cxx/encoding/block-helpers.hpp b/ndn-cxx/encoding/block-helpers.hpp index 653280df..94720610 100644 --- a/ndn-cxx/encoding/block-helpers.hpp +++ b/ndn-cxx/encoding/block-helpers.hpp @@ -181,73 +181,64 @@ namespace detail { * @brief Create a binary block copying from RandomAccessIterator. */ template -class BinaryBlockFast +Block +makeBinaryBlockFast(uint32_t type, Iterator first, Iterator last) { -public: BOOST_CONCEPT_ASSERT((boost::RandomAccessIterator)); - static Block - makeBlock(uint32_t type, Iterator first, Iterator last) - { - EncodingEstimator estimator; - size_t valueLength = last - first; - size_t totalLength = valueLength; - totalLength += estimator.prependVarNumber(valueLength); - totalLength += estimator.prependVarNumber(type); + EncodingEstimator estimator; + size_t valueLength = last - first; + size_t totalLength = valueLength; + totalLength += estimator.prependVarNumber(valueLength); + totalLength += estimator.prependVarNumber(type); - EncodingBuffer encoder(totalLength, 0); - encoder.prependRange(first, last); - encoder.prependVarNumber(valueLength); - encoder.prependVarNumber(type); + EncodingBuffer encoder(totalLength, 0); + encoder.prependRange(first, last); + encoder.prependVarNumber(valueLength); + encoder.prependVarNumber(type); - return encoder.block(); - } -}; + return encoder.block(); +} /** * @brief Create a binary block copying from generic InputIterator. */ template -class BinaryBlockSlow +Block +makeBinaryBlockSlow(uint32_t type, Iterator first, Iterator last) { -public: BOOST_CONCEPT_ASSERT((boost::InputIterator)); - static Block - makeBlock(uint32_t type, Iterator first, Iterator last) - { - // reserve 4 bytes in front (common for 1(type)-3(length) encoding - // Actual size will be adjusted as necessary by the encoder - EncodingBuffer encoder(4, 4); - size_t valueLength = encoder.appendRange(first, last); - encoder.prependVarNumber(valueLength); - encoder.prependVarNumber(type); + // Reserve 4 bytes in front, common for 1(type)-3(length) encoding. + // Actual size will be adjusted as necessary by the encoder. + EncodingBuffer encoder(4, 4); + size_t valueLength = encoder.appendRange(first, last); + encoder.prependVarNumber(valueLength); + encoder.prependVarNumber(type); - return encoder.block(); - } -}; + return encoder.block(); +} } // namespace detail -/** @brief Create a TLV block copying TLV-VALUE from iterators. - * @tparam Iterator an InputIterator dereferenceable to a 1-octet type; a faster implementation is - * automatically selected for RandomAccessIterator - * @param type TLV-TYPE number - * @param first begin iterator - * @param last past-the-end iterator - * @sa prependBinaryBlock +/** + * @brief Create a TLV block copying TLV-VALUE from iterators. + * @tparam Iterator an InputIterator dereferenceable to a 1-octet type; a faster implementation is + * automatically selected for RandomAccessIterator + * @param type TLV-TYPE number + * @param first begin iterator + * @param last past-the-end iterator + * @sa prependBinaryBlock */ template Block makeBinaryBlock(uint32_t type, Iterator first, Iterator last) { - using BinaryBlockHelper = std::conditional_t< - std::is_base_of_v::iterator_category>, - detail::BinaryBlockFast, - detail::BinaryBlockSlow>; - - return BinaryBlockHelper::makeBlock(type, first, last); + if constexpr (std::is_base_of_v::iterator_category>) + return detail::makeBinaryBlockFast(type, first, last); + else + return detail::makeBinaryBlockSlow(type, first, last); } /** diff --git a/ndn-cxx/encoding/tlv.hpp b/ndn-cxx/encoding/tlv.hpp index 64805382..12bc854e 100644 --- a/ndn-cxx/encoding/tlv.hpp +++ b/ndn-cxx/encoding/tlv.hpp @@ -177,7 +177,7 @@ isCriticalType(uint32_t type) noexcept * @return true if number was successfully read from input, false otherwise */ template -[[nodiscard]] bool +[[nodiscard]] constexpr bool readVarNumber(Iterator& begin, Iterator end, uint64_t& number) noexcept; /** @@ -194,7 +194,7 @@ readVarNumber(Iterator& begin, Iterator end, uint64_t& number) noexcept; * the TLV-TYPE is zero or larger than 2^32-1 (maximum allowed by the packet format). */ template -[[nodiscard]] bool +[[nodiscard]] constexpr bool readType(Iterator& begin, Iterator end, uint32_t& type) noexcept; /** @@ -208,7 +208,7 @@ readType(Iterator& begin, Iterator end, uint32_t& type) noexcept; * @throw tlv::Error VAR-NUMBER cannot be read */ template -uint64_t +constexpr uint64_t readVarNumber(Iterator& begin, Iterator end); /** @@ -224,13 +224,13 @@ readVarNumber(Iterator& begin, Iterator end); * the TLV-TYPE is zero or larger than 2^32-1 (maximum allowed by the packet format). */ template -uint32_t +constexpr uint32_t readType(Iterator& begin, Iterator end); /** * @brief Get the number of bytes necessary to hold the value of @p number encoded as VAR-NUMBER. */ -constexpr size_t +[[nodiscard]] constexpr size_t sizeOfVarNumber(uint64_t number) noexcept; /** @@ -254,13 +254,13 @@ writeVarNumber(std::ostream& os, uint64_t number); * If \p size differs from `std::distance(begin, end)`, tlv::Error exception will be thrown. */ template -uint64_t +constexpr uint64_t readNonNegativeInteger(size_t size, Iterator& begin, Iterator end); /** * @brief Get the number of bytes necessary to hold the value of @p integer encoded as NonNegativeInteger. */ -constexpr size_t +[[nodiscard]] constexpr size_t sizeOfNonNegativeInteger(uint64_t integer) noexcept; /** @@ -271,48 +271,35 @@ size_t writeNonNegativeInteger(std::ostream& os, uint64_t integer); ///////////////////////////////////////////////////////////////////////////////// -///////////////////////////////////////////////////////////////////////////////// -///////////////////////////////////////////////////////////////////////////////// - // Inline definitions - -///////////////////////////////////////////////////////////////////////////////// -///////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////// namespace detail { -/** @brief Function object to read a number from InputIterator. +/** + * @brief Determine whether to select the readNumber() implementation for ContiguousIterator. + * + * This is not a full ContiguousIterator detection implementation. It returns true for + * the most common ContiguousIterator types used with TLV decoding function templates. */ -template -class ReadNumberSlow -{ -public: - constexpr bool - operator()(size_t size, Iterator& begin, Iterator end, uint64_t& number) const noexcept - { - number = 0; - size_t count = 0; - for (; begin != end && count < size; ++begin, ++count) { - number = (number << 8) | *begin; - } - return count == size; - } -}; +template, + typename ValueType = typename std::iterator_traits::value_type> +inline constexpr bool IsContiguousIterator = + (std::is_convertible_v || + std::is_convertible_v::const_iterator> || + std::is_convertible_v::const_iterator>) && + sizeof(ValueType) == 1 && !std::is_same_v; -/** @brief Function object to read a number from ContiguousIterator. - */ template -class ReadNumberFast +constexpr bool +readNumber(size_t size, Iterator& begin, Iterator end, uint64_t& number) noexcept { -public: - constexpr bool - operator()(size_t size, Iterator& begin, Iterator end, uint64_t& number) const noexcept - { + if constexpr (IsContiguousIterator) { + // fast path if (begin + size > end) { return false; } - switch (size) { case 1: { number = *begin; @@ -345,36 +332,21 @@ public: } } } -}; - -/** @brief Determine whether to select ReadNumber implementation for ContiguousIterator. - * - * This is not a full ContiguousIterator detection implementation. It returns true for the most - * common ContiguousIterator types used with TLV decoding function templates. - */ -template, - typename ValueType = typename std::iterator_traits::value_type> -constexpr bool -shouldSelectContiguousReadNumber() -{ - return (std::is_convertible_v || - std::is_convertible_v::const_iterator> || - std::is_convertible_v::const_iterator>) && - sizeof(ValueType) == 1 && - !std::is_same_v; + else { + // slow path + number = 0; + size_t count = 0; + for (; begin != end && count < size; ++begin, ++count) { + number = (number << 8) | *begin; + } + return count == size; + } } -template -class ReadNumber : public std::conditional_t(), - ReadNumberFast, ReadNumberSlow> -{ -}; - } // namespace detail template -bool +constexpr bool readVarNumber(Iterator& begin, Iterator end, uint64_t& number) noexcept { if (begin == end) @@ -387,13 +359,13 @@ readVarNumber(Iterator& begin, Iterator end, uint64_t& number) noexcept return true; } - size_t size = firstOctet == 253 ? 2 : - firstOctet == 254 ? 4 : 8; - return detail::ReadNumber()(size, begin, end, number); + size_t len = firstOctet == 253 ? 2 : + firstOctet == 254 ? 4 : 8; + return detail::readNumber(len, begin, end, number); } template -bool +constexpr bool readType(Iterator& begin, Iterator end, uint32_t& type) noexcept { uint64_t number = 0; @@ -407,24 +379,20 @@ readType(Iterator& begin, Iterator end, uint32_t& type) noexcept } template -uint64_t +constexpr uint64_t readVarNumber(Iterator& begin, Iterator end) { - if (begin == end) { - NDN_THROW(Error("Empty buffer during TLV parsing")); - } - - uint64_t value = 0; - bool isOk = readVarNumber(begin, end, value); + uint64_t number = 0; + bool isOk = readVarNumber(begin, end, number); if (!isOk) { NDN_THROW(Error("Insufficient data during TLV parsing")); } - return value; + return number; } template -uint32_t +constexpr uint32_t readType(Iterator& begin, Iterator end) { uint64_t type = readVarNumber(begin, end); @@ -471,15 +439,15 @@ writeVarNumber(std::ostream& os, uint64_t number) } template -uint64_t -readNonNegativeInteger(size_t size, Iterator& begin, Iterator end) +constexpr uint64_t +readNonNegativeInteger(size_t len, Iterator& begin, Iterator end) { - if (size != 1 && size != 2 && size != 4 && size != 8) { - NDN_THROW(Error("Invalid length " + to_string(size) + " for NonNegativeInteger")); + if (len != 1 && len != 2 && len != 4 && len != 8) { + NDN_THROW(Error("Invalid NonNegativeInteger length " + to_string(len))); } uint64_t number = 0; - bool isOk = detail::ReadNumber()(size, begin, end, number); + bool isOk = detail::readNumber(len, begin, end, number); if (!isOk) { NDN_THROW(Error("Insufficient data during NonNegativeInteger parsing")); } diff --git a/ndn-cxx/lp/field.hpp b/ndn-cxx/lp/field.hpp index cae13f79..3ede2b5c 100644 --- a/ndn-cxx/lp/field.hpp +++ b/ndn-cxx/lp/field.hpp @@ -34,33 +34,24 @@ namespace lp { */ namespace field_location_tags { -class Base -{ -}; - /** * \brief A header field. */ -class Header : public Base -{ -}; +struct Header {}; /** * \brief The Fragment field. */ -class Fragment : public Base -{ -}; +struct Fragment {}; } // namespace field_location_tags /** * \brief Concept check for NDNLPv2 fields. */ -template +template struct Field { - static_assert(std::is_base_of_v, ""); static_assert(std::is_same_v, ""); static_assert(std::is_same_v, ""); static_assert(std::is_default_constructible_v, ""); diff --git a/ndn-cxx/lp/packet.cpp b/ndn-cxx/lp/packet.cpp index 70973841..e942acca 100644 --- a/ndn-cxx/lp/packet.cpp +++ b/ndn-cxx/lp/packet.cpp @@ -30,44 +30,33 @@ namespace lp { namespace { -template -int -getLocationSortOrder() noexcept; - -template<> -constexpr int -getLocationSortOrder() noexcept +template +constexpr int8_t +getLocationSortOrder() noexcept { - return 1; + if constexpr (std::is_same_v) + return 1; + if constexpr (std::is_same_v) + return 2; } -template<> -constexpr int -getLocationSortOrder() noexcept +struct FieldInfo { - return 2; -} - -class FieldInfo -{ -public: constexpr FieldInfo() noexcept = default; explicit FieldInfo(uint32_t type) noexcept; -public: uint32_t tlvType = 0; ///< TLV-TYPE of the field; 0 if field does not exist bool isRecognized = false; ///< is this field known bool canIgnore = false; ///< can this unknown field be ignored bool isRepeatable = false; ///< is the field repeatable - int locationSortOrder = getLocationSortOrder(); ///< sort order of field_location_tag + int8_t locationSortOrder = getLocationSortOrder(); ///< sort order of field_location_tag }; -class ExtractFieldInfo +struct ExtractFieldInfo { -public: using result_type = void; template @@ -115,7 +104,7 @@ Block Packet::wireEncode() const { // If no header or trailer, return bare network packet - Block::element_container elements = m_wire.elements(); + auto elements = m_wire.elements(); if (elements.size() == 1 && elements.front().type() == FragmentField::TlvType::value) { elements.front().parse(); return elements.front().elements().front(); @@ -169,9 +158,7 @@ Packet::wireDecode(const Block& wire) bool Packet::comparePos(uint32_t first, const Block& second) noexcept { - FieldInfo firstInfo(first); - FieldInfo secondInfo(second.type()); - return compareFieldSortOrder(firstInfo, secondInfo); + return compareFieldSortOrder(FieldInfo(first), FieldInfo(second.type())); } } // namespace lp diff --git a/ndn-cxx/net/impl/netlink-message.hpp b/ndn-cxx/net/impl/netlink-message.hpp index 01d52825..5eb44b88 100644 --- a/ndn-cxx/net/impl/netlink-message.hpp +++ b/ndn-cxx/net/impl/netlink-message.hpp @@ -47,92 +47,52 @@ namespace net { template constexpr size_t -getAttributeLength(const T* attr); - -template<> -constexpr size_t -getAttributeLength(const nlattr* attr) +getAttributeLength(const T* attr) { - return attr->nla_len; -} - -template<> -constexpr size_t -getAttributeLength(const rtattr* attr) -{ - return attr->rta_len; + if constexpr (std::is_same_v) + return attr->nla_len; + if constexpr (std::is_same_v) + return attr->rta_len; } template constexpr size_t -getAttributeLengthAligned(const T* attr); - -template<> -constexpr size_t -getAttributeLengthAligned(const nlattr* attr) +getAttributeLengthAligned(const T* attr) { - return NLA_ALIGN(attr->nla_len); -} - -template<> -constexpr size_t -getAttributeLengthAligned(const rtattr* attr) -{ - return RTA_ALIGN(attr->rta_len); + if constexpr (std::is_same_v) + return NLA_ALIGN(attr->nla_len); + if constexpr (std::is_same_v) + return RTA_ALIGN(attr->rta_len); } template -constexpr uint16_t -getAttributeType(const T* attr); - -template<> -constexpr uint16_t -getAttributeType(const nlattr* attr) +uint16_t +getAttributeType(const T* attr) { - return attr->nla_type & NLA_TYPE_MASK; -} - -template<> -constexpr uint16_t -getAttributeType(const rtattr* attr) -{ - return attr->rta_type; + if constexpr (std::is_same_v) + return attr->nla_type & NLA_TYPE_MASK; + if constexpr (std::is_same_v) + return attr->rta_type; } template const uint8_t* -getAttributeValue(const T* attr); - -template<> -inline const uint8_t* -getAttributeValue(const nlattr* attr) +getAttributeValue(const T* attr) { - return reinterpret_cast(attr) + NLA_HDRLEN; -} - -template<> -inline const uint8_t* -getAttributeValue(const rtattr* attr) -{ - return reinterpret_cast(RTA_DATA(const_cast(attr))); + if constexpr (std::is_same_v) + return reinterpret_cast(attr) + NLA_HDRLEN; + if constexpr (std::is_same_v) + return reinterpret_cast(RTA_DATA(const_cast(attr))); } template constexpr size_t -getAttributeValueLength(const T* attr); - -template<> -constexpr size_t -getAttributeValueLength(const nlattr* attr) +getAttributeValueLength(const T* attr) { - return attr->nla_len - NLA_HDRLEN; -} - -template<> -constexpr size_t -getAttributeValueLength(const rtattr* attr) -{ - return RTA_PAYLOAD(attr); + if constexpr (std::is_same_v) + return attr->nla_len - NLA_HDRLEN; + if constexpr (std::is_same_v) + return RTA_PAYLOAD(attr); } template diff --git a/tests/benchmarks/encoding-bench.cpp b/tests/benchmarks/encoding-bench.cpp index 3ab246d7..1adb452d 100644 --- a/tests/benchmarks/encoding-bench.cpp +++ b/tests/benchmarks/encoding-bench.cpp @@ -1,6 +1,6 @@ /* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */ /* - * Copyright (c) 2013-2020 Regents of the University of California. + * Copyright (c) 2013-2023 Regents of the University of California. * * This file is part of ndn-cxx library (NDN C++ library with eXperimental eXtensions). * @@ -42,34 +42,30 @@ struct ReadVarNumberTest; template<> struct ReadVarNumberTest<1> { - static const uint8_t WIRE[]; - static const uint64_t VALUE = 252; + static constexpr uint8_t WIRE[] = {0xfc}; + static constexpr uint64_t VALUE = 252; }; -const uint8_t ReadVarNumberTest<1>::WIRE[] = {0xfc}; template<> struct ReadVarNumberTest<3> { - static const uint8_t WIRE[]; - static const uint64_t VALUE = 253; + static constexpr uint8_t WIRE[] = {0xfd, 0x00, 0xfd}; + static constexpr uint64_t VALUE = 253; }; -const uint8_t ReadVarNumberTest<3>::WIRE[] = {0xfd, 0x00, 0xfd}; template<> struct ReadVarNumberTest<5> { - static const uint8_t WIRE[]; - static const uint64_t VALUE = 65536; + static constexpr uint8_t WIRE[] = {0xfe, 0x00, 0x01, 0x00, 0x00}; + static constexpr uint64_t VALUE = 65536; }; -const uint8_t ReadVarNumberTest<5>::WIRE[] = {0xfe, 0x00, 0x01, 0x00, 0x00}; template<> struct ReadVarNumberTest<9> { - static const uint8_t WIRE[]; - static const uint64_t VALUE = 4294967296; + static constexpr uint8_t WIRE[] = {0xff, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00}; + static constexpr uint64_t VALUE = 4294967296; }; -const uint8_t ReadVarNumberTest<9>::WIRE[] = {0xff, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00}; template struct ReadVarNumberAlignTest : public ReadVarNumberTest @@ -102,7 +98,7 @@ using ReadVarNumberTests = boost::mpl::vector< // It is recommended to run the benchmark multiple times and take the average. BOOST_AUTO_TEST_CASE_TEMPLATE(ReadVarNumber, Test, ReadVarNumberTests) { - const int N_ITERATIONS = 100000000; + constexpr int N_ITERATIONS = 100000000; alignas(8) uint8_t buffer[16]; static_assert(Test::AlignmentOffset::value + sizeof(Test::WIRE) <= sizeof(buffer), ""); diff --git a/tests/unit/encoding/block.t.cpp b/tests/unit/encoding/block.t.cpp index f75eaf06..8dd21d35 100644 --- a/tests/unit/encoding/block.t.cpp +++ b/tests/unit/encoding/block.t.cpp @@ -169,7 +169,7 @@ BOOST_AUTO_TEST_CASE(FromType) BOOST_CHECK_EQUAL(b3.isValid(), false); BOOST_CHECK_EQUAL(b3.type(), tlv::Invalid); BOOST_CHECK_EXCEPTION(b3.size(), Block::Error, [] (const auto& e) { - return e.what() == "Cannot determine size of invalid block"s; + return e.what() == "Cannot determine size of invalid block"sv; }); BOOST_CHECK_EQUAL(b3.hasValue(), false); BOOST_CHECK_EQUAL(b3.value_size(), 0); @@ -253,7 +253,7 @@ BOOST_AUTO_TEST_CASE(FromStream) BOOST_CHECK(stream.eof()); BOOST_CHECK_EXCEPTION(Block::fromStream(stream), tlv::Error, [] (const auto& e) { - return e.what() == "Empty buffer during TLV parsing"s; + return e.what() == "Insufficient data during TLV parsing"sv; }); } @@ -315,7 +315,7 @@ BOOST_AUTO_TEST_CASE(FromStreamZeroLength) BOOST_CHECK(b3.value() == nullptr); BOOST_CHECK_EXCEPTION(Block::fromStream(stream), tlv::Error, [] (const auto& e) { - return e.what() == "Empty buffer during TLV parsing"s; + return e.what() == "Insufficient data during TLV parsing"sv; }); } @@ -331,7 +331,7 @@ BOOST_AUTO_TEST_CASE(FromStreamPacketTooLarge) stream.seekg(0); BOOST_CHECK_EXCEPTION(Block::fromStream(stream), tlv::Error, [] (const auto& e) { - return e.what() == "TLV-LENGTH from stream exceeds limit"s; + return e.what() == "TLV-LENGTH from stream exceeds limit"sv; }); } @@ -447,12 +447,12 @@ BOOST_AUTO_TEST_CASE(BlockFromValue) { Block b1(301); BOOST_CHECK_EXCEPTION(b1.blockFromValue(), Block::Error, [] (const auto& e) { - return e.what() == "Cannot construct block from empty TLV-VALUE"s; + return e.what() == "Cannot construct block from empty TLV-VALUE"sv; }); Block b2(302, std::make_shared()); BOOST_CHECK_EXCEPTION(b2.blockFromValue(), Block::Error, [] (const auto& e) { - return e.what() == "Cannot construct block from empty TLV-VALUE"s; + return e.what() == "Cannot construct block from empty TLV-VALUE"sv; }); b1.encode(); @@ -491,7 +491,7 @@ BOOST_AUTO_TEST_CASE(Parse) BOOST_CHECK(data.get(0x15) == data.elements().at(2)); BOOST_CHECK_EXCEPTION(data.get(0x01), Block::Error, [] (const auto& e) { - return e.what() == "No sub-element of type 1 found in block of type 6"s; + return e.what() == "No sub-element of type 1 found in block of type 6"sv; }); BOOST_CHECK(data.find(0x15) == data.elements_begin() + 2); @@ -503,7 +503,7 @@ BOOST_AUTO_TEST_CASE(Parse) }; Block bad(MALFORMED); BOOST_CHECK_EXCEPTION(bad.parse(), Block::Error, [] (const auto& e) { - return e.what() == "TLV-LENGTH of sub-element of type 7 exceeds TLV-VALUE boundary of parent block"s; + return e.what() == "TLV-LENGTH of sub-element of type 7 exceeds TLV-VALUE boundary of parent block"sv; }); } diff --git a/tests/unit/encoding/tlv.t.cpp b/tests/unit/encoding/tlv.t.cpp index 5eaa17a1..9cd7cc4b 100644 --- a/tests/unit/encoding/tlv.t.cpp +++ b/tests/unit/encoding/tlv.t.cpp @@ -59,11 +59,9 @@ using ArrayStream = boost::iostreams::stream; using StreamIterator = std::istream_iterator; #define ASSERT_READ_NUMBER_IS_FAST(T) \ - static_assert(std::is_base_of_v, detail::ReadNumber>, \ - # T " should use ReadNumberFast") + static_assert(detail::IsContiguousIterator, #T " is not fast") #define ASSERT_READ_NUMBER_IS_SLOW(T) \ - static_assert(std::is_base_of_v, detail::ReadNumber>, \ - # T " should use ReadNumberSlow") + static_assert(!detail::IsContiguousIterator, #T " is not slow") ASSERT_READ_NUMBER_IS_FAST(const uint8_t*); ASSERT_READ_NUMBER_IS_FAST(uint8_t*); @@ -80,6 +78,8 @@ ASSERT_READ_NUMBER_IS_FAST(Uint8Array::const_iterator); ASSERT_READ_NUMBER_IS_FAST(Uint8Array::iterator); using CharArray = std::array; ASSERT_READ_NUMBER_IS_FAST(CharArray::iterator); +ASSERT_READ_NUMBER_IS_FAST(span::iterator); +ASSERT_READ_NUMBER_IS_FAST(span::iterator); ASSERT_READ_NUMBER_IS_FAST(std::string::const_iterator); ASSERT_READ_NUMBER_IS_FAST(std::string::iterator); ASSERT_READ_NUMBER_IS_FAST(Buffer::const_iterator);