diff --git a/stl/inc/algorithm b/stl/inc/algorithm index c338842a32a..506234cbf3a 100644 --- a/stl/inc/algorithm +++ b/stl/inc/algorithm @@ -766,6 +766,13 @@ namespace ranges { template _NODISCARD static constexpr bool _Equal_count( _It1 _First1, _It2 _First2, _Size _Count, _Pr _Pred, _Pj1 _Proj1, _Pj2 _Proj2) { + if constexpr (_Equal_memcmp_is_safe<_It1, _It2, + _Pr> && same_as<_Pj1, identity> && same_as<_Pj2, identity>) { + if (!_STD is_constant_evaluated()) { + return _Memcmp_count(_First1, _First2, static_cast(_Count)) == 0; + } + } + for (; _Count != 0; ++_First1, (void) ++_First2, --_Count) { if (!_STD invoke(_Pred, _STD invoke(_Proj1, *_First1), _STD invoke(_Proj2, *_First2))) { return false; @@ -2000,10 +2007,7 @@ _NODISCARD _CONSTEXPR20 bool _Equal_rev_pred_unchecked(_InIt1 _First1, _InIt2 _F if (!_STD is_constant_evaluated()) #endif // __cpp_lib_is_constant_evaluated { - const auto _First1_ch = _To_pointer(_First1); - const auto _First2_ch = _To_pointer(_First2); - const auto _Count = static_cast(_To_pointer(_Last2) - _First2_ch); - return _CSTD memcmp(_First1_ch, _First2_ch, _Count) == 0; + return _Memcmp_ranges(_First2, _Last2, _First1) == 0; } } @@ -2095,7 +2099,7 @@ namespace ranges { // clang-format off template concept _Equal_rev_pred_can_memcmp = is_same_v<_Pj1, identity> && is_same_v<_Pj2, identity> - && is_same_v<_Se2, _It2> && _Equal_memcmp_is_safe<_It1, _It2, _Pr>; + && sized_sentinel_for<_Se2, _It2> && _Equal_memcmp_is_safe<_It1, _It2, _Pr>; template _Se2, class _Pr, class _Pj1, class _Pj2> requires indirectly_comparable<_It1, _It2, _Pr, _Pj1, _Pj2> @@ -2106,12 +2110,14 @@ namespace ranges { constexpr bool _Optimize = _Equal_rev_pred_can_memcmp<_It1, _It2, _Se2, _Pr, _Pj1, _Pj2>; if constexpr (_Optimize) { if (!_STD is_constant_evaluated()) { - const auto _First1_ch = reinterpret_cast(_STD to_address(_First1)); - const auto _First2_ch = reinterpret_cast(_STD to_address(_First2)); - const auto _Count = - static_cast(reinterpret_cast(_STD to_address(_Last2)) - _First2_ch); - const bool _Eq = _CSTD memcmp(_First1_ch, _First2_ch, _Count) == 0; - if (_Eq) { + bool _Ans; + if constexpr (same_as<_It2, _Se2>) { + _Ans = _Memcmp_ranges(_First2, _Last2, _First1) == 0; + } else { + _Ans = _Memcmp_count(_First1, _First2, static_cast(_Last2 - _First2)) == 0; + } + + if (_Ans) { _First1 += (_Last2 - _First2); return {true, _STD move(_First1)}; } else { @@ -3469,17 +3475,19 @@ namespace ranges { auto _UFirst = _Get_unwrapped(_STD move(_First)); const auto _ULast = _Get_unwrapped(_STD move(_Last)); if (!_STD is_constant_evaluated()) { - if constexpr (_Fill_memset_is_safe) { - const auto _Distance = static_cast(_ULast - _UFirst); - _Fill_memset(_UFirst, _Value, _Distance); - _Seek_wrapped(_First, _UFirst + _Distance); - return _First; - } else if constexpr (_Fill_zero_memset_is_safe) { - if (_Is_all_bits_zero(_Value)) { + if constexpr (sized_sentinel_for) { + if constexpr (_Fill_memset_is_safe) { const auto _Distance = static_cast(_ULast - _UFirst); - _Fill_zero_memset(_UFirst, _Distance); + _Fill_memset(_UFirst, _Value, _Distance); _Seek_wrapped(_First, _UFirst + _Distance); return _First; + } else if constexpr (_Fill_zero_memset_is_safe) { + if (_Is_all_bits_zero(_Value)) { + const auto _Distance = static_cast(_ULast - _UFirst); + _Fill_zero_memset(_UFirst, _Distance); + _Seek_wrapped(_First, _UFirst + _Distance); + return _First; + } } } } @@ -4355,10 +4363,11 @@ _CONSTEXPR20 _OutIt reverse_copy(_BidIt _First, _BidIt _Last, _OutIt _Dest) { auto _UDest = _Get_unwrapped_n(_Dest, _Idl_distance<_BidIt>(_UFirst, _ULast)); #if _USE_STD_VECTOR_ALGORITHMS - using _Elem = remove_pointer_t; - using _DestElem = remove_pointer_t; + using _Elem = remove_reference_t<_Iter_ref_t>>; + using _DestElem = remove_reference_t<_Iter_ref_t>; constexpr bool _Allow_vectorization = conjunction_v, _DestElem>, - is_pointer, is_trivially_copyable<_Elem>, negation>>; + bool_constant<_Iterators_are_contiguous>, is_trivially_copyable<_Elem>, + negation>>; constexpr size_t _Nx = sizeof(_Elem); #pragma warning(suppress : 6326) // Potential comparison of a constant with another constant @@ -4368,13 +4377,13 @@ _CONSTEXPR20 _OutIt reverse_copy(_BidIt _First, _BidIt _Last, _OutIt _Dest) { #endif // __cpp_lib_is_constant_evaluated { if constexpr (_Nx == 1) { - __std_reverse_copy_trivially_copyable_1(_UFirst, _ULast, _UDest); + __std_reverse_copy_trivially_copyable_1(_To_address(_UFirst), _To_address(_ULast), _To_address(_UDest)); } else if constexpr (_Nx == 2) { - __std_reverse_copy_trivially_copyable_2(_UFirst, _ULast, _UDest); + __std_reverse_copy_trivially_copyable_2(_To_address(_UFirst), _To_address(_ULast), _To_address(_UDest)); } else if constexpr (_Nx == 4) { - __std_reverse_copy_trivially_copyable_4(_UFirst, _ULast, _UDest); + __std_reverse_copy_trivially_copyable_4(_To_address(_UFirst), _To_address(_ULast), _To_address(_UDest)); } else { - __std_reverse_copy_trivially_copyable_8(_UFirst, _ULast, _UDest); + __std_reverse_copy_trivially_copyable_8(_To_address(_UFirst), _To_address(_ULast), _To_address(_UDest)); } _UDest += _ULast - _UFirst; @@ -10359,6 +10368,18 @@ namespace ranges { _STL_INTERNAL_STATIC_ASSERT(sentinel_for<_Se2, _It2>); _STL_INTERNAL_STATIC_ASSERT(indirect_strict_weak_order<_Pr, projected<_It1, _Pj1>, projected<_It2, _Pj2>>); + using _Memcmp_classification_pred = + typename decltype(_Lex_compare_memcmp_classify(_First1, _First2, _Pred))::_Pred; + if constexpr (!is_void_v<_Memcmp_classification_pred> && sized_sentinel_for<_Se1, _It1> // + && sized_sentinel_for<_Se2, _It2> && same_as<_Pj1, identity> && same_as<_Pj2, identity>) { + if (!_STD is_constant_evaluated()) { + const auto _Num1 = static_cast(_Last1 - _First1); + const auto _Num2 = static_cast(_Last2 - _First2); + const int _Ans = _Memcmp_count(_First1, _First2, (_STD min)(_Num1, _Num2)); + return _Memcmp_classification_pred{}(_Ans, 0) || (_Ans == 0 && _Num1 < _Num2); + } + } + for (;; ++_First1, (void) ++_First2) { if (_First2 == _Last2) { return false; diff --git a/stl/inc/functional b/stl/inc/functional index a8aee539270..649ab6d2a97 100644 --- a/stl/inc/functional +++ b/stl/inc/functional @@ -2229,20 +2229,6 @@ namespace ranges { using is_transparent = int; }; - // STRUCT ranges::greater - struct greater { - // clang-format off - template - requires totally_ordered_with<_Ty1, _Ty2> // TRANSITION, GH-489 - _NODISCARD constexpr bool operator()(_Ty1&& _Left, _Ty2&& _Right) const noexcept(noexcept( - static_cast(static_cast<_Ty2&&>(_Right) < static_cast<_Ty1&&>(_Left)))) /* strengthened */ { - return static_cast(static_cast<_Ty2&&>(_Right) < static_cast<_Ty1&&>(_Left)); - } - // clang-format on - - using is_transparent = int; - }; - // STRUCT ranges::greater_equal struct greater_equal { // clang-format off diff --git a/stl/inc/xmemory b/stl/inc/xmemory index aea3f2e9166..dc31ee00a66 100644 --- a/stl/inc/xmemory +++ b/stl/inc/xmemory @@ -1781,14 +1781,15 @@ void uninitialized_fill(const _NoThrowFwdIt _First, const _NoThrowFwdIt _Last, c // FUNCTION TEMPLATE _Uninitialized_value_construct_n WITH ALLOCATOR template -_INLINE_VAR constexpr bool _Use_memset_value_construct_v = conjunction_v, - is_scalar<_Iter_value_t<_NoThrowFwdIt>>, negation>>>, - negation>>>; +_INLINE_VAR constexpr bool _Use_memset_value_construct_v = + conjunction_v>, is_scalar<_Iter_value_t<_NoThrowFwdIt>>, + negation>>>, + negation>>>; template _Ptr _Zero_range(const _Ptr _First, const _Ptr _Last) { // fill [_First, _Last) with zeroes - char* const _First_ch = reinterpret_cast(_First); - char* const _Last_ch = reinterpret_cast(_Last); + char* const _First_ch = reinterpret_cast(_To_address(_First)); + char* const _Last_ch = reinterpret_cast(_To_address(_Last)); _CSTD memset(_First_ch, 0, static_cast(_Last_ch - _First_ch)); return _Last; } diff --git a/stl/inc/xutility b/stl/inc/xutility index 9c7fa721a6c..7a290b49aea 100644 --- a/stl/inc/xutility +++ b/stl/inc/xutility @@ -3173,6 +3173,20 @@ namespace ranges { using is_transparent = int; }; + // STRUCT ranges::greater + struct greater { + // clang-format off + template + requires totally_ordered_with<_Ty1, _Ty2> // TRANSITION, GH-489 + _NODISCARD constexpr bool operator()(_Ty1&& _Left, _Ty2&& _Right) const noexcept(noexcept( + static_cast(static_cast<_Ty2&&>(_Right) < static_cast<_Ty1&&>(_Left)))) /* strengthened */ { + return static_cast(static_cast<_Ty2&&>(_Right) < static_cast<_Ty1&&>(_Left)); + } + // clang-format on + + using is_transparent = int; + }; + // CONCEPT ranges::common_range // clang-format off template @@ -4345,6 +4359,38 @@ _BidIt2 move_backward(_ExPo&&, _BidIt1 _First, _BidIt1 _Last, _BidIt2 _Dest) noe #endif // _HAS_CXX17 // FUNCTION TEMPLATE fill + +// _Iterator_is_contiguous<_Iter> reports whether an iterator is known to be contiguous. +// (Without concepts, this detection is limited, which will limit when we can activate optimizations.) + +#ifdef __cpp_lib_concepts +// When concepts are available, we can detect arbitrary contiguous iterators. +template +inline constexpr bool _Iterator_is_contiguous = contiguous_iterator<_Iter>; + +template +_NODISCARD constexpr auto _To_address(const _Iter& _Val) noexcept { + _STL_INTERNAL_STATIC_ASSERT(contiguous_iterator<_Iter>); + return _STD to_address(_Val); +} +#else // ^^^ defined(__cpp_lib_concepts) ^^^ / vvv !defined(__cpp_lib_concepts) vvv +// When concepts aren't available, we can detect pointers. (Iterators should be unwrapped before using this.) +template +_INLINE_VAR constexpr bool _Iterator_is_contiguous = is_pointer_v<_Iter>; + +template +_NODISCARD constexpr auto _To_address(const _Iter& _Val) noexcept { + _STL_INTERNAL_STATIC_ASSERT(is_pointer_v<_Iter>); + return _Val; +} +#endif // ^^^ !defined(__cpp_lib_concepts) ^^^ + +// _Iterators_are_contiguous<_Iter1, _Iter2> reports whether both iterators are known to be contiguous. + +template +_INLINE_VAR constexpr bool _Iterators_are_contiguous = + _Iterator_is_contiguous<_Iter1>&& _Iterator_is_contiguous<_Iter2>; + template struct _Is_character : false_type {}; // by default, not a character type @@ -4363,19 +4409,22 @@ struct _Is_character : true_type {}; // UTF-8 code units are sort-of ch #endif // __cpp_char8_t template -struct _Is_character_or_byte_or_bool : _Is_character<_Ty>::type {}; +struct _Is_character_or_bool : _Is_character<_Ty>::type {}; + +template <> +struct _Is_character_or_bool : true_type {}; + +template +struct _Is_character_or_byte_or_bool : _Is_character_or_bool<_Ty>::type {}; #ifdef __cpp_lib_byte template <> struct _Is_character_or_byte_or_bool : true_type {}; #endif // __cpp_lib_byte -template <> -struct _Is_character_or_byte_or_bool : true_type {}; - // _Fill_memset_is_safe determines if _FwdIt and _Ty are eligible for memset optimization in fill. // Need to explicitly test for volatile because _Unwrap_enum_t discards qualifiers. -template > +template > _INLINE_VAR constexpr bool _Fill_memset_is_safe = conjunction_v, _Is_character_or_byte_or_bool<_Unwrap_enum_t>>>, negation>>>, is_assignable<_Iter_ref_t<_FwdIt>, const _Ty&>>; @@ -4383,7 +4432,7 @@ _INLINE_VAR constexpr bool _Fill_memset_is_safe = conjunction_v, template _INLINE_VAR constexpr bool _Fill_memset_is_safe<_FwdIt, _Ty, false> = false; -template > +template > _INLINE_VAR constexpr bool _Fill_zero_memset_is_safe = conjunction_v, is_scalar<_Iter_value_t<_FwdIt>>, negation>>, negation>>>, is_assignable<_Iter_ref_t<_FwdIt>, const _Ty&>>; @@ -4391,15 +4440,16 @@ _INLINE_VAR constexpr bool _Fill_zero_memset_is_safe = template _INLINE_VAR constexpr bool _Fill_zero_memset_is_safe<_FwdIt, _Ty, false> = false; -template -void _Fill_memset(_DestTy* const _Dest, const _Ty _Val, const size_t _Count) { - _DestTy _Dest_val = _Val; // implicitly convert (a cast would suppress warnings); also handles _DestTy being bool - _CSTD memset(_Dest, static_cast(_Dest_val), _Count); +template +void _Fill_memset(_CtgIt _Dest, const _Ty _Val, const size_t _Count) { + // implicitly convert (a cast would suppress warnings); also handles _Iter_value_t<_CtgIt> being bool + _Iter_value_t<_CtgIt> _Dest_val = _Val; + _CSTD memset(_To_address(_Dest), static_cast(_Dest_val), _Count); } -template -void _Fill_zero_memset(_DestTy* const _Dest, const size_t _Count) { - _CSTD memset(_Dest, 0, _Count * sizeof(_DestTy)); +template +void _Fill_zero_memset(_CtgIt _Dest, const size_t _Count) { + _CSTD memset(_To_address(_Dest), 0, _Count * sizeof(_Iter_value_t<_CtgIt>)); } template @@ -4565,32 +4615,6 @@ template _INLINE_VAR constexpr bool _Can_memcmp_elements_with_pred = _Can_memcmp_elements<_Elem1, _Elem2> // && _Pred_is_consistent_with_memcmp<_Elem1, _Elem2, _Pr>; -// _Iterators_are_contiguous<_Iter1, _Iter2> reports whether both iterators are known to be contiguous. -// (Without concepts, this detection is limited, which will limit when we can activate the memcmp optimization.) - -#ifdef __cpp_lib_concepts -// When concepts are available, we can detect arbitrary contiguous iterators. -template -_INLINE_VAR constexpr bool _Iterators_are_contiguous = contiguous_iterator<_Iter1> // - && contiguous_iterator<_Iter2>; - -template -_NODISCARD constexpr _Target* _To_pointer(const _Iter& _It) noexcept { - _STL_INTERNAL_STATIC_ASSERT(contiguous_iterator<_Iter>); - return reinterpret_cast<_Target*>(_STD to_address(_It)); -} -#else // ^^^ defined(__cpp_lib_concepts) ^^^ / vvv !defined(__cpp_lib_concepts) vvv -// When concepts aren't available, we can detect pointers. (Iterators should be unwrapped before using this.) -template -_INLINE_VAR constexpr bool _Iterators_are_contiguous = conjunction_v, is_pointer<_Iter2>>; - -template -_NODISCARD constexpr _Target* _To_pointer(const _Iter& _It) noexcept { - _STL_INTERNAL_STATIC_ASSERT(is_pointer_v<_Iter>); - return reinterpret_cast<_Target*>(_It); -} -#endif // ^^^ !defined(__cpp_lib_concepts) ^^^ - // _Equal_memcmp_is_safe<_Iter1, _Iter2, _Pr> reports whether we can activate the memcmp optimization // for arbitrary iterators and predicates. // It ignores top-level constness on the iterators and on the elements. @@ -4603,6 +4627,23 @@ template _INLINE_VAR constexpr bool _Equal_memcmp_is_safe = _Equal_memcmp_is_safe_helper, remove_const_t<_Iter2>, _Pr>; +template +_NODISCARD int _Memcmp_ranges(_CtgIt1 _First1, _CtgIt1 _Last1, _CtgIt2 _First2) { + _STL_INTERNAL_STATIC_ASSERT(sizeof(_Iter_value_t<_CtgIt1>) == sizeof(_Iter_value_t<_CtgIt2>)); + const auto _First1_ch = reinterpret_cast(_To_address(_First1)); + const auto _Last1_ch = reinterpret_cast(_To_address(_Last1)); + const auto _First2_ch = reinterpret_cast(_To_address(_First2)); + return _CSTD memcmp(_First1_ch, _First2_ch, static_cast(_Last1_ch - _First1_ch)); +} + +template +_NODISCARD int _Memcmp_count(_CtgIt1 _First1, _CtgIt2 _First2, const size_t _Count) { + _STL_INTERNAL_STATIC_ASSERT(sizeof(_Iter_value_t<_CtgIt1>) == sizeof(_Iter_value_t<_CtgIt2>)); + const auto _First1_ch = reinterpret_cast(_To_address(_First1)); + const auto _First2_ch = reinterpret_cast(_To_address(_First2)); + return _CSTD memcmp(_First1_ch, _First2_ch, _Count * sizeof(_Iter_value_t<_CtgIt1>)); +} + template _NODISCARD _CONSTEXPR20 bool equal(const _InIt1 _First1, const _InIt1 _Last1, const _InIt2 _First2, _Pr _Pred) { // compare [_First1, _Last1) to [_First2, ...) @@ -4615,10 +4656,7 @@ _NODISCARD _CONSTEXPR20 bool equal(const _InIt1 _First1, const _InIt1 _Last1, co if (!_STD is_constant_evaluated()) #endif // __cpp_lib_is_constant_evaluated { - const auto _First1_ch = _To_pointer(_UFirst1); - const auto _First2_ch = _To_pointer(_UFirst2); - const auto _Count = static_cast(_To_pointer(_ULast1) - _First1_ch); - return _CSTD memcmp(_First1_ch, _First2_ch, _Count) == 0; + return _Memcmp_ranges(_UFirst1, _ULast1, _UFirst2) == 0; } } @@ -4712,21 +4750,6 @@ _NODISCARD bool equal(_ExPo&& _Exec, const _FwdIt1 _First1, const _FwdIt1 _Last1 #ifdef __cpp_lib_concepts namespace ranges { - // clang-format off - // concept-constrained for strict enforcement as it is used by several algorithms - template _Se, class _Ty, class _Pj = identity> - requires indirect_binary_predicate, const _Ty*> - _NODISCARD constexpr _It _Find_unchecked(_It _First, const _Se _Last, const _Ty& _Val, _Pj _Proj = {}) { - for (; _First != _Last; ++_First) { - if (_STD invoke(_Proj, *_First) == _Val) { - break; - } - } - - return _First; - } - // clang-format on - // CONCEPT _Convertible_from template concept _Convertible_from = convertible_to<_From, _To>; @@ -4875,6 +4898,8 @@ struct _Lex_compare_check_element_types_helper : true_type { template struct _Lex_compare_optimize { explicit _Lex_compare_optimize() = default; + + using _Pred = _Memcmp_pr; }; // optimization tag for lexicographical_compare template @@ -4888,18 +4913,36 @@ constexpr auto _Lex_compare_memcmp_classify(const _InIt1&, const _InIt2&, const return _Lex_compare_optimize{}; } -template -constexpr auto _Lex_compare_memcmp_classify(_Obj1* const&, _Obj2* const&, const less<_FTy>&) { - // return lex_compare optimization category for pointer iterators and less<_FTy> - return _Lex_compare_check_element_types, _Obj1, _Obj2, _FTy>{}; +template , int> = 0> +constexpr auto _Lex_compare_memcmp_classify(const _CtgIt1&, const _CtgIt2&, const less<_FTy>&) { + // return lex_compare optimization category for contiguous iterators and less<_FTy> + return _Lex_compare_check_element_types, remove_reference_t<_Iter_ref_t<_CtgIt1>>, + remove_reference_t<_Iter_ref_t<_CtgIt2>>, _FTy>{}; +} + +template , int> = 0> +constexpr auto _Lex_compare_memcmp_classify(const _CtgIt1&, const _CtgIt2&, const greater<_FTy>&) { + // return lex_compare optimization category for contiguous iterators and greater<_FTy> + return _Lex_compare_check_element_types, remove_reference_t<_Iter_ref_t<_CtgIt1>>, + remove_reference_t<_Iter_ref_t<_CtgIt2>>, _FTy>{}; } -template -constexpr auto _Lex_compare_memcmp_classify(_Obj1* const&, _Obj2* const&, const greater<_FTy>&) { - // return lex_compare optimization category for pointer iterators and greater<_FTy> - return _Lex_compare_check_element_types, _Obj1, _Obj2, _FTy>{}; +#ifdef __cpp_lib_concepts +template , int> = 0> +constexpr auto _Lex_compare_memcmp_classify(const _CtgIt1&, const _CtgIt2&, const _RANGES less&) { + // return lex_compare optimization category for contiguous iterators and ranges::less + return _Lex_compare_check_element_types, remove_reference_t<_Iter_ref_t<_CtgIt1>>, + remove_reference_t<_Iter_ref_t<_CtgIt2>>, void>{}; } +template , int> = 0> +constexpr auto _Lex_compare_memcmp_classify(const _CtgIt1&, const _CtgIt2&, const _RANGES greater&) { + // return lex_compare optimization category for contiguous iterators and ranges::greater + return _Lex_compare_check_element_types, remove_reference_t<_Iter_ref_t<_CtgIt1>>, + remove_reference_t<_Iter_ref_t<_CtgIt2>>, void>{}; +} +#endif // __cpp_lib_concepts + template _NODISCARD constexpr bool _Lex_compare_unchecked( _InIt1 _First1, _InIt1 _Last1, _InIt2 _First2, _InIt2 _Last2, _Pr _Pred, _Lex_compare_optimize) { @@ -4915,9 +4958,9 @@ _NODISCARD constexpr bool _Lex_compare_unchecked( return _First1 == _Last1 && _First2 != _Last2; } -template +template _NODISCARD _CONSTEXPR20 bool _Lex_compare_unchecked( - _InIt1 _First1, _InIt1 _Last1, _InIt2 _First2, _InIt2 _Last2, _Pr _Pred, _Lex_compare_optimize<_Memcmp_pr>) { + _CtgIt1 _First1, _CtgIt1 _Last1, _CtgIt2 _First2, _CtgIt2 _Last2, _Pr _Pred, _Lex_compare_optimize<_Memcmp_pr>) { // order [_First1, _Last1) vs. [_First2, _Last2) memcmp optimization #ifdef __cpp_lib_is_constant_evaluated if (_STD is_constant_evaluated()) { @@ -4927,7 +4970,7 @@ _NODISCARD _CONSTEXPR20 bool _Lex_compare_unchecked( (void) _Pred; const auto _Num1 = static_cast(_Last1 - _First1); const auto _Num2 = static_cast(_Last2 - _First2); - const int _Ans = _CSTD memcmp(_First1, _First2, _Num1 < _Num2 ? _Num1 : _Num2); + const int _Ans = _Memcmp_count(_First1, _First2, (_STD min)(_Num1, _Num2)); return _Memcmp_pr{}(_Ans, 0) || (_Ans == 0 && _Num1 < _Num2); } @@ -4989,17 +5032,17 @@ _NODISCARD constexpr auto lexicographical_compare_three_way( using _Ty1 = remove_const_t>; using _Ty2 = remove_const_t>; - if constexpr (conjunction_v, is_pointer<_UIt1>, is_pointer<_UIt2>, - disjunction< + if constexpr ( + conjunction_v, bool_constant<_Iterators_are_contiguous<_UIt1, _UIt2>>, + disjunction< #ifdef __cpp_lib_byte - conjunction, is_same<_Ty2, byte>>, + conjunction, is_same<_Ty2, byte>>, #endif // __cpp_lib_byte - conjunction<_Is_character<_Ty1>, is_unsigned<_Ty1>, _Is_character<_Ty2>, - is_unsigned<_Ty2>>>>) { + conjunction<_Is_character<_Ty1>, is_unsigned<_Ty1>, _Is_character<_Ty2>, is_unsigned<_Ty2>>>>) { if (!_STD is_constant_evaluated()) { const auto _Num1 = static_cast(_ULast1 - _UFirst1); const auto _Num2 = static_cast(_ULast2 - _UFirst2); - const int _Ans = _CSTD memcmp(_UFirst1, _UFirst2, (_STD min)(_Num1, _Num2)); + const int _Ans = _Memcmp_count(_UFirst1, _UFirst2, (_STD min)(_Num1, _Num2)); if (_Ans == 0) { return _Num1 <=> _Num2; } else { @@ -5035,46 +5078,71 @@ _NODISCARD constexpr auto lexicographical_compare_three_way( // FUNCTION TEMPLATE find template -_NODISCARD constexpr bool _Within_limits(const _Ty& _Val, true_type, true_type, _Any_tag) { // signed _Elem, signed _Ty +_NODISCARD constexpr bool _Within_limits(const _Ty& _Val, true_type, true_type, _Any_tag, false_type) { + // signed _Elem, signed _Ty return SCHAR_MIN <= _Val && _Val <= SCHAR_MAX; } template -_NODISCARD constexpr bool _Within_limits(const _Ty& _Val, true_type, false_type, true_type) { +_NODISCARD constexpr bool _Within_limits(const _Ty& _Val, true_type, false_type, true_type, false_type) { // signed _Elem, unsigned _Ty, -1 == static_cast<_Ty>(-1) return _Val <= SCHAR_MAX || static_cast<_Ty>(SCHAR_MIN) <= _Val; } template -_NODISCARD constexpr bool _Within_limits(const _Ty& _Val, true_type, false_type, false_type) { +_NODISCARD constexpr bool _Within_limits(const _Ty& _Val, true_type, false_type, false_type, false_type) { // signed _Elem, unsigned _Ty, -1 != static_cast<_Ty>(-1) return _Val <= SCHAR_MAX; } template -_NODISCARD constexpr bool _Within_limits(const _Ty& _Val, false_type, true_type, _Any_tag) { +_NODISCARD constexpr bool _Within_limits(const _Ty& _Val, false_type, true_type, _Any_tag, false_type) { // unsigned _Elem, signed _Ty return 0 <= _Val && _Val <= UCHAR_MAX; } template -_NODISCARD constexpr bool _Within_limits(const _Ty& _Val, false_type, false_type, _Any_tag) { +_NODISCARD constexpr bool _Within_limits(const _Ty& _Val, false_type, false_type, _Any_tag, false_type) { // unsigned _Elem, unsigned _Ty return _Val <= UCHAR_MAX; } +template +_NODISCARD constexpr bool _Within_limits(const _Ty& _Val, _Any_tag, _Any_tag, _Any_tag, true_type) { + // bool _Elem + return _Val == true || _Val == false; +} + template -_NODISCARD constexpr bool _Within_limits(_InIt, const _Ty& _Val) { // check whether _Val is within the limits of _Elem - using _Elem = remove_pointer_t<_InIt>; +_NODISCARD constexpr bool _Within_limits(const _InIt&, const _Ty& _Val) { + // check whether _Val is within the limits of _Elem + using _Elem = _Iter_value_t<_InIt>; return _Within_limits(_Val, bool_constant>{}, bool_constant>{}, - bool_constant<-1 == static_cast<_Ty>(-1)>{}); + bool_constant<-1 == static_cast<_Ty>(-1)>{}, bool_constant>{}); } template -_NODISCARD constexpr bool _Within_limits(_InIt, const bool&) { // bools are always within the limits of _Elem +_NODISCARD constexpr bool _Within_limits(const _InIt&, const bool&) { // bools are always within the limits of _Elem return true; } +#ifdef __cpp_lib_byte +template +_NODISCARD constexpr bool _Within_limits(const _InIt&, const byte&) { // bytes are only comparable with other bytes + return true; +} +#endif // __cpp_lib_byte + +template +_INLINE_VAR constexpr bool _Memchr_in_find_is_safe = + _Iterator_is_contiguous<_Iter>&& + disjunction_v, _Is_character_or_bool<_Iter_value_t<_Iter>>> +#ifdef __cpp_lib_byte + , + conjunction, is_same<_Iter_value_t<_Iter>, byte>> +#endif // __cpp_lib_byte + > && !is_volatile_v>>; + template _NODISCARD constexpr _InIt _Find_unchecked1(_InIt _First, const _InIt _Last, const _Ty& _Val, false_type) { // find first matching _Val @@ -5096,24 +5164,25 @@ _NODISCARD _CONSTEXPR20 _InIt _Find_unchecked1(_InIt _First, const _InIt _Last, #ifdef __cpp_lib_is_constant_evaluated if (_STD is_constant_evaluated()) { - using _Elem = remove_pointer_t<_InIt>; + using _Elem = _Iter_value_t<_InIt>; return _Find_unchecked1(_First, _Last, static_cast<_Elem>(_Val), false_type{}); } #endif // __cpp_lib_is_constant_evaluated - _First = - static_cast<_InIt>(_CSTD memchr(_First, static_cast(_Val), static_cast(_Last - _First))); - return _First ? _First : _Last; + const auto _First_ptr = _To_address(_First); + const auto _Result = static_cast>*>( + _CSTD memchr(_First_ptr, static_cast(_Val), static_cast(_Last - _First))); + if constexpr (is_pointer_v<_InIt>) { + return _Result ? _Result : _Last; + } else { + return _Result ? _First + (_Result - _First_ptr) : _Last; + } } template _NODISCARD _CONSTEXPR20 _InIt _Find_unchecked(const _InIt _First, const _InIt _Last, const _Ty& _Val) { // find first matching _Val; choose optimization - // activate optimization for pointers to (const) bytes and integral values - using _Memchr_opt = bool_constant< - is_integral_v<_Ty> && _Is_any_of_v<_InIt, char*, signed char*, unsigned char*, // - const char*, const signed char*, const unsigned char*>>; - - return _Find_unchecked1(_First, _Last, _Val, _Memchr_opt{}); + // activate optimization for contiguous iterators to (const) bytes and integral values + return _Find_unchecked1(_First, _Last, _Val, bool_constant<_Memchr_in_find_is_safe<_InIt, _Ty>>{}); } template @@ -5128,6 +5197,46 @@ template _Se, class _Ty, class _Pj = identity> + requires indirect_binary_predicate, const _Ty*> + _NODISCARD constexpr _It _Find_unchecked(_It _First, const _Se _Last, const _Ty& _Val, _Pj _Proj = {}) { + if constexpr (_Memchr_in_find_is_safe<_It, _Ty> && sized_sentinel_for<_Se, _It> && same_as<_Pj, identity>) { + if (!_STD is_constant_evaluated()) { + if (!_Within_limits(_First, _Val)) { + return _RANGES next(_STD move(_First), _Last); + } + + const auto _First_ptr = _STD to_address(_First); + const auto _Result = static_cast>*>(_CSTD memchr(_First_ptr, + static_cast(_Val), static_cast(_Last - _First))); + if (_Result) { + if constexpr (is_pointer_v<_It>) { + return _Result; + } else { + return _RANGES next(_STD move(_First), _Result - _First_ptr); + } + } else { + return _RANGES next(_STD move(_First), _Last); + } + } + } + + for (; _First != _Last; ++_First) { + if (_STD invoke(_Proj, *_First) == _Val) { + break; + } + } + + return _First; + } + // clang-format on +} // namespace ranges +#endif // __cpp_lib_concepts + // FUNCTION TEMPLATE count template _NODISCARD _CONSTEXPR20 _Iter_diff_t<_InIt> count(const _InIt _First, const _InIt _Last, const _Ty& _Val) { @@ -5217,10 +5326,10 @@ _CONSTEXPR20 void reverse(const _BidIt _First, const _BidIt _Last) { // reverse auto _UFirst = _Get_unwrapped(_First); auto _ULast = _Get_unwrapped(_Last); #if _USE_STD_VECTOR_ALGORITHMS - using _Elem = remove_pointer_t; - constexpr bool _Allow_vectorization = - conjunction_v, _Is_trivially_swappable<_Elem>, negation>>; - constexpr size_t _Nx = sizeof(_Elem); + using _Elem = remove_reference_t<_Iter_ref_t>; + constexpr bool _Allow_vectorization = conjunction_v>, + _Is_trivially_swappable<_Elem>, negation>>; + constexpr size_t _Nx = sizeof(_Elem); #pragma warning(suppress : 6326) // Potential comparison of a constant with another constant if constexpr (_Allow_vectorization && _Nx <= 8 && (_Nx & (_Nx - 1)) == 0) { @@ -5229,13 +5338,13 @@ _CONSTEXPR20 void reverse(const _BidIt _First, const _BidIt _Last) { // reverse #endif // __cpp_lib_is_constant_evaluated { if constexpr (_Nx == 1) { - __std_reverse_trivially_swappable_1(_UFirst, _ULast); + __std_reverse_trivially_swappable_1(_To_address(_UFirst), _To_address(_ULast)); } else if constexpr (_Nx == 2) { - __std_reverse_trivially_swappable_2(_UFirst, _ULast); + __std_reverse_trivially_swappable_2(_To_address(_UFirst), _To_address(_ULast)); } else if constexpr (_Nx == 4) { - __std_reverse_trivially_swappable_4(_UFirst, _ULast); + __std_reverse_trivially_swappable_4(_To_address(_UFirst), _To_address(_ULast)); } else { - __std_reverse_trivially_swappable_8(_UFirst, _ULast); + __std_reverse_trivially_swappable_8(_To_address(_UFirst), _To_address(_ULast)); } return; diff --git a/tests/std/tests/Dev11_0316853_find_memchr_optimization/test.cpp b/tests/std/tests/Dev11_0316853_find_memchr_optimization/test.cpp index e621519332d..39bd5af4eca 100644 --- a/tests/std/tests/Dev11_0316853_find_memchr_optimization/test.cpp +++ b/tests/std/tests/Dev11_0316853_find_memchr_optimization/test.cpp @@ -345,4 +345,11 @@ int main() { assert(find(begin(sc), end(sc), 0xFFFFFFFFFFFFFF7FULL) == end(sc)); assert(find(begin(sc), end(sc), 0xFFFFFFFFFFFFFF00ULL) == end(sc)); } + + { // Test bools + const bool arr[]{true, true, true, false, true, false}; + assert(find(begin(arr), end(arr), false) == begin(arr) + 3); + assert(find(begin(arr), end(arr), true) == begin(arr)); + assert(find(begin(arr), end(arr), 2) == end(arr)); + } } diff --git a/tests/std/tests/P0896R4_ranges_alg_equal/test.cpp b/tests/std/tests/P0896R4_ranges_alg_equal/test.cpp index 32c6b8ad22a..9ff968ec58b 100644 --- a/tests/std/tests/P0896R4_ranges_alg_equal/test.cpp +++ b/tests/std/tests/P0896R4_ranges_alg_equal/test.cpp @@ -63,6 +63,14 @@ constexpr void smoke_test() { int const two_ints[] = {0, 1}; assert(!equal(one_int, two_ints, comp, proj, proj)); } + { + // Validate memcmp case + int arr1[3]{0, 2, 5}; + int arr2[3]{0, 2, 5}; + assert(equal(arr1, arr2)); + arr2[1] = 7; + assert(!equal(arr1, arr2)); + } } int main() { diff --git a/tests/std/tests/P0896R4_ranges_alg_find/test.cpp b/tests/std/tests/P0896R4_ranges_alg_find/test.cpp index 768de98b7fc..f364a6823d2 100644 --- a/tests/std/tests/P0896R4_ranges_alg_find/test.cpp +++ b/tests/std/tests/P0896R4_ranges_alg_find/test.cpp @@ -48,6 +48,16 @@ struct instantiator { STATIC_ASSERT(same_as>); assert(result == wrapped_input.end()); } + { // Validate memchr case [found case] + char arr[5]{4, 8, 1, -15, 125}; + auto result = find(arr, 1); + assert(*result == 1); + } + { // Validate memchr case [not found case] + char arr[5]{4, 8, 1, -15, 125}; + auto result = find(arr, 10); + assert(result == end(arr)); + } } }; diff --git a/tests/std/tests/P0896R4_ranges_alg_lexicographical_compare/test.cpp b/tests/std/tests/P0896R4_ranges_alg_lexicographical_compare/test.cpp index 3d37682ccd9..5bc0e34358b 100644 --- a/tests/std/tests/P0896R4_ranges_alg_lexicographical_compare/test.cpp +++ b/tests/std/tests/P0896R4_ranges_alg_lexicographical_compare/test.cpp @@ -193,6 +193,15 @@ struct instantiator { empty1.begin(), empty1.end(), empty2.begin(), empty2.end(), less{}, get_first, get_second); assert(!result); } + { // Validate memcmp case + unsigned char arr1[3]{0, 1, 2}; + unsigned char arr2[3]{0, 1, 3}; + assert(lexicographical_compare(arr1, arr2)); + arr2[2] = 2; + assert(!lexicographical_compare(arr1, arr2)); + arr2[2] = 1; + assert(!lexicographical_compare(arr1, arr2)); + } } };