summaryrefslogtreecommitdiffstats
path: root/src/corelib/io/qsettings.cpp
diff options
context:
space:
mode:
authorMarc Mutz <marc.mutz@qt.io>2022-02-22 21:27:37 +0100
committerMarc Mutz <marc.mutz@qt.io>2022-03-19 07:09:55 +0000
commit6ec1dc904d59bab3a0330ff2a6c26600a35c87a9 (patch)
tree14acfbccb73bc6f722492d86900da61338ea17cd /src/corelib/io/qsettings.cpp
parent03de9ff7eefd9e635d16867c231153363acfdf4b (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.cpp80
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");