Remove '\0' space reservation logic in QByteArray

Changed QByteArray to use implicit element reserved by QArrayData

Task-number: QTBUG-84320
Change-Id: I2a0091c814a47a5c052da7a83d10cb641834c7bd
Reviewed-by: Lars Knoll <lars.knoll@qt.io>
Reviewed-by: Mårten Nordheim <marten.nordheim@qt.io>
This commit is contained in:
Andrei Golubev 2020-08-26 20:19:21 +02:00
parent 7249a87b06
commit 04d78df344
2 changed files with 39 additions and 25 deletions

View File

@ -642,7 +642,7 @@ QByteArray qUncompress(const uchar* data, qsizetype nbytes)
return invalidCompressedData(); return invalidCompressedData();
} }
QByteArray::DataPointer d(QByteArray::Data::allocate(expectedSize + 1)); QByteArray::DataPointer d(QByteArray::Data::allocate(len));
if (Q_UNLIKELY(d.data() == nullptr)) if (Q_UNLIKELY(d.data() == nullptr))
return invalidCompressedData(); return invalidCompressedData();
@ -1149,12 +1149,11 @@ QByteArray &QByteArray::operator=(const char *str)
d = DataPointer::fromRawData(&_empty, 0); d = DataPointer::fromRawData(&_empty, 0);
} else { } else {
const qsizetype len = qsizetype(strlen(str)); const qsizetype len = qsizetype(strlen(str));
const size_t fullLen = size_t(len) + 1;
const auto capacityAtEnd = d->allocatedCapacity() - d.freeSpaceAtBegin(); const auto capacityAtEnd = d->allocatedCapacity() - d.freeSpaceAtBegin();
if (d->needsDetach() || fullLen > capacityAtEnd if (d->needsDetach() || size_t(len) > capacityAtEnd
|| (len < size() && fullLen < (capacityAtEnd >> 1))) || (len < size() && size_t(len) < (capacityAtEnd >> 1)))
reallocData(fullLen, d->detachFlags()); reallocData(len, d->detachFlags());
memcpy(d.data(), str, fullLen); // include null terminator memcpy(d.data(), str, len + 1); // include null terminator
d.size = len; d.size = len;
} }
return *this; return *this;
@ -1582,10 +1581,14 @@ QByteArray::QByteArray(const char *data, qsizetype size)
d = DataPointer(); d = DataPointer();
} else { } else {
if (size < 0) if (size < 0)
size = int(strlen(data)); size = qstrlen(data);
d = DataPointer(Data::allocate(size + 1u), size); if (!size) {
memcpy(d.data(), data, size); d = DataPointer::fromRawData(&_empty, 0);
d.data()[size] = '\0'; } else {
d = DataPointer(Data::allocate(size), size);
memcpy(d.data(), data, size);
d.data()[size] = '\0';
}
} }
} }
@ -1600,7 +1603,7 @@ QByteArray::QByteArray(qsizetype size, char ch)
if (size <= 0) { if (size <= 0) {
d = DataPointer::fromRawData(&_empty, 0); d = DataPointer::fromRawData(&_empty, 0);
} else { } else {
d = DataPointer(Data::allocate(size + 1u), size); d = DataPointer(Data::allocate(size), size);
memset(d.data(), ch, size); memset(d.data(), ch, size);
d.data()[size] = '\0'; d.data()[size] = '\0';
} }
@ -1614,8 +1617,12 @@ QByteArray::QByteArray(qsizetype size, char ch)
QByteArray::QByteArray(qsizetype size, Qt::Initialization) QByteArray::QByteArray(qsizetype size, Qt::Initialization)
{ {
d = DataPointer(Data::allocate(size + 1u), size); if (size <= 0) {
d.data()[size] = '\0'; d = DataPointer::fromRawData(&_empty, 0);
} else {
d = DataPointer(Data::allocate(size), size);
d.data()[size] = '\0';
}
} }
/*! /*!
@ -1637,7 +1644,7 @@ void QByteArray::resize(qsizetype size)
const auto capacityAtEnd = capacity() - d.freeSpaceAtBegin(); const auto capacityAtEnd = capacity() - d.freeSpaceAtBegin();
if (d->needsDetach() || size > capacityAtEnd) if (d->needsDetach() || size > capacityAtEnd)
reallocData(size_t(size) + 1u, d->detachFlags() | Data::GrowsForward); reallocData(size_t(size), d->detachFlags() | Data::GrowsForward);
d.size = size; d.size = size;
if (d->allocatedCapacity()) if (d->allocatedCapacity())
d.data()[size] = 0; d.data()[size] = 0;
@ -1663,13 +1670,18 @@ QByteArray &QByteArray::fill(char ch, qsizetype size)
void QByteArray::reallocData(size_t alloc, Data::ArrayOptions options) void QByteArray::reallocData(size_t alloc, Data::ArrayOptions options)
{ {
if (!alloc) {
d = DataPointer::fromRawData(&_empty, 0);
return;
}
// there's a case of slow reallocate path where we need to memmove the data // there's a case of slow reallocate path where we need to memmove the data
// before a call to ::realloc(), meaning that there's an extra "heavy" // before a call to ::realloc(), meaning that there's an extra "heavy"
// operation. just prefer ::malloc() branch in this case // operation. just prefer ::malloc() branch in this case
const bool slowReallocatePath = d.freeSpaceAtBegin() > 0; const bool slowReallocatePath = d.freeSpaceAtBegin() > 0;
if (d->needsDetach() || slowReallocatePath) { if (d->needsDetach() || slowReallocatePath) {
DataPointer dd(Data::allocate(alloc, options), qMin(qsizetype(alloc) - 1, d.size)); DataPointer dd(Data::allocate(alloc, options), qMin(qsizetype(alloc), d.size));
if (dd.size > 0) if (dd.size > 0)
::memcpy(dd.data(), d.data(), dd.size); ::memcpy(dd.data(), d.data(), dd.size);
dd.data()[dd.size] = 0; dd.data()[dd.size] = 0;
@ -1681,8 +1693,11 @@ void QByteArray::reallocData(size_t alloc, Data::ArrayOptions options)
void QByteArray::reallocGrowData(size_t alloc, Data::ArrayOptions options) void QByteArray::reallocGrowData(size_t alloc, Data::ArrayOptions options)
{ {
if (!alloc) // expected to always allocate
alloc = 1;
if (d->needsDetach()) { if (d->needsDetach()) {
const auto newSize = qMin(qsizetype(alloc) - 1, d.size); const auto newSize = qMin(qsizetype(alloc), d.size);
DataPointer dd(DataPointer::allocateGrow(d, alloc, newSize, options)); DataPointer dd(DataPointer::allocateGrow(d, alloc, newSize, options));
dd->copyAppend(d.data(), d.data() + newSize); dd->copyAppend(d.data(), d.data() + newSize);
dd.data()[dd.size] = 0; dd.data()[dd.size] = 0;
@ -1851,7 +1866,7 @@ QByteArray& QByteArray::append(char ch)
{ {
const bool shouldGrow = d->shouldGrowBeforeInsert(d.end(), 1); const bool shouldGrow = d->shouldGrowBeforeInsert(d.end(), 1);
if (d->needsDetach() || size() + 1 > capacity() || shouldGrow) if (d->needsDetach() || size() + 1 > capacity() || shouldGrow)
reallocGrowData(size_t(size()) + 2u, d->detachFlags() | Data::GrowsForward); reallocGrowData(size_t(size()) + 1u, d->detachFlags() | Data::GrowsForward);
d->copyAppend(1, ch); d->copyAppend(1, ch);
d.data()[d.size] = '\0'; d.data()[d.size] = '\0';
return *this; return *this;
@ -1888,7 +1903,7 @@ QByteArray &QByteArray::insert(qsizetype i, QByteArrayView data)
auto flags = d->detachFlags() | Data::GrowsForward; auto flags = d->detachFlags() | Data::GrowsForward;
if (oldSize != 0 && i <= oldSize / 4) // using QList's policy if (oldSize != 0 && i <= oldSize / 4) // using QList's policy
flags |= Data::GrowsBackwards; flags |= Data::GrowsBackwards;
reallocGrowData(newSize + 1, flags); reallocGrowData(newSize, flags);
} }
if (i > oldSize) // set spaces in the uninitialized gap if (i > oldSize) // set spaces in the uninitialized gap
@ -1944,7 +1959,7 @@ QByteArray &QByteArray::insert(qsizetype i, qsizetype count, char ch)
auto flags = d->detachFlags() | Data::GrowsForward; auto flags = d->detachFlags() | Data::GrowsForward;
if (oldSize != 0 && i <= oldSize / 4) // using QList's policy if (oldSize != 0 && i <= oldSize / 4) // using QList's policy
flags |= Data::GrowsBackwards; flags |= Data::GrowsBackwards;
reallocGrowData(newSize + 1, flags); reallocGrowData(newSize, flags);
} }
if (i > oldSize) // set spaces in the uninitialized gap if (i > oldSize) // set spaces in the uninitialized gap

View File

@ -1,6 +1,6 @@
/**************************************************************************** /****************************************************************************
** **
** Copyright (C) 2016 The Qt Company Ltd. ** Copyright (C) 2020 The Qt Company Ltd.
** Copyright (C) 2016 Intel Corporation. ** Copyright (C) 2016 Intel Corporation.
** Contact: https://www.qt.io/licensing/ ** Contact: https://www.qt.io/licensing/
** **
@ -513,19 +513,18 @@ inline const char *QByteArray::data() const
inline const char *QByteArray::constData() const inline const char *QByteArray::constData() const
{ return data(); } { return data(); }
inline void QByteArray::detach() inline void QByteArray::detach()
{ if (d->needsDetach()) reallocData(size_t(size()) + 1u, d->detachFlags()); } { if (d->needsDetach()) reallocData(size_t(size()), d->detachFlags()); }
inline bool QByteArray::isDetached() const inline bool QByteArray::isDetached() const
{ return !d->isShared(); } { return !d->isShared(); }
inline QByteArray::QByteArray(const QByteArray &a) noexcept : d(a.d) inline QByteArray::QByteArray(const QByteArray &a) noexcept : d(a.d)
{} {}
inline qsizetype QByteArray::capacity() const inline qsizetype QByteArray::capacity() const { return qsizetype(d->constAllocatedCapacity()); }
{ const auto realCapacity = d->constAllocatedCapacity(); return realCapacity ? realCapacity - 1 : 0; }
inline void QByteArray::reserve(qsizetype asize) inline void QByteArray::reserve(qsizetype asize)
{ {
if (d->needsDetach() || asize > capacity() - d->freeSpaceAtBegin()) { if (d->needsDetach() || asize > capacity() - d->freeSpaceAtBegin()) {
reallocData(qMax(size_t(size()), size_t(asize)) + 1u, d->detachFlags() | Data::CapacityReserved); reallocData(qMax(size_t(size()), size_t(asize)), d->detachFlags() | Data::CapacityReserved);
} else { } else {
d->setFlag(Data::CapacityReserved); d->setFlag(Data::CapacityReserved);
} }
@ -536,7 +535,7 @@ inline void QByteArray::squeeze()
if ((d->flags() & Data::CapacityReserved) == 0) if ((d->flags() & Data::CapacityReserved) == 0)
return; return;
if (d->needsDetach() || size() < capacity()) { if (d->needsDetach() || size() < capacity()) {
reallocData(size_t(size()) + 1u, d->detachFlags() & ~Data::CapacityReserved); reallocData(size_t(size()), d->detachFlags() & ~Data::CapacityReserved);
} else { } else {
d->clearFlag(Data::CapacityReserved); d->clearFlag(Data::CapacityReserved);
} }