Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 10 additions & 15 deletions stl/inc/algorithm
Original file line number Diff line number Diff line change
Expand Up @@ -1363,7 +1363,7 @@ _NODISCARD _CONSTEXPR20 _FwdIt partition_point(_FwdIt _First, _FwdIt _Last, _Pr
template <class _InIt1, class _InIt2, class _Pr>
_NODISCARD _CONSTEXPR20 bool _Equal_rev_pred_unchecked(_InIt1 _First1, _InIt2 _First2, const _InIt2 _Last2, _Pr _Pred) {
// compare [_First1, ...) to [_First2, _Last2) using _Pred
if constexpr (decltype(_Equal_memcmp_is_safe(_First1, _First2, _Pred))::value) {
if constexpr (_Equal_memcmp_is_safe<_InIt1, _InIt2, _Pr>) {
#ifdef __cpp_lib_is_constant_evaluated
if (!_STD is_constant_evaluated())
#endif // __cpp_lib_is_constant_evaluated
Expand All @@ -1384,8 +1384,8 @@ _NODISCARD _CONSTEXPR20 bool _Equal_rev_pred_unchecked(_InIt1 _First1, _InIt2 _F
return true;
}
#else // ^^^ _HAS_IF_CONSTEXPR ^^^ // vvv !_HAS_IF_CONSTEXPR vvv
template <class _InIt1, class _InIt2, class _Pr>
bool _Equal_rev_pred_unchecked1(_InIt1 _First1, _InIt2 _First2, const _InIt2 _Last2, _Pr _Pred, false_type) {
template <class _InIt1, class _InIt2, class _Pr, enable_if_t<!_Equal_memcmp_is_safe<_InIt1, _InIt2, _Pr>, int> = 0>
bool _Equal_rev_pred_unchecked(_InIt1 _First1, _InIt2 _First2, const _InIt2 _Last2, _Pr _Pred) {
// compare [_First1, ...) to [_First2, _Last2) using _Pred, no special optimization
for (; _First2 != _Last2; ++_First1, (void) ++_First2) {
if (!_Pred(*_First1, *_First2)) {
Expand All @@ -1396,20 +1396,14 @@ bool _Equal_rev_pred_unchecked1(_InIt1 _First1, _InIt2 _First2, const _InIt2 _La
return true;
}

template <class _InIt1, class _InIt2, class _Pr>
bool _Equal_rev_pred_unchecked1(const _InIt1 _First1, const _InIt2 _First2, const _InIt2 _Last2, _Pr, true_type) {
template <class _InIt1, class _InIt2, class _Pr, enable_if_t<_Equal_memcmp_is_safe<_InIt1, _InIt2, _Pr>, int> = 0>
bool _Equal_rev_pred_unchecked(const _InIt1 _First1, const _InIt2 _First2, const _InIt2 _Last2, _Pr) {
// compare [_First1, ...) to [_First2, _Last2), memcmp optimization
const auto _First1_ch = reinterpret_cast<const char*>(_First1);
const auto _First2_ch = reinterpret_cast<const char*>(_First2);
const auto _Count = static_cast<size_t>(reinterpret_cast<const char*>(_Last2) - _First2_ch);
return _CSTD memcmp(_First1_ch, _First2_ch, _Count) == 0;
}

template <class _InIt1, class _InIt2, class _Pr>
bool _Equal_rev_pred_unchecked(const _InIt1 _First1, const _InIt2 _First2, const _InIt2 _Last2, _Pr _Pred) {
// compare [_First1, ...) to [_First2, _Last2) using _Pred, choose optimization
return _Equal_rev_pred_unchecked1(_First1, _First2, _Last2, _Pred, _Equal_memcmp_is_safe(_First1, _First2, _Pred));
}
#endif // _HAS_IF_CONSTEXPR

// FUNCTION TEMPLATE search
Expand Down Expand Up @@ -1916,11 +1910,12 @@ namespace ranges {
// compare [_First1, ...) to [_First2, _Last2)
constexpr bool _Both_identity = is_same_v<_Pj1, identity> && is_same_v<_Pj2, identity>;
// Performance note: teach _Equal_memcmp_is_safe about contiguous iterators and ranges::equal_to
if constexpr (_Both_identity && decltype(_Equal_memcmp_is_safe(_First1, _First2, _Pred))::value) {
if constexpr (_Both_identity && _Equal_memcmp_is_safe<_It1, _It2, _Pr>) {
if (!_STD is_constant_evaluated()) {
const auto _First1_ch = reinterpret_cast<const char*>(_First1);
const auto _First2_ch = reinterpret_cast<const char*>(_First2);
const auto _Count = static_cast<size_t>(reinterpret_cast<const char*>(_Last2) - _First2_ch);
const auto _First1_ch = reinterpret_cast<const char*>(_STD to_address(_First1));
const auto _First2_ch = reinterpret_cast<const char*>(_STD to_address(_First2));
const auto _Count =
static_cast<size_t>(reinterpret_cast<const char*>(_STD to_address(_Last2)) - _First2_ch);
return _CSTD memcmp(_First1_ch, _First2_ch, _Count) == 0;
}
}
Expand Down
5 changes: 3 additions & 2 deletions stl/inc/xstring
Original file line number Diff line number Diff line change
Expand Up @@ -549,9 +549,10 @@ struct _Char_traits_lt {
}
};

// library-provided char_traits::eq behaves like equal_to<_Elem>
// TRANSITION: This should not be activated for user-defined specializations of char_traits
template <class _Elem>
struct _Equal_memcmp_is_safe_helper<_Elem, _Elem, _Char_traits_eq<char_traits<_Elem>>>
: _Equal_memcmp_is_safe_helper<_Elem, _Elem, equal_to<>>::type {}; // builtin char_traits::eq acts like equal_to<>
_INLINE_VAR constexpr bool _Pred_is_consistent_with_memcmp<_Elem, _Elem, _Char_traits_eq<char_traits<_Elem>>> = true;

template <class _Traits>
using _Traits_ch_t = typename _Traits::char_type;
Expand Down
128 changes: 80 additions & 48 deletions stl/inc/xutility
Original file line number Diff line number Diff line change
Expand Up @@ -4657,55 +4657,93 @@ _FwdIt fill_n(_ExPo&&, _FwdIt _Dest, _Diff _Count_raw, const _Ty& _Val) noexcept
#endif // _HAS_CXX17

// FUNCTION TEMPLATE equal

// _Can_memcmp_elements<_Elem1, _Elem2> reports whether `_Elem1 == _Elem2` can be optimized to memcmp.
// Here, _Elem1 and _Elem2 aren't top-level const, because we remove_const_t before using _Can_memcmp_elements.

// Integral types are eligible for memcmp in very specific cases.
// * They must be the same size. (`int == long` is eligible; `int == long long` isn't.)
// * They can't be bool. (Not even `bool == bool`; we're concerned about representations other than 0 and 1.)
// * They can't be volatile.
// * Finally, the usual arithmetic conversions must preserve bit patterns. (This is true for `int == unsigned int`,
// but false for `short == unsigned short`.)
template <class _Elem1, class _Elem2,
bool = sizeof(_Elem1) == sizeof(_Elem2) //
&& _Is_nonbool_integral<_Elem1> && !is_volatile_v<_Elem1> //
&& _Is_nonbool_integral<_Elem2> && !is_volatile_v<_Elem2>>
_INLINE_VAR constexpr bool _Can_memcmp_elements = static_cast<_Elem1>(-1) == static_cast<_Elem2>(-1);

#ifdef __cpp_lib_byte
// Allow memcmping std::byte.
// inline is required here as explicit specializations of variable templates are problematic in C++14.
// However, std::byte is C++17 and above so we are safe.
template <>
inline constexpr bool _Can_memcmp_elements<byte, byte, false> = true;
#endif // __cpp_lib_byte

// Pointer elements are eligible for memcmp when they point to the same type, ignoring cv-qualification.
// This handles pointers to object types, pointers to void, and pointers to function types.
// Performance note: This doesn't attempt to handle `object* == void*`.
template <class _Ty1, class _Ty2>
_INLINE_VAR constexpr bool _Can_memcmp_elements<_Ty1*, _Ty2*, false> = is_same_v<remove_cv_t<_Ty1>, remove_cv_t<_Ty2>>;

template <class _Elem1, class _Elem2>
struct _Value_equality_is_bitwise_equality : bool_constant<static_cast<_Elem1>(-1) == static_cast<_Elem2>(-1)> {
// Tests whether the usual arithmetic conversions will preserve the bit-pattern when promoting to int
// e.g. short == unsigned short -> false
// int == unsigned int -> true
};
_INLINE_VAR constexpr bool _Can_memcmp_elements<_Elem1, _Elem2, false> = false;

// _Pred_is_consistent_with_memcmp<_Elem1, _Elem2, _Pr> reports whether `_Pr(_Elem1, _Elem2)` returns
// `_Elem1 == _Elem2` without performing any type conversions that could affect the memcmp optimization.
// (It isn't concerned with whether the elements are actually eligible.)
// Again, _Elem1 and _Elem2 aren't top-level const here.
template <class _Elem1, class _Elem2, class _Pr>
struct _Equal_memcmp_is_safe_helper : false_type {
// determines whether it is safe to call memcmp to compare things; defaults to false
};
_INLINE_VAR constexpr bool _Pred_is_consistent_with_memcmp = false;

// When all of the types are the same, equal_to<_Elem> is effectively transparent.
// Performance note: This doesn't attempt to handle equal_to<long>(int, long) or equal_to<const T*>(T*, const T*).
template <class _Elem>
_INLINE_VAR constexpr bool _Pred_is_consistent_with_memcmp<_Elem, _Elem, equal_to<_Elem>> = true;

// equal_to<> is transparent.
template <class _Elem1, class _Elem2>
struct _Equal_memcmp_is_safe_helper<_Elem1, _Elem2, equal_to<>>
: bool_constant<conjunction_v<bool_constant<sizeof(_Elem1) == sizeof(_Elem2)
&& _Is_nonbool_integral<_Elem1> && _Is_nonbool_integral<_Elem2>>,
negation<is_volatile<_Elem1>>, negation<is_volatile<_Elem2>>,
// order matters here: being integral is a precondition of _Value_equality_is_bitwise_equality
_Value_equality_is_bitwise_equality<_Elem1, _Elem2>>> {
// allow memcmping same-size integral non-bool non-volatile bitwise types using equal_to<>
};
_INLINE_VAR constexpr bool _Pred_is_consistent_with_memcmp<_Elem1, _Elem2, equal_to<>> = true;

#ifdef __cpp_lib_concepts
// ranges::equal_to is also transparent.
template <class _Elem1, class _Elem2>
struct _Equal_memcmp_is_safe_helper<_Elem1*, _Elem2*, equal_to<>>
: is_same<remove_cv_t<_Elem1>, remove_cv_t<_Elem2>>::type {}; // allow memcmping pointers-to-cv-T with equal_to<>
_INLINE_VAR constexpr bool _Pred_is_consistent_with_memcmp<_Elem1, _Elem2, _RANGES equal_to> = true;
#endif // __cpp_lib_concepts

#ifdef __cpp_lib_byte
template <>
struct _Equal_memcmp_is_safe_helper<byte, byte, equal_to<>> : true_type {}; // allow memcmping std::byte
#endif // __cpp_lib_byte
// _Can_memcmp_elements_with_pred<_Elem1, _Elem2, _Pr> reports whether the memcmp optimization is applicable,
// given contiguously stored elements. (This avoids having to repeat the metaprogramming that finds the element types.)
// _Elem1 and _Elem2 aren't top-level const here.
template <class _Elem1, class _Elem2, class _Pr>
_INLINE_VAR constexpr bool _Can_memcmp_elements_with_pred = _Can_memcmp_elements<_Elem1, _Elem2> //
&& _Pred_is_consistent_with_memcmp<_Elem1, _Elem2, _Pr>;

template <class _Elem>
struct _Equal_memcmp_is_safe_helper<_Elem, _Elem, equal_to<_Elem>>
: _Equal_memcmp_is_safe_helper<_Elem, _Elem, equal_to<>>::type {
// treat equal_to with exact T as equal_to<> this is safe because we only activate the optimization for builtin
// _Elem (and std::byte)
};
// _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 <class _Iter1, class _Iter2>
_INLINE_VAR constexpr bool _Iterators_are_contiguous = contiguous_iterator<_Iter1> //
&& contiguous_iterator<_Iter2>;
#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 <class _Iter1, class _Iter2>
_INLINE_VAR constexpr bool _Iterators_are_contiguous = conjunction_v<is_pointer<_Iter1>, is_pointer<_Iter2>>;
#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.
template <class _Iter1, class _Iter2, class _Pr>
false_type _Equal_memcmp_is_safe(const _Iter1&, const _Iter2&, const _Pr&) {
// return equal optimization category for arbitrary iterators
return {};
}
_INLINE_VAR constexpr bool _Equal_memcmp_is_safe_helper = _Iterators_are_contiguous<_Iter1, _Iter2> //
&& _Can_memcmp_elements_with_pred<remove_const_t<remove_reference_t<_Iter_ref_t<_Iter1>>>,
remove_const_t<remove_reference_t<_Iter_ref_t<_Iter2>>>, _Pr>;

template <class _Obj1, class _Obj2, class _Pr>
typename _Equal_memcmp_is_safe_helper<remove_const_t<_Obj1>, remove_const_t<_Obj2>, _Pr>::type _Equal_memcmp_is_safe(
_Obj1* const&, _Obj2* const&, const _Pr&) { // return equal optimization category for pointers
return {};
}
template <class _Iter1, class _Iter2, class _Pr>
_INLINE_VAR constexpr bool _Equal_memcmp_is_safe =
_Equal_memcmp_is_safe_helper<remove_const_t<_Iter1>, remove_const_t<_Iter2>, _Pr>;

#if _HAS_IF_CONSTEXPR
template <class _InIt1, class _InIt2, class _Pr>
Expand All @@ -4715,7 +4753,7 @@ _NODISCARD _CONSTEXPR20 bool equal(const _InIt1 _First1, const _InIt1 _Last1, co
auto _UFirst1 = _Get_unwrapped(_First1);
const auto _ULast1 = _Get_unwrapped(_Last1);
auto _UFirst2 = _Get_unwrapped_n(_First2, _Idl_distance<_InIt1>(_UFirst1, _ULast1));
if constexpr (decltype(_Equal_memcmp_is_safe(_UFirst1, _UFirst2, _Pred))::value) {
if constexpr (_Equal_memcmp_is_safe<decltype(_UFirst1), decltype(_UFirst2), _Pr>) {
#ifdef __cpp_lib_is_constant_evaluated
if (!_STD is_constant_evaluated())
#endif // __cpp_lib_is_constant_evaluated
Expand All @@ -4736,8 +4774,8 @@ _NODISCARD _CONSTEXPR20 bool equal(const _InIt1 _First1, const _InIt1 _Last1, co
return true;
}
#else // ^^^ _HAS_IF_CONSTEXPR / !_HAS_IF_CONSTEXPR vvv
template <class _InIt1, class _InIt2, class _Pr>
bool _Equal_unchecked1(_InIt1 _First1, const _InIt1 _Last1, _InIt2 _First2, _Pr _Pred, false_type) {
template <class _InIt1, class _InIt2, class _Pr, enable_if_t<!_Equal_memcmp_is_safe<_InIt1, _InIt2, _Pr>, int> = 0>
bool _Equal_unchecked(_InIt1 _First1, const _InIt1 _Last1, _InIt2 _First2, _Pr _Pred) {
// compare [_First1, _Last1) to [_First2, ...) using _Pred, no special optimization
for (; _First1 != _Last1; ++_First1, (void) ++_First2) {
if (!_Pred(*_First1, *_First2)) {
Expand All @@ -4748,21 +4786,15 @@ bool _Equal_unchecked1(_InIt1 _First1, const _InIt1 _Last1, _InIt2 _First2, _Pr
return true;
}

template <class _InIt1, class _InIt2, class _Pr>
bool _Equal_unchecked1(const _InIt1 _First1, const _InIt1 _Last1, const _InIt2 _First2, _Pr, true_type) {
template <class _InIt1, class _InIt2, class _Pr, enable_if_t<_Equal_memcmp_is_safe<_InIt1, _InIt2, _Pr>, int> = 0>
bool _Equal_unchecked(const _InIt1 _First1, const _InIt1 _Last1, const _InIt2 _First2, _Pr) {
// compare [_First1, _Last1) to [_First2, ...), memcmp optimization
const auto _First1_ch = reinterpret_cast<const char*>(_First1);
const auto _First2_ch = reinterpret_cast<const char*>(_First2);
const auto _Count = static_cast<size_t>(reinterpret_cast<const char*>(_Last1) - _First1_ch);
return _CSTD memcmp(_First1_ch, _First2_ch, _Count) == 0;
}

template <class _InIt1, class _InIt2, class _Pr>
bool _Equal_unchecked(const _InIt1 _First1, const _InIt1 _Last1, const _InIt2 _First2, _Pr _Pred) {
// compare [_First1, _Last1) to [_First2, ...) using _Pred, choose optimization
return _Equal_unchecked1(_First1, _Last1, _First2, _Pred, _Equal_memcmp_is_safe(_First1, _First2, _Pred));
}

template <class _InIt1, class _InIt2, class _Pr>
_NODISCARD bool equal(const _InIt1 _First1, const _InIt1 _Last1, const _InIt2 _First2, _Pr _Pred) {
// compare [_First1, _Last1) to [_First2, ...) using _Pred
Expand Down
Loading