diff options
-rw-r--r-- | qmake/generators/win32/msvc_vcproj.cpp | 2 | ||||
-rw-r--r-- | src/corelib/plugin/quuid.cpp | 156 | ||||
-rw-r--r-- | src/corelib/plugin/quuid.h | 2 | ||||
-rw-r--r-- | src/plugins/platforms/windows/qwindowsdialoghelpers.cpp | 2 | ||||
-rw-r--r-- | tests/auto/corelib/plugin/quuid/tst_quuid.cpp | 56 | ||||
-rw-r--r-- | tests/auto/corelib/plugin/quuid/tst_quuid_darwin.mm | 12 |
6 files changed, 154 insertions, 76 deletions
diff --git a/qmake/generators/win32/msvc_vcproj.cpp b/qmake/generators/win32/msvc_vcproj.cpp index 48b8bea812..dfbb4c69ea 100644 --- a/qmake/generators/win32/msvc_vcproj.cpp +++ b/qmake/generators/win32/msvc_vcproj.cpp @@ -223,7 +223,7 @@ QUuid VcprojGenerator::getProjectUUID(const QString &filename) bool validUUID = true; // Read GUID from variable-space - QUuid uuid = project->first("GUID").toQString(); + auto uuid = QUuid::fromString(project->first("GUID").toQStringView()); // If none, create one based on the MD5 of absolute project path if(uuid.isNull() || !filename.isEmpty()) { 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 <class Char, class Integral> -bool _q_fromHex(const Char *&src, Integral &value) +template <class Integral> +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 <class Char> -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<const ushort *>(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:<GUID>" (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; diff --git a/tests/auto/corelib/plugin/quuid/tst_quuid.cpp b/tests/auto/corelib/plugin/quuid/tst_quuid.cpp index d3102c7ee5..9e3edc96ec 100644 --- a/tests/auto/corelib/plugin/quuid/tst_quuid.cpp +++ b/tests/auto/corelib/plugin/quuid/tst_quuid.cpp @@ -41,6 +41,7 @@ private slots: void fromChar(); void toString(); + void fromString_data(); void fromString(); void toByteArray(); void fromByteArray(); @@ -127,15 +128,58 @@ void tst_QUuid::toString() QCOMPARE(uuidB.toString(), QString("{1ab6e93a-b1cb-4a87-ba47-ec7e99039a7b}")); } +void tst_QUuid::fromString_data() +{ + QTest::addColumn<QUuid>("expected"); + QTest::addColumn<QString>("input"); + + QUuid invalid = {}; + +#define ROW(which, string) \ + QTest::addRow("%-38s -> %s", string, #which) << which << string + ROW(uuidA, "{fc69b59e-cc34-4436-a43c-ee95d128b8c5}"); + ROW(uuidA, "fc69b59e-cc34-4436-a43c-ee95d128b8c5}"); + ROW(uuidA, "{fc69b59e-cc34-4436-a43c-ee95d128b8c5" ); + ROW(uuidA, "fc69b59e-cc34-4436-a43c-ee95d128b8c5" ); + + ROW(uuidA, "{fc69b59e-cc34-4436-a43c-ee95d128b8c56"); // too long (not an error!) + ROW(invalid, "{fc69b59e-cc34-4436-a43c-ee95d128b8c" ); // premature end (within length limits) + ROW(invalid, " fc69b59e-cc34-4436-a43c-ee95d128b8c5}"); // leading space + ROW(uuidA, "{fc69b59e-cc34-4436-a43c-ee95d128b8c5 "); // trailing space (not an error!) + ROW(invalid, "{gc69b59e-cc34-4436-a43c-ee95d128b8c5}"); // non-hex digit in 1st group + ROW(invalid, "{fc69b59e-cp34-4436-a43c-ee95d128b8c5}"); // non-hex digit in 2nd group + ROW(invalid, "{fc69b59e-cc34-44r6-a43c-ee95d128b8c5}"); // non-hex digit in 3rd group + ROW(invalid, "{fc69b59e-cc34-4436-a4yc-ee95d128b8c5}"); // non-hex digit in 4th group + ROW(invalid, "{fc69b59e-cc34-4436-a43c-ee95d128j8c5}"); // non-hex digit in last group + ROW(invalid, "(fc69b59e-cc34-4436-a43c-ee95d128b8c5}"); // wrong initial character + ROW(invalid, "{fc69b59e+cc34-4436-a43c-ee95d128b8c5}"); // wrong 1st separator + ROW(invalid, "{fc69b59e-cc34*4436-a43c-ee95d128b8c5}"); // wrong 2nd separator + ROW(invalid, "{fc69b59e-cc34-44366a43c-ee95d128b8c5}"); // wrong 3rd separator + ROW(invalid, "{fc69b59e-cc34-4436-a43c\303\244ee95d128b8c5}"); // wrong 4th separator (ä) + ROW(uuidA, "{fc69b59e-cc34-4436-a43c-ee95d128b8c5)"); // wrong final character (not an error!) + + ROW(uuidB, "{1ab6e93a-b1cb-4a87-ba47-ec7e99039a7b}"); +#undef ROW +} + void tst_QUuid::fromString() { - QCOMPARE(uuidA, QUuid(QString("{fc69b59e-cc34-4436-a43c-ee95d128b8c5}"))); - QCOMPARE(uuidA, QUuid(QString("fc69b59e-cc34-4436-a43c-ee95d128b8c5}"))); - QCOMPARE(uuidA, QUuid(QString("{fc69b59e-cc34-4436-a43c-ee95d128b8c5"))); - QCOMPARE(uuidA, QUuid(QString("fc69b59e-cc34-4436-a43c-ee95d128b8c5"))); - QCOMPARE(QUuid(), QUuid(QString("{fc69b59e-cc34-4436-a43c-ee95d128b8c"))); + QFETCH(const QUuid, expected); + QFETCH(const QString, input); - QCOMPARE(uuidB, QUuid(QString("{1ab6e93a-b1cb-4a87-ba47-ec7e99039a7b}"))); + const auto inputL1 = input.toLatin1(); + const auto inputU8 = input.toUtf8(); + + QCOMPARE(expected, QUuid(input)); + QCOMPARE(expected, QUuid(inputU8)); + QCOMPARE(expected, QUuid(inputL1)); + + QCOMPARE(expected, QUuid::fromString(input)); + + // for QLatin1String, construct one whose data() is not NUL-terminated: + const auto longerInputL1 = inputL1 + '5'; // the '5' makes the premature end check incorrectly succeed + const auto inputL1S = QLatin1String(longerInputL1.data(), inputL1.size()); + QCOMPARE(expected, QUuid::fromString(inputL1S)); } void tst_QUuid::toByteArray() diff --git a/tests/auto/corelib/plugin/quuid/tst_quuid_darwin.mm b/tests/auto/corelib/plugin/quuid/tst_quuid_darwin.mm index d90bff65b3..41ccece115 100644 --- a/tests/auto/corelib/plugin/quuid/tst_quuid_darwin.mm +++ b/tests/auto/corelib/plugin/quuid/tst_quuid_darwin.mm @@ -36,7 +36,7 @@ void tst_QUuid_darwinTypes() { // QUuid <-> CFUUID { - QUuid qtUuid(QByteArrayLiteral("0f7169cc-5711-4af9-99d9-fecb2329fdef")); + const auto qtUuid = QUuid::fromString(QLatin1String("0f7169cc-5711-4af9-99d9-fecb2329fdef")); const CFUUIDRef cfuuid = qtUuid.toCFUUID(); QCOMPARE(QUuid::fromCFUUID(cfuuid), qtUuid); CFStringRef cfstring = CFUUIDCreateString(0, cfuuid); @@ -45,10 +45,10 @@ void tst_QUuid_darwinTypes() CFRelease(cfuuid); } { - QUuid qtUuid(QByteArrayLiteral("0f7169cc-5711-4af9-99d9-fecb2329fdef")); + auto qtUuid = QUuid::fromString(QLatin1String("0f7169cc-5711-4af9-99d9-fecb2329fdef")); const CFUUIDRef cfuuid = qtUuid.toCFUUID(); QUuid qtUuidCopy(qtUuid); - qtUuid = QUuid(QByteArrayLiteral("93eec131-13c5-4d13-aaea-e456b4c57efa")); // modify + qtUuid = QUuid::fromString(QLatin1String("93eec131-13c5-4d13-aaea-e456b4c57efa")); // modify QCOMPARE(QUuid::fromCFUUID(cfuuid), qtUuidCopy); CFStringRef cfstring = CFUUIDCreateString(0, cfuuid); QCOMPARE(QString::fromCFString(cfstring), qtUuidCopy.toString().mid(1, 36).toUpper()); @@ -59,7 +59,7 @@ void tst_QUuid_darwinTypes() { QMacAutoReleasePool pool; - QUuid qtUuid(QByteArrayLiteral("0f7169cc-5711-4af9-99d9-fecb2329fdef")); + const auto qtUuid = QUuid::fromString(QLatin1String("0f7169cc-5711-4af9-99d9-fecb2329fdef")); const NSUUID *nsuuid = qtUuid.toNSUUID(); QCOMPARE(QUuid::fromNSUUID(nsuuid), qtUuid); QCOMPARE(QString::fromNSString([nsuuid UUIDString]), qtUuid.toString().mid(1, 36).toUpper()); @@ -67,10 +67,10 @@ void tst_QUuid_darwinTypes() { QMacAutoReleasePool pool; - QUuid qtUuid(QByteArrayLiteral("0f7169cc-5711-4af9-99d9-fecb2329fdef")); + auto qtUuid = QUuid::fromString(QLatin1String("0f7169cc-5711-4af9-99d9-fecb2329fdef")); const NSUUID *nsuuid = qtUuid.toNSUUID(); QUuid qtUuidCopy(qtUuid); - qtUuid = QUuid(QByteArrayLiteral("93eec131-13c5-4d13-aaea-e456b4c57efa")); // modify + qtUuid = QUuid::fromString(QLatin1String("93eec131-13c5-4d13-aaea-e456b4c57efa")); // modify QCOMPARE(QUuid::fromNSUUID(nsuuid), qtUuidCopy); QCOMPARE(QString::fromNSString([nsuuid UUIDString]), qtUuidCopy.toString().mid(1, 36).toUpper()); } |