diff --git a/share/qtcreator/debugger/creatortypes.py b/share/qtcreator/debugger/creatortypes.py index 8d0edf2e804..2e440d5d2bc 100644 --- a/share/qtcreator/debugger/creatortypes.py +++ b/share/qtcreator/debugger/creatortypes.py @@ -198,14 +198,8 @@ def qdump__CPlusPlus__Internal__Value(d, value): def qdump__Utils__FilePath(d, value): - try: - # support FilePath before 4.15 as well - if not d.extractPointer(value["m_url"]): # there is no valid URL - d.putStringValue(value["m_data"]) - else: - d.putItem(value["m_url"]) - except: - scheme, host, path = d.split("{@QString}{@QString}{@QString}", value) + data, path_len, scheme_len, host_len = d.split("{@QString}IHH", value) + if False: scheme_enc = d.encodeString(scheme) host_enc = d.encodeString(host) elided, path_enc = d.encodeStringHelper(path, d.displayStringLimit) @@ -218,7 +212,10 @@ def qdump__Utils__FilePath(d, value): if not path_enc.startswith(slash): val += slash + dot + slash val += path_enc - d.putValue(val, "utf16", elided=elided) + else: + elided, data_enc = d.encodeStringHelper(data, d.displayStringLimit) + val = data_enc + d.putValue(val, "utf16", elided=elided) d.putPlainChildren(value) diff --git a/src/libs/utils/filepath.cpp b/src/libs/utils/filepath.cpp index fe2f6cbc6e3..6ba842a9fed 100644 --- a/src/libs/utils/filepath.cpp +++ b/src/libs/utils/filepath.cpp @@ -176,39 +176,39 @@ bool FilePath::isRootPath() const QString FilePath::encodedHost() const { - QString host = m_host; - host.replace('%', "%25"); - host.replace('/', "%2f"); - return host; + QString result = host().toString(); + result.replace('%', "%25"); + result.replace('/', "%2f"); + return result; } /// \returns a QString for passing on to QString based APIs QString FilePath::toString() const { - if (m_scheme.isEmpty()) - return m_path; + if (!needsDevice()) + return path(); if (isRelativePath()) - return m_scheme + "://" + encodedHost() + "/./" + m_path; - return m_scheme + "://" + encodedHost() + m_path; + return scheme() + "://" + encodedHost() + "/./" + path(); + return scheme() + "://" + encodedHost() + path(); } QString FilePath::toFSPathString() const { - if (m_scheme.isEmpty()) - return m_path; + if (scheme().isEmpty()) + return path(); if (isRelativePath()) - return specialPath(SpecialPathComponent::RootPath) + "/" + m_scheme + "/" + encodedHost() + "/./" + m_path; - return specialPath(SpecialPathComponent::RootPath) + "/" + m_scheme + "/" + encodedHost() + m_path; + return specialPath(SpecialPathComponent::RootPath) + "/" + scheme() + "/" + encodedHost() + "/./" + path(); + return specialPath(SpecialPathComponent::RootPath) + "/" + scheme() + "/" + encodedHost() + path(); } QUrl FilePath::toUrl() const { QUrl url; - url.setScheme(m_scheme); - url.setHost(m_host); - url.setPath(m_path); + url.setScheme(scheme().toString()); + url.setHost(host().toString()); + url.setPath(path()); return url; } @@ -217,13 +217,10 @@ QUrl FilePath::toUrl() const /// this path belongs to. QString FilePath::toUserOutput() const { - if (needsDevice()) { - if (isRelativePath()) - return m_scheme + "://" + encodedHost() + "/./" + m_path; - return m_scheme + "://" + encodedHost() + m_path; - } - QString tmp = toString(); + if (needsDevice()) + return tmp; + if (osType() == OsTypeWindows) tmp.replace('/', '\\'); return tmp; @@ -333,25 +330,29 @@ QString FilePath::completeSuffix() const QStringView FilePath::scheme() const { - return m_scheme; + return QStringView{m_data}.mid(m_pathLen, m_schemeLen); } QStringView FilePath::host() const { - return m_host; + return QStringView{m_data}.mid(m_pathLen + m_schemeLen, m_hostLen); } QString FilePath::path() const { - return m_path; + if (m_data.startsWith("/./")) + return m_data.mid(3, m_pathLen - 3); + return m_data.left(m_pathLen); } void FilePath::setParts(const QStringView scheme, const QStringView host, const QStringView path) { - QTC_CHECK(!m_scheme.contains('/')); - m_scheme = scheme.toString(); - m_host = host.toString(); - setPath(path); + QTC_CHECK(!scheme.contains('/')); + + m_data = path.toString() + scheme.toString() + host.toString(); + m_schemeLen = scheme.size(); + m_hostLen = host.size(); + m_pathLen = path.size(); } /// \returns a bool indicating whether a file with this @@ -611,7 +612,7 @@ void FilePath::asyncWriteFileContents(const Continuation &cont, const QByt bool FilePath::needsDevice() const { - return !m_scheme.isEmpty(); + return m_schemeLen != 0; } bool FilePath::isSameDevice(const FilePath &other) const @@ -764,7 +765,7 @@ FilePath FilePath::normalizedPathName() const { FilePath result = *this; if (!needsDevice()) // FIXME: Assumes no remote Windows and Mac for now. - result.m_path = FileUtils::normalizedPathName(result.m_path); + result.setParts(scheme(), host(), FileUtils::normalizedPathName(path())); return result; } @@ -833,15 +834,13 @@ bool isWindowsDriveLetter(QChar ch) void FilePath::setPath(QStringView path) { - if (path.startsWith(QStringLiteral("/./"))) - path = path.mid(3); - m_path = path.toString(); + setParts(scheme(), host(), path); } void FilePath::setFromString(const QString &unnormalizedFileName) { - static const QLatin1String qtcDevSlash("__qtc_devices__/"); - static const QLatin1String colonSlashSlash("://"); + static const QStringView qtcDevSlash(u"__qtc_devices__/"); + static const QStringView colonSlashSlash(u"://"); QString fileName = unnormalizedFileName; if (fileName.contains('\\')) @@ -865,23 +864,21 @@ void FilePath::setFromString(const QString &unnormalizedFileName) const int firstSlash = withoutQtcDeviceRoot.indexOf(slash); if (firstSlash != -1) { - m_scheme = withoutQtcDeviceRoot.left(firstSlash).toString(); + QString scheme = withoutQtcDeviceRoot.left(firstSlash).toString(); const int secondSlash = withoutQtcDeviceRoot.indexOf(slash, firstSlash + 1); - m_host = withoutQtcDeviceRoot.mid(firstSlash + 1, secondSlash - firstSlash - 1) + QString host = withoutQtcDeviceRoot.mid(firstSlash + 1, secondSlash - firstSlash - 1) .toString(); if (secondSlash != -1) { QStringView path = withoutQtcDeviceRoot.mid(secondSlash); - setPath(path); + setParts(scheme, host, path); return; } - m_path = slash; + setParts(scheme, host, u"/"); return; } - m_scheme.clear(); - m_host.clear(); - m_path = fileName; + setParts({}, {}, fileName); return; } @@ -889,16 +886,14 @@ void FilePath::setFromString(const QString &unnormalizedFileName) const int schemeEnd = fileName.indexOf(colonSlashSlash); if (schemeEnd != -1 && schemeEnd < firstSlash) { // This is a pseudo Url, we can't use QUrl here sadly. - m_scheme = fileName.left(schemeEnd); + QString scheme = fileName.left(schemeEnd); const int hostEnd = fileName.indexOf(slash, schemeEnd + 3); - m_host = fileName.mid(schemeEnd + 3, hostEnd - schemeEnd - 3); - if (hostEnd != -1) - setPath(QStringView(fileName).mid(hostEnd)); + QString host = fileName.mid(schemeEnd + 3, hostEnd - schemeEnd - 3); + setParts(scheme, host, hostEnd != -1 ? QStringView(fileName).mid(hostEnd) : QStringView()); return; } - setPath(fileName); - return; + setParts({}, {}, fileName); } /// Constructs a FilePath from \a filePath. The \a defaultExtension is appended @@ -951,9 +946,9 @@ QVariant FilePath::toVariant() const bool FilePath::operator==(const FilePath &other) const { - return QString::compare(m_path, other.m_path, caseSensitivity()) == 0 - && m_host == other.m_host - && m_scheme == other.m_scheme; + return QString::compare(path(), other.path(), caseSensitivity()) == 0 + && host() == other.host() + && scheme() == other.scheme(); } bool FilePath::operator!=(const FilePath &other) const @@ -963,12 +958,12 @@ bool FilePath::operator!=(const FilePath &other) const bool FilePath::operator<(const FilePath &other) const { - const int cmp = QString::compare(m_path, other.m_path, caseSensitivity()); + const int cmp = QString::compare(path(), other.path(), caseSensitivity()); if (cmp != 0) return cmp < 0; - if (m_host != other.m_host) - return m_host < other.m_host; - return m_scheme < other.m_scheme; + if (host() != other.host()) + return host() < other.host(); + return scheme() < other.scheme(); } bool FilePath::operator<=(const FilePath &other) const @@ -996,15 +991,15 @@ bool FilePath::isChildOf(const FilePath &s) const { if (s.isEmpty()) return false; - if (!m_path.startsWith(s.m_path, caseSensitivity())) + if (!path().startsWith(s.path(), caseSensitivity())) return false; - if (m_path.size() <= s.m_path.size()) + if (path().size() <= s.path().size()) return false; // s is root, '/' was already tested in startsWith - if (s.m_path.endsWith(QLatin1Char('/'))) + if (s.path().endsWith(QLatin1Char('/'))) return true; // s is a directory, next character should be '/' (/tmpdir is NOT a child of /tmp) - return s.m_path.isEmpty() || m_path.at(s.m_path.size()) == QLatin1Char('/'); + return s.path().isEmpty() || path().at(s.path().size()) == QLatin1Char('/'); } /// \returns whether path() startsWith \a s @@ -1029,7 +1024,7 @@ bool FilePath::endsWith(const QString &s) const */ bool FilePath::startsWithDriveLetter() const { - return !needsDevice() && m_path.size() >= 2 && isWindowsDriveLetter(m_path[0]) && m_path.at(1) == ':'; + return !needsDevice() && path().size() >= 2 && isWindowsDriveLetter(path()[0]) && path().at(1) == ':'; } /*! @@ -1043,11 +1038,10 @@ FilePath FilePath::relativeChildPath(const FilePath &parent) const { FilePath res; if (isChildOf(parent)) { - res.m_scheme = m_scheme; - res.m_host = m_host; - res.m_path = m_path.mid(parent.m_path.size()); - if (res.m_path.startsWith('/')) - res.m_path = res.m_path.mid(1); + QString p = path().mid(parent.path().size()); + if (p.startsWith('/')) + p = p.mid(1); + res.setParts(scheme(), host(), p); } return res; } @@ -1164,15 +1158,13 @@ QString FilePath::calcRelativePath(const QString &absolutePath, const QString &a */ FilePath FilePath::onDevice(const FilePath &deviceTemplate) const { - const bool sameDevice = m_scheme == deviceTemplate.m_scheme && m_host == deviceTemplate.m_host; + const bool sameDevice = scheme() == deviceTemplate.scheme() && host() == deviceTemplate.host(); if (sameDevice) return *this; // TODO: converting paths between different non local devices is still unsupported QTC_CHECK(!needsDevice()); FilePath res; - res.m_scheme = deviceTemplate.m_scheme; - res.m_host = deviceTemplate.m_host; - res.m_path = m_path; + res.setParts(deviceTemplate.scheme(), deviceTemplate.host(), path()); res.setPath(res.mapToDevicePath()); return res; } @@ -1191,9 +1183,7 @@ FilePath FilePath::onDevice(const FilePath &deviceTemplate) const FilePath FilePath::withNewPath(const QString &newPath) const { FilePath res; - res.setPath(newPath); - res.m_host = m_host; - res.m_scheme = m_scheme; + res.setParts(scheme(), host(), newPath); return res; } @@ -1286,9 +1276,11 @@ FilePath FilePath::pathAppended(const QString &path) const if (isEmpty()) { return other; } - FilePath fn = *this; - join(fn.m_path, other.path()); - return fn; + + QString p = this->path(); + join(p, other.path()); + + return withNewPath(p); } FilePath FilePath::stringAppended(const QString &str) const @@ -1506,7 +1498,7 @@ bool FilePath::isNewerThan(const QDateTime &timeStamp) const */ Qt::CaseSensitivity FilePath::caseSensitivity() const { - if (m_scheme.isEmpty()) + if (scheme().isEmpty()) return HostOsInfo::fileNameCaseSensitivity(); // FIXME: This could or possibly should the target device's file name case sensitivity @@ -1606,7 +1598,7 @@ void FilePath::clear() */ bool FilePath::isEmpty() const { - return m_path.isEmpty(); + return m_pathLen == 0; } /*! @@ -1634,9 +1626,9 @@ QString FilePath::shortNativePath() const */ bool FilePath::isRelativePath() const { - if (m_path.startsWith('/')) + if (path().startsWith('/')) return false; - if (m_path.size() > 1 && isWindowsDriveLetter(m_path[0]) && m_path.at(1) == ':') + if (path().size() > 1 && isWindowsDriveLetter(path()[0]) && path().at(1) == ':') return false; return true; } diff --git a/src/libs/utils/filepath.h b/src/libs/utils/filepath.h index 7227aa519a0..75491b96c62 100644 --- a/src/libs/utils/filepath.h +++ b/src/libs/utils/filepath.h @@ -219,9 +219,10 @@ private: [[nodiscard]] QString mapToDevicePath() const; [[nodiscard]] QString encodedHost() const; - QString m_scheme; - QString m_host; // May contain raw slashes. - QString m_path; // Includes the root bits + QString m_data; // Concatenated m_path, m_scheme, m_host + unsigned int m_pathLen = 0; + unsigned short m_schemeLen = 0; + unsigned short m_hostLen = 0; }; inline size_t qHash(const Utils::FilePath &a, uint seed = 0) diff --git a/tests/auto/utils/fileutils/tst_fileutils.cpp b/tests/auto/utils/fileutils/tst_fileutils.cpp index ddbbd5647b3..f93aa74a58d 100644 --- a/tests/auto/utils/fileutils/tst_fileutils.cpp +++ b/tests/auto/utils/fileutils/tst_fileutils.cpp @@ -498,6 +498,7 @@ void tst_fileutils::fromString_data() QTest::newRow("single-colon") << D(":", "", "", ":"); QTest::newRow("single-slash") << D("/", "", "", "/"); QTest::newRow("single-char") << D("a", "", "", "a"); + QTest::newRow("relative") << D("./rel", "", "", "./rel"); QTest::newRow("qrc") << D(":/test.txt", "", "", ":/test.txt"); QTest::newRow("qrc-no-slash") << D(":test.txt", "", "", ":test.txt"); @@ -520,6 +521,7 @@ void tst_fileutils::fromString_data() QTest::newRow("docker-root-url") << D("docker://1234/", "docker", "1234", "/"); QTest::newRow("docker-root-url-special-linux") << D("/__qtc_devices__/docker/1234/", "docker", "1234", "/"); QTest::newRow("docker-root-url-special-win") << D("c:/__qtc_devices__/docker/1234/", "docker", "1234", "/"); + QTest::newRow("docker-relative-path") << D("docker://1234/./rel", "docker", "1234", "rel"); QTest::newRow("qtc-dev-linux") << D("/__qtc_devices__", "", "", "/__qtc_devices__"); QTest::newRow("qtc-dev-win") << D("c:/__qtc_devices__", "", "", "c:/__qtc_devices__");