diff options
Diffstat (limited to 'src/corelib/tools/qarraydata.cpp')
-rw-r--r-- | src/corelib/tools/qarraydata.cpp | 128 |
1 files changed, 74 insertions, 54 deletions
diff --git a/src/corelib/tools/qarraydata.cpp b/src/corelib/tools/qarraydata.cpp index b1634d61b5..6aebd4306a 100644 --- a/src/corelib/tools/qarraydata.cpp +++ b/src/corelib/tools/qarraydata.cpp @@ -54,8 +54,8 @@ qsizetype qCalculateBlockSize(qsizetype elementCount, qsizetype elementSize, qsi Q_ASSERT(elementSize); size_t bytes; - if (Q_UNLIKELY(mul_overflow(size_t(elementSize), size_t(elementCount), &bytes)) || - Q_UNLIKELY(add_overflow(bytes, size_t(headerSize), &bytes))) + if (Q_UNLIKELY(qMulOverflow(size_t(elementSize), size_t(elementCount), &bytes)) || + Q_UNLIKELY(qAddOverflow(bytes, size_t(headerSize), &bytes))) return -1; if (Q_UNLIKELY(qsizetype(bytes) < 0)) return -1; @@ -107,33 +107,30 @@ qCalculateGrowingBlockSize(qsizetype elementCount, qsizetype elementSize, qsizet return result; } -/*! - \internal +/* + Calculate the byte size for a block of \a capacity objects of size \a + objectSize, with a header of size \a headerSize. If the \a option is + QArrayData::Grow, the capacity itself adjusted up, preallocating room for + more elements to be added later; otherwise, it is an exact calculation. - Returns \a allocSize plus extra reserved bytes necessary to store '\0'. - */ -static inline qsizetype reserveExtraBytes(qsizetype allocSize) + Returns a structure containing the size in bytes and elements available. +*/ +static inline CalculateGrowingBlockSizeResult +calculateBlockSize(qsizetype capacity, qsizetype objectSize, qsizetype headerSize, QArrayData::AllocationOption option) { - // We deal with QByteArray and QString only - constexpr qsizetype extra = qMax(sizeof(QByteArray::value_type), sizeof(QString::value_type)); - if (Q_UNLIKELY(allocSize < 0)) - return -1; - if (Q_UNLIKELY(add_overflow(allocSize, extra, &allocSize))) - return -1; - return allocSize; -} + // Adjust the header size up to account for the trailing null for QString + // and QByteArray. This is not checked for overflow because headers sizes + // should not be anywhere near the overflow limit. + constexpr qsizetype FooterSize = qMax(sizeof(QString::value_type), sizeof(QByteArray::value_type)); + if (objectSize <= FooterSize) + headerSize += FooterSize; -static inline qsizetype calculateBlockSize(qsizetype &capacity, qsizetype objectSize, qsizetype headerSize, QArrayData::AllocationOption option) -{ - // Calculate the byte size // allocSize = objectSize * capacity + headerSize, but checked for overflow // plus padded to grow in size if (option == QArrayData::Grow) { - auto r = qCalculateGrowingBlockSize(capacity, objectSize, headerSize); - capacity = r.elementCount; - return r.size; + return qCalculateGrowingBlockSize(capacity, objectSize, headerSize); } else { - return qCalculateBlockSize(capacity, objectSize, headerSize); + return { qCalculateBlockSize(capacity, objectSize, headerSize), capacity }; } } @@ -148,27 +145,20 @@ static QArrayData *allocateData(qsizetype allocSize) return header; } - namespace { -// QArrayData with strictest alignment requirements supported by malloc() -struct alignas(std::max_align_t) AlignedQArrayData : QArrayData -{ +struct AllocationResult { + void *data; + QArrayData *header; }; } +using QtPrivate::AlignedQArrayData; - -void *QArrayData::allocate(QArrayData **dptr, qsizetype objectSize, qsizetype alignment, - qsizetype capacity, QArrayData::AllocationOption option) noexcept +static inline AllocationResult +allocateHelper(qsizetype objectSize, qsizetype alignment, qsizetype capacity, + QArrayData::AllocationOption option) noexcept { - Q_ASSERT(dptr); - // Alignment is a power of two - Q_ASSERT(alignment >= qsizetype(alignof(QArrayData)) - && !(alignment & (alignment - 1))); - - if (capacity == 0) { - *dptr = nullptr; - return nullptr; - } + if (capacity == 0) + return {}; qsizetype headerSize = sizeof(AlignedQArrayData); const qsizetype headerAlignment = alignof(AlignedQArrayData); @@ -177,16 +167,16 @@ void *QArrayData::allocate(QArrayData **dptr, qsizetype objectSize, qsizetype al // Allocate extra (alignment - Q_ALIGNOF(AlignedQArrayData)) padding // bytes so we can properly align the data array. This assumes malloc is // able to provide appropriate alignment for the header -- as it should! + // Effectively, we allocate one QTypedArrayData<T>::AlignmentDummy. headerSize += alignment - headerAlignment; } Q_ASSERT(headerSize > 0); - qsizetype allocSize = calculateBlockSize(capacity, objectSize, headerSize, option); - allocSize = reserveExtraBytes(allocSize); - if (Q_UNLIKELY(allocSize < 0)) { // handle overflow. cannot allocate reliably - *dptr = nullptr; - return nullptr; - } + auto blockSize = calculateBlockSize(capacity, objectSize, headerSize, option); + capacity = blockSize.elementCount; + qsizetype allocSize = blockSize.size; + if (Q_UNLIKELY(allocSize < 0)) // handle overflow. cannot allocate reliably + return {}; QArrayData *header = allocateData(allocSize); void *data = nullptr; @@ -196,20 +186,54 @@ void *QArrayData::allocate(QArrayData **dptr, qsizetype objectSize, qsizetype al header->alloc = qsizetype(capacity); } - *dptr = header; - return data; + return { data, header }; } -QPair<QArrayData *, void *> +// Generic size and alignment allocation function +void *QArrayData::allocate(QArrayData **dptr, qsizetype objectSize, qsizetype alignment, + qsizetype capacity, AllocationOption option) noexcept +{ + Q_ASSERT(dptr); + // Alignment is a power of two + Q_ASSERT(alignment >= qsizetype(alignof(QArrayData)) + && !(alignment & (alignment - 1))); + + auto r = allocateHelper(objectSize, alignment, capacity, option); + *dptr = r.header; + return r.data; +} + +// Fixed size and alignment allocation functions +void *QArrayData::allocate1(QArrayData **dptr, qsizetype capacity, AllocationOption option) noexcept +{ + Q_ASSERT(dptr); + + auto r = allocateHelper(1, alignof(AlignedQArrayData), capacity, option); + *dptr = r.header; + return r.data; +} + +void *QArrayData::allocate2(QArrayData **dptr, qsizetype capacity, AllocationOption option) noexcept +{ + Q_ASSERT(dptr); + + auto r = allocateHelper(2, alignof(AlignedQArrayData), capacity, option); + *dptr = r.header; + return r.data; +} + +std::pair<QArrayData *, void *> QArrayData::reallocateUnaligned(QArrayData *data, void *dataPointer, qsizetype objectSize, qsizetype capacity, AllocationOption option) noexcept { Q_ASSERT(!data || !data->isShared()); const qsizetype headerSize = sizeof(AlignedQArrayData); - qsizetype allocSize = calculateBlockSize(capacity, objectSize, headerSize, option); + auto r = calculateBlockSize(capacity, objectSize, headerSize, option); + qsizetype allocSize = r.size; + capacity = r.elementCount; if (Q_UNLIKELY(allocSize < 0)) - return qMakePair<QArrayData *, void *>(nullptr, nullptr); + return {}; const qptrdiff offset = dataPointer ? reinterpret_cast<char *>(dataPointer) - reinterpret_cast<char *>(data) @@ -217,10 +241,6 @@ QArrayData::reallocateUnaligned(QArrayData *data, void *dataPointer, Q_ASSERT(offset > 0); Q_ASSERT(offset <= allocSize); // equals when all free space is at the beginning - allocSize = reserveExtraBytes(allocSize); - if (Q_UNLIKELY(allocSize < 0)) // handle overflow. cannot reallocate reliably - return qMakePair(data, dataPointer); - QArrayData *header = static_cast<QArrayData *>(::realloc(data, size_t(allocSize))); if (header) { header->alloc = capacity; @@ -228,7 +248,7 @@ QArrayData::reallocateUnaligned(QArrayData *data, void *dataPointer, } else { dataPointer = nullptr; } - return qMakePair(static_cast<QArrayData *>(header), dataPointer); + return {header, dataPointer}; } void QArrayData::deallocate(QArrayData *data, qsizetype objectSize, |