Clean up out allocation handling
Get rid of the allocation options inside the flags field of QArrayData, they are really a completely separate thing. Change-Id: I823750ab9e4ca85642a0bd0e471ee79c9cde43fb Reviewed-by: Andrei Golubev <andrei.golubev@qt.io> Reviewed-by: Thiago Macieira <thiago.macieira@intel.com>
This commit is contained in:
parent
419eaa0679
commit
b76fbb48fb
@ -1175,7 +1175,8 @@ QByteArray &QByteArray::operator=(const char *str)
|
||||
const auto capacityAtEnd = d->allocatedCapacity() - d.freeSpaceAtBegin();
|
||||
if (d->needsDetach() || len > capacityAtEnd
|
||||
|| (len < size() && len < (capacityAtEnd >> 1)))
|
||||
reallocData(len, d->detachFlags());
|
||||
// ### inefficient! reallocData() does copy the old data and we then overwrite it in the next line
|
||||
reallocData(len, QArrayData::KeepSize);
|
||||
memcpy(d.data(), str, len + 1); // include null terminator
|
||||
d.size = len;
|
||||
}
|
||||
@ -1676,7 +1677,7 @@ void QByteArray::resize(qsizetype size)
|
||||
|
||||
const auto capacityAtEnd = capacity() - d.freeSpaceAtBegin();
|
||||
if (d->needsDetach() || size > capacityAtEnd)
|
||||
reallocData(size, d->detachFlags() | Data::GrowsForward);
|
||||
reallocData(size, QArrayData::Grow);
|
||||
d.size = size;
|
||||
if (d->allocatedCapacity())
|
||||
d.data()[size] = 0;
|
||||
@ -1700,7 +1701,7 @@ QByteArray &QByteArray::fill(char ch, qsizetype size)
|
||||
return *this;
|
||||
}
|
||||
|
||||
void QByteArray::reallocData(qsizetype alloc, Data::ArrayOptions options)
|
||||
void QByteArray::reallocData(qsizetype alloc, QArrayData::AllocationOption option)
|
||||
{
|
||||
if (!alloc) {
|
||||
d = DataPointer::fromRawData(&_empty, 0);
|
||||
@ -1713,13 +1714,13 @@ void QByteArray::reallocData(qsizetype alloc, Data::ArrayOptions options)
|
||||
const bool slowReallocatePath = d.freeSpaceAtBegin() > 0;
|
||||
|
||||
if (d->needsDetach() || slowReallocatePath) {
|
||||
DataPointer dd(Data::allocate(alloc, options), qMin(alloc, d.size));
|
||||
DataPointer dd(Data::allocate(alloc, option), qMin(alloc, d.size));
|
||||
if (dd.size > 0)
|
||||
::memcpy(dd.data(), d.data(), dd.size);
|
||||
dd.data()[dd.size] = 0;
|
||||
d = dd;
|
||||
} else {
|
||||
d->reallocate(alloc, options & (QArrayData::GrowsBackwards|QArrayData::GrowsForward) ? QArrayData::Grow : QArrayData::KeepSize);
|
||||
d->reallocate(alloc, option);
|
||||
}
|
||||
}
|
||||
|
||||
@ -1729,7 +1730,7 @@ void QByteArray::reallocGrowData(qsizetype n)
|
||||
n = 1;
|
||||
|
||||
if (d->needsDetach()) {
|
||||
DataPointer dd(DataPointer::allocateGrow(d, n, DataPointer::AllocateAtEnd));
|
||||
DataPointer dd(DataPointer::allocateGrow(d, n, QArrayData::AllocateAtEnd));
|
||||
dd->copyAppend(d.data(), d.data() + d.size);
|
||||
dd.data()[dd.size] = 0;
|
||||
d = dd;
|
||||
@ -1936,9 +1937,9 @@ QByteArray &QByteArray::insert(qsizetype i, QByteArrayView data)
|
||||
sizeToGrow += i - oldSize;
|
||||
|
||||
if (d->needsDetach() || (sizeToGrow > d.freeSpaceAtBegin() && sizeToGrow > d.freeSpaceAtEnd())) {
|
||||
DataPointer::AllocationPosition pos = DataPointer::AllocateAtEnd;
|
||||
QArrayData::AllocationPosition pos = QArrayData::AllocateAtEnd;
|
||||
if (oldSize != 0 && i <= (oldSize >> 1))
|
||||
pos = DataPointer::AllocateAtBeginning;
|
||||
pos = QArrayData::AllocateAtBeginning;
|
||||
|
||||
DataPointer detached(DataPointer::allocateGrow(d, sizeToGrow, pos));
|
||||
auto where = d.constBegin() + qMin(i, d->size);
|
||||
@ -2000,9 +2001,9 @@ QByteArray &QByteArray::insert(qsizetype i, qsizetype count, char ch)
|
||||
sizeToGrow += i - oldSize;
|
||||
|
||||
if (d->needsDetach() || (sizeToGrow > d.freeSpaceAtBegin() && sizeToGrow > d.freeSpaceAtEnd())) {
|
||||
DataPointer::AllocationPosition pos = DataPointer::AllocateAtEnd;
|
||||
QArrayData::AllocationPosition pos = QArrayData::AllocateAtEnd;
|
||||
if (oldSize != 0 && i <= (oldSize >> 1))
|
||||
pos = DataPointer::AllocateAtBeginning;
|
||||
pos = QArrayData::AllocateAtBeginning;
|
||||
|
||||
DataPointer detached(DataPointer::allocateGrow(d, sizeToGrow, pos));
|
||||
auto where = d.constBegin() + qMin(i, d->size);
|
||||
|
@ -509,7 +509,7 @@ public:
|
||||
}
|
||||
|
||||
private:
|
||||
void reallocData(qsizetype alloc, Data::ArrayOptions options);
|
||||
void reallocData(qsizetype alloc, QArrayData::AllocationOption option);
|
||||
void reallocGrowData(qsizetype n);
|
||||
void expand(qsizetype i);
|
||||
QByteArray nulTerminated() const;
|
||||
@ -562,7 +562,7 @@ inline const char *QByteArray::data() const
|
||||
inline const char *QByteArray::constData() const
|
||||
{ return data(); }
|
||||
inline void QByteArray::detach()
|
||||
{ if (d->needsDetach()) reallocData(size(), d->detachFlags()); }
|
||||
{ if (d->needsDetach()) reallocData(size(), QArrayData::KeepSize); }
|
||||
inline bool QByteArray::isDetached() const
|
||||
{ return !d->isShared(); }
|
||||
inline QByteArray::QByteArray(const QByteArray &a) noexcept : d(a.d)
|
||||
@ -572,22 +572,20 @@ inline qsizetype QByteArray::capacity() const { return qsizetype(d->constAllocat
|
||||
|
||||
inline void QByteArray::reserve(qsizetype asize)
|
||||
{
|
||||
if (d->needsDetach() || asize > capacity() - d->freeSpaceAtBegin()) {
|
||||
reallocData(qMax(size(), asize), d->detachFlags() | Data::CapacityReserved);
|
||||
} else {
|
||||
if (d->needsDetach() || asize > capacity() - d->freeSpaceAtBegin())
|
||||
reallocData(qMax(size(), asize), QArrayData::KeepSize);
|
||||
if (d->constAllocatedCapacity())
|
||||
d->setFlag(Data::CapacityReserved);
|
||||
}
|
||||
}
|
||||
|
||||
inline void QByteArray::squeeze()
|
||||
{
|
||||
if (!d.isMutable())
|
||||
return;
|
||||
if (d->needsDetach() || size() < capacity()) {
|
||||
reallocData(size(), d->detachFlags() & ~Data::CapacityReserved);
|
||||
} else {
|
||||
if (d->needsDetach() || size() < capacity())
|
||||
reallocData(size(), QArrayData::KeepSize);
|
||||
if (d->constAllocatedCapacity())
|
||||
d->clearFlag(Data::CapacityReserved);
|
||||
}
|
||||
}
|
||||
|
||||
inline char &QByteArray::operator[](qsizetype i)
|
||||
|
@ -2415,7 +2415,7 @@ void QString::resize(qsizetype size)
|
||||
|
||||
const auto capacityAtEnd = capacity() - d.freeSpaceAtBegin();
|
||||
if (d->needsDetach() || size > capacityAtEnd)
|
||||
reallocData(size, d->detachFlags() | Data::GrowsForward);
|
||||
reallocData(size, QArrayData::Grow);
|
||||
d.size = size;
|
||||
if (d->allocatedCapacity())
|
||||
d.data()[size] = 0;
|
||||
@ -2497,7 +2497,7 @@ void QString::resize(qsizetype size, QChar fillChar)
|
||||
\sa reserve(), capacity()
|
||||
*/
|
||||
|
||||
void QString::reallocData(qsizetype alloc, Data::ArrayOptions allocOptions)
|
||||
void QString::reallocData(qsizetype alloc, QArrayData::AllocationOption option)
|
||||
{
|
||||
if (!alloc) {
|
||||
d = DataPointer::fromRawData(&_empty, 0);
|
||||
@ -2510,13 +2510,13 @@ void QString::reallocData(qsizetype alloc, Data::ArrayOptions allocOptions)
|
||||
const bool slowReallocatePath = d.freeSpaceAtBegin() > 0;
|
||||
|
||||
if (d->needsDetach() || slowReallocatePath) {
|
||||
DataPointer dd(Data::allocate(alloc, allocOptions), qMin(alloc, d.size));
|
||||
DataPointer dd(Data::allocate(alloc, option), qMin(alloc, d.size));
|
||||
if (dd.size > 0)
|
||||
::memcpy(dd.data(), d.data(), dd.size * sizeof(QChar));
|
||||
dd.data()[dd.size] = 0;
|
||||
d = dd;
|
||||
} else {
|
||||
d->reallocate(alloc, allocOptions & (QArrayData::GrowsBackwards|QArrayData::GrowsForward) ? QArrayData::Grow : QArrayData::KeepSize);
|
||||
d->reallocate(alloc, option);
|
||||
}
|
||||
}
|
||||
|
||||
@ -2526,7 +2526,7 @@ void QString::reallocGrowData(qsizetype n)
|
||||
n = 1;
|
||||
|
||||
if (d->needsDetach()) {
|
||||
DataPointer dd(DataPointer::allocateGrow(d, n, DataPointer::AllocateAtEnd));
|
||||
DataPointer dd(DataPointer::allocateGrow(d, n, QArrayData::AllocateAtEnd));
|
||||
dd->copyAppend(d.data(), d.data() + d.size);
|
||||
dd.data()[dd.size] = 0;
|
||||
d = dd;
|
||||
@ -2737,9 +2737,9 @@ QString& QString::insert(qsizetype i, const QChar *unicode, qsizetype size)
|
||||
sizeToGrow += i - oldSize;
|
||||
|
||||
if (d->needsDetach() || (sizeToGrow > d.freeSpaceAtBegin() && sizeToGrow > d.freeSpaceAtEnd())) {
|
||||
DataPointer::AllocationPosition pos = DataPointer::AllocateAtEnd;
|
||||
QArrayData::AllocationPosition pos = QArrayData::AllocateAtEnd;
|
||||
if (oldSize != 0 && i <= (oldSize >> 1))
|
||||
pos = DataPointer::AllocateAtBeginning;
|
||||
pos = QArrayData::AllocateAtBeginning;
|
||||
|
||||
DataPointer detached(DataPointer::allocateGrow(d, sizeToGrow, pos));
|
||||
auto where = d.constBegin() + qMin(i, d->size);
|
||||
@ -4054,7 +4054,7 @@ QString &QString::replace(const QRegularExpression &re, const QString &after)
|
||||
if (!iterator.hasNext()) // no matches at all
|
||||
return *this;
|
||||
|
||||
reallocData(d.size, d->detachFlags());
|
||||
reallocData(d.size, QArrayData::KeepSize);
|
||||
|
||||
qsizetype numCaptures = re.captureCount();
|
||||
|
||||
@ -6157,7 +6157,7 @@ const ushort *QString::utf16() const
|
||||
{
|
||||
if (!d->isMutable()) {
|
||||
// ensure '\0'-termination for ::fromRawData strings
|
||||
const_cast<QString*>(this)->reallocData(d.size, d->detachFlags());
|
||||
const_cast<QString*>(this)->reallocData(d.size, QArrayData::KeepSize);
|
||||
}
|
||||
return reinterpret_cast<const ushort *>(d.data());
|
||||
}
|
||||
|
@ -1058,7 +1058,7 @@ private:
|
||||
DataPointer d;
|
||||
static const char16_t _empty;
|
||||
|
||||
void reallocData(qsizetype alloc, Data::ArrayOptions options);
|
||||
void reallocData(qsizetype alloc, QArrayData::AllocationOption option);
|
||||
void reallocGrowData(qsizetype n);
|
||||
static int compare_helper(const QChar *data1, qsizetype length1,
|
||||
const QChar *data2, qsizetype length2,
|
||||
@ -1195,7 +1195,7 @@ inline QChar *QString::data()
|
||||
inline const QChar *QString::constData() const
|
||||
{ return data(); }
|
||||
inline void QString::detach()
|
||||
{ if (d->needsDetach()) reallocData(d.size, d->detachFlags()); }
|
||||
{ if (d->needsDetach()) reallocData(d.size, QArrayData::KeepSize); }
|
||||
inline bool QString::isDetached() const
|
||||
{ return !d->isShared(); }
|
||||
inline void QString::clear()
|
||||
@ -1267,22 +1267,20 @@ inline QString::~QString() {}
|
||||
|
||||
inline void QString::reserve(qsizetype asize)
|
||||
{
|
||||
if (d->needsDetach() || asize >= capacity() - d.freeSpaceAtBegin()) {
|
||||
reallocData(qMax(asize, size()), d->detachFlags() | Data::CapacityReserved);
|
||||
} else {
|
||||
if (d->needsDetach() || asize >= capacity() - d.freeSpaceAtBegin())
|
||||
reallocData(qMax(asize, size()), QArrayData::KeepSize);
|
||||
if (d->constAllocatedCapacity())
|
||||
d->setFlag(Data::CapacityReserved);
|
||||
}
|
||||
}
|
||||
|
||||
inline void QString::squeeze()
|
||||
{
|
||||
if (!d.isMutable())
|
||||
return;
|
||||
if (d->needsDetach() || size() < capacity()) {
|
||||
reallocData(d.size, d->detachFlags() & ~Data::CapacityReserved);
|
||||
} else {
|
||||
if (d->needsDetach() || size() < capacity())
|
||||
reallocData(d.size, QArrayData::KeepSize);
|
||||
if (d->constAllocatedCapacity())
|
||||
d->clearFlag(Data::CapacityReserved);
|
||||
}
|
||||
}
|
||||
|
||||
inline QString &QString::setUtf16(const ushort *autf16, qsizetype asize)
|
||||
|
@ -185,7 +185,7 @@ static QArrayData *allocateData(qsizetype allocSize)
|
||||
}
|
||||
|
||||
void *QArrayData::allocate(QArrayData **dptr, qsizetype objectSize, qsizetype alignment,
|
||||
qsizetype capacity, ArrayOptions options) noexcept
|
||||
qsizetype capacity, QArrayData::AllocationOption option) noexcept
|
||||
{
|
||||
Q_ASSERT(dptr);
|
||||
// Alignment is a power of two
|
||||
@ -208,7 +208,7 @@ void *QArrayData::allocate(QArrayData **dptr, qsizetype objectSize, qsizetype al
|
||||
}
|
||||
Q_ASSERT(headerSize > 0);
|
||||
|
||||
qsizetype allocSize = calculateBlockSize(capacity, objectSize, headerSize, (options & (GrowsForward|GrowsBackwards)) ? QArrayData::Grow : QArrayData::KeepSize);
|
||||
qsizetype allocSize = calculateBlockSize(capacity, objectSize, headerSize, option);
|
||||
allocSize = reserveExtraBytes(allocSize);
|
||||
if (Q_UNLIKELY(allocSize < 0)) { // handle overflow. cannot allocate reliably
|
||||
*dptr = nullptr;
|
||||
@ -220,7 +220,6 @@ void *QArrayData::allocate(QArrayData **dptr, qsizetype objectSize, qsizetype al
|
||||
if (header) {
|
||||
// find where offset should point to so that data() is aligned to alignment bytes
|
||||
data = QTypedArrayData<void>::dataStart(header, alignment);
|
||||
header->flags = options & CapacityReserved;
|
||||
header->alloc = qsizetype(capacity);
|
||||
}
|
||||
|
||||
|
@ -56,12 +56,14 @@ struct Q_CORE_EXPORT QArrayData
|
||||
KeepSize
|
||||
};
|
||||
|
||||
enum ArrayOption {
|
||||
/// this option is used by the allocate() function
|
||||
DefaultAllocationFlags = 0,
|
||||
CapacityReserved = 0x1, //!< the capacity was reserved by the user, try to keep it
|
||||
GrowsForward = 0x2, //!< allocate with eyes towards growing through append()
|
||||
GrowsBackwards = 0x4 //!< allocate with eyes towards growing through prepend()
|
||||
enum AllocationPosition {
|
||||
AllocateAtEnd,
|
||||
AllocateAtBeginning
|
||||
};
|
||||
|
||||
enum ArrayOption {
|
||||
ArrayOptionDefault = 0,
|
||||
CapacityReserved = 0x1 //!< the capacity was reserved by the user, try to keep it
|
||||
};
|
||||
Q_DECLARE_FLAGS(ArrayOptions, ArrayOption)
|
||||
|
||||
@ -112,20 +114,12 @@ struct Q_CORE_EXPORT QArrayData
|
||||
return newSize;
|
||||
}
|
||||
|
||||
ArrayOptions detachFlags() const noexcept
|
||||
{
|
||||
ArrayOptions result = DefaultAllocationFlags;
|
||||
if (flags & CapacityReserved)
|
||||
result |= CapacityReserved;
|
||||
return result;
|
||||
}
|
||||
|
||||
[[nodiscard]]
|
||||
#if defined(Q_CC_GNU)
|
||||
__attribute__((__malloc__))
|
||||
#endif
|
||||
static void *allocate(QArrayData **pdata, qsizetype objectSize, qsizetype alignment,
|
||||
qsizetype capacity, ArrayOptions options = DefaultAllocationFlags) noexcept;
|
||||
qsizetype capacity, AllocationOption option = QArrayData::KeepSize) noexcept;
|
||||
[[nodiscard]] static QPair<QArrayData *, void *> reallocateUnaligned(QArrayData *data, void *dataPointer,
|
||||
qsizetype objectSize, qsizetype newCapacity, AllocationOption option) noexcept;
|
||||
static void deallocate(QArrayData *data, qsizetype objectSize,
|
||||
@ -213,12 +207,11 @@ struct QTypedArrayData
|
||||
|
||||
struct AlignmentDummy { QArrayData header; T data; };
|
||||
|
||||
[[nodiscard]] static QPair<QTypedArrayData *, T *> allocate(qsizetype capacity,
|
||||
ArrayOptions options = DefaultAllocationFlags)
|
||||
[[nodiscard]] static QPair<QTypedArrayData *, T *> allocate(qsizetype capacity, AllocationOption option = QArrayData::KeepSize)
|
||||
{
|
||||
static_assert(sizeof(QTypedArrayData) == sizeof(QArrayData));
|
||||
QArrayData *d;
|
||||
void *result = QArrayData::allocate(&d, sizeof(T), alignof(AlignmentDummy), capacity, options);
|
||||
void *result = QArrayData::allocate(&d, sizeof(T), alignof(AlignmentDummy), capacity, option);
|
||||
#if (defined(Q_CC_GNU) && Q_CC_GNU >= 407) || QT_HAS_BUILTIN(__builtin_assume_aligned)
|
||||
result = __builtin_assume_aligned(result, Q_ALIGNOF(AlignmentDummy));
|
||||
#endif
|
||||
|
@ -1094,33 +1094,6 @@ public:
|
||||
}
|
||||
}
|
||||
|
||||
// Returns whether reallocation is desirable before adding more elements
|
||||
// into the container. This is a helper function that one can use to
|
||||
// theoretically improve average operations performance. Ignoring this
|
||||
// function does not affect the correctness of the array operations.
|
||||
bool shouldGrowBeforeInsert(const_iterator where, qsizetype n) const noexcept
|
||||
{
|
||||
if (this->d == nullptr)
|
||||
return true;
|
||||
if (this->d->flags & QArrayData::CapacityReserved)
|
||||
return false;
|
||||
if (!(this->d->flags & (QArrayData::GrowsForward | QArrayData::GrowsBackwards)))
|
||||
return false;
|
||||
Q_ASSERT(where >= this->begin() && where <= this->end()); // in range
|
||||
|
||||
const qsizetype freeAtBegin = this->freeSpaceAtBegin();
|
||||
const qsizetype freeAtEnd = this->freeSpaceAtEnd();
|
||||
|
||||
// Idea: always reallocate when not enough space at the corresponding end
|
||||
if (where == this->end()) { // append or size == 0
|
||||
return freeAtEnd < n;
|
||||
} else if (where == this->begin()) { // prepend
|
||||
return freeAtBegin < n;
|
||||
} else { // general insert
|
||||
return (freeAtBegin < n && freeAtEnd < n);
|
||||
}
|
||||
}
|
||||
|
||||
// using Base::truncate;
|
||||
// using Base::destroyAll;
|
||||
// using Base::assign;
|
||||
|
@ -165,7 +165,7 @@ public:
|
||||
bool detach()
|
||||
{
|
||||
if (needsDetach()) {
|
||||
QPair<Data *, T *> copy = clone(detachFlags());
|
||||
QPair<Data *, T *> copy = clone();
|
||||
QArrayDataPointer old(d, ptr, size);
|
||||
d = copy.first;
|
||||
ptr = copy.second;
|
||||
@ -185,10 +185,9 @@ public:
|
||||
bool isSharedWith(const QArrayDataPointer &other) const noexcept { return d && d == other.d; }
|
||||
bool needsDetach() const noexcept { return !d || d->needsDetach(); }
|
||||
qsizetype detachCapacity(qsizetype newSize) const noexcept { return d ? d->detachCapacity(newSize) : newSize; }
|
||||
const typename Data::ArrayOptions flags() const noexcept { return d ? typename Data::ArrayOption(d->flags) : Data::DefaultAllocationFlags; }
|
||||
const typename Data::ArrayOptions flags() const noexcept { return d ? typename Data::ArrayOption(d->flags) : Data::ArrayOptionDefault; }
|
||||
void setFlag(typename Data::ArrayOptions f) noexcept { Q_ASSERT(d); d->flags |= f; }
|
||||
void clearFlag(typename Data::ArrayOptions f) noexcept { Q_ASSERT(d); d->flags &= ~f; }
|
||||
typename Data::ArrayOptions detachFlags() const noexcept { return d ? d->detachFlags() : Data::DefaultAllocationFlags; }
|
||||
|
||||
Data *d_ptr() noexcept { return d; }
|
||||
void setBegin(T *begin) noexcept { ptr = begin; }
|
||||
@ -207,40 +206,8 @@ public:
|
||||
return d->constAllocatedCapacity() - freeSpaceAtBegin() - this->size;
|
||||
}
|
||||
|
||||
static QArrayDataPointer allocateGrow(const QArrayDataPointer &from,
|
||||
qsizetype newSize, QArrayData::ArrayOptions options)
|
||||
{
|
||||
return allocateGrow(from, from.detachCapacity(newSize), newSize, options);
|
||||
}
|
||||
|
||||
static QArrayDataPointer allocateGrow(const QArrayDataPointer &from, qsizetype capacity,
|
||||
qsizetype newSize, QArrayData::ArrayOptions options)
|
||||
{
|
||||
auto [header, dataPtr] = Data::allocate(capacity, options);
|
||||
const bool valid = header != nullptr && dataPtr != nullptr;
|
||||
const bool grows = (options & (Data::GrowsForward | Data::GrowsBackwards));
|
||||
if (!valid || !grows)
|
||||
return QArrayDataPointer(header, dataPtr);
|
||||
|
||||
// must always hold true, as valid is the first condition we check and
|
||||
// if-statement short-circuits
|
||||
Q_ASSERT(valid);
|
||||
|
||||
// Idea: * when growing backwards, adjust pointer to prepare free space at the beginning
|
||||
// * when growing forward, adjust by the previous data pointer offset
|
||||
|
||||
// TODO: what's with CapacityReserved?
|
||||
dataPtr += (options & Data::GrowsBackwards) ? (header->alloc - newSize) / 2
|
||||
: from.freeSpaceAtBegin();
|
||||
return QArrayDataPointer(header, dataPtr);
|
||||
}
|
||||
|
||||
enum AllocationPosition {
|
||||
AllocateAtEnd,
|
||||
AllocateAtBeginning
|
||||
};
|
||||
// allocate and grow. Ensure that at the minimum requiredSpace is available at the requested end
|
||||
static QArrayDataPointer allocateGrow(const QArrayDataPointer &from, qsizetype n, AllocationPosition position)
|
||||
static QArrayDataPointer allocateGrow(const QArrayDataPointer &from, qsizetype n, QArrayData::AllocationPosition position)
|
||||
{
|
||||
// calculate new capacity. We keep the free capacity at the side that does not have to grow
|
||||
// to avoid quadratic behavior with mixed append/prepend cases
|
||||
@ -249,10 +216,10 @@ public:
|
||||
qsizetype minimalCapacity = qMax(from.size, from.constAllocatedCapacity()) + n;
|
||||
// subtract the free space at the side we want to allocate. This ensures that the total size requested is
|
||||
// the existing allocation at the other side + size + n.
|
||||
minimalCapacity -= (position == AllocateAtEnd) ? from.freeSpaceAtEnd() : from.freeSpaceAtBegin();
|
||||
minimalCapacity -= (position == QArrayData::AllocateAtEnd) ? from.freeSpaceAtEnd() : from.freeSpaceAtBegin();
|
||||
qsizetype capacity = from.detachCapacity(minimalCapacity);
|
||||
const bool grows = capacity > from.constAllocatedCapacity();
|
||||
auto [header, dataPtr] = Data::allocate(capacity, grows ? QArrayData::GrowsBackwards : QArrayData::DefaultAllocationFlags);
|
||||
auto [header, dataPtr] = Data::allocate(capacity, grows ? QArrayData::Grow : QArrayData::KeepSize);
|
||||
const bool valid = header != nullptr && dataPtr != nullptr;
|
||||
if (!valid)
|
||||
return QArrayDataPointer(header, dataPtr);
|
||||
@ -261,8 +228,9 @@ public:
|
||||
// * when growing forward, adjust by the previous data pointer offset
|
||||
|
||||
// TODO: what's with CapacityReserved?
|
||||
dataPtr += (position == AllocateAtBeginning) ? qMax(0, (header->alloc - from.size - n) / 2)
|
||||
dataPtr += (position == QArrayData::AllocateAtBeginning) ? qMax(0, (header->alloc - from.size - n) / 2)
|
||||
: from.freeSpaceAtBegin();
|
||||
header->flags = from.flags();
|
||||
return QArrayDataPointer(header, dataPtr);
|
||||
}
|
||||
|
||||
@ -277,14 +245,15 @@ public:
|
||||
}
|
||||
|
||||
private:
|
||||
[[nodiscard]] QPair<Data *, T *> clone(QArrayData::ArrayOptions options) const
|
||||
[[nodiscard]] QPair<Data *, T *> clone() const
|
||||
{
|
||||
QPair<Data *, T *> pair = Data::allocate(detachCapacity(size), options);
|
||||
QPair<Data *, T *> pair = Data::allocate(detachCapacity(size), QArrayData::KeepSize);
|
||||
QArrayDataPointer copy(pair.first, pair.second, 0);
|
||||
if (size)
|
||||
copy->copyAppend(begin(), end());
|
||||
|
||||
pair.first = copy.d;
|
||||
if (pair.first)
|
||||
pair.first->flags = flags();
|
||||
copy.d = nullptr;
|
||||
copy.ptr = nullptr;
|
||||
return pair;
|
||||
|
@ -291,7 +291,7 @@ public:
|
||||
return;
|
||||
if (d->needsDetach()) {
|
||||
// must allocate memory
|
||||
DataPointer detached(Data::allocate(d.allocatedCapacity(), d->detachFlags()));
|
||||
DataPointer detached(Data::allocate(d.allocatedCapacity()));
|
||||
d.swap(detached);
|
||||
} else {
|
||||
d->truncate(0);
|
||||
@ -582,8 +582,7 @@ inline void QList<T>::resize_internal(qsizetype newSize)
|
||||
|
||||
if (d->needsDetach() || newSize > capacity() - d.freeSpaceAtBegin()) {
|
||||
// must allocate memory
|
||||
DataPointer detached(Data::allocate(d->detachCapacity(newSize),
|
||||
d->detachFlags()));
|
||||
DataPointer detached(Data::allocate(d->detachCapacity(newSize)));
|
||||
if (size() && newSize) {
|
||||
detached->copyAppend(constBegin(), constBegin() + qMin(newSize, size()));
|
||||
}
|
||||
@ -608,9 +607,10 @@ void QList<T>::reserve(qsizetype asize)
|
||||
}
|
||||
}
|
||||
|
||||
DataPointer detached(Data::allocate(qMax(asize, size()),
|
||||
d->detachFlags() | Data::CapacityReserved));
|
||||
DataPointer detached(Data::allocate(qMax(asize, size())));
|
||||
detached->copyAppend(constBegin(), constEnd());
|
||||
if (detached.d_ptr())
|
||||
detached->setFlag(Data::CapacityReserved);
|
||||
d.swap(detached);
|
||||
}
|
||||
|
||||
@ -621,15 +621,14 @@ inline void QList<T>::squeeze()
|
||||
return;
|
||||
if (d->needsDetach() || size() < capacity()) {
|
||||
// must allocate memory
|
||||
DataPointer detached(Data::allocate(size(), d->detachFlags() & ~Data::CapacityReserved));
|
||||
DataPointer detached(Data::allocate(size()));
|
||||
if (size()) {
|
||||
detached->copyAppend(constBegin(), constEnd());
|
||||
}
|
||||
d.swap(detached);
|
||||
} else {
|
||||
// We're detached so this is fine
|
||||
d->clearFlag(Data::CapacityReserved);
|
||||
}
|
||||
// We're detached so this is fine
|
||||
d->clearFlag(Data::CapacityReserved);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
@ -646,7 +645,7 @@ inline void QList<T>::remove(qsizetype i, qsizetype n)
|
||||
((d->flags() & Data::CapacityReserved) == 0
|
||||
&& newSize < d->allocatedCapacity()/2)) {
|
||||
// allocate memory
|
||||
DataPointer detached(Data::allocate(d->detachCapacity(newSize), d->detachFlags()));
|
||||
DataPointer detached(Data::allocate(d->detachCapacity(newSize)));
|
||||
const_iterator where = constBegin() + i;
|
||||
if (newSize) {
|
||||
detached->copyAppend(constBegin(), where);
|
||||
@ -672,7 +671,7 @@ inline void QList<T>::append(const_iterator i1, const_iterator i2)
|
||||
return;
|
||||
const auto distance = std::distance(i1, i2);
|
||||
if (d->needsDetach() || distance > d.freeSpaceAtEnd()) {
|
||||
DataPointer detached(DataPointer::allocateGrow(d, distance, DataPointer::AllocateAtEnd));
|
||||
DataPointer detached(DataPointer::allocateGrow(d, distance, QArrayData::AllocateAtEnd));
|
||||
detached->copyAppend(constBegin(), constEnd());
|
||||
detached->copyAppend(i1, i2);
|
||||
d.swap(detached);
|
||||
@ -691,7 +690,7 @@ inline void QList<T>::append(QList<T> &&other)
|
||||
return append(other);
|
||||
|
||||
if (d->needsDetach() || other.size() > d.freeSpaceAtEnd()) {
|
||||
DataPointer detached(DataPointer::allocateGrow(d, other.size(), DataPointer::AllocateAtEnd));
|
||||
DataPointer detached(DataPointer::allocateGrow(d, other.size(), QArrayData::AllocateAtEnd));
|
||||
|
||||
if (!d->needsDetach())
|
||||
detached->moveAppend(begin(), end());
|
||||
@ -711,7 +710,7 @@ template<typename... Args>
|
||||
inline typename QList<T>::reference QList<T>::emplaceFront(Args &&... args)
|
||||
{
|
||||
if (d->needsDetach() || !d.freeSpaceAtBegin()) {
|
||||
DataPointer detached(DataPointer::allocateGrow(d, 1, DataPointer::AllocateAtBeginning));
|
||||
DataPointer detached(DataPointer::allocateGrow(d, 1, QArrayData::AllocateAtBeginning));
|
||||
|
||||
detached->emplace(detached.begin(), std::forward<Args>(args)...);
|
||||
if (!d.needsDetach())
|
||||
@ -737,9 +736,9 @@ QList<T>::insert(qsizetype i, qsizetype n, parameter_type t)
|
||||
// it's not worth wasting CPU cycles for that
|
||||
|
||||
if (d->needsDetach() || (n > d.freeSpaceAtBegin() && n > d.freeSpaceAtEnd())) {
|
||||
typename DataPointer::AllocationPosition pos = DataPointer::AllocateAtEnd;
|
||||
typename QArrayData::AllocationPosition pos = QArrayData::AllocateAtEnd;
|
||||
if (d.size != 0 && i <= (d.size >> 1))
|
||||
pos = DataPointer::AllocateAtBeginning;
|
||||
pos = QArrayData::AllocateAtBeginning;
|
||||
|
||||
DataPointer detached(DataPointer::allocateGrow(d, n, pos));
|
||||
const_iterator where = constBegin() + i;
|
||||
@ -767,9 +766,9 @@ QList<T>::emplace(qsizetype i, Args&&... args)
|
||||
Q_ASSERT_X(i >= 0 && i <= d->size, "QList<T>::insert", "index out of range");
|
||||
|
||||
if (d->needsDetach() || (d.size == d.constAllocatedCapacity())) {
|
||||
typename DataPointer::AllocationPosition pos = DataPointer::AllocateAtEnd;
|
||||
typename QArrayData::AllocationPosition pos = QArrayData::AllocateAtEnd;
|
||||
if (d.size != 0 && i <= (d.size >> 1))
|
||||
pos = DataPointer::AllocateAtBeginning;
|
||||
pos = QArrayData::AllocateAtBeginning;
|
||||
|
||||
DataPointer detached(DataPointer::allocateGrow(d, 1, pos));
|
||||
const_iterator where = constBegin() + i;
|
||||
@ -810,8 +809,7 @@ inline QList<T> &QList<T>::fill(parameter_type t, qsizetype newSize)
|
||||
newSize = size();
|
||||
if (d->needsDetach() || newSize > capacity()) {
|
||||
// must allocate memory
|
||||
DataPointer detached(Data::allocate(d->detachCapacity(newSize),
|
||||
d->detachFlags()));
|
||||
DataPointer detached(Data::allocate(d->detachCapacity(newSize)));
|
||||
detached->copyAppend(newSize, t);
|
||||
d.swap(detached);
|
||||
} else {
|
||||
|
@ -51,25 +51,31 @@ public:
|
||||
{
|
||||
}
|
||||
|
||||
explicit SimpleVector(size_t n, typename Data::ArrayOptions f = Data::DefaultAllocationFlags)
|
||||
: d(Data::allocate(n, f))
|
||||
explicit SimpleVector(size_t n, bool capacityReserved = false)
|
||||
: d(Data::allocate(n))
|
||||
{
|
||||
if (n)
|
||||
d->appendInitialize(n);
|
||||
if (capacityReserved)
|
||||
d.setFlag(QArrayData::CapacityReserved);
|
||||
}
|
||||
|
||||
SimpleVector(size_t n, const T &t, typename Data::ArrayOptions f = Data::DefaultAllocationFlags)
|
||||
: d(Data::allocate(n, f))
|
||||
SimpleVector(size_t n, const T &t, bool capacityReserved = false)
|
||||
: d(Data::allocate(n))
|
||||
{
|
||||
if (n)
|
||||
d->copyAppend(n, t);
|
||||
if (capacityReserved)
|
||||
d.setFlag(QArrayData::CapacityReserved);
|
||||
}
|
||||
|
||||
SimpleVector(const T *begin, const T *end, typename Data::ArrayOptions f = Data::DefaultAllocationFlags)
|
||||
: d(Data::allocate(end - begin, f))
|
||||
SimpleVector(const T *begin, const T *end, bool capacityReserved = false)
|
||||
: d(Data::allocate(end - begin))
|
||||
{
|
||||
if (end - begin)
|
||||
d->copyAppend(begin, end);
|
||||
if (capacityReserved)
|
||||
d.setFlag(QArrayData::CapacityReserved);
|
||||
}
|
||||
|
||||
SimpleVector(Data *header, T *data, size_t len = 0)
|
||||
@ -153,10 +159,11 @@ public:
|
||||
}
|
||||
}
|
||||
|
||||
SimpleVector detached(Data::allocate(qMax(n, size()),
|
||||
d->detachFlags() | Data::CapacityReserved));
|
||||
if (size())
|
||||
SimpleVector detached(Data::allocate(qMax(n, size())));
|
||||
if (size()) {
|
||||
detached.d->copyAppend(constBegin(), constEnd());
|
||||
detached.d->setFlag(QArrayData::CapacityReserved);
|
||||
}
|
||||
detached.swap(*this);
|
||||
}
|
||||
|
||||
@ -166,8 +173,7 @@ public:
|
||||
return;
|
||||
|
||||
if (d->needsDetach() || newSize > capacity()) {
|
||||
SimpleVector detached(Data::allocate(
|
||||
d->detachCapacity(newSize), d->detachFlags()));
|
||||
SimpleVector detached(Data::allocate(d->detachCapacity(newSize)));
|
||||
if (newSize) {
|
||||
if (newSize < size()) {
|
||||
const T *const begin = constBegin();
|
||||
@ -201,14 +207,9 @@ public:
|
||||
return;
|
||||
|
||||
T *const begin = d->begin();
|
||||
const bool shouldGrow = d->shouldGrowBeforeInsert(d.begin(), last - first);
|
||||
const auto newSize = size() + (last - first);
|
||||
if (d->needsDetach()
|
||||
|| capacity() - size() < size_t(last - first)
|
||||
|| shouldGrow) {
|
||||
const auto flags = d->detachFlags() | Data::GrowsBackwards;
|
||||
SimpleVector detached(DataPointer::allocateGrow(d, d->detachCapacity(newSize), newSize,
|
||||
flags));
|
||||
const auto n = (last - first);
|
||||
if (d->needsDetach() || n > d.freeSpaceAtBegin()) {
|
||||
SimpleVector detached(DataPointer::allocateGrow(d, n, QArrayData::AllocateAtBeginning));
|
||||
|
||||
detached.d->copyAppend(first, last);
|
||||
detached.d->copyAppend(begin, begin + d->size);
|
||||
@ -227,7 +228,7 @@ public:
|
||||
|
||||
auto requiredSize = qsizetype(last - first);
|
||||
if (d->needsDetach() || d.freeSpaceAtEnd() < requiredSize) {
|
||||
SimpleVector detached(DataPointer::allocateGrow(d, requiredSize, DataPointer::AllocateAtEnd));
|
||||
SimpleVector detached(DataPointer::allocateGrow(d, requiredSize, QArrayData::AllocateAtEnd));
|
||||
|
||||
if (d->size) {
|
||||
const T *const begin = constBegin();
|
||||
@ -263,16 +264,13 @@ public:
|
||||
const iterator begin = d->begin();
|
||||
const iterator where = begin + position;
|
||||
const iterator end = begin + d->size;
|
||||
const bool shouldGrow = d->shouldGrowBeforeInsert(d.begin() + position, last - first);
|
||||
const auto newSize = size() + (last - first);
|
||||
if (d->needsDetach()
|
||||
|| capacity() - size() < size_t(last - first)
|
||||
|| shouldGrow) {
|
||||
auto flags = d->detachFlags() | Data::GrowsForward;
|
||||
if (size_t(position) <= (size() + (last - first)) / 4)
|
||||
flags |= Data::GrowsBackwards;
|
||||
SimpleVector detached(DataPointer::allocateGrow(d, d->detachCapacity(newSize), newSize,
|
||||
flags));
|
||||
const qsizetype n = last - first;
|
||||
if (d->needsDetach() || (n > d.freeSpaceAtBegin() && n > d.freeSpaceAtEnd())) {
|
||||
typename QArrayData::AllocationPosition pos = QArrayData::AllocateAtEnd;
|
||||
if (d.size != 0 && position <= (d.size >> 1))
|
||||
pos = QArrayData::AllocateAtBeginning;
|
||||
|
||||
SimpleVector detached(DataPointer::allocateGrow(d, n, pos));
|
||||
|
||||
if (position)
|
||||
detached.d->copyAppend(begin, where);
|
||||
@ -307,9 +305,7 @@ public:
|
||||
const T *const end = begin + d->size;
|
||||
|
||||
if (d->needsDetach()) {
|
||||
SimpleVector detached(Data::allocate(
|
||||
d->detachCapacity(size() - (last - first)),
|
||||
d->detachFlags()));
|
||||
SimpleVector detached(Data::allocate(d->detachCapacity(size() - (last - first))));
|
||||
if (first != begin)
|
||||
detached.d->copyAppend(begin, first);
|
||||
detached.d->copyAppend(last, end);
|
||||
|
@ -80,7 +80,7 @@ private slots:
|
||||
void grow();
|
||||
void freeSpace_data();
|
||||
void freeSpace();
|
||||
void dataPointerAllocate_data() { arrayOps_data(); }
|
||||
void dataPointerAllocate_data();
|
||||
void dataPointerAllocate();
|
||||
#ifndef QT_NO_EXCEPTIONS
|
||||
void exceptionSafetyPrimitives_constructor();
|
||||
@ -126,7 +126,7 @@ void tst_QArrayData::simpleVector()
|
||||
|
||||
SimpleVector<int> v1;
|
||||
SimpleVector<int> v2(v1);
|
||||
SimpleVector<int> v3(nullptr, nullptr, 0);
|
||||
SimpleVector<int> v3(nullptr, (int *)nullptr, 0);
|
||||
SimpleVector<int> v4(nullptr, data, 0);
|
||||
SimpleVector<int> v5(nullptr, data, 1);
|
||||
SimpleVector<int> v6(nullptr, data, 7);
|
||||
@ -457,8 +457,7 @@ void tst_QArrayData::allocate_data()
|
||||
{
|
||||
QTest::addColumn<size_t>("objectSize");
|
||||
QTest::addColumn<size_t>("alignment");
|
||||
QTest::addColumn<QArrayData::ArrayOptions>("allocateOptions");
|
||||
QTest::addColumn<bool>("isCapacityReserved");
|
||||
QTest::addColumn<bool>("grow");
|
||||
|
||||
struct {
|
||||
char const *typeName;
|
||||
@ -472,13 +471,10 @@ void tst_QArrayData::allocate_data()
|
||||
|
||||
struct {
|
||||
char const *description;
|
||||
QArrayData::ArrayOptions allocateOptions;
|
||||
bool isCapacityReserved;
|
||||
bool grow;
|
||||
} options[] = {
|
||||
{ "Default", QArrayData::DefaultAllocationFlags, false },
|
||||
{ "Reserved", QArrayData::CapacityReserved, true },
|
||||
{ "Grow", QArrayData::GrowsForward, false },
|
||||
{ "GrowBack", QArrayData::GrowsBackwards, false }
|
||||
{ "Default", false },
|
||||
{ "Grow", true }
|
||||
};
|
||||
|
||||
for (size_t i = 0; i < sizeof(types)/sizeof(types[0]); ++i)
|
||||
@ -488,15 +484,14 @@ void tst_QArrayData::allocate_data()
|
||||
+ QLatin1String(": ")
|
||||
+ QLatin1String(options[j].description)))
|
||||
<< types[i].objectSize << types[i].alignment
|
||||
<< options[j].allocateOptions << options[j].isCapacityReserved;
|
||||
<< options[j].grow;
|
||||
}
|
||||
|
||||
void tst_QArrayData::allocate()
|
||||
{
|
||||
QFETCH(size_t, objectSize);
|
||||
QFETCH(size_t, alignment);
|
||||
QFETCH(QArrayData::ArrayOptions, allocateOptions);
|
||||
QFETCH(bool, isCapacityReserved);
|
||||
QFETCH(bool, grow);
|
||||
|
||||
// Minimum alignment that can be requested is that of QArrayData.
|
||||
// Typically, this alignment is sizeof(void *) and ensured by malloc.
|
||||
@ -507,16 +502,14 @@ void tst_QArrayData::allocate()
|
||||
|
||||
for (qsizetype capacity = 1; capacity <= 1024; capacity <<= 1) {
|
||||
QArrayData *data;
|
||||
void *dataPointer = QArrayData::allocate(&data, objectSize, minAlignment,
|
||||
capacity, QArrayData::ArrayOptions(allocateOptions));
|
||||
void *dataPointer = QArrayData::allocate(&data, objectSize, minAlignment, capacity, grow ? QArrayData::Grow : QArrayData::KeepSize);
|
||||
|
||||
keeper.headers.append(data);
|
||||
|
||||
if (allocateOptions & (QArrayData::GrowsForward | QArrayData::GrowsBackwards))
|
||||
if (grow)
|
||||
QVERIFY(data->allocatedCapacity() > capacity);
|
||||
else
|
||||
QCOMPARE(data->allocatedCapacity(), capacity);
|
||||
QCOMPARE(bool(data->flags & QArrayData::CapacityReserved), isCapacityReserved);
|
||||
|
||||
// Check that the allocated array can be used. Best tested with a
|
||||
// memory checker, such as valgrind, running.
|
||||
@ -528,8 +521,7 @@ void tst_QArrayData::reallocate()
|
||||
{
|
||||
QFETCH(size_t, objectSize);
|
||||
QFETCH(size_t, alignment);
|
||||
QFETCH(QArrayData::ArrayOptions, allocateOptions);
|
||||
QFETCH(bool, isCapacityReserved);
|
||||
QFETCH(bool, grow);
|
||||
|
||||
// Minimum alignment that can be requested is that of QArrayData.
|
||||
// Typically, this alignment is sizeof(void *) and ensured by malloc.
|
||||
@ -538,28 +530,24 @@ void tst_QArrayData::reallocate()
|
||||
int capacity = 10;
|
||||
Deallocator keeper(objectSize, minAlignment);
|
||||
QArrayData *data;
|
||||
void *dataPointer = QArrayData::allocate(&data, objectSize, minAlignment, capacity,
|
||||
QArrayData::ArrayOptions(allocateOptions) & ~QArrayData::GrowsForward);
|
||||
void *dataPointer = QArrayData::allocate(&data, objectSize, minAlignment, capacity, grow ? QArrayData::Grow : QArrayData::KeepSize);
|
||||
keeper.headers.append(data);
|
||||
|
||||
memset(dataPointer, 'A', objectSize * capacity);
|
||||
|
||||
// now try to reallocate
|
||||
int newCapacity = 40;
|
||||
auto pair = QArrayData::reallocateUnaligned(data, dataPointer, objectSize, newCapacity,
|
||||
(allocateOptions & (QArrayData::GrowsBackwards|QArrayData::GrowsForward)) ?
|
||||
QArrayData::Grow : QArrayData::KeepSize);
|
||||
auto pair = QArrayData::reallocateUnaligned(data, dataPointer, objectSize, newCapacity, grow ? QArrayData::Grow : QArrayData::KeepSize);
|
||||
data = pair.first;
|
||||
dataPointer = pair.second;
|
||||
QVERIFY(data);
|
||||
keeper.headers.clear();
|
||||
keeper.headers.append(data);
|
||||
|
||||
if (allocateOptions & (QArrayData::GrowsForward | QArrayData::GrowsBackwards))
|
||||
if (grow)
|
||||
QVERIFY(data->allocatedCapacity() > newCapacity);
|
||||
else
|
||||
QCOMPARE(data->allocatedCapacity(), newCapacity);
|
||||
QCOMPARE(!(data->flags & QArrayData::CapacityReserved), !isCapacityReserved);
|
||||
|
||||
for (int i = 0; i < capacity; ++i)
|
||||
QCOMPARE(static_cast<char *>(dataPointer)[i], 'A');
|
||||
@ -593,8 +581,7 @@ void tst_QArrayData::alignment()
|
||||
|
||||
for (int i = 0; i < 100; ++i) {
|
||||
QArrayData *data;
|
||||
void *dataPointer = QArrayData::allocate(&data, sizeof(Unaligned),
|
||||
minAlignment, 8, QArrayData::DefaultAllocationFlags);
|
||||
void *dataPointer = QArrayData::allocate(&data, sizeof(Unaligned), minAlignment, 8, QArrayData::KeepSize);
|
||||
keeper.headers.append(data);
|
||||
|
||||
QVERIFY(data);
|
||||
@ -773,27 +760,15 @@ size_t CountedObject::liveCount = 0;
|
||||
|
||||
void tst_QArrayData::arrayOps_data()
|
||||
{
|
||||
QTest::addColumn<QArrayData::ArrayOptions>("allocationOptions");
|
||||
QTest::addColumn<bool>("capacityReserved");
|
||||
|
||||
QTest::newRow("default-alloc") << QArrayData::ArrayOptions(QArrayData::DefaultAllocationFlags);
|
||||
QTest::newRow("grows-forward") << QArrayData::ArrayOptions(QArrayData::GrowsForward);
|
||||
QTest::newRow("grows-backwards") << QArrayData::ArrayOptions(QArrayData::GrowsBackwards);
|
||||
QTest::newRow("grows-bidirectional")
|
||||
<< QArrayData::ArrayOptions(QArrayData::GrowsForward | QArrayData::GrowsBackwards);
|
||||
QTest::newRow("reserved-capacity")
|
||||
<< QArrayData::ArrayOptions(QArrayData::CapacityReserved);
|
||||
QTest::newRow("reserved-capacity-grows-forward")
|
||||
<< QArrayData::ArrayOptions(QArrayData::GrowsForward | QArrayData::CapacityReserved);
|
||||
QTest::newRow("reserved-capacity-grows-backwards")
|
||||
<< QArrayData::ArrayOptions(QArrayData::GrowsBackwards | QArrayData::CapacityReserved);
|
||||
QTest::newRow("reserved-capacity-grows-bidirectional")
|
||||
<< QArrayData::ArrayOptions(QArrayData::GrowsForward | QArrayData::GrowsBackwards
|
||||
| QArrayData::CapacityReserved);
|
||||
QTest::newRow("default") << false;
|
||||
QTest::newRow("capacity-reserved") << true;
|
||||
}
|
||||
|
||||
void tst_QArrayData::arrayOps()
|
||||
{
|
||||
QFETCH(QArrayData::ArrayOptions, allocationOptions);
|
||||
QFETCH(bool, capacityReserved);
|
||||
CountedObject::LeakChecker leakChecker; Q_UNUSED(leakChecker);
|
||||
|
||||
const int intArray[5] = { 80, 101, 100, 114, 111 };
|
||||
@ -819,9 +794,9 @@ void tst_QArrayData::arrayOps()
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
// copyAppend (I)
|
||||
SimpleVector<int> vi(intArray, intArray + 5, allocationOptions);
|
||||
SimpleVector<QString> vs(stringArray, stringArray + 5, allocationOptions);
|
||||
SimpleVector<CountedObject> vo(objArray, objArray + 5, allocationOptions);
|
||||
SimpleVector<int> vi(intArray, intArray + 5, capacityReserved);
|
||||
SimpleVector<QString> vs(stringArray, stringArray + 5, capacityReserved);
|
||||
SimpleVector<CountedObject> vo(objArray, objArray + 5, capacityReserved);
|
||||
|
||||
QCOMPARE(CountedObject::liveCount, size_t(10));
|
||||
for (int i = 0; i < 5; ++i) {
|
||||
@ -847,9 +822,9 @@ void tst_QArrayData::arrayOps()
|
||||
QString referenceString = QLatin1String("reference");
|
||||
CountedObject referenceObject;
|
||||
|
||||
vi = SimpleVector<int>(5, referenceInt, allocationOptions);
|
||||
vs = SimpleVector<QString>(5, referenceString, allocationOptions);
|
||||
vo = SimpleVector<CountedObject>(5, referenceObject, allocationOptions);
|
||||
vi = SimpleVector<int>(5, referenceInt, capacityReserved);
|
||||
vs = SimpleVector<QString>(5, referenceString, capacityReserved);
|
||||
vo = SimpleVector<CountedObject>(5, referenceObject, capacityReserved);
|
||||
|
||||
QCOMPARE(vi.size(), size_t(5));
|
||||
QCOMPARE(vs.size(), size_t(5));
|
||||
@ -976,14 +951,14 @@ void tst_QArrayData::arrayOps2_data()
|
||||
|
||||
void tst_QArrayData::arrayOps2()
|
||||
{
|
||||
QFETCH(QArrayData::ArrayOptions, allocationOptions);
|
||||
QFETCH(bool, capacityReserved);
|
||||
CountedObject::LeakChecker leakChecker; Q_UNUSED(leakChecker);
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
// appendInitialize
|
||||
SimpleVector<int> vi(5, allocationOptions);
|
||||
SimpleVector<QString> vs(5, allocationOptions);
|
||||
SimpleVector<CountedObject> vo(5, allocationOptions);
|
||||
SimpleVector<int> vi(5, capacityReserved);
|
||||
SimpleVector<QString> vs(5, capacityReserved);
|
||||
SimpleVector<CountedObject> vo(5, capacityReserved);
|
||||
|
||||
QCOMPARE(vi.size(), size_t(5));
|
||||
QCOMPARE(vs.size(), size_t(5));
|
||||
@ -1119,13 +1094,13 @@ void tst_QArrayData::arrayOps2()
|
||||
|
||||
void tst_QArrayData::arrayOpsExtra_data()
|
||||
{
|
||||
arrayOps_data();
|
||||
dataPointerAllocate_data();
|
||||
}
|
||||
|
||||
void tst_QArrayData::arrayOpsExtra()
|
||||
{
|
||||
QSKIP("Skipped while changing QArrayData operations.", SkipAll);
|
||||
QFETCH(QArrayData::ArrayOptions, allocationOptions);
|
||||
QFETCH(QArrayData::AllocationPosition, allocationPosition);
|
||||
CountedObject::LeakChecker leakChecker; Q_UNUSED(leakChecker);
|
||||
|
||||
constexpr size_t inputSize = 5;
|
||||
@ -1144,14 +1119,11 @@ void tst_QArrayData::arrayOpsExtra()
|
||||
for (size_t i = 0; i < 5; ++i)
|
||||
QCOMPARE(objArray[i].id, i);
|
||||
|
||||
const auto setupDataPointers = [&allocationOptions] (size_t capacity, size_t initialSize = 0) {
|
||||
const auto setupDataPointers = [&allocationPosition] (size_t capacity, size_t initialSize = 0) {
|
||||
const qsizetype alloc = qsizetype(capacity);
|
||||
auto i = QArrayDataPointer<int>::allocateGrow(QArrayDataPointer<int>(), alloc,
|
||||
initialSize, allocationOptions);
|
||||
auto s = QArrayDataPointer<QString>::allocateGrow(QArrayDataPointer<QString>(), alloc,
|
||||
initialSize, allocationOptions);
|
||||
auto o = QArrayDataPointer<CountedObject>::allocateGrow(QArrayDataPointer<CountedObject>(), alloc,
|
||||
initialSize, allocationOptions);
|
||||
auto i = QArrayDataPointer<int>::allocateGrow(QArrayDataPointer<int>(), alloc, allocationPosition);
|
||||
auto s = QArrayDataPointer<QString>::allocateGrow(QArrayDataPointer<QString>(), alloc, allocationPosition);
|
||||
auto o = QArrayDataPointer<CountedObject>::allocateGrow(QArrayDataPointer<CountedObject>(), alloc, allocationPosition);
|
||||
if (initialSize) {
|
||||
i->appendInitialize(initialSize);
|
||||
s->appendInitialize(initialSize);
|
||||
@ -1168,7 +1140,7 @@ void tst_QArrayData::arrayOpsExtra()
|
||||
const auto cloneArrayDataPointer = [] (auto &dataPointer, size_t capacity) {
|
||||
using ArrayPointer = std::decay_t<decltype(dataPointer)>;
|
||||
using Type = std::decay_t<typename ArrayPointer::parameter_type>;
|
||||
ArrayPointer copy(QTypedArrayData<Type>::allocate(qsizetype(capacity), dataPointer.flags()));
|
||||
ArrayPointer copy(QTypedArrayData<Type>::allocate(qsizetype(capacity)));
|
||||
copy->copyAppend(dataPointer.begin(), dataPointer.end());
|
||||
return copy;
|
||||
};
|
||||
@ -1273,7 +1245,7 @@ void tst_QArrayData::arrayOpsExtra()
|
||||
std::generate(objData.begin(), objData.end(), [] () { return CountedObject(); });
|
||||
|
||||
// sanity checks:
|
||||
if (allocationOptions & QArrayData::GrowsBackwards) {
|
||||
if (allocationPosition & QArrayData::AllocateAtBeginning) {
|
||||
QVERIFY(intData.freeSpaceAtBegin() > 0);
|
||||
QVERIFY(strData.freeSpaceAtBegin() > 0);
|
||||
QVERIFY(objData.freeSpaceAtBegin() > 0);
|
||||
@ -1372,7 +1344,7 @@ void tst_QArrayData::arrayOpsExtra()
|
||||
std::generate(objData.begin(), objData.end(), [] () { return CountedObject(); });
|
||||
|
||||
// sanity checks:
|
||||
if (allocationOptions & QArrayData::GrowsBackwards) {
|
||||
if (allocationPosition & QArrayData::AllocateAtBeginning) {
|
||||
QVERIFY(intData.freeSpaceAtBegin() > 0);
|
||||
QVERIFY(strData.freeSpaceAtBegin() > 0);
|
||||
QVERIFY(objData.freeSpaceAtBegin() > 0);
|
||||
@ -1471,7 +1443,7 @@ void tst_QArrayData::arrayOpsExtra()
|
||||
std::generate(objData.begin(), objData.end(), [] () { return CountedObject(); });
|
||||
|
||||
// sanity checks:
|
||||
if (allocationOptions & QArrayData::GrowsBackwards) {
|
||||
if (allocationPosition & QArrayData::AllocateAtBeginning) {
|
||||
QVERIFY(intData.freeSpaceAtBegin() > 0);
|
||||
QVERIFY(strData.freeSpaceAtBegin() > 0);
|
||||
QVERIFY(objData.freeSpaceAtBegin() > 0);
|
||||
@ -2045,44 +2017,46 @@ void tst_QArrayData::grow()
|
||||
|
||||
void tst_QArrayData::freeSpace_data()
|
||||
{
|
||||
QTest::addColumn<QArrayData::ArrayOptions>("allocationOptions");
|
||||
QTest::addColumn<size_t>("n");
|
||||
|
||||
for (const size_t n : {1, 3, 5, 7, 16, 25}) {
|
||||
QString suffix = QString::number(n) + QLatin1String("-elements");
|
||||
QTest::newRow(qPrintable(QLatin1String("default-alloc-") + suffix))
|
||||
<< QArrayData::ArrayOptions(QArrayData::DefaultAllocationFlags) << n;
|
||||
QTest::newRow(qPrintable(QLatin1String("grows-forward-") + suffix))
|
||||
<< QArrayData::ArrayOptions(QArrayData::GrowsForward) << n;
|
||||
QTest::newRow(qPrintable(QLatin1String("grows-bidirectional-") + suffix))
|
||||
<< QArrayData::ArrayOptions(QArrayData::GrowsForward | QArrayData::GrowsBackwards) << n;
|
||||
QTest::newRow(qPrintable(QLatin1String("alloc-") + suffix))
|
||||
<< n;
|
||||
}
|
||||
}
|
||||
|
||||
void tst_QArrayData::freeSpace()
|
||||
{
|
||||
QFETCH(QArrayData::ArrayOptions, allocationOptions);
|
||||
QFETCH(size_t, n);
|
||||
const auto testFreeSpace = [] (auto dummy, auto options, qsizetype n) {
|
||||
const auto testFreeSpace = [] (auto dummy, qsizetype n) {
|
||||
using Type = std::decay_t<decltype(dummy)>;
|
||||
using DataPointer = QArrayDataPointer<Type>;
|
||||
Q_UNUSED(dummy);
|
||||
const qsizetype capacity = n + 1;
|
||||
auto ptr = DataPointer::allocateGrow(DataPointer(), capacity, n, options);
|
||||
auto ptr = DataPointer::allocateGrow(DataPointer(), capacity, QArrayData::AllocateAtEnd);
|
||||
const auto alloc = qsizetype(ptr.constAllocatedCapacity());
|
||||
QVERIFY(alloc >= capacity);
|
||||
QCOMPARE(ptr.freeSpaceAtBegin() + ptr.freeSpaceAtEnd(), alloc);
|
||||
};
|
||||
RUN_TEST_FUNC(testFreeSpace, char(0), allocationOptions, n);
|
||||
RUN_TEST_FUNC(testFreeSpace, char16_t(0), allocationOptions, n);
|
||||
RUN_TEST_FUNC(testFreeSpace, int(0), allocationOptions, n);
|
||||
RUN_TEST_FUNC(testFreeSpace, QString(), allocationOptions, n);
|
||||
RUN_TEST_FUNC(testFreeSpace, CountedObject(), allocationOptions, n);
|
||||
RUN_TEST_FUNC(testFreeSpace, char(0), n);
|
||||
RUN_TEST_FUNC(testFreeSpace, char16_t(0), n);
|
||||
RUN_TEST_FUNC(testFreeSpace, int(0), n);
|
||||
RUN_TEST_FUNC(testFreeSpace, QString(), n);
|
||||
RUN_TEST_FUNC(testFreeSpace, CountedObject(), n);
|
||||
}
|
||||
|
||||
void tst_QArrayData::dataPointerAllocate_data()
|
||||
{
|
||||
QTest::addColumn<QArrayData::AllocationPosition>("allocationPosition");
|
||||
|
||||
QTest::newRow("at-end") << QArrayData::AllocateAtEnd;
|
||||
QTest::newRow("at-begin") << QArrayData::AllocateAtBeginning;
|
||||
}
|
||||
|
||||
void tst_QArrayData::dataPointerAllocate()
|
||||
{
|
||||
QFETCH(QArrayData::ArrayOptions, allocationOptions);
|
||||
QFETCH(QArrayData::AllocationPosition, allocationPosition);
|
||||
const auto createDataPointer = [] (qsizetype capacity, auto initValue) {
|
||||
using Type = std::decay_t<decltype(initValue)>;
|
||||
Q_UNUSED(initValue);
|
||||
@ -2098,21 +2072,18 @@ void tst_QArrayData::dataPointerAllocate()
|
||||
oldDataPointer->insert(oldDataPointer.begin(), 1, initValue); // trigger prepend
|
||||
QVERIFY(!oldDataPointer.needsDetach());
|
||||
|
||||
auto newDataPointer = DataPointer::allocateGrow(
|
||||
oldDataPointer, oldDataPointer->detachCapacity(newSize), newSize, allocationOptions);
|
||||
auto newDataPointer = DataPointer::allocateGrow(oldDataPointer, newSize, allocationPosition);
|
||||
const auto newAlloc = newDataPointer.constAllocatedCapacity();
|
||||
const auto freeAtBegin = newDataPointer.freeSpaceAtBegin();
|
||||
const auto freeAtEnd = newDataPointer.freeSpaceAtEnd();
|
||||
|
||||
QVERIFY(newAlloc > oldDataPointer.constAllocatedCapacity());
|
||||
QCOMPARE(freeAtBegin + freeAtEnd, newAlloc);
|
||||
if (allocationOptions & QArrayData::GrowsBackwards) {
|
||||
// bad check, but will suffice for now, hopefully
|
||||
QCOMPARE(freeAtBegin, (newAlloc - newSize) / 2);
|
||||
} else if (allocationOptions & QArrayData::GrowsForward) {
|
||||
if (allocationPosition == QArrayData::AllocateAtBeginning) {
|
||||
QVERIFY(freeAtBegin > 0);
|
||||
} else if (allocationPosition & QArrayData::AllocateAtEnd) {
|
||||
QCOMPARE(freeAtBegin, oldDataPointer.freeSpaceAtBegin());
|
||||
} else {
|
||||
QCOMPARE(freeAtBegin, 0);
|
||||
QVERIFY(freeAtEnd > 0);
|
||||
}
|
||||
};
|
||||
|
||||
@ -2133,18 +2104,18 @@ void tst_QArrayData::dataPointerAllocate()
|
||||
auto oldDataPointerCopy = oldDataPointer; // force detach later
|
||||
QVERIFY(oldDataPointer.needsDetach());
|
||||
|
||||
auto newDataPointer = DataPointer::allocateGrow(
|
||||
oldDataPointer, oldDataPointer->detachCapacity(newSize), newSize, allocationOptions);
|
||||
auto newDataPointer = DataPointer::allocateGrow(oldDataPointer, oldDataPointer->detachCapacity(newSize), allocationPosition);
|
||||
const auto newAlloc = newDataPointer.constAllocatedCapacity();
|
||||
const auto freeAtBegin = newDataPointer.freeSpaceAtBegin();
|
||||
const auto freeAtEnd = newDataPointer.freeSpaceAtEnd();
|
||||
|
||||
QVERIFY(newAlloc > oldDataPointer.constAllocatedCapacity());
|
||||
QCOMPARE(freeAtBegin + freeAtEnd, newAlloc);
|
||||
if (allocationOptions & QArrayData::GrowsBackwards) {
|
||||
QCOMPARE(freeAtBegin, (newAlloc - newSize) / 2);
|
||||
} else {
|
||||
QCOMPARE(freeAtBegin, 0);
|
||||
if (allocationPosition == QArrayData::AllocateAtBeginning) {
|
||||
QVERIFY(freeAtBegin > 0);
|
||||
} else if (allocationPosition & QArrayData::AllocateAtEnd) {
|
||||
QCOMPARE(freeAtBegin, oldDataPointer.freeSpaceAtBegin());
|
||||
QVERIFY(freeAtEnd > 0);
|
||||
}
|
||||
};
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user