qtbase/src/corelib/global/qalloc.h

129 lines
4.3 KiB
C++

// Copyright (C) 2025 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#ifndef QALLOC_H
#define QALLOC_H
//
// W A R N I N G
// -------------
//
// This file is not part of the Qt API. It exists purely as an
// implementation detail. This header file may change from version to
// version without notice, or even be removed.
//
// We mean it.
//
#include <QtCore/qtconfigmacros.h>
#include <QtCore/qtcoreexports.h>
#include <QtCore/qnumeric.h>
#include <QtCore/qtypeinfo.h>
#include <cstddef>
QT_BEGIN_NAMESPACE
namespace QtPrivate {
/**
* \internal
* \return the size that would be allocated for the given request.
*
* Computes the actual allocation size for \a allocSize and \a alignment,
* as determined by the active allocator, without performing the allocation.
*
* In practice, it only returns nonzero when using jemalloc.
*/
Q_CORE_EXPORT Q_DECL_PURE_FUNCTION
size_t expectedAllocSize(size_t allocSize, size_t alignment) noexcept;
/**
* \internal
* \brief Computes the best allocation size for the requested minimum capacity, and updates capacity.
*
* Computes the allocation size starting from \a headerSize and a requested minimum capacity in \a capacity,
* multiplied by the \a elementSize and adjusted by the \a unusedCapacity.
* The final capacity is written back into \a capacity.
* The \a headerSize and \a unusedCapacity values are not included in the final reported capacity.
*/
inline size_t fittedAllocSize(size_t headerSize, size_t *capacity,
size_t elementSize, size_t unusedCapacity, size_t alignment) noexcept
{
size_t totalCapacity = 0; // = capacity + unusedCapacity
if (Q_UNLIKELY(qAddOverflow(*capacity, unusedCapacity, &totalCapacity)))
return 0; // or handle error
size_t payloadSize = 0; // = totalCapacity * elementSize
if (Q_UNLIKELY(qMulOverflow(totalCapacity, elementSize, &payloadSize)))
return 0;
size_t allocSize = 0; // = headerSize + payloadSize
if (Q_UNLIKELY(qAddOverflow(headerSize, payloadSize, &allocSize)))
return 0;
if (size_t fittedSize = expectedAllocSize(allocSize, alignment); fittedSize != 0) {
// no need to overflow/underflow check from fittedSize,
// since allocSize <= fittedSize <= SIZE_T_MAX
*capacity = (fittedSize - headerSize) / elementSize - unusedCapacity;
size_t newTotalCapacity = *capacity + unusedCapacity;
size_t newPayloadSize = newTotalCapacity * elementSize;
return headerSize + newPayloadSize;
}
return allocSize;
}
#ifdef Q_CC_GNU
__attribute__((malloc))
#endif
inline void *fittedMalloc(size_t headerSize, size_t *capacity,
size_t elementSize, size_t unusedCapacity) noexcept
{
size_t allocSize = fittedAllocSize(headerSize, capacity,
elementSize, unusedCapacity, alignof(std::max_align_t));
if (Q_LIKELY(allocSize != 0))
return malloc(allocSize);
else
return nullptr;
}
inline void *fittedMalloc(size_t headerSize, qsizetype *capacity,
size_t elementSize, size_t unusedCapacity = 0) noexcept
{
size_t uCapacity = size_t(*capacity);
void *ptr = fittedMalloc(headerSize, &uCapacity, elementSize, unusedCapacity);
*capacity = qsizetype(uCapacity);
return ptr;
}
inline void *fittedRealloc(void *ptr, size_t headerSize, size_t *capacity,
size_t elementSize, size_t unusedCapacity) noexcept
{
size_t allocSize = fittedAllocSize(headerSize, capacity,
elementSize, unusedCapacity, alignof(std::max_align_t));
if (Q_LIKELY(allocSize != 0))
return realloc(ptr, allocSize);
else
return nullptr;
}
inline void *fittedRealloc(void *ptr, size_t headerSize, qsizetype *capacity,
size_t elementSize, size_t unusedCapacity = 0) noexcept
{
size_t uCapacity = size_t(*capacity);
ptr = fittedRealloc(ptr, headerSize, &uCapacity, elementSize, unusedCapacity);
*capacity = qsizetype(uCapacity);
return ptr;
}
Q_CORE_EXPORT void sizedFree(void *ptr, size_t allocSize) noexcept;
inline void sizedFree(void *ptr, size_t capacity, size_t elementSize) noexcept
{
sizedFree(ptr, capacity * elementSize);
}
} // namespace QtPrivate
QT_END_NAMESPACE
#endif // QALLOC_H