QJsonObject::iterator: add keyView()
Unlike CBOR maps, JSON objects can only have string keys, but, like CBOR, they are internally stored as either US-ASCII (ie. Latin-1), UTF-8, or UTF-16, so in order to return a view on the internal storage, QAnyStringView is the perfect fit. Add (const_)iterator::keyView() and add a benchmark, prepared for testing a similar change to the value side of things. Results (fastest each of ten runs) on my machine suggest a 40% speedup: PASS : BenchmarkQtJson::iteratorKey() RESULT : BenchmarkQtJson::iteratorKey(): 0.071 msecs per iteration (total: 73, iterations: 1024) PASS : BenchmarkQtJson::iteratorKeyView() RESULT : BenchmarkQtJson::iteratorKeyView(): 0.042 msecs per iteration (total: 87, iterations: 2048) [ChangeLog][QtCore][QJsonObject] Added keyView() methods to iterator and const_iterator, allowing zero-copy inspection of the key(). Task-number: QTBUG-133688 Change-Id: I0ccedaf8a4fa41125b12bdbab5bea3bd2468d9a5 Reviewed-by: Thiago Macieira <thiago.macieira@intel.com>
This commit is contained in:
parent
6aaa874d79
commit
bbdbcb2eba
@ -286,6 +286,18 @@ public:
|
|||||||
return data->asLatin1();
|
return data->asLatin1();
|
||||||
return data->toUtf8String();
|
return data->toUtf8String();
|
||||||
}
|
}
|
||||||
|
QAnyStringView anyStringViewAt(qsizetype idx) const
|
||||||
|
{
|
||||||
|
const auto &e = elements.at(idx);
|
||||||
|
const auto data = byteData(e);
|
||||||
|
if (!data)
|
||||||
|
return nullptr;
|
||||||
|
if (e.flags & QtCbor::Element::StringIsUtf16)
|
||||||
|
return data->asStringView();
|
||||||
|
if (e.flags & QtCbor::Element::StringIsAscii)
|
||||||
|
return data->asLatin1();
|
||||||
|
return data->asUtf8StringView();
|
||||||
|
}
|
||||||
|
|
||||||
static void resetValue(QCborValue &v)
|
static void resetValue(QCborValue &v)
|
||||||
{
|
{
|
||||||
|
@ -914,7 +914,24 @@ QJsonObject::const_iterator QJsonObject::constFindImpl(T key) const
|
|||||||
iterator, although it can be done by calling QJsonObject::erase()
|
iterator, although it can be done by calling QJsonObject::erase()
|
||||||
followed by QJsonObject::insert().
|
followed by QJsonObject::insert().
|
||||||
|
|
||||||
\sa value()
|
\sa value(), keyView()
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*!
|
||||||
|
\fn QAnyStringView QJsonObject::iterator::keyView() const
|
||||||
|
\since 6.10
|
||||||
|
|
||||||
|
Returns the current item's key as a QAnyStringView. This function does not
|
||||||
|
allocate memory.
|
||||||
|
|
||||||
|
Since QJsonObject stores keys in US-ASCII, UTF-8 or UTF-16, the returned
|
||||||
|
QAnyStringView may be in any of these encodings.
|
||||||
|
|
||||||
|
There is no direct way of changing an item's key through an
|
||||||
|
iterator, although it can be done by calling QJsonObject::erase()
|
||||||
|
followed by QJsonObject::insert().
|
||||||
|
|
||||||
|
\sa key(), value()
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/*! \fn QJsonValueRef QJsonObject::iterator::value() const
|
/*! \fn QJsonValueRef QJsonObject::iterator::value() const
|
||||||
@ -930,7 +947,7 @@ QJsonObject::const_iterator QJsonObject::constFindImpl(T key) const
|
|||||||
the assignment will apply to the element in the QJsonArray or QJsonObject
|
the assignment will apply to the element in the QJsonArray or QJsonObject
|
||||||
from which you got the reference.
|
from which you got the reference.
|
||||||
|
|
||||||
\sa key(), operator*()
|
\sa key(), keyView(), operator*()
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/*! \fn QJsonValueRef QJsonObject::iterator::operator*() const
|
/*! \fn QJsonValueRef QJsonObject::iterator::operator*() const
|
||||||
@ -945,7 +962,7 @@ QJsonObject::const_iterator QJsonObject::constFindImpl(T key) const
|
|||||||
the assignment will apply to the element in the QJsonArray or QJsonObject
|
the assignment will apply to the element in the QJsonArray or QJsonObject
|
||||||
from which you got the reference.
|
from which you got the reference.
|
||||||
|
|
||||||
\sa key()
|
\sa key(), keyView()
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/*! \fn QJsonValueRef *QJsonObject::iterator::operator->()
|
/*! \fn QJsonValueRef *QJsonObject::iterator::operator->()
|
||||||
@ -1189,14 +1206,27 @@ QJsonObject::const_iterator QJsonObject::constFindImpl(T key) const
|
|||||||
|
|
||||||
Returns the current item's key.
|
Returns the current item's key.
|
||||||
|
|
||||||
\sa value()
|
\sa value(), keyView()
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*!
|
||||||
|
\fn QAnyStringView QJsonObject::const_iterator::keyView() const
|
||||||
|
\since 6.10
|
||||||
|
|
||||||
|
Returns the current item's key as a QAnyStringView. This function does not
|
||||||
|
allocate.
|
||||||
|
|
||||||
|
Since QJsonObject stores keys in US-ASCII, UTF-8 or UTF-16, the returned
|
||||||
|
QAnyStringView may be in any of these encodings.
|
||||||
|
|
||||||
|
\sa value(), key()
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/*! \fn QJsonValueConstRef QJsonObject::const_iterator::value() const
|
/*! \fn QJsonValueConstRef QJsonObject::const_iterator::value() const
|
||||||
|
|
||||||
Returns the current item's value.
|
Returns the current item's value.
|
||||||
|
|
||||||
\sa key(), operator*()
|
\sa key(), keyView(), operator*()
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/*! \fn const QJsonValueConstRef QJsonObject::const_iterator::operator*() const
|
/*! \fn const QJsonValueConstRef QJsonObject::const_iterator::operator*() const
|
||||||
@ -1205,7 +1235,7 @@ QJsonObject::const_iterator QJsonObject::constFindImpl(T key) const
|
|||||||
|
|
||||||
Same as value().
|
Same as value().
|
||||||
|
|
||||||
\sa key()
|
\sa key(), keyView()
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/*! \fn const QJsonValueConstRef *QJsonObject::const_iterator::operator->() const
|
/*! \fn const QJsonValueConstRef *QJsonObject::const_iterator::operator->() const
|
||||||
|
@ -102,6 +102,7 @@ public:
|
|||||||
}
|
}
|
||||||
|
|
||||||
inline QString key() const { return item.objectKey(); }
|
inline QString key() const { return item.objectKey(); }
|
||||||
|
QAnyStringView keyView() const { return item.objectKeyView(); }
|
||||||
inline QJsonValueRef value() const { return item; }
|
inline QJsonValueRef value() const { return item; }
|
||||||
inline QJsonValueRef operator*() const { return item; }
|
inline QJsonValueRef operator*() const { return item; }
|
||||||
inline const QJsonValueConstRef *operator->() const { return &item; }
|
inline const QJsonValueConstRef *operator->() const { return &item; }
|
||||||
@ -215,6 +216,7 @@ public:
|
|||||||
}
|
}
|
||||||
|
|
||||||
inline QString key() const { return item.objectKey(); }
|
inline QString key() const { return item.objectKey(); }
|
||||||
|
QAnyStringView keyView() const { return item.objectKeyView(); }
|
||||||
inline QJsonValueConstRef value() const { return item; }
|
inline QJsonValueConstRef value() const { return item; }
|
||||||
inline const QJsonValueConstRef operator*() const { return item; }
|
inline const QJsonValueConstRef operator*() const { return item; }
|
||||||
inline const QJsonValueConstRef *operator->() const { return &item; }
|
inline const QJsonValueConstRef *operator->() const { return &item; }
|
||||||
|
@ -1073,6 +1073,17 @@ QJsonValue QJsonValueConstRef::concrete(QJsonValueConstRef self) noexcept
|
|||||||
return QJsonPrivate::Value::fromTrustedCbor(d->valueAt(index));
|
return QJsonPrivate::Value::fromTrustedCbor(d->valueAt(index));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
QAnyStringView QJsonValueConstRef::objectKeyView(QJsonValueConstRef self)
|
||||||
|
{
|
||||||
|
Q_ASSERT(self.is_object);
|
||||||
|
const QCborContainerPrivate *d = QJsonPrivate::Value::container(self);
|
||||||
|
const qsizetype index = QJsonPrivate::Value::indexHelper(self);
|
||||||
|
|
||||||
|
Q_ASSERT(d);
|
||||||
|
Q_ASSERT(index < d->elements.size());
|
||||||
|
return d->anyStringViewAt(index - 1);
|
||||||
|
}
|
||||||
|
|
||||||
QString QJsonValueConstRef::objectKey(QJsonValueConstRef self)
|
QString QJsonValueConstRef::objectKey(QJsonValueConstRef self)
|
||||||
{
|
{
|
||||||
Q_ASSERT(self.is_object);
|
Q_ASSERT(self.is_object);
|
||||||
|
@ -201,6 +201,9 @@ protected:
|
|||||||
Q_CORE_EXPORT static QString objectKey(QJsonValueConstRef self);
|
Q_CORE_EXPORT static QString objectKey(QJsonValueConstRef self);
|
||||||
QString objectKey() const { return objectKey(*this); }
|
QString objectKey() const { return objectKey(*this); }
|
||||||
|
|
||||||
|
Q_CORE_EXPORT static QAnyStringView objectKeyView(QJsonValueConstRef self);
|
||||||
|
QAnyStringView objectKeyView() const { return objectKeyView(*this); }
|
||||||
|
|
||||||
#if QT_VERSION < QT_VERSION_CHECK(7, 0, 0) && !defined(QT_BOOTSTRAPPED)
|
#if QT_VERSION < QT_VERSION_CHECK(7, 0, 0) && !defined(QT_BOOTSTRAPPED)
|
||||||
QJsonValueConstRef(QJsonArray *array, qsizetype idx)
|
QJsonValueConstRef(QJsonArray *array, qsizetype idx)
|
||||||
: a(array), is_object(false), index(static_cast<quint64>(idx)) {}
|
: a(array), is_object(false), index(static_cast<quint64>(idx)) {}
|
||||||
|
@ -1135,6 +1135,7 @@ void tst_QtJson::testObjectIteration()
|
|||||||
|
|
||||||
for (QJsonObject::iterator it = object.begin(); it != object.end(); ++it) {
|
for (QJsonObject::iterator it = object.begin(); it != object.end(); ++it) {
|
||||||
QJsonValue value = it.value();
|
QJsonValue value = it.value();
|
||||||
|
QCOMPARE(it.keyView(), QString::number(it.value().toInteger()));
|
||||||
QCOMPARE((double)it.key().toInt(), value.toDouble());
|
QCOMPARE((double)it.key().toInt(), value.toDouble());
|
||||||
QT_TEST_EQUALITY_OPS(it, QJsonObject::iterator(), false);
|
QT_TEST_EQUALITY_OPS(it, QJsonObject::iterator(), false);
|
||||||
}
|
}
|
||||||
@ -1600,8 +1601,10 @@ void tst_QtJson::keySorting()
|
|||||||
QCOMPARE(o.keys(), sortedKeys);
|
QCOMPARE(o.keys(), sortedKeys);
|
||||||
QJsonObject::const_iterator it = o.constBegin();
|
QJsonObject::const_iterator it = o.constBegin();
|
||||||
QStringList::const_iterator it2 = sortedKeys.constBegin();
|
QStringList::const_iterator it2 = sortedKeys.constBegin();
|
||||||
for ( ; it != o.constEnd(); ++it, ++it2)
|
for ( ; it != o.constEnd(); ++it, ++it2) {
|
||||||
QCOMPARE(it.key(), *it2);
|
QCOMPARE(it.key(), *it2);
|
||||||
|
QCOMPARE(it.keyView(), *it2);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void tst_QtJson::undefinedValues()
|
void tst_QtJson::undefinedValues()
|
||||||
|
@ -306,6 +306,7 @@ void tst_QCborValue_Json::nonStringKeysInMaps()
|
|||||||
QJsonObject o = m.toJsonObject();
|
QJsonObject o = m.toJsonObject();
|
||||||
auto it = o.begin();
|
auto it = o.begin();
|
||||||
QVERIFY(it != o.end());
|
QVERIFY(it != o.end());
|
||||||
|
QCOMPARE(it.keyView(), converted);
|
||||||
QCOMPARE(it.key(), converted);
|
QCOMPARE(it.key(), converted);
|
||||||
QCOMPARE(it.value(), 0);
|
QCOMPARE(it.value(), 0);
|
||||||
QCOMPARE(++it, o.end());
|
QCOMPARE(++it, o.end());
|
||||||
|
@ -2,6 +2,8 @@
|
|||||||
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
|
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
|
||||||
|
|
||||||
#include <QTest>
|
#include <QTest>
|
||||||
|
|
||||||
|
#include <QtCore/qjsonarray.h>
|
||||||
#include <QVariantMap>
|
#include <QVariantMap>
|
||||||
#include <qjsondocument.h>
|
#include <qjsondocument.h>
|
||||||
#include <qjsonobject.h>
|
#include <qjsonobject.h>
|
||||||
@ -24,6 +26,21 @@ private Q_SLOTS:
|
|||||||
|
|
||||||
void jsonObjectInsert();
|
void jsonObjectInsert();
|
||||||
void variantMapInsert();
|
void variantMapInsert();
|
||||||
|
|
||||||
|
void iteratorKey()
|
||||||
|
{
|
||||||
|
iteratorKeyImpl([](auto it) { return it.key(); },
|
||||||
|
[](auto it) { return it->toString(); });
|
||||||
|
}
|
||||||
|
void iteratorKeyView()
|
||||||
|
{
|
||||||
|
iteratorKeyImpl([](auto it) { return it.keyView(); },
|
||||||
|
[](auto it) { return it->toString(); });
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
template <typename KeyFunc, typename ValueFunc>
|
||||||
|
void iteratorKeyImpl(KeyFunc key, ValueFunc val);
|
||||||
};
|
};
|
||||||
|
|
||||||
BenchmarkQtJson::BenchmarkQtJson(QObject *parent) : QObject(parent)
|
BenchmarkQtJson::BenchmarkQtJson(QObject *parent) : QObject(parent)
|
||||||
@ -117,6 +134,55 @@ void BenchmarkQtJson::variantMapInsert()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
namespace Iteration {
|
||||||
|
template <typename KeyFunc, typename ValueFunc>
|
||||||
|
void visitObject(const QJsonObject &o, KeyFunc key, ValueFunc val);
|
||||||
|
template <typename KeyFunc, typename ValueFunc>
|
||||||
|
void visitArray(const QJsonArray &a, KeyFunc key, ValueFunc val) {
|
||||||
|
for (const auto &value : a) {
|
||||||
|
if (value.isObject())
|
||||||
|
visitObject(value.toObject(), key, val);
|
||||||
|
else if (value.isArray())
|
||||||
|
visitArray(value.toArray(), key, val);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
template <typename KeyFunc, typename ValueFunc>
|
||||||
|
void visitObject(const QJsonObject &o, KeyFunc key, ValueFunc val) {
|
||||||
|
for (auto it = o.begin(), end = o.end(); it != end; ++it) {
|
||||||
|
[[maybe_unused]] const auto k = key(it);
|
||||||
|
[[maybe_unused]] const auto v = val(it);
|
||||||
|
if (it->isObject())
|
||||||
|
visitObject(it->toObject(), key, val);
|
||||||
|
else if (it->isArray())
|
||||||
|
visitArray(it->toArray(), key, val);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} // namespace Iteration
|
||||||
|
|
||||||
|
template <typename KeyFunc, typename ValueFunc>
|
||||||
|
void BenchmarkQtJson::iteratorKeyImpl(KeyFunc key, ValueFunc val)
|
||||||
|
{
|
||||||
|
const QString testFile = QFINDTESTDATA("test.json");
|
||||||
|
QVERIFY2(!testFile.isEmpty(), "cannot find test file test.json!");
|
||||||
|
QFile file(testFile);
|
||||||
|
QVERIFY2(file.open(QFile::ReadOnly), qPrintable(file.errorString()));
|
||||||
|
const QByteArray content = file.readAll();
|
||||||
|
QVERIFY(!content.isEmpty());
|
||||||
|
QJsonParseError error;
|
||||||
|
const auto doc = QJsonDocument::fromJson(content, &error);
|
||||||
|
QVERIFY2(!error.error, qPrintable(QString::asprintf("at offset %d: %ls",
|
||||||
|
error.offset,
|
||||||
|
qUtf16Printable(error.errorString()))));
|
||||||
|
QVERIFY(!doc.isNull());
|
||||||
|
QVERIFY(doc.isArray());
|
||||||
|
const auto array = doc.array();
|
||||||
|
QCOMPARE_GT(array.size(), 0);
|
||||||
|
|
||||||
|
QBENCHMARK {
|
||||||
|
Iteration::visitArray(array, key, val);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
QTEST_MAIN(BenchmarkQtJson)
|
QTEST_MAIN(BenchmarkQtJson)
|
||||||
#include "tst_bench_qtjson.moc"
|
#include "tst_bench_qtjson.moc"
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user