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:
Giuseppe D'Angelo 2018-06-19 21:25:02 +02:00
parent 305f57411d
commit 06af9a1e38
11 changed files with 168 additions and 368 deletions

View File

@ -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();
}
/*!

View File

@ -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)

View File

@ -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

View File

@ -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"

View File

@ -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

View File

@ -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"

View File

@ -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

View File

@ -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"

View File

@ -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

View File

@ -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 &regexp,
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"

View File

@ -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();
};