QFileDialog: optimize string handling in qt_tildeExpansion

- Instead of QString::split()-ing the path, just to inspect the
  first item in the list returned, simply find the location of
  the first separator and work with that.
  -> saves creating a QList, and its QString elements
  -> saves attempted detaches of that list when calling
     first()
- When extracting the user name, don't do it in a QString, do
  it in a QStringRef.
- When constructing the result, don't use QString::replace(),
  use QStringBuilder with a QStringRef into the original string.
- Eradicate the out parameter, it is easily calculated from the
  return value.
- Don't calculate userName on VXWORKS and INTEGRITY, where it
  is not used. Requires a different #ifdef sequence. Fixed
  preprocessor directives' indention as a drive-by.

Costs 84b in text size on optimized GCC 4.9 Linux AMD64 builds.

Change-Id: I61f1e8d558db7fb0c5c1170bdfd6f5ac1f1a9e62
Reviewed-by: Olivier Goffart (Woboq GmbH) <ogoffart@woboq.com>
bb10
Marc Mutz 2015-12-29 21:51:39 +01:00
parent 024a52d0d1
commit be94e009ae
2 changed files with 43 additions and 41 deletions

View File

@ -1091,46 +1091,43 @@ void QFileDialog::selectUrl(const QUrl &url)
}
#ifdef Q_OS_UNIX
Q_AUTOTEST_EXPORT QString qt_tildeExpansion(const QString &path, bool *expanded = 0)
Q_AUTOTEST_EXPORT QString qt_tildeExpansion(const QString &path)
{
if (expanded != 0)
*expanded = false;
if (!path.startsWith(QLatin1Char('~')))
return path;
QString ret = path;
QStringList tokens = ret.split(QDir::separator());
if (tokens.first() == QLatin1String("~")) {
ret.replace(0, 1, QDir::homePath());
int separatorPosition = path.indexOf(QDir::separator());
if (separatorPosition < 0)
separatorPosition = path.size();
if (separatorPosition == 1) {
return QDir::homePath() + path.midRef(1);
} else {
QString userName = tokens.first();
userName.remove(0, 1);
#if defined(Q_OS_VXWORKS) || defined(Q_OS_INTEGRITY)
const QString homePath = QDir::homePath();
#elif defined(_POSIX_THREAD_SAFE_FUNCTIONS) && !defined(Q_OS_OPENBSD)
#else
const QByteArray userName = path.midRef(1, separatorPosition - 1).toLocal8Bit();
# if defined(_POSIX_THREAD_SAFE_FUNCTIONS) && !defined(Q_OS_OPENBSD)
passwd pw;
passwd *tmpPw;
char buf[200];
const int bufSize = sizeof(buf);
int err = 0;
#if defined(Q_OS_SOLARIS) && (_POSIX_C_SOURCE - 0 < 199506L)
tmpPw = getpwnam_r(userName.toLocal8Bit().constData(), &pw, buf, bufSize);
#else
err = getpwnam_r(userName.toLocal8Bit().constData(), &pw, buf, bufSize, &tmpPw);
#endif
# if defined(Q_OS_SOLARIS) && (_POSIX_C_SOURCE - 0 < 199506L)
tmpPw = getpwnam_r(userName.constData(), &pw, buf, bufSize);
# else
err = getpwnam_r(userName.constData(), &pw, buf, bufSize, &tmpPw);
# endif
if (err || !tmpPw)
return ret;
return path;
const QString homePath = QString::fromLocal8Bit(pw.pw_dir);
#else
passwd *pw = getpwnam(userName.toLocal8Bit().constData());
# else
passwd *pw = getpwnam(userName.constData());
if (!pw)
return ret;
return path;
const QString homePath = QString::fromLocal8Bit(pw->pw_dir);
# endif
#endif
ret.replace(0, tokens.first().length(), homePath);
return homePath + path.midRef(separatorPosition);
}
if (expanded != 0)
*expanded = true;
return ret;
}
#endif
@ -4044,15 +4041,17 @@ QStringList QFSCompleter::splitPath(const QString &path) const
else
doubleSlash.clear();
#elif defined(Q_OS_UNIX)
bool expanded;
pathCopy = qt_tildeExpansion(pathCopy, &expanded);
if (expanded) {
QFileSystemModel *dirModel;
if (proxyModel)
dirModel = qobject_cast<QFileSystemModel *>(proxyModel->sourceModel());
else
dirModel = sourceModel;
dirModel->fetchMore(dirModel->index(pathCopy));
{
QString tildeExpanded = qt_tildeExpansion(pathCopy);
if (tildeExpanded != pathCopy) {
QFileSystemModel *dirModel;
if (proxyModel)
dirModel = qobject_cast<QFileSystemModel *>(proxyModel->sourceModel());
else
dirModel = sourceModel;
dirModel->fetchMore(dirModel->index(tildeExpanded));
}
pathCopy = std::move(tildeExpanded);
}
#endif

View File

@ -68,7 +68,7 @@
#include <unistd.h> // for pathconf() on OS X
#ifdef QT_BUILD_INTERNAL
QT_BEGIN_NAMESPACE
extern Q_GUI_EXPORT QString qt_tildeExpansion(const QString &path, bool *expanded = 0);
extern Q_GUI_EXPORT QString qt_tildeExpansion(const QString &path);
QT_END_NAMESPACE
#endif
#endif
@ -1395,15 +1395,18 @@ void tst_QFiledialog::tildeExpansion_data()
QTest::addColumn<QString>("tildePath");
QTest::addColumn<QString>("expandedPath");
const QString tilde = QStringLiteral("~");
const QString tildeUser = tilde + QString(qgetenv("USER"));
const QLatin1String someSubDir("/some/sub/dir");
const QString homePath = QDir::homePath();
const QString invalid = QStringLiteral("~thisIsNotAValidUserName");
QTest::newRow("empty path") << QString() << QString();
QTest::newRow("~") << QString::fromLatin1("~") << QDir::homePath();
QTest::newRow("~/some/sub/dir/") << QString::fromLatin1("~/some/sub/dir") << QDir::homePath()
+ QString::fromLatin1("/some/sub/dir");
QString userHome = QString(qgetenv("USER"));
userHome.prepend('~');
QTest::newRow("current user (~<user> syntax)") << userHome << QDir::homePath();
QString invalid = QString::fromLatin1("~thisIsNotAValidUserName");
QTest::newRow("invalid user name") << invalid << invalid;
QTest::newRow("~") << tilde << homePath;
QTest::newRow("~/some/sub/dir/") << tilde + someSubDir << homePath + someSubDir;
QTest::newRow("~<user>") << tildeUser << homePath;
QTest::newRow("~<user>/some/sub/dir") << tildeUser + someSubDir << homePath + someSubDir;
QTest::newRow("invalid user name") << invalid << invalid;
}
#endif // QT_BUILD_INTERNAL