QRegularExpression: add ways to force an immediate optimization
The fact that we kick in a pattern study and possibly a JIT compilation after an undocumented number of usages is suboptimal, for a number or reasons: users may want to JIT compile a pattern immediately, and at the same time they may not want a random delay in the program (due to the pattern getting optimized at a random usage). So: add an optimize() call to force an immediate pattern optimization, and a pattern option to force an optimization on the first usage. Change-Id: I95efdecfd31f11ca7cceb9c05037df613601a11c Reviewed-by: Lars Knoll <lars.knoll@digia.com> Reviewed-by: Thiago Macieira <thiago.macieira@intel.com>bb10
parent
eedd300b0e
commit
260f24228b
|
|
@ -699,6 +699,12 @@ QT_BEGIN_NAMESPACE
|
|||
\c{\w} to match any character with either the Unicode L (letter) or N
|
||||
(digit) property, plus underscore, and so on. This option corresponds
|
||||
to the \c{/u} modifier in Perl regular expressions.
|
||||
|
||||
\value OptimizeOnFirstUsageOption
|
||||
The regular expression will be optimized (and possibly
|
||||
JIT-compiled) on its first usage, instead of after a certain (undefined)
|
||||
number of usages. See also \l{QRegularExpression::}{optimize()}.
|
||||
This enum value has been introduced in Qt 5.4.
|
||||
*/
|
||||
|
||||
/*!
|
||||
|
|
@ -802,7 +808,13 @@ struct QRegularExpressionPrivate : QSharedData
|
|||
void cleanCompiledPattern();
|
||||
void compilePattern();
|
||||
void getPatternInfo();
|
||||
void optimizePattern();
|
||||
|
||||
enum OptimizePatternOption {
|
||||
LazyOptimizeOption,
|
||||
ImmediateOptimizeOption
|
||||
};
|
||||
|
||||
void optimizePattern(OptimizePatternOption option);
|
||||
|
||||
QRegularExpressionMatchPrivate *doMatch(const QString &subject,
|
||||
int offset,
|
||||
|
|
@ -1107,13 +1119,16 @@ static bool isJitEnabled()
|
|||
(localStudyData) before using storeRelease on studyData. In doMatch there's
|
||||
the corresponding loadAcquire.
|
||||
*/
|
||||
void QRegularExpressionPrivate::optimizePattern()
|
||||
void QRegularExpressionPrivate::optimizePattern(OptimizePatternOption option)
|
||||
{
|
||||
Q_ASSERT(compiledPattern);
|
||||
|
||||
QMutexLocker lock(&mutex);
|
||||
|
||||
if (studyData.load() || (++usedCount != qt_qregularexpression_optimize_after_use_count))
|
||||
if (studyData.load()) // already optimized
|
||||
return;
|
||||
|
||||
if ((option == LazyOptimizeOption) && (++usedCount != qt_qregularexpression_optimize_after_use_count))
|
||||
return;
|
||||
|
||||
static const bool enableJit = isJitEnabled();
|
||||
|
|
@ -1233,8 +1248,13 @@ QRegularExpressionMatchPrivate *QRegularExpressionPrivate::doMatch(const QString
|
|||
matchType, matchOptions,
|
||||
capturingCount + 1);
|
||||
|
||||
const OptimizePatternOption optimizePatternOption =
|
||||
(patternOptions & QRegularExpression::OptimizeOnFirstUsageOption)
|
||||
? ImmediateOptimizeOption
|
||||
: LazyOptimizeOption;
|
||||
|
||||
// this is mutex protected
|
||||
const_cast<QRegularExpressionPrivate *>(this)->optimizePattern();
|
||||
const_cast<QRegularExpressionPrivate *>(this)->optimizePattern(optimizePatternOption);
|
||||
|
||||
// work with a local copy of the study data, as we are running pcre_exec
|
||||
// potentially more than once, and we don't want to run call it
|
||||
|
|
@ -1666,6 +1686,27 @@ QRegularExpressionMatchIterator QRegularExpression::globalMatch(const QString &s
|
|||
return QRegularExpressionMatchIterator(*priv);
|
||||
}
|
||||
|
||||
/*!
|
||||
\since 5.4
|
||||
|
||||
Forces an immediate optimization of the pattern, including
|
||||
JIT-compiling it (if the JIT compiler is enabled).
|
||||
|
||||
Patterns are normally optimized only after a certain number of usages.
|
||||
If you can predict that this QRegularExpression object is going to be
|
||||
used for several matches, it may be convenient to optimize it in
|
||||
advance by calling this function.
|
||||
|
||||
\sa QRegularExpression::OptimizeOnFirstUsageOption
|
||||
*/
|
||||
void QRegularExpression::optimize()
|
||||
{
|
||||
if (!isValid()) // will compile the pattern
|
||||
return;
|
||||
|
||||
d->optimizePattern(QRegularExpressionPrivate::ImmediateOptimizeOption);
|
||||
}
|
||||
|
||||
/*!
|
||||
Returns \c true if the regular expression is equal to \a re, or false
|
||||
otherwise. Two QRegularExpression objects are equal if they have
|
||||
|
|
@ -2327,6 +2368,8 @@ QDebug operator<<(QDebug debug, QRegularExpression::PatternOptions patternOption
|
|||
flags.append("DontCaptureOption|");
|
||||
if (patternOptions & QRegularExpression::UseUnicodePropertiesOption)
|
||||
flags.append("UseUnicodePropertiesOption|");
|
||||
if (patternOptions & QRegularExpression::OptimizeOnFirstUsageOption)
|
||||
flags.append("OptimizeOnFirstUsageOption|");
|
||||
flags.chop(1);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -69,7 +69,8 @@ public:
|
|||
ExtendedPatternSyntaxOption = 0x0008,
|
||||
InvertedGreedinessOption = 0x0010,
|
||||
DontCaptureOption = 0x0020,
|
||||
UseUnicodePropertiesOption = 0x0040
|
||||
UseUnicodePropertiesOption = 0x0040,
|
||||
OptimizeOnFirstUsageOption = 0x0080
|
||||
};
|
||||
Q_DECLARE_FLAGS(PatternOptions, PatternOption)
|
||||
|
||||
|
|
@ -122,6 +123,8 @@ public:
|
|||
MatchType matchType = NormalMatch,
|
||||
MatchOptions matchOptions = NoMatchOption) const;
|
||||
|
||||
void optimize();
|
||||
|
||||
static QString escape(const QString &str);
|
||||
|
||||
bool operator==(const QRegularExpression &re) const;
|
||||
|
|
|
|||
|
|
@ -1,2 +1,3 @@
|
|||
tst_qregularexpression_alwaysoptimize
|
||||
tst_qregularexpression_defaultoptimize
|
||||
tst_qregularexpression_forceoptimize
|
||||
|
|
|
|||
|
|
@ -0,0 +1,8 @@
|
|||
CONFIG += testcase parallel_test
|
||||
TARGET = tst_qregularexpression_forceoptimize
|
||||
QT = core testlib
|
||||
HEADERS = ../tst_qregularexpression.h
|
||||
SOURCES = \
|
||||
tst_qregularexpression_forceoptimize.cpp \
|
||||
../tst_qregularexpression.cpp
|
||||
DEFINES += forceOptimize=true
|
||||
|
|
@ -0,0 +1,52 @@
|
|||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2014 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com, author Giuseppe D'Angelo <giuseppe.dangelo@kdab.com>
|
||||
** Contact: http://www.qt-project.org/legal
|
||||
**
|
||||
** This file is part of the test suite of the Qt Toolkit.
|
||||
**
|
||||
** $QT_BEGIN_LICENSE:LGPL$
|
||||
** Commercial License Usage
|
||||
** Licensees holding valid commercial Qt licenses may use this file in
|
||||
** accordance with the commercial license agreement provided with the
|
||||
** Software or, alternatively, in accordance with the terms contained in
|
||||
** a written agreement between you and Digia. For licensing terms and
|
||||
** conditions see http://qt.digia.com/licensing. For further information
|
||||
** use the contact form at http://qt.digia.com/contact-us.
|
||||
**
|
||||
** GNU Lesser General Public License Usage
|
||||
** Alternatively, this file may be used under the terms of the GNU Lesser
|
||||
** General Public License version 2.1 as published by the Free Software
|
||||
** Foundation and appearing in the file LICENSE.LGPL included in the
|
||||
** packaging of this file. Please review the following information to
|
||||
** ensure the GNU Lesser General Public License version 2.1 requirements
|
||||
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
|
||||
**
|
||||
** In addition, as a special exception, Digia gives you certain additional
|
||||
** rights. These rights are described in the Digia Qt LGPL Exception
|
||||
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
|
||||
**
|
||||
** GNU General Public License Usage
|
||||
** Alternatively, this file may be used under the terms of the GNU
|
||||
** General Public License version 3.0 as published by the Free Software
|
||||
** Foundation and appearing in the file LICENSE.GPL included in the
|
||||
** packaging of this file. Please review the following information to
|
||||
** ensure the GNU General Public License version 3.0 requirements will be
|
||||
** met: http://www.gnu.org/copyleft/gpl.html.
|
||||
**
|
||||
**
|
||||
** $QT_END_LICENSE$
|
||||
**
|
||||
****************************************************************************/
|
||||
|
||||
#include <QtTest/QtTest>
|
||||
#include "../tst_qregularexpression.h"
|
||||
|
||||
class tst_QRegularExpression_ForceOptimize : public tst_QRegularExpression
|
||||
{
|
||||
Q_OBJECT
|
||||
};
|
||||
|
||||
QTEST_APPLESS_MAIN(tst_QRegularExpression_ForceOptimize)
|
||||
|
||||
#include "tst_qregularexpression_forceoptimize.moc"
|
||||
|
|
@ -1,3 +1,3 @@
|
|||
TEMPLATE = subdirs
|
||||
SUBDIRS = defaultoptimize
|
||||
SUBDIRS = defaultoptimize forceoptimize
|
||||
contains(QT_CONFIG,private_tests):SUBDIRS += alwaysoptimize
|
||||
|
|
|
|||
|
|
@ -48,6 +48,10 @@
|
|||
|
||||
#include "tst_qregularexpression.h"
|
||||
|
||||
#ifndef forceOptimize
|
||||
#define forceOptimize false
|
||||
#endif
|
||||
|
||||
struct Match
|
||||
{
|
||||
Match()
|
||||
|
|
@ -329,22 +333,30 @@ void tst_QRegularExpression::gettersSetters()
|
|||
{
|
||||
QRegularExpression re;
|
||||
re.setPattern(pattern);
|
||||
if (forceOptimize)
|
||||
re.optimize();
|
||||
QCOMPARE(re.pattern(), pattern);
|
||||
QCOMPARE(re.patternOptions(), QRegularExpression::NoPatternOption);
|
||||
}
|
||||
{
|
||||
QRegularExpression re;
|
||||
re.setPatternOptions(patternOptions);
|
||||
if (forceOptimize)
|
||||
re.optimize();
|
||||
QCOMPARE(re.pattern(), QString());
|
||||
QCOMPARE(re.patternOptions(), patternOptions);
|
||||
}
|
||||
{
|
||||
QRegularExpression re(pattern);
|
||||
if (forceOptimize)
|
||||
re.optimize();
|
||||
QCOMPARE(re.pattern(), pattern);
|
||||
QCOMPARE(re.patternOptions(), QRegularExpression::NoPatternOption);
|
||||
}
|
||||
{
|
||||
QRegularExpression re(pattern, patternOptions);
|
||||
if (forceOptimize)
|
||||
re.optimize();
|
||||
QCOMPARE(re.pattern(), pattern);
|
||||
QCOMPARE(re.patternOptions(), patternOptions);
|
||||
}
|
||||
|
|
@ -385,6 +397,8 @@ void tst_QRegularExpression::escape()
|
|||
QFETCH(QString, escaped);
|
||||
QCOMPARE(QRegularExpression::escape(string), escaped);
|
||||
QRegularExpression re(escaped);
|
||||
if (forceOptimize)
|
||||
re.optimize();
|
||||
QCOMPARE(re.isValid(), true);
|
||||
}
|
||||
|
||||
|
|
@ -415,6 +429,8 @@ void tst_QRegularExpression::validity()
|
|||
QFETCH(QString, pattern);
|
||||
QFETCH(bool, validity);
|
||||
QRegularExpression re(pattern);
|
||||
if (forceOptimize)
|
||||
re.optimize();
|
||||
QCOMPARE(re.isValid(), validity);
|
||||
if (!validity)
|
||||
QTest::ignoreMessage(QtWarningMsg, "QRegularExpressionPrivate::doMatch(): called on an invalid QRegularExpression object");
|
||||
|
|
@ -501,6 +517,9 @@ void tst_QRegularExpression::patternOptions()
|
|||
QFETCH(QString, subject);
|
||||
QFETCH(Match, match);
|
||||
|
||||
if (forceOptimize)
|
||||
regexp.optimize();
|
||||
|
||||
QRegularExpressionMatch m = regexp.match(subject);
|
||||
consistencyCheck(m);
|
||||
QVERIFY(m == match);
|
||||
|
|
@ -717,6 +736,9 @@ void tst_QRegularExpression::normalMatch()
|
|||
QFETCH(QRegularExpression::MatchOptions, matchOptions);
|
||||
QFETCH(Match, match);
|
||||
|
||||
if (forceOptimize)
|
||||
regexp.optimize();
|
||||
|
||||
{
|
||||
QRegularExpressionMatch m = regexp.match(subject, offset, QRegularExpression::NormalMatch, matchOptions);
|
||||
consistencyCheck(m);
|
||||
|
|
@ -995,6 +1017,9 @@ void tst_QRegularExpression::partialMatch()
|
|||
QFETCH(QRegularExpression::MatchOptions, matchOptions);
|
||||
QFETCH(Match, match);
|
||||
|
||||
if (forceOptimize)
|
||||
regexp.optimize();
|
||||
|
||||
{
|
||||
QRegularExpressionMatch m = regexp.match(subject, offset, matchType, matchOptions);
|
||||
consistencyCheck(m);
|
||||
|
|
@ -1286,6 +1311,10 @@ void tst_QRegularExpression::globalMatch()
|
|||
QFETCH(QRegularExpression::MatchType, matchType);
|
||||
QFETCH(QRegularExpression::MatchOptions, matchOptions);
|
||||
QFETCH(QList<Match>, matchList);
|
||||
|
||||
if (forceOptimize)
|
||||
regexp.optimize();
|
||||
|
||||
{
|
||||
QRegularExpressionMatchIterator iterator = regexp.globalMatch(subject, offset, matchType, matchOptions);
|
||||
consistencyCheck(iterator);
|
||||
|
|
@ -1320,6 +1349,10 @@ void tst_QRegularExpression::serialize()
|
|||
QFETCH(QString, pattern);
|
||||
QFETCH(QRegularExpression::PatternOptions, patternOptions);
|
||||
QRegularExpression outRe(pattern, patternOptions);
|
||||
|
||||
if (forceOptimize)
|
||||
outRe.optimize();
|
||||
|
||||
QByteArray buffer;
|
||||
{
|
||||
QDataStream out(&buffer, QIODevice::WriteOnly);
|
||||
|
|
@ -1376,16 +1409,34 @@ void tst_QRegularExpression::operatoreq()
|
|||
{
|
||||
QRegularExpression re1(pattern);
|
||||
QRegularExpression re2(pattern);
|
||||
|
||||
if (forceOptimize)
|
||||
re1.optimize();
|
||||
if (forceOptimize)
|
||||
re2.optimize();
|
||||
|
||||
verifyEquality(re1, re2);
|
||||
}
|
||||
{
|
||||
QRegularExpression re1(QString(), patternOptions);
|
||||
QRegularExpression re2(QString(), patternOptions);
|
||||
|
||||
if (forceOptimize)
|
||||
re1.optimize();
|
||||
if (forceOptimize)
|
||||
re2.optimize();
|
||||
|
||||
verifyEquality(re1, re2);
|
||||
}
|
||||
{
|
||||
QRegularExpression re1(pattern, patternOptions);
|
||||
QRegularExpression re2(pattern, patternOptions);
|
||||
|
||||
if (forceOptimize)
|
||||
re1.optimize();
|
||||
if (forceOptimize)
|
||||
re2.optimize();
|
||||
|
||||
verifyEquality(re1, re2);
|
||||
}
|
||||
}
|
||||
|
|
@ -1414,6 +1465,10 @@ void tst_QRegularExpression::captureCount()
|
|||
{
|
||||
QFETCH(QString, pattern);
|
||||
QRegularExpression re(pattern);
|
||||
|
||||
if (forceOptimize)
|
||||
re.optimize();
|
||||
|
||||
QTEST(re.captureCount(), "captureCount");
|
||||
if (!re.isValid())
|
||||
QCOMPARE(re.captureCount(), -1);
|
||||
|
|
@ -1480,7 +1535,11 @@ void tst_QRegularExpression::captureNames()
|
|||
QFETCH(QString, pattern);
|
||||
QFETCH(StringToIntMap, namedCapturesIndexMap);
|
||||
|
||||
const QRegularExpression re(pattern);
|
||||
QRegularExpression re(pattern);
|
||||
|
||||
if (forceOptimize)
|
||||
re.optimize();
|
||||
|
||||
QStringList namedCaptureGroups = re.namedCaptureGroups();
|
||||
int namedCaptureGroupsCount = namedCaptureGroups.size();
|
||||
|
||||
|
|
@ -1515,6 +1574,10 @@ void tst_QRegularExpression::pcreJitStackUsage()
|
|||
QFETCH(QString, subject);
|
||||
|
||||
QRegularExpression re(pattern);
|
||||
|
||||
if (forceOptimize)
|
||||
re.optimize();
|
||||
|
||||
QVERIFY(re.isValid());
|
||||
QRegularExpressionMatch match = re.match(subject);
|
||||
consistencyCheck(match);
|
||||
|
|
@ -1541,6 +1604,10 @@ void tst_QRegularExpression::regularExpressionMatch()
|
|||
QFETCH(QString, subject);
|
||||
|
||||
QRegularExpression re(pattern);
|
||||
|
||||
if (forceOptimize)
|
||||
re.optimize();
|
||||
|
||||
QVERIFY(re.isValid());
|
||||
QRegularExpressionMatch match = re.match(subject);
|
||||
consistencyCheck(match);
|
||||
|
|
@ -1580,5 +1647,7 @@ void tst_QRegularExpression::JOptionUsage()
|
|||
QRegularExpression re(pattern);
|
||||
if (isValid && JOptionUsed)
|
||||
QTest::ignoreMessage(QtWarningMsg, qPrintable(warningMessage.arg(pattern)));
|
||||
if (forceOptimize)
|
||||
re.optimize();
|
||||
QCOMPARE(re.isValid(), isValid);
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue