diff --git a/stl/inc/vector b/stl/inc/vector index 8b6f54bca9a..82939e0cbab 100644 --- a/stl/inc/vector +++ b/stl/inc/vector @@ -2880,6 +2880,417 @@ namespace pmr { using vector = _STD vector<_Ty, polymorphic_allocator<_Ty>>; } // namespace pmr #endif // _HAS_CXX17 + +#if _HAS_IF_CONSTEXPR +// VARIABLE TEMPLATE _Is_vb_iterator +template +_INLINE_VAR constexpr bool _Is_vb_iterator<_Vb_const_iterator<_Alloc>> = true; + +template +_INLINE_VAR constexpr bool _Is_vb_iterator<_Vb_iterator<_Alloc>> = true; + +template +_CONSTEXPR20 _OutIt _Copy_vbool(_InIt _First, _InIt _Last, _OutIt _Dest) { + // copy [_First, _Last) to [_Dest, ...) + + const auto _DestEnd = _Dest + (_Last - _First); + _Vbase* _UFirst = const_cast<_Vbase*>(_First._Myptr); + _Vbase* _UDest = const_cast<_Vbase*>(_Dest._Myptr); + _Vbase* _ULast = const_cast<_Vbase*>(_Last._Myptr); + + const bool _IsRightShift = _Dest._Myoff < _First._Myoff; + const auto _SourceShift = _IsRightShift ? _First._Myoff - _Dest._Myoff : _Dest._Myoff - _First._Myoff; + + const auto _SourceMask = _Vbase(-1) << _First._Myoff; + const auto _DestMask = _Dest._Myoff == 0 ? _Vbase(0) : _Vbase(-1) >> (_VBITS - _Dest._Myoff); + const auto _LastMask = _Last._Myoff == 0 ? _Vbase(0) : _Vbase(-1) >> (_VBITS - _Last._Myoff); + const auto _LastDestMask = _IsRightShift ? _Vbase(-1) << (_Last._Myoff - _SourceShift) // + : _Vbase(-1) << (_Last._Myoff + _SourceShift); + + // Fast path for less than _VBITS + // Always needed in case we only copy a range within one char + if (_UFirst == _ULast) { + const auto _SourceVal = _IsRightShift ? (*_UFirst & _SourceMask & _LastMask) >> _SourceShift // + : (*_UFirst & _SourceMask & _LastMask) << _SourceShift; + *_UDest = (*_UDest & (_DestMask | _LastDestMask)) | _SourceVal; + + if (_DestEnd._Myptr == _Dest._Myptr) { + return _DestEnd; + } + + ++_UDest; + const auto _CarryShift = _Last._Myoff - _DestEnd._Myoff; + const auto _CarryMask = _Vbase(-1) << _CarryShift; + const auto _CarryVal = (*_UFirst & _CarryMask & _LastMask) >> _CarryShift; + + const auto _DestEndMask = _Vbase(-1) << _DestEnd._Myoff; + *_UDest = (*_UDest & _DestEndMask) | _CarryVal; + return _DestEnd; + } + + // _First and _Dest have matching char alignment, so use memmove + const auto _UnalignedFirstBits = _First._Myoff & _Vbase(7); + const auto _UnalignedDestBits = _Dest._Myoff & _Vbase(7); + if (_UnalignedFirstBits == _UnalignedDestBits) { + const auto _UnalignedLastBits = _Last._Myoff & _Vbase(7); + + // What is the Strict Aliasing Rule and Why do we care? + char* _UFirst_ch = reinterpret_cast(_UFirst) + (_First._Myoff - _UnalignedFirstBits) / 8; + char* _UDest_ch = reinterpret_cast(_UDest) + (_Dest._Myoff - _UnalignedFirstBits) / 8; + char* _ULast_ch = reinterpret_cast(_ULast) + (_Last._Myoff - _UnalignedLastBits) / 8; + + // Copy bits until the next char alignment + if (_UnalignedFirstBits != 0) { + const auto _SourceBitMask = static_cast(UCHAR_MAX << _UnalignedFirstBits); + const auto _DestBitMask = static_cast(UCHAR_MAX >> (8 - _UnalignedFirstBits)); + *_UDest_ch = (*_UDest_ch & _DestBitMask) | (*_UFirst_ch & _SourceBitMask); + ++_UFirst_ch; + ++_UDest_ch; + } + + if (true +#ifdef __cpp_lib_is_constant_evaluated + && !_STD is_constant_evaluated() +#endif // __cpp_lib_is_constant_evaluated + ) { + _UDest_ch = _Copy_memmove(_UFirst_ch, _ULast_ch, _UDest_ch); + } else { + for (; _UFirst_ch != _ULast_ch; ++_UDest_ch, (void) ++_UFirst_ch) { + *_UDest_ch = *_UFirst_ch; + } + } + + // Copy remaining last bits, shifts needed as we are already in + if (_UnalignedLastBits != 0) { + const auto _SourceBitMask = static_cast(UCHAR_MAX >> (8 - _UnalignedLastBits)); + const auto _DestBitMask = static_cast(UCHAR_MAX << _UnalignedLastBits); + *_UDest_ch = (*_UDest_ch & _DestBitMask) | (*_ULast_ch & _SourceBitMask); + } + return _DestEnd; + } + + // Unaligned _UFirst and _ULast require a two step copy with carry + const auto _FirstSourceVal = _IsRightShift ? (*_UFirst & _SourceMask) >> _SourceShift // + : (*_UFirst & _SourceMask) << _SourceShift; + *_UDest = (*_UDest & _DestMask) | _FirstSourceVal; + + const auto _CarryShift = _VBITS - _SourceShift; + if (_IsRightShift) { + // Source : | | | + // Dest : | | | + // ^^^^^^^ ^^^^^^^^^^^^^^^^^ ^^^^^^^ ^^^^^^^^^^^^^^^^^ + // _CarryVal _SourceVal _CarryVal _SourceVal + const auto _SourceCarryMask = _Vbase(-1) >> _CarryShift; + const auto _DestCarryMask = _Vbase(-1) >> _SourceShift; + + ++_UFirst; + for (; _UFirst != _ULast; ++_UFirst) { + const auto _CarryVal = (*_UFirst & _SourceCarryMask) << _CarryShift; + *_UDest = (*_UDest & _DestCarryMask) | _CarryVal; + + ++_UDest; + const auto _SourceVal = (*_UFirst & _SourceMask) >> _SourceShift; + *_UDest = _SourceVal; + } + + if (_Last._Myoff != 0) { + const auto _LastSourceCarryMask = _SourceCarryMask & _LastMask; + const auto _LastDestCarryMask = _DestCarryMask | (_Vbase(-1) << (_VBITS - _Last._Myoff)); + const auto _CarryVal = (*_UFirst & _LastSourceCarryMask) << _CarryShift; + *_UDest = (*_UDest & _LastDestCarryMask) | _CarryVal; + } + + if (_Last._Myoff > _SourceShift) { + const auto _LastSourceMask = _SourceMask & _LastMask; + const auto _SourceVal = (*_UFirst & _LastSourceMask) >> _SourceShift; + ++_UDest; + *_UDest = (*_UDest & _LastDestMask) | _SourceVal; + } + } else { + // Source : | | | + // Dest : | | | + // ^^^^^^^ ^^^^^^^^^^^^^^^^^ ^^^^^^^ ^^^^^^^^^^^^^^^^^ + // _CarryVal _SourceVal _CarryVal _SourceVal + const auto _SourceCarryMask = _Vbase(-1) << _CarryShift; + + auto _CarryVal = (*_UFirst & _SourceCarryMask) >> _CarryShift; + ++_UFirst; + ++_UDest; + for (; _UFirst != _ULast; ++_UFirst, (void) ++_UDest) { + const auto _SourceVal = *_UFirst << _SourceShift; + *_UDest = _CarryVal | _SourceVal; + + _CarryVal = (*_UFirst & _SourceCarryMask) >> _CarryShift; + } + + const auto _SourceVal = _Last._Myoff != 0 ? (*_UFirst & _LastMask) << _SourceShift : _Vbase(0); + *_UDest = (*_UDest & _LastDestMask) | _CarryVal | _SourceVal; + } + return _DestEnd; +} + +template +_CONSTEXPR20 _BidIt2 _Copy_backward_vbool(_BidIt1 _First, _BidIt1 _Last, _BidIt2 _Dest) { + // copy [_First, _Last) backwards to [..., _Dest) + + // Slow path as _Last and _Dest are not aligned + if (_Last._Myoff != _Dest._Myoff) { + while (_First != _Last) { + *--_Dest = *--_Last; + } + return _Dest; + } + + _Vbase* _UFirst = const_cast<_Vbase*>(_First._Myptr); + _Vbase* _UDest = const_cast<_Vbase*>(_Dest._Myptr); + _Vbase* _ULast = const_cast<_Vbase*>(_Last._Myptr); + + // Fast path for less than _VBITS + const auto _LastShift = _VBITS - _Last._Myoff; + if (_UFirst == _ULast) { + const auto _SourceMask = (_Vbase(-1) << _First._Myoff) & (_Vbase(-1) >> _LastShift); + const auto _DestMask = _Vbase(-1) ^ _SourceMask; + *_UDest = (*_UDest & _DestMask) | (*_UFirst & _SourceMask); + return _Dest - (_Last - _First); + } + + if (_Last._Myoff != 0) { + const auto _LastSourceMask = _Vbase(-1) >> _LastShift; + const auto _LastDestMask = _Vbase(-1) ^ _LastSourceMask; + *_UDest = (*_UDest & _LastDestMask) | (*_ULast & _LastSourceMask); + } + + if (true +#ifdef __cpp_lib_is_constant_evaluated + && !_STD is_constant_evaluated() +#endif // __cpp_lib_is_constant_evaluated + ) { + // Copy only to _UFirst to properly handle unaligned _First + _UDest = _Copy_backward_memmove(_UFirst + 1, _ULast, _UDest); + --_UDest; + } else { + --_UDest; + --_ULast; + for (; _UFirst != _ULast; --_UDest, (void) --_ULast) { + *_UDest = *_ULast; + } + } + + const auto _FirstSourceMask = _Vbase(-1) << _First._Myoff; + const auto _FirstDestMask = _Vbase(-1) ^ _FirstSourceMask; + *_UDest = (*_UDest & _FirstDestMask) | (*_UFirst & _FirstSourceMask); + + return _Dest - (_Last - _First); +} + +template +_CONSTEXPR20 void _Fill_vbool(_FwdIt _First, _FwdIt _Last, const _Ty& _Val) { + // Set [_First, _Last) to _Val + + _Vbase* _UFirst = const_cast<_Vbase*>(_First._Myptr); + const _Vbase* const _ULast = _Last._Myptr; + + // Fast path for less than _VBITS + const auto _LastShift = _VBITS - _Last._Myoff; + if (_UFirst == _ULast) { + const auto _SourceMask = (_Vbase(-1) << _First._Myoff) & (_Vbase(-1) >> _LastShift); + const auto _DestMask = _Vbase(-1) ^ _SourceMask; + *_UFirst = _Val ? (*_UFirst & _DestMask) | _SourceMask : (*_UFirst & _DestMask); + return; + } + + if (_First._Myoff != 0) { + const auto _FirstSourceMask = _Vbase(-1) << _First._Myoff; + const auto _FirstDestMask = _Vbase(-1) ^ _FirstSourceMask; + *_UFirst = _Val ? (*_UFirst & _FirstDestMask) | _FirstSourceMask : (*_UFirst & _FirstDestMask); + ++_UFirst; + } + + if (true +#ifdef __cpp_lib_is_constant_evaluated + && !_STD is_constant_evaluated() +#endif // __cpp_lib_is_constant_evaluated + ) { + const auto _UFirst_ch = reinterpret_cast(_UFirst); + const auto _ULast_ch = reinterpret_cast(_ULast); + const auto _Count_ch = static_cast(_ULast_ch - _UFirst_ch); + const auto _ValChar = static_cast(_Val ? -1 : 0); + _CSTD memset(_UFirst, _ValChar, _Count_ch); + _UFirst += _Count_ch / sizeof(_Vbase); + } else { + for (; _UFirst != _ULast; ++_UFirst) { + *_UFirst = _Val ? _Vbase(-1) : _Vbase(0); + } + } + + if (_Last._Myoff != 0) { + const auto _LastSourceMask = _Vbase(-1) >> _LastShift; + const auto _LastDestMask = _Vbase(-1) ^ _LastSourceMask; + *_UFirst = _Val ? (*_UFirst & _LastDestMask) | _LastSourceMask : (*_UFirst & _LastDestMask); + } +} + +template +_NODISCARD _CONSTEXPR20 bool _Equal_vbool(_InIt1 _First1, _InIt1 _Last1, _InIt2 _First2, _Pr) { + // compare [_First1, _Last1) to [_First2, ...) + + // Slow path as _First1 and _First2 are not aligned + if (_First1._Myoff != _First2._Myoff) { + for (; _First1 != _Last1; ++_First2, (void) ++_First1) { + if (*_First1 != *_First2) { + return false; + } + } + + return true; + } + + _Vbase* _UFirst1 = const_cast<_Vbase*>(_First1._Myptr); + _Vbase* _UFirst2 = const_cast<_Vbase*>(_First2._Myptr); + const _Vbase* const _ULast1 = _Last1._Myptr; + + // Fast path for less than _VBITS + const auto _LastShift = _VBITS - _Last1._Myoff; + if (_UFirst1 == _ULast1) { + const auto _Mask = (_Vbase(-1) << _First1._Myoff) & (_Vbase(-1) >> _LastShift); + return (*_UFirst1 & _Mask) == (*_UFirst2 & _Mask); + } + + if (_First1._Myoff != 0) { + const auto _FirstMask = _Vbase(-1) << _First1._Myoff; + if ((*_UFirst1 & _FirstMask) != (*_UFirst2 & _FirstMask)) { + return false; + } + ++_UFirst1; + ++_UFirst2; + } + + if (true +#ifdef __cpp_lib_is_constant_evaluated + && !_STD is_constant_evaluated() +#endif // __cpp_lib_is_constant_evaluated + ) { + const auto _First1_ch = reinterpret_cast(_UFirst1); + const auto _First2_ch = reinterpret_cast(_UFirst2); + const auto _Count_ch = static_cast(reinterpret_cast(_ULast1) - _First1_ch); + if (_CSTD memcmp(_First1_ch, _First2_ch, _Count_ch) != 0) { + return false; + } + _UFirst1 += _Count_ch / sizeof(_Vbase); + _UFirst2 += _Count_ch / sizeof(_Vbase); + } else { + for (; _UFirst1 != _ULast1; ++_UFirst1, (void) ++_UFirst2) { + if (*_UFirst1 != *_UFirst2) { + return false; + } + } + } + + if (_Last1._Myoff != 0) { + const auto _LastMask = _Vbase(-1) >> _LastShift; + return (*_UFirst1 & _LastMask) == (*_UFirst2 & _LastMask); + } + return true; +} + +template +_NODISCARD _CONSTEXPR20 _InIt _Find_vbool(_InIt _First, const _InIt _Last, const _Ty& _Val) { +#ifdef __cpp_lib_bitops // TRANSITION, VSO-1020212 + _Vbase* _UFirst = const_cast<_Vbase*>(_First._Myptr); + const _Vbase* const _ULast = _Last._Myptr; + + // Fast path for less than _VBITS + const auto _LastShift = _VBITS - _Last._Myoff; + if (_UFirst == _ULast) { + const auto _Mask = (_Vbase(-1) << _First._Myoff) & (_Vbase(-1) >> _LastShift); + const auto _SelectVal = _Val ? *_UFirst : static_cast<_Vbase>(~*_UFirst); + const auto _Count = _Countr_zero(_SelectVal & _Mask); + return _First + (_Count - _First._Myoff); + } + + _Iter_diff_t<_InIt> _TotalCount = 0; + if (_First._Myoff != 0) { + const auto _FirstMask = _Vbase(-1) << _First._Myoff; + const auto _SelectVal = _Val ? *_UFirst : static_cast<_Vbase>(~*_UFirst); + const auto _Count = _Countr_zero(_SelectVal & _FirstMask); + if (_Count != _VBITS) { + return _First + (_Count - _First._Myoff); + } + _TotalCount += _Count - _First._Myoff; + ++_UFirst; + } + + for (; _UFirst != _ULast; ++_UFirst, _TotalCount += _VBITS) { + const auto _SelectVal = _Val ? *_UFirst : static_cast<_Vbase>(~*_UFirst); + const auto _Count = _Countr_zero(_SelectVal); + if (_Count != _VBITS) { + return _First + (_TotalCount + _Count); + } + } + + if (_Last._Myoff != 0) { + const auto _LastMask = _Vbase(-1) >> _LastShift; + const auto _SelectVal = _Val ? *_UFirst : static_cast<_Vbase>(~*_UFirst); + const auto _Count = _Countr_zero(_SelectVal & _LastMask); + if (_Count < static_cast(_Last._Myoff)) { + return _First + (_TotalCount + _Count); + } + } +#else // ^^^ __cpp_lib_bitops / !__cpp_lib_bitops vvv + for (; _First != _Last; ++_First) { + if (*_First == _Val) { + return _First; + } + } +#endif // !__cpp_lib_bitops + + return _Last; +} + +template +_NODISCARD _CONSTEXPR20 _Iter_diff_t<_InIt> _Count_vbool(_InIt _First, const _InIt _Last, const _Ty& _Val) { + _Iter_diff_t<_InIt> _Count = 0; + +#ifdef __cpp_lib_bitops // TRANSITION, VSO-1020212 + _Vbase* _UFirst = const_cast<_Vbase*>(_First._Myptr); + const _Vbase* const _ULast = _Last._Myptr; + + // Fast path for less than _VBITS + const auto _LastShift = _VBITS - _Last._Myoff; + if (_UFirst == _ULast) { + const auto _Mask = (_Vbase(-1) << _First._Myoff) & (_Vbase(-1) >> _LastShift); + const auto _SelectVal = _Val ? *_UFirst : static_cast<_Vbase>(~*_UFirst); + return __builtin_popcount(_SelectVal & _Mask); + } + + if (_First._Myoff != 0) { + const auto _FirstMask = _Vbase(-1) << _First._Myoff; + const auto _SelectVal = _Val ? *_UFirst : static_cast<_Vbase>(~*_UFirst); + _Count += __builtin_popcount(_SelectVal & _FirstMask); + ++_UFirst; + } + + for (; _UFirst != _ULast; ++_UFirst) { + const auto _SelectVal = _Val ? *_UFirst : static_cast<_Vbase>(~*_UFirst); + _Count += __builtin_popcount(_SelectVal); + } + + if (_Last._Myoff != 0) { + const auto _LastMask = _Vbase(-1) >> _LastShift; + const auto _SelectVal = _Val ? *_UFirst : static_cast<_Vbase>(~*_UFirst); + _Count += __builtin_popcount(_SelectVal & _LastMask); + } +#else // ^^^ __cpp_lib_bitops / !__cpp_lib_bitops vvv + for (; _First != _Last; ++_First) { + if (*_First == _Val) { + ++_Count; + } + } +#endif // !__cpp_lib_bitops + + return _Count; +} +#endif // _HAS_IF_CONSTEXPR _STD_END #pragma pop_macro("new") diff --git a/stl/inc/xutility b/stl/inc/xutility index 83693c452fe..01fc78c180f 100644 --- a/stl/inc/xutility +++ b/stl/inc/xutility @@ -3774,11 +3774,17 @@ _OutIt _Copy_memmove(move_iterator<_InIt> _First, move_iterator<_InIt> _Last, _O } #if _HAS_IF_CONSTEXPR +// VARIABLE TEMPLATE _Is_vb_iterator +template +_INLINE_VAR constexpr bool _Is_vb_iterator = false; + template _CONSTEXPR20 _OutIt _Copy_unchecked(_InIt _First, _InIt _Last, _OutIt _Dest) { // copy [_First, _Last) to [_Dest, ...) // note: _Copy_unchecked has callers other than the copy family - if constexpr (_Ptr_copy_cat<_InIt, _OutIt>::_Trivially_copyable) { + if constexpr (_Is_vb_iterator<_InIt> && _Is_vb_iterator<_OutIt>) { + return _Copy_vbool(_First, _Last, _Dest); + } else if constexpr (_Ptr_copy_cat<_InIt, _OutIt>::_Trivially_copyable) { #ifdef __cpp_lib_is_constant_evaluated if (!_STD is_constant_evaluated()) #endif // __cpp_lib_is_constant_evaluated @@ -3866,7 +3872,9 @@ _CONSTEXPR20 _OutIt copy_n(_InIt _First, _Diff _Count_raw, _OutIt _Dest) { if (0 < _Count) { auto _UFirst = _Get_unwrapped_n(_First, _Count); auto _UDest = _Get_unwrapped_n(_Dest, _Count); - if constexpr (_Ptr_copy_cat::_Trivially_copyable) { + if constexpr (_Is_vb_iterator<_InIt> && _Is_vb_iterator<_OutIt>) { + return _Copy_vbool(_First, _First + _Count, _Dest); + } else if constexpr (_Ptr_copy_cat::_Trivially_copyable) { #ifdef __cpp_lib_is_constant_evaluated if (!_STD is_constant_evaluated()) #endif // __cpp_lib_is_constant_evaluated @@ -4038,7 +4046,9 @@ _CONSTEXPR20 _BidIt2 copy_backward(_BidIt1 _First, _BidIt1 _Last, _BidIt2 _Dest) const auto _UFirst = _Get_unwrapped(_First); auto _ULast = _Get_unwrapped(_Last); auto _UDest = _Get_unwrapped_n(_Dest, -_Idl_distance<_BidIt1>(_UFirst, _ULast)); - if constexpr (_Ptr_copy_cat::_Trivially_copyable) { + if constexpr (_Is_vb_iterator<_BidIt1> && _Is_vb_iterator<_BidIt2>) { + return _Copy_backward_vbool(_First, _Last, _Dest); + } else if constexpr (_Ptr_copy_cat::_Trivially_copyable) { #ifdef __cpp_lib_is_constant_evaluated if (!_STD is_constant_evaluated()) #endif // __cpp_lib_is_constant_evaluated @@ -4099,7 +4109,9 @@ template _CONSTEXPR20 _OutIt _Move_unchecked(_InIt _First, _InIt _Last, _OutIt _Dest) { // move [_First, _Last) to [_Dest, ...) // note: _Move_unchecked has callers other than the move family - if constexpr (_Ptr_move_cat<_InIt, _OutIt>::_Trivially_copyable) { + if constexpr (_Is_vb_iterator<_InIt> && _Is_vb_iterator<_OutIt>) { + return _Copy_vbool(_First, _Last, _Dest); + } else if constexpr (_Ptr_move_cat<_InIt, _OutIt>::_Trivially_copyable) { #ifdef __cpp_lib_is_constant_evaluated if (!_STD is_constant_evaluated()) #endif // __cpp_lib_is_constant_evaluated @@ -4184,7 +4196,9 @@ template _CONSTEXPR20 _BidIt2 _Move_backward_unchecked(_BidIt1 _First, _BidIt1 _Last, _BidIt2 _Dest) { // move [_First, _Last) backwards to [..., _Dest) // note: _Move_backward_unchecked has callers other than the move_backward family - if constexpr (_Ptr_move_cat<_BidIt1, _BidIt2>::_Trivially_copyable) { + if constexpr (_Is_vb_iterator<_BidIt1> && _Is_vb_iterator<_BidIt2>) { + return _Copy_backward_vbool(_First, _Last, _Dest); + } else if constexpr (_Ptr_move_cat<_BidIt1, _BidIt2>::_Trivially_copyable) { #ifdef __cpp_lib_is_constant_evaluated if (!_STD is_constant_evaluated()) #endif // __cpp_lib_is_constant_evaluated @@ -4289,7 +4303,9 @@ _CONSTEXPR20 void fill(const _FwdIt _First, const _FwdIt _Last, const _Ty& _Val) _Adl_verify_range(_First, _Last); auto _UFirst = _Get_unwrapped(_First); const auto _ULast = _Get_unwrapped(_Last); - if constexpr (_Fill_memset_is_safe) { + if constexpr (_Is_vb_iterator<_FwdIt>) { + return _Fill_vbool(_First, _Last, _Val); + } else if constexpr (_Fill_memset_is_safe) { #ifdef __cpp_lib_is_constant_evaluated if (!_STD is_constant_evaluated()) #endif // __cpp_lib_is_constant_evaluated @@ -4344,7 +4360,11 @@ _CONSTEXPR20 _OutIt fill_n(_OutIt _Dest, const _Diff _Count_raw, const _Ty& _Val _Algorithm_int_t<_Diff> _Count = _Count_raw; if (0 < _Count) { auto _UDest = _Get_unwrapped_n(_Dest, _Count); - if constexpr (_Fill_memset_is_safe) { + if constexpr (_Is_vb_iterator<_OutIt>) { + auto _Last = _Dest + _Count; + _Fill_vbool(_Dest, _Last, _Val); + return _Last; + } else if constexpr (_Fill_memset_is_safe) { #ifdef __cpp_lib_is_constant_evaluated if (!_STD is_constant_evaluated()) #endif // __cpp_lib_is_constant_evaluated @@ -4467,7 +4487,9 @@ _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 (_Is_vb_iterator<_InIt1> && _Is_vb_iterator<_InIt2> && is_same_v<_Pr, equal_to<>>) { + return _Equal_vbool(_First1, _Last1, _First2, _Pred); + } else if constexpr (decltype(_Equal_memcmp_is_safe(_UFirst1, _UFirst2, _Pred))::value) { #ifdef __cpp_lib_is_constant_evaluated if (!_STD is_constant_evaluated()) #endif // __cpp_lib_is_constant_evaluated @@ -4597,7 +4619,12 @@ _NODISCARD _CONSTEXPR20 bool equal( const auto _ULast1 = _Get_unwrapped(_Last1); auto _UFirst2 = _Get_unwrapped(_First2); const auto _ULast2 = _Get_unwrapped(_Last2); - if constexpr (_Is_random_iter_v<_InIt1> && _Is_random_iter_v<_InIt2>) { + if constexpr (_Is_vb_iterator<_InIt1> && _Is_vb_iterator<_InIt2> && is_same_v<_Pr, equal_to<>>) { + if (_Last1 - _First1 != _Last2 - _First2) { + return false; + } + return _Equal_vbool(_First1, _Last1, _First2, _Pred); + } else if constexpr (_Is_random_iter_v<_InIt1> && _Is_random_iter_v<_InIt2>) { if (_ULast1 - _UFirst1 != _ULast2 - _UFirst2) { return false; } @@ -4963,8 +4990,16 @@ _NODISCARD _CONSTEXPR20 _InIt _Find_unchecked(const _InIt _First, const _InIt _L template _NODISCARD _CONSTEXPR20 _InIt find(_InIt _First, const _InIt _Last, const _Ty& _Val) { // find first matching _Val _Adl_verify_range(_First, _Last); - _Seek_wrapped(_First, _Find_unchecked(_Get_unwrapped(_First), _Get_unwrapped(_Last), _Val)); - return _First; +#if _HAS_IF_CONSTEXPR + if constexpr (_Is_vb_iterator<_InIt> && is_same_v<_Ty, bool>) { + return _Find_vbool(_First, _Last, _Val); + } else { +#endif // _HAS_IF_CONSTEXPR + _Seek_wrapped(_First, _Find_unchecked(_Get_unwrapped(_First), _Get_unwrapped(_Last), _Val)); + return _First; +#if _HAS_IF_CONSTEXPR + } +#endif // _HAS_IF_CONSTEXPR } #if _HAS_CXX17 @@ -4977,17 +5012,25 @@ template _NODISCARD _CONSTEXPR20 _Iter_diff_t<_InIt> count(const _InIt _First, const _InIt _Last, const _Ty& _Val) { // count elements that match _Val _Adl_verify_range(_First, _Last); - auto _UFirst = _Get_unwrapped(_First); - const auto _ULast = _Get_unwrapped(_Last); - _Iter_diff_t<_InIt> _Count = 0; +#if _HAS_IF_CONSTEXPR + if constexpr (_Is_vb_iterator<_InIt> && is_same_v<_Ty, bool>) { + return _Count_vbool(_First, _Last, _Val); + } else { +#endif // _HAS_IF_CONSTEXPR + auto _UFirst = _Get_unwrapped(_First); + const auto _ULast = _Get_unwrapped(_Last); + _Iter_diff_t<_InIt> _Count = 0; - for (; _UFirst != _ULast; ++_UFirst) { - if (*_UFirst == _Val) { - ++_Count; + for (; _UFirst != _ULast; ++_UFirst) { + if (*_UFirst == _Val) { + ++_Count; + } } - } - return _Count; + return _Count; +#if _HAS_IF_CONSTEXPR + } +#endif // _HAS_IF_CONSTEXPR } #if _HAS_CXX17 diff --git a/tests/std/tests/GH_000625_vector_bool_optimization/env.lst b/tests/std/tests/GH_000625_vector_bool_optimization/env.lst new file mode 100644 index 00000000000..19f025bd0e6 --- /dev/null +++ b/tests/std/tests/GH_000625_vector_bool_optimization/env.lst @@ -0,0 +1,4 @@ +# Copyright (c) Microsoft Corporation. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +RUNALL_INCLUDE ..\usual_matrix.lst diff --git a/tests/std/tests/GH_000625_vector_bool_optimization/test.cpp b/tests/std/tests/GH_000625_vector_bool_optimization/test.cpp new file mode 100644 index 00000000000..a5504d23573 --- /dev/null +++ b/tests/std/tests/GH_000625_vector_bool_optimization/test.cpp @@ -0,0 +1,1151 @@ +// Copyright (c) Microsoft Corporation. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +#include +#include +#include +#include + +using namespace std; + +constexpr int blockSize = 32; +static_assert(blockSize == _VBITS, "Invalid block size"); + +// clang-format off +vector source = { true, false, true, false, true, true, true, false, + true, false, true, false, true, true, true, false, + true, false, true, false, true, true, true, false, + true, false, true, false, true, true, true, false, + true, false, true, false, true, true, true, false, + true, false, true, false, true, true, true, false, + true, false, true, false, true, true, true, false, + true, false, true, false, true, true, true, false, + true, false, true, false, true, true, true, false, + true, false, true, false, true, true, true, false }; +// clang-format on + +void test_copy_no_offset(const int length) { + vector result; + // clang-format off + switch (length) { + case 3: + result = { true, false, true }; + break; + case 8: + result = { true, false, true, false, true, true, true, false}; + break; + case 22: + result = { true, false, true, false, true, true, true, false, + true, false, true, false, true, true, true, false, + true, false, true, false, true, true }; + break; + case 31: + result = { true, false, true, false, true, true, true, false, + true, false, true, false, true, true, true, false, + true, false, true, false, true, true, true, false, + true, false, true, false, true, true, true }; + break; + case 32: + result = { true, false, true, false, true, true, true, false, + true, false, true, false, true, true, true, false, + true, false, true, false, true, true, true, false, + true, false, true, false, true, true, true, false}; + break; + case 67: + result = { true, false, true, false, true, true, true, false, + true, false, true, false, true, true, true, false, + true, false, true, false, true, true, true, false, + true, false, true, false, true, true, true, false, + true, false, true, false, true, true, true, false, + true, false, true, false, true, true, true, false, + true, false, true, false, true, true, true, false, + true, false, true, false, true, true, true, false, + true, false, true }; + break; + default: + assert(false); + } + // clang-format on + vector dest(length, false); + const auto res_copy = copy(source.begin(), next(source.begin(), length), dest.begin()); + assert(dest == result); + assert(res_copy == dest.end()); + + vector dest_n(length, false); + const auto res_copy_n = copy_n(source.begin(), length, dest_n.begin()); + assert(dest_n == result); + assert(res_copy_n == dest_n.end()); + + vector dest_backward(length, false); + const auto res_copy_backward = copy_backward(source.begin(), next(source.begin(), length), dest_backward.end()); + assert(dest_backward == result); + assert(res_copy_backward == dest_backward.begin()); + + vector dest_move(length, false); + const auto res_move = move(source.begin(), next(source.begin(), length), dest_move.begin()); + assert(dest_move == result); + assert(res_move == dest_move.end()); + + vector dest_move_backward(length, false); + const auto res_move_backward = + move_backward(source.begin(), next(source.begin(), length), dest_move_backward.end()); + assert(dest_move_backward == result); + assert(res_move_backward == dest_move_backward.begin()); +} + +void test_copy_offset_source(const int length) { + vector result; + // clang-format off + switch (length) { + case 3: + result = { false, true, false}; + break; + case 8: + result = { false, true, false, true, true, true, false, + true }; + break; + case 22: + result = { false, true, false, true, true, true, false, + true, false, true, false, true, true, true, false, + true, false, true, false, true, true, true }; + break; + case 31: + result = { false, true, false, true, true, true, false, + true, false, true, false, true, true, true, false, + true, false, true, false, true, true, true, false, + true, false, true, false, true, true, true, false }; + break; + case 32: + result = { false, true, false, true, true, true, false, + true, false, true, false, true, true, true, false, + true, false, true, false, true, true, true, false, + true, false, true, false, true, true, true, false, + true}; + break; + case 67: + result = { false, true, false, true, true, true, false, + true, false, true, false, true, true, true, false, + true, false, true, false, true, true, true, false, + true, false, true, false, true, true, true, false, + true, false, true, false, true, true, true, false, + true, false, true, false, true, true, true, false, + true, false, true, false, true, true, true, false, + true, false, true, false, true, true, true, false, + true, false, true, false }; + break; + default: + assert(false); + } + // clang-format on + vector dest(length, false); + const auto res_copy = copy(next(source.begin()), next(source.begin(), length + 1), dest.begin()); + assert(dest == result); + assert(res_copy == dest.end()); + + vector dest_n(length, false); + const auto res_copy_n = copy_n(next(source.begin()), length, dest_n.begin()); + assert(dest_n == result); + assert(res_copy_n == dest_n.end()); + + vector dest_backward(length, false); + const auto res_copy_backward = + copy_backward(next(source.begin()), next(source.begin(), length + 1), dest_backward.end()); + assert(dest_backward == result); + assert(res_copy_backward == dest_backward.begin()); + + vector dest_move(length, false); + const auto res_move = move(next(source.begin()), next(source.begin(), length + 1), dest_move.begin()); + assert(dest_move == result); + assert(res_move == dest_move.end()); + + vector dest_move_backward(length, false); + const auto res_move_backward = + move_backward(next(source.begin()), next(source.begin(), length + 1), dest_move_backward.end()); + assert(dest_move_backward == result); + assert(res_move_backward == dest_move_backward.begin()); +} + +void test_copy_offset_dest(const int length) { + vector result; + // clang-format off + switch (length) { + case 3: + result = { false, + true, false, true }; + break; + case 8: + result = { false, + true, false, true, false, true, true, true, false }; + break; + case 22: + result = { false, + true, false, true, false, true, true, true, false, + true, false, true, false, true, true, true, false, + true, false, true, false, true, true }; + break; + case 31: + result = { false, + true, false, true, false, true, true, true, false, + true, false, true, false, true, true, true, false, + true, false, true, false, true, true, true, false, + true, false, true, false, true, true, true }; + break; + case 32: + result = { false, + true, false, true, false, true, true, true, false, + true, false, true, false, true, true, true, false, + true, false, true, false, true, true, true, false, + true, false, true, false, true, true, true, false }; + break; + case 67: + result = { false, + true, false, true, false, true, true, true, false, + true, false, true, false, true, true, true, false, + true, false, true, false, true, true, true, false, + true, false, true, false, true, true, true, false, + true, false, true, false, true, true, true, false, + true, false, true, false, true, true, true, false, + true, false, true, false, true, true, true, false, + true, false, true, false, true, true, true, false, + true, false, true }; + break; + default: + assert(false); + } + // clang-format on + vector dest(length + 1, false); + const auto res_copy = copy(source.begin(), next(source.begin(), length), next(dest.begin())); + assert(dest == result); + assert(res_copy == dest.end()); + + vector dest_n(length + 1, false); + const auto res_copy_n = copy_n(source.begin(), length, next(dest_n.begin())); + assert(dest_n == result); + assert(res_copy_n == dest_n.end()); + + vector dest_backward(length + 1, false); + const auto res_copy_backward = copy_backward(source.begin(), next(source.begin(), length), dest_backward.end()); + assert(dest_backward == result); + assert(res_copy_backward == next(dest_backward.begin())); + + vector dest_move(length + 1, false); + const auto res_move = move(source.begin(), next(source.begin(), length), next(dest_move.begin())); + assert(dest_move == result); + assert(res_move == dest_move.end()); + + vector dest_move_backward(length + 1, false); + const auto res_move_backward = + move_backward(source.begin(), next(source.begin(), length), dest_move_backward.end()); + assert(dest_move_backward == result); + assert(res_move_backward == next(dest_move_backward.begin())); +} + +void test_copy_offset_match(const int length) { + vector result; + // clang-format off + switch (length) { + case 3: + result = { false, + false, true }; + break; + case 8: + result = { false, + false, true, false, true, true, true, false }; + break; + case 22: + result = { false, + false, true, false, true, true, true, false, + true, false, true, false, true, true, true, false, + true, false, true, false, true, true }; + break; + case 31: + result = { false, + false, true, false, true, true, true, false, + true, false, true, false, true, true, true, false, + true, false, true, false, true, true, true, false, + true, false, true, false, true, true, true }; + break; + case 32: + result = { false, + false, true, false, true, true, true, false, + true, false, true, false, true, true, true, false, + true, false, true, false, true, true, true, false, + true, false, true, false, true, true, true, false }; + break; + case 67: + result = { false, + false, true, false, true, true, true, false, + true, false, true, false, true, true, true, false, + true, false, true, false, true, true, true, false, + true, false, true, false, true, true, true, false, + true, false, true, false, true, true, true, false, + true, false, true, false, true, true, true, false, + true, false, true, false, true, true, true, false, + true, false, true, false, true, true, true, false, + true, false, true }; + break; + default: + assert(false); + } + // clang-format on + vector dest(length, false); + const auto res_copy = copy(next(source.begin()), next(source.begin(), length), next(dest.begin())); + assert(dest == result); + assert(res_copy == dest.end()); + + vector dest_n(length, false); + const auto res_copy_n = copy_n(next(source.begin()), length - 1, next(dest_n.begin())); + assert(dest_n == result); + assert(res_copy_n == dest_n.end()); + + vector dest_backward(length, false); + const auto res_copy_backward = + copy_backward(next(source.begin()), next(source.begin(), length), dest_backward.end()); + assert(dest_backward == result); + assert(res_copy_backward == next(dest_backward.begin())); + + vector dest_move(length, false); + const auto res_move = move(next(source.begin()), next(source.begin(), length), next(dest_move.begin())); + assert(dest_move == result); + assert(res_move == dest_move.end()); + + vector dest_move_backward(length, false); + const auto res_move_backward = + move_backward(next(source.begin()), next(source.begin(), length), dest_move_backward.end()); + assert(dest_move_backward == result); + assert(res_move_backward == next(dest_move_backward.begin())); +} + +void test_copy_offset_mismatch_leftshift(const int length) { + vector result; + // clang-format off + switch (length) { + case 3: + result = { false, false, + false, true }; + break; + case 8: + result = { false, false, + false, true, false, true, true, true, false }; + break; + case 22: + result = { false, false, + false, true, false, true, true, true, false, + true, false, true, false, true, true, true, false, + true, false, true, false, true, true }; + break; + case 31: + result = { false, false, + false, true, false, true, true, true, false, + true, false, true, false, true, true, true, false, + true, false, true, false, true, true, true, false, + true, false, true, false, true, true, true }; + break; + case 32: + result = { false, false, + false, true, false, true, true, true, false, + true, false, true, false, true, true, true, false, + true, false, true, false, true, true, true, false, + true, false, true, false, true, true, true, false }; + break; + case 67: + result = { false, false, + false, true, false, true, true, true, false, + true, false, true, false, true, true, true, false, + true, false, true, false, true, true, true, false, + true, false, true, false, true, true, true, false, + true, false, true, false, true, true, true, false, + true, false, true, false, true, true, true, false, + true, false, true, false, true, true, true, false, + true, false, true, false, true, true, true, false, + true, false, true }; + break; + default: + assert(false); + } + // clang-format on + vector dest(length + 1, false); + const auto res_copy = copy(next(source.begin()), next(source.begin(), length), next(dest.begin(), 2)); + assert(dest == result); + assert(res_copy == dest.end()); + + vector dest_n(length + 1, false); + const auto res_copy_n = copy_n(next(source.begin()), length - 1, next(dest_n.begin(), 2)); + assert(dest_n == result); + assert(res_copy_n == dest_n.end()); + + vector dest_backward(length + 1, false); + const auto res_copy_backward = + copy_backward(next(source.begin()), next(source.begin(), length), dest_backward.end()); + assert(dest_backward == result); + assert(res_copy_backward == next(dest_backward.begin(), 2)); + + vector dest_move(length + 1, false); + const auto res_move = move(next(source.begin()), next(source.begin(), length), next(dest_move.begin(), 2)); + assert(dest_move == result); + assert(res_move == dest_move.end()); + + vector dest_move_backward(length + 1, false); + const auto res_move_backward = + move_backward(next(source.begin()), next(source.begin(), length), dest_move_backward.end()); + assert(dest_move_backward == result); + assert(res_move_backward == next(dest_move_backward.begin(), 2)); +} + +void test_copy_offset_mismatch_rightshift(const int length) { + vector result; + // clang-format off + switch (length) { + case 3: + result = { false, + true, false }; + break; + case 8: + result = { false, + true, false, true, true, true, false, + true }; + break; + case 22: + result = { false, + true, false, true, true, true, false, + true, false, true, false, true, true, true, false, + true, false, true, false, true, true, true }; + break; + case 31: + result = { false, + true, false, true, true, true, false, + true, false, true, false, true, true, true, false, + true, false, true, false, true, true, true, false, + true, false, true, false, true, true, true, false }; + break; + case 32: + result = { false, + true, false, true, true, true, false, + true, false, true, false, true, true, true, false, + true, false, true, false, true, true, true, false, + true, false, true, false, true, true, true, false, + true }; + break; + case 67: + result = { false, + true, false, true, true, true, false, + true, false, true, false, true, true, true, false, + true, false, true, false, true, true, true, false, + true, false, true, false, true, true, true, false, + true, false, true, false, true, true, true, false, + true, false, true, false, true, true, true, false, + true, false, true, false, true, true, true, false, + true, false, true, false, true, true, true, false, + true, false, true, false }; + break; + default: + assert(false); + } + // clang-format on + vector dest(length, false); + const auto res_copy = copy(next(source.begin(), 2), next(source.begin(), length + 1), next(dest.begin())); + assert(dest == result); + assert(res_copy == dest.end()); + + vector dest_n(length, false); + const auto res_copy_n = copy_n(next(source.begin(), 2), length - 1, next(dest_n.begin())); + assert(dest_n == result); + assert(res_copy_n == dest_n.end()); + + vector dest_backward(length, false); + const auto res_copy_backward = + copy_backward(next(source.begin(), 2), next(source.begin(), length + 1), dest_backward.end()); + assert(dest_backward == result); + assert(res_copy_backward == next(dest_backward.begin())); + + vector dest_move(length, false); + const auto res_move = move(next(source.begin(), 2), next(source.begin(), length + 1), next(dest_move.begin())); + assert(dest_move == result); + assert(res_move == dest_move.end()); + + vector dest_move_backward(length, false); + const auto res_move_backward = + move_backward(next(source.begin(), 2), next(source.begin(), length + 1), dest_move_backward.end()); + assert(dest_move_backward == result); + assert(res_move_backward == next(dest_move_backward.begin())); +} + +void test_copy_offset_aligned(const int length) { + vector result; + // clang-format off + switch (length) { + case 3: + result = { false, + false, true }; + break; + case 8: + result = { false, + false, true, false, true, true, true, false }; + break; + case 22: + result = { false, + false, true, false, true, true, true, false, + true, false, true, false, true, true, true, false, + true, false, true, false, true, true }; + break; + case 31: + result = { false, + false, true, false, true, true, true, false, + true, false, true, false, true, true, true, false, + true, false, true, false, true, true, true, false, + true, false, true, false, true, true, true }; + break; + case 32: + result = { false, + false, true, false, true, true, true, false, + true, false, true, false, true, true, true, false, + true, false, true, false, true, true, true, false, + true, false, true, false, true, true, true, false }; + break; + case 67: + result = { false, + false, true, false, true, true, true, false, + true, false, true, false, true, true, true, false, + true, false, true, false, true, true, true, false, + true, false, true, false, true, true, true, false, + true, false, true, false, true, true, true, false, + true, false, true, false, true, true, true, false, + true, false, true, false, true, true, true, false, + true, false, true, false, true, true, true, false, + true, false, true }; + break; + default: + assert(false); + } + // clang-format on + vector dest(length, false); + const auto res_copy = copy(next(source.begin(), 9), next(source.begin(), length + 8), next(dest.begin())); + assert(dest == result); + assert(res_copy == dest.end()); + + vector dest_n(length, false); + const auto res_copy_n = copy_n(next(source.begin(), 9), length - 1, next(dest_n.begin())); + assert(dest_n == result); + assert(res_copy_n == dest_n.end()); + + vector dest_backward(length, false); + const auto res_copy_backward = + copy_backward(next(source.begin(), 9), next(source.begin(), length + 8), dest_backward.end()); + assert(dest_backward == result); + assert(res_copy_backward == next(dest_backward.begin())); + + vector dest_move(length, false); + const auto res_move = move(next(source.begin(), 9), next(source.begin(), length + 8), next(dest_move.begin())); + assert(dest_move == result); + assert(res_move == dest_move.end()); + + vector dest_move_backward(length, false); + const auto res_move_backward = + move_backward(next(source.begin(), 9), next(source.begin(), length + 8), dest_move_backward.end()); + assert(dest_move_backward == result); + assert(res_move_backward == next(dest_move_backward.begin())); +} + +bool test_copy() { + test_copy_no_offset(3); + test_copy_no_offset(8); + test_copy_no_offset(22); + test_copy_no_offset(31); + test_copy_no_offset(32); + test_copy_no_offset(67); + + test_copy_offset_source(3); + test_copy_offset_source(8); + test_copy_offset_source(22); + test_copy_offset_source(31); + test_copy_offset_source(32); + test_copy_offset_source(67); + + test_copy_offset_dest(3); + test_copy_offset_dest(8); + test_copy_offset_dest(22); + test_copy_offset_dest(31); + test_copy_offset_dest(32); + test_copy_offset_dest(67); + + test_copy_offset_match(3); + test_copy_offset_match(8); + test_copy_offset_match(22); + test_copy_offset_match(31); + test_copy_offset_match(32); + test_copy_offset_match(67); + + test_copy_offset_mismatch_leftshift(3); + test_copy_offset_mismatch_leftshift(8); + test_copy_offset_mismatch_leftshift(22); + test_copy_offset_mismatch_leftshift(31); + test_copy_offset_mismatch_leftshift(32); + test_copy_offset_mismatch_leftshift(67); + + test_copy_offset_mismatch_rightshift(3); + test_copy_offset_mismatch_rightshift(8); + test_copy_offset_mismatch_rightshift(22); + test_copy_offset_mismatch_rightshift(31); + test_copy_offset_mismatch_rightshift(32); + test_copy_offset_mismatch_rightshift(67); + + test_copy_offset_aligned(3); + test_copy_offset_aligned(8); + test_copy_offset_aligned(22); + test_copy_offset_aligned(31); + test_copy_offset_aligned(32); + test_copy_offset_aligned(67); + + return true; +} + +bool test_fill() { + // No offset, less than char size + { + vector result(3, true); + vector dest(3, false); + fill(dest.begin(), dest.end(), true); + assert(dest == result); + + fill(dest.begin(), dest.end(), false); + assert(dest == vector(3, false)); + + const auto res_fill_n = fill_n(dest.begin(), 3, true); + assert(dest == result); + assert(res_fill_n == dest.end()); + + fill_n(dest.begin(), 3, false); + assert(dest == vector(3, false)); + } + + // With Offset, less than blockSize + { + vector result(3, true); + result[0] = false; + + vector dest(3, false); + fill(next(dest.begin()), dest.end(), true); + assert(dest == result); + + fill(next(dest.begin()), dest.end(), false); + assert(dest == vector(3, false)); + + const auto res_fill_n = fill_n(next(dest.begin()), 2, true); + assert(dest == result); + assert(res_fill_n == dest.end()); + + fill_n(next(dest.begin()), 2, false); + assert(dest == vector(3, false)); + } + + // No offset, less than blockSize + { + vector result(8, true); + vector dest(8, false); + fill(dest.begin(), dest.end(), true); + assert(dest == result); + + fill(dest.begin(), dest.end(), false); + assert(dest == vector(8, false)); + + const auto res_fill_n = fill_n(dest.begin(), 8, true); + assert(dest == result); + assert(res_fill_n == dest.end()); + + fill_n(dest.begin(), 8, false); + assert(dest == vector(8, false)); + } + + // With Offset, less than blockSize + { + vector result(8, true); + result[0] = false; + + vector dest(8, false); + fill(next(dest.begin()), dest.end(), true); + assert(dest == result); + + fill(next(dest.begin()), dest.end(), false); + assert(dest == vector(8, false)); + + const auto res_fill_n = fill_n(next(dest.begin()), 7, true); + assert(dest == result); + assert(res_fill_n == dest.end()); + + fill_n(next(dest.begin()), 7, false); + assert(dest == vector(8, false)); + } + + // With offset, end at boundary + { + vector result(blockSize, true); + result[0] = false; + + vector dest(blockSize, false); + fill(next(dest.begin()), dest.end(), true); + assert(dest == result); + + fill(next(dest.begin()), dest.end(), false); + assert(dest == vector(blockSize, false)); + + const auto res_fill_n = fill_n(next(dest.begin()), blockSize - 1, true); + assert(dest == result); + assert(res_fill_n == dest.end()); + + fill_n(next(dest.begin()), blockSize - 1, false); + assert(dest == vector(blockSize, false)); + } + + // No offset, exactly blockSize + { + vector result(blockSize, true); + vector dest(blockSize, false); + fill(dest.begin(), dest.end(), true); + assert(dest == result); + + fill(dest.begin(), dest.end(), false); + assert(dest == vector(blockSize, false)); + + const auto res_fill_n = fill_n(dest.begin(), blockSize, true); + assert(dest == result); + assert(res_fill_n == dest.end()); + + fill_n(dest.begin(), blockSize, false); + assert(dest == vector(blockSize, false)); + } + + // With offset, ends at boundary + { + vector result(blockSize, true); + result[0] = false; + + vector dest(blockSize, false); + fill(next(dest.begin()), dest.end(), true); + assert(dest == result); + + fill(next(dest.begin()), dest.end(), false); + assert(dest == vector(blockSize, false)); + + const auto res_fill_n = fill_n(next(dest.begin()), blockSize - 1, true); + assert(dest == result); + assert(res_fill_n == dest.end()); + + fill_n(next(dest.begin()), blockSize - 1, false); + assert(dest == vector(blockSize, false)); + } + + // With offset, exactly blockSize + { + vector result(blockSize + 1, true); + result[0] = false; + + vector dest(blockSize + 1, false); + fill(next(dest.begin()), dest.end(), true); + assert(dest == result); + + fill(next(dest.begin()), dest.end(), false); + assert(dest == vector(blockSize + 1, false)); + + const auto res_fill_n = fill_n(next(dest.begin()), blockSize, true); + assert(dest == result); + assert(res_fill_n == dest.end()); + + fill_n(next(dest.begin()), blockSize, false); + assert(dest == vector(blockSize + 1, false)); + } + + // No offset, multiple blockSize + { + vector result(2 * blockSize, true); + vector dest(2 * blockSize, false); + fill(dest.begin(), dest.end(), true); + assert(dest == result); + + fill(dest.begin(), dest.end(), false); + assert(dest == vector(2 * blockSize, false)); + + const auto res_fill_n = fill_n(dest.begin(), 2 * blockSize, true); + assert(dest == result); + assert(res_fill_n == dest.end()); + + fill_n(dest.begin(), 2 * blockSize, false); + assert(dest == vector(2 * blockSize, false)); + } + + // With offset, multiple blockSize + { + vector result(2 * blockSize + 5, true); + result[0] = false; + + vector dest(2 * blockSize + 5, false); + fill(next(dest.begin()), dest.end(), true); + assert(dest == result); + + fill(next(dest.begin()), dest.end(), false); + assert(dest == vector(2 * blockSize + 5, false)); + + const auto res_fill_n = fill_n(next(dest.begin()), 2 * blockSize + 4, true); + assert(dest == result); + assert(res_fill_n == dest.end()); + + fill_n(next(dest.begin()), 2 * blockSize + 4, false); + assert(dest == vector(2 * blockSize + 5, false)); + } + + return true; +} + +bool test_equal() { + // No offset, less than blockSize + { + const vector input(source.begin(), next(source.begin(), 8)); + const vector valid = input; + assert(equal(input.begin(), input.end(), valid.begin())); + assert(equal(input.begin(), input.end(), valid.begin(), valid.end())); + + vector missmatchFirst = valid; + missmatchFirst[3] = !missmatchFirst[3]; + assert(!equal(input.begin(), input.end(), missmatchFirst.begin())); + assert(!equal(input.begin(), input.end(), missmatchFirst.begin(), missmatchFirst.end())); + } + + // With Offset input, less than blockSize + { + const vector input(source.begin(), next(source.begin(), 8)); + const vector valid(next(input.begin()), input.end()); + assert(equal(next(input.begin()), input.end(), valid.begin())); + assert(equal(next(input.begin()), input.end(), valid.begin(), valid.end())); + + vector missmatchFirst = valid; + missmatchFirst[3] = !missmatchFirst[3]; + assert(!equal(next(input.begin()), input.end(), missmatchFirst.begin())); + assert(!equal(next(input.begin()), input.end(), missmatchFirst.begin(), missmatchFirst.end())); + } + + // With Offset valid, less than blockSize + { + const vector valid(source.begin(), next(source.begin(), 8)); + const vector input(next(valid.begin()), valid.end()); + assert(equal(input.begin(), input.end(), next(valid.begin()))); + assert(equal(input.begin(), input.end(), next(valid.begin()), valid.end())); + + vector missmatchFirst = valid; + missmatchFirst[3] = !missmatchFirst[3]; + assert(!equal(input.begin(), input.end(), next(missmatchFirst.begin()))); + assert(!equal(input.begin(), input.end(), next(missmatchFirst.begin()), missmatchFirst.end())); + } + + // With matching Offset, less than blockSize + { + const vector input(source.begin(), next(source.begin(), 8)); + const vector valid = input; + assert(equal(next(input.begin()), input.end(), next(valid.begin()))); + assert(equal(next(input.begin()), input.end(), next(valid.begin()), valid.end())); + + vector missmatchFirst = valid; + missmatchFirst[3] = !missmatchFirst[3]; + assert(!equal(next(input.begin()), input.end(), next(missmatchFirst.begin()))); + assert(!equal(next(input.begin()), input.end(), next(missmatchFirst.begin()), missmatchFirst.end())); + } + + // No offset, exactly blockSize + { + const vector input(source.begin(), next(source.begin(), blockSize)); + const vector valid = input; + assert(equal(input.begin(), input.end(), valid.begin())); + assert(equal(input.begin(), input.end(), valid.begin(), valid.end())); + + vector missmatchFirst = valid; + missmatchFirst[3] = !missmatchFirst[3]; + assert(!equal(input.begin(), input.end(), missmatchFirst.begin())); + assert(!equal(input.begin(), input.end(), missmatchFirst.begin(), missmatchFirst.end())); + } + + // With Offset input, exactly blockSize + { + const vector input(source.begin(), next(source.begin(), blockSize + 1)); + const vector valid(next(input.begin()), input.end()); + assert(equal(next(input.begin()), input.end(), valid.begin())); + assert(equal(next(input.begin()), input.end(), valid.begin(), valid.end())); + + vector missmatchFirst = valid; + missmatchFirst[3] = !missmatchFirst[3]; + assert(!equal(next(input.begin()), input.end(), missmatchFirst.begin())); + assert(!equal(next(input.begin()), input.end(), missmatchFirst.begin(), missmatchFirst.end())); + + vector missmatchLast = valid; + missmatchLast.back() = !missmatchLast.back(); + assert(!equal(next(input.begin()), input.end(), missmatchLast.begin())); + assert(!equal(next(input.begin()), input.end(), missmatchLast.begin(), missmatchLast.end())); + } + + // With Offset valid, exactly blockSize + { + const vector valid(source.begin(), next(source.begin(), blockSize + 1)); + const vector input(next(valid.begin()), valid.end()); + assert(equal(input.begin(), input.end(), next(valid.begin()))); + assert(equal(input.begin(), input.end(), next(valid.begin()), valid.end())); + + vector missmatchFirst = valid; + missmatchFirst[3] = !missmatchFirst[3]; + assert(!equal(input.begin(), input.end(), next(missmatchFirst.begin()))); + assert(!equal(input.begin(), input.end(), next(missmatchFirst.begin()), missmatchFirst.end())); + + vector missmatchLast = valid; + missmatchLast.back() = !missmatchLast.back(); + assert(!equal(input.begin(), input.end(), next(missmatchLast.begin()))); + assert(!equal(input.begin(), input.end(), next(missmatchLast.begin()), missmatchLast.end())); + } + + // With matching Offset, exactly blockSize + { + const vector input(source.begin(), next(source.begin(), blockSize + 1)); + const vector valid = input; + assert(equal(next(input.begin()), input.end(), next(valid.begin()))); + assert(equal(next(input.begin()), input.end(), next(valid.begin()), valid.end())); + + vector missmatchFirst = valid; + missmatchFirst[3] = !missmatchFirst[3]; + assert(!equal(next(input.begin()), input.end(), next(missmatchFirst.begin()))); + assert(!equal(next(input.begin()), input.end(), next(missmatchFirst.begin()), missmatchFirst.end())); + + vector missmatchLast = valid; + missmatchLast.back() = !missmatchLast.back(); + assert(!equal(next(input.begin()), input.end(), next(missmatchLast.begin()))); + assert(!equal(next(input.begin()), input.end(), next(missmatchLast.begin()), missmatchLast.end())); + } + + // No offset, multiple blockSize + { + const vector input(source.begin(), next(source.begin(), 2 * blockSize + 2)); + const vector valid = input; + assert(equal(input.begin(), input.end(), valid.begin())); + assert(equal(input.begin(), input.end(), valid.begin(), valid.end())); + + vector missmatchFirst = valid; + missmatchFirst[3] = !missmatchFirst[3]; + assert(!equal(input.begin(), input.end(), missmatchFirst.begin())); + assert(!equal(input.begin(), input.end(), missmatchFirst.begin(), missmatchFirst.end())); + + vector missmatchMemCmp = valid; + missmatchMemCmp[40] = !missmatchMemCmp[40]; + assert(!equal(next(input.begin()), input.end(), next(missmatchMemCmp.begin()))); + assert(!equal(next(input.begin()), input.end(), next(missmatchMemCmp.begin()), missmatchMemCmp.end())); + + vector missmatchLast = valid; + missmatchLast.back() = !missmatchLast.back(); + assert(!equal(next(input.begin()), input.end(), next(missmatchLast.begin()))); + assert(!equal(next(input.begin()), input.end(), next(missmatchLast.begin()), missmatchLast.end())); + } + + // With Offset input, exactly blockSize + { + const vector input(source.begin(), next(source.begin(), 2 * blockSize + 5)); + const vector valid(next(input.begin()), input.end()); + assert(equal(next(input.begin()), input.end(), valid.begin())); + assert(equal(next(input.begin()), input.end(), valid.begin(), valid.end())); + + vector missmatchFirst = valid; + missmatchFirst[3] = !missmatchFirst[3]; + assert(!equal(next(input.begin()), input.end(), missmatchFirst.begin())); + assert(!equal(next(input.begin()), input.end(), missmatchFirst.begin(), missmatchFirst.end())); + + vector missmatchMemCmp = valid; + missmatchMemCmp[40] = !missmatchMemCmp[40]; + assert(!equal(next(input.begin()), input.end(), missmatchMemCmp.begin())); + assert(!equal(next(input.begin()), input.end(), missmatchMemCmp.begin(), missmatchMemCmp.end())); + + vector missmatchLast = valid; + missmatchLast.back() = !missmatchLast.back(); + assert(!equal(next(input.begin()), input.end(), missmatchLast.begin())); + assert(!equal(next(input.begin()), input.end(), missmatchLast.begin(), missmatchLast.end())); + } + + // With Offset valid, exactly blockSize + { + const vector valid(source.begin(), next(source.begin(), 2 * blockSize + 5)); + const vector input(next(valid.begin()), valid.end()); + assert(equal(input.begin(), input.end(), next(valid.begin()))); + assert(equal(input.begin(), input.end(), next(valid.begin()), valid.end())); + + vector missmatchFirst = valid; + missmatchFirst[3] = !missmatchFirst[3]; + assert(!equal(input.begin(), input.end(), next(missmatchFirst.begin()))); + assert(!equal(input.begin(), input.end(), next(missmatchFirst.begin()), missmatchFirst.end())); + + vector missmatchMemCmp = valid; + missmatchMemCmp[40] = !missmatchMemCmp[40]; + assert(!equal(input.begin(), input.end(), next(missmatchMemCmp.begin()))); + assert(!equal(input.begin(), input.end(), next(missmatchMemCmp.begin()), missmatchMemCmp.end())); + + vector missmatchLast = valid; + missmatchLast.back() = !missmatchLast.back(); + assert(!equal(input.begin(), input.end(), next(missmatchLast.begin()))); + assert(!equal(input.begin(), input.end(), next(missmatchLast.begin()), missmatchLast.end())); + } + + // With matching Offset, exactly blockSize + { + const vector input(source.begin(), next(source.begin(), 2 * blockSize + 5)); + const vector valid = input; + assert(equal(next(input.begin()), input.end(), next(valid.begin()))); + assert(equal(next(input.begin()), input.end(), next(valid.begin()), valid.end())); + + vector missmatchFirst = valid; + missmatchFirst[3] = !missmatchFirst[3]; + assert(!equal(next(input.begin()), input.end(), next(missmatchFirst.begin()))); + assert(!equal(next(input.begin()), input.end(), next(missmatchFirst.begin()), missmatchFirst.end())); + + + vector missmatchMemCmp = valid; + missmatchMemCmp[40] = !missmatchMemCmp[40]; + assert(!equal(next(input.begin()), input.end(), next(missmatchMemCmp.begin()))); + assert(!equal(next(input.begin()), input.end(), next(missmatchMemCmp.begin()), missmatchMemCmp.end())); + + vector missmatchLast = valid; + missmatchLast.back() = !missmatchLast.back(); + assert(!equal(next(input.begin()), input.end(), next(missmatchLast.begin()))); + assert(!equal(next(input.begin()), input.end(), next(missmatchLast.begin()), missmatchLast.end())); + } + + return true; +} + +bool test_find() { + // Less than blockSize + { + vector input_true(blockSize, false); + input_true[5].flip(); + const auto result_true = find(input_true.begin(), next(input_true.begin(), 8), true); + assert(result_true == next(input_true.begin(), 5)); + + vector input_false(blockSize, true); + input_false[6].flip(); + const auto result_false = find(input_false.begin(), next(input_false.begin(), 8), false); + assert(result_false == next(input_false.begin(), 6)); + } + + // Exactly blockSize + { + vector input_true(blockSize, false); + input_true[24].flip(); + const auto result_true = find(input_true.begin(), next(input_true.begin(), blockSize), true); + assert(result_true == next(input_true.begin(), 24)); + + vector input_false(blockSize, true); + input_false[27].flip(); + const auto result_false = find(input_false.begin(), next(input_false.begin(), blockSize), false); + assert(result_false == next(input_false.begin(), 27)); + } + + // More than blockSize + { + vector input_true(blockSize + 5, false); + input_true[24].flip(); + const auto result_true = find(input_true.begin(), next(input_true.begin(), blockSize), true); + assert(result_true == next(input_true.begin(), 24)); + + vector input_false(blockSize + 5, true); + input_false[27].flip(); + const auto result_false = find(input_false.begin(), next(input_false.begin(), blockSize), false); + assert(result_false == next(input_false.begin(), 27)); + } + + // More than blockSize ends with offset + { + vector input_true(blockSize + 8, false); + input_true[33].flip(); + const auto result_true = find(input_true.begin(), next(input_true.begin(), blockSize + 8), true); + assert(result_true == next(input_true.begin(), 33)); + + vector input_false(blockSize + 8, true); + input_false[35].flip(); + const auto result_false = find(input_false.begin(), next(input_false.begin(), blockSize + 8), false); + assert(result_false == next(input_false.begin(), 35)); + } + + // Multiple blockSize + { + vector input_true(3 * blockSize, false); + input_true[33].flip(); + const auto result_true = find(input_true.begin(), next(input_true.begin(), 3 * blockSize), true); + assert(result_true == next(input_true.begin(), 33)); + + vector input_false(3 * blockSize, true); + input_false[35].flip(); + const auto result_false = find(input_false.begin(), next(input_false.begin(), 3 * blockSize), false); + assert(result_false == next(input_false.begin(), 35)); + } + + // Multiple blockSize, ends with offset + { + vector input_true(3 * blockSize + 5, false); + input_true[3 * blockSize + 3].flip(); + const auto result_true = find(input_true.begin(), next(input_true.begin(), 3 * blockSize + 5), true); + assert(result_true == next(input_true.begin(), 3 * blockSize + 3)); + + vector input_false(3 * blockSize + 5, true); + input_false[3 * blockSize + 2].flip(); + const auto result_false = find(input_false.begin(), next(input_false.begin(), 3 * blockSize + 5), false); + assert(result_false == next(input_false.begin(), 3 * blockSize + 2)); + } + + return true; +} + +bool test_count() { + // Less than blockSize + { + const auto result_true = count(source.begin(), next(source.begin(), 8), true); + assert(result_true == 5); + + const auto result_false = count(source.begin(), next(source.begin(), 8), false); + assert(result_false == 3); + } + + // Exactly blockSize + { + const auto result_true = count(source.begin(), next(source.begin(), blockSize), true); + assert(result_true == 20); + + const auto result_false = count(source.begin(), next(source.begin(), blockSize), false); + assert(result_false == 12); + } + + // More than blockSize + { + const auto result_true = count(source.begin(), next(source.begin(), blockSize + 3), true); + assert(result_true == 22); + + const auto result_false = count(source.begin(), next(source.begin(), blockSize + 3), false); + assert(result_false == 13); + } + + // Multiple blockSize ends at boundary + { + const auto result_true = count(source.begin(), next(source.begin(), 2 * blockSize), true); + assert(result_true == 40); + + const auto result_false = count(source.begin(), next(source.begin(), 2 * blockSize), false); + assert(result_false == 24); + } + + // Multiple blockSize ends with offset + { + const auto result_true = count(source.begin(), next(source.begin(), 2 * blockSize + 5), true); + assert(result_true == 43); + + const auto result_false = count(source.begin(), next(source.begin(), 2 * blockSize + 5), false); + assert(result_false == 26); + } + + return true; +} + +int main() { + test_copy(); + test_fill(); + test_equal(); + test_find(); + test_count(); +}