QCbor/JsonValue: add toStringView()

This is like toString(), but returns a view on the underlying string
data (which may be US-ASCII (= L1), UTF-8, or UTF-16, so the return
value is QASV).

Use it around the code a bit.

Benchmark results on my machine (with -perf -iterations 1000):

  PASS   : BenchmarkQtJson::iteratorKey()
  RESULT : BenchmarkQtJson::iteratorKey():
     78,268.426 nsecs per iteration (total: 78,268,427, iterations: 1000)
     349,458.080 CPU cycles per iteration, 4,46 GHz (total: 349,458,080, iterations: 1000)
     780,327.957 instructions per iteration, 2,233 instr/cycle (total: 780,327,957, iterations: 1000)
     173,511.019 branch instructions per iteration, 2,22 G/sec (total: 173,511,020, iterations: 1000)
  PASS   : BenchmarkQtJson::iteratorKeyValueView()
  RESULT : BenchmarkQtJson::iteratorKeyValueView():
     58,424.756 nsecs per iteration (total: 58,424,756, iterations: 1000)
     267,176.603 CPU cycles per iteration, 4,57 GHz (total: 267,176,604, iterations: 1000)
     614,805.388 instructions per iteration, 2,301 instr/cycle (total: 614,805,389, iterations: 1000)
     140,681.105 branch instructions per iteration, 2,41 G/sec (total: 140,681,106, iterations: 1000)
  PASS   : BenchmarkQtJson::iteratorKeyView()
  RESULT : BenchmarkQtJson::iteratorKeyView():
     44,060.925 nsecs per iteration (total: 44,060,925, iterations: 1000)
     196,344.233 CPU cycles per iteration, 4,46 GHz (total: 196,344,233, iterations: 1000)
     426,631.322 instructions per iteration, 2,173 instr/cycle (total: 426,631,323, iterations: 1000)
     93,703.100 branch instructions per iteration, 2,13 G/sec (total: 93,703,101, iterations: 1000)
  PASS   : BenchmarkQtJson::iteratorKeyViewValueView()
  RESULT : BenchmarkQtJson::iteratorKeyViewValueView():
     29,205.569 nsecs per iteration (total: 29,205,570, iterations: 1000)
     131,089.040 CPU cycles per iteration, 4,49 GHz (total: 131,089,041, iterations: 1000)
     258,529.469 instructions per iteration, 1,972 instr/cycle (total: 258,529,469, iterations: 1000)
     60,875.631 branch instructions per iteration, 2,08 G/sec (total: 60,875,631, iterations: 1000)

[ChangeLog][QtCore][QCborValue/QJsonValue] Add toStringView() method.

Fixes: QTBUG-133688
Change-Id: I4200035e995426f1cce46a73a429921a0cd56c85
Reviewed-by: Thiago Macieira <thiago.macieira@intel.com>
This commit is contained in:
Marc Mutz 2025-02-11 16:34:13 +01:00
parent 3e1dffd5dd
commit 3070efb64a
8 changed files with 108 additions and 8 deletions

View File

@ -2195,7 +2195,7 @@ QByteArray QCborValue::toByteArray(const QByteArray &defaultValue) const
Note that this function performs no conversion from other types to Note that this function performs no conversion from other types to
QString. QString.
\sa isString(), isByteArray(), toByteArray() \sa toStringView(), isString(), isByteArray(), toByteArray()
*/ */
QString QCborValue::toString(const QString &defaultValue) const QString QCborValue::toString(const QString &defaultValue) const
{ {
@ -2206,6 +2206,33 @@ QString QCborValue::toString(const QString &defaultValue) const
return container->stringAt(n); return container->stringAt(n);
} }
/*!
\since 6.10
Returns the string value stored in this QCborValue, if it is of the string
type. Otherwise, it returns \a defaultValue. Since QCborValue stores
strings in either US-ASCII, UTF-8 or UTF-16, the returned QAnyStringView
may be in any of these encodings.
This function does not allocate memory. The return value is valid until the
next call to a non-const member function on this object. If this object goes
out of scope, the return value is valid until the next call to a non-const
member function on the parent CBOR object (map or array).
Note that this function performs no conversion from other types to
QString.
\sa toString(), isString(), isByteArray(), toByteArray()
*/
QAnyStringView QCborValue::toStringView(QAnyStringView defaultValue) const
{
if (!container || !isString())
return defaultValue;
Q_ASSERT(n >= 0);
return container->anyStringViewAt(n);
}
#if QT_CONFIG(datestring) #if QT_CONFIG(datestring)
/*! /*!
Returns the date/time value stored in this QCborValue, if it is of the Returns the date/time value stored in this QCborValue, if it is of the
@ -2901,6 +2928,14 @@ QString QCborValueConstRef::concreteString(QCborValueConstRef self, const QStrin
return self.d->stringAt(self.i); return self.d->stringAt(self.i);
} }
QAnyStringView QCborValueConstRef::concreteStringView(QCborValueConstRef self, QAnyStringView defaultValue)
{
QtCbor::Element e = self.d->elements.at(self.i);
if (e.type != QCborValue::String)
return defaultValue;
return self.d->anyStringViewAt(self.i);
}
bool bool
QCborValueConstRef::comparesEqual_helper(QCborValueConstRef lhs, QCborValueConstRef rhs) noexcept QCborValueConstRef::comparesEqual_helper(QCborValueConstRef lhs, QCborValueConstRef rhs) noexcept
{ {

View File

@ -198,6 +198,7 @@ public:
QByteArray toByteArray(const QByteArray &defaultValue = {}) const; QByteArray toByteArray(const QByteArray &defaultValue = {}) const;
QString toString(const QString &defaultValue = {}) const; QString toString(const QString &defaultValue = {}) const;
QAnyStringView toStringView(QAnyStringView defaultValue = {}) const;
#if QT_CONFIG(datestring) #if QT_CONFIG(datestring)
QDateTime toDateTime(const QDateTime &defaultValue = {}) const; QDateTime toDateTime(const QDateTime &defaultValue = {}) const;
#endif #endif
@ -350,6 +351,8 @@ public:
{ return concrete().toByteArray(defaultValue); } { return concrete().toByteArray(defaultValue); }
QString toString(const QString &defaultValue = {}) const QString toString(const QString &defaultValue = {}) const
{ return concrete().toString(defaultValue); } { return concrete().toString(defaultValue); }
QAnyStringView toStringView(QAnyStringView defaultValue = {}) const
{ return concreteStringView(*this, defaultValue); }
#if QT_CONFIG(datestring) #if QT_CONFIG(datestring)
QDateTime toDateTime(const QDateTime &defaultValue = {}) const QDateTime toDateTime(const QDateTime &defaultValue = {}) const
{ return concrete().toDateTime(defaultValue); } { return concrete().toDateTime(defaultValue); }
@ -442,6 +445,8 @@ protected:
concreteByteArray(QCborValueConstRef that, const QByteArray &defaultValue); concreteByteArray(QCborValueConstRef that, const QByteArray &defaultValue);
static Q_CORE_EXPORT QString static Q_CORE_EXPORT QString
concreteString(QCborValueConstRef that, const QString &defaultValue); concreteString(QCborValueConstRef that, const QString &defaultValue);
static Q_CORE_EXPORT QAnyStringView
concreteStringView(QCborValueConstRef that, QAnyStringView defaultValue);
constexpr QCborValueConstRef() : d(nullptr), i(0) {} // this will actually be invalid constexpr QCborValueConstRef() : d(nullptr), i(0) {} // this will actually be invalid
constexpr QCborValueConstRef(QCborContainerPrivate *dd, qsizetype ii) constexpr QCborValueConstRef(QCborContainerPrivate *dd, qsizetype ii)

View File

@ -744,12 +744,34 @@ double QJsonValue::toDouble(double defaultValue) const
Converts the value to a QString and returns it. Converts the value to a QString and returns it.
If type() is not String, the \a defaultValue will be returned. If type() is not String, the \a defaultValue will be returned.
\sa toStringView()
*/ */
QString QJsonValue::toString(const QString &defaultValue) const QString QJsonValue::toString(const QString &defaultValue) const
{ {
return value.toString(defaultValue); return value.toString(defaultValue);
} }
/*!
\since 6.10
Returns the string value stored in this QJsonValue, if it is of the
\l{String}{string} type. Otherwise, it returns \a defaultValue. Since
QJsonValue stores strings in either US-ASCII, UTF-8 or UTF-16, the returned
QAnyStringView may be in any of these encodings.
This function does not allocate memory. The return value is valid until the
next call to a non-const member function on this object. If this object goes
out of scope, the return value is valid until the next call to a non-const
member function on the parent JSON object or array.
\sa toString()
*/
QAnyStringView QJsonValue::toStringView(QAnyStringView defaultValue) const
{
return value.toStringView(defaultValue);
}
/*! /*!
Converts the value to a QString and returns it. Converts the value to a QString and returns it.
@ -1066,6 +1088,15 @@ QString QJsonValueConstRef::concreteString(QJsonValueConstRef self, const QStrin
return d->stringAt(index); return d->stringAt(index);
} }
QAnyStringView QJsonValueConstRef::concreteStringView(QJsonValueConstRef self, QAnyStringView defaultValue)
{
const QCborContainerPrivate *d = QJsonPrivate::Value::container(self);
const qsizetype index = QJsonPrivate::Value::indexHelper(self);
if (d->elements.at(index).type != QCborValue::String)
return defaultValue;
return d->anyStringViewAt(index);
}
QJsonValue QJsonValueConstRef::concrete(QJsonValueConstRef self) noexcept QJsonValue QJsonValueConstRef::concrete(QJsonValueConstRef self) noexcept
{ {
const QCborContainerPrivate *d = QJsonPrivate::Value::container(self); const QCborContainerPrivate *d = QJsonPrivate::Value::container(self);

View File

@ -100,6 +100,7 @@ public:
double toDouble(double defaultValue = 0) const; double toDouble(double defaultValue = 0) const;
QString toString() const; QString toString() const;
QString toString(const QString &defaultValue) const; QString toString(const QString &defaultValue) const;
QAnyStringView toStringView(QAnyStringView defaultValue = {}) const;
QJsonArray toArray() const; QJsonArray toArray() const;
QJsonArray toArray(const QJsonArray &defaultValue) const; QJsonArray toArray(const QJsonArray &defaultValue) const;
QJsonObject toObject() const; QJsonObject toObject() const;
@ -165,6 +166,8 @@ public:
{ return concreteDouble(*this, defaultValue); } { return concreteDouble(*this, defaultValue); }
QString toString(const QString &defaultValue = {}) const QString toString(const QString &defaultValue = {}) const
{ return concreteString(*this, defaultValue); } { return concreteString(*this, defaultValue); }
QAnyStringView toStringView(QAnyStringView defaultValue = {}) const
{ return concreteStringView(*this, defaultValue); }
Q_CORE_EXPORT QJsonArray toArray() const; Q_CORE_EXPORT QJsonArray toArray() const;
Q_CORE_EXPORT QJsonObject toObject() const; Q_CORE_EXPORT QJsonObject toObject() const;
@ -195,6 +198,7 @@ protected:
Q_CORE_EXPORT static double Q_CORE_EXPORT static double
concreteDouble(QJsonValueConstRef self, double defaultValue) noexcept Q_DECL_PURE_FUNCTION; concreteDouble(QJsonValueConstRef self, double defaultValue) noexcept Q_DECL_PURE_FUNCTION;
Q_CORE_EXPORT static QString concreteString(QJsonValueConstRef self, const QString &defaultValue); Q_CORE_EXPORT static QString concreteString(QJsonValueConstRef self, const QString &defaultValue);
Q_CORE_EXPORT static QAnyStringView concreteStringView(QJsonValueConstRef self, QAnyStringView defaultValue);
Q_CORE_EXPORT static QJsonValue concrete(QJsonValueConstRef self) noexcept; Q_CORE_EXPORT static QJsonValue concrete(QJsonValueConstRef self) noexcept;
// for iterators // for iterators
@ -287,6 +291,8 @@ public:
inline qint64 toInteger(qint64 defaultValue = 0) const { return QJsonValueConstRef::toInteger(defaultValue); } inline qint64 toInteger(qint64 defaultValue = 0) const { return QJsonValueConstRef::toInteger(defaultValue); }
inline double toDouble(double defaultValue = 0) const { return QJsonValueConstRef::toDouble(defaultValue); } inline double toDouble(double defaultValue = 0) const { return QJsonValueConstRef::toDouble(defaultValue); }
inline QString toString(const QString &defaultValue = {}) const { return QJsonValueConstRef::toString(defaultValue); } inline QString toString(const QString &defaultValue = {}) const { return QJsonValueConstRef::toString(defaultValue); }
QAnyStringView toStringView(QAnyStringView defaultValue = {}) const
{ return QJsonValueConstRef::toStringView(defaultValue); }
QJsonArray toArray() const; QJsonArray toArray() const;
QJsonObject toObject() const; QJsonObject toObject() const;

View File

@ -176,8 +176,8 @@ VersionTerm VersionTerm::fromJson(const QJsonValue &v)
if (!v.isObject()) if (!v.isObject())
return result; return result;
const QJsonObject o = v.toObject(); const QJsonObject o = v.toObject();
result.number = QVersionNumber::fromString(o.value("value"_L1).toString()); result.number = QVersionNumber::fromString(o.value("value"_L1).toStringView());
const QString opS = o.value("op"_L1).toString(); const auto opS = o.value("op"_L1).toStringView();
for (size_t i = 0; i < sizeof(operators) / sizeof(operators[0]); ++i) { for (size_t i = 0; i < sizeof(operators) / sizeof(operators[0]); ++i) {
if (opS == QLatin1StringView(operators[i])) { if (opS == QLatin1StringView(operators[i])) {
result.op = static_cast<Operator>(i); result.op = static_cast<Operator>(i);

View File

@ -1044,14 +1044,15 @@ void QKmsScreenConfig::loadConfig()
m_devicePath = object.value("device"_L1).toString(); m_devicePath = object.value("device"_L1).toString();
m_separateScreens = object.value("separateScreens"_L1).toBool(m_separateScreens); m_separateScreens = object.value("separateScreens"_L1).toBool(m_separateScreens);
const QString vdOriString = object.value("virtualDesktopLayout"_L1).toString(); const auto vdOriString = object.value("virtualDesktopLayout"_L1).toStringView();
if (!vdOriString.isEmpty()) { if (!vdOriString.isEmpty()) {
if (vdOriString == "horizontal"_L1) if (vdOriString == "horizontal"_L1)
m_virtualDesktopLayout = VirtualDesktopLayoutHorizontal; m_virtualDesktopLayout = VirtualDesktopLayoutHorizontal;
else if (vdOriString == "vertical"_L1) else if (vdOriString == "vertical"_L1)
m_virtualDesktopLayout = VirtualDesktopLayoutVertical; m_virtualDesktopLayout = VirtualDesktopLayoutVertical;
else else
qCWarning(qLcKmsDebug) << "Unknown virtualDesktopOrientation value" << vdOriString; qCWarning(qLcKmsDebug, "Unknown virtualDesktopOrientation value %ls",
qUtf16Printable(vdOriString.toString()));
} }
const QJsonArray outputs = object.value("outputs"_L1).toArray(); const QJsonArray outputs = object.value("outputs"_L1).toArray();

View File

@ -342,6 +342,7 @@ static void basicTypeCheck(QCborValue::Type type, const QCborValue &v, const QVa
QVERIFY(validexpr) QVERIFY(validexpr)
CMP(v.toByteArray(), QByteArray, v.toByteArray().isNull()); CMP(v.toByteArray(), QByteArray, v.toByteArray().isNull());
CMP(v.toString(), QString, v.toString().isNull()); CMP(v.toString(), QString, v.toString().isNull());
CMP(v.toStringView(), QString, v.toStringView().isNull());
CMP(v.toDateTime(), QDateTime, !v.toDateTime().isValid()); CMP(v.toDateTime(), QDateTime, !v.toDateTime().isValid());
CMP(v.toUrl(), QUrl, !v.toUrl().isValid()); CMP(v.toUrl(), QUrl, !v.toUrl().isValid());
CMP(v.toRegularExpression(), QRegularExpression, v.toRegularExpression().pattern().isNull()); CMP(v.toRegularExpression(), QRegularExpression, v.toRegularExpression().pattern().isNull());
@ -886,6 +887,7 @@ void tst_QCborValue::mapSimpleInitializerList()
QCborValue v = m.value(2); QCborValue v = m.value(2);
QVERIFY(v.isString()); QVERIFY(v.isString());
QCOMPARE(v.toString(), "Hello"); QCOMPARE(v.toString(), "Hello");
QCOMPARE(v.toStringView(), u8"Hello");
QCOMPARE(vmap[2], v); QCOMPARE(vmap[2], v);
} }
{ {
@ -893,6 +895,7 @@ void tst_QCborValue::mapSimpleInitializerList()
QCborValue v = m.value(3); QCborValue v = m.value(3);
QVERIFY(v.isString()); QVERIFY(v.isString());
QCOMPARE(v.toString(), "World"); QCOMPARE(v.toString(), "World");
QCOMPARE(v.toStringView(), "World"_L1);
QCOMPARE(vmap[3], v); QCOMPARE(vmap[3], v);
} }
{ {
@ -934,6 +937,7 @@ void tst_QCborValue::mapSimpleInitializerList()
QCOMPARE((it + 1).key(), QCborValue(2)); QCOMPARE((it + 1).key(), QCborValue(2));
QVERIFY((it + 1)->isString()); QVERIFY((it + 1)->isString());
QCOMPARE((it + 1)->toString(), "Hello"); QCOMPARE((it + 1)->toString(), "Hello");
QCOMPARE((it + 1)->toStringView(), u8"Hello");
it += 2; it += 2;
QCOMPARE(it.key(), QCborValue("Hello")); QCOMPARE(it.key(), QCborValue("Hello"));
QVERIFY(it->isInteger()); QVERIFY(it->isInteger());
@ -941,6 +945,7 @@ void tst_QCborValue::mapSimpleInitializerList()
QCOMPARE(it.key(), QCborValue(3)); QCOMPARE(it.key(), QCborValue(3));
QVERIFY(it->isString()); QVERIFY(it->isString());
QCOMPARE(it.value().toString(), "World"); QCOMPARE(it.value().toString(), "World");
QCOMPARE(it.value().toStringView(), "World"_L1);
--end; --end;
QCOMPARE(end.key(), QCborValue("World")); QCOMPARE(end.key(), QCborValue("World"));
QCOMPARE(end.value(), QCborValue(3)); QCOMPARE(end.value(), QCborValue(3));
@ -1462,6 +1467,7 @@ void tst_QCborValue::arrayStringElements()
QCborValueRef r1 = a[0]; QCborValueRef r1 = a[0];
QCOMPARE(r1.toString(), "Hello"); QCOMPARE(r1.toString(), "Hello");
QCOMPARE(r1.toStringView(), u8"Hello");
QCOMPARE(r1.operator QCborValue(), QCborValue("Hello")); QCOMPARE(r1.operator QCborValue(), QCborValue("Hello"));
QT_TEST_EQUALITY_OPS(r1, QCborValue("Hello"), true); QT_TEST_EQUALITY_OPS(r1, QCborValue("Hello"), true);
@ -1476,10 +1482,11 @@ void tst_QCborValue::arrayStringElements()
v2 = a.at(1); v2 = a.at(1);
QCOMPARE(v2.toString(), "World"); QCOMPARE(v2.toString(), "World");
QCOMPARE(v2.toStringView(), u8"World");
QT_TEST_EQUALITY_OPS(v2, QCborValue("World"), true); QT_TEST_EQUALITY_OPS(v2, QCborValue("World"), true);
QCOMPARE(a.takeAt(1).toString(), "World"); QCOMPARE(a.takeAt(1).toString(), "World");
QCOMPARE(a.takeAt(0).toString(), "Hello"); QCOMPARE(a.takeAt(0).toStringView(), "Hello"_L1);
QVERIFY(a.isEmpty()); QVERIFY(a.isEmpty());
} }
@ -1492,6 +1499,7 @@ void tst_QCborValue::mapStringValues()
QCborValueRef r1 = m[0]; QCborValueRef r1 = m[0];
QCOMPARE(r1.toString(), "Hello"); QCOMPARE(r1.toString(), "Hello");
QCOMPARE(r1.toStringView(), "Hello"_L1);
QCOMPARE(r1.operator QCborValue(), QCborValue("Hello")); QCOMPARE(r1.operator QCborValue(), QCborValue("Hello"));
QT_TEST_EQUALITY_OPS(r1, QCborValue("Hello"), true); QT_TEST_EQUALITY_OPS(r1, QCborValue("Hello"), true);
@ -1506,10 +1514,11 @@ void tst_QCborValue::mapStringValues()
v2 = (m.begin() + 1).value(); v2 = (m.begin() + 1).value();
QCOMPARE(v2.toString(), "World"); QCOMPARE(v2.toString(), "World");
QCOMPARE(v2.toStringView(), "World"_L1);
QCOMPARE(v2, QCborValue("World")); QCOMPARE(v2, QCborValue("World"));
QCOMPARE(m.extract(m.begin() + 1).toString(), "World"); QCOMPARE(m.extract(m.begin() + 1).toString(), "World");
QCOMPARE(m.take(0).toString(), "Hello"); QCOMPARE(m.take(0).toStringView(), u8"Hello");
QVERIFY(m.isEmpty()); QVERIFY(m.isEmpty());
} }
@ -1530,6 +1539,7 @@ void tst_QCborValue::mapStringKeys()
QVERIFY(m2.value(QCborValue(QByteArray("foo"))).isUndefined()); QVERIFY(m2.value(QCborValue(QByteArray("foo"))).isUndefined());
QVERIFY(m.value(QCborValue(QLatin1String("foo"))).isUndefined()); QVERIFY(m.value(QCborValue(QLatin1String("foo"))).isUndefined());
QCOMPARE(m.value(QCborValue(QByteArray("foo"))).toString(), "bar"); QCOMPARE(m.value(QCborValue(QByteArray("foo"))).toString(), "bar");
QCOMPARE(m.value(QCborValue(QByteArray("foo"))).toStringView(), "bar");
m.insert(u"World"_s, QCborValue(3)); // replaces m.insert(u"World"_s, QCborValue(3)); // replaces
QCOMPARE(m.size(), 3); QCOMPARE(m.size(), 3);
@ -1796,7 +1806,7 @@ void tst_QCborValue::mapComplexKeys()
// basics_data() strings are Latin1 // basics_data() strings are Latin1
QByteArray latin1 = v.toString().toLatin1(); QByteArray latin1 = v.toString().toLatin1();
Q_ASSERT(v.toString() == QString::fromLatin1(latin1)); Q_ASSERT(v.toStringView() == QString::fromLatin1(latin1));
QCOMPARE(m[QLatin1String(latin1)].toInteger(), 42); QCOMPARE(m[QLatin1String(latin1)].toInteger(), 42);
} }
@ -3007,6 +3017,7 @@ template <typename ValueRef> static void cborValueRef_template()
QCOMPARE(ref.toDouble(47), v.toDouble(47)); QCOMPARE(ref.toDouble(47), v.toDouble(47));
QCOMPARE(ref.toByteArray("other"), v.toByteArray("other")); QCOMPARE(ref.toByteArray("other"), v.toByteArray("other"));
QCOMPARE(ref.toString("other"), v.toString("other")); QCOMPARE(ref.toString("other"), v.toString("other"));
QCOMPARE(ref.toStringView("other"_L1), v.toStringView("other"));
QCOMPARE(ref.toArray(otherArray), v.toArray(otherArray)); QCOMPARE(ref.toArray(otherArray), v.toArray(otherArray));
QCOMPARE(ref.toMap(otherMap), v.toMap(otherMap)); QCOMPARE(ref.toMap(otherMap), v.toMap(otherMap));
QCOMPARE(ref.toDateTime(otherDateTime), v.toDateTime(otherDateTime)); QCOMPARE(ref.toDateTime(otherDateTime), v.toDateTime(otherDateTime));

View File

@ -38,6 +38,17 @@ private Q_SLOTS:
[](auto it) { return it->toString(); }); [](auto it) { return it->toString(); });
} }
void iteratorKeyValueView()
{
iteratorKeyImpl([](auto it) { return it.key(); },
[](auto it) { return it->toStringView(); });
}
void iteratorKeyViewValueView()
{
iteratorKeyImpl([](auto it) { return it.keyView(); },
[](auto it) { return it->toStringView(); });
}
private: private:
template <typename KeyFunc, typename ValueFunc> template <typename KeyFunc, typename ValueFunc>
void iteratorKeyImpl(KeyFunc key, ValueFunc val); void iteratorKeyImpl(KeyFunc key, ValueFunc val);