QStorageInfo/Linux: decode the names encoded by udev in-place

This function is only called with the name of a file coming from
QFileInfo::fileName() so it's usually already detached anyway. And if
there's nothing to decode, pass the string through without even
attempting to modify it.

Pick-to: 6.6
Change-Id: I9d43e5b91eb142d6945cfffd1787651437074d35
Reviewed-by: Ahmad Samir <a.samirh78@gmail.com>
Reviewed-by: Axel Spoerl <axel.spoerl@qt.io>
This commit is contained in:
Thiago Macieira 2023-09-22 19:08:42 -07:00
parent 4107e4d8ca
commit 39843b65f4

View File

@ -8,6 +8,7 @@
#include "qdiriterator.h"
#include <private/qcore_unix_p.h>
#include <private/qtools_p.h>
#if defined(Q_OS_ANDROID)
# include <sys/mount.h>
@ -35,27 +36,39 @@ using namespace Qt::StringLiterals;
// udev encodes the labels with ID_LABEL_FS_ENC which is done with
// blkid_encode_string(). Within this function some 1-byte utf-8
// characters not considered safe (e.g. '\' or ' ') are encoded as hex
static QString decodeFsEncString(const QString &str)
static QString decodeFsEncString(QString &&str)
{
QString decoded;
decoded.reserve(str.size());
using namespace QtMiscUtils;
qsizetype start = str.indexOf(u'\\');
if (start < 0)
return std::move(str);
int i = 0;
while (i < str.size()) {
if (i <= str.size() - 4) { // we need at least four characters \xAB
if (QStringView{str}.sliced(i).startsWith("\\x"_L1)) {
bool bOk;
const int code = QStringView{str}.mid(i+2, 2).toInt(&bOk, 16);
if (bOk && code >= 0x20 && code < 0x80) {
decoded += QChar(code);
i += 4;
continue;
}
}
// decode in-place
QString decoded = std::move(str);
auto ptr = reinterpret_cast<char16_t *>(decoded.begin());
qsizetype in = start;
qsizetype out = start;
qsizetype size = decoded.size();
while (in < size) {
Q_ASSERT(ptr[in] == u'\\');
if (size - in >= 4 && ptr[in + 1] == u'x') { // we need four characters: \xAB
int c = fromHex(ptr[in + 2]) << 4;
c |= fromHex(ptr[in + 3]);
if (Q_UNLIKELY(c < 0))
c = QChar::ReplacementCharacter; // bad hex sequence
ptr[out++] = c;
in += 4;
}
for ( ; in < size; ++in) {
char16_t c = ptr[in];
if (c == u'\\')
break;
ptr[out++] = c;
}
decoded += str.at(i);
++i;
}
decoded.resize(out);
return decoded;
}