From 91e5b279e9b67eb3c70c2fdabf11e6ffa20bfbe8 Mon Sep 17 00:00:00 2001 From: Marc Mutz Date: Tue, 22 Feb 2022 16:06:08 +0100 Subject: Prepare for QByteArrayView number parsing modernization MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Remove the unholy bool out parameter and make QtPrivate::to{Double, Float,{Signed,Unsigned}Integer}() return a struct instead. The struct contains what we'll most likely need for a full QParsedNumber in the future: the value, an error code (always zero atm), and a pointer to the first character that wasn't parsed (always nullptr atm), so we don't need to change the ABI when QParsedNumber eventually lands. As an immediate positive contribution, even without the backend ported away from bool out parameters, the functions can now be marked as PURE and, in case of the FP versions, also noexcept (the int versions have a narrow contract d/t the base argument, which, unlike the return value, can be fixed later, by overloading). Change-Id: I67945af80a9b53d6f170502a6df3384895e82d3e Reviewed-by: Sona Kurazyan Reviewed-by: MÃ¥rten Nordheim Reviewed-by: Qt CI Bot Reviewed-by: Thiago Macieira (cherry picked from commit 1775029c9b34dde6c5d5ca2d15b25556ad7bc7c8) Reviewed-by: Qt Cherry-pick Bot --- src/corelib/text/qbytearray.cpp | 49 +++++++++++++++++++-------------- src/corelib/text/qbytearrayalgorithms.h | 47 ++++++++++++++++++++++--------- src/corelib/text/qbytearrayview.h | 14 ++++++++-- 3 files changed, 75 insertions(+), 35 deletions(-) diff --git a/src/corelib/text/qbytearray.cpp b/src/corelib/text/qbytearray.cpp index 4b2896433c..ce86f8df83 100644 --- a/src/corelib/text/qbytearray.cpp +++ b/src/corelib/text/qbytearray.cpp @@ -3528,7 +3528,7 @@ bool QByteArray::isNull() const noexcept return d->isNull(); } -qlonglong QtPrivate::toSignedInteger(QByteArrayView data, bool *ok, int base) +auto QtPrivate::toSignedInteger(QByteArrayView data, int base) -> ParsedNumber { #if defined(QT_CHECK_RANGE) if (base != 0 && (base < 2 || base > 36)) { @@ -3536,16 +3536,17 @@ qlonglong QtPrivate::toSignedInteger(QByteArrayView data, bool *ok, int base) base = 10; } #endif - if (data.isEmpty()) { - if (ok) - *ok = false; - return 0; - } + if (data.isEmpty()) + return {}; - return QLocaleData::bytearrayToLongLong(data, base, ok); + bool ok = false; + const auto i = QLocaleData::bytearrayToLongLong(data, base, &ok); + if (ok) + return ParsedNumber(i); + return {}; } -qulonglong QtPrivate::toUnsignedInteger(QByteArrayView data, bool *ok, int base) +auto QtPrivate::toUnsignedInteger(QByteArrayView data, int base) -> ParsedNumber { #if defined(QT_CHECK_RANGE) if (base != 0 && (base < 2 || base > 36)) { @@ -3553,13 +3554,14 @@ qulonglong QtPrivate::toUnsignedInteger(QByteArrayView data, bool *ok, int base) base = 10; } #endif - if (data.isEmpty()) { - if (ok) - *ok = false; - return 0; - } + if (data.isEmpty()) + return {}; - return QLocaleData::bytearrayToUnsLongLong(data, base, ok); + bool ok = false; + const auto u = QLocaleData::bytearrayToUnsLongLong(data, base, &ok); + if (ok) + return ParsedNumber(u); + return {}; } /*! @@ -3814,14 +3816,15 @@ double QByteArray::toDouble(bool *ok) const return QByteArrayView(*this).toDouble(ok); } -double QtPrivate::toDouble(QByteArrayView a, bool *ok) +auto QtPrivate::toDouble(QByteArrayView a) noexcept -> ParsedNumber { bool nonNullOk = false; int processed = 0; double d = qt_asciiToDouble(a.data(), a.size(), nonNullOk, processed, WhitespacesAllowed); - if (ok) - *ok = nonNullOk; - return d; + if (nonNullOk) + return ParsedNumber{d}; + else + return {}; } /*! @@ -3854,9 +3857,15 @@ float QByteArray::toFloat(bool *ok) const return QLocaleData::convertDoubleToFloat(toDouble(ok), ok); } -float QtPrivate::toFloat(QByteArrayView a, bool *ok) +auto QtPrivate::toFloat(QByteArrayView a) noexcept -> ParsedNumber { - return QLocaleData::convertDoubleToFloat(a.toDouble(ok), ok); + if (const auto r = toDouble(a)) { + bool ok = true; + const auto f = QLocaleData::convertDoubleToFloat(*r, &ok); + if (ok) + return ParsedNumber(f); + } + return {}; } /*! diff --git a/src/corelib/text/qbytearrayalgorithms.h b/src/corelib/text/qbytearrayalgorithms.h index f80b97e04e..164868fa91 100644 --- a/src/corelib/text/qbytearrayalgorithms.h +++ b/src/corelib/text/qbytearrayalgorithms.h @@ -76,10 +76,31 @@ qsizetype count(QByteArrayView haystack, QByteArrayView needle) noexcept; [[nodiscard]] Q_CORE_EXPORT Q_DECL_PURE_FUNCTION bool isValidUtf8(QByteArrayView s) noexcept; -[[nodiscard]] Q_CORE_EXPORT double toDouble(QByteArrayView a, bool *ok); -[[nodiscard]] Q_CORE_EXPORT float toFloat(QByteArrayView a, bool *ok); -[[nodiscard]] Q_CORE_EXPORT qlonglong toSignedInteger(QByteArrayView data, bool *ok, int base); -[[nodiscard]] Q_CORE_EXPORT qulonglong toUnsignedInteger(QByteArrayView data, bool *ok, int base); +template +class ParsedNumber +{ + T m_value; + quint32 m_error : 1; + quint32 m_reserved : 31; + void *m_reserved2 = nullptr; +public: + constexpr ParsedNumber() noexcept : m_value(), m_error(true), m_reserved(0) {} + constexpr explicit ParsedNumber(T v) : m_value(v), m_error(false), m_reserved(0) {} + + // minimal optional-like API: + explicit operator bool() const noexcept { return !m_error; } + T &operator*() { Q_ASSERT(*this); return m_value; } + const T &operator*() const { Q_ASSERT(*this); return m_value; } + T *operator->() noexcept { return *this ? &m_value : nullptr; } + const T *operator->() const noexcept { return *this ? &m_value : nullptr; } + template // not = T, as that'd allow calls that are incompatible with std::optional + T value_or(U &&u) const { return *this ? m_value : T(std::forward(u)); } +}; + +[[nodiscard]] Q_CORE_EXPORT Q_DECL_PURE_FUNCTION ParsedNumber toDouble(QByteArrayView a) noexcept; +[[nodiscard]] Q_CORE_EXPORT Q_DECL_PURE_FUNCTION ParsedNumber toFloat(QByteArrayView a) noexcept; +[[nodiscard]] Q_CORE_EXPORT Q_DECL_PURE_FUNCTION ParsedNumber toSignedInteger(QByteArrayView data, int base); +[[nodiscard]] Q_CORE_EXPORT Q_DECL_PURE_FUNCTION ParsedNumber toUnsignedInteger(QByteArrayView data, int base); // QByteArrayView has incomplete type here, and we can't include qbytearrayview.h, // since it includes qbytearrayalgorithms.h. Use the ByteArrayView template type as @@ -88,18 +109,18 @@ template >> static inline T toIntegral(ByteArrayView data, bool *ok, int base) { - auto val = [&] { + const auto val = [&] { if constexpr (std::is_unsigned_v) - return toUnsignedInteger(data, ok, base); + return toUnsignedInteger(data, base); else - return toSignedInteger(data, ok, base); + return toSignedInteger(data, base); }(); - if (T(val) != val) { - if (ok) - *ok = false; - val = 0; - } - return T(val); + const bool failed = !val || T(*val) != *val; + if (ok) + *ok = !failed; + if (failed) + return 0; + return T(*val); } } // namespace QtPrivate diff --git a/src/corelib/text/qbytearrayview.h b/src/corelib/text/qbytearrayview.h index 40205cae9c..612dbc3e82 100644 --- a/src/corelib/text/qbytearrayview.h +++ b/src/corelib/text/qbytearrayview.h @@ -260,9 +260,19 @@ public: [[nodiscard]] qulonglong toULongLong(bool *ok = nullptr, int base = 10) const { return QtPrivate::toIntegral(*this, ok, base); } [[nodiscard]] float toFloat(bool *ok = nullptr) const - { return QtPrivate::toFloat(*this, ok); } + { + const auto r = QtPrivate::toFloat(*this); + if (ok) + *ok = bool(r); + return r.value_or(0.0f); + } [[nodiscard]] double toDouble(bool *ok = nullptr) const - { return QtPrivate::toDouble(*this, ok); } + { + const auto r = QtPrivate::toDouble(*this); + if (ok) + *ok = bool(r); + return r.value_or(0.0); + } [[nodiscard]] bool startsWith(QByteArrayView other) const noexcept { return QtPrivate::startsWith(*this, other); } -- cgit v1.2.3