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
QString.
\sa isString(), isByteArray(), toByteArray()
\sa toStringView(), isString(), isByteArray(), toByteArray()
*/
QString QCborValue::toString(const QString &defaultValue) const
{
@ -2206,6 +2206,33 @@ QString QCborValue::toString(const QString &defaultValue) const
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)
/*!
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);
}
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
QCborValueConstRef::comparesEqual_helper(QCborValueConstRef lhs, QCborValueConstRef rhs) noexcept
{

View File

@ -198,6 +198,7 @@ public:
QByteArray toByteArray(const QByteArray &defaultValue = {}) const;
QString toString(const QString &defaultValue = {}) const;
QAnyStringView toStringView(QAnyStringView defaultValue = {}) const;
#if QT_CONFIG(datestring)
QDateTime toDateTime(const QDateTime &defaultValue = {}) const;
#endif
@ -350,6 +351,8 @@ public:
{ return concrete().toByteArray(defaultValue); }
QString toString(const QString &defaultValue = {}) const
{ return concrete().toString(defaultValue); }
QAnyStringView toStringView(QAnyStringView defaultValue = {}) const
{ return concreteStringView(*this, defaultValue); }
#if QT_CONFIG(datestring)
QDateTime toDateTime(const QDateTime &defaultValue = {}) const
{ return concrete().toDateTime(defaultValue); }
@ -442,6 +445,8 @@ protected:
concreteByteArray(QCborValueConstRef that, const QByteArray &defaultValue);
static Q_CORE_EXPORT QString
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(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.
If type() is not String, the \a defaultValue will be returned.
\sa toStringView()
*/
QString QJsonValue::toString(const QString &defaultValue) const
{
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.
@ -1066,6 +1088,15 @@ QString QJsonValueConstRef::concreteString(QJsonValueConstRef self, const QStrin
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
{
const QCborContainerPrivate *d = QJsonPrivate::Value::container(self);

View File

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

View File

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

View File

@ -1044,14 +1044,15 @@ void QKmsScreenConfig::loadConfig()
m_devicePath = object.value("device"_L1).toString();
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 == "horizontal"_L1)
m_virtualDesktopLayout = VirtualDesktopLayoutHorizontal;
else if (vdOriString == "vertical"_L1)
m_virtualDesktopLayout = VirtualDesktopLayoutVertical;
else
qCWarning(qLcKmsDebug) << "Unknown virtualDesktopOrientation value" << vdOriString;
qCWarning(qLcKmsDebug, "Unknown virtualDesktopOrientation value %ls",
qUtf16Printable(vdOriString.toString()));
}
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)
CMP(v.toByteArray(), QByteArray, v.toByteArray().isNull());
CMP(v.toString(), QString, v.toString().isNull());
CMP(v.toStringView(), QString, v.toStringView().isNull());
CMP(v.toDateTime(), QDateTime, !v.toDateTime().isValid());
CMP(v.toUrl(), QUrl, !v.toUrl().isValid());
CMP(v.toRegularExpression(), QRegularExpression, v.toRegularExpression().pattern().isNull());
@ -886,6 +887,7 @@ void tst_QCborValue::mapSimpleInitializerList()
QCborValue v = m.value(2);
QVERIFY(v.isString());
QCOMPARE(v.toString(), "Hello");
QCOMPARE(v.toStringView(), u8"Hello");
QCOMPARE(vmap[2], v);
}
{
@ -893,6 +895,7 @@ void tst_QCborValue::mapSimpleInitializerList()
QCborValue v = m.value(3);
QVERIFY(v.isString());
QCOMPARE(v.toString(), "World");
QCOMPARE(v.toStringView(), "World"_L1);
QCOMPARE(vmap[3], v);
}
{
@ -934,6 +937,7 @@ void tst_QCborValue::mapSimpleInitializerList()
QCOMPARE((it + 1).key(), QCborValue(2));
QVERIFY((it + 1)->isString());
QCOMPARE((it + 1)->toString(), "Hello");
QCOMPARE((it + 1)->toStringView(), u8"Hello");
it += 2;
QCOMPARE(it.key(), QCborValue("Hello"));
QVERIFY(it->isInteger());
@ -941,6 +945,7 @@ void tst_QCborValue::mapSimpleInitializerList()
QCOMPARE(it.key(), QCborValue(3));
QVERIFY(it->isString());
QCOMPARE(it.value().toString(), "World");
QCOMPARE(it.value().toStringView(), "World"_L1);
--end;
QCOMPARE(end.key(), QCborValue("World"));
QCOMPARE(end.value(), QCborValue(3));
@ -1462,6 +1467,7 @@ void tst_QCborValue::arrayStringElements()
QCborValueRef r1 = a[0];
QCOMPARE(r1.toString(), "Hello");
QCOMPARE(r1.toStringView(), u8"Hello");
QCOMPARE(r1.operator QCborValue(), QCborValue("Hello"));
QT_TEST_EQUALITY_OPS(r1, QCborValue("Hello"), true);
@ -1476,10 +1482,11 @@ void tst_QCborValue::arrayStringElements()
v2 = a.at(1);
QCOMPARE(v2.toString(), "World");
QCOMPARE(v2.toStringView(), u8"World");
QT_TEST_EQUALITY_OPS(v2, QCborValue("World"), true);
QCOMPARE(a.takeAt(1).toString(), "World");
QCOMPARE(a.takeAt(0).toString(), "Hello");
QCOMPARE(a.takeAt(0).toStringView(), "Hello"_L1);
QVERIFY(a.isEmpty());
}
@ -1492,6 +1499,7 @@ void tst_QCborValue::mapStringValues()
QCborValueRef r1 = m[0];
QCOMPARE(r1.toString(), "Hello");
QCOMPARE(r1.toStringView(), "Hello"_L1);
QCOMPARE(r1.operator QCborValue(), QCborValue("Hello"));
QT_TEST_EQUALITY_OPS(r1, QCborValue("Hello"), true);
@ -1506,10 +1514,11 @@ void tst_QCborValue::mapStringValues()
v2 = (m.begin() + 1).value();
QCOMPARE(v2.toString(), "World");
QCOMPARE(v2.toStringView(), "World"_L1);
QCOMPARE(v2, QCborValue("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());
}
@ -1530,6 +1539,7 @@ void tst_QCborValue::mapStringKeys()
QVERIFY(m2.value(QCborValue(QByteArray("foo"))).isUndefined());
QVERIFY(m.value(QCborValue(QLatin1String("foo"))).isUndefined());
QCOMPARE(m.value(QCborValue(QByteArray("foo"))).toString(), "bar");
QCOMPARE(m.value(QCborValue(QByteArray("foo"))).toStringView(), "bar");
m.insert(u"World"_s, QCborValue(3)); // replaces
QCOMPARE(m.size(), 3);
@ -1796,7 +1806,7 @@ void tst_QCborValue::mapComplexKeys()
// basics_data() strings are Latin1
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);
}
@ -3007,6 +3017,7 @@ template <typename ValueRef> static void cborValueRef_template()
QCOMPARE(ref.toDouble(47), v.toDouble(47));
QCOMPARE(ref.toByteArray("other"), v.toByteArray("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.toMap(otherMap), v.toMap(otherMap));
QCOMPARE(ref.toDateTime(otherDateTime), v.toDateTime(otherDateTime));

View File

@ -38,6 +38,17 @@ private Q_SLOTS:
[](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:
template <typename KeyFunc, typename ValueFunc>
void iteratorKeyImpl(KeyFunc key, ValueFunc val);