diff options
author | Marc Mutz <[email protected]> | 2024-05-17 15:29:12 +0200 |
---|---|---|
committer | Marc Mutz <[email protected]> | 2024-05-29 14:24:29 +0200 |
commit | c70c81b371993ca865d523bb5f37eac4eb8a972b (patch) | |
tree | a1d1b0012a27947a0ffc7e44e7ad81344f4c5f29 | |
parent | 39189190fee872d2e6fcaca0f5c10d0ba6a70ea6 (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.cpp | 31 | ||||
-rw-r--r-- | src/corelib/tools/qcryptographichash.h | 8 | ||||
-rw-r--r-- | tests/auto/corelib/tools/qcryptographichash/tst_qcryptographichash.cpp | 6 |
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); } |