mirror of
https://github.com/ClickHouse/ClickHouse.git
synced 2024-11-28 02:21:59 +00:00
Shuffle class order (and just that)
This commit is contained in:
parent
ef529de7db
commit
421afeeae0
@ -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
|
||||
|
Loading…
Reference in New Issue
Block a user