From ca2be2f499986d37b186353415bee3e98c967343 Mon Sep 17 00:00:00 2001 From: Vladimir Golovnev Date: Sun, 20 Mar 2022 12:26:13 +0300 Subject: [PATCH] Prevent Digest32 shared data from being detached Delayed hash string generation should not cause detaching of shared data. PR #16664. --- src/base/digest32.h | 92 +++++++++++++++++++++++++++++---------------- 1 file changed, 59 insertions(+), 33 deletions(-) diff --git a/src/base/digest32.h b/src/base/digest32.h index 111f82e60..b187f3bb4 100644 --- a/src/base/digest32.h +++ b/src/base/digest32.h @@ -48,10 +48,8 @@ public: Digest32(Digest32 &&other) = default; Digest32(const UnderlyingType &nativeDigest) + : m_dataPtr {new Data(nativeDigest)} { - m_dataPtr->valid = true; - m_dataPtr->nativeDigest = nativeDigest; - m_dataPtr->hashString.clear(); // hashString is created on demand } static constexpr int length() @@ -61,7 +59,7 @@ public: bool isValid() const { - return m_dataPtr->valid; + return m_dataPtr->isValid(); } Digest32 &operator=(const Digest32 &other) = default; @@ -69,48 +67,76 @@ public: operator UnderlyingType() const { - return m_dataPtr->nativeDigest; - } - - static Digest32 fromString(const QString &digestString) - { - if (digestString.size() != (length() * 2)) - return {}; - - const QByteArray raw = QByteArray::fromHex(digestString.toLatin1()); - if (raw.size() != length()) // QByteArray::fromHex() will skip over invalid characters - return {}; - - Digest32 result; - result.m_dataPtr->valid = true; - result.m_dataPtr->hashString = digestString; - result.m_dataPtr->nativeDigest.assign(raw.constData()); - - return result; + return m_dataPtr->nativeDigest(); } QString toString() const { - if (m_dataPtr->hashString.isEmpty()) - { - const QByteArray raw = QByteArray::fromRawData(m_dataPtr->nativeDigest.data(), length()); - const_cast(this)->m_dataPtr->hashString = QString::fromLatin1(raw.toHex()); - } + return m_dataPtr->hashString(); + } - return m_dataPtr->hashString; + static Digest32 fromString(const QString &digestString) + { + return Digest32(QSharedDataPointer(new Data(digestString))); } private: - struct Data : public QSharedData + class Data; + + explicit Digest32(QSharedDataPointer dataPtr) + : m_dataPtr {dataPtr} { - bool valid = false; - UnderlyingType nativeDigest; - QString hashString; - }; + } QSharedDataPointer m_dataPtr {new Data}; }; +template +class Digest32::Data : public QSharedData +{ +public: + Data() = default; + + explicit Data(UnderlyingType nativeDigest) + : m_isValid {true} + , m_nativeDigest {nativeDigest} + { + } + + explicit Data(const QString &digestString) + { + if (digestString.size() != (length() * 2)) + return; + + const QByteArray raw = QByteArray::fromHex(digestString.toLatin1()); + if (raw.size() != length()) // QByteArray::fromHex() will skip over invalid characters + return; + + m_isValid = true; + m_hashString = digestString; + m_nativeDigest.assign(raw.constData()); + } + + bool isValid() const { return m_isValid; } + UnderlyingType nativeDigest() const { return m_nativeDigest; } + + QString hashString() const + { + if (m_hashString.isEmpty()) + { + const QByteArray raw = QByteArray::fromRawData(m_nativeDigest.data(), length()); + m_hashString = QString::fromLatin1(raw.toHex()); + } + + return m_hashString; + } + +private: + bool m_isValid = false; + UnderlyingType m_nativeDigest; + mutable QString m_hashString; +}; + template bool operator==(const Digest32 &left, const Digest32 &right) {