Skip to content

Commit ab227d4

Browse files
committed
HL: fast path for ascii delimiters
We can use a bit set for ascii delimiters. All our syntax definitions use use ASCII so this is a clear win. We still leave the old path for non ASCII delimiters. This brings up in the 1100-1000 msec (originally from 1500-1400). ``` [qutepart-cpp] ./cbuild/editor ~/benchmark.cpp 22:10:13 ☁ main ☂ 𝝙 ⚡ ✭ 𝝙 Loading itme = 1098 [qutepart-cpp] ./cbuild/editor ~/benchmark.cpp 22:10:17 ☁ main ☂ 𝝙 ⚡ ✭ 𝝙 Loading itme = 1104 [qutepart-cpp] ./cbuild/editor ~/benchmark.cpp 22:10:20 ☁ main ☂ 𝝙 ⚡ ✭ 𝝙 Loading itme = 1127 [qutepart-cpp] ./cbuild/editor ~/benchmark.cpp 22:10:24 ☁ main ☂ 𝝙 ⚡ ✭ 𝝙 Loading itme = 1105 [qutepart-cpp] ./cbuild/editor ~/benchmark.cpp 22:10:27 ☁ main ☂ 𝝙 ⚡ ✭ 𝝙 Loading itme = 1139 [qutepart-cpp] ./cbuild/editor ~/benchmark.cpp 22:10:31 ☁ main ☂ 𝝙 ⚡ ✭ 𝝙 Loading itme = 1087 ``` refs #73
1 parent bac8751 commit ab227d4

4 files changed

Lines changed: 50 additions & 11 deletions

File tree

src/hl/rules.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -128,7 +128,7 @@ void KeywordRule::setKeywordParams(const QHash<QString, QStringList> &lists, boo
128128
return;
129129
}
130130
this->caseSensitive = newCaseSensitive;
131-
this->deliminators = newDeliminators;
131+
this->deliminators = DeliminatorSet(newDeliminators);
132132

133133
items.clear();
134134
const auto &list = lists[listName];
@@ -232,7 +232,7 @@ bool WordDetectRule::tryMatchImpl(const TextToMatch &textToMatch, MatchResult &r
232232

233233
void WordDetectRule::setKeywordParams(const QHash<QString, QStringList> &, bool,
234234
const QString &deliminatorSet, QString &) {
235-
mDeliminatorSet = deliminatorSet;
235+
mDeliminatorSet = DeliminatorSet(deliminatorSet);
236236
}
237237

238238
RegExpRule::RegExpRule(const AbstractRuleParams &params, const QString &value, bool insensitive,

src/hl/rules.h

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
#include <QTextStream>
1414

1515
#include "context.h"
16+
#include "text_to_match.h"
1617

1718
namespace Qutepart {
1819

@@ -105,7 +106,7 @@ class KeywordRule : public AbstractRule {
105106
QString listName;
106107
QHash<QString, bool> items;
107108
bool caseSensitive;
108-
QString deliminators;
109+
DeliminatorSet deliminators;
109110
};
110111

111112
class DetectCharRule : public AbstractRule {
@@ -162,7 +163,7 @@ class WordDetectRule : public AbstractStringRule {
162163

163164
private:
164165
virtual bool tryMatchImpl(const TextToMatch &textToMatch, MatchResult &result) const override;
165-
QString mDeliminatorSet;
166+
DeliminatorSet mDeliminatorSet;
166167
};
167168

168169
class RegExpRule : public AbstractRule {

src/hl/text_to_match.cpp

Lines changed: 25 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,25 @@
88

99
namespace Qutepart {
1010

11+
DeliminatorSet::DeliminatorSet(const QString &deliminators) {
12+
for (QChar ch : deliminators) {
13+
ushort code = ch.unicode();
14+
if (code < asciiChars_.size()) {
15+
asciiChars_.set(code);
16+
} else {
17+
nonAsciiChars_.append(ch);
18+
}
19+
}
20+
}
21+
22+
bool DeliminatorSet::contains(QChar ch) const {
23+
ushort code = ch.unicode();
24+
if (code < asciiChars_.size()) {
25+
return asciiChars_.test(code);
26+
}
27+
return nonAsciiChars_.contains(ch);
28+
}
29+
1130
TextToMatch::TextToMatch(const QString &text, const QStringList &contextData)
1231
: currentColumnIndex(0), wholeLineText(text), text(wholeLineText.left(wholeLineText.length())),
1332
textLength(text.length()), firstNonSpace(true), // copy-paste from Py code
@@ -42,25 +61,25 @@ void TextToMatch::shift(int count) {
4261

4362
bool TextToMatch::isEmpty() const { return text.isEmpty(); }
4463

45-
QString TextToMatch::word(const QString &deliminatorSet) const {
64+
QStringView TextToMatch::word(const DeliminatorSet &deliminators) const {
4665
if (currentColumnIndex > 0) {
4766
QChar prevChar = wholeLineText[currentColumnIndex - 1];
48-
if (!deliminatorSet.contains(prevChar)) {
49-
return QString();
67+
if (!deliminators.contains(prevChar)) {
68+
return QStringView();
5069
}
5170
}
5271

5372
int wordEndIndex = 0;
5473
for (; wordEndIndex < text.length(); wordEndIndex++) {
55-
if (deliminatorSet.contains(text.at(wordEndIndex))) {
74+
if (deliminators.contains(text.at(wordEndIndex))) {
5675
break;
5776
}
5877
}
5978
if (wordEndIndex != 0) {
60-
return text.left(wordEndIndex).toString();
79+
return text.left(wordEndIndex);
6180
}
6281

63-
return QString();
82+
return QStringView();
6483
}
6584

6685
} // namespace Qutepart

src/hl/text_to_match.h

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,10 +6,29 @@
66

77
#pragma once
88

9+
#include <bitset>
10+
911
#include <QString>
1012

1113
namespace Qutepart {
1214

15+
/* A set of "word deliminator" characters, with O(1) membership testing.
16+
* Built once (when a rule's deliminator string is set) and reused on every
17+
* character checked while extracting a word - avoids rescanning the
18+
* deliminator string for every character of every candidate word.
19+
*/
20+
class DeliminatorSet {
21+
public:
22+
DeliminatorSet() = default;
23+
explicit DeliminatorSet(const QString &deliminators);
24+
25+
bool contains(QChar ch) const;
26+
27+
private:
28+
std::bitset<128> asciiChars_;
29+
QString nonAsciiChars_; // deliminators outside ASCII range; expected to be empty in practice
30+
};
31+
1332
/* Peace of text, which shall be matched.
1433
* Contains pre-calculated and pre-checked data for performance optimization
1534
*/
@@ -22,7 +41,7 @@ class TextToMatch {
2241

2342
bool isEmpty() const;
2443

25-
QString word(const QString &deliminators) const;
44+
QStringView word(const DeliminatorSet &deliminators) const;
2645

2746
int currentColumnIndex;
2847
QString wholeLineText;

0 commit comments

Comments
 (0)