Optimize mime type matching
Don't count "*", don't search for "[" and for "?" inside m_pattern on every call to matchFileName(). Do it once, when constructing the MimeGlobPattern. Fix matching the pattern for names without any wildcard: index of question mark should be -1, not just different from 0. This shortens loading a Qt6 project in Creator by about 500 ms. Pick-to: 6.2 5.15 Change-Id: Ifa40c2cec4aba07a0312ef36877e571a8c8fb151 Reviewed-by: Qt CI Bot <qt_ci_bot@qt-project.org> Reviewed-by: David Faure <david.faure@kdab.com>bb10
parent
4ed5f826c8
commit
3c747aafa4
|
|
@ -91,6 +91,40 @@ void QMimeGlobMatchResult::addMatch(const QString &mimeType, int weight, const Q
|
|||
}
|
||||
}
|
||||
|
||||
QMimeGlobPattern::PatternType QMimeGlobPattern::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
|
||||
\class QMimeGlobPattern
|
||||
|
|
@ -100,58 +134,66 @@ void QMimeGlobMatchResult::addMatch(const QString &mimeType, int weight, const Q
|
|||
\sa QMimeType, QMimeDatabase, QMimeMagicRuleMatcher, QMimeMagicRule
|
||||
*/
|
||||
|
||||
bool QMimeGlobPattern::matchFileName(const QString &inputFilename) const
|
||||
bool QMimeGlobPattern::matchFileName(const QString &inputFileName) const
|
||||
{
|
||||
// "Applications MUST match globs case-insensitively, except when the case-sensitive
|
||||
// attribute is set to true."
|
||||
// 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();
|
||||
if (!pattern_len)
|
||||
const int patternLength = m_pattern.length();
|
||||
if (!patternLength)
|
||||
return false;
|
||||
const int len = filename.length();
|
||||
const int fileNameLength = fileName.length();
|
||||
|
||||
const int starCount = m_pattern.count(QLatin1Char('*'));
|
||||
switch (m_patternType) {
|
||||
case SuffixPattern: {
|
||||
if (fileNameLength + 1 < patternLength)
|
||||
return false;
|
||||
|
||||
// Patterns like "*~", "*.extension"
|
||||
if (m_pattern[0] == QLatin1Char('*') && m_pattern.indexOf(QLatin1Char('[')) == -1 && starCount == 1)
|
||||
{
|
||||
if (len + 1 < pattern_len) return false;
|
||||
|
||||
const QChar *c1 = m_pattern.unicode() + pattern_len - 1;
|
||||
const QChar *c2 = filename.unicode() + len - 1;
|
||||
const QChar *c1 = m_pattern.unicode() + patternLength - 1;
|
||||
const QChar *c2 = fileName.unicode() + fileNameLength - 1;
|
||||
int cnt = 1;
|
||||
while (cnt < pattern_len && *c1-- == *c2--)
|
||||
while (cnt < patternLength && *c1-- == *c2--)
|
||||
++cnt;
|
||||
return cnt == pattern_len;
|
||||
return cnt == patternLength;
|
||||
}
|
||||
|
||||
// Patterns like "README*" (well this is currently the only one like that...)
|
||||
if (starCount == 1 && m_pattern.at(pattern_len - 1) == QLatin1Char('*')) {
|
||||
if (len + 1 < pattern_len) return false;
|
||||
if (m_pattern.at(0) == QLatin1Char('*'))
|
||||
return filename.indexOf(QStringView{m_pattern}.mid(1, pattern_len - 2)) != -1;
|
||||
case PrefixPattern: {
|
||||
if (fileNameLength + 1 < patternLength)
|
||||
return false;
|
||||
|
||||
const QChar *c1 = m_pattern.unicode();
|
||||
const QChar *c2 = filename.unicode();
|
||||
const QChar *c2 = fileName.unicode();
|
||||
int cnt = 1;
|
||||
while (cnt < pattern_len && *c1++ == *c2++)
|
||||
while (cnt < patternLength && *c1++ == *c2++)
|
||||
++cnt;
|
||||
return cnt == pattern_len;
|
||||
return cnt == patternLength;
|
||||
}
|
||||
|
||||
// Names without any wildcards like "README"
|
||||
if (m_pattern.indexOf(QLatin1Char('[')) == -1 && starCount == 0 && m_pattern.indexOf(QLatin1Char('?')))
|
||||
return (m_pattern == filename);
|
||||
|
||||
// Other (quite rare) patterns, like "*.anim[1-9j]": use slow but correct method
|
||||
case LiteralPattern:
|
||||
return (m_pattern == fileName);
|
||||
case VdrPattern: // "[0-9][0-9][0-9].vdr" case
|
||||
return fileNameLength == 7
|
||||
&& fileName.at(0).isDigit() && fileName.at(1).isDigit() && fileName.at(2).isDigit()
|
||||
&& QStringView{fileName}.mid(3, 4) == QLatin1String(".vdr");
|
||||
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
|
||||
#if QT_CONFIG(regularexpression)
|
||||
auto rx = QRegularExpression::fromWildcard(m_pattern);
|
||||
return rx.match(filename).hasMatch();
|
||||
auto rx = QRegularExpression::fromWildcard(m_pattern);
|
||||
return rx.match(fileName).hasMatch();
|
||||
#else
|
||||
return false;
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool isSimplePattern(const QString &pattern)
|
||||
|
|
|
|||
|
|
@ -80,7 +80,10 @@ public:
|
|||
|
||||
explicit QMimeGlobPattern(const QString &thePattern, const QString &theMimeType, unsigned theWeight = DefaultWeight, Qt::CaseSensitivity s = Qt::CaseInsensitive) :
|
||||
m_pattern(s == Qt::CaseInsensitive ? thePattern.toLower() : thePattern),
|
||||
m_mimeType(theMimeType), m_weight(theWeight), m_caseSensitivity(s)
|
||||
m_mimeType(theMimeType),
|
||||
m_weight(theWeight),
|
||||
m_caseSensitivity(s),
|
||||
m_patternType(detectPatternType(m_pattern))
|
||||
{
|
||||
}
|
||||
|
||||
|
|
@ -90,9 +93,10 @@ public:
|
|||
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 &filename) const;
|
||||
bool matchFileName(const QString &inputFileName) const;
|
||||
|
||||
inline const QString &pattern() const { return m_pattern; }
|
||||
inline unsigned weight() const { return m_weight; }
|
||||
|
|
@ -100,10 +104,21 @@ public:
|
|||
inline bool isCaseSensitive() const { return m_caseSensitivity == Qt::CaseSensitive; }
|
||||
|
||||
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_mimeType;
|
||||
int m_weight;
|
||||
Qt::CaseSensitivity m_caseSensitivity;
|
||||
PatternType m_patternType;
|
||||
};
|
||||
Q_DECLARE_SHARED(QMimeGlobPattern)
|
||||
|
||||
|
|
|
|||
|
|
@ -278,7 +278,6 @@ void QMimeBinaryProvider::matchGlobList(QMimeGlobMatchResult &result, CacheFile
|
|||
//qDebug() << pattern << mimeType << weight << caseSensitive;
|
||||
QMimeGlobPattern glob(pattern, QString() /*unused*/, weight, qtCaseSensitive);
|
||||
|
||||
// TODO: this could be done faster for literals where a simple == would do.
|
||||
if (glob.matchFileName(fileName))
|
||||
result.addMatch(QLatin1String(mimeType), weight, pattern);
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue