QCryptographicHash: don't present the same data over and over again
Need to decrement 'remaining' (check), but also increment data (meep). Testing is a bit complicated, as most algorithms are just too slow to fit into the 5min QTestLib timeout. Picked the fast ones and Sha512 (which completes here in < 17s, with threads), at least. Amends e12577b56396cca0df05f88f8787706a3a12c82d. [ChangeLog][QtCore][QCryptographicHash] Fixed a bug where presenting more than 4GiB in a single addData() call would calculate the wrong result(). Change-Id: Ic72916ebc33ba087d58225af6d8240e46e41f434 Reviewed-by: Lars Knoll <lars.knoll@qt.io> (cherry picked from commit 35453446a511366e1250858b249e36c80b6ad044) Reviewed-by: Mårten Nordheim <marten.nordheim@qt.io>
This commit is contained in:
parent
b26f39b946
commit
3fa9c19481
@ -363,10 +363,8 @@ void QCryptographicHash::addData(const char *data, qsizetype length)
|
|||||||
// feed the data UINT_MAX bytes at a time, as some of the methods below
|
// feed the data UINT_MAX bytes at a time, as some of the methods below
|
||||||
// take a uint (of course, feeding more than 4G of data into the hashing
|
// take a uint (of course, feeding more than 4G of data into the hashing
|
||||||
// functions will be pretty slow anyway)
|
// functions will be pretty slow anyway)
|
||||||
qsizetype remaining = length;
|
for (auto remaining = length; remaining; remaining -= length, data += length) {
|
||||||
while (remaining) {
|
|
||||||
length = qMin(qsizetype(std::numeric_limits<uint>::max()), remaining);
|
length = qMin(qsizetype(std::numeric_limits<uint>::max()), remaining);
|
||||||
remaining -= length;
|
|
||||||
#else
|
#else
|
||||||
{
|
{
|
||||||
#endif
|
#endif
|
||||||
|
@ -29,9 +29,14 @@
|
|||||||
|
|
||||||
#include <QtCore/QCoreApplication>
|
#include <QtCore/QCoreApplication>
|
||||||
#include <QTest>
|
#include <QTest>
|
||||||
|
#include <QScopeGuard>
|
||||||
#include <QCryptographicHash>
|
#include <QCryptographicHash>
|
||||||
#include <QtCore/QMetaEnum>
|
#include <QtCore/QMetaEnum>
|
||||||
|
|
||||||
|
#if QT_CONFIG(cxx11_future)
|
||||||
|
# include <thread>
|
||||||
|
#endif
|
||||||
|
|
||||||
Q_DECLARE_METATYPE(QCryptographicHash::Algorithm)
|
Q_DECLARE_METATYPE(QCryptographicHash::Algorithm)
|
||||||
|
|
||||||
class tst_QCryptographicHash : public QObject
|
class tst_QCryptographicHash : public QObject
|
||||||
@ -51,6 +56,11 @@ private slots:
|
|||||||
void files();
|
void files();
|
||||||
void hashLength_data();
|
void hashLength_data();
|
||||||
void hashLength();
|
void hashLength();
|
||||||
|
// keep last
|
||||||
|
void moreThan4GiBOfData_data();
|
||||||
|
void moreThan4GiBOfData();
|
||||||
|
private:
|
||||||
|
std::vector<char> large;
|
||||||
};
|
};
|
||||||
|
|
||||||
void tst_QCryptographicHash::repeated_result_data()
|
void tst_QCryptographicHash::repeated_result_data()
|
||||||
@ -419,5 +429,79 @@ void tst_QCryptographicHash::hashLength()
|
|||||||
QCOMPARE(QCryptographicHash::hashLength(algorithm), output.length());
|
QCOMPARE(QCryptographicHash::hashLength(algorithm), output.length());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void tst_QCryptographicHash::moreThan4GiBOfData_data()
|
||||||
|
{
|
||||||
|
#if QT_POINTER_SIZE > 4
|
||||||
|
QElapsedTimer timer;
|
||||||
|
timer.start();
|
||||||
|
const size_t GiB = 1024 * 1024 * 1024;
|
||||||
|
try {
|
||||||
|
large.resize(4 * GiB + 1, '\0');
|
||||||
|
} catch (const std::bad_alloc &) {
|
||||||
|
QSKIP("Could not allocate 4GiB plus one byte of RAM.");
|
||||||
|
}
|
||||||
|
QCOMPARE(large.size(), 4 * GiB + 1);
|
||||||
|
large.back() = '\1';
|
||||||
|
qDebug("created dataset in %lld ms", timer.elapsed());
|
||||||
|
|
||||||
|
QTest::addColumn<QCryptographicHash::Algorithm>("algorithm");
|
||||||
|
auto me = QMetaEnum::fromType<QCryptographicHash::Algorithm>();
|
||||||
|
auto row = [me] (QCryptographicHash::Algorithm algo) {
|
||||||
|
QTest::addRow("%s", me.valueToKey(int(algo))) << algo;
|
||||||
|
};
|
||||||
|
// these are reasonably fast (O(secs))
|
||||||
|
row(QCryptographicHash::Md4);
|
||||||
|
row(QCryptographicHash::Md5);
|
||||||
|
row(QCryptographicHash::Sha1);
|
||||||
|
// this is already significantly slower, but important (O(min)
|
||||||
|
row(QCryptographicHash::Sha512);
|
||||||
|
// the rest is just too slow
|
||||||
|
#else
|
||||||
|
QSKIP("This test is 64-bit only.");
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
void tst_QCryptographicHash::moreThan4GiBOfData()
|
||||||
|
{
|
||||||
|
QFETCH(const QCryptographicHash::Algorithm, algorithm);
|
||||||
|
|
||||||
|
# if QT_CONFIG(cxx11_future)
|
||||||
|
using MaybeThread = std::thread;
|
||||||
|
# else
|
||||||
|
struct MaybeThread {
|
||||||
|
std::function<void()> func;
|
||||||
|
void join() { func(); }
|
||||||
|
};
|
||||||
|
# endif
|
||||||
|
|
||||||
|
QElapsedTimer timer;
|
||||||
|
timer.start();
|
||||||
|
const auto sg = qScopeGuard([&] {
|
||||||
|
qDebug() << algorithm << "test finished in" << timer.restart() << "ms";
|
||||||
|
});
|
||||||
|
|
||||||
|
const auto begin = large.data();
|
||||||
|
const auto mid = begin + large.size() / 2;
|
||||||
|
const auto end = begin + large.size();
|
||||||
|
|
||||||
|
QByteArray single;
|
||||||
|
QByteArray chunked;
|
||||||
|
|
||||||
|
auto t = MaybeThread{[&] {
|
||||||
|
QCryptographicHash h(algorithm);
|
||||||
|
h.addData(begin, end - begin);
|
||||||
|
single = h.result();
|
||||||
|
}};
|
||||||
|
{
|
||||||
|
QCryptographicHash h(algorithm);
|
||||||
|
h.addData(begin, mid - begin);
|
||||||
|
h.addData(mid, end - mid);
|
||||||
|
chunked = h.result();
|
||||||
|
}
|
||||||
|
t.join();
|
||||||
|
|
||||||
|
QCOMPARE(single, chunked);
|
||||||
|
}
|
||||||
|
|
||||||
QTEST_MAIN(tst_QCryptographicHash)
|
QTEST_MAIN(tst_QCryptographicHash)
|
||||||
#include "tst_qcryptographichash.moc"
|
#include "tst_qcryptographichash.moc"
|
||||||
|
Loading…
x
Reference in New Issue
Block a user