Reduce foldCasing of the needle in Boyer-Moore QString searches
Before searching, foldCase the first up to 256 characters, and use this buffer to compare against the haystack. If the needle is larger than the buffer, compare the rest of the needle against the rest of the haystack for every potential match. The buffer is placed on the stack and must be refolded for each search, but this change does not break the API. This is faster than the old implementation, except if the needle is long and it is found near the beginning of the haystack, or if the needle is long and it is not found in a short haystack where few comparisons are done and hence few case foldings were needed in the old implementation. Benchmarking using tst_bench_qstringtokenizer tokenize_qstring_qstring shows an improvement for the the total testcase and usually for each individual test. Fixes: QTBUG-100239 Change-Id: Ie61342eb5c19f32de3c1ba0a51dbb0db503bdf3a Reviewed-by: Thiago Macieira <thiago.macieira@intel.com>bb10
parent
266eb124a2
commit
d8387fc538
|
|
@ -6,10 +6,13 @@
|
|||
|
||||
QT_BEGIN_NAMESPACE
|
||||
|
||||
static constexpr qsizetype FoldBufferCapacity = 256;
|
||||
|
||||
static void bm_init_skiptable(QStringView needle, uchar *skiptable, Qt::CaseSensitivity cs)
|
||||
{
|
||||
const char16_t *uc = needle.utf16();
|
||||
const qsizetype len = needle.size();
|
||||
const qsizetype len =
|
||||
cs == Qt::CaseSensitive ? needle.size() : qMin(needle.size(), FoldBufferCapacity);
|
||||
qsizetype l = qMin(len, qsizetype(255));
|
||||
memset(skiptable, l, 256 * sizeof(uchar));
|
||||
uc += len - l;
|
||||
|
|
@ -37,11 +40,12 @@ static inline qsizetype bm_find(QStringView haystack, qsizetype index, QStringVi
|
|||
|
||||
if (pl == 0)
|
||||
return index > l ? -1 : index;
|
||||
const qsizetype pl_minus_one = pl - 1;
|
||||
|
||||
const char16_t *current = uc + index + pl_minus_one;
|
||||
const char16_t *end = uc + l;
|
||||
if (cs == Qt::CaseSensitive) {
|
||||
const qsizetype pl_minus_one = pl - 1;
|
||||
const char16_t *current = uc + index + pl_minus_one;
|
||||
const char16_t *end = uc + l;
|
||||
|
||||
while (current < end) {
|
||||
qsizetype skip = skiptable[*current & 0xff];
|
||||
if (!skip) {
|
||||
|
|
@ -66,21 +70,38 @@ static inline qsizetype bm_find(QStringView haystack, qsizetype index, QStringVi
|
|||
current += skip;
|
||||
}
|
||||
} else {
|
||||
char16_t foldBuffer[FoldBufferCapacity];
|
||||
const qsizetype foldBufferLength = qMin(FoldBufferCapacity, pl);
|
||||
const char16_t *start = puc;
|
||||
for (qsizetype i = 0; i < foldBufferLength; ++i)
|
||||
foldBuffer[i] = foldCase(&puc[i], start);
|
||||
QStringView restNeedle = needle.sliced(foldBufferLength);
|
||||
const qsizetype foldBufferEnd = foldBufferLength - 1;
|
||||
const char16_t *current = uc + index + foldBufferEnd;
|
||||
const char16_t *end = uc + l;
|
||||
|
||||
while (current < end) {
|
||||
qsizetype skip = skiptable[foldCase(current, uc) & 0xff];
|
||||
if (!skip) {
|
||||
// possible match
|
||||
while (skip < pl) {
|
||||
if (foldCase(current - skip, uc) != foldCase(puc + pl_minus_one - skip, puc))
|
||||
while (skip < foldBufferLength) {
|
||||
if (foldCase(current - skip, uc) != foldBuffer[foldBufferEnd - skip])
|
||||
break;
|
||||
++skip;
|
||||
}
|
||||
if (skip > pl_minus_one) // we have a match
|
||||
return (current - uc) - pl_minus_one;
|
||||
if (skip > foldBufferEnd) { // Matching foldBuffer
|
||||
qsizetype candidatePos = (current - uc) - foldBufferEnd;
|
||||
QStringView restHaystack =
|
||||
haystack.sliced(qMin(haystack.size(), candidatePos + foldBufferLength));
|
||||
if (restNeedle.size() == 0
|
||||
|| restHaystack.startsWith(
|
||||
restNeedle, Qt::CaseInsensitive)) // Check the rest of the string
|
||||
return candidatePos;
|
||||
}
|
||||
// in case we don't have a match we are a bit inefficient as we only skip by one
|
||||
// when we have the non matching char in the string.
|
||||
if (skiptable[foldCase(current - skip, uc) & 0xff] == pl)
|
||||
skip = pl - skip;
|
||||
if (skiptable[foldCase(current - skip, uc) & 0xff] == foldBufferLength)
|
||||
skip = foldBufferLength - skip;
|
||||
else
|
||||
skip = 1;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -92,7 +92,30 @@ void tst_QStringMatcher::setCaseSensitivity_data()
|
|||
|
||||
QTest::newRow("overshot") << QString("foo") << QString("baFooz foo bar") << 14 << -1 << (int) Qt::CaseSensitive;
|
||||
QTest::newRow("sensitive") << QString("foo") << QString("baFooz foo bar") << 1 << 7 << (int) Qt::CaseSensitive;
|
||||
QTest::newRow("insensitive") << QString("foo") << QString("baFooz foo bar") << 1 << 2 << (int) Qt::CaseInsensitive;
|
||||
QTest::newRow("insensitive-1")
|
||||
<< QString("foo") << QString("baFooz foo bar") << 0 << 2 << (int)Qt::CaseInsensitive;
|
||||
QTest::newRow("insensitive-2")
|
||||
<< QString("foo") << QString("baFooz foo bar") << 1 << 2 << (int)Qt::CaseInsensitive;
|
||||
QTest::newRow("insensitive-3")
|
||||
<< QString("foo") << QString("baFooz foo bar") << 4 << 7 << (int)Qt::CaseInsensitive;
|
||||
QTest::newRow("insensitive-4")
|
||||
<< QString("foogabooga") << QString("baFooGaBooga foogabooga bar") << 1 << 2
|
||||
<< (int)Qt::CaseInsensitive;
|
||||
QTest::newRow("insensitive-5")
|
||||
<< QString("foogabooga") << QString("baFooGaBooga foogabooga bar") << 3 << 13
|
||||
<< (int)Qt::CaseInsensitive;
|
||||
QTest::newRow("insensitive-6") << QString("foogabooga") << QString("GaBoogaFoogaBooga bar") << 0
|
||||
<< 7 << (int)Qt::CaseInsensitive;
|
||||
QTest::newRow("insensitive-7") << QString("foogabooga") << QString("FoGaBoogaFoogaBooga") << 9
|
||||
<< 9 << (int)Qt::CaseInsensitive;
|
||||
QTest::newRow("insensitive-8") << QString("foogaBooga") << QString("zzzzaazzffoogaBooga") << 0
|
||||
<< 9 << (int)Qt::CaseInsensitive;
|
||||
QString stringOf32("abcdefghijklmnopqrstuvwxyz123456");
|
||||
Q_ASSERT(stringOf32.size() == 32);
|
||||
QString stringOf128 = stringOf32 + stringOf32 + stringOf32 + stringOf32;
|
||||
QString needle = stringOf128 + stringOf128 + "CAse";
|
||||
QString haystack = stringOf128 + stringOf128 + "caSE";
|
||||
QTest::newRow("insensitive-9") << needle << haystack << 0 << 0 << (int)Qt::CaseInsensitive;
|
||||
}
|
||||
|
||||
void tst_QStringMatcher::setCaseSensitivity()
|
||||
|
|
|
|||
Loading…
Reference in New Issue