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
Thiago Macieira 2012-06-04 22:31:26 +02:00 committed by Lars Knoll
parent 8fb45ae5b8
commit f6a151024b
8 changed files with 82 additions and 62 deletions

View File

@ -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;
}

View File

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

View File

@ -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;
}

View File

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

View File

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

View File

@ -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;

View File

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

View File

@ -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 }