From 7d4bf142d7a313fcec210edf7fee2850d886421d Mon Sep 17 00:00:00 2001 From: Marc Mutz Date: Mon, 10 Apr 2017 10:17:34 +0200 Subject: QUuid: add fromString(QStringView/QLatin1String) As for the formatting code, de-duplicate the parsing code by only parsing char*s, converting QChars to Latin-1 first in a small buffer. The QUuid(const char*) ctor performed no length checking, relying instead on the checks performed within _q_uuidFromHex(), which includes an implicit check for premature end (because NUL is not a valid token for the parser). The (QString) and (QByteArray) ctors did perform length checking. To the extent possible, this is removed, since it is handled by _q_uuidFromHex(). Failure cases need not be optimized. Only the QLatin1String overload needs to do some checking, because views in general are not NUL-terminated. The QStringView overload can just append a NUL when it converts to Latin-1. The only check I added to _q_uuidFromHex() is that for src == nullptr. It would otherwise be duplicated in several callers. While touching the internal functions, port to passing and returning by value. Saves 1.6KiB in text size on optimized GCC 6.1 Linux AMD64 builds, even though we added new API. Port some users to the new functions. Expand fromString() test. [ChangeLog][QtCore][QUuid] Added fromString(QStringView/QLatin1String). Change-Id: I519339419129550c86e0ea80514865cd6a768f5d Reviewed-by: Anton Kudryavtsev Reviewed-by: Edward Welbourne --- src/corelib/plugin/quuid.cpp | 156 +++++++++++++-------- src/corelib/plugin/quuid.h | 2 + .../platforms/windows/qwindowsdialoghelpers.cpp | 2 +- 3 files changed, 97 insertions(+), 63 deletions(-) (limited to 'src') diff --git a/src/corelib/plugin/quuid.cpp b/src/corelib/plugin/quuid.cpp index 3b010b3ae1..c9644c73d3 100644 --- a/src/corelib/plugin/quuid.cpp +++ b/src/corelib/plugin/quuid.cpp @@ -67,8 +67,8 @@ void _q_toHex(char *&dst, Integral value) } } -template -bool _q_fromHex(const Char *&src, Integral &value) +template +bool _q_fromHex(const char *&src, Integral &value) { value = 0; @@ -102,30 +102,46 @@ static char *_q_uuidToHex(const QUuid &uuid, char *dst) return dst; } -template -bool _q_uuidFromHex(const Char *&src, uint &d1, ushort &d2, ushort &d3, uchar (&d4)[8]) +/*! + \internal + + Parses the string representation of a UUID (with optional surrounding "{}") + by reading at most MaxStringUuidLength (38) characters from \a src, which + may be \c nullptr. Stops at the first invalid character (which includes a + premature NUL). + + Returns the successfully parsed QUuid, or a null QUuid in case of failure. +*/ +Q_NEVER_INLINE +static QUuid _q_uuidFromHex(const char *src) { - if (*src == Char('{')) - src++; - if (!_q_fromHex(src, d1) - || *src++ != Char('-') - || !_q_fromHex(src, d2) - || *src++ != Char('-') - || !_q_fromHex(src, d3) - || *src++ != Char('-') - || !_q_fromHex(src, d4[0]) - || !_q_fromHex(src, d4[1]) - || *src++ != Char('-') - || !_q_fromHex(src, d4[2]) - || !_q_fromHex(src, d4[3]) - || !_q_fromHex(src, d4[4]) - || !_q_fromHex(src, d4[5]) - || !_q_fromHex(src, d4[6]) - || !_q_fromHex(src, d4[7])) { - return false; + uint d1; + ushort d2, d3; + uchar d4[8]; + + if (src) { + if (*src == '{') + src++; + if (Q_LIKELY( _q_fromHex(src, d1) + && *src++ == '-' + && _q_fromHex(src, d2) + && *src++ == '-' + && _q_fromHex(src, d3) + && *src++ == '-' + && _q_fromHex(src, d4[0]) + && _q_fromHex(src, d4[1]) + && *src++ == '-' + && _q_fromHex(src, d4[2]) + && _q_fromHex(src, d4[3]) + && _q_fromHex(src, d4[4]) + && _q_fromHex(src, d4[5]) + && _q_fromHex(src, d4[6]) + && _q_fromHex(src, d4[7]))) { + return QUuid(d1, d2, d3, d4[0], d4[1], d4[2], d4[3], d4[4], d4[5], d4[6], d4[7]); + } } - return true; + return QUuid(); } #ifndef QT_BOOTSTRAPPED @@ -336,7 +352,7 @@ static QUuid createFromName(const QUuid &ns, const QByteArray &baseData, QCrypto /*! Creates a QUuid object from the string \a text, which must be formatted as five hex fields separated by '-', e.g., - "{xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx}" where 'x' is a hex + "{xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx}" where each 'x' is a hex digit. The curly braces shown here are optional, but it is normal to include them. If the conversion fails, a null UUID is created. See toString() for an explanation of how the five hex fields map to the @@ -345,45 +361,76 @@ static QUuid createFromName(const QUuid &ns, const QByteArray &baseData, QCrypto \sa toString(), QUuid() */ QUuid::QUuid(const QString &text) + : QUuid(fromString(text)) { - if (text.length() < 36) { - *this = QUuid(); - return; - } +} + +/*! + \since 5.10 - const ushort *data = reinterpret_cast(text.unicode()); + Creates a QUuid object from the string \a text, which must be + formatted as five hex fields separated by '-', e.g., + "{xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx}" where each 'x' is a hex + digit. The curly braces shown here are optional, but it is normal to + include them. If the conversion fails, a null UUID is returned. See + toString() for an explanation of how the five hex fields map to the + public data members in QUuid. - if (*data == '{' && text.length() < 37) { - *this = QUuid(); - return; - } + \sa toString(), QUuid() +*/ +QUuid QUuid::fromString(QStringView text) Q_DECL_NOTHROW +{ + if (text.size() > MaxStringUuidLength) + text = text.left(MaxStringUuidLength); // text.truncate(MaxStringUuidLength); - if (!_q_uuidFromHex(data, data1, data2, data3, data4)) { - *this = QUuid(); - return; + char latin1[MaxStringUuidLength + 1]; + char *dst = latin1; + + for (QChar ch : text) + *dst++ = ch.toLatin1(); + + *dst++ = '\0'; // don't read garbage as potentially valid data + + return _q_uuidFromHex(latin1); +} + +/*! + \since 5.10 + \overload + + Creates a QUuid object from the string \a text, which must be + formatted as five hex fields separated by '-', e.g., + "{xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx}" where each 'x' is a hex + digit. The curly braces shown here are optional, but it is normal to + include them. If the conversion fails, a null UUID is returned. See + toString() for an explanation of how the five hex fields map to the + public data members in QUuid. + + \sa toString(), QUuid() +*/ +QUuid QUuid::fromString(QLatin1String text) Q_DECL_NOTHROW +{ + if (Q_UNLIKELY(text.size() < MaxStringUuidLength - 2 + || (text.front() == QLatin1Char('{') && text.size() < MaxStringUuidLength - 1))) { + // Too short. Don't call _q_uuidFromHex(); QL1Ss need not be NUL-terminated, + // and we don't want to read trailing garbage as potentially valid data. + text = QLatin1String(); } + return _q_uuidFromHex(text.data()); } /*! \internal */ QUuid::QUuid(const char *text) + : QUuid(_q_uuidFromHex(text)) { - if (!text) { - *this = QUuid(); - return; - } - - if (!_q_uuidFromHex(text, data1, data2, data3, data4)) { - *this = QUuid(); - return; - } } /*! Creates a QUuid object from the QByteArray \a text, which must be formatted as five hex fields separated by '-', e.g., - "{xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx}" where 'x' is a hex + "{xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx}" where each 'x' is a hex digit. The curly braces shown here are optional, but it is normal to include them. If the conversion fails, a null UUID is created. See toByteArray() for an explanation of how the five hex fields map to the @@ -394,23 +441,8 @@ QUuid::QUuid(const char *text) \sa toByteArray(), QUuid() */ QUuid::QUuid(const QByteArray &text) + : QUuid(fromString(QLatin1String(text.data(), text.size()))) { - if (text.length() < 36) { - *this = QUuid(); - return; - } - - const char *data = text.constData(); - - if (*data == '{' && text.length() < 37) { - *this = QUuid(); - return; - } - - if (!_q_uuidFromHex(data, data1, data2, data3, data4)) { - *this = QUuid(); - return; - } } /*! diff --git a/src/corelib/plugin/quuid.h b/src/corelib/plugin/quuid.h index 264f572993..d975528dcf 100644 --- a/src/corelib/plugin/quuid.h +++ b/src/corelib/plugin/quuid.h @@ -116,6 +116,8 @@ public: #endif QUuid(const QString &); + static QUuid fromString(QStringView string) Q_DECL_NOTHROW; + static QUuid fromString(QLatin1String string) Q_DECL_NOTHROW; QUuid(const char *); QString toString() const; QUuid(const QByteArray &); diff --git a/src/plugins/platforms/windows/qwindowsdialoghelpers.cpp b/src/plugins/platforms/windows/qwindowsdialoghelpers.cpp index 0c4f27def2..8a00ed0e40 100644 --- a/src/plugins/platforms/windows/qwindowsdialoghelpers.cpp +++ b/src/plugins/platforms/windows/qwindowsdialoghelpers.cpp @@ -909,7 +909,7 @@ IShellItem *QWindowsNativeFileDialogBase::shellItem(const QUrl &url) // (see https://msdn.microsoft.com/en-us/library/windows/desktop/dd378457(v=vs.85).aspx) // specified as "clsid:" (without '{', '}'). IShellItem *result = Q_NULLPTR; - const QUuid uuid(url.path()); + const auto uuid = QUuid::fromString(url.path()); if (uuid.isNull()) { qWarning() << __FUNCTION__ << ": Invalid CLSID: " << url.path(); return Q_NULLPTR; -- cgit v1.2.3