Utils: Use a single QString as data store for FilePath

Plus a few integers to get access to the pieces.

This reduces sizeof(FilePath) from 72 to 32.

Change-Id: I65eb856ad47b6a250c705d8d01893781a21d8e02
Reviewed-by: Christian Stenger <christian.stenger@qt.io>
Reviewed-by: hjk <hjk@qt.io>
This commit is contained in:
hjk
2022-09-21 12:59:02 +02:00
parent 604730f14d
commit 23e96dd6fe
4 changed files with 83 additions and 91 deletions

View File

@@ -198,14 +198,8 @@ def qdump__CPlusPlus__Internal__Value(d, value):
def qdump__Utils__FilePath(d, value): def qdump__Utils__FilePath(d, value):
try: data, path_len, scheme_len, host_len = d.split("{@QString}IHH", value)
# support FilePath before 4.15 as well if False:
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)
scheme_enc = d.encodeString(scheme) scheme_enc = d.encodeString(scheme)
host_enc = d.encodeString(host) host_enc = d.encodeString(host)
elided, path_enc = d.encodeStringHelper(path, d.displayStringLimit) elided, path_enc = d.encodeStringHelper(path, d.displayStringLimit)
@@ -218,7 +212,10 @@ def qdump__Utils__FilePath(d, value):
if not path_enc.startswith(slash): if not path_enc.startswith(slash):
val += slash + dot + slash val += slash + dot + slash
val += path_enc 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) d.putPlainChildren(value)

View File

@@ -176,39 +176,39 @@ bool FilePath::isRootPath() const
QString FilePath::encodedHost() const QString FilePath::encodedHost() const
{ {
QString host = m_host; QString result = host().toString();
host.replace('%', "%25"); result.replace('%', "%25");
host.replace('/', "%2f"); result.replace('/', "%2f");
return host; return result;
} }
/// \returns a QString for passing on to QString based APIs /// \returns a QString for passing on to QString based APIs
QString FilePath::toString() const QString FilePath::toString() const
{ {
if (m_scheme.isEmpty()) if (!needsDevice())
return m_path; return path();
if (isRelativePath()) if (isRelativePath())
return m_scheme + "://" + encodedHost() + "/./" + m_path; return scheme() + "://" + encodedHost() + "/./" + path();
return m_scheme + "://" + encodedHost() + m_path; return scheme() + "://" + encodedHost() + path();
} }
QString FilePath::toFSPathString() const QString FilePath::toFSPathString() const
{ {
if (m_scheme.isEmpty()) if (scheme().isEmpty())
return m_path; return path();
if (isRelativePath()) if (isRelativePath())
return specialPath(SpecialPathComponent::RootPath) + "/" + m_scheme + "/" + encodedHost() + "/./" + m_path; return specialPath(SpecialPathComponent::RootPath) + "/" + scheme() + "/" + encodedHost() + "/./" + path();
return specialPath(SpecialPathComponent::RootPath) + "/" + m_scheme + "/" + encodedHost() + m_path; return specialPath(SpecialPathComponent::RootPath) + "/" + scheme() + "/" + encodedHost() + path();
} }
QUrl FilePath::toUrl() const QUrl FilePath::toUrl() const
{ {
QUrl url; QUrl url;
url.setScheme(m_scheme); url.setScheme(scheme().toString());
url.setHost(m_host); url.setHost(host().toString());
url.setPath(m_path); url.setPath(path());
return url; return url;
} }
@@ -217,13 +217,10 @@ QUrl FilePath::toUrl() const
/// this path belongs to. /// this path belongs to.
QString FilePath::toUserOutput() const QString FilePath::toUserOutput() const
{ {
if (needsDevice()) {
if (isRelativePath())
return m_scheme + "://" + encodedHost() + "/./" + m_path;
return m_scheme + "://" + encodedHost() + m_path;
}
QString tmp = toString(); QString tmp = toString();
if (needsDevice())
return tmp;
if (osType() == OsTypeWindows) if (osType() == OsTypeWindows)
tmp.replace('/', '\\'); tmp.replace('/', '\\');
return tmp; return tmp;
@@ -333,25 +330,29 @@ QString FilePath::completeSuffix() const
QStringView FilePath::scheme() const QStringView FilePath::scheme() const
{ {
return m_scheme; return QStringView{m_data}.mid(m_pathLen, m_schemeLen);
} }
QStringView FilePath::host() const QStringView FilePath::host() const
{ {
return m_host; return QStringView{m_data}.mid(m_pathLen + m_schemeLen, m_hostLen);
} }
QString FilePath::path() const 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) void FilePath::setParts(const QStringView scheme, const QStringView host, const QStringView path)
{ {
QTC_CHECK(!m_scheme.contains('/')); QTC_CHECK(!scheme.contains('/'));
m_scheme = scheme.toString();
m_host = host.toString(); m_data = path.toString() + scheme.toString() + host.toString();
setPath(path); m_schemeLen = scheme.size();
m_hostLen = host.size();
m_pathLen = path.size();
} }
/// \returns a bool indicating whether a file with this /// \returns a bool indicating whether a file with this
@@ -611,7 +612,7 @@ void FilePath::asyncWriteFileContents(const Continuation<bool> &cont, const QByt
bool FilePath::needsDevice() const bool FilePath::needsDevice() const
{ {
return !m_scheme.isEmpty(); return m_schemeLen != 0;
} }
bool FilePath::isSameDevice(const FilePath &other) const bool FilePath::isSameDevice(const FilePath &other) const
@@ -764,7 +765,7 @@ FilePath FilePath::normalizedPathName() const
{ {
FilePath result = *this; FilePath result = *this;
if (!needsDevice()) // FIXME: Assumes no remote Windows and Mac for now. 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; return result;
} }
@@ -833,15 +834,13 @@ bool isWindowsDriveLetter(QChar ch)
void FilePath::setPath(QStringView path) void FilePath::setPath(QStringView path)
{ {
if (path.startsWith(QStringLiteral("/./"))) setParts(scheme(), host(), path);
path = path.mid(3);
m_path = path.toString();
} }
void FilePath::setFromString(const QString &unnormalizedFileName) void FilePath::setFromString(const QString &unnormalizedFileName)
{ {
static const QLatin1String qtcDevSlash("__qtc_devices__/"); static const QStringView qtcDevSlash(u"__qtc_devices__/");
static const QLatin1String colonSlashSlash("://"); static const QStringView colonSlashSlash(u"://");
QString fileName = unnormalizedFileName; QString fileName = unnormalizedFileName;
if (fileName.contains('\\')) if (fileName.contains('\\'))
@@ -865,23 +864,21 @@ void FilePath::setFromString(const QString &unnormalizedFileName)
const int firstSlash = withoutQtcDeviceRoot.indexOf(slash); const int firstSlash = withoutQtcDeviceRoot.indexOf(slash);
if (firstSlash != -1) { if (firstSlash != -1) {
m_scheme = withoutQtcDeviceRoot.left(firstSlash).toString(); QString scheme = withoutQtcDeviceRoot.left(firstSlash).toString();
const int secondSlash = withoutQtcDeviceRoot.indexOf(slash, firstSlash + 1); 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(); .toString();
if (secondSlash != -1) { if (secondSlash != -1) {
QStringView path = withoutQtcDeviceRoot.mid(secondSlash); QStringView path = withoutQtcDeviceRoot.mid(secondSlash);
setPath(path); setParts(scheme, host, path);
return; return;
} }
m_path = slash; setParts(scheme, host, u"/");
return; return;
} }
m_scheme.clear(); setParts({}, {}, fileName);
m_host.clear();
m_path = fileName;
return; return;
} }
@@ -889,16 +886,14 @@ void FilePath::setFromString(const QString &unnormalizedFileName)
const int schemeEnd = fileName.indexOf(colonSlashSlash); const int schemeEnd = fileName.indexOf(colonSlashSlash);
if (schemeEnd != -1 && schemeEnd < firstSlash) { if (schemeEnd != -1 && schemeEnd < firstSlash) {
// This is a pseudo Url, we can't use QUrl here sadly. // 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); const int hostEnd = fileName.indexOf(slash, schemeEnd + 3);
m_host = fileName.mid(schemeEnd + 3, hostEnd - schemeEnd - 3); QString host = fileName.mid(schemeEnd + 3, hostEnd - schemeEnd - 3);
if (hostEnd != -1) setParts(scheme, host, hostEnd != -1 ? QStringView(fileName).mid(hostEnd) : QStringView());
setPath(QStringView(fileName).mid(hostEnd));
return; return;
} }
setPath(fileName); setParts({}, {}, fileName);
return;
} }
/// Constructs a FilePath from \a filePath. The \a defaultExtension is appended /// 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 bool FilePath::operator==(const FilePath &other) const
{ {
return QString::compare(m_path, other.m_path, caseSensitivity()) == 0 return QString::compare(path(), other.path(), caseSensitivity()) == 0
&& m_host == other.m_host && host() == other.host()
&& m_scheme == other.m_scheme; && scheme() == other.scheme();
} }
bool FilePath::operator!=(const FilePath &other) const 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 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) if (cmp != 0)
return cmp < 0; return cmp < 0;
if (m_host != other.m_host) if (host() != other.host())
return m_host < other.m_host; return host() < other.host();
return m_scheme < other.m_scheme; return scheme() < other.scheme();
} }
bool FilePath::operator<=(const FilePath &other) const bool FilePath::operator<=(const FilePath &other) const
@@ -996,15 +991,15 @@ bool FilePath::isChildOf(const FilePath &s) const
{ {
if (s.isEmpty()) if (s.isEmpty())
return false; return false;
if (!m_path.startsWith(s.m_path, caseSensitivity())) if (!path().startsWith(s.path(), caseSensitivity()))
return false; return false;
if (m_path.size() <= s.m_path.size()) if (path().size() <= s.path().size())
return false; return false;
// s is root, '/' was already tested in startsWith // s is root, '/' was already tested in startsWith
if (s.m_path.endsWith(QLatin1Char('/'))) if (s.path().endsWith(QLatin1Char('/')))
return true; return true;
// s is a directory, next character should be '/' (/tmpdir is NOT a child of /tmp) // 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 /// \returns whether path() startsWith \a s
@@ -1029,7 +1024,7 @@ bool FilePath::endsWith(const QString &s) const
*/ */
bool FilePath::startsWithDriveLetter() 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; FilePath res;
if (isChildOf(parent)) { if (isChildOf(parent)) {
res.m_scheme = m_scheme; QString p = path().mid(parent.path().size());
res.m_host = m_host; if (p.startsWith('/'))
res.m_path = m_path.mid(parent.m_path.size()); p = p.mid(1);
if (res.m_path.startsWith('/')) res.setParts(scheme(), host(), p);
res.m_path = res.m_path.mid(1);
} }
return res; return res;
} }
@@ -1164,15 +1158,13 @@ QString FilePath::calcRelativePath(const QString &absolutePath, const QString &a
*/ */
FilePath FilePath::onDevice(const FilePath &deviceTemplate) const 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) if (sameDevice)
return *this; return *this;
// TODO: converting paths between different non local devices is still unsupported // TODO: converting paths between different non local devices is still unsupported
QTC_CHECK(!needsDevice()); QTC_CHECK(!needsDevice());
FilePath res; FilePath res;
res.m_scheme = deviceTemplate.m_scheme; res.setParts(deviceTemplate.scheme(), deviceTemplate.host(), path());
res.m_host = deviceTemplate.m_host;
res.m_path = m_path;
res.setPath(res.mapToDevicePath()); res.setPath(res.mapToDevicePath());
return res; return res;
} }
@@ -1191,9 +1183,7 @@ FilePath FilePath::onDevice(const FilePath &deviceTemplate) const
FilePath FilePath::withNewPath(const QString &newPath) const FilePath FilePath::withNewPath(const QString &newPath) const
{ {
FilePath res; FilePath res;
res.setPath(newPath); res.setParts(scheme(), host(), newPath);
res.m_host = m_host;
res.m_scheme = m_scheme;
return res; return res;
} }
@@ -1286,9 +1276,11 @@ FilePath FilePath::pathAppended(const QString &path) const
if (isEmpty()) { if (isEmpty()) {
return other; return other;
} }
FilePath fn = *this;
join(fn.m_path, other.path()); QString p = this->path();
return fn; join(p, other.path());
return withNewPath(p);
} }
FilePath FilePath::stringAppended(const QString &str) const FilePath FilePath::stringAppended(const QString &str) const
@@ -1506,7 +1498,7 @@ bool FilePath::isNewerThan(const QDateTime &timeStamp) const
*/ */
Qt::CaseSensitivity FilePath::caseSensitivity() const Qt::CaseSensitivity FilePath::caseSensitivity() const
{ {
if (m_scheme.isEmpty()) if (scheme().isEmpty())
return HostOsInfo::fileNameCaseSensitivity(); return HostOsInfo::fileNameCaseSensitivity();
// FIXME: This could or possibly should the target device's file name case sensitivity // 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 bool FilePath::isEmpty() const
{ {
return m_path.isEmpty(); return m_pathLen == 0;
} }
/*! /*!
@@ -1634,9 +1626,9 @@ QString FilePath::shortNativePath() const
*/ */
bool FilePath::isRelativePath() const bool FilePath::isRelativePath() const
{ {
if (m_path.startsWith('/')) if (path().startsWith('/'))
return false; 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 false;
return true; return true;
} }

View File

@@ -219,9 +219,10 @@ private:
[[nodiscard]] QString mapToDevicePath() const; [[nodiscard]] QString mapToDevicePath() const;
[[nodiscard]] QString encodedHost() const; [[nodiscard]] QString encodedHost() const;
QString m_scheme; QString m_data; // Concatenated m_path, m_scheme, m_host
QString m_host; // May contain raw slashes. unsigned int m_pathLen = 0;
QString m_path; // Includes the root bits unsigned short m_schemeLen = 0;
unsigned short m_hostLen = 0;
}; };
inline size_t qHash(const Utils::FilePath &a, uint seed = 0) inline size_t qHash(const Utils::FilePath &a, uint seed = 0)

View File

@@ -498,6 +498,7 @@ void tst_fileutils::fromString_data()
QTest::newRow("single-colon") << D(":", "", "", ":"); QTest::newRow("single-colon") << D(":", "", "", ":");
QTest::newRow("single-slash") << D("/", "", "", "/"); QTest::newRow("single-slash") << D("/", "", "", "/");
QTest::newRow("single-char") << D("a", "", "", "a"); QTest::newRow("single-char") << D("a", "", "", "a");
QTest::newRow("relative") << D("./rel", "", "", "./rel");
QTest::newRow("qrc") << D(":/test.txt", "", "", ":/test.txt"); QTest::newRow("qrc") << D(":/test.txt", "", "", ":/test.txt");
QTest::newRow("qrc-no-slash") << 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") << 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-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-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-linux") << D("/__qtc_devices__", "", "", "/__qtc_devices__");
QTest::newRow("qtc-dev-win") << D("c:/__qtc_devices__", "", "", "c:/__qtc_devices__"); QTest::newRow("qtc-dev-win") << D("c:/__qtc_devices__", "", "", "c:/__qtc_devices__");