Add getter and setter for qt_qhash_seed
In some cases it's not possible to use QT_HASH_SEED, specially when we need to set the environment variable from inside the application, as dynamically loaded libraries or plugins may create static QHash instances. That would set qt_qhash_seed to a value different from -1 and skip the env var value. For those cases, and when we still want to set qt_qhash_seed, we provide a way to enforce its value. Auto-tests accessing qt_qhash_seed directly have been updated accordingly. Usage in qdoc, uic and rcc has been left as is for the time being. Change-Id: I3b35b4fa0223c83b1348a6508641905a2a63266f Reviewed-by: Lars Knoll <lars.knoll@theqtcompany.com>bb10
parent
863dfb1541
commit
a14fadfb64
|
|
@ -290,6 +290,53 @@ static void qt_initialize_qhash_seed()
|
|||
}
|
||||
}
|
||||
|
||||
/*! \relates QHash
|
||||
\since 5.6
|
||||
|
||||
Returns the current global QHash seed.
|
||||
|
||||
The seed is set in any newly created QHash. See \l{qHash} about how this seed
|
||||
is being used by QHash.
|
||||
|
||||
\sa qSetGlobalQHashSeed
|
||||
*/
|
||||
int qGlobalQHashSeed()
|
||||
{
|
||||
return qt_qhash_seed.load();
|
||||
}
|
||||
|
||||
/*! \relates QHash
|
||||
\since 5.6
|
||||
|
||||
Sets the global QHash seed.
|
||||
|
||||
Manually setting the global QHash seed value should be done only for testing
|
||||
and debugging purposes, when deterministic and reproducible behavior on a QHash
|
||||
is needed. We discourage to do it in production code as it can make your
|
||||
application susceptible to \l{algorithmic complexity attacks}.
|
||||
|
||||
The seed is set in any newly created QHash. See \l{qHash} about how this seed
|
||||
is being used by QHash.
|
||||
|
||||
If the environment variable \c QT_HASH_SEED is set, calling this function will
|
||||
result in a no-op.
|
||||
|
||||
Passing the value -1 will reinitialize the global QHash seed to a random value.
|
||||
|
||||
\sa qGlobalQHashSeed
|
||||
*/
|
||||
void qSetGlobalQHashSeed(int newSeed)
|
||||
{
|
||||
if (qEnvironmentVariableIsSet("QT_HASH_SEED"))
|
||||
return;
|
||||
if (newSeed == -1) {
|
||||
int x(qt_create_qhash_seed() & INT_MAX);
|
||||
qt_qhash_seed.store(x);
|
||||
} else {
|
||||
qt_qhash_seed.store(newSeed & INT_MAX);
|
||||
}
|
||||
}
|
||||
|
||||
/*!
|
||||
\internal
|
||||
|
||||
|
|
@ -1132,7 +1179,8 @@ uint qHash(long double key, uint seed) Q_DECL_NOTHROW
|
|||
where you temporarily need deterministic behavior, for example for debugging or
|
||||
regression testing. To disable the randomization, define the environment
|
||||
variable \c QT_HASH_SEED. The contents of that variable, interpreted as a
|
||||
decimal value, will be used as the seed for qHash().
|
||||
decimal value, will be used as the seed for qHash(). Alternatively, you can
|
||||
call the qSetGlobalQHashSeed() function.
|
||||
|
||||
\sa QHashIterator, QMutableHashIterator, QMap, QSet
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -1037,6 +1037,9 @@ Q_INLINE_TEMPLATE int QMultiHash<Key, T>::count(const Key &key, const T &value)
|
|||
return n;
|
||||
}
|
||||
|
||||
Q_CORE_EXPORT int qGlobalQHashSeed();
|
||||
Q_CORE_EXPORT void qSetGlobalQHashSeed(int newSeed);
|
||||
|
||||
Q_DECLARE_ASSOCIATIVE_ITERATOR(Hash)
|
||||
Q_DECLARE_MUTABLE_ASSOCIATIVE_ITERATOR(Hash)
|
||||
|
||||
|
|
|
|||
|
|
@ -50,6 +50,8 @@ private Q_SLOTS:
|
|||
void qthash();
|
||||
void range();
|
||||
void rangeCommutative();
|
||||
|
||||
void setGlobalQHashSeed();
|
||||
};
|
||||
|
||||
void tst_QHashFunctions::qhash()
|
||||
|
|
@ -207,5 +209,21 @@ void tst_QHashFunctions::rangeCommutative()
|
|||
(void)qHashRangeCommutative(hashables, hashables + numHashables);
|
||||
}
|
||||
|
||||
void tst_QHashFunctions::setGlobalQHashSeed()
|
||||
{
|
||||
// Setter works as advertised
|
||||
qSetGlobalQHashSeed(0x10101010);
|
||||
QCOMPARE(qGlobalQHashSeed(), 0x10101010);
|
||||
|
||||
// Creating a new QHash doesn't reset the seed
|
||||
QHash<QString, int> someHash;
|
||||
someHash.insert("foo", 42);
|
||||
QCOMPARE(qGlobalQHashSeed(), 0x10101010);
|
||||
|
||||
// Reset works as advertised
|
||||
qSetGlobalQHashSeed(-1);
|
||||
QVERIFY(qGlobalQHashSeed() != -1);
|
||||
}
|
||||
|
||||
QTEST_APPLESS_MAIN(tst_QHashFunctions)
|
||||
#include "tst_qhashfunctions.moc"
|
||||
|
|
|
|||
|
|
@ -970,24 +970,6 @@ void tst_QSet::initializerList()
|
|||
#endif
|
||||
}
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
extern Q_CORE_EXPORT QBasicAtomicInt qt_qhash_seed; // from qhash.cpp
|
||||
QT_END_NAMESPACE
|
||||
|
||||
class QtQHashSeedSaver {
|
||||
int oldSeed, newSeed;
|
||||
public:
|
||||
explicit QtQHashSeedSaver(int seed)
|
||||
: oldSeed(qt_qhash_seed.fetchAndStoreRelaxed(seed)),
|
||||
newSeed(seed)
|
||||
{}
|
||||
~QtQHashSeedSaver()
|
||||
{
|
||||
// only restore when no-one else changed the seed in the meantime:
|
||||
qt_qhash_seed.testAndSetRelaxed(newSeed, oldSeed);
|
||||
}
|
||||
};
|
||||
|
||||
void tst_QSet::qhash()
|
||||
{
|
||||
//
|
||||
|
|
@ -995,14 +977,14 @@ void tst_QSet::qhash()
|
|||
//
|
||||
{
|
||||
// create some deterministic initial state:
|
||||
const QtQHashSeedSaver seed1(0);
|
||||
qSetGlobalQHashSeed(0);
|
||||
|
||||
QSet<int> s1;
|
||||
s1.reserve(4);
|
||||
s1 << 400 << 300 << 200 << 100;
|
||||
|
||||
// also change the seed:
|
||||
const QtQHashSeedSaver seed2(0x10101010);
|
||||
qSetGlobalQHashSeed(0x10101010);
|
||||
|
||||
QSet<int> s2;
|
||||
s2.reserve(100); // provoke different bucket counts
|
||||
|
|
@ -1049,7 +1031,7 @@ void tst_QSet::intersects()
|
|||
s1 << 200;
|
||||
QVERIFY(s1.intersects(s2));
|
||||
|
||||
const QtQHashSeedSaver seedSaver(0x10101010);
|
||||
qSetGlobalQHashSeed(0x10101010);
|
||||
QSet<int> s3;
|
||||
s3 << 500;
|
||||
QVERIFY(!s1.intersects(s3));
|
||||
|
|
|
|||
|
|
@ -64,15 +64,10 @@ private slots:
|
|||
void properties();
|
||||
};
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
// Avoid QHash randomization so that the order of the XML attributes is stable
|
||||
extern Q_CORE_EXPORT QBasicAtomicInt qt_qhash_seed; // from qhash.cpp
|
||||
QT_END_NAMESPACE
|
||||
|
||||
void tst_QDBusXmlParser::initTestCase()
|
||||
{
|
||||
// Always initialize the hash seed to 0 to get reliable test results
|
||||
qt_qhash_seed.store(0);
|
||||
qSetGlobalQHashSeed(0);
|
||||
}
|
||||
|
||||
void tst_QDBusXmlParser::parsing_data()
|
||||
|
|
|
|||
|
|
@ -259,13 +259,9 @@ void tst_Lancelot::paint(QPaintDevice *device, GraphicsEngine engine, const QStr
|
|||
QTEST_MAIN(tst_Lancelot)
|
||||
#undef main
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
extern Q_DECL_IMPORT QBasicAtomicInt qt_qhash_seed; // from qhash.cpp
|
||||
QT_END_NAMESPACE
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
qt_qhash_seed.store(0); // Avoid rendering variations caused by QHash randomization
|
||||
qSetGlobalQHashSeed(0); // Avoid rendering variations caused by QHash randomization
|
||||
|
||||
QBaselineTest::handleCmdLineArgs(&argc, &argv);
|
||||
return _realmain(argc, argv);
|
||||
|
|
|
|||
Loading…
Reference in New Issue