diff --git a/folly/Range.h b/folly/Range.h index 611f828b576..6d09055e42b 100644 --- a/folly/Range.h +++ b/folly/Range.h @@ -67,6 +67,7 @@ #include #include #include +#include // Ignore shadowing warnings within this file, so includers can use -Wshadow. FOLLY_PUSH_WARNING @@ -164,6 +165,75 @@ struct IsUnsignedCharPointer { using type = int; }; +void range_is_char_type_f_(char const*); +void range_is_char_type_f_(wchar_t const*); +#if (defined(__cpp_char8_t) && __cpp_char8_t >= 201811L) || \ + FOLLY_CPLUSPLUS >= 202002 +void range_is_char_type_f_(char8_t const*); +#endif +void range_is_char_type_f_(char16_t const*); +void range_is_char_type_f_(char32_t const*); +template +using range_is_char_type_d_ = + decltype(folly::detail::range_is_char_type_f_(FOLLY_DECLVAL(Iter))); +template +constexpr bool range_is_char_type_v_ = + is_detected_v; + +void range_is_byte_type_f_(unsigned char const*); +void range_is_byte_type_f_(signed char const*); +void range_is_byte_type_f_(byte const*); +template +using range_is_byte_type_d_ = + decltype(folly::detail::range_is_byte_type_f_(FOLLY_DECLVAL(Iter))); +template +constexpr bool range_is_byte_type_v_ = + is_detected_v; + +struct range_traits_char_ { + template + using apply = std::char_traits; +}; +struct range_traits_byte_ { + template + struct apply { + FOLLY_ERASE static constexpr int compare( + Value const* a, Value const* b, std::size_t c) { + return !c ? 0 : std::memcmp(a, b, c); + } + }; +}; +struct range_traits_fbck_ { + template + struct apply { + FOLLY_ERASE static constexpr int compare( + Value const* a, Value const* b, std::size_t c) { + while (c--) { + auto&& ai = *a++; + auto&& bi = *b++; + if (ai < bi) { + return -1; + } + if (bi < ai) { + return +1; + } + } + return 0; + } + }; +}; + +template +using range_traits_c_ = conditional_t< + range_is_char_type_v_, + range_traits_char_, + conditional_t< // + range_is_byte_type_v_, + range_traits_byte_, + range_traits_fbck_>>; +template +using range_traits_t_ = typename range_traits_c_::template apply; + } // namespace detail template @@ -193,8 +263,9 @@ class Range { Range, Range>::type; - using traits_type = - std::char_traits::type>; + using traits_type = detail::range_traits_t_< // + Iter, + typename std::remove_const::type>; static const size_type npos; diff --git a/folly/test/RangeTest.cpp b/folly/test/RangeTest.cpp index cd7135e88c2..b6bfa041367 100644 --- a/folly/test/RangeTest.cpp +++ b/folly/test/RangeTest.cpp @@ -49,6 +49,9 @@ CPP_assert(ranges::view_); using namespace folly; using namespace std; +static_assert(folly::detail::range_is_char_type_v_, ""); +static_assert(folly::detail::range_is_byte_type_v_, ""); + static_assert(std::is_literal_type::value, ""); BOOST_CONCEPT_ASSERT((boost::RandomAccessRangeConcept)); @@ -1257,6 +1260,41 @@ TEST(CRangeFunc, Collection) { EXPECT_THAT(numCollRange, testing::ElementsAreArray({17, 1})); } +TEST(Range, CompareChar) { + EXPECT_EQ(""_sp, ""_sp); + EXPECT_LT(""_sp, "world"_sp); + EXPECT_GT("world"_sp, ""_sp); + EXPECT_EQ("hello"_sp, "hello"_sp); + EXPECT_LT("hello"_sp, "world"_sp); + EXPECT_LT("hello"_sp, "helloworld"_sp); + EXPECT_GT("world"_sp, "hello"_sp); + EXPECT_GT("helloworld"_sp, "hello"_sp); +} + +TEST(Range, CompareByte) { + auto br = [](auto sp) { return ByteRange(sp); }; + EXPECT_EQ(br(""_sp), br(""_sp)); + EXPECT_LT(br(""_sp), br("world"_sp)); + EXPECT_GT(br("world"_sp), br(""_sp)); + EXPECT_EQ(br("hello"_sp), br("hello"_sp)); + EXPECT_LT(br("hello"_sp), br("world"_sp)); + EXPECT_LT(br("hello"_sp), br("helloworld"_sp)); + EXPECT_GT(br("world"_sp), br("hello"_sp)); + EXPECT_GT(br("helloworld"_sp), br("hello"_sp)); +} + +TEST(Range, CompareFbck) { + auto vr = [](std::vector const& _) { return folly::range(_); }; + EXPECT_EQ(vr({}), vr({})); + EXPECT_LT(vr({}), vr({1})); + EXPECT_GT(vr({1}), vr({})); + EXPECT_EQ(vr({1}), vr({1})); + EXPECT_LT(vr({1}), vr({2})); + EXPECT_LT(vr({1}), vr({1, 2})); + EXPECT_GT(vr({2}), vr({1})); + EXPECT_GT(vr({1, 1}), vr({1})); +} + std::string get_rand_str( size_t size, std::uniform_int_distribution<>& dist, std::mt19937& gen) { std::string ret(size, '\0');