summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMarc Mutz <[email protected]>2024-05-17 15:29:12 +0200
committerMarc Mutz <[email protected]>2024-05-29 14:24:29 +0200
commitc70c81b371993ca865d523bb5f37eac4eb8a972b (patch)
treea1d1b0012a27947a0ffc7e44e7ad81344f4c5f29
parent39189190fee872d2e6fcaca0f5c10d0ba6a70ea6 (diff)
Long live QCryptographicHash::hashInto()!
Until now, QCryptographicHash had a big drawback over the underlying C APIs: at least one extra memory allocation: Either you created a QCryptographicHash object (allocates a Private), and enjoy noexcept addData() and resultView(), or you used the static hash() function (which creates a Private on the stack, so doesn't need to allocate one), but then we needed to return in an owning container. Enter QSpan and hashInto(), which allow the user to provide a buffer into which to write, and therefore can be completely noexcept and (apart from a missing optimization which, however, doesn't affect the API) deliver near C-API efficiency. We don't have QMutableByteArrayView, which would deal with the different types of equivalent char types for us, so we overload for QSpan<std::bytes>, QSpan<char> and QSpan<uchar>, which makes the overload set accept roughly what QByteArrayView would accept, too. Return by QByteArrayView because that has a richer API than QSpan, which lacks (on purpose) string-ish API. [ChangeLog][QtCore][QCryptographicHash] Added hashInto(). Task-number: QTBUG-125431 Change-Id: I80ecc7151d3418a36c4d5db6d22d0b82c869b19f Reviewed-by: Mårten Nordheim <[email protected]>
-rw-r--r--src/corelib/tools/qcryptographichash.cpp31
-rw-r--r--src/corelib/tools/qcryptographichash.h8
-rw-r--r--tests/auto/corelib/tools/qcryptographichash/tst_qcryptographichash.cpp6
3 files changed, 44 insertions, 1 deletions
diff --git a/src/corelib/tools/qcryptographichash.cpp b/src/corelib/tools/qcryptographichash.cpp
index 8baec8ecc8b..3807e32a723 100644
--- a/src/corelib/tools/qcryptographichash.cpp
+++ b/src/corelib/tools/qcryptographichash.cpp
@@ -1153,13 +1153,42 @@ void QCryptographicHashPrivate::State::finalizeUnchecked(QCryptographicHash::Alg
\note In Qt versions prior to 6.3, this function took QByteArray,
not QByteArrayView.
+
+ \sa hashInto()
*/
QByteArray QCryptographicHash::hash(QByteArrayView data, Algorithm method)
{
+ QByteArray ba(hashLengthInternal(method), Qt::Uninitialized);
+ [[maybe_unused]] const auto r = hashInto(ba, data, method);
+ Q_ASSERT(r.size() == ba.size());
+ return ba;
+}
+
+/*!
+ \since 6.8
+ \fn QCryptographicHash::hashInto(QSpan<char> buffer, QByteArrayView data, Algorithm method);
+ \fn QCryptographicHash::hashInto(QSpan<uchar> buffer, QByteArrayView data, Algorithm method);
+ \fn QCryptographicHash::hashInto(QSpan<std::byte> buffer, QByteArrayView data, Algorithm method);
+
+ Returns the hash of \a data using \a method, using \a buffer to store the result.
+
+ The return value will be a sub-span of \a buffer, unless \a buffer is of
+ insufficient size, in which case a null QByteArrayView is returned.
+
+ \sa hash()
+*/
+QByteArrayView QCryptographicHash::hashInto(QSpan<std::byte> buffer, QByteArrayView data,
+ Algorithm method) noexcept
+{
QCryptographicHashPrivate hash(method);
hash.addData(data);
hash.finalizeUnchecked(); // no mutex needed: no-one but us has access to 'hash'
- return hash.resultView().toByteArray();
+ auto result = hash.resultView();
+ if (buffer.size() < result.size())
+ return {}; // buffer too small
+ // ### optimize: have the method directly write into `buffer`
+ memcpy(buffer.data(), result.data(), result.size());
+ return buffer.first(result.size());
}
/*!
diff --git a/src/corelib/tools/qcryptographichash.h b/src/corelib/tools/qcryptographichash.h
index 294453adceb..3d91c1f917c 100644
--- a/src/corelib/tools/qcryptographichash.h
+++ b/src/corelib/tools/qcryptographichash.h
@@ -8,6 +8,7 @@
#include <QtCore/qbytearray.h>
#include <QtCore/qobjectdefs.h>
+#include <QtCore/qspan.h>
QT_BEGIN_NAMESPACE
@@ -91,6 +92,13 @@ public:
static QByteArray hash(const QByteArray &data, Algorithm method);
#endif
static QByteArray hash(QByteArrayView data, Algorithm method);
+
+ static QByteArrayView hashInto(QSpan<char> buffer, QByteArrayView data, Algorithm method) noexcept
+ { return hashInto(as_writable_bytes(buffer), data, method); }
+ static QByteArrayView hashInto(QSpan<uchar> buffer, QByteArrayView data, Algorithm method) noexcept
+ { return hashInto(as_writable_bytes(buffer), data, method); }
+ static QByteArrayView hashInto(QSpan<std::byte> buffer, QByteArrayView data, Algorithm method) noexcept;
+
static int hashLength(Algorithm method);
static bool supportsAlgorithm(Algorithm method);
private:
diff --git a/tests/auto/corelib/tools/qcryptographichash/tst_qcryptographichash.cpp b/tests/auto/corelib/tools/qcryptographichash/tst_qcryptographichash.cpp
index fe1a9b828af..47e0ead2708 100644
--- a/tests/auto/corelib/tools/qcryptographichash/tst_qcryptographichash.cpp
+++ b/tests/auto/corelib/tools/qcryptographichash/tst_qcryptographichash.cpp
@@ -209,6 +209,9 @@ void tst_QCryptographicHash::static_hash()
const auto _algo = QCryptographicHash::Algorithm(algo);
QCOMPARE(QCryptographicHash::hash(first, _algo), hash_first);
+
+ std::byte buffer[1024];
+ QCOMPARE(QCryptographicHash::hashInto(buffer, first, _algo), hash_first);
}
@@ -511,6 +514,9 @@ void tst_QCryptographicHash::hashLength()
expectedSize = 0;
} else {
expectedSize = QCryptographicHash::hash("test", algorithm).size();
+
+ std::byte buffer[1024];
+ QCOMPARE(QCryptographicHash::hashInto(buffer, "foo", algorithm).size(), expectedSize);
}
QCOMPARE(QCryptographicHash::hashLength(algorithm), expectedSize);
}