summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/corelib/serialization/qjsonparser.cpp54
-rw-r--r--tests/auto/corelib/serialization/json/tst_qtjson.cpp20
2 files changed, 64 insertions, 10 deletions
diff --git a/src/corelib/serialization/qjsonparser.cpp b/src/corelib/serialization/qjsonparser.cpp
index 7421902135..da0535cef2 100644
--- a/src/corelib/serialization/qjsonparser.cpp
+++ b/src/corelib/serialization/qjsonparser.cpp
@@ -379,10 +379,30 @@ error:
return QCborValue();
}
+// We need to retain the _last_ value for any duplicate keys and we need to deref containers.
+// Therefore the manual implementation of std::unique().
+template<typename Iterator, typename Compare, typename Assign>
+static Iterator customAssigningUniqueLast(Iterator first, Iterator last,
+ Compare compare, Assign assign)
+{
+ first = std::adjacent_find(first, last, compare);
+ if (first == last)
+ return last;
+
+ Iterator result = first;
+ while (++first != last) {
+ if (!compare(*result, *first))
+ ++result;
+ if (result != first)
+ assign(*result, *first);
+ }
+
+ return ++result;
+}
+
static void sortContainer(QCborContainerPrivate *container)
{
using Forward = QJsonPrivate::KeyIterator;
- using Reverse = std::reverse_iterator<Forward>;
using Value = Forward::value_type;
auto compare = [container](const Value &a, const Value &b)
@@ -420,17 +440,31 @@ static void sortContainer(QCborContainerPrivate *container)
}
};
- std::sort(Forward(container->elements.begin()), Forward(container->elements.end()),
- [&compare](const Value &a, const Value &b) { return compare(a, b) < 0; });
+ // The elements' containers are owned by the outer container, not by the elements themselves.
+ auto move = [](Forward::reference target, Forward::reference source)
+ {
+ QtCbor::Element &targetValue = target.value();
+
+ // If the target has a container, deref it before overwriting, so that we don't leak.
+ if (targetValue.flags & QtCbor::Element::IsContainer)
+ targetValue.container->deref();
+
+ // Do not move, so that we can clear the value afterwards.
+ target = source;
+
+ // Clear the source value, so that we don't store the same container twice.
+ source.value() = QtCbor::Element();
+ };
+
+ std::stable_sort(
+ Forward(container->elements.begin()), Forward(container->elements.end()),
+ [&compare](const Value &a, const Value &b) { return compare(a, b) < 0; });
- // We need to retain the _last_ value for any duplicate keys. Therefore the reverse dance here.
- auto it = std::unique(Reverse(container->elements.end()), Reverse(container->elements.begin()),
- [&compare](const Value &a, const Value &b) {
- return compare(a, b) == 0;
- }).base().elementsIterator();
+ Forward result = customAssigningUniqueLast(
+ Forward(container->elements.begin()), Forward(container->elements.end()),
+ [&compare](const Value &a, const Value &b) { return compare(a, b) == 0; }, move);
- // The erase from beginning is expensive but hopefully rare.
- container->elements.erase(container->elements.begin(), it);
+ container->elements.erase(result.elementsIterator(), container->elements.end());
}
diff --git a/tests/auto/corelib/serialization/json/tst_qtjson.cpp b/tests/auto/corelib/serialization/json/tst_qtjson.cpp
index 3e58dd03cc..c8f82ef5d5 100644
--- a/tests/auto/corelib/serialization/json/tst_qtjson.cpp
+++ b/tests/auto/corelib/serialization/json/tst_qtjson.cpp
@@ -176,6 +176,8 @@ private Q_SLOTS:
void fromToVariantConversions_data();
void fromToVariantConversions();
+ void noLeakOnNameClash();
+
private:
QString testDataDir;
};
@@ -3685,5 +3687,23 @@ void tst_QtJson::fromToVariantConversions()
}
}
+void tst_QtJson::noLeakOnNameClash()
+{
+ QJsonDocument doc = QJsonDocument::fromJson("{\"\":{\"\":0},\"\":0}");
+ QVERIFY(!doc.isNull());
+ const QJsonObject obj = doc.object();
+
+ // Removed the duplicate key.
+ QCOMPARE(obj.length(), 1);
+
+ // Retained the last of the duplicates.
+ const QJsonValue val = obj.begin().value();
+ QVERIFY(val.isDouble());
+ QCOMPARE(val.toDouble(), 0.0);
+
+ // It should not leak.
+ // In particular it should not forget to deref the container for the inner object.
+}
+
QTEST_MAIN(tst_QtJson)
#include "tst_qtjson.moc"