diff options
author | Marc Mutz <marc.mutz@qt.io> | 2022-02-22 21:27:37 +0100 |
---|---|---|
committer | Marc Mutz <marc.mutz@qt.io> | 2022-03-19 07:09:55 +0000 |
commit | 6ec1dc904d59bab3a0330ff2a6c26600a35c87a9 (patch) | |
tree | 14acfbccb73bc6f722492d86900da61338ea17cd /src/corelib/io/qsettings.cpp | |
parent | 03de9ff7eefd9e635d16867c231153363acfdf4b (diff) |
QSettings: port key processing to QAnyStringView
... in preparation for replacing the QString keys in the public API
with QAnyStringView ones.
This removes the "important optimization" that avoids a detach in the
common case where the input is the same as the output of
normalization. But that optimization is beside the point, because it
trades a memory allocation avoided in the library for O(N) allocations
inserted into user code for each call to QSettings::value(), the vast
majority of which are calls with string literals.
With the public interface ported to QAnyStringView in the follow-up
patch, we can then internally optimize memory allocations _in a central
place_ (e.g. by returning std::u16string or QVarLengthArray<QChar> from
normalizeKey() instead of QString). But first we need to get rid of all
the unwarranted allocations in user code.
Change-Id: I45fc83d972c552a220c9c29508001d3f172e1162
Reviewed-by: Qt CI Bot <qt_ci_bot@qt-project.org>
Reviewed-by: Edward Welbourne <edward.welbourne@qt.io>
Reviewed-by: Sona Kurazyan <sona.kurazyan@qt.io>
Diffstat (limited to 'src/corelib/io/qsettings.cpp')
-rw-r--r-- | src/corelib/io/qsettings.cpp | 80 |
1 files changed, 55 insertions, 25 deletions
diff --git a/src/corelib/io/qsettings.cpp b/src/corelib/io/qsettings.cpp index fc31eca5de..2d5246352a 100644 --- a/src/corelib/io/qsettings.cpp +++ b/src/corelib/io/qsettings.cpp @@ -1,6 +1,6 @@ /**************************************************************************** ** -** Copyright (C) 2020 The Qt Company Ltd. +** Copyright (C) 2022 The Qt Company Ltd. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the QtCore module of the Qt Toolkit. @@ -53,7 +53,7 @@ #include "qtemporaryfile.h" #include "qstandardpaths.h" #include <qdatastream.h> -#include <qstringconverter.h> +#include "private/qstringconverter_p.h" #ifndef QT_NO_GEOM_VARIANT #include "qsize.h" @@ -230,13 +230,32 @@ QSettingsPrivate::~QSettingsPrivate() { } -QString QSettingsPrivate::actualKey(const QString &key) const +QString QSettingsPrivate::actualKey(QAnyStringView key) const { auto n = normalizedKey(key); Q_ASSERT_X(!n.isEmpty(), "QSettings", "empty key"); return groupPrefix + n; } +namespace { + // ### this needs some public API (QStringConverter?) + QChar *write(QChar *out, QUtf8StringView v) + { + return QUtf8::convertToUnicode(out, QByteArrayView(v)); + } + QChar *write(QChar *out, QLatin1String v) + { + for (char ch : v) + *out++ = QLatin1Char(ch); + return out; + } + QChar *write(QChar *out, QStringView v) + { + memcpy(out, v.data(), v.size() * sizeof(QChar)); + return out + v.size(); + } +} + /* Returns a string that never starts nor ends with a slash (or an empty string). Examples: @@ -244,32 +263,43 @@ QString QSettingsPrivate::actualKey(const QString &key) const "foo" becomes "foo" "/foo//bar///" becomes "foo/bar" "///" becomes "" - - This function is optimized to avoid a QString deep copy in the - common case where the key is already normalized. */ -QString QSettingsPrivate::normalizedKey(const QString &key) +QString QSettingsPrivate::normalizedKey(QAnyStringView key) { - QString result = key; + QString result(key.size(), Qt::Uninitialized); + auto out = const_cast<QChar*>(result.constData()); // don't detach - int i = 0; - while (i < result.size()) { - while (result.at(i) == QLatin1Char('/')) { - result.remove(i, 1); - if (i == result.size()) - goto after_loop; - } - while (result.at(i) != QLatin1Char('/')) { - ++i; - if (i == result.size()) - return result; + const bool maybeEndsInSlash = key.visit([&out](auto key) { + using View = decltype(key); + + auto it = key.begin(); + const auto end = key.end(); + + while (it != end) { + while (*it == u'/') { + ++it; + if (it == end) + return true; + } + auto mark = it; + while (*it != u'/') { + ++it; + if (it == end) + break; + } + out = write(out, View{mark, it}); + if (it == end) + return false; + Q_ASSERT(*it == u'/'); + *out++ = u'/'; + ++it; } - ++i; // leave the slash alone - } + return true; + }); -after_loop: - if (!result.isEmpty()) - result.truncate(i - 1); // remove the trailing slash + if (maybeEndsInSlash && out != result.constData()) + --out; // remove the trailing slash + result.truncate(out - result.constData()); return result; } @@ -3290,7 +3320,7 @@ QVariant QSettings::value(const QString &key, const QVariant &defaultValue) cons return d->value(key, &defaultValue); } -QVariant QSettingsPrivate::value(const QString &key, const QVariant *defaultValue) const +QVariant QSettingsPrivate::value(QAnyStringView key, const QVariant *defaultValue) const { if (key.isEmpty()) { qWarning("QSettings::value: Empty key passed"); |