QRegularExpression: refactor pattern optimization
After the move to PCRE2, optimizing patterns has been a thorn in the side due to the fact that PCRE2's JIT compiler modifies the pattern object itself (instead of returning a new set of data, like PCRE1 did). To make this fit with the existing behavior, a read/write lock was introduced, with the read part locking when matching and the write when compiling (or JIT-compiling) the pattern. This locking strategy however introduced a performance issue, as we needed: * to acquire a write lock to compile/optimize the pattern (incl. the common case where the pattern was already compiled, so bailing out immediately); * to acquire a read lock during the actual match, to prevent some other thread from optimizing the pattern under our nose. This was due to the "lazy" optimization policy of QRegularExpression -- optimize a pattern after a certain number of usages. The excessive amount of locking effectively limited scalability. Simplify the code, and drop that policy altogether: since JIT compiling in PCRE2 is faster and pretty much "always recommended", just always do it for any pattern (unless it gets disabled via env variables) when compiling it. This allows to go back to a plain QMutex, and now the actual matching doesn't require acquiring any locks any longer. Of course, there is still a mutex acquired just before matching for checking whether the pattern needs recompiling in the first place; this can probably be further optimized via double-checked locking (using atomics), but not doing it right now. This shift makes a couple of pattern options controlling optimization useless, and allows to centralize the 3 QRegularExpression tests (which were actually the very same test, just setting slightly different optimizations strategies). While at it, install a stress-test for threading, with the idea of running it under TSAN or helgrind to catch bugs in QRegularExpression's locking. [ChangeLog][Important Behavior Changes][QRegularExpression] Regular expressions are now automatically optimized (including JIT compiling) on their first usage. The pattern options OptimizeOnFirstUsageOption and DontAutomaticallyOptimizeOption no longer have any effect, and will get removed in a future version of Qt. QRegularExpression::optimize() can be still used to compile and optimize the regular expression in advance (before any match), if needed. Task-number: QTBUG-66781 Change-Id: Ia0e97208ae78255fe811b78029ed01c204e47bd2 Reviewed-by: David Faure <david.faure@kdab.com>
This commit is contained in:
parent
305f57411d
commit
06af9a1e38
@ -43,7 +43,7 @@
|
||||
|
||||
#include <QtCore/qcoreapplication.h>
|
||||
#include <QtCore/qhashfunctions.h>
|
||||
#include <QtCore/qreadwritelock.h>
|
||||
#include <QtCore/qmutex.h>
|
||||
#include <QtCore/qvector.h>
|
||||
#include <QtCore/qstringlist.h>
|
||||
#include <QtCore/qdebug.h>
|
||||
@ -720,21 +720,14 @@ QT_BEGIN_NAMESPACE
|
||||
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.
|
||||
This option is ignored. A regular expression is automatically optimized
|
||||
(including JIT compiling) the first time it is used. This enum value
|
||||
was introduced in Qt 5.4.
|
||||
|
||||
\value DontAutomaticallyOptimizeOption
|
||||
Regular expressions are automatically optimized after a
|
||||
certain number of usages; setting this option prevents such
|
||||
optimizations, therefore avoiding possible unpredictable spikes in
|
||||
CPU and memory usage. If both this option and the
|
||||
\c{OptimizeOnFirstUsageOption} option are set, then this option takes
|
||||
precedence. Note: this option will still let the regular expression
|
||||
to be optimized by manually calling
|
||||
\l{QRegularExpression::}{optimize()}. This enum value has been
|
||||
introduced in Qt 5.4.
|
||||
This option is ignored. A regular expression is automatically optimized
|
||||
(including JIT compiling) the first time it is used. This enum value
|
||||
was introduced in Qt 5.4.
|
||||
*/
|
||||
|
||||
/*!
|
||||
@ -791,14 +784,6 @@ QT_BEGIN_NAMESPACE
|
||||
Qt 5.4.
|
||||
*/
|
||||
|
||||
// after how many usages we optimize the regexp
|
||||
#ifdef QT_BUILD_INTERNAL
|
||||
Q_AUTOTEST_EXPORT unsigned int qt_qregularexpression_optimize_after_use_count = 10;
|
||||
#else
|
||||
static const unsigned int qt_qregularexpression_optimize_after_use_count = 10;
|
||||
#endif // QT_BUILD_INTERNAL
|
||||
|
||||
|
||||
namespace QtPrivate {
|
||||
/*!
|
||||
internal
|
||||
@ -924,13 +909,7 @@ struct QRegularExpressionPrivate : QSharedData
|
||||
void cleanCompiledPattern();
|
||||
void compilePattern();
|
||||
void getPatternInfo();
|
||||
|
||||
enum OptimizePatternOption {
|
||||
LazyOptimizeOption,
|
||||
ImmediateOptimizeOption
|
||||
};
|
||||
|
||||
void optimizePattern(OptimizePatternOption option);
|
||||
void optimizePattern();
|
||||
|
||||
enum CheckSubjectStringOption {
|
||||
CheckSubjectString,
|
||||
@ -955,7 +934,7 @@ struct QRegularExpressionPrivate : QSharedData
|
||||
// *All* of the following members are managed while holding this mutex,
|
||||
// except for isDirty which is set to true by QRegularExpression setters
|
||||
// (right after a detach happened).
|
||||
mutable QReadWriteLock mutex;
|
||||
mutable QMutex mutex;
|
||||
|
||||
// The PCRE code pointer is reference-counted by the QRegularExpressionPrivate
|
||||
// objects themselves; when the private is copied (i.e. a detach happened)
|
||||
@ -964,7 +943,6 @@ struct QRegularExpressionPrivate : QSharedData
|
||||
int errorCode;
|
||||
int errorOffset;
|
||||
int capturingCount;
|
||||
unsigned int usedCount;
|
||||
bool usingCrLfNewlines;
|
||||
bool isDirty;
|
||||
};
|
||||
@ -1033,7 +1011,6 @@ QRegularExpressionPrivate::QRegularExpressionPrivate()
|
||||
errorCode(0),
|
||||
errorOffset(-1),
|
||||
capturingCount(0),
|
||||
usedCount(0),
|
||||
usingCrLfNewlines(false),
|
||||
isDirty(true)
|
||||
{
|
||||
@ -1065,7 +1042,6 @@ QRegularExpressionPrivate::QRegularExpressionPrivate(const QRegularExpressionPri
|
||||
errorCode(0),
|
||||
errorOffset(-1),
|
||||
capturingCount(0),
|
||||
usedCount(0),
|
||||
usingCrLfNewlines(false),
|
||||
isDirty(true)
|
||||
{
|
||||
@ -1081,7 +1057,6 @@ void QRegularExpressionPrivate::cleanCompiledPattern()
|
||||
errorCode = 0;
|
||||
errorOffset = -1;
|
||||
capturingCount = 0;
|
||||
usedCount = 0;
|
||||
usingCrLfNewlines = false;
|
||||
}
|
||||
|
||||
@ -1090,7 +1065,7 @@ void QRegularExpressionPrivate::cleanCompiledPattern()
|
||||
*/
|
||||
void QRegularExpressionPrivate::compilePattern()
|
||||
{
|
||||
const QWriteLocker lock(&mutex);
|
||||
const QMutexLocker lock(&mutex);
|
||||
|
||||
if (!isDirty)
|
||||
return;
|
||||
@ -1117,6 +1092,7 @@ void QRegularExpressionPrivate::compilePattern()
|
||||
errorCode = 0;
|
||||
}
|
||||
|
||||
optimizePattern();
|
||||
getPatternInfo();
|
||||
}
|
||||
|
||||
@ -1217,15 +1193,10 @@ static bool isJitEnabled()
|
||||
The purpose of the function is to call pcre2_jit_compile_16, which
|
||||
JIT-compiles the pattern.
|
||||
|
||||
It gets called by doMatch() every time a match is performed.
|
||||
|
||||
As of now, the optimizations on the pattern are performed after a certain
|
||||
number of usages (i.e. the qt_qregularexpression_optimize_after_use_count
|
||||
constant) unless the DontAutomaticallyOptimizeOption option is set on the
|
||||
QRegularExpression object, or anyhow by calling optimize() (which will pass
|
||||
ImmediateOptimizeOption).
|
||||
It gets called when a pattern is recompiled by us (in compilePattern()),
|
||||
under mutex protection.
|
||||
*/
|
||||
void QRegularExpressionPrivate::optimizePattern(OptimizePatternOption option)
|
||||
void QRegularExpressionPrivate::optimizePattern()
|
||||
{
|
||||
Q_ASSERT(compiledPattern);
|
||||
|
||||
@ -1234,11 +1205,6 @@ void QRegularExpressionPrivate::optimizePattern(OptimizePatternOption option)
|
||||
if (!enableJit)
|
||||
return;
|
||||
|
||||
const QWriteLocker lock(&mutex);
|
||||
|
||||
if ((option == LazyOptimizeOption) && (++usedCount != qt_qregularexpression_optimize_after_use_count))
|
||||
return;
|
||||
|
||||
pcre2_jit_compile_16(compiledPattern, PCRE2_JIT_COMPLETE | PCRE2_JIT_PARTIAL_SOFT | PCRE2_JIT_PARTIAL_HARD);
|
||||
}
|
||||
|
||||
@ -1344,22 +1310,12 @@ QRegularExpressionMatchPrivate *QRegularExpressionPrivate::doMatch(const QString
|
||||
return priv;
|
||||
}
|
||||
|
||||
// skip optimizing and doing the actual matching if NoMatch type was requested
|
||||
// skip doing the actual matching if NoMatch type was requested
|
||||
if (matchType == QRegularExpression::NoMatch) {
|
||||
priv->isValid = true;
|
||||
return priv;
|
||||
}
|
||||
|
||||
if (!(patternOptions & QRegularExpression::DontAutomaticallyOptimizeOption)) {
|
||||
const OptimizePatternOption optimizePatternOption =
|
||||
(patternOptions & QRegularExpression::OptimizeOnFirstUsageOption)
|
||||
? ImmediateOptimizeOption
|
||||
: LazyOptimizeOption;
|
||||
|
||||
// this is mutex protected
|
||||
const_cast<QRegularExpressionPrivate *>(this)->optimizePattern(optimizePatternOption);
|
||||
}
|
||||
|
||||
int pcreOptions = convertToPcreOptions(matchOptions);
|
||||
|
||||
if (matchType == QRegularExpression::PartialPreferCompleteMatch)
|
||||
@ -1384,8 +1340,6 @@ QRegularExpressionMatchPrivate *QRegularExpressionPrivate::doMatch(const QString
|
||||
|
||||
int result;
|
||||
|
||||
QReadLocker lock(&mutex);
|
||||
|
||||
if (!previousMatchWasEmpty) {
|
||||
result = safe_pcre2_match_16(compiledPattern,
|
||||
subjectUtf16, subjectLength,
|
||||
@ -1417,8 +1371,6 @@ QRegularExpressionMatchPrivate *QRegularExpressionPrivate::doMatch(const QString
|
||||
}
|
||||
}
|
||||
|
||||
lock.unlock();
|
||||
|
||||
#ifdef QREGULAREXPRESSION_DEBUG
|
||||
qDebug() << "Matching" << pattern << "against" << subject
|
||||
<< "starting at" << subjectStart << "len" << subjectLength
|
||||
@ -1928,22 +1880,14 @@ QRegularExpressionMatchIterator QRegularExpression::globalMatch(const QStringRef
|
||||
/*!
|
||||
\since 5.4
|
||||
|
||||
Forces an immediate optimization of the pattern, including
|
||||
JIT-compiling it (if the JIT compiler is enabled).
|
||||
Compiles the pattern immediately, including JIT compiling it (if
|
||||
the JIT is enabled) for optimization.
|
||||
|
||||
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
|
||||
\sa isValid(), {Debugging Code that Uses QRegularExpression}
|
||||
*/
|
||||
void QRegularExpression::optimize() const
|
||||
{
|
||||
if (!isValid()) // will compile the pattern
|
||||
return;
|
||||
|
||||
d->optimizePattern(QRegularExpressionPrivate::ImmediateOptimizeOption);
|
||||
d.data()->compilePattern();
|
||||
}
|
||||
|
||||
/*!
|
||||
|
@ -73,8 +73,8 @@ public:
|
||||
InvertedGreedinessOption = 0x0010,
|
||||
DontCaptureOption = 0x0020,
|
||||
UseUnicodePropertiesOption = 0x0040,
|
||||
OptimizeOnFirstUsageOption = 0x0080,
|
||||
DontAutomaticallyOptimizeOption = 0x0100
|
||||
OptimizeOnFirstUsageOption Q_DECL_ENUMERATOR_DEPRECATED_X("This option does not have any effect since Qt 5.12") = 0x0080,
|
||||
DontAutomaticallyOptimizeOption Q_DECL_ENUMERATOR_DEPRECATED_X("This option does not have any effect since Qt 5.12") = 0x0100,
|
||||
};
|
||||
Q_DECLARE_FLAGS(PatternOptions, PatternOption)
|
||||
|
||||
|
@ -1,7 +0,0 @@
|
||||
CONFIG += testcase
|
||||
TARGET = tst_qregularexpression_alwaysoptimize
|
||||
QT = core testlib
|
||||
HEADERS = ../tst_qregularexpression.h
|
||||
SOURCES = \
|
||||
tst_qregularexpression_alwaysoptimize.cpp \
|
||||
../tst_qregularexpression.cpp
|
@ -1,51 +0,0 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2012 Giuseppe D'Angelo <dangelog@gmail.com>.
|
||||
** Contact: https://www.qt.io/licensing/
|
||||
**
|
||||
** This file is part of the test suite of the Qt Toolkit.
|
||||
**
|
||||
** $QT_BEGIN_LICENSE:GPL-EXCEPT$
|
||||
** 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 The Qt Company. For licensing terms
|
||||
** and conditions see https://www.qt.io/terms-conditions. For further
|
||||
** information use the contact form at https://www.qt.io/contact-us.
|
||||
**
|
||||
** GNU General Public License Usage
|
||||
** Alternatively, this file may be used under the terms of the GNU
|
||||
** General Public License version 3 as published by the Free Software
|
||||
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
|
||||
** included in the packaging of this file. Please review the following
|
||||
** information to ensure the GNU General Public License requirements will
|
||||
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
|
||||
**
|
||||
** $QT_END_LICENSE$
|
||||
**
|
||||
****************************************************************************/
|
||||
|
||||
#include <QtTest/QtTest>
|
||||
#include "../tst_qregularexpression.h"
|
||||
|
||||
class tst_QRegularExpression_AlwaysOptimize : public tst_QRegularExpression
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
private slots:
|
||||
void initTestCase();
|
||||
};
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
extern Q_CORE_EXPORT unsigned int qt_qregularexpression_optimize_after_use_count; // from qregularexpression.cpp
|
||||
QT_END_NAMESPACE
|
||||
|
||||
void tst_QRegularExpression_AlwaysOptimize::initTestCase()
|
||||
{
|
||||
qt_qregularexpression_optimize_after_use_count = 1;
|
||||
}
|
||||
|
||||
QTEST_APPLESS_MAIN(tst_QRegularExpression_AlwaysOptimize)
|
||||
|
||||
#include "tst_qregularexpression_alwaysoptimize.moc"
|
@ -1,7 +0,0 @@
|
||||
CONFIG += testcase
|
||||
TARGET = tst_qregularexpression_defaultoptimize
|
||||
QT = core testlib
|
||||
HEADERS = ../tst_qregularexpression.h
|
||||
SOURCES = \
|
||||
tst_qregularexpression_defaultoptimize.cpp \
|
||||
../tst_qregularexpression.cpp
|
@ -1,39 +0,0 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2012 Giuseppe D'Angelo <dangelog@gmail.com>.
|
||||
** Contact: https://www.qt.io/licensing/
|
||||
**
|
||||
** This file is part of the test suite of the Qt Toolkit.
|
||||
**
|
||||
** $QT_BEGIN_LICENSE:GPL-EXCEPT$
|
||||
** 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 The Qt Company. For licensing terms
|
||||
** and conditions see https://www.qt.io/terms-conditions. For further
|
||||
** information use the contact form at https://www.qt.io/contact-us.
|
||||
**
|
||||
** GNU General Public License Usage
|
||||
** Alternatively, this file may be used under the terms of the GNU
|
||||
** General Public License version 3 as published by the Free Software
|
||||
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
|
||||
** included in the packaging of this file. Please review the following
|
||||
** information to ensure the GNU General Public License requirements will
|
||||
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
|
||||
**
|
||||
** $QT_END_LICENSE$
|
||||
**
|
||||
****************************************************************************/
|
||||
|
||||
#include <QtTest/QtTest>
|
||||
#include "../tst_qregularexpression.h"
|
||||
|
||||
class tst_QRegularExpression_DefaultOptimize : public tst_QRegularExpression
|
||||
{
|
||||
Q_OBJECT
|
||||
};
|
||||
|
||||
QTEST_APPLESS_MAIN(tst_QRegularExpression_DefaultOptimize)
|
||||
|
||||
#include "tst_qregularexpression_defaultoptimize.moc"
|
@ -1,8 +0,0 @@
|
||||
CONFIG += testcase
|
||||
TARGET = tst_qregularexpression_forceoptimize
|
||||
QT = core testlib
|
||||
HEADERS = ../tst_qregularexpression.h
|
||||
SOURCES = \
|
||||
tst_qregularexpression_forceoptimize.cpp \
|
||||
../tst_qregularexpression.cpp
|
||||
DEFINES += forceOptimize=true
|
@ -1,39 +0,0 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2014 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com, author Giuseppe D'Angelo <giuseppe.dangelo@kdab.com>
|
||||
** Contact: https://www.qt.io/licensing/
|
||||
**
|
||||
** This file is part of the test suite of the Qt Toolkit.
|
||||
**
|
||||
** $QT_BEGIN_LICENSE:GPL-EXCEPT$
|
||||
** 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 The Qt Company. For licensing terms
|
||||
** and conditions see https://www.qt.io/terms-conditions. For further
|
||||
** information use the contact form at https://www.qt.io/contact-us.
|
||||
**
|
||||
** GNU General Public License Usage
|
||||
** Alternatively, this file may be used under the terms of the GNU
|
||||
** General Public License version 3 as published by the Free Software
|
||||
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
|
||||
** included in the packaging of this file. Please review the following
|
||||
** information to ensure the GNU General Public License requirements will
|
||||
** be met: https://www.gnu.org/licenses/gpl-3.0.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,4 @@
|
||||
TEMPLATE = subdirs
|
||||
SUBDIRS = defaultoptimize forceoptimize
|
||||
qtConfig(private_tests): SUBDIRS += alwaysoptimize
|
||||
CONFIG += testcase
|
||||
TARGET = tst_qregularexpression
|
||||
QT = core testlib
|
||||
SOURCES = tst_qregularexpression.cpp
|
||||
|
@ -33,11 +33,60 @@
|
||||
#include <qstringlist.h>
|
||||
#include <qhash.h>
|
||||
|
||||
#include "tst_qregularexpression.h"
|
||||
#include <qobject.h>
|
||||
#include <qregularexpression.h>
|
||||
#include <qthread.h>
|
||||
|
||||
#ifndef forceOptimize
|
||||
#define forceOptimize false
|
||||
#endif
|
||||
Q_DECLARE_METATYPE(QRegularExpression::PatternOptions)
|
||||
Q_DECLARE_METATYPE(QRegularExpression::MatchType)
|
||||
Q_DECLARE_METATYPE(QRegularExpression::MatchOptions)
|
||||
|
||||
class tst_QRegularExpression : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
private slots:
|
||||
void defaultConstructors();
|
||||
void gettersSetters_data();
|
||||
void gettersSetters();
|
||||
void escape_data();
|
||||
void escape();
|
||||
void validity_data();
|
||||
void validity();
|
||||
void patternOptions_data();
|
||||
void patternOptions();
|
||||
void normalMatch_data();
|
||||
void normalMatch();
|
||||
void partialMatch_data();
|
||||
void partialMatch();
|
||||
void globalMatch_data();
|
||||
void globalMatch();
|
||||
void serialize_data();
|
||||
void serialize();
|
||||
void operatoreq_data();
|
||||
void operatoreq();
|
||||
void captureCount_data();
|
||||
void captureCount();
|
||||
void captureNames_data();
|
||||
void captureNames();
|
||||
void pcreJitStackUsage_data();
|
||||
void pcreJitStackUsage();
|
||||
void regularExpressionMatch_data();
|
||||
void regularExpressionMatch();
|
||||
void JOptionUsage_data();
|
||||
void JOptionUsage();
|
||||
void QStringAndQStringRefEquivalence();
|
||||
void threadSafety_data();
|
||||
void threadSafety();
|
||||
|
||||
void wildcard_data();
|
||||
void wildcard();
|
||||
void testInvalidWildcard_data();
|
||||
void testInvalidWildcard();
|
||||
|
||||
private:
|
||||
void provideRegularExpressions();
|
||||
};
|
||||
|
||||
struct Match
|
||||
{
|
||||
@ -292,9 +341,6 @@ static void testMatch(const QRegularExpression ®exp,
|
||||
QRegularExpression::MatchOptions matchOptions,
|
||||
const Result &result)
|
||||
{
|
||||
if (forceOptimize)
|
||||
regexp.optimize();
|
||||
|
||||
// test with QString as subject type
|
||||
testMatchImpl<QREMatch>(regexp, matchingMethodForString, subject, offset, matchType, matchOptions, result);
|
||||
|
||||
@ -401,30 +447,22 @@ 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);
|
||||
}
|
||||
@ -465,8 +503,6 @@ void tst_QRegularExpression::escape()
|
||||
QFETCH(QString, escaped);
|
||||
QCOMPARE(QRegularExpression::escape(string), escaped);
|
||||
QRegularExpression re(escaped);
|
||||
if (forceOptimize)
|
||||
re.optimize();
|
||||
QCOMPARE(re.isValid(), true);
|
||||
}
|
||||
|
||||
@ -497,8 +533,6 @@ 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");
|
||||
@ -585,9 +619,6 @@ void tst_QRegularExpression::patternOptions()
|
||||
QFETCH(QString, subject);
|
||||
QFETCH(Match, match);
|
||||
|
||||
if (forceOptimize)
|
||||
regexp.optimize();
|
||||
|
||||
QRegularExpressionMatch m = regexp.match(subject);
|
||||
consistencyCheck(m);
|
||||
QVERIFY(m == match);
|
||||
@ -1403,9 +1434,6 @@ void tst_QRegularExpression::serialize()
|
||||
QFETCH(QRegularExpression::PatternOptions, patternOptions);
|
||||
QRegularExpression outRe(pattern, patternOptions);
|
||||
|
||||
if (forceOptimize)
|
||||
outRe.optimize();
|
||||
|
||||
QByteArray buffer;
|
||||
{
|
||||
QDataStream out(&buffer, QIODevice::WriteOnly);
|
||||
@ -1468,33 +1496,18 @@ 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);
|
||||
}
|
||||
}
|
||||
@ -1524,9 +1537,6 @@ 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);
|
||||
@ -1595,9 +1605,6 @@ void tst_QRegularExpression::captureNames()
|
||||
|
||||
QRegularExpression re(pattern);
|
||||
|
||||
if (forceOptimize)
|
||||
re.optimize();
|
||||
|
||||
QStringList namedCaptureGroups = re.namedCaptureGroups();
|
||||
int namedCaptureGroupsCount = namedCaptureGroups.size();
|
||||
|
||||
@ -1633,9 +1640,6 @@ void tst_QRegularExpression::pcreJitStackUsage()
|
||||
|
||||
QRegularExpression re(pattern);
|
||||
|
||||
if (forceOptimize)
|
||||
re.optimize();
|
||||
|
||||
QVERIFY(re.isValid());
|
||||
QRegularExpressionMatch match = re.match(subject);
|
||||
consistencyCheck(match);
|
||||
@ -1663,9 +1667,6 @@ void tst_QRegularExpression::regularExpressionMatch()
|
||||
|
||||
QRegularExpression re(pattern);
|
||||
|
||||
if (forceOptimize)
|
||||
re.optimize();
|
||||
|
||||
QVERIFY(re.isValid());
|
||||
QRegularExpressionMatch match = re.match(subject);
|
||||
consistencyCheck(match);
|
||||
@ -1705,8 +1706,6 @@ 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);
|
||||
}
|
||||
|
||||
@ -2067,6 +2066,92 @@ void tst_QRegularExpression::QStringAndQStringRefEquivalence()
|
||||
}
|
||||
}
|
||||
|
||||
class MatcherThread : public QThread
|
||||
{
|
||||
public:
|
||||
explicit MatcherThread(const QRegularExpression &re, const QString &subject, QObject *parent = nullptr)
|
||||
: QThread(parent),
|
||||
m_re(re),
|
||||
m_subject(subject)
|
||||
{
|
||||
}
|
||||
|
||||
private:
|
||||
static const int MATCH_ITERATIONS = 50;
|
||||
|
||||
void run() override
|
||||
{
|
||||
yieldCurrentThread();
|
||||
for (int i = 0; i < MATCH_ITERATIONS; ++i)
|
||||
m_re.match(m_subject);
|
||||
}
|
||||
|
||||
const QRegularExpression &m_re;
|
||||
const QString &m_subject;
|
||||
};
|
||||
|
||||
void tst_QRegularExpression::threadSafety_data()
|
||||
{
|
||||
QTest::addColumn<QString>("pattern");
|
||||
QTest::addColumn<QString>("subject");
|
||||
|
||||
int i = 0;
|
||||
QTest::addRow("pattern%d", ++i) << "ab.*cd" << "abcd";
|
||||
QTest::addRow("pattern%d", ++i) << "ab.*cd" << "abd";
|
||||
QTest::addRow("pattern%d", ++i) << "ab.*cd" << "abbbbcccd";
|
||||
QTest::addRow("pattern%d", ++i) << "ab.*cd" << "abababcd";
|
||||
QTest::addRow("pattern%d", ++i) << "ab.*cd" << "abcabcd";
|
||||
QTest::addRow("pattern%d", ++i) << "ab.*cd" << "abccccccababd";
|
||||
|
||||
{
|
||||
QString subject(512*1024, QLatin1Char('x'));
|
||||
QTest::addRow("pattern%d", ++i) << "ab.*cd" << subject;
|
||||
}
|
||||
|
||||
{
|
||||
QString subject = "ab";
|
||||
subject.append(QString(512*1024, QLatin1Char('x')));
|
||||
subject.append("c");
|
||||
QTest::addRow("pattern%d", ++i) << "ab.*cd" << subject;
|
||||
}
|
||||
|
||||
{
|
||||
QString subject = "ab";
|
||||
subject.append(QString(512*1024, QLatin1Char('x')));
|
||||
subject.append("cd");
|
||||
QTest::addRow("pattern%d", ++i) << "ab.*cd" << subject;
|
||||
}
|
||||
|
||||
QTest::addRow("pattern%d", ++i) << "(?(R)a*(?1)|((?R))b)" << "aaaabcde";
|
||||
QTest::addRow("pattern%d", ++i) << "(?(R)a*(?1)|((?R))b)" << "aaaaaaabcde";
|
||||
}
|
||||
|
||||
void tst_QRegularExpression::threadSafety()
|
||||
{
|
||||
QFETCH(QString, pattern);
|
||||
QFETCH(QString, subject);
|
||||
|
||||
static const int THREAD_SAFETY_ITERATIONS = 50;
|
||||
|
||||
const int threadCount = qMax(QThread::idealThreadCount(), 4);
|
||||
|
||||
for (int threadSafetyIteration = 0; threadSafetyIteration < THREAD_SAFETY_ITERATIONS; ++threadSafetyIteration) {
|
||||
QRegularExpression re(pattern);
|
||||
|
||||
QVector<MatcherThread *> threads;
|
||||
for (int i = 0; i < threadCount; ++i) {
|
||||
MatcherThread *thread = new MatcherThread(re, subject);
|
||||
thread->start();
|
||||
threads.push_back(thread);
|
||||
}
|
||||
|
||||
for (int i = 0; i < threadCount; ++i)
|
||||
threads[i]->wait();
|
||||
|
||||
qDeleteAll(threads);
|
||||
}
|
||||
}
|
||||
|
||||
void tst_QRegularExpression::wildcard_data()
|
||||
{
|
||||
QTest::addColumn<QString>("pattern");
|
||||
@ -2108,8 +2193,6 @@ void tst_QRegularExpression::wildcard()
|
||||
|
||||
QRegularExpression re;
|
||||
re.setWildcardPattern(pattern);
|
||||
if (forceOptimize)
|
||||
re.optimize();
|
||||
|
||||
QRegularExpressionMatch match = re.match(string);
|
||||
|
||||
@ -2137,9 +2220,11 @@ void tst_QRegularExpression::testInvalidWildcard()
|
||||
|
||||
QRegularExpression re;
|
||||
re.setWildcardPattern(pattern);
|
||||
if (forceOptimize)
|
||||
re.optimize();
|
||||
|
||||
QFETCH(bool, isValid);
|
||||
QCOMPARE(re.isValid(), isValid);
|
||||
}
|
||||
|
||||
QTEST_APPLESS_MAIN(tst_QRegularExpression)
|
||||
|
||||
#include "tst_qregularexpression.moc"
|
||||
|
@ -1,79 +0,0 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2012 Giuseppe D'Angelo <dangelog@gmail.com>.
|
||||
** Contact: https://www.qt.io/licensing/
|
||||
**
|
||||
** This file is part of the test suite of the Qt Toolkit.
|
||||
**
|
||||
** $QT_BEGIN_LICENSE:GPL-EXCEPT$
|
||||
** 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 The Qt Company. For licensing terms
|
||||
** and conditions see https://www.qt.io/terms-conditions. For further
|
||||
** information use the contact form at https://www.qt.io/contact-us.
|
||||
**
|
||||
** GNU General Public License Usage
|
||||
** Alternatively, this file may be used under the terms of the GNU
|
||||
** General Public License version 3 as published by the Free Software
|
||||
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
|
||||
** included in the packaging of this file. Please review the following
|
||||
** information to ensure the GNU General Public License requirements will
|
||||
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
|
||||
**
|
||||
** $QT_END_LICENSE$
|
||||
**
|
||||
****************************************************************************/
|
||||
|
||||
#include <qobject.h>
|
||||
#include <qregularexpression.h>
|
||||
|
||||
Q_DECLARE_METATYPE(QRegularExpression::PatternOptions)
|
||||
Q_DECLARE_METATYPE(QRegularExpression::MatchType)
|
||||
Q_DECLARE_METATYPE(QRegularExpression::MatchOptions)
|
||||
|
||||
class tst_QRegularExpression : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
private slots:
|
||||
void defaultConstructors();
|
||||
void gettersSetters_data();
|
||||
void gettersSetters();
|
||||
void escape_data();
|
||||
void escape();
|
||||
void validity_data();
|
||||
void validity();
|
||||
void patternOptions_data();
|
||||
void patternOptions();
|
||||
void normalMatch_data();
|
||||
void normalMatch();
|
||||
void partialMatch_data();
|
||||
void partialMatch();
|
||||
void globalMatch_data();
|
||||
void globalMatch();
|
||||
void serialize_data();
|
||||
void serialize();
|
||||
void operatoreq_data();
|
||||
void operatoreq();
|
||||
void captureCount_data();
|
||||
void captureCount();
|
||||
void captureNames_data();
|
||||
void captureNames();
|
||||
void pcreJitStackUsage_data();
|
||||
void pcreJitStackUsage();
|
||||
void regularExpressionMatch_data();
|
||||
void regularExpressionMatch();
|
||||
void JOptionUsage_data();
|
||||
void JOptionUsage();
|
||||
void QStringAndQStringRefEquivalence();
|
||||
|
||||
void wildcard_data();
|
||||
void wildcard();
|
||||
void testInvalidWildcard_data();
|
||||
void testInvalidWildcard();
|
||||
|
||||
private:
|
||||
void provideRegularExpressions();
|
||||
};
|
Loading…
Reference in New Issue
Block a user