Move container block-size calculations to qarraydata.cpp

These were in qbytearray.cpp, which doesn't use them, is big and I
intend to move it to a different directory than the header,
qtools_p.h, that declares them. So move them to a small file that does
use them.

Change-Id: I5a4684f8c7628e617546019cc3f01d92d829f085
Reviewed-by: Thiago Macieira <thiago.macieira@intel.com>
Edward Welbourne 2019-05-29 12:22:09 +02:00
parent a3b931e7c4
commit c7e6407f84
3 changed files with 107 additions and 104 deletions

View File

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

View File

@ -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/
**
@ -130,106 +130,6 @@ int qFindByteArray(
const char *haystack0, int haystackLen, int from,
const char *needle0, int needleLen);
/*
* 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;
}
/*****************************************************************************
Safe and portable C string functions; extensions to standard string.h
*****************************************************************************/

View File

@ -1,6 +1,6 @@
/****************************************************************************
**
** Copyright (C) 2016 The Qt Company Ltd.
** Copyright (C) 2019 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the QtCore module of the Qt Toolkit.
@ -96,7 +96,7 @@ struct CalculateGrowingBlockSizeResult {
size_t elementCount;
};
// implemented in qbytearray.cpp
// Implemented in qarraydata.cpp:
size_t Q_CORE_EXPORT Q_DECL_CONST_FUNCTION
qCalculateBlockSize(size_t elementCount, size_t elementSize, size_t headerSize = 0) noexcept;
CalculateGrowingBlockSizeResult Q_CORE_EXPORT Q_DECL_CONST_FUNCTION