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):
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)

View File

@@ -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<bool> &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;
}

View File

@@ -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)

View File

@@ -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__");