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:
Lars Knoll 2020-10-30 14:21:34 +01:00
parent 419eaa0679
commit b76fbb48fb
11 changed files with 174 additions and 278 deletions

View File

@ -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);

View File

@ -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)

View File

@ -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());
}

View File

@ -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)

View File

@ -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);
}

View File

@ -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

View File

@ -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;

View File

@ -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;

View File

@ -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 {

View File

@ -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);

View File

@ -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);
}
};