Introduce flags to indicate the QArrayData type
These flags allow us to determine what type of data QArrayData is carrying. There are currently only two supported types: - raw data type: constructed via fromRawData or static data - allocated data type: regular data done via heap allocation The QArrayData object is usually allocated on the heap, unless its own reference count is -1 (indicating static const QArrayData). Such object should have a type of RawDataType, since we can't call free(). Add GrowsBackward for completeness as well as the StaticDataFlags default for static data. Change-Id: Icc915a468a2acf2eae91a94e82451f852d382c92 Reviewed-by: Simon Hausmann <simon.hausmann@qt.io>bb10
parent
8fb45ae5b8
commit
f6a151024b
|
|
@ -63,7 +63,7 @@
|
|||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#define IS_RAW_DATA(d) ((d)->offset != sizeof(QByteArrayData))
|
||||
#define IS_RAW_DATA(d) (!(d)->isMutable())
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
|
||||
|
|
@ -4469,16 +4469,13 @@ QByteArray QByteArray::fromRawData(const char *data, int size)
|
|||
*/
|
||||
QByteArray &QByteArray::setRawData(const char *data, uint size)
|
||||
{
|
||||
if (d->ref.isShared() || d->allocatedCapacity()) {
|
||||
if (!data || !size) {
|
||||
clear();
|
||||
} else if (d->ref.isShared() || (d->flags & Data::RawDataType) == 0) {
|
||||
*this = fromRawData(data, size);
|
||||
} else {
|
||||
if (data) {
|
||||
d->size = size;
|
||||
d->offset = data - reinterpret_cast<char *>(d);
|
||||
} else {
|
||||
d->offset = sizeof(QByteArrayData);
|
||||
d->size = 0;
|
||||
}
|
||||
d->size = size;
|
||||
d->offset = data - reinterpret_cast<char *>(d);
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -495,7 +495,7 @@ inline const char *QByteArray::data() const
|
|||
inline const char *QByteArray::constData() const
|
||||
{ return d->data(); }
|
||||
inline void QByteArray::detach()
|
||||
{ if (d->ref.isShared() || (d->offset != sizeof(QByteArrayData))) reallocData(uint(d->size) + 1u, d->detachFlags()); }
|
||||
{ if (d->ref.isShared() || !d->isMutable()) reallocData(uint(d->size) + 1u, d->detachFlags()); }
|
||||
inline bool QByteArray::isDetached() const
|
||||
{ return !d->ref.isShared(); }
|
||||
inline QByteArray::QByteArray(const QByteArray &a) noexcept : d(a.d)
|
||||
|
|
|
|||
|
|
@ -100,7 +100,7 @@
|
|||
#define ULLONG_MAX quint64_C(18446744073709551615)
|
||||
#endif
|
||||
|
||||
#define IS_RAW_DATA(d) ((d)->offset != sizeof(QStringData))
|
||||
#define IS_RAW_DATA(d) (!(d)->isMutable())
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
|
||||
|
|
@ -2272,7 +2272,7 @@ void QString::resize(int size)
|
|||
|
||||
if (d->ref.isShared() || uint(size) + 1u > d->allocatedCapacity())
|
||||
reallocData(uint(size) + 1u, true);
|
||||
if (d->alloc) {
|
||||
if (d->flags & Data::AllocatedDataType) {
|
||||
d->size = size;
|
||||
d->data()[size] = '\0';
|
||||
}
|
||||
|
|
@ -9149,16 +9149,13 @@ QString QString::fromRawData(const QChar *unicode, int size)
|
|||
*/
|
||||
QString &QString::setRawData(const QChar *unicode, int size)
|
||||
{
|
||||
if (d->ref.isShared() || d->allocatedCapacity()) {
|
||||
if (!unicode || !size) {
|
||||
clear();
|
||||
} else if (d->ref.isShared() || !IS_RAW_DATA(d)) {
|
||||
*this = fromRawData(unicode, size);
|
||||
} else {
|
||||
if (unicode) {
|
||||
d->size = size;
|
||||
d->offset = reinterpret_cast<const char *>(unicode) - reinterpret_cast<char *>(d);
|
||||
} else {
|
||||
d->offset = sizeof(QStringData);
|
||||
d->size = 0;
|
||||
}
|
||||
d->size = size;
|
||||
d->offset = reinterpret_cast<const char *>(unicode) - reinterpret_cast<char *>(d);
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1041,7 +1041,7 @@ inline QChar *QString::data()
|
|||
inline const QChar *QString::constData() const
|
||||
{ return reinterpret_cast<const QChar*>(d->data()); }
|
||||
inline void QString::detach()
|
||||
{ if (d->ref.isShared() || (d->offset != sizeof(QStringData))) reallocData(uint(d->size) + 1u); }
|
||||
{ if (d->ref.isShared() || !d->isMutable()) reallocData(uint(d->size) + 1u); }
|
||||
inline bool QString::isDetached() const
|
||||
{ return !d->ref.isShared(); }
|
||||
inline void QString::clear()
|
||||
|
|
|
|||
|
|
@ -75,7 +75,7 @@ Q_STATIC_ASSERT_X(sizeof(qunicodechar) == 2,
|
|||
/**/
|
||||
|
||||
#define Q_STATIC_STRING_DATA_HEADER_INITIALIZER_WITH_OFFSET(size, offset) \
|
||||
{ Q_REFCOUNT_INITIALIZE_STATIC, QArrayData::DefaultAllocationFlags, size, 0, offset } \
|
||||
{ Q_REFCOUNT_INITIALIZE_STATIC, QArrayData::StaticDataFlags, size, 0, offset } \
|
||||
/**/
|
||||
|
||||
#define Q_STATIC_STRING_DATA_HEADER_INITIALIZER(size) \
|
||||
|
|
|
|||
|
|
@ -153,17 +153,17 @@ QT_WARNING_PUSH
|
|||
QT_WARNING_DISABLE_GCC("-Wmissing-field-initializers")
|
||||
|
||||
const QArrayData QArrayData::shared_null[2] = {
|
||||
{ Q_REFCOUNT_INITIALIZE_STATIC, 0, 0, 0, sizeof(QArrayData) }, // shared null
|
||||
{ Q_REFCOUNT_INITIALIZE_STATIC, QArrayData::StaticDataFlags, 0, 0, sizeof(QArrayData) }, // shared null
|
||||
/* zero initialized terminator */};
|
||||
|
||||
static const QArrayData qt_array[3] = {
|
||||
{ Q_REFCOUNT_INITIALIZE_STATIC, 0, 0, 0, sizeof(QArrayData) }, // shared empty
|
||||
{ { Q_BASIC_ATOMIC_INITIALIZER(0) }, 0, 0, 0, sizeof(QArrayData) }, // unsharable empty
|
||||
static const QArrayData emptyNotNullShared[2] = {
|
||||
{ Q_REFCOUNT_INITIALIZE_STATIC, QArrayData::StaticDataFlags, 0, 0, sizeof(QArrayData) }, // shared empty
|
||||
/* zero initialized terminator */};
|
||||
|
||||
QT_WARNING_POP
|
||||
|
||||
static const QArrayData &qt_array_empty = qt_array[0];
|
||||
static const QArrayData &qt_array_empty = emptyNotNullShared[0];
|
||||
|
||||
static inline size_t calculateBlockSize(size_t &capacity, size_t objectSize, size_t headerSize,
|
||||
uint options)
|
||||
{
|
||||
|
|
@ -179,6 +179,17 @@ static inline size_t calculateBlockSize(size_t &capacity, size_t objectSize, siz
|
|||
}
|
||||
}
|
||||
|
||||
static QArrayData *allocateData(size_t allocSize, uint options)
|
||||
{
|
||||
QArrayData *header = static_cast<QArrayData *>(::malloc(allocSize));
|
||||
if (header) {
|
||||
header->ref.atomic.storeRelaxed(1);
|
||||
header->flags = options;
|
||||
header->size = 0;
|
||||
}
|
||||
return header;
|
||||
}
|
||||
|
||||
static QArrayData *reallocateData(QArrayData *header, size_t allocSize, uint options)
|
||||
{
|
||||
header = static_cast<QArrayData *>(::realloc(header, allocSize));
|
||||
|
|
@ -194,38 +205,43 @@ QArrayData *QArrayData::allocate(size_t objectSize, size_t alignment,
|
|||
Q_ASSERT(alignment >= alignof(QArrayData)
|
||||
&& !(alignment & (alignment - 1)));
|
||||
|
||||
// Don't allocate empty headers
|
||||
if (!(options & RawData) && !capacity)
|
||||
if (capacity == 0)
|
||||
// optimization for empty headers
|
||||
return const_cast<QArrayData *>(&qt_array_empty);
|
||||
|
||||
size_t headerSize = sizeof(QArrayData);
|
||||
|
||||
// Allocate extra (alignment - alignof(QArrayData)) 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!
|
||||
// Padding is skipped when allocating a header for RawData.
|
||||
if (!(options & RawData))
|
||||
headerSize += (alignment - alignof(QArrayData));
|
||||
if (alignment > alignof(QArrayData)) {
|
||||
// Allocate extra (alignment - Q_ALIGNOF(QArrayData)) 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!
|
||||
headerSize += alignment - alignof(QArrayData);
|
||||
}
|
||||
|
||||
if (headerSize > size_t(MaxAllocSize))
|
||||
return nullptr;
|
||||
|
||||
size_t allocSize = calculateBlockSize(capacity, objectSize, headerSize, options);
|
||||
QArrayData *header = static_cast<QArrayData *>(::malloc(allocSize));
|
||||
options |= ArrayOption(AllocatedDataType);
|
||||
QArrayData *header = allocateData(allocSize, options);
|
||||
if (header) {
|
||||
quintptr data = (quintptr(header) + sizeof(QArrayData) + alignment - 1)
|
||||
& ~(alignment - 1);
|
||||
|
||||
header->ref.atomic.storeRelaxed(1);
|
||||
header->size = 0;
|
||||
header->alloc = capacity;
|
||||
header->flags = options;
|
||||
header->offset = data - quintptr(header);
|
||||
header->alloc = capacity;
|
||||
}
|
||||
|
||||
return header;
|
||||
}
|
||||
|
||||
QArrayData *QArrayData::prepareRawData(ArrayOptions options) Q_DECL_NOTHROW
|
||||
{
|
||||
QArrayData *header = allocateData(sizeof(QArrayData), (options & ~DataTypeBits) | RawDataType);
|
||||
if (header)
|
||||
header->alloc = 0;
|
||||
return header;
|
||||
}
|
||||
|
||||
QArrayData *QArrayData::reallocateUnaligned(QArrayData *data, size_t objectSize, size_t capacity,
|
||||
ArrayOptions options) noexcept
|
||||
{
|
||||
|
|
@ -233,9 +249,10 @@ QArrayData *QArrayData::reallocateUnaligned(QArrayData *data, size_t objectSize,
|
|||
Q_ASSERT(data->isMutable());
|
||||
Q_ASSERT(!data->ref.isShared());
|
||||
|
||||
options |= ArrayOption(AllocatedDataType);
|
||||
size_t headerSize = sizeof(QArrayData);
|
||||
size_t allocSize = calculateBlockSize(capacity, objectSize, headerSize, options);
|
||||
QArrayData *header = static_cast<QArrayData *>(reallocateData(data, allocSize, options));
|
||||
QArrayData *header = reallocateData(data, allocSize, options);
|
||||
if (header)
|
||||
header->alloc = capacity;
|
||||
return header;
|
||||
|
|
|
|||
|
|
@ -48,6 +48,24 @@ QT_BEGIN_NAMESPACE
|
|||
|
||||
struct Q_CORE_EXPORT QArrayData
|
||||
{
|
||||
enum ArrayOption {
|
||||
RawDataType = 0x0001, //!< this class is really a QArrayData
|
||||
AllocatedDataType = 0x0002, //!< this class is really a QArrayAllocatedData
|
||||
DataTypeBits = 0x000f,
|
||||
|
||||
CapacityReserved = 0x0010, //!< the capacity was reserved by the user, try to keep it
|
||||
GrowsForward = 0x0020, //!< allocate with eyes towards growing through append()
|
||||
GrowsBackwards = 0x0040, //!< allocate with eyes towards growing through prepend()
|
||||
|
||||
/// this option is used by the Q_ARRAY_LITERAL and similar macros
|
||||
StaticDataFlags = RawDataType,
|
||||
/// this option is used by the allocate() function
|
||||
DefaultAllocationFlags = 0,
|
||||
/// this option is used by the prepareRawData() function
|
||||
DefaultRawFlags = 0
|
||||
};
|
||||
Q_DECLARE_FLAGS(ArrayOptions, ArrayOption)
|
||||
|
||||
QtPrivate::RefCount ref;
|
||||
uint flags;
|
||||
int size;
|
||||
|
|
@ -84,20 +102,9 @@ struct Q_CORE_EXPORT QArrayData
|
|||
// follow COW principles.
|
||||
bool isMutable() const
|
||||
{
|
||||
return alloc != 0;
|
||||
return flags & AllocatedDataType;
|
||||
}
|
||||
|
||||
enum ArrayOption {
|
||||
CapacityReserved = 0x1,
|
||||
RawData = 0x4,
|
||||
GrowsForward = 0x8,
|
||||
|
||||
DefaultAllocationFlags = 0,
|
||||
DefaultRawFlags = 0
|
||||
};
|
||||
|
||||
Q_DECLARE_FLAGS(ArrayOptions, ArrayOption)
|
||||
|
||||
size_t detachCapacity(size_t newSize) const
|
||||
{
|
||||
if (flags & CapacityReserved && newSize < constAllocatedCapacity())
|
||||
|
|
@ -107,7 +114,7 @@ struct Q_CORE_EXPORT QArrayData
|
|||
|
||||
ArrayOptions detachFlags() const
|
||||
{
|
||||
ArrayOptions result;
|
||||
ArrayOptions result = DefaultAllocationFlags;
|
||||
if (flags & CapacityReserved)
|
||||
result |= CapacityReserved;
|
||||
return result;
|
||||
|
|
@ -115,7 +122,7 @@ struct Q_CORE_EXPORT QArrayData
|
|||
|
||||
ArrayOptions cloneFlags() const
|
||||
{
|
||||
ArrayOptions result;
|
||||
ArrayOptions result = DefaultAllocationFlags;
|
||||
if (flags & CapacityReserved)
|
||||
result |= CapacityReserved;
|
||||
return result;
|
||||
|
|
@ -129,6 +136,8 @@ struct Q_CORE_EXPORT QArrayData
|
|||
size_t capacity, ArrayOptions options = DefaultAllocationFlags) noexcept;
|
||||
Q_REQUIRED_RESULT static QArrayData *reallocateUnaligned(QArrayData *data, size_t objectSize,
|
||||
size_t newCapacity, ArrayOptions newOptions = DefaultAllocationFlags) noexcept;
|
||||
Q_REQUIRED_RESULT static QArrayData *prepareRawData(ArrayOptions options = ArrayOptions(RawDataType))
|
||||
Q_DECL_NOTHROW;
|
||||
static void deallocate(QArrayData *data, size_t objectSize,
|
||||
size_t alignment) noexcept;
|
||||
|
||||
|
|
@ -269,7 +278,7 @@ struct QTypedArrayData
|
|||
ArrayOptions options = DefaultRawFlags)
|
||||
{
|
||||
Q_STATIC_ASSERT(sizeof(QTypedArrayData) == sizeof(QArrayData));
|
||||
QTypedArrayData *result = allocate(0, options | RawData);
|
||||
QTypedArrayData *result = static_cast<QTypedArrayData *>(prepareRawData(options));
|
||||
if (result) {
|
||||
Q_ASSERT(!result->ref.isShared()); // No shared empty, please!
|
||||
|
||||
|
|
@ -302,6 +311,7 @@ struct QTypedArrayData
|
|||
template <class T, size_t N>
|
||||
struct QStaticArrayData
|
||||
{
|
||||
// static arrays are of type RawDataType
|
||||
QArrayData header;
|
||||
T data[N];
|
||||
};
|
||||
|
|
@ -314,7 +324,7 @@ struct QArrayDataPointerRef
|
|||
};
|
||||
|
||||
#define Q_STATIC_ARRAY_DATA_HEADER_INITIALIZER_WITH_OFFSET(size, offset) \
|
||||
{ Q_REFCOUNT_INITIALIZE_STATIC, QArrayData::DefaultAllocationFlags, size, 0, offset } \
|
||||
{ Q_REFCOUNT_INITIALIZE_STATIC, QArrayData::StaticDataFlags, size, 0, offset } \
|
||||
/**/
|
||||
|
||||
#define Q_STATIC_ARRAY_DATA_HEADER_INITIALIZER(type, size) \
|
||||
|
|
|
|||
|
|
@ -84,7 +84,7 @@ void tst_QArrayData::referenceCounting()
|
|||
{
|
||||
{
|
||||
// Reference counting initialized to 1 (owned)
|
||||
QArrayData array = { { Q_BASIC_ATOMIC_INITIALIZER(1) }, 0, 0, 0, 0 };
|
||||
QArrayData array = { { Q_BASIC_ATOMIC_INITIALIZER(1) }, QArrayData::DefaultRawFlags, 0, 0, 0 };
|
||||
|
||||
QCOMPARE(array.ref.atomic.loadRelaxed(), 1);
|
||||
|
||||
|
|
@ -107,10 +107,9 @@ void tst_QArrayData::referenceCounting()
|
|||
|
||||
// Now would be a good time to free/release allocated data
|
||||
}
|
||||
|
||||
{
|
||||
// Reference counting initialized to -1 (static read-only data)
|
||||
QArrayData array = { Q_REFCOUNT_INITIALIZE_STATIC, 0, 0, 0, 0 };
|
||||
QArrayData array = { Q_REFCOUNT_INITIALIZE_STATIC, QArrayData::StaticDataFlags, 0, 0, 0 };
|
||||
|
||||
QCOMPARE(array.ref.atomic.loadRelaxed(), -1);
|
||||
|
||||
|
|
@ -186,7 +185,7 @@ void tst_QArrayData::staticData()
|
|||
|
||||
void tst_QArrayData::simpleVector()
|
||||
{
|
||||
QArrayData data0 = { Q_REFCOUNT_INITIALIZE_STATIC, 0, 0, 0, 0 };
|
||||
QArrayData data0 = { Q_REFCOUNT_INITIALIZE_STATIC, QArrayData::StaticDataFlags, 0, 0, 0 };
|
||||
QStaticArrayData<int, 7> data1 = {
|
||||
Q_STATIC_ARRAY_DATA_HEADER_INITIALIZER(int, 7),
|
||||
{ 0, 1, 2, 3, 4, 5, 6 }
|
||||
|
|
|
|||
Loading…
Reference in New Issue