Implement QCborContainerPrivate::compact()
... and use it in QCborContainerPrivate::replaceAt_complex() to avoid unconstrained memory growth in certain scenarios. Remove the `reserved` parameter, because it was referring to the elements array, not to the byte data, so it cannot really be used in the implementation. Fixes: QTBUG-109073 Change-Id: I2e8fe7e4a4bf7a0ce06c87ca657f2bc01bae0341 Reviewed-by: Thiago Macieira <thiago.macieira@intel.com> (cherry picked from commit 83f0796192d9f0ed3bc005dbcf68b98de62955b8) Reviewed-by: Qt Cherry-pick Bot <cherrypick_bot@qt-project.org>
This commit is contained in:
parent
f0bc5cac4f
commit
6e269fe18e
@ -922,14 +922,24 @@ QCborContainerPrivate::~QCborContainerPrivate()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void QCborContainerPrivate::compact(qsizetype reserved)
|
void QCborContainerPrivate::compact()
|
||||||
{
|
{
|
||||||
if (usedData > data.size() / 2)
|
if (usedData > data.size() / 2)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
// 50% savings if we recreate the byte data
|
// 50% savings if we recreate the byte data
|
||||||
// ### TBD
|
QByteArray newData;
|
||||||
Q_UNUSED(reserved);
|
QByteArray::size_type newUsedData = 0;
|
||||||
|
// Compact only elements that have byte data.
|
||||||
|
// Nested containers will be compacted when their data changes.
|
||||||
|
for (auto &e : elements) {
|
||||||
|
if (e.flags & Element::HasByteData) {
|
||||||
|
if (const ByteData *b = byteData(e))
|
||||||
|
e.value = addByteDataImpl(newData, newUsedData, b->byte(), b->len);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
data = newData;
|
||||||
|
usedData = newUsedData;
|
||||||
}
|
}
|
||||||
|
|
||||||
QCborContainerPrivate *QCborContainerPrivate::clone(QCborContainerPrivate *d, qsizetype reserved)
|
QCborContainerPrivate *QCborContainerPrivate::clone(QCborContainerPrivate *d, qsizetype reserved)
|
||||||
@ -941,7 +951,7 @@ QCborContainerPrivate *QCborContainerPrivate::clone(QCborContainerPrivate *d, qs
|
|||||||
QExplicitlySharedDataPointer u(new QCborContainerPrivate(*d));
|
QExplicitlySharedDataPointer u(new QCborContainerPrivate(*d));
|
||||||
if (reserved >= 0) {
|
if (reserved >= 0) {
|
||||||
u->elements.reserve(reserved);
|
u->elements.reserve(reserved);
|
||||||
u->compact(reserved);
|
u->compact();
|
||||||
}
|
}
|
||||||
|
|
||||||
d = u.take();
|
d = u.take();
|
||||||
@ -1013,10 +1023,23 @@ void QCborContainerPrivate::replaceAt_complex(Element &e, const QCborValue &valu
|
|||||||
|
|
||||||
// Copy string data, if any
|
// Copy string data, if any
|
||||||
if (const ByteData *b = value.container->byteData(value.n)) {
|
if (const ByteData *b = value.container->byteData(value.n)) {
|
||||||
if (this == value.container)
|
const auto flags = e.flags;
|
||||||
e.value = addByteData(b->toByteArray(), b->len);
|
// The element e has an invalid e.value, because it is copied from
|
||||||
else
|
// value. It means that calling compact() will trigger an assertion
|
||||||
|
// or just silently corrupt the data.
|
||||||
|
// Temporarily unset the Element::HasByteData flag in order to skip
|
||||||
|
// the element e in the call to compact().
|
||||||
|
e.flags = e.flags & ~Element::HasByteData;
|
||||||
|
if (this == value.container) {
|
||||||
|
const QByteArray valueData = b->toByteArray();
|
||||||
|
compact();
|
||||||
|
e.value = addByteData(valueData, valueData.size());
|
||||||
|
} else {
|
||||||
|
compact();
|
||||||
e.value = addByteData(b->byte(), b->len);
|
e.value = addByteData(b->byte(), b->len);
|
||||||
|
}
|
||||||
|
// restore the flags
|
||||||
|
e.flags = flags;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (disp == MoveContainer)
|
if (disp == MoveContainer)
|
||||||
@ -1053,7 +1076,7 @@ QCborValue QCborContainerPrivate::extractAt_complex(Element e)
|
|||||||
// make a shallow copy of the byte data
|
// make a shallow copy of the byte data
|
||||||
container->appendByteData(b->byte(), b->len, e.type, e.flags);
|
container->appendByteData(b->byte(), b->len, e.type, e.flags);
|
||||||
usedData -= b->len + qsizetype(sizeof(*b));
|
usedData -= b->len + qsizetype(sizeof(*b));
|
||||||
compact(elements.size());
|
compact();
|
||||||
} else {
|
} else {
|
||||||
// just share with the original byte data
|
// just share with the original byte data
|
||||||
container->data = data;
|
container->data = data;
|
||||||
|
@ -103,18 +103,19 @@ public:
|
|||||||
QList<QtCbor::Element> elements;
|
QList<QtCbor::Element> elements;
|
||||||
|
|
||||||
void deref() { if (!ref.deref()) delete this; }
|
void deref() { if (!ref.deref()) delete this; }
|
||||||
void compact(qsizetype reserved);
|
void compact();
|
||||||
static QCborContainerPrivate *clone(QCborContainerPrivate *d, qsizetype reserved = -1);
|
static QCborContainerPrivate *clone(QCborContainerPrivate *d, qsizetype reserved = -1);
|
||||||
static QCborContainerPrivate *detach(QCborContainerPrivate *d, qsizetype reserved);
|
static QCborContainerPrivate *detach(QCborContainerPrivate *d, qsizetype reserved);
|
||||||
static QCborContainerPrivate *grow(QCborContainerPrivate *d, qsizetype index);
|
static QCborContainerPrivate *grow(QCborContainerPrivate *d, qsizetype index);
|
||||||
|
|
||||||
qptrdiff addByteData(const char *block, qsizetype len)
|
static qptrdiff addByteDataImpl(QByteArray &target, QByteArray::size_type &targetUsed,
|
||||||
|
const char *block, qsizetype len)
|
||||||
{
|
{
|
||||||
// This function does not do overflow checking, since the len parameter
|
// This function does not do overflow checking, since the len parameter
|
||||||
// is expected to be trusted. There's another version of this function
|
// is expected to be trusted. There's another version of this function
|
||||||
// in decodeStringFromCbor(), which checks.
|
// in decodeStringFromCbor(), which checks.
|
||||||
|
|
||||||
qptrdiff offset = data.size();
|
qptrdiff offset = target.size();
|
||||||
|
|
||||||
// align offset
|
// align offset
|
||||||
offset += alignof(QtCbor::ByteData) - 1;
|
offset += alignof(QtCbor::ByteData) - 1;
|
||||||
@ -122,10 +123,10 @@ public:
|
|||||||
|
|
||||||
qptrdiff increment = qptrdiff(sizeof(QtCbor::ByteData)) + len;
|
qptrdiff increment = qptrdiff(sizeof(QtCbor::ByteData)) + len;
|
||||||
|
|
||||||
usedData += increment;
|
targetUsed += increment;
|
||||||
data.resize(offset + increment);
|
target.resize(offset + increment);
|
||||||
|
|
||||||
char *ptr = data.begin() + offset;
|
char *ptr = target.begin() + offset;
|
||||||
auto b = new (ptr) QtCbor::ByteData;
|
auto b = new (ptr) QtCbor::ByteData;
|
||||||
b->len = len;
|
b->len = len;
|
||||||
if (block)
|
if (block)
|
||||||
@ -134,6 +135,11 @@ public:
|
|||||||
return offset;
|
return offset;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
qptrdiff addByteData(const char *block, qsizetype len)
|
||||||
|
{
|
||||||
|
return addByteDataImpl(data, usedData, block, len);
|
||||||
|
}
|
||||||
|
|
||||||
const QtCbor::ByteData *byteData(QtCbor::Element e) const
|
const QtCbor::ByteData *byteData(QtCbor::Element e) const
|
||||||
{
|
{
|
||||||
if ((e.flags & QtCbor::Element::HasByteData) == 0)
|
if ((e.flags & QtCbor::Element::HasByteData) == 0)
|
||||||
|
Loading…
x
Reference in New Issue
Block a user