diff --git a/benchmarks/src/find_and_count.cpp b/benchmarks/src/find_and_count.cpp index b0addf6a97c..448e67ac7ab 100644 --- a/benchmarks/src/find_and_count.cpp +++ b/benchmarks/src/find_and_count.cpp @@ -19,6 +19,8 @@ enum class Op { Count, StringFind, StringRFind, + StringFindNotFirstOne, + StringFindNotLastOne, }; using namespace std; @@ -28,13 +30,13 @@ void bm(benchmark::State& state) { const auto size = static_cast(state.range(0)); const auto pos = static_cast(state.range(1)); - using Container = conditional_t, Alloc>, vector>>; + using Container = + conditional_t= Op::StringFind, basic_string, Alloc>, vector>>; Container a(size, T{'0'}); if (pos < size) { - if constexpr (Operation == Op::StringRFind) { + if constexpr (Operation == Op::StringRFind || Operation == Op::StringFindNotLastOne) { a[size - pos - 1] = T{'1'}; } else { a[pos] = T{'1'}; @@ -56,6 +58,10 @@ void bm(benchmark::State& state) { benchmark::DoNotOptimize(a.find(T{'1'})); } else if constexpr (Operation == Op::StringRFind) { benchmark::DoNotOptimize(a.rfind(T{'1'})); + } else if constexpr (Operation == Op::StringFindNotFirstOne) { + benchmark::DoNotOptimize(a.find_first_not_of(T{'0'})); + } else if constexpr (Operation == Op::StringFindNotLastOne) { + benchmark::DoNotOptimize(a.find_last_not_of(T{'0'})); } } } @@ -66,27 +72,29 @@ void common_args(auto bm) { bm->Args({63, 62})->Args({31, 30})->Args({15, 14})->Args({7, 6}); } - BENCHMARK(bm)->Apply(common_args); BENCHMARK(bm)->Apply(common_args); BENCHMARK(bm)->Apply(common_args); BENCHMARK(bm)->Apply(common_args); BENCHMARK(bm)->Apply(common_args); -BENCHMARK(bm)->Apply(common_args); BENCHMARK(bm)->Apply(common_args); -BENCHMARK(bm)->Apply(common_args); BENCHMARK(bm)->Apply(common_args); -BENCHMARK(bm)->Apply(common_args); +BENCHMARK(bm)->Apply(common_args); +BENCHMARK(bm)->Apply(common_args); BENCHMARK(bm)->Apply(common_args); BENCHMARK(bm)->Apply(common_args); BENCHMARK(bm)->Apply(common_args); BENCHMARK(bm)->Apply(common_args); +BENCHMARK(bm)->Apply(common_args); +BENCHMARK(bm)->Apply(common_args); BENCHMARK(bm)->Apply(common_args); BENCHMARK(bm)->Apply(common_args); BENCHMARK(bm)->Apply(common_args); BENCHMARK(bm)->Apply(common_args); +BENCHMARK(bm)->Apply(common_args); +BENCHMARK(bm)->Apply(common_args); BENCHMARK(bm)->Apply(common_args); BENCHMARK(bm)->Apply(common_args); diff --git a/stl/inc/__msvc_string_view.hpp b/stl/inc/__msvc_string_view.hpp index c078de097fc..eba67768432 100644 --- a/stl/inc/__msvc_string_view.hpp +++ b/stl/inc/__msvc_string_view.hpp @@ -43,6 +43,20 @@ __declspec(noalias) size_t __stdcall __std_find_last_of_trivial_pos_1( __declspec(noalias) size_t __stdcall __std_find_last_of_trivial_pos_2( const void* _Haystack, size_t _Haystack_length, const void* _Needle, size_t _Needle_length) noexcept; +const void* __stdcall __std_find_not_ch_1(const void* _First, const void* _Last, uint8_t _Val) noexcept; +const void* __stdcall __std_find_not_ch_2(const void* _First, const void* _Last, uint16_t _Val) noexcept; +const void* __stdcall __std_find_not_ch_4(const void* _First, const void* _Last, uint32_t _Val) noexcept; +const void* __stdcall __std_find_not_ch_8(const void* _First, const void* _Last, uint64_t _Val) noexcept; + +__declspec(noalias) size_t __stdcall __std_find_last_not_ch_pos_1( + const void* _First, const void* _Last, uint8_t _Val) noexcept; +__declspec(noalias) size_t __stdcall __std_find_last_not_ch_pos_2( + const void* _First, const void* _Last, uint16_t _Val) noexcept; +__declspec(noalias) size_t __stdcall __std_find_last_not_ch_pos_4( + const void* _First, const void* _Last, uint32_t _Val) noexcept; +__declspec(noalias) size_t __stdcall __std_find_last_not_ch_pos_8( + const void* _First, const void* _Last, uint64_t _Val) noexcept; + } // extern "C" _STD_BEGIN @@ -77,6 +91,35 @@ size_t _Find_last_of_pos_vectorized(const _Ty1* const _Haystack, const size_t _H } } +template +const _Ty* _Find_not_ch_vectorized(const _Ty* const _First, const _Ty* const _Last, const _Ty _Ch) noexcept { + if constexpr (sizeof(_Ty) == 1) { + return static_cast(::__std_find_not_ch_1(_First, _Last, static_cast(_Ch))); + } else if constexpr (sizeof(_Ty) == 2) { + return static_cast(::__std_find_not_ch_2(_First, _Last, static_cast(_Ch))); + } else if constexpr (sizeof(_Ty) == 4) { + return static_cast(::__std_find_not_ch_4(_First, _Last, static_cast(_Ch))); + } else if constexpr (sizeof(_Ty) == 8) { + return static_cast(::__std_find_not_ch_8(_First, _Last, static_cast(_Ch))); + } else { + _STL_INTERNAL_STATIC_ASSERT(false); // unexpected size + } +} + +template +size_t _Find_last_not_ch_pos_vectorized(const _Ty* const _First, const _Ty* const _Last, const _Ty _Ch) noexcept { + if constexpr (sizeof(_Ty) == 1) { + return ::__std_find_last_not_ch_pos_1(_First, _Last, static_cast(_Ch)); + } else if constexpr (sizeof(_Ty) == 2) { + return ::__std_find_last_not_ch_pos_2(_First, _Last, static_cast(_Ch)); + } else if constexpr (sizeof(_Ty) == 4) { + return ::__std_find_last_not_ch_pos_4(_First, _Last, static_cast(_Ch)); + } else if constexpr (sizeof(_Ty) == 8) { + return ::__std_find_last_not_ch_pos_8(_First, _Last, static_cast(_Ch)); + } else { + _STL_INTERNAL_STATIC_ASSERT(false); // unexpected size + } +} _STD_END #endif // _USE_STD_VECTOR_ALGORITHMS @@ -987,14 +1030,30 @@ template constexpr size_t _Traits_find_not_ch(_In_reads_(_Hay_size) const _Traits_ptr_t<_Traits> _Haystack, const size_t _Hay_size, const size_t _Start_at, const _Traits_ch_t<_Traits> _Ch) noexcept { // search [_Haystack, _Haystack + _Hay_size) for any value other than _Ch, at/after _Start_at - if (_Start_at < _Hay_size) { // room for match, look for it - const auto _End = _Haystack + _Hay_size; - for (auto _Match_try = _Haystack + _Start_at; _Match_try < _End; ++_Match_try) { - if (!_Traits::eq(*_Match_try, _Ch)) { - return static_cast(_Match_try - _Haystack); // found a match + if (_Start_at >= _Hay_size) { // no room for match + return static_cast(-1); // no match + } + + const auto _End = _Haystack + _Hay_size; + +#if _USE_STD_VECTOR_ALGORITHMS + if constexpr (_Is_implementation_handled_char_traits<_Traits>) { + if (!_STD _Is_constant_evaluated()) { + const auto _Result = _STD _Find_not_ch_vectorized(_Haystack + _Start_at, _End, _Ch); + if (_Result != _End) { + return static_cast(_Result - _Haystack); + } else { + return static_cast(-1); // no match } } } +#endif // _USE_STD_VECTOR_ALGORITHMS + + for (auto _Match_try = _Haystack + _Start_at; _Match_try < _End; ++_Match_try) { + if (!_Traits::eq(*_Match_try, _Ch)) { + return static_cast(_Match_try - _Haystack); // found a match + } + } return static_cast(-1); // no match } @@ -1047,7 +1106,17 @@ constexpr size_t _Traits_rfind_not_ch(_In_reads_(_Hay_size) const _Traits_ptr_t< return static_cast(-1); } - for (auto _Match_try = _Haystack + (_STD min)(_Start_at, _Hay_size - 1);; --_Match_try) { + const size_t _Actual_start_at = (_STD min)(_Start_at, _Hay_size - 1); + +#if _USE_STD_VECTOR_ALGORITHMS + if constexpr (_Is_implementation_handled_char_traits<_Traits>) { + if (!_STD _Is_constant_evaluated()) { + return _STD _Find_last_not_ch_pos_vectorized(_Haystack, _Haystack + _Actual_start_at + 1, _Ch); + } + } +#endif // _USE_STD_VECTOR_ALGORITHMS + + for (auto _Match_try = _Haystack + _Actual_start_at;; --_Match_try) { if (!_Traits::eq(*_Match_try, _Ch)) { return static_cast(_Match_try - _Haystack); // found a match } diff --git a/stl/src/vector_algorithms.cpp b/stl/src/vector_algorithms.cpp index 41c2daac2f6..15442d480d1 100644 --- a/stl/src/vector_algorithms.cpp +++ b/stl/src/vector_algorithms.cpp @@ -2556,11 +2556,13 @@ namespace { return _Ptr; } + enum class _Find_one_predicate { _Equal, _Not_equal }; + // The below functions have exactly the same signature as the extern "C" functions, up to calling convention. // This makes sure the template specialization can be fused with the extern "C" function. // In optimized builds it avoids an extra call, as these functions are too large to inline. - template + template const void* __stdcall __std_find_trivial_impl(const void* _First, const void* _Last, _Ty _Val) noexcept { #ifndef _M_ARM64EC const size_t _Size_bytes = _Byte_length(_First, _Last); @@ -2574,7 +2576,11 @@ namespace { do { const __m256i _Data = _mm256_loadu_si256(static_cast(_First)); - const int _Bingo = _mm256_movemask_epi8(_Traits::_Cmp_avx(_Data, _Comparand)); + int _Bingo = _mm256_movemask_epi8(_Traits::_Cmp_avx(_Data, _Comparand)); + + if constexpr (_Pred == _Find_one_predicate::_Not_equal) { + _Bingo ^= 0xFFFF'FFFF; + } if (_Bingo != 0) { const unsigned long _Offset = _tzcnt_u32(_Bingo); @@ -2588,8 +2594,11 @@ namespace { if (const size_t _Avx_tail_size = _Size_bytes & 0x1C; _Avx_tail_size != 0) { const __m256i _Tail_mask = _Avx2_tail_mask_32(_Avx_tail_size >> 2); const __m256i _Data = _mm256_maskload_epi32(static_cast(_First), _Tail_mask); - const int _Bingo = - _mm256_movemask_epi8(_mm256_and_si256(_Traits::_Cmp_avx(_Data, _Comparand), _Tail_mask)); + int _Bingo = _mm256_movemask_epi8(_mm256_and_si256(_Traits::_Cmp_avx(_Data, _Comparand), _Tail_mask)); + + if constexpr (_Pred == _Find_one_predicate::_Not_equal) { + _Bingo ^= (1 << _Avx_tail_size) - 1; + } if (_Bingo != 0) { const unsigned long _Offset = _tzcnt_u32(_Bingo); @@ -2610,7 +2619,11 @@ namespace { do { const __m128i _Data = _mm_loadu_si128(static_cast(_First)); - const int _Bingo = _mm_movemask_epi8(_Traits::_Cmp_sse(_Data, _Comparand)); + int _Bingo = _mm_movemask_epi8(_Traits::_Cmp_sse(_Data, _Comparand)); + + if constexpr (_Pred == _Find_one_predicate::_Not_equal) { + _Bingo ^= 0xFFFF; + } if (_Bingo != 0) { unsigned long _Offset; @@ -2625,13 +2638,19 @@ namespace { } #endif // !_M_ARM64EC auto _Ptr = static_cast(_First); - while (_Ptr != _Last && *_Ptr != _Val) { - ++_Ptr; + if constexpr (_Pred == _Find_one_predicate::_Not_equal) { + while (_Ptr != _Last && *_Ptr == _Val) { + ++_Ptr; + } + } else { + while (_Ptr != _Last && *_Ptr != _Val) { + ++_Ptr; + } } return _Ptr; } - template + template const void* __stdcall __std_find_last_trivial_impl(const void* _First, const void* _Last, _Ty _Val) noexcept { const void* const _Real_last = _Last; #ifndef _M_ARM64EC @@ -2647,7 +2666,11 @@ namespace { do { _Rewind_bytes(_Last, 32); const __m256i _Data = _mm256_loadu_si256(static_cast(_Last)); - const int _Bingo = _mm256_movemask_epi8(_Traits::_Cmp_avx(_Data, _Comparand)); + int _Bingo = _mm256_movemask_epi8(_Traits::_Cmp_avx(_Data, _Comparand)); + + if constexpr (_Pred == _Find_one_predicate::_Not_equal) { + _Bingo ^= 0xFFFF'FFFF; + } if (_Bingo != 0) { const unsigned long _Offset = _lzcnt_u32(_Bingo); @@ -2660,8 +2683,11 @@ namespace { _Rewind_bytes(_Last, _Avx_tail_size); const __m256i _Tail_mask = _Avx2_tail_mask_32(_Avx_tail_size >> 2); const __m256i _Data = _mm256_maskload_epi32(static_cast(_Last), _Tail_mask); - const int _Bingo = - _mm256_movemask_epi8(_mm256_and_si256(_Traits::_Cmp_avx(_Data, _Comparand), _Tail_mask)); + int _Bingo = _mm256_movemask_epi8(_mm256_and_si256(_Traits::_Cmp_avx(_Data, _Comparand), _Tail_mask)); + + if constexpr (_Pred == _Find_one_predicate::_Not_equal) { + _Bingo ^= (1 << _Avx_tail_size) - 1; + } if (_Bingo != 0) { const unsigned long _Offset = _lzcnt_u32(_Bingo); @@ -2681,7 +2707,11 @@ namespace { do { _Rewind_bytes(_Last, 16); const __m128i _Data = _mm_loadu_si128(static_cast(_Last)); - const int _Bingo = _mm_movemask_epi8(_Traits::_Cmp_sse(_Data, _Comparand)); + int _Bingo = _mm_movemask_epi8(_Traits::_Cmp_sse(_Data, _Comparand)); + + if constexpr (_Pred == _Find_one_predicate::_Not_equal) { + _Bingo ^= 0xFFFF; + } if (_Bingo != 0) { unsigned long _Offset; @@ -2699,12 +2729,28 @@ namespace { return _Real_last; } --_Ptr; - if (*_Ptr == _Val) { - return _Ptr; + if constexpr (_Pred == _Find_one_predicate::_Not_equal) { + if (*_Ptr != _Val) { + return _Ptr; + } + } else { + if (*_Ptr == _Val) { + return _Ptr; + } } } } + template + size_t __stdcall __std_find_last_pos(const void* const _First, const void* const _Last, const _Ty _Val) noexcept { + const void* const _Result = __std_find_last_trivial_impl<_Traits, _Pred>(_First, _Last, _Val); + if (_Result == _Last) { + return static_cast(-1); + } else { + return _Byte_length(_First, _Result) / sizeof(_Ty); + } + } + struct _Count_traits_8 : _Find_traits_8 { #ifndef _M_ARM64EC static __m256i _Sub_avx(const __m256i _Lhs, const __m256i _Rhs) noexcept { @@ -4089,7 +4135,8 @@ namespace { } if (_Count2 == 1) { - return __std_find_trivial_impl<_Traits>(_First1, _Last1, *static_cast(_First2)); + return __std_find_trivial_impl<_Traits, _Find_one_predicate::_Equal>( + _First1, _Last1, *static_cast(_First2)); } const size_t _Size_bytes_1 = _Byte_length(_First1, _Last1); @@ -4238,7 +4285,8 @@ namespace { } if (_Count2 == 1) { - return __std_find_last_trivial_impl<_Traits>(_First1, _Last1, *static_cast(_First2)); + return __std_find_last_trivial_impl<_Traits, _Find_one_predicate::_Equal>( + _First1, _Last1, *static_cast(_First2)); } const size_t _Size_bytes_1 = _Byte_length(_First1, _Last1); @@ -4507,42 +4555,82 @@ const void* __stdcall __std_find_trivial_unsized_8(const void* const _First, con const void* __stdcall __std_find_trivial_1( const void* const _First, const void* const _Last, const uint8_t _Val) noexcept { - return __std_find_trivial_impl<_Find_traits_1>(_First, _Last, _Val); + return __std_find_trivial_impl<_Find_traits_1, _Find_one_predicate::_Equal>(_First, _Last, _Val); } const void* __stdcall __std_find_trivial_2( const void* const _First, const void* const _Last, const uint16_t _Val) noexcept { - return __std_find_trivial_impl<_Find_traits_2>(_First, _Last, _Val); + return __std_find_trivial_impl<_Find_traits_2, _Find_one_predicate::_Equal>(_First, _Last, _Val); } const void* __stdcall __std_find_trivial_4( const void* const _First, const void* const _Last, const uint32_t _Val) noexcept { - return __std_find_trivial_impl<_Find_traits_4>(_First, _Last, _Val); + return __std_find_trivial_impl<_Find_traits_4, _Find_one_predicate::_Equal>(_First, _Last, _Val); } const void* __stdcall __std_find_trivial_8( const void* const _First, const void* const _Last, const uint64_t _Val) noexcept { - return __std_find_trivial_impl<_Find_traits_8>(_First, _Last, _Val); + return __std_find_trivial_impl<_Find_traits_8, _Find_one_predicate::_Equal>(_First, _Last, _Val); } const void* __stdcall __std_find_last_trivial_1( const void* const _First, const void* const _Last, const uint8_t _Val) noexcept { - return __std_find_last_trivial_impl<_Find_traits_1>(_First, _Last, _Val); + return __std_find_last_trivial_impl<_Find_traits_1, _Find_one_predicate::_Equal>(_First, _Last, _Val); } const void* __stdcall __std_find_last_trivial_2( const void* const _First, const void* const _Last, const uint16_t _Val) noexcept { - return __std_find_last_trivial_impl<_Find_traits_2>(_First, _Last, _Val); + return __std_find_last_trivial_impl<_Find_traits_2, _Find_one_predicate::_Equal>(_First, _Last, _Val); } const void* __stdcall __std_find_last_trivial_4( const void* const _First, const void* const _Last, const uint32_t _Val) noexcept { - return __std_find_last_trivial_impl<_Find_traits_4>(_First, _Last, _Val); + return __std_find_last_trivial_impl<_Find_traits_4, _Find_one_predicate::_Equal>(_First, _Last, _Val); } const void* __stdcall __std_find_last_trivial_8( const void* const _First, const void* const _Last, const uint64_t _Val) noexcept { - return __std_find_last_trivial_impl<_Find_traits_8>(_First, _Last, _Val); + return __std_find_last_trivial_impl<_Find_traits_8, _Find_one_predicate::_Equal>(_First, _Last, _Val); +} + +const void* __stdcall __std_find_not_ch_1( + const void* const _First, const void* const _Last, const uint8_t _Val) noexcept { + return __std_find_trivial_impl<_Find_traits_1, _Find_one_predicate::_Not_equal>(_First, _Last, _Val); +} + +const void* __stdcall __std_find_not_ch_2( + const void* const _First, const void* const _Last, const uint16_t _Val) noexcept { + return __std_find_trivial_impl<_Find_traits_2, _Find_one_predicate::_Not_equal>(_First, _Last, _Val); +} + +const void* __stdcall __std_find_not_ch_4( + const void* const _First, const void* const _Last, const uint32_t _Val) noexcept { + return __std_find_trivial_impl<_Find_traits_4, _Find_one_predicate::_Not_equal>(_First, _Last, _Val); +} + +const void* __stdcall __std_find_not_ch_8( + const void* const _First, const void* const _Last, const uint64_t _Val) noexcept { + return __std_find_trivial_impl<_Find_traits_8, _Find_one_predicate::_Not_equal>(_First, _Last, _Val); +} + +__declspec(noalias) size_t __stdcall __std_find_last_not_ch_pos_1( + const void* const _First, const void* const _Last, const uint8_t _Val) noexcept { + return __std_find_last_pos<_Find_traits_1, _Find_one_predicate::_Not_equal>(_First, _Last, _Val); +} + +__declspec(noalias) size_t __stdcall __std_find_last_not_ch_pos_2( + const void* const _First, const void* const _Last, const uint16_t _Val) noexcept { + return __std_find_last_pos<_Find_traits_2, _Find_one_predicate::_Not_equal>(_First, _Last, _Val); +} + +__declspec(noalias) size_t __stdcall __std_find_last_not_ch_pos_4( + const void* const _First, const void* const _Last, const uint32_t _Val) noexcept { + return __std_find_last_pos<_Find_traits_4, _Find_one_predicate::_Not_equal>(_First, _Last, _Val); +} + +__declspec(noalias) size_t __stdcall __std_find_last_not_ch_pos_8( + const void* const _First, const void* const _Last, const uint64_t _Val) noexcept { + return __std_find_last_pos<_Find_traits_8, _Find_one_predicate::_Not_equal>(_First, _Last, _Val); } __declspec(noalias) size_t __stdcall __std_count_trivial_1( diff --git a/tests/std/tests/VSO_0000000_vector_algorithms/test.cpp b/tests/std/tests/VSO_0000000_vector_algorithms/test.cpp index 27ba7340b3f..4c41919a660 100644 --- a/tests/std/tests/VSO_0000000_vector_algorithms/test.cpp +++ b/tests/std/tests/VSO_0000000_vector_algorithms/test.cpp @@ -169,25 +169,25 @@ void test_count(mt19937_64& gen) { } } -template -auto last_known_good_find(FwdIt first, FwdIt last, T v) { +template > +auto last_known_good_find(FwdIt first, FwdIt last, T v, Pred pred = {}) { for (; first != last; ++first) { - if (*first == v) { + if (pred(*first, v)) { break; } } return first; } -template -auto last_known_good_find_last(FwdIt first, FwdIt last, T v) { +template > +auto last_known_good_find_last(FwdIt first, FwdIt last, T v, Pred pred = {}) { FwdIt last_save = last; for (;;) { if (last == first) { return last_save; } --last; - if (*last == v) { + if (pred(*last, v)) { return last; } } @@ -1160,12 +1160,48 @@ void test_case_string_rfind_str(const basic_string& input_haystack, const bas assert(expected == actual); } +template +void test_case_string_find_not_ch(const basic_string& input_haystack, const T value) { + ptrdiff_t expected; + + const auto expected_iter = + last_known_good_find(input_haystack.begin(), input_haystack.end(), value, not_equal_to<>{}); + + if (expected_iter != input_haystack.end()) { + expected = expected_iter - input_haystack.begin(); + } else { + expected = -1; + } + + const auto actual = static_cast(input_haystack.find_first_not_of(value)); + assert(expected == actual); +} + +template +void test_case_string_rfind_not_ch(const basic_string& input_haystack, const T value) { + ptrdiff_t expected; + + const auto expected_iter = + last_known_good_find_last(input_haystack.begin(), input_haystack.end(), value, not_equal_to<>{}); + + if (expected_iter != input_haystack.end()) { + expected = expected_iter - input_haystack.begin(); + } else { + expected = -1; + } + + const auto actual = static_cast(input_haystack.find_last_not_of(value)); + assert(expected == actual); +} + template void test_basic_string_dis(mt19937_64& gen, D& dis) { basic_string input_haystack; + basic_string input_haystack_not; basic_string input_needle; basic_string temp; input_haystack.reserve(haystackDataCount); + input_haystack_not.reserve(haystackDataCount); input_needle.reserve(needleDataCount); temp.reserve(needleDataCount); @@ -1201,6 +1237,22 @@ void test_basic_string_dis(mt19937_64& gen, D& dis) { } } + const auto input_not_ch = static_cast(dis(gen)); + input_haystack_not.assign(input_haystack.size(), input_not_ch); + + test_case_string_find_not_ch(input_haystack_not, input_not_ch); + test_case_string_rfind_not_ch(input_haystack_not, input_not_ch); + if (!input_haystack_not.empty()) { + uniform_int_distribution not_pos_dis(0, input_haystack_not.size() - 1); + + for (size_t attempts = 0; attempts < needleDataCount; ++attempts) { + const size_t pos = not_pos_dis(gen); + input_haystack_not[pos] = input_haystack[pos]; + test_case_string_find_not_ch(input_haystack_not, input_not_ch); + test_case_string_rfind_not_ch(input_haystack_not, input_not_ch); + } + } + if (input_haystack.size() == haystackDataCount) { break; }