diff options
Diffstat (limited to 'src/corelib/tools/qarraydata.cpp')
-rw-r--r-- | src/corelib/tools/qarraydata.cpp | 117 |
1 files changed, 110 insertions, 7 deletions
diff --git a/src/corelib/tools/qarraydata.cpp b/src/corelib/tools/qarraydata.cpp index a91d833e3b..234a44f6b6 100644 --- a/src/corelib/tools/qarraydata.cpp +++ b/src/corelib/tools/qarraydata.cpp @@ -1,6 +1,6 @@ /**************************************************************************** ** -** Copyright (C) 2016 The Qt Company Ltd. +** Copyright (C) 2019 The Qt Company Ltd. ** Copyright (C) 2016 Intel Corporation. ** Contact: https://www.qt.io/licensing/ ** @@ -41,11 +41,114 @@ #include <QtCore/qarraydata.h> #include <QtCore/private/qnumeric_p.h> #include <QtCore/private/qtools_p.h> +#include <QtCore/qmath.h> #include <stdlib.h> QT_BEGIN_NAMESPACE +/* + * This pair of functions is declared in qtools_p.h and is used by the Qt + * containers to allocate memory and grow the memory block during append + * operations. + * + * They take size_t parameters and return size_t so they will change sizes + * according to the pointer width. However, knowing Qt containers store the + * container size and element indexes in ints, these functions never return a + * size larger than INT_MAX. This is done by casting the element count and + * memory block size to int in several comparisons: the check for negative is + * very fast on most platforms as the code only needs to check the sign bit. + * + * These functions return SIZE_MAX on overflow, which can be passed to malloc() + * and will surely cause a NULL return (there's no way you can allocate a + * memory block the size of your entire VM space). + */ + +/*! + \internal + \since 5.7 + + Returns the memory block size for a container containing \a elementCount + elements, each of \a elementSize bytes, plus a header of \a headerSize + bytes. That is, this function returns \c + {elementCount * elementSize + headerSize} + + but unlike the simple calculation, it checks for overflows during the + multiplication and the addition. + + Both \a elementCount and \a headerSize can be zero, but \a elementSize + cannot. + + This function returns SIZE_MAX (~0) on overflow or if the memory block size + would not fit an int. +*/ +size_t qCalculateBlockSize(size_t elementCount, size_t elementSize, size_t headerSize) noexcept +{ + unsigned count = unsigned(elementCount); + unsigned size = unsigned(elementSize); + unsigned header = unsigned(headerSize); + Q_ASSERT(elementSize); + Q_ASSERT(size == elementSize); + Q_ASSERT(header == headerSize); + + if (Q_UNLIKELY(count != elementCount)) + return std::numeric_limits<size_t>::max(); + + unsigned bytes; + if (Q_UNLIKELY(mul_overflow(size, count, &bytes)) || + Q_UNLIKELY(add_overflow(bytes, header, &bytes))) + return std::numeric_limits<size_t>::max(); + if (Q_UNLIKELY(int(bytes) < 0)) // catches bytes >= 2GB + return std::numeric_limits<size_t>::max(); + + return bytes; +} + +/*! + \internal + \since 5.7 + + Returns the memory block size and the number of elements that will fit in + that block for a container containing \a elementCount elements, each of \a + elementSize bytes, plus a header of \a headerSize bytes. This function + assumes the container will grow and pre-allocates a growth factor. + + Both \a elementCount and \a headerSize can be zero, but \a elementSize + cannot. + + This function returns SIZE_MAX (~0) on overflow or if the memory block size + would not fit an int. + + \note The memory block may contain up to \a elementSize - 1 bytes more than + needed. +*/ +CalculateGrowingBlockSizeResult +qCalculateGrowingBlockSize(size_t elementCount, size_t elementSize, size_t headerSize) noexcept +{ + CalculateGrowingBlockSizeResult result = { + std::numeric_limits<size_t>::max(),std::numeric_limits<size_t>::max() + }; + + unsigned bytes = unsigned(qCalculateBlockSize(elementCount, elementSize, headerSize)); + if (int(bytes) < 0) // catches std::numeric_limits<size_t>::max() + return result; + + unsigned morebytes = qNextPowerOfTwo(bytes); + if (Q_UNLIKELY(int(morebytes) < 0)) { + // catches morebytes == 2GB + // grow by half the difference between bytes and morebytes + bytes += (morebytes - bytes) / 2; + } else { + bytes = morebytes; + } + + result.elementCount = (bytes - unsigned(headerSize)) / unsigned(elementSize); + result.size = bytes; + return result; +} + +// End of qtools_p.h implementation + QT_WARNING_PUSH QT_WARNING_DISABLE_GCC("-Wmissing-field-initializers") @@ -87,7 +190,7 @@ static QArrayData *reallocateData(QArrayData *header, size_t allocSize, uint opt } QArrayData *QArrayData::allocate(size_t objectSize, size_t alignment, - size_t capacity, AllocationOptions options) Q_DECL_NOTHROW + size_t capacity, AllocationOptions options) noexcept { // Alignment is a power of two Q_ASSERT(alignment >= Q_ALIGNOF(QArrayData) @@ -112,7 +215,7 @@ QArrayData *QArrayData::allocate(size_t objectSize, size_t alignment, headerSize += (alignment - Q_ALIGNOF(QArrayData)); if (headerSize > size_t(MaxAllocSize)) - return 0; + return nullptr; size_t allocSize = calculateBlockSize(capacity, objectSize, headerSize, options); QArrayData *header = static_cast<QArrayData *>(::malloc(allocSize)); @@ -121,9 +224,9 @@ QArrayData *QArrayData::allocate(size_t objectSize, size_t alignment, & ~(alignment - 1); #if !defined(QT_NO_UNSHARABLE_CONTAINERS) - header->ref.atomic.store(bool(!(options & Unsharable))); + header->ref.atomic.storeRelaxed(bool(!(options & Unsharable))); #else - header->ref.atomic.store(1); + header->ref.atomic.storeRelaxed(1); #endif header->size = 0; header->alloc = capacity; @@ -135,7 +238,7 @@ QArrayData *QArrayData::allocate(size_t objectSize, size_t alignment, } QArrayData *QArrayData::reallocateUnaligned(QArrayData *data, size_t objectSize, size_t capacity, - AllocationOptions options) Q_DECL_NOTHROW + AllocationOptions options) noexcept { Q_ASSERT(data); Q_ASSERT(data->isMutable()); @@ -150,7 +253,7 @@ QArrayData *QArrayData::reallocateUnaligned(QArrayData *data, size_t objectSize, } void QArrayData::deallocate(QArrayData *data, size_t objectSize, - size_t alignment) Q_DECL_NOTHROW + size_t alignment) noexcept { // Alignment is a power of two Q_ASSERT(alignment >= Q_ALIGNOF(QArrayData) |