diff options
Diffstat (limited to 'src/corelib/text')
-rw-r--r-- | src/corelib/text/qbytearray.cpp | 269 | ||||
-rw-r--r-- | src/corelib/text/qbytearray.h | 63 | ||||
-rw-r--r-- | src/corelib/text/qlocale.cpp | 8 | ||||
-rw-r--r-- | src/corelib/text/qlocale_tools.cpp | 2 | ||||
-rw-r--r-- | src/corelib/text/qregularexpression.cpp | 14 | ||||
-rw-r--r-- | src/corelib/text/qtextboundaryfinder.cpp | 14 |
6 files changed, 312 insertions, 58 deletions
diff --git a/src/corelib/text/qbytearray.cpp b/src/corelib/text/qbytearray.cpp index 3f5fef80e2..83182dbd93 100644 --- a/src/corelib/text/qbytearray.cpp +++ b/src/corelib/text/qbytearray.cpp @@ -2,6 +2,7 @@ ** ** Copyright (C) 2019 The Qt Company Ltd. ** Copyright (C) 2016 Intel Corporation. +** Copyright (C) 2019 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com, author Giuseppe D'Angelo <giuseppe.dangelo@kdab.com> ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the QtCore module of the Qt Toolkit. @@ -41,6 +42,7 @@ #include "qbytearray.h" #include "qbytearraymatcher.h" #include "private/qtools_p.h" +#include "qhashfunctions.h" #include "qstring.h" #include "qlist.h" #include "qlocale.h" @@ -986,10 +988,20 @@ QByteArray qUncompress(const uchar* data, int nbytes) four. \value OmitTrailingEquals Omits adding the padding equal signs at the end of the encoded data. - - QByteArray::fromBase64() ignores the KeepTrailingEquals and - OmitTrailingEquals options and will not flag errors in case they are - missing or if there are too many of them. + \value IgnoreBase64DecodingErrors When decoding Base64-encoded data, ignores errors + in the input; invalid characters are simply skipped. + This enum value has been added in Qt 5.15. + \value AbortOnBase64DecodingErrors When decoding Base64-encoded data, stops at the first + decoding error. + This enum value has been added in Qt 5.15. + + QByteArray::fromBase64Encoding() and QByteArray::fromBase64() + ignore the KeepTrailingEquals and OmitTrailingEquals options. If + the IgnoreBase64DecodingErrors option is specified, they will not + flag errors in case trailing equal signs are missing or if there + are too many of them. If instead the AbortOnBase64DecodingErrors is + specified, then the input must either have no padding or have the + correct amount of equal signs. */ /*! \fn QByteArray::iterator QByteArray::begin() @@ -4411,63 +4423,166 @@ QByteArray &QByteArray::setRawData(const char *data, uint size) return *this; } -/*! - \since 5.2 - - Returns a decoded copy of the Base64 array \a base64, using the alphabet - defined by \a options. Input is not checked for validity; invalid - characters in the input are skipped, enabling the decoding process to - continue with subsequent characters. - - For example: - - \snippet code/src_corelib_tools_qbytearray.cpp 44 - - The algorithm used to decode Base64-encoded data is defined in \l{RFC 4648}. +namespace { +struct fromBase64_helper_result { + qsizetype decodedLength; + QByteArray::Base64DecodingStatus status; +}; - \sa toBase64() -*/ -QByteArray QByteArray::fromBase64(const QByteArray &base64, Base64Options options) +fromBase64_helper_result fromBase64_helper(const char *input, qsizetype inputSize, + char *output /* may alias input */, + QByteArray::Base64Options options) { + fromBase64_helper_result result{ 0, QByteArray::Base64DecodingStatus::Ok }; + unsigned int buf = 0; int nbits = 0; - QByteArray tmp((base64.size() * 3) / 4, Qt::Uninitialized); - int offset = 0; - for (int i = 0; i < base64.size(); ++i) { - int ch = base64.at(i); + qsizetype offset = 0; + for (qsizetype i = 0; i < inputSize; ++i) { + int ch = input[i]; int d; - if (ch >= 'A' && ch <= 'Z') + if (ch >= 'A' && ch <= 'Z') { d = ch - 'A'; - else if (ch >= 'a' && ch <= 'z') + } else if (ch >= 'a' && ch <= 'z') { d = ch - 'a' + 26; - else if (ch >= '0' && ch <= '9') + } else if (ch >= '0' && ch <= '9') { d = ch - '0' + 52; - else if (ch == '+' && (options & Base64UrlEncoding) == 0) + } else if (ch == '+' && (options & QByteArray::Base64UrlEncoding) == 0) { d = 62; - else if (ch == '-' && (options & Base64UrlEncoding) != 0) + } else if (ch == '-' && (options & QByteArray::Base64UrlEncoding) != 0) { d = 62; - else if (ch == '/' && (options & Base64UrlEncoding) == 0) + } else if (ch == '/' && (options & QByteArray::Base64UrlEncoding) == 0) { d = 63; - else if (ch == '_' && (options & Base64UrlEncoding) != 0) + } else if (ch == '_' && (options & QByteArray::Base64UrlEncoding) != 0) { d = 63; - else - d = -1; + } else { + if (options & QByteArray::AbortOnBase64DecodingErrors) { + if (ch == '=') { + // can have 1 or 2 '=' signs, in both cases padding base64Size to + // a multiple of 4. Any other case is illegal. + if ((inputSize % 4) != 0) { + result.status = QByteArray::Base64DecodingStatus::IllegalInputLength; + return result; + } else if ((i == inputSize - 1) || + (i == inputSize - 2 && input[++i] == '=')) { + d = -1; // ... and exit the loop, normally + } else { + result.status = QByteArray::Base64DecodingStatus::IllegalPadding; + return result; + } + } else { + result.status = QByteArray::Base64DecodingStatus::IllegalCharacter; + return result; + } + } else { + d = -1; + } + } if (d != -1) { buf = (buf << 6) | d; nbits += 6; if (nbits >= 8) { nbits -= 8; - tmp[offset++] = buf >> nbits; + Q_ASSERT(offset < i); + output[offset++] = buf >> nbits; buf &= (1 << nbits) - 1; } } } - tmp.truncate(offset); - return tmp; + result.decodedLength = offset; + return result; +} +} // anonymous namespace + +/*! + \fn QByteArray::FromBase64Result QByteArray::fromBase64Encoding(QByteArray &&base64, Base64Options options) + \fn QByteArray::FromBase64Result QByteArray::fromBase64Encoding(const QByteArray &base64, Base64Options options) + \since 5.15 + \overload + + Decodes the Base64 array \a base64, using the options + defined by \a options. If \a options contains \c{IgnoreBase64DecodingErrors} + (the default), the input is not checked for validity; invalid + characters in the input are skipped, enabling the decoding process to + continue with subsequent characters. If \a options contains + \c{AbortOnBase64DecodingErrors}, then decoding will stop at the first + invalid character. + + For example: + + \snippet code/src_corelib_tools_qbytearray.cpp 44ter + + The algorithm used to decode Base64-encoded data is defined in \l{RFC 4648}. + + Returns a QByteArrayFromBase64Result object, containing the decoded + data and a flag telling whether decoding was successful. If the + \c{AbortOnBase64DecodingErrors} option was passed and the input + data was invalid, it is unspecified what the decoded data contains. + + \sa toBase64() +*/ +QByteArray::FromBase64Result QByteArray::fromBase64Encoding(QByteArray &&base64, Base64Options options) +{ + // try to avoid a detach when calling data(), as it would over-allocate + // (we need less space when decoding than the one required by the full copy) + if (base64.isDetached()) { + const auto base64result = fromBase64_helper(base64.data(), + base64.size(), + base64.data(), // in-place + options); + base64.truncate(int(base64result.decodedLength)); + return { std::move(base64), base64result.status }; + } + + return fromBase64Encoding(base64, options); +} + + +QByteArray::FromBase64Result QByteArray::fromBase64Encoding(const QByteArray &base64, Base64Options options) +{ + const auto base64Size = base64.size(); + QByteArray result((base64Size * 3) / 4, Qt::Uninitialized); + const auto base64result = fromBase64_helper(base64.data(), + base64Size, + const_cast<char *>(result.constData()), + options); + result.truncate(int(base64result.decodedLength)); + return { std::move(result), base64result.status }; +} + +/*! + \since 5.2 + + Returns a decoded copy of the Base64 array \a base64, using the options + defined by \a options. If \a options contains \c{IgnoreBase64DecodingErrors} + (the default), the input is not checked for validity; invalid + characters in the input are skipped, enabling the decoding process to + continue with subsequent characters. If \a options contains + \c{AbortOnBase64DecodingErrors}, then decoding will stop at the first + invalid character. + + For example: + + \snippet code/src_corelib_tools_qbytearray.cpp 44 + + The algorithm used to decode Base64-encoded data is defined in \l{RFC 4648}. + + Returns the decoded data, or, if the \c{AbortOnBase64DecodingErrors} + option was passed and the input data was invalid, an empty byte array. + + \note The fromBase64Encoding() function is recommended in new code. + + \sa toBase64(), fromBase64Encoding() +*/ +QByteArray QByteArray::fromBase64(const QByteArray &base64, Base64Options options) +{ + if (auto result = fromBase64Encoding(base64, options)) + return std::move(result.decoded); + return QByteArray(); } /*! @@ -4838,4 +4953,86 @@ QByteArray QByteArray::toPercentEncoding(const QByteArray &exclude, const QByteA \sa QStringLiteral */ +/*! + \class QByteArray::FromBase64Result + \inmodule QtCore + \ingroup tools + \since 5.15 + + \brief The QByteArray::FromBase64Result class holds the result of + a call to QByteArray::fromBase64Encoding. + + Objects of this class can be used to check whether the conversion + was successful, and if so, retrieve the decoded QByteArray. The + conversion operators defined for QByteArray::FromBase64Result make + its usage straightforward: + + \snippet code/src_corelib_tools_qbytearray.cpp 44ter + + In alternative, it is possible to access the conversion status + and the decoded data directly: + + \snippet code/src_corelib_tools_qbytearray.cpp 44quater + + \sa QByteArray::fromBase64 +*/ + +/*! + \variable QByteArray::FromBase64Result::decoded + + Contains the decoded byte array. +*/ + +/*! + \variable QByteArray::FromBase64Result::decodingStatus + + Contains whether the decoding was successful, expressed as a value + of type QByteArray::Base64DecodingStatus. +*/ + +/*! + \fn QByteArray::FromBase64Result::operator bool() const + + Returns whether the decoding was successful. This is equivalent + to checking whether the \c{decodingStatus} member is equal to + QByteArray::Base64DecodingStatus::Ok. +*/ + +/*! + \fn QByteArray::FromBase64Result::operator QByteArray() const + + Returns the decoded byte array. +*/ + +/*! + \fn bool operator==(const QByteArray::FromBase64Result &lhs, const QByteArray::FromBase64Result &rhs) noexcept + \relates QByteArray::FromBase64Result + + Compares \a lhs and \a rhs for equality. \a lhs and \a rhs are equal + if and only if they contain the same decoding status and, if the + status is QByteArray::Base64DecodingStatus::Ok, if and only if + they contain the same decoded data. +*/ + +/*! + \fn bool operator!=(const QByteArray::FromBase64Result &lhs, const QByteArray::FromBase64Result &rhs) noexcept + \relates QByteArray::FromBase64Result + + Compares \a lhs and \a rhs for inequality. +*/ + +/*! + \relates QByteArray::FromBase64Result + + Returns the hash value for \a key, using + \a seed to seed the calculation. +*/ +uint qHash(const QByteArray::FromBase64Result &key, uint seed) noexcept +{ + QtPrivate::QHashCombine hash; + seed = hash(seed, key.decoded); + seed = hash(seed, static_cast<int>(key.decodingStatus)); + return seed; +} + QT_END_NAMESPACE diff --git a/src/corelib/text/qbytearray.h b/src/corelib/text/qbytearray.h index af8e4da36d..fa87ebf058 100644 --- a/src/corelib/text/qbytearray.h +++ b/src/corelib/text/qbytearray.h @@ -145,10 +145,20 @@ public: Base64UrlEncoding = 1, KeepTrailingEquals = 0, - OmitTrailingEquals = 2 + OmitTrailingEquals = 2, + + IgnoreBase64DecodingErrors = 0, + AbortOnBase64DecodingErrors = 4, }; Q_DECLARE_FLAGS(Base64Options, Base64Option) + enum class Base64DecodingStatus { + Ok, + IllegalInputLength, + IllegalCharacter, + IllegalPadding, + }; + inline QByteArray() noexcept; QByteArray(const char *, int size = -1); QByteArray(int size, char c); @@ -356,8 +366,11 @@ public: Q_REQUIRED_RESULT static QByteArray number(qulonglong, int base = 10); Q_REQUIRED_RESULT static QByteArray number(double, char f = 'g', int prec = 6); Q_REQUIRED_RESULT static QByteArray fromRawData(const char *, int size); - Q_REQUIRED_RESULT static QByteArray fromBase64(const QByteArray &base64, - Base64Options options = Base64Encoding); + + class FromBase64Result; + Q_REQUIRED_RESULT static FromBase64Result fromBase64Encoding(QByteArray &&base64, Base64Options options = Base64Encoding); + Q_REQUIRED_RESULT static FromBase64Result fromBase64Encoding(const QByteArray &base64, Base64Options options = Base64Encoding); + Q_REQUIRED_RESULT static QByteArray fromBase64(const QByteArray &base64, Base64Options options = Base64Encoding); Q_REQUIRED_RESULT static QByteArray fromHex(const QByteArray &hexEncoded); Q_REQUIRED_RESULT static QByteArray fromPercentEncoding(const QByteArray &pctEncoded, char percent = '%'); @@ -642,6 +655,50 @@ inline QByteArray qUncompress(const QByteArray& data) Q_DECLARE_SHARED(QByteArray) +class QByteArray::FromBase64Result +{ +public: + QByteArray decoded; + QByteArray::Base64DecodingStatus decodingStatus; + + void swap(QByteArray::FromBase64Result &other) noexcept + { + qSwap(decoded, other.decoded); + qSwap(decodingStatus, other.decodingStatus); + } + + explicit operator bool() const noexcept { return decodingStatus == QByteArray::Base64DecodingStatus::Ok; } + +#if defined(Q_COMPILER_REF_QUALIFIERS) && !defined(Q_QDOC) + QByteArray &operator*() & noexcept { return decoded; } + const QByteArray &operator*() const & noexcept { return decoded; } + QByteArray &&operator*() && noexcept { return std::move(decoded); } +#else + QByteArray &operator*() noexcept { return decoded; } + const QByteArray &operator*() const noexcept { return decoded; } +#endif +}; + +Q_DECLARE_SHARED(QByteArray::FromBase64Result) + +inline bool operator==(const QByteArray::FromBase64Result &lhs, const QByteArray::FromBase64Result &rhs) noexcept +{ + if (lhs.decodingStatus != rhs.decodingStatus) + return false; + + if (lhs.decodingStatus == QByteArray::Base64DecodingStatus::Ok && lhs.decoded != rhs.decoded) + return false; + + return true; +} + +inline bool operator!=(const QByteArray::FromBase64Result &lhs, const QByteArray::FromBase64Result &rhs) noexcept +{ + return !operator==(lhs, rhs); +} + +Q_CORE_EXPORT Q_DECL_PURE_FUNCTION uint qHash(const QByteArray::FromBase64Result &key, uint seed = 0) noexcept; + QT_END_NAMESPACE #endif // QBYTEARRAY_H diff --git a/src/corelib/text/qlocale.cpp b/src/corelib/text/qlocale.cpp index 75a5bc802e..694d491273 100644 --- a/src/corelib/text/qlocale.cpp +++ b/src/corelib/text/qlocale.cpp @@ -79,7 +79,7 @@ QT_BEGIN_NAMESPACE #ifndef QT_NO_SYSTEMLOCALE -static QSystemLocale *_systemLocale = 0; +static QSystemLocale *_systemLocale = nullptr; class QSystemLocaleSingleton: public QSystemLocale { public: @@ -695,7 +695,7 @@ QSystemLocale::QSystemLocale(bool) QSystemLocale::~QSystemLocale() { if (_systemLocale == this) { - _systemLocale = 0; + _systemLocale = nullptr; globalLocaleData.m_language_id = 0; } @@ -2429,7 +2429,7 @@ QTime QLocale::toTime(const QString &string, const QString &format, QCalendar ca QDateTimeParser dt(QVariant::Time, QDateTimeParser::FromString, cal); dt.setDefaultLocale(*this); if (dt.parseFormat(format)) - dt.fromString(string, 0, &time); + dt.fromString(string, nullptr, &time); #else Q_UNUSED(cal); Q_UNUSED(string); @@ -2468,7 +2468,7 @@ QDate QLocale::toDate(const QString &string, const QString &format, QCalendar ca QDateTimeParser dt(QVariant::Date, QDateTimeParser::FromString, cal); dt.setDefaultLocale(*this); if (dt.parseFormat(format)) - dt.fromString(string, &date, 0); + dt.fromString(string, &date, nullptr); #else Q_UNUSED(string); Q_UNUSED(format); diff --git a/src/corelib/text/qlocale_tools.cpp b/src/corelib/text/qlocale_tools.cpp index c246028b4d..0da769d694 100644 --- a/src/corelib/text/qlocale_tools.cpp +++ b/src/corelib/text/qlocale_tools.cpp @@ -322,7 +322,7 @@ double qt_asciiToDouble(const char *num, int numLen, bool &ok, int &processed, conv_flags = double_conversion::StringToDoubleConverter::ALLOW_LEADING_SPACES | double_conversion::StringToDoubleConverter::ALLOW_TRAILING_SPACES; } - double_conversion::StringToDoubleConverter conv(conv_flags, 0.0, qt_qnan(), 0, 0); + double_conversion::StringToDoubleConverter conv(conv_flags, 0.0, qt_qnan(), nullptr, nullptr); d = conv.StringToDouble(num, numLen, &processed); if (!qIsFinite(d)) { diff --git a/src/corelib/text/qregularexpression.cpp b/src/corelib/text/qregularexpression.cpp index d0bdc0d45c..e05bef450b 100644 --- a/src/corelib/text/qregularexpression.cpp +++ b/src/corelib/text/qregularexpression.cpp @@ -831,7 +831,7 @@ struct QRegularExpressionPrivate : QSharedData QRegularExpression::MatchType matchType, QRegularExpression::MatchOptions matchOptions, CheckSubjectStringOption checkSubjectStringOption = CheckSubjectString, - const QRegularExpressionMatchPrivate *previous = 0) const; + const QRegularExpressionMatchPrivate *previous = nullptr) const; int captureIndexForName(QStringView name) const; @@ -990,7 +990,7 @@ void QRegularExpressionPrivate::compilePattern() options, &errorCode, &patternErrorOffset, - NULL); + nullptr); if (!compiledPattern) { errorOffset = static_cast<int>(patternErrorOffset); @@ -1049,7 +1049,7 @@ public: { // The default JIT stack size in PCRE is 32K, // we allocate from 32K up to 512K. - stack = pcre2_jit_stack_create_16(32 * 1024, 512 * 1024, NULL); + stack = pcre2_jit_stack_create_16(32 * 1024, 512 * 1024, nullptr); } /*! \internal @@ -1073,7 +1073,7 @@ static pcre2_jit_stack_16 *qtPcreCallback(void *) if (jitStacks()->hasLocalData()) return jitStacks()->localData()->stack; - return 0; + return nullptr; } /*! @@ -1240,9 +1240,9 @@ QRegularExpressionMatchPrivate *QRegularExpressionPrivate::doMatch(const QString previousMatchWasEmpty = true; } - pcre2_match_context_16 *matchContext = pcre2_match_context_create_16(NULL); - pcre2_jit_stack_assign_16(matchContext, &qtPcreCallback, NULL); - pcre2_match_data_16 *matchData = pcre2_match_data_create_from_pattern_16(compiledPattern, NULL); + pcre2_match_context_16 *matchContext = pcre2_match_context_create_16(nullptr); + pcre2_jit_stack_assign_16(matchContext, &qtPcreCallback, nullptr); + pcre2_match_data_16 *matchData = pcre2_match_data_create_from_pattern_16(compiledPattern, nullptr); const unsigned short * const subjectUtf16 = subject.utf16() + subjectStart; diff --git a/src/corelib/text/qtextboundaryfinder.cpp b/src/corelib/text/qtextboundaryfinder.cpp index 070b041220..ebdba6b2c5 100644 --- a/src/corelib/text/qtextboundaryfinder.cpp +++ b/src/corelib/text/qtextboundaryfinder.cpp @@ -161,10 +161,10 @@ static void init(QTextBoundaryFinder::BoundaryType type, const QChar *chars, int */ QTextBoundaryFinder::QTextBoundaryFinder() : t(Grapheme) - , chars(0) + , chars(nullptr) , length(0) , freePrivate(true) - , d(0) + , d(nullptr) { } @@ -178,7 +178,7 @@ QTextBoundaryFinder::QTextBoundaryFinder(const QTextBoundaryFinder &other) , length(other.length) , pos(other.pos) , freePrivate(true) - , d(0) + , d(nullptr) { if (other.d) { Q_ASSERT(length > 0); @@ -199,7 +199,7 @@ QTextBoundaryFinder &QTextBoundaryFinder::operator=(const QTextBoundaryFinder &o if (other.d) { Q_ASSERT(other.length > 0); uint newCapacity = (other.length + 1) * sizeof(QCharAttributes); - QTextBoundaryFinderPrivate *newD = (QTextBoundaryFinderPrivate *) realloc(freePrivate ? d : 0, newCapacity); + QTextBoundaryFinderPrivate *newD = (QTextBoundaryFinderPrivate *) realloc(freePrivate ? d : nullptr, newCapacity); Q_CHECK_PTR(newD); freePrivate = true; d = newD; @@ -216,7 +216,7 @@ QTextBoundaryFinder &QTextBoundaryFinder::operator=(const QTextBoundaryFinder &o } else { if (freePrivate) free(d); - d = 0; + d = nullptr; } return *this; @@ -242,7 +242,7 @@ QTextBoundaryFinder::QTextBoundaryFinder(BoundaryType type, const QString &strin , length(string.length()) , pos(0) , freePrivate(true) - , d(0) + , d(nullptr) { if (length > 0) { d = (QTextBoundaryFinderPrivate *) malloc((length + 1) * sizeof(QCharAttributes)); @@ -271,7 +271,7 @@ QTextBoundaryFinder::QTextBoundaryFinder(BoundaryType type, const QChar *chars, , length(length) , pos(0) , freePrivate(true) - , d(0) + , d(nullptr) { if (!chars) { length = 0; |