Shuffle class order (and just that)

This commit is contained in:
Robert Schulze 2023-02-24 10:13:35 +00:00
parent ef529de7db
commit 421afeeae0
No known key found for this signature in database
GPG Key ID: 26703B55FB13728A

View File

@ -57,6 +57,450 @@ private:
/// Performs case-sensitive or case-insensitive search of ASCII or UTF-8 strings
template <bool CaseSensitive, bool ASCII> class StringSearcher;
/// Case-sensitive ASCII and UTF8 searcher
template <bool ASCII>
class StringSearcher<true, ASCII> : public StringSearcherBase
{
private:
/// string to be searched for
const uint8_t * const needle;
const uint8_t * const needle_end;
/// first character in `needle`
uint8_t first_needle_character = 0;
#ifdef __SSE4_1__
/// second character of "needle" (if its length is > 1)
uint8_t second_needle_character = 0;
/// first/second needle character broadcasted into a 16 bytes vector
__m128i first_needle_character_vec;
__m128i second_needle_character_vec;
/// vector of first 16 characters of `needle`
__m128i cache = _mm_setzero_si128();
uint16_t cachemask = 0;
#endif
public:
template <typename CharT>
requires (sizeof(CharT) == 1)
StringSearcher(const CharT * needle_, size_t needle_size)
: needle(reinterpret_cast<const uint8_t *>(needle_))
, needle_end(needle + needle_size)
{
if (needle_size == 0)
return;
first_needle_character = *needle;
#ifdef __SSE4_1__
first_needle_character_vec = _mm_set1_epi8(first_needle_character);
if (needle_size > 1)
{
second_needle_character = *(needle + 1);
second_needle_character_vec = _mm_set1_epi8(second_needle_character);
}
const auto * needle_pos = needle;
for (uint8_t i = 0; i < N; ++i)
{
cache = _mm_srli_si128(cache, 1);
if (needle_pos != needle_end)
{
cache = _mm_insert_epi8(cache, *needle_pos, N - 1);
cachemask |= 1 << i;
++needle_pos;
}
}
#endif
}
template <typename CharT>
requires (sizeof(CharT) == 1)
ALWAYS_INLINE bool compare(const CharT * /*haystack*/, const CharT * /*haystack_end*/, const CharT * pos) const
{
#ifdef __SSE4_1__
if (isPageSafe(pos))
{
const __m128i haystack_characters = _mm_loadu_si128(reinterpret_cast<const __m128i *>(pos));
const __m128i comparison_result = _mm_cmpeq_epi8(haystack_characters, cache);
const uint16_t comparison_result_mask = _mm_movemask_epi8(comparison_result);
if (0xffff == cachemask)
{
if (comparison_result_mask == cachemask)
{
pos += N;
const auto * needle_pos = needle + N;
while (needle_pos < needle_end && *pos == *needle_pos)
++pos, ++needle_pos;
if (needle_pos == needle_end)
return true;
}
}
else if ((comparison_result_mask & cachemask) == cachemask)
return true;
return false;
}
#endif
if (*pos == first_needle_character)
{
++pos;
const auto * needle_pos = needle + 1;
while (needle_pos < needle_end && *pos == *needle_pos)
++pos, ++needle_pos;
if (needle_pos == needle_end)
return true;
}
return false;
}
template <typename CharT>
requires (sizeof(CharT) == 1)
const CharT * search(const CharT * haystack, const CharT * const haystack_end) const
{
const auto needle_size = needle_end - needle;
if (needle == needle_end)
return haystack;
#ifdef __SSE4_1__
/// Fast path for single-character needles. Compare 16 characters of the haystack against the needle character at once.
if (needle_size == 1)
{
while (haystack < haystack_end)
{
if (haystack + N <= haystack_end && isPageSafe(haystack))
{
const __m128i haystack_characters = _mm_loadu_si128(reinterpret_cast<const __m128i *>(haystack));
const __m128i comparison_result = _mm_cmpeq_epi8(haystack_characters, first_needle_character_vec);
const uint16_t comparison_result_mask = _mm_movemask_epi8(comparison_result);
if (comparison_result_mask == 0)
{
haystack += N;
continue;
}
const int offset = std::countr_zero(comparison_result_mask);
haystack += offset;
return haystack;
}
if (haystack == haystack_end)
return haystack_end;
if (*haystack == first_needle_character)
return haystack;
++haystack;
}
return haystack_end;
}
#endif
while (haystack < haystack_end && haystack_end - haystack >= needle_size)
{
#ifdef __SSE4_1__
/// Compare the [0:15] bytes from haystack and broadcasted 16 bytes vector from first character of needle.
/// Compare the [1:16] bytes from haystack and broadcasted 16 bytes vector from second character of needle.
/// Bit AND the results of above two comparisons and get the mask.
if ((haystack + 1 + N) <= haystack_end && isPageSafe(haystack + 1))
{
const __m128i haystack_characters_from_1st = _mm_loadu_si128(reinterpret_cast<const __m128i *>(haystack));
const __m128i haystack_characters_from_2nd = _mm_loadu_si128(reinterpret_cast<const __m128i *>(haystack + 1));
const __m128i comparison_result_1st = _mm_cmpeq_epi8(haystack_characters_from_1st, first_needle_character_vec);
const __m128i comparison_result_2nd = _mm_cmpeq_epi8(haystack_characters_from_2nd, second_needle_character_vec);
const __m128i comparison_result_combined = _mm_and_si128(comparison_result_1st, comparison_result_2nd);
const uint16_t comparison_result_mask = _mm_movemask_epi8(comparison_result_combined);
/// If the mask = 0, then first two characters [0:1] from needle are not in the [0:17] bytes of haystack.
if (comparison_result_mask == 0)
{
haystack += N;
continue;
}
const int offset = std::countr_zero(comparison_result_mask);
haystack += offset;
if (haystack + N <= haystack_end && isPageSafe(haystack))
{
/// Already find the haystack position where the [pos:pos + 1] two characters exactly match the first two characters of needle.
/// Compare the 16 bytes from needle (cache) and the first 16 bytes from haystack at once if the haystack size >= 16 bytes.
const __m128i haystack_characters = _mm_loadu_si128(reinterpret_cast<const __m128i *>(haystack));
const __m128i comparison_result_cache = _mm_cmpeq_epi8(haystack_characters, cache);
const uint16_t mask_offset = _mm_movemask_epi8(comparison_result_cache);
if (0xffff == cachemask)
{
if (mask_offset == cachemask)
{
const auto * haystack_pos = haystack + N;
const auto * needle_pos = needle + N;
while (haystack_pos < haystack_end && needle_pos < needle_end &&
*haystack_pos == *needle_pos)
++haystack_pos, ++needle_pos;
if (needle_pos == needle_end)
return haystack;
}
}
else if ((mask_offset & cachemask) == cachemask)
return haystack;
++haystack;
continue;
}
}
#endif
if (haystack == haystack_end)
return haystack_end;
if (*haystack == first_needle_character)
{
const auto * haystack_pos = haystack + 1;
const auto * needle_pos = needle + 1;
while (haystack_pos < haystack_end && needle_pos < needle_end &&
*haystack_pos == *needle_pos)
++haystack_pos, ++needle_pos;
if (needle_pos == needle_end)
return haystack;
}
++haystack;
}
return haystack_end;
}
template <typename CharT>
requires (sizeof(CharT) == 1)
const CharT * search(const CharT * haystack, size_t haystack_size) const
{
return search(haystack, haystack + haystack_size);
}
};
/// Case-insensitive ASCII searcher
template <>
class StringSearcher<false, true> : public StringSearcherBase
{
private:
/// string to be searched for
const uint8_t * const needle;
const uint8_t * const needle_end;
/// lower and uppercase variants of the first character in `needle`
uint8_t l = 0;
uint8_t u = 0;
#ifdef __SSE4_1__
/// vectors filled with `l` and `u`, for determining leftmost position of the first symbol
__m128i patl, patu;
/// lower and uppercase vectors of first 16 characters of `needle`
__m128i cachel = _mm_setzero_si128(), cacheu = _mm_setzero_si128();
int cachemask = 0;
#endif
public:
template <typename CharT>
requires (sizeof(CharT) == 1)
StringSearcher(const CharT * needle_, size_t needle_size)
: needle(reinterpret_cast<const uint8_t *>(needle_))
, needle_end(needle + needle_size)
{
if (0 == needle_size)
return;
l = static_cast<uint8_t>(std::tolower(*needle));
u = static_cast<uint8_t>(std::toupper(*needle));
#ifdef __SSE4_1__
patl = _mm_set1_epi8(l);
patu = _mm_set1_epi8(u);
const auto * needle_pos = needle;
for (size_t i = 0; i < N; ++i)
{
cachel = _mm_srli_si128(cachel, 1);
cacheu = _mm_srli_si128(cacheu, 1);
if (needle_pos != needle_end)
{
cachel = _mm_insert_epi8(cachel, std::tolower(*needle_pos), N - 1);
cacheu = _mm_insert_epi8(cacheu, std::toupper(*needle_pos), N - 1);
cachemask |= 1 << i;
++needle_pos;
}
}
#endif
}
template <typename CharT>
requires (sizeof(CharT) == 1)
ALWAYS_INLINE bool compare(const CharT * /*haystack*/, const CharT * /*haystack_end*/, const CharT * pos) const
{
#ifdef __SSE4_1__
if (isPageSafe(pos))
{
const auto v_haystack = _mm_loadu_si128(reinterpret_cast<const __m128i *>(pos));
const auto v_against_l = _mm_cmpeq_epi8(v_haystack, cachel);
const auto v_against_u = _mm_cmpeq_epi8(v_haystack, cacheu);
const auto v_against_l_or_u = _mm_or_si128(v_against_l, v_against_u);
const auto mask = _mm_movemask_epi8(v_against_l_or_u);
if (0xffff == cachemask)
{
if (mask == cachemask)
{
pos += N;
const auto * needle_pos = needle + N;
while (needle_pos < needle_end && std::tolower(*pos) == std::tolower(*needle_pos))
{
++pos;
++needle_pos;
}
if (needle_pos == needle_end)
return true;
}
}
else if ((mask & cachemask) == cachemask)
return true;
return false;
}
#endif
if (*pos == l || *pos == u)
{
++pos;
const auto * needle_pos = needle + 1;
while (needle_pos < needle_end && std::tolower(*pos) == std::tolower(*needle_pos))
{
++pos;
++needle_pos;
}
if (needle_pos == needle_end)
return true;
}
return false;
}
template <typename CharT>
requires (sizeof(CharT) == 1)
const CharT * search(const CharT * haystack, const CharT * const haystack_end) const
{
if (needle == needle_end)
return haystack;
while (haystack < haystack_end)
{
#ifdef __SSE4_1__
if (haystack + N <= haystack_end && isPageSafe(haystack))
{
const auto v_haystack = _mm_loadu_si128(reinterpret_cast<const __m128i *>(haystack));
const auto v_against_l = _mm_cmpeq_epi8(v_haystack, patl);
const auto v_against_u = _mm_cmpeq_epi8(v_haystack, patu);
const auto v_against_l_or_u = _mm_or_si128(v_against_l, v_against_u);
const auto mask = _mm_movemask_epi8(v_against_l_or_u);
if (mask == 0)
{
haystack += N;
continue;
}
const auto offset = __builtin_ctz(mask);
haystack += offset;
if (haystack + N <= haystack_end && isPageSafe(haystack))
{
const auto v_haystack_offset = _mm_loadu_si128(reinterpret_cast<const __m128i *>(haystack));
const auto v_against_l_offset = _mm_cmpeq_epi8(v_haystack_offset, cachel);
const auto v_against_u_offset = _mm_cmpeq_epi8(v_haystack_offset, cacheu);
const auto v_against_l_or_u_offset = _mm_or_si128(v_against_l_offset, v_against_u_offset);
const auto mask_offset = _mm_movemask_epi8(v_against_l_or_u_offset);
if (0xffff == cachemask)
{
if (mask_offset == cachemask)
{
const auto * haystack_pos = haystack + N;
const auto * needle_pos = needle + N;
while (haystack_pos < haystack_end && needle_pos < needle_end &&
std::tolower(*haystack_pos) == std::tolower(*needle_pos))
{
++haystack_pos;
++needle_pos;
}
if (needle_pos == needle_end)
return haystack;
}
}
else if ((mask_offset & cachemask) == cachemask)
return haystack;
++haystack;
continue;
}
}
#endif
if (haystack == haystack_end)
return haystack_end;
if (*haystack == l || *haystack == u)
{
const auto * haystack_pos = haystack + 1;
const auto * needle_pos = needle + 1;
while (haystack_pos < haystack_end && needle_pos < needle_end &&
std::tolower(*haystack_pos) == std::tolower(*needle_pos))
{
++haystack_pos;
++needle_pos;
}
if (needle_pos == needle_end)
return haystack;
}
++haystack;
}
return haystack_end;
}
template <typename CharT>
requires (sizeof(CharT) == 1)
const CharT * search(const CharT * haystack, size_t haystack_size) const
{
return search(haystack, haystack + haystack_size);
}
};
/// Case-insensitive UTF-8 searcher
template <>
class StringSearcher<false, false> : public StringSearcherBase
@ -356,448 +800,6 @@ public:
};
/// Case-insensitive ASCII searcher
template <>
class StringSearcher<false, true> : public StringSearcherBase
{
private:
/// string to be searched for
const uint8_t * const needle;
const uint8_t * const needle_end;
/// lower and uppercase variants of the first character in `needle`
uint8_t l = 0;
uint8_t u = 0;
#ifdef __SSE4_1__
/// vectors filled with `l` and `u`, for determining leftmost position of the first symbol
__m128i patl, patu;
/// lower and uppercase vectors of first 16 characters of `needle`
__m128i cachel = _mm_setzero_si128(), cacheu = _mm_setzero_si128();
int cachemask = 0;
#endif
public:
template <typename CharT>
requires (sizeof(CharT) == 1)
StringSearcher(const CharT * needle_, size_t needle_size)
: needle(reinterpret_cast<const uint8_t *>(needle_))
, needle_end(needle + needle_size)
{
if (0 == needle_size)
return;
l = static_cast<uint8_t>(std::tolower(*needle));
u = static_cast<uint8_t>(std::toupper(*needle));
#ifdef __SSE4_1__
patl = _mm_set1_epi8(l);
patu = _mm_set1_epi8(u);
const auto * needle_pos = needle;
for (size_t i = 0; i < N; ++i)
{
cachel = _mm_srli_si128(cachel, 1);
cacheu = _mm_srli_si128(cacheu, 1);
if (needle_pos != needle_end)
{
cachel = _mm_insert_epi8(cachel, std::tolower(*needle_pos), N - 1);
cacheu = _mm_insert_epi8(cacheu, std::toupper(*needle_pos), N - 1);
cachemask |= 1 << i;
++needle_pos;
}
}
#endif
}
template <typename CharT>
requires (sizeof(CharT) == 1)
ALWAYS_INLINE bool compare(const CharT * /*haystack*/, const CharT * /*haystack_end*/, const CharT * pos) const
{
#ifdef __SSE4_1__
if (isPageSafe(pos))
{
const auto v_haystack = _mm_loadu_si128(reinterpret_cast<const __m128i *>(pos));
const auto v_against_l = _mm_cmpeq_epi8(v_haystack, cachel);
const auto v_against_u = _mm_cmpeq_epi8(v_haystack, cacheu);
const auto v_against_l_or_u = _mm_or_si128(v_against_l, v_against_u);
const auto mask = _mm_movemask_epi8(v_against_l_or_u);
if (0xffff == cachemask)
{
if (mask == cachemask)
{
pos += N;
const auto * needle_pos = needle + N;
while (needle_pos < needle_end && std::tolower(*pos) == std::tolower(*needle_pos))
{
++pos;
++needle_pos;
}
if (needle_pos == needle_end)
return true;
}
}
else if ((mask & cachemask) == cachemask)
return true;
return false;
}
#endif
if (*pos == l || *pos == u)
{
++pos;
const auto * needle_pos = needle + 1;
while (needle_pos < needle_end && std::tolower(*pos) == std::tolower(*needle_pos))
{
++pos;
++needle_pos;
}
if (needle_pos == needle_end)
return true;
}
return false;
}
template <typename CharT>
requires (sizeof(CharT) == 1)
const CharT * search(const CharT * haystack, const CharT * const haystack_end) const
{
if (needle == needle_end)
return haystack;
while (haystack < haystack_end)
{
#ifdef __SSE4_1__
if (haystack + N <= haystack_end && isPageSafe(haystack))
{
const auto v_haystack = _mm_loadu_si128(reinterpret_cast<const __m128i *>(haystack));
const auto v_against_l = _mm_cmpeq_epi8(v_haystack, patl);
const auto v_against_u = _mm_cmpeq_epi8(v_haystack, patu);
const auto v_against_l_or_u = _mm_or_si128(v_against_l, v_against_u);
const auto mask = _mm_movemask_epi8(v_against_l_or_u);
if (mask == 0)
{
haystack += N;
continue;
}
const auto offset = __builtin_ctz(mask);
haystack += offset;
if (haystack + N <= haystack_end && isPageSafe(haystack))
{
const auto v_haystack_offset = _mm_loadu_si128(reinterpret_cast<const __m128i *>(haystack));
const auto v_against_l_offset = _mm_cmpeq_epi8(v_haystack_offset, cachel);
const auto v_against_u_offset = _mm_cmpeq_epi8(v_haystack_offset, cacheu);
const auto v_against_l_or_u_offset = _mm_or_si128(v_against_l_offset, v_against_u_offset);
const auto mask_offset = _mm_movemask_epi8(v_against_l_or_u_offset);
if (0xffff == cachemask)
{
if (mask_offset == cachemask)
{
const auto * haystack_pos = haystack + N;
const auto * needle_pos = needle + N;
while (haystack_pos < haystack_end && needle_pos < needle_end &&
std::tolower(*haystack_pos) == std::tolower(*needle_pos))
{
++haystack_pos;
++needle_pos;
}
if (needle_pos == needle_end)
return haystack;
}
}
else if ((mask_offset & cachemask) == cachemask)
return haystack;
++haystack;
continue;
}
}
#endif
if (haystack == haystack_end)
return haystack_end;
if (*haystack == l || *haystack == u)
{
const auto * haystack_pos = haystack + 1;
const auto * needle_pos = needle + 1;
while (haystack_pos < haystack_end && needle_pos < needle_end &&
std::tolower(*haystack_pos) == std::tolower(*needle_pos))
{
++haystack_pos;
++needle_pos;
}
if (needle_pos == needle_end)
return haystack;
}
++haystack;
}
return haystack_end;
}
template <typename CharT>
requires (sizeof(CharT) == 1)
const CharT * search(const CharT * haystack, size_t haystack_size) const
{
return search(haystack, haystack + haystack_size);
}
};
/// Case-sensitive ASCII and UTF8 searcher
template <bool ASCII>
class StringSearcher<true, ASCII> : public StringSearcherBase
{
private:
/// string to be searched for
const uint8_t * const needle;
const uint8_t * const needle_end;
/// first character in `needle`
uint8_t first_needle_character = 0;
#ifdef __SSE4_1__
/// second character of "needle" (if its length is > 1)
uint8_t second_needle_character = 0;
/// first/second needle character broadcasted into a 16 bytes vector
__m128i first_needle_character_vec;
__m128i second_needle_character_vec;
/// vector of first 16 characters of `needle`
__m128i cache = _mm_setzero_si128();
uint16_t cachemask = 0;
#endif
public:
template <typename CharT>
requires (sizeof(CharT) == 1)
StringSearcher(const CharT * needle_, size_t needle_size)
: needle(reinterpret_cast<const uint8_t *>(needle_))
, needle_end(needle + needle_size)
{
if (needle_size == 0)
return;
first_needle_character = *needle;
#ifdef __SSE4_1__
first_needle_character_vec = _mm_set1_epi8(first_needle_character);
if (needle_size > 1)
{
second_needle_character = *(needle + 1);
second_needle_character_vec = _mm_set1_epi8(second_needle_character);
}
const auto * needle_pos = needle;
for (uint8_t i = 0; i < N; ++i)
{
cache = _mm_srli_si128(cache, 1);
if (needle_pos != needle_end)
{
cache = _mm_insert_epi8(cache, *needle_pos, N - 1);
cachemask |= 1 << i;
++needle_pos;
}
}
#endif
}
template <typename CharT>
requires (sizeof(CharT) == 1)
ALWAYS_INLINE bool compare(const CharT * /*haystack*/, const CharT * /*haystack_end*/, const CharT * pos) const
{
#ifdef __SSE4_1__
if (isPageSafe(pos))
{
const __m128i haystack_characters = _mm_loadu_si128(reinterpret_cast<const __m128i *>(pos));
const __m128i comparison_result = _mm_cmpeq_epi8(haystack_characters, cache);
const uint16_t comparison_result_mask = _mm_movemask_epi8(comparison_result);
if (0xffff == cachemask)
{
if (comparison_result_mask == cachemask)
{
pos += N;
const auto * needle_pos = needle + N;
while (needle_pos < needle_end && *pos == *needle_pos)
++pos, ++needle_pos;
if (needle_pos == needle_end)
return true;
}
}
else if ((comparison_result_mask & cachemask) == cachemask)
return true;
return false;
}
#endif
if (*pos == first_needle_character)
{
++pos;
const auto * needle_pos = needle + 1;
while (needle_pos < needle_end && *pos == *needle_pos)
++pos, ++needle_pos;
if (needle_pos == needle_end)
return true;
}
return false;
}
template <typename CharT>
requires (sizeof(CharT) == 1)
const CharT * search(const CharT * haystack, const CharT * const haystack_end) const
{
const auto needle_size = needle_end - needle;
if (needle == needle_end)
return haystack;
#ifdef __SSE4_1__
/// Fast path for single-character needles. Compare 16 characters of the haystack against the needle character at once.
if (needle_size == 1)
{
while (haystack < haystack_end)
{
if (haystack + N <= haystack_end && isPageSafe(haystack))
{
const __m128i haystack_characters = _mm_loadu_si128(reinterpret_cast<const __m128i *>(haystack));
const __m128i comparison_result = _mm_cmpeq_epi8(haystack_characters, first_needle_character_vec);
const uint16_t comparison_result_mask = _mm_movemask_epi8(comparison_result);
if (comparison_result_mask == 0)
{
haystack += N;
continue;
}
const int offset = std::countr_zero(comparison_result_mask);
haystack += offset;
return haystack;
}
if (haystack == haystack_end)
return haystack_end;
if (*haystack == first_needle_character)
return haystack;
++haystack;
}
return haystack_end;
}
#endif
while (haystack < haystack_end && haystack_end - haystack >= needle_size)
{
#ifdef __SSE4_1__
/// Compare the [0:15] bytes from haystack and broadcasted 16 bytes vector from first character of needle.
/// Compare the [1:16] bytes from haystack and broadcasted 16 bytes vector from second character of needle.
/// Bit AND the results of above two comparisons and get the mask.
if ((haystack + 1 + N) <= haystack_end && isPageSafe(haystack + 1))
{
const __m128i haystack_characters_from_1st = _mm_loadu_si128(reinterpret_cast<const __m128i *>(haystack));
const __m128i haystack_characters_from_2nd = _mm_loadu_si128(reinterpret_cast<const __m128i *>(haystack + 1));
const __m128i comparison_result_1st = _mm_cmpeq_epi8(haystack_characters_from_1st, first_needle_character_vec);
const __m128i comparison_result_2nd = _mm_cmpeq_epi8(haystack_characters_from_2nd, second_needle_character_vec);
const __m128i comparison_result_combined = _mm_and_si128(comparison_result_1st, comparison_result_2nd);
const uint16_t comparison_result_mask = _mm_movemask_epi8(comparison_result_combined);
/// If the mask = 0, then first two characters [0:1] from needle are not in the [0:17] bytes of haystack.
if (comparison_result_mask == 0)
{
haystack += N;
continue;
}
const int offset = std::countr_zero(comparison_result_mask);
haystack += offset;
if (haystack + N <= haystack_end && isPageSafe(haystack))
{
/// Already find the haystack position where the [pos:pos + 1] two characters exactly match the first two characters of needle.
/// Compare the 16 bytes from needle (cache) and the first 16 bytes from haystack at once if the haystack size >= 16 bytes.
const __m128i haystack_characters = _mm_loadu_si128(reinterpret_cast<const __m128i *>(haystack));
const __m128i comparison_result_cache = _mm_cmpeq_epi8(haystack_characters, cache);
const uint16_t mask_offset = _mm_movemask_epi8(comparison_result_cache);
if (0xffff == cachemask)
{
if (mask_offset == cachemask)
{
const auto * haystack_pos = haystack + N;
const auto * needle_pos = needle + N;
while (haystack_pos < haystack_end && needle_pos < needle_end &&
*haystack_pos == *needle_pos)
++haystack_pos, ++needle_pos;
if (needle_pos == needle_end)
return haystack;
}
}
else if ((mask_offset & cachemask) == cachemask)
return haystack;
++haystack;
continue;
}
}
#endif
if (haystack == haystack_end)
return haystack_end;
if (*haystack == first_needle_character)
{
const auto * haystack_pos = haystack + 1;
const auto * needle_pos = needle + 1;
while (haystack_pos < haystack_end && needle_pos < needle_end &&
*haystack_pos == *needle_pos)
++haystack_pos, ++needle_pos;
if (needle_pos == needle_end)
return haystack;
}
++haystack;
}
return haystack_end;
}
template <typename CharT>
requires (sizeof(CharT) == 1)
const CharT * search(const CharT * haystack, size_t haystack_size) const
{
return search(haystack, haystack + haystack_size);
}
};
// Searches for needle surrounded by token-separators.
// Separators are anything inside ASCII (0-128) and not alphanum.
// Any value outside of basic ASCII (>=128) is considered a non-separator symbol, hence UTF-8 strings