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->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)
|
||||
{
|
||||
|
@ -914,7 +914,24 @@ QJsonObject::const_iterator QJsonObject::constFindImpl(T key) const
|
||||
iterator, although it can be done by calling QJsonObject::erase()
|
||||
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
|
||||
@ -930,7 +947,7 @@ QJsonObject::const_iterator QJsonObject::constFindImpl(T key) const
|
||||
the assignment will apply to the element in the QJsonArray or QJsonObject
|
||||
from which you got the reference.
|
||||
|
||||
\sa key(), operator*()
|
||||
\sa key(), keyView(), operator*()
|
||||
*/
|
||||
|
||||
/*! \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
|
||||
from which you got the reference.
|
||||
|
||||
\sa key()
|
||||
\sa key(), keyView()
|
||||
*/
|
||||
|
||||
/*! \fn QJsonValueRef *QJsonObject::iterator::operator->()
|
||||
@ -1189,14 +1206,27 @@ QJsonObject::const_iterator QJsonObject::constFindImpl(T key) const
|
||||
|
||||
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
|
||||
|
||||
Returns the current item's value.
|
||||
|
||||
\sa key(), operator*()
|
||||
\sa key(), keyView(), operator*()
|
||||
*/
|
||||
|
||||
/*! \fn const QJsonValueConstRef QJsonObject::const_iterator::operator*() const
|
||||
@ -1205,7 +1235,7 @@ QJsonObject::const_iterator QJsonObject::constFindImpl(T key) const
|
||||
|
||||
Same as value().
|
||||
|
||||
\sa key()
|
||||
\sa key(), keyView()
|
||||
*/
|
||||
|
||||
/*! \fn const QJsonValueConstRef *QJsonObject::const_iterator::operator->() const
|
||||
|
@ -102,6 +102,7 @@ public:
|
||||
}
|
||||
|
||||
inline QString key() const { return item.objectKey(); }
|
||||
QAnyStringView keyView() const { return item.objectKeyView(); }
|
||||
inline QJsonValueRef value() const { return item; }
|
||||
inline QJsonValueRef operator*() const { return item; }
|
||||
inline const QJsonValueConstRef *operator->() const { return &item; }
|
||||
@ -215,6 +216,7 @@ public:
|
||||
}
|
||||
|
||||
inline QString key() const { return item.objectKey(); }
|
||||
QAnyStringView keyView() const { return item.objectKeyView(); }
|
||||
inline QJsonValueConstRef value() 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));
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
Q_ASSERT(self.is_object);
|
||||
|
@ -201,6 +201,9 @@ protected:
|
||||
Q_CORE_EXPORT static QString objectKey(QJsonValueConstRef self);
|
||||
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)
|
||||
QJsonValueConstRef(QJsonArray *array, qsizetype 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) {
|
||||
QJsonValue value = it.value();
|
||||
QCOMPARE(it.keyView(), QString::number(it.value().toInteger()));
|
||||
QCOMPARE((double)it.key().toInt(), value.toDouble());
|
||||
QT_TEST_EQUALITY_OPS(it, QJsonObject::iterator(), false);
|
||||
}
|
||||
@ -1600,8 +1601,10 @@ void tst_QtJson::keySorting()
|
||||
QCOMPARE(o.keys(), sortedKeys);
|
||||
QJsonObject::const_iterator it = o.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.keyView(), *it2);
|
||||
}
|
||||
}
|
||||
|
||||
void tst_QtJson::undefinedValues()
|
||||
|
@ -306,6 +306,7 @@ void tst_QCborValue_Json::nonStringKeysInMaps()
|
||||
QJsonObject o = m.toJsonObject();
|
||||
auto it = o.begin();
|
||||
QVERIFY(it != o.end());
|
||||
QCOMPARE(it.keyView(), converted);
|
||||
QCOMPARE(it.key(), converted);
|
||||
QCOMPARE(it.value(), 0);
|
||||
QCOMPARE(++it, o.end());
|
||||
|
@ -2,6 +2,8 @@
|
||||
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
|
||||
|
||||
#include <QTest>
|
||||
|
||||
#include <QtCore/qjsonarray.h>
|
||||
#include <QVariantMap>
|
||||
#include <qjsondocument.h>
|
||||
#include <qjsonobject.h>
|
||||
@ -24,6 +26,21 @@ private Q_SLOTS:
|
||||
|
||||
void jsonObjectInsert();
|
||||
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)
|
||||
@ -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)
|
||||
#include "tst_bench_qtjson.moc"
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user