Sync mimeglobpattern sources with Qt

The optimization for VdrPattern and AnimPattern speeds up
loading of Qt6 project with further ~200 ms.

Change-Id: I28451a627d6c509854907736da48efcf68a86019
Reviewed-by: hjk <hjk@qt.io>
This commit is contained in:
Jarek Kobus
2021-10-11 23:06:51 +02:00
parent 13bca02801
commit 10a6497146
2 changed files with 94 additions and 38 deletions

View File

@@ -83,6 +83,39 @@ void MimeGlobMatchResult::addMatch(const QString &mimeType, int weight, const QS
} }
} }
MimeGlobPattern::PatternType MimeGlobPattern::detectPatternType(const QString &pattern) const
{
const int patternLength = pattern.length();
if (!patternLength)
return OtherPattern;
const bool starCount = pattern.count(QLatin1Char('*')) == 1;
const bool hasSquareBracket = pattern.indexOf(QLatin1Char('[')) != -1;
const bool hasQuestionMark = pattern.indexOf(QLatin1Char('?')) != -1;
if (!hasSquareBracket && !hasQuestionMark) {
if (starCount == 1) {
// Patterns like "*~", "*.extension"
if (pattern.at(0) == QLatin1Char('*'))
return SuffixPattern;
// Patterns like "README*" (well this is currently the only one like that...)
if (pattern.at(patternLength - 1) == QLatin1Char('*'))
return PrefixPattern;
}
// Names without any wildcards like "README"
if (starCount == 0)
return LiteralPattern;
}
if (pattern == QLatin1String("[0-9][0-9][0-9].vdr"))
return VdrPattern;
if (pattern == QLatin1String("*.anim[1-9j]"))
return AnimPattern;
return OtherPattern;
}
/*! /*!
\internal \internal
\class MimeGlobPattern \class MimeGlobPattern
@@ -92,54 +125,63 @@ void MimeGlobMatchResult::addMatch(const QString &mimeType, int weight, const QS
\sa MimeType, MimeDatabase, MimeMagicRuleMatcher, MimeMagicRule \sa MimeType, MimeDatabase, MimeMagicRuleMatcher, MimeMagicRule
*/ */
bool MimeGlobPattern::matchFileName(const QString &inputFilename) const bool MimeGlobPattern::matchFileName(const QString &inputFileName) const
{ {
// "Applications MUST match globs case-insensitively, except when the case-sensitive // "Applications MUST match globs case-insensitively, except when the case-sensitive
// attribute is set to true." // attribute is set to true."
// The constructor takes care of putting case-insensitive patterns in lowercase. // The constructor takes care of putting case-insensitive patterns in lowercase.
const QString filename = m_caseSensitivity == Qt::CaseInsensitive ? inputFilename.toLower() : inputFilename; const QString fileName = m_caseSensitivity == Qt::CaseInsensitive
? inputFileName.toLower() : inputFileName;
const int pattern_len = m_pattern.length(); const int patternLength = m_pattern.length();
if (!pattern_len) if (!patternLength)
return false; return false;
const int len = filename.length(); const int fileNameLength = fileName.length();
// Patterns like "*~", "*.extension" switch (m_patternType) {
if (m_pattern[0] == QLatin1Char('*') && m_openingSquareBracketPos == -1 && m_starCount == 1) { case SuffixPattern: {
if (len + 1 < pattern_len) if (fileNameLength + 1 < patternLength)
return false; return false;
const QChar *c1 = m_pattern.unicode() + pattern_len - 1; const QChar *c1 = m_pattern.unicode() + patternLength - 1;
const QChar *c2 = filename.unicode() + len - 1; const QChar *c2 = fileName.unicode() + fileNameLength - 1;
int cnt = 1; int cnt = 1;
while (cnt < pattern_len && *c1-- == *c2--) while (cnt < patternLength && *c1-- == *c2--)
++cnt; ++cnt;
return cnt == pattern_len; return cnt == patternLength;
} }
case PrefixPattern: {
// Patterns like "README*" (well this is currently the only one like that...) if (fileNameLength + 1 < patternLength)
if (m_starCount == 1 && m_pattern.at(pattern_len - 1) == QLatin1Char('*')) {
if (len + 1 < pattern_len)
return false; return false;
if (m_pattern.at(0) == QLatin1Char('*'))
return filename.indexOf(QStringView(m_pattern).mid(1, pattern_len - 2)) != -1;
const QChar *c1 = m_pattern.unicode(); const QChar *c1 = m_pattern.unicode();
const QChar *c2 = filename.unicode(); const QChar *c2 = fileName.unicode();
int cnt = 1; int cnt = 1;
while (cnt < pattern_len && *c1++ == *c2++) while (cnt < patternLength && *c1++ == *c2++)
++cnt; ++cnt;
return cnt == pattern_len; return cnt == patternLength;
} }
case LiteralPattern:
// Names without any wildcards like "README" return (m_pattern == fileName);
if (m_openingSquareBracketPos == -1 && m_starCount == 0 && m_questionMarkPos == -1) case VdrPattern: // "[0-9][0-9][0-9].vdr" case
return (m_pattern == filename); return fileNameLength == 7
&& fileName.at(0).isDigit() && fileName.at(1).isDigit() && fileName.at(2).isDigit()
// Other (quite rare) patterns, like "*.anim[1-9j]": use slow but correct method && QStringView{fileName}.mid(3, 4) == QLatin1String(".vdr");
const QRegularExpression rx(QRegularExpression::anchoredPattern( case AnimPattern: { // "*.anim[1-9j]" case
if (fileNameLength < 6)
return false;
const QChar lastChar = fileName.at(fileNameLength - 1);
const bool lastCharOK = (lastChar.isDigit() && lastChar != QLatin1Char('0'))
|| lastChar == QLatin1Char('j');
return lastCharOK && QStringView{fileName}.mid(fileNameLength - 6, 5) == QLatin1String(".anim");
}
case OtherPattern:
// Other fallback patterns: slow but correct method
const QRegularExpression rx(QRegularExpression::anchoredPattern(
QRegularExpression::wildcardToRegularExpression(m_pattern))); QRegularExpression::wildcardToRegularExpression(m_pattern)));
return rx.match(filename).hasMatch(); return rx.match(fileName).hasMatch();
}
return false;
} }
static bool isFastPattern(const QString &pattern) static bool isFastPattern(const QString &pattern)

View File

@@ -79,15 +79,21 @@ public:
m_pattern(s == Qt::CaseInsensitive ? thePattern.toLower() : thePattern), m_pattern(s == Qt::CaseInsensitive ? thePattern.toLower() : thePattern),
m_mimeType(theMimeType), m_mimeType(theMimeType),
m_weight(theWeight), m_weight(theWeight),
m_starCount(m_pattern.count(QLatin1Char('*'))), m_caseSensitivity(s),
m_openingSquareBracketPos(m_pattern.indexOf(QLatin1Char('['))), m_patternType(detectPatternType(m_pattern))
m_questionMarkPos(m_pattern.indexOf(QLatin1Char('?'))),
m_caseSensitivity(s)
{ {
} }
~MimeGlobPattern() {}
bool matchFileName(const QString &filename) const; void swap(MimeGlobPattern &other) noexcept
{
qSwap(m_pattern, other.m_pattern);
qSwap(m_mimeType, other.m_mimeType);
qSwap(m_weight, other.m_weight);
qSwap(m_caseSensitivity, other.m_caseSensitivity);
qSwap(m_patternType, other.m_patternType);
}
bool matchFileName(const QString &inputFileName) const;
inline const QString &pattern() const { return m_pattern; } inline const QString &pattern() const { return m_pattern; }
inline unsigned weight() const { return m_weight; } inline unsigned weight() const { return m_weight; }
@@ -95,13 +101,21 @@ public:
inline bool isCaseSensitive() const { return m_caseSensitivity == Qt::CaseSensitive; } inline bool isCaseSensitive() const { return m_caseSensitivity == Qt::CaseSensitive; }
private: private:
enum PatternType {
SuffixPattern,
PrefixPattern,
LiteralPattern,
VdrPattern, // special handling for "[0-9][0-9][0-9].vdr" pattern
AnimPattern, // special handling for "*.anim[1-9j]" pattern
OtherPattern
};
PatternType detectPatternType(const QString &pattern) const;
QString m_pattern; QString m_pattern;
QString m_mimeType; QString m_mimeType;
int m_weight; int m_weight;
int m_starCount;
int m_openingSquareBracketPos;
int m_questionMarkPos;
Qt::CaseSensitivity m_caseSensitivity; Qt::CaseSensitivity m_caseSensitivity;
PatternType m_patternType;
}; };
class MimeGlobPatternList : public QList<MimeGlobPattern> class MimeGlobPatternList : public QList<MimeGlobPattern>