QFileSystemIterator/Unix: use QStringDecoder

The end result is a QFileInfo constructed from the QFileSystemEntry;
QFileSystemEntry's API will at some point convert the NativePath (QBA on
Unix) to a QString, e.g. when it calls findLastSeparator().

So instead of converting the parent dir path from Utf8 to Utf16 multiple
times, store the dir path as a QString once in the constructor.

Use QStringDecoder to convert the dirent->d_name to Utf16 and check the
decoder's hasError(); which as Thiago pointed out, is more efficient
than using isValidUtf8(), throwing away the results, then converting to
unicode anyway later on in QDirIteratorPrivate::advance().

(Ironically) `QFileSystemEntry::NativePath nativePath` is still used on
Windows.

Change-Id: Icd2b130103640b502862d210d64926b99c499a01
Reviewed-by: Thiago Macieira <thiago.macieira@intel.com>
bb10
Ahmad Samir 2023-12-14 18:26:33 +02:00
parent 74722411d3
commit 83f7125ac8
3 changed files with 27 additions and 20 deletions

View File

@ -26,6 +26,10 @@
#include <QtCore/private/qfilesystementry_p.h>
#include <QtCore/private/qfilesystemmetadata_p.h>
#if !defined(Q_OS_WIN)
#include <private/qstringconverter_p.h>
#endif
#include <memory>
QT_BEGIN_NAMESPACE
@ -39,11 +43,11 @@ public:
bool advance(QFileSystemEntry &fileEntry, QFileSystemMetaData &metaData);
private:
QFileSystemEntry::NativePath nativePath;
QString dirPath;
// Platform-specific data
#if defined(Q_OS_WIN)
QString dirPath;
QFileSystemEntry::NativePath nativePath;
HANDLE findFileHandle;
QStringList uncShares;
bool uncFallback;
@ -58,6 +62,7 @@ private:
QT_DIRENT *dirEntry = nullptr;
int lastError = 0;
QStringDecoder toUtf16;
#endif
Q_DISABLE_COPY_MOVE(QFileSystemIterator)

View File

@ -4,10 +4,10 @@
#include "qplatformdefs.h"
#include "qfilesystemiterator_p.h"
#include <private/qstringconverter_p.h>
#ifndef QT_NO_FILESYSTEMITERATOR
#include <qvarlengtharray.h>
#include <memory>
#include <stdlib.h>
@ -15,20 +15,13 @@
QT_BEGIN_NAMESPACE
static bool checkNameDecodable(const char *d_name, qsizetype len)
{
// This function is called in a loop from advance() below, but the loop is
// usually run only once.
return QUtf8::isValidUtf8(QByteArrayView(d_name, len)).isValidUtf8;
}
/*
Native filesystem iterator, which uses ::opendir()/readdir()/dirent from the system
libraries to iterate over the directory represented by \a entry.
*/
QFileSystemIterator::QFileSystemIterator(const QFileSystemEntry &entry, QDir::Filters filters)
: nativePath(entry.nativeFilePath())
: dirPath(entry.filePath()),
toUtf16(QStringDecoder::Utf8)
{
Q_UNUSED(filters);
@ -36,8 +29,8 @@ QFileSystemIterator::QFileSystemIterator(const QFileSystemEntry &entry, QDir::Fi
if (!dir) {
lastError = errno;
} else {
if (!nativePath.endsWith('/'))
nativePath.append('/');
if (!dirPath.endsWith(u'/'))
dirPath.append(u'/');
}
}
@ -58,9 +51,18 @@ bool QFileSystemIterator::advance(QFileSystemEntry &fileEntry, QFileSystemMetaDa
dirEntry = QT_READDIR(dir.get());
if (dirEntry) {
qsizetype len = strlen(dirEntry->d_name);
if (checkNameDecodable(dirEntry->d_name, len)) {
fileEntry = QFileSystemEntry(nativePath + QByteArray(dirEntry->d_name, len), QFileSystemEntry::FromNativePath());
// POSIX allows readdir() to return a file name in struct dirent that
// extends past the end of the d_name array (it's a char[1] array on QNX, for
// example). Therefore, we *must* call strlen() on it to get the actual length
// of the file name. See:
// https://pubs.opengroup.org/onlinepubs/9699919799/basedefs/dirent.h.html#tag_13_07_05
QByteArrayView name(dirEntry->d_name, strlen(dirEntry->d_name));
// name.size() is sufficient here, see QUtf8::convertToUnicode() for details
QVarLengthArray<char16_t> buffer(name.size());
auto *end = toUtf16.appendToBuffer(buffer.data(), name);
buffer.resize(end - buffer.constData());
if (!toUtf16.hasError()) {
fileEntry = {dirPath + QStringView(buffer), QFileSystemEntry::FromInternalPath()};
metaData.fillFromDirEnt(*dirEntry);
return true;
} else {

View File

@ -15,8 +15,8 @@ using namespace Qt::StringLiterals;
bool done = true;
QFileSystemIterator::QFileSystemIterator(const QFileSystemEntry &entry, QDir::Filters filters)
: nativePath(entry.nativeFilePath())
, dirPath(entry.filePath())
: dirPath(entry.filePath())
, nativePath(entry.nativeFilePath())
, findFileHandle(INVALID_HANDLE_VALUE)
, uncFallback(false)
, uncShareIndex(0)