diff --git a/src/corelib/text/qbytearray.cpp b/src/corelib/text/qbytearray.cpp index 901e0fcbc1..30d5b65fce 100644 --- a/src/corelib/text/qbytearray.cpp +++ b/src/corelib/text/qbytearray.cpp @@ -3514,7 +3514,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)) { @@ -3522,16 +3522,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)) { @@ -3539,13 +3540,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 {}; } /*! @@ -3800,14 +3802,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 {}; } /*! @@ -3840,9 +3843,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 03acaa50d8..57d4711fdb 100644 --- a/src/corelib/text/qbytearrayview.h +++ b/src/corelib/text/qbytearrayview.h @@ -261,9 +261,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); }