diff options
24 files changed, 2513 insertions, 112 deletions
diff --git a/src/corelib/tools/qarraydata.cpp b/src/corelib/tools/qarraydata.cpp new file mode 100644 index 0000000000..efed984aef --- /dev/null +++ b/src/corelib/tools/qarraydata.cpp @@ -0,0 +1,100 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtCore module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include <QtCore/qarraydata.h> + +QT_BEGIN_NAMESPACE + +const QArrayData QArrayData::shared_null = { Q_REFCOUNT_INITIALIZE_STATIC, 0, 0, 0, 0 }; + +static const QArrayData qt_array_empty = { Q_REFCOUNT_INITIALIZE_STATIC, 0, 0, 0, 0 }; +static const QArrayData qt_array_unsharable_empty = { { Q_BASIC_ATOMIC_INITIALIZER(0) }, 0, 0, 0, 0 }; + +QArrayData *QArrayData::allocate(size_t objectSize, size_t alignment, + size_t capacity, AllocateOptions options) +{ + // Alignment is a power of two + Q_ASSERT(alignment >= Q_ALIGNOF(QArrayData) + && !(alignment & (alignment - 1))); + + // Don't allocate empty headers + if (!capacity) + return !(options & Unsharable) + ? const_cast<QArrayData *>(&qt_array_empty) + : const_cast<QArrayData *>(&qt_array_unsharable_empty); + + // 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! + size_t allocSize = sizeof(QArrayData) + objectSize * capacity + + (alignment - Q_ALIGNOF(QArrayData)); + + QArrayData *header = static_cast<QArrayData *>(qMalloc(allocSize)); + Q_CHECK_PTR(header); + if (header) { + quintptr data = (quintptr(header) + sizeof(QArrayData) + alignment - 1) + & ~(alignment - 1); + + header->ref.atomic.store(bool(!(options & Unsharable))); + header->size = 0; + header->alloc = capacity; + header->capacityReserved = bool(options & CapacityReserved); + header->offset = data - quintptr(header); + } + + return header; +} + +void QArrayData::deallocate(QArrayData *data, size_t objectSize, + size_t alignment) +{ + // Alignment is a power of two + Q_ASSERT(alignment >= Q_ALIGNOF(QArrayData) + && !(alignment & (alignment - 1))); + Q_UNUSED(objectSize) Q_UNUSED(alignment) + + if (data == &qt_array_unsharable_empty) + return; + + qFree(data); +} + +QT_END_NAMESPACE diff --git a/src/corelib/tools/qarraydata.h b/src/corelib/tools/qarraydata.h new file mode 100644 index 0000000000..8eb543ee51 --- /dev/null +++ b/src/corelib/tools/qarraydata.h @@ -0,0 +1,166 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtCore module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QARRAYDATA_H +#define QARRAYDATA_H + +#include <QtCore/qrefcount.h> + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Core) + +struct Q_CORE_EXPORT QArrayData +{ + QtPrivate::RefCount ref; + int size; + uint alloc : 31; + uint capacityReserved : 1; + + qptrdiff offset; // in bytes from beginning of header + + void *data() + { + Q_ASSERT(size == 0 + || offset < 0 || size_t(offset) >= sizeof(QArrayData)); + return reinterpret_cast<char *>(this) + offset; + } + + const void *data() const + { + Q_ASSERT(size == 0 + || offset < 0 || size_t(offset) >= sizeof(QArrayData)); + return reinterpret_cast<const char *>(this) + offset; + } + + enum AllocateOption { + CapacityReserved = 0x1, + Unsharable = 0x2, + + Default = 0 + }; + + Q_DECLARE_FLAGS(AllocateOptions, AllocateOption) + + AllocateOptions detachFlags() const + { + AllocateOptions result; + if (!ref.isSharable()) + result |= Unsharable; + if (capacityReserved) + result |= CapacityReserved; + return result; + } + + AllocateOptions cloneFlags() const + { + AllocateOptions result; + if (capacityReserved) + result |= CapacityReserved; + return result; + } + + static QArrayData *allocate(size_t objectSize, size_t alignment, + size_t capacity, AllocateOptions options = Default) Q_REQUIRED_RESULT; + static void deallocate(QArrayData *data, size_t objectSize, + size_t alignment); + + static const QArrayData shared_null; +}; + +Q_DECLARE_OPERATORS_FOR_FLAGS(QArrayData::AllocateOptions) + +template <class T> +struct QTypedArrayData + : QArrayData +{ + typedef T *iterator; + typedef const T *const_iterator; + + T *data() { return static_cast<T *>(QArrayData::data()); } + const T *data() const { return static_cast<const T *>(QArrayData::data()); } + + T *begin() { return data(); } + T *end() { return data() + size; } + const T *begin() const { return data(); } + const T *end() const { return data() + size; } + + class AlignmentDummy { QArrayData header; T data; }; + + static QTypedArrayData *allocate(size_t capacity, + AllocateOptions options = Default) Q_REQUIRED_RESULT + { + return static_cast<QTypedArrayData *>(QArrayData::allocate(sizeof(T), + Q_ALIGNOF(AlignmentDummy), capacity, options)); + } + + static void deallocate(QArrayData *data) + { + QArrayData::deallocate(data, sizeof(T), Q_ALIGNOF(AlignmentDummy)); + } + + static QTypedArrayData *sharedNull() + { + return static_cast<QTypedArrayData *>( + const_cast<QArrayData *>(&QArrayData::shared_null)); + } +}; + +template <class T, size_t N> +struct QStaticArrayData +{ + QArrayData header; + T data[N]; +}; + +#define Q_STATIC_ARRAY_DATA_HEADER_INITIALIZER(type, size) { \ + Q_REFCOUNT_INITIALIZE_STATIC, size, 0, 0, \ + (sizeof(QArrayData) + (Q_ALIGNOF(type) - 1)) \ + & ~(Q_ALIGNOF(type) - 1) } \ + /**/ + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif // include guard diff --git a/src/corelib/tools/qarraydataops.h b/src/corelib/tools/qarraydataops.h new file mode 100644 index 0000000000..a3c372fe6b --- /dev/null +++ b/src/corelib/tools/qarraydataops.h @@ -0,0 +1,324 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtCore module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QARRAYDATAOPS_H +#define QARRAYDATAOPS_H + +#include <QtCore/qarraydata.h> + +#include <new> +#include <string.h> + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Core) + +namespace QtPrivate { + +template <class T> +struct QPodArrayOps + : QTypedArrayData<T> +{ + void copyAppend(const T *b, const T *e) + { + Q_ASSERT(!this->ref.isShared()); + Q_ASSERT(b < e); + Q_ASSERT(size_t(e - b) <= this->alloc - uint(this->size)); + + ::memcpy(this->end(), b, (e - b) * sizeof(T)); + this->size += e - b; + } + + void copyAppend(size_t n, const T &t) + { + Q_ASSERT(!this->ref.isShared()); + Q_ASSERT(n <= this->alloc - uint(this->size)); + + T *iter = this->end(); + const T *const end = iter + n; + for (; iter != end; ++iter) + ::memcpy(iter, &t, sizeof(T)); + this->size += n; + } + + void destroyAll() // Call from destructors, ONLY! + { + Q_ASSERT(this->ref == 0); + + // As this is to be called only from destructor, it doesn't need to be + // exception safe; size not updated. + } + + void insert(T *where, const T *b, const T *e) + { + Q_ASSERT(!this->ref.isShared()); + Q_ASSERT(where >= this->begin() && where < this->end()); // Use copyAppend at end + Q_ASSERT(b < e); + Q_ASSERT(e <= where || b > this->end()); // No overlap + Q_ASSERT(size_t(e - b) <= this->alloc - uint(this->size)); + + ::memmove(where + (e - b), where, (this->end() - where) * sizeof(T)); + ::memcpy(where, b, (e - b) * sizeof(T)); + this->size += (e - b); + } +}; + +template <class T> +struct QGenericArrayOps + : QTypedArrayData<T> +{ + void copyAppend(const T *b, const T *e) + { + Q_ASSERT(!this->ref.isShared()); + Q_ASSERT(b < e); + Q_ASSERT(size_t(e - b) <= this->alloc - uint(this->size)); + + T *iter = this->end(); + for (; b != e; ++iter, ++b) { + new (iter) T(*b); + ++this->size; + } + } + + void copyAppend(size_t n, const T &t) + { + Q_ASSERT(!this->ref.isShared()); + Q_ASSERT(n <= this->alloc - uint(this->size)); + + T *iter = this->end(); + const T *const end = iter + n; + for (; iter != end; ++iter) { + new (iter) T(t); + ++this->size; + } + } + + void destroyAll() // Call from destructors, ONLY + { + // As this is to be called only from destructor, it doesn't need to be + // exception safe; size not updated. + + Q_ASSERT(this->ref == 0); + + const T *const b = this->begin(); + const T *i = this->end(); + + while (i != b) + (--i)->~T(); + } + + void insert(T *where, const T *b, const T *e) + { + Q_ASSERT(!this->ref.isShared()); + Q_ASSERT(where >= this->begin() && where < this->end()); // Use copyAppend at end + Q_ASSERT(b < e); + Q_ASSERT(e <= where || b > this->end()); // No overlap + Q_ASSERT(size_t(e - b) <= this->alloc - uint(this->size)); + + // Array may be truncated at where in case of exceptions + + T *const end = this->end(); + const T *readIter = end; + T *writeIter = end + (e - b); + + const T *const step1End = where + qMax(e - b, end - where); + + struct Destructor + { + Destructor(T *&iter) + : iter(&iter) + , end(iter) + { + } + + void commit() + { + iter = &end; + } + + ~Destructor() + { + for (; *iter != end; --*iter) + (*iter)->~T(); + } + + T **iter; + T *end; + } destroyer(writeIter); + + // Construct new elements in array + do { + --readIter, --writeIter; + new (writeIter) T(*readIter); + } while (writeIter != step1End); + + while (writeIter != end) { + --e, --writeIter; + new (writeIter) T(*e); + } + + destroyer.commit(); + this->size += destroyer.end - end; + + // Copy assign over existing elements + while (readIter != where) { + --readIter, --writeIter; + *writeIter = *readIter; + } + + while (writeIter != where) { + --e, --writeIter; + *writeIter = *e; + } + } +}; + +template <class T> +struct QMovableArrayOps + : QGenericArrayOps<T> +{ + // using QGenericArrayOps<T>::copyAppend; + // using QGenericArrayOps<T>::destroyAll; + + void insert(T *where, const T *b, const T *e) + { + Q_ASSERT(!this->ref.isShared()); + Q_ASSERT(where >= this->begin() && where < this->end()); // Use copyAppend at end + Q_ASSERT(b < e); + Q_ASSERT(e <= where || b > this->end()); // No overlap + Q_ASSERT(size_t(e - b) <= this->alloc - uint(this->size)); + + // Provides strong exception safety guarantee, + // provided T::~T() nothrow + + struct ReversibleDisplace + { + ReversibleDisplace(T *begin, T *end, size_t displace) + : begin(begin) + , end(end) + , displace(displace) + { + ::memmove(begin + displace, begin, (end - begin) * sizeof(T)); + } + + void commit() { displace = 0; } + + ~ReversibleDisplace() + { + if (displace) + ::memmove(begin, begin + displace, (end - begin) * sizeof(T)); + } + + T *const begin; + T *const end; + size_t displace; + + } displace(where, this->end(), size_t(e - b)); + + struct CopyConstructor + { + CopyConstructor(T *where) : where(where) {} + + void copy(const T *src, const T *const srcEnd) + { + n = 0; + for (; src != srcEnd; ++src) { + new (where + n) T(*src); + ++n; + } + n = 0; + } + + ~CopyConstructor() + { + while (n) + where[--n].~T(); + } + + T *const where; + size_t n; + } copier(where); + + copier.copy(b, e); + displace.commit(); + this->size += (e - b); + } +}; + +template <class T, class = void> +struct QArrayOpsSelector +{ + typedef QGenericArrayOps<T> Type; +}; + +template <class T> +struct QArrayOpsSelector<T, + typename QEnableIf< + !QTypeInfo<T>::isComplex && !QTypeInfo<T>::isStatic + >::Type> +{ + typedef QPodArrayOps<T> Type; +}; + +template <class T> +struct QArrayOpsSelector<T, + typename QEnableIf< + QTypeInfo<T>::isComplex && !QTypeInfo<T>::isStatic + >::Type> +{ + typedef QMovableArrayOps<T> Type; +}; + +} // namespace QtPrivate + +template <class T> +struct QArrayDataOps + : QtPrivate::QArrayOpsSelector<T>::Type +{ +}; + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif // include guard diff --git a/src/corelib/tools/qarraydatapointer.h b/src/corelib/tools/qarraydatapointer.h new file mode 100644 index 0000000000..1dc02daa63 --- /dev/null +++ b/src/corelib/tools/qarraydatapointer.h @@ -0,0 +1,200 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the QtCore module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QARRAYDATAPOINTER_H +#define QARRAYDATAPOINTER_H + +#include <QtCore/qarraydataops.h> + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Core) + +template <class T> +struct QArrayDataPointer +{ +private: + typedef QTypedArrayData<T> Data; + typedef QArrayDataOps<T> DataOps; + +public: + QArrayDataPointer() + : d(Data::sharedNull()) + { + } + + QArrayDataPointer(const QArrayDataPointer &other) + : d(other.d->ref.ref() + ? other.d + : other.clone(other.d->cloneFlags())) + { + } + + explicit QArrayDataPointer(QTypedArrayData<T> *ptr) + : d(ptr) + { + } + + QArrayDataPointer &operator=(const QArrayDataPointer &other) + { + QArrayDataPointer tmp(other); + this->swap(tmp); + return *this; + } + + DataOps &operator*() const + { + Q_ASSERT(d); + return *static_cast<DataOps *>(d); + } + + DataOps *operator->() const + { + Q_ASSERT(d); + return static_cast<DataOps *>(d); + } + + ~QArrayDataPointer() + { + if (!d->ref.deref()) { + (*this)->destroyAll(); + Data::deallocate(d); + } + } + + bool isNull() const + { + return d == Data::sharedNull(); + } + + Data *data() const + { + return d; + } + + void setSharable(bool sharable) + { + if (d->alloc == 0 && d->size == 0) { + d = Data::allocate(0, sharable + ? QArrayData::Default + : QArrayData::Unsharable); + return; + } + + detach(); + d->ref.setSharable(sharable); + } + + void swap(QArrayDataPointer &other) + { + qSwap(d, other.d); + } + + void clear() + { + QArrayDataPointer tmp(d); + d = Data::allocate(0); + } + + bool detach() + { + if (d->ref.isShared()) { + Data *copy = clone(d->detachFlags()); + QArrayDataPointer old(d); + d = copy; + return true; + } + + return false; + } + +private: + Data *clone(QArrayData::AllocateOptions options) const Q_REQUIRED_RESULT + { + QArrayDataPointer copy(Data::allocate(d->alloc ? d->alloc : d->size, + options)); + if (d->size) + copy->copyAppend(d->begin(), d->end()); + + Data *result = copy.d; + copy.d = Data::sharedNull(); + return result; + } + + Data *d; +}; + +template <class T> +inline bool operator==(const QArrayDataPointer<T> &lhs, const QArrayDataPointer<T> &rhs) +{ + return lhs.data() == rhs.data(); +} + +template <class T> +inline bool operator!=(const QArrayDataPointer<T> &lhs, const QArrayDataPointer<T> &rhs) +{ + return lhs.data() != rhs.data(); +} + +template <class T> +inline void qSwap(QArrayDataPointer<T> &p1, QArrayDataPointer<T> &p2) +{ + p1.swap(p2); +} + +QT_END_NAMESPACE + +namespace std +{ + template <class T> + inline void swap( + QT_PREPEND_NAMESPACE(QArrayDataPointer)<T> &p1, + QT_PREPEND_NAMESPACE(QArrayDataPointer)<T> &p2) + { + p1.swap(p2); + } +} + +QT_END_HEADER + +#endif // include guard diff --git a/src/corelib/tools/qbytearray.cpp b/src/corelib/tools/qbytearray.cpp index 8c625c2868..47bf5da619 100644 --- a/src/corelib/tools/qbytearray.cpp +++ b/src/corelib/tools/qbytearray.cpp @@ -576,7 +576,7 @@ QByteArray qUncompress(const uchar* data, int nbytes) d.take(); // realloc was successful d.reset(p); } - d->ref = 1; + d->ref.initializeOwned(); d->size = len; d->alloc = len; d->capacityReserved = false; @@ -614,9 +614,9 @@ static inline char qToLower(char c) return c; } -const QConstByteArrayData<1> QByteArray::shared_null = { { Q_REFCOUNT_INITIALIZER(-1), +const QStaticByteArrayData<1> QByteArray::shared_null = { { Q_REFCOUNT_INITIALIZE_STATIC, 0, 0, 0, { 0 } }, { 0 } }; -const QConstByteArrayData<1> QByteArray::shared_empty = { { Q_REFCOUNT_INITIALIZER(-1), +const QStaticByteArrayData<1> QByteArray::shared_empty = { { Q_REFCOUNT_INITIALIZE_STATIC, 0, 0, 0, { 0 } }, { 0 } }; /*! @@ -1304,7 +1304,7 @@ QByteArray::QByteArray(const char *str) int len = qstrlen(str); d = static_cast<Data *>(malloc(sizeof(Data) + len + 1)); Q_CHECK_PTR(d); - d->ref = 1; + d->ref.initializeOwned(); d->size = len; d->alloc = len; d->capacityReserved = false; @@ -1333,7 +1333,7 @@ QByteArray::QByteArray(const char *data, int size) } else { d = static_cast<Data *>(malloc(sizeof(Data) + size + 1)); Q_CHECK_PTR(d); - d->ref = 1; + d->ref.initializeOwned(); d->size = size; d->alloc = size; d->capacityReserved = false; @@ -1357,7 +1357,7 @@ QByteArray::QByteArray(int size, char ch) } else { d = static_cast<Data *>(malloc(sizeof(Data) + size + 1)); Q_CHECK_PTR(d); - d->ref = 1; + d->ref.initializeOwned(); d->size = size; d->alloc = size; d->capacityReserved = false; @@ -1377,7 +1377,7 @@ QByteArray::QByteArray(int size, Qt::Initialization) { d = static_cast<Data *>(malloc(sizeof(Data) + size + 1)); Q_CHECK_PTR(d); - d->ref = 1; + d->ref.initializeOwned(); d->size = size; d->alloc = size; d->capacityReserved = false; @@ -1424,7 +1424,7 @@ void QByteArray::resize(int size) // Data *x = static_cast<Data *>(malloc(sizeof(Data) + size + 1)); Q_CHECK_PTR(x); - x->ref = 1; + x->ref.initializeOwned(); x->size = size; x->alloc = size; x->capacityReserved = false; @@ -1466,7 +1466,7 @@ void QByteArray::realloc(int alloc) if (d->ref != 1 || d->offset) { Data *x = static_cast<Data *>(malloc(sizeof(Data) + alloc + 1)); Q_CHECK_PTR(x); - x->ref = 1; + x->ref.initializeOwned(); x->size = qMin(alloc, d->size); x->alloc = alloc; x->capacityReserved = d->capacityReserved; @@ -3887,7 +3887,7 @@ QByteArray QByteArray::fromRawData(const char *data, int size) } else { x = static_cast<Data *>(malloc(sizeof(Data) + 1)); Q_CHECK_PTR(x); - x->ref = 1; + x->ref.initializeOwned(); x->size = size; x->alloc = 0; x->capacityReserved = false; diff --git a/src/corelib/tools/qbytearray.h b/src/corelib/tools/qbytearray.h index e96997909f..2409ff1232 100644 --- a/src/corelib/tools/qbytearray.h +++ b/src/corelib/tools/qbytearray.h @@ -133,24 +133,24 @@ struct QByteArrayData inline const char *data() const { return d + sizeof(qptrdiff) + offset; } }; -template<int N> struct QConstByteArrayData +template<int N> struct QStaticByteArrayData { - const QByteArrayData ba; - const char data[N + 1]; + QByteArrayData ba; + char data[N + 1]; }; -template<int N> struct QConstByteArrayDataPtr +template<int N> struct QStaticByteArrayDataPtr { - const QConstByteArrayData<N> *ptr; + const QStaticByteArrayData<N> *ptr; }; #if defined(Q_COMPILER_LAMBDA) -# define QByteArrayLiteral(str) ([]() -> QConstByteArrayDataPtr<sizeof(str) - 1> { \ +# define QByteArrayLiteral(str) ([]() -> QStaticByteArrayDataPtr<sizeof(str) - 1> { \ enum { Size = sizeof(str) - 1 }; \ - static const QConstByteArrayData<Size> qbytearray_literal = \ - { { Q_REFCOUNT_INITIALIZER(-1), Size, 0, 0, { 0 } }, str }; \ - QConstByteArrayDataPtr<Size> holder = { &qbytearray_literal }; \ + static const QStaticByteArrayData<Size> qbytearray_literal = \ + { { Q_REFCOUNT_INITIALIZE_STATIC, Size, 0, 0, { 0 } }, str }; \ + QStaticByteArrayDataPtr<Size> holder = { &qbytearray_literal }; \ return holder; }()) #elif defined(Q_CC_GNU) @@ -161,9 +161,9 @@ template<int N> struct QConstByteArrayDataPtr # define QByteArrayLiteral(str) \ __extension__ ({ \ enum { Size = sizeof(str) - 1 }; \ - static const QConstByteArrayData<Size> qbytearray_literal = \ - { { Q_REFCOUNT_INITIALIZER(-1), Size, 0, 0, { 0 } }, str }; \ - QConstByteArrayDataPtr<Size> holder = { &qbytearray_literal }; \ + static const QStaticByteArrayData<Size> qbytearray_literal = \ + { { Q_REFCOUNT_INITIALIZE_STATIC, Size, 0, 0, { 0 } }, str }; \ + QStaticByteArrayDataPtr<Size> holder = { &qbytearray_literal }; \ holder; }) #endif @@ -378,16 +378,16 @@ public: bool isNull() const; template <int n> - inline QByteArray(const QConstByteArrayData<n> &dd) + inline QByteArray(const QStaticByteArrayData<n> &dd) : d(const_cast<QByteArrayData *>(&dd.str)) {} template <int N> - Q_DECL_CONSTEXPR inline QByteArray(QConstByteArrayDataPtr<N> dd) + Q_DECL_CONSTEXPR inline QByteArray(QStaticByteArrayDataPtr<N> dd) : d(const_cast<QByteArrayData *>(&dd.ptr->ba)) {} private: operator QNoImplicitBoolCast() const; - static const QConstByteArrayData<1> shared_null; - static const QConstByteArrayData<1> shared_empty; + static const QStaticByteArrayData<1> shared_null; + static const QStaticByteArrayData<1> shared_empty; Data *d; QByteArray(Data *dd, int /*dummy*/, int /*dummy*/) : d(dd) {} void realloc(int alloc); diff --git a/src/corelib/tools/qhash.cpp b/src/corelib/tools/qhash.cpp index fac8c2f8ac..82ac8a0591 100644 --- a/src/corelib/tools/qhash.cpp +++ b/src/corelib/tools/qhash.cpp @@ -166,7 +166,7 @@ static int countBits(int hint) const int MinNumBits = 4; const QHashData QHashData::shared_null = { - 0, 0, Q_REFCOUNT_INITIALIZER(-1), 0, 0, MinNumBits, 0, 0, true, false, 0 + 0, 0, Q_REFCOUNT_INITIALIZE_STATIC, 0, 0, MinNumBits, 0, 0, true, false, 0 }; void *QHashData::allocateNode(int nodeAlign) @@ -196,7 +196,7 @@ QHashData *QHashData::detach_helper(void (*node_duplicate)(Node *, void *), d = new QHashData; d->fakeNext = 0; d->buckets = 0; - d->ref = 1; + d->ref.initializeOwned(); d->size = size; d->nodeSize = nodeSize; d->userNumBits = userNumBits; diff --git a/src/corelib/tools/qlinkedlist.cpp b/src/corelib/tools/qlinkedlist.cpp index 3ef67cb85b..acfbcd07eb 100644 --- a/src/corelib/tools/qlinkedlist.cpp +++ b/src/corelib/tools/qlinkedlist.cpp @@ -46,7 +46,7 @@ QT_BEGIN_NAMESPACE const QLinkedListData QLinkedListData::shared_null = { const_cast<QLinkedListData *>(&QLinkedListData::shared_null), const_cast<QLinkedListData *>(&QLinkedListData::shared_null), - Q_REFCOUNT_INITIALIZER(-1), 0, true + Q_REFCOUNT_INITIALIZE_STATIC, 0, true }; /*! \class QLinkedList diff --git a/src/corelib/tools/qlinkedlist.h b/src/corelib/tools/qlinkedlist.h index f1def3d166..966b74ddfa 100644 --- a/src/corelib/tools/qlinkedlist.h +++ b/src/corelib/tools/qlinkedlist.h @@ -253,7 +253,7 @@ void QLinkedList<T>::detach_helper() { union { QLinkedListData *d; Node *e; } x; x.d = new QLinkedListData; - x.d->ref = 1; + x.d->ref.initializeOwned(); x.d->size = d->size; x.d->sharable = true; Node *original = e->n; diff --git a/src/corelib/tools/qlist.cpp b/src/corelib/tools/qlist.cpp index adc6ee7a4b..2afe1ab969 100644 --- a/src/corelib/tools/qlist.cpp +++ b/src/corelib/tools/qlist.cpp @@ -59,7 +59,7 @@ QT_BEGIN_NAMESPACE the number of elements in the list. */ -const QListData::Data QListData::shared_null = { Q_REFCOUNT_INITIALIZER(-1), 0, 0, 0, true, { 0 } }; +const QListData::Data QListData::shared_null = { Q_REFCOUNT_INITIALIZE_STATIC, 0, 0, 0, true, { 0 } }; static int grow(int size) { @@ -87,7 +87,7 @@ QListData::Data *QListData::detach_grow(int *idx, int num) Data* t = static_cast<Data *>(::malloc(DataHeaderSize + alloc * sizeof(void *))); Q_CHECK_PTR(t); - t->ref = 1; + t->ref.initializeOwned(); t->sharable = true; t->alloc = alloc; // The space reservation algorithm's optimization is biased towards appending: @@ -129,7 +129,7 @@ QListData::Data *QListData::detach(int alloc) Data* t = static_cast<Data *>(::malloc(DataHeaderSize + alloc * sizeof(void *))); Q_CHECK_PTR(t); - t->ref = 1; + t->ref.initializeOwned(); t->sharable = true; t->alloc = alloc; if (!alloc) { diff --git a/src/corelib/tools/qmap.cpp b/src/corelib/tools/qmap.cpp index a688ae1c1a..b1e8ecb0ad 100644 --- a/src/corelib/tools/qmap.cpp +++ b/src/corelib/tools/qmap.cpp @@ -53,7 +53,7 @@ QT_BEGIN_NAMESPACE const QMapData QMapData::shared_null = { const_cast<QMapData *>(&shared_null), { const_cast<QMapData *>(&shared_null), 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, - Q_REFCOUNT_INITIALIZER(-1), 0, 0, 0, false, true, false, 0 + Q_REFCOUNT_INITIALIZE_STATIC, 0, 0, 0, false, true, false, 0 }; QMapData *QMapData::createData(int alignment) @@ -63,7 +63,7 @@ QMapData *QMapData::createData(int alignment) Node *e = reinterpret_cast<Node *>(d); e->backward = e; e->forward[0] = e; - d->ref = 1; + d->ref.initializeOwned(); d->topLevel = 0; d->size = 0; d->randomBits = 0; diff --git a/src/corelib/tools/qrefcount.h b/src/corelib/tools/qrefcount.h index 5b3884806f..698351456f 100644 --- a/src/corelib/tools/qrefcount.h +++ b/src/corelib/tools/qrefcount.h @@ -56,17 +56,51 @@ namespace QtPrivate class RefCount { public: - inline void ref() { - if (atomic.load() > 0) + inline bool ref() { + int count = atomic.load(); + if (count == 0) // !isSharable + return false; + if (count != -1) // !isStatic atomic.ref(); + return true; } inline bool deref() { - if (atomic.load() <= 0) + int count = atomic.load(); + if (count == 0) // !isSharable + return false; + if (count == -1) // isStatic return true; return atomic.deref(); } + bool setSharable(bool sharable) + { + Q_ASSERT(!isShared()); + if (sharable) + return atomic.testAndSetRelaxed(0, 1); + else + return atomic.testAndSetRelaxed(1, 0); + } + + bool isStatic() const + { + // Persistent object, never deleted + return atomic.load() == -1; + } + + bool isSharable() const + { + // Sharable === Shared ownership. + return atomic.load() != 0; + } + + bool isShared() const + { + int count = atomic.load(); + return (count != 1) && (count != 0); + } + inline bool operator==(int value) const { return atomic.load() == value; } inline bool operator!=(int value) const @@ -75,18 +109,17 @@ public: { return !atomic.load(); } inline operator int() const { return atomic.load(); } - inline RefCount &operator=(int value) - { atomic.store(value); return *this; } - inline RefCount &operator=(const RefCount &other) - { atomic.store(other.atomic.load()); return *this; } + + void initializeOwned() { atomic.store(1); } + void initializeUnsharable() { atomic.store(0); } QBasicAtomicInt atomic; }; -#define Q_REFCOUNT_INITIALIZER(a) { Q_BASIC_ATOMIC_INITIALIZER(a) } - } +#define Q_REFCOUNT_INITIALIZE_STATIC { Q_BASIC_ATOMIC_INITIALIZER(-1) } + QT_END_NAMESPACE QT_END_HEADER diff --git a/src/corelib/tools/qstring.cpp b/src/corelib/tools/qstring.cpp index 1b6ff3c5df..dbe1e913c3 100644 --- a/src/corelib/tools/qstring.cpp +++ b/src/corelib/tools/qstring.cpp @@ -798,8 +798,8 @@ const QString::Null QString::null = { }; \sa split() */ -const QConstStringData<1> QString::shared_null = { { Q_REFCOUNT_INITIALIZER(-1), 0, 0, false, { 0 } }, { 0 } }; -const QConstStringData<1> QString::shared_empty = { { Q_REFCOUNT_INITIALIZER(-1), 0, 0, false, { 0 } }, { 0 } }; +const QStaticStringData<1> QString::shared_null = { { Q_REFCOUNT_INITIALIZE_STATIC, 0, 0, false, { 0 } }, { 0 } }; +const QStaticStringData<1> QString::shared_empty = { { Q_REFCOUNT_INITIALIZE_STATIC, 0, 0, false, { 0 } }, { 0 } }; int QString::grow(int size) { @@ -1032,7 +1032,7 @@ QString::QString(const QChar *unicode, int size) } else { d = (Data*) ::malloc(sizeof(Data)+(size+1)*sizeof(QChar)); Q_CHECK_PTR(d); - d->ref = 1; + d->ref.initializeOwned(); d->size = size; d->alloc = (uint) size; d->capacityReserved = false; @@ -1064,7 +1064,7 @@ QString::QString(const QChar *unicode) } else { d = (Data*) ::malloc(sizeof(Data)+(size+1)*sizeof(QChar)); Q_CHECK_PTR(d); - d->ref = 1; + d->ref.initializeOwned(); d->size = size; d->alloc = (uint) size; d->capacityReserved = false; @@ -1089,7 +1089,7 @@ QString::QString(int size, QChar ch) } else { d = (Data*) ::malloc(sizeof(Data)+(size+1)*sizeof(QChar)); Q_CHECK_PTR(d); - d->ref = 1; + d->ref.initializeOwned(); d->size = size; d->alloc = (uint) size; d->capacityReserved = false; @@ -1113,7 +1113,7 @@ QString::QString(int size, Qt::Initialization) { d = (Data*) ::malloc(sizeof(Data)+(size+1)*sizeof(QChar)); Q_CHECK_PTR(d); - d->ref = 1; + d->ref.initializeOwned(); d->size = size; d->alloc = (uint) size; d->capacityReserved = false; @@ -1135,7 +1135,7 @@ QString::QString(QChar ch) { d = (Data *) ::malloc(sizeof(Data) + 2*sizeof(QChar)); Q_CHECK_PTR(d); - d->ref = 1; + d->ref.initializeOwned(); d->size = 1; d->alloc = 1; d->capacityReserved = false; @@ -1314,7 +1314,7 @@ void QString::realloc(int alloc) if (d->ref != 1 || d->offset) { Data *x = static_cast<Data *>(::malloc(sizeof(Data) + (alloc+1) * sizeof(QChar))); Q_CHECK_PTR(x); - x->ref = 1; + x->ref.initializeOwned(); x->size = qMin(alloc, d->size); x->alloc = (uint) alloc; x->capacityReserved = d->capacityReserved; @@ -3758,7 +3758,7 @@ QString::Data *QString::fromLatin1_helper(const char *str, int size) size = qstrlen(str); d = static_cast<Data *>(::malloc(sizeof(Data) + (size+1) * sizeof(QChar))); Q_CHECK_PTR(d); - d->ref = 1; + d->ref.initializeOwned(); d->size = size; d->alloc = (uint) size; d->capacityReserved = false; @@ -7065,7 +7065,7 @@ QString QString::fromRawData(const QChar *unicode, int size) } else { x = static_cast<Data *>(::malloc(sizeof(Data) + sizeof(ushort))); Q_CHECK_PTR(x); - x->ref = 1; + x->ref.initializeOwned(); x->size = size; x->alloc = 0; x->capacityReserved = false; diff --git a/src/corelib/tools/qstring.h b/src/corelib/tools/qstring.h index 9d92f403eb..f7898bbadb 100644 --- a/src/corelib/tools/qstring.h +++ b/src/corelib/tools/qstring.h @@ -84,27 +84,27 @@ struct QStringData { inline const ushort *data() const { return d + sizeof(qptrdiff)/sizeof(ushort) + offset; } }; -template<int N> struct QConstStringData; -template<int N> struct QConstStringDataPtr +template<int N> struct QStaticStringData; +template<int N> struct QStaticStringDataPtr { - const QConstStringData<N> *ptr; + const QStaticStringData<N> *ptr; }; #if defined(Q_COMPILER_UNICODE_STRINGS) -template<int N> struct QConstStringData +template<int N> struct QStaticStringData { - const QStringData str; - const char16_t data[N + 1]; + QStringData str; + char16_t data[N + 1]; }; #define QT_UNICODE_LITERAL_II(str) u"" str #elif defined(Q_OS_WIN) || (defined(__SIZEOF_WCHAR_T__) && __SIZEOF_WCHAR_T__ == 2) || defined(WCHAR_MAX) && (WCHAR_MAX - 0 < 65536) // wchar_t is 2 bytes -template<int N> struct QConstStringData +template<int N> struct QStaticStringData { - const QStringData str; - const wchar_t data[N + 1]; + QStringData str; + wchar_t data[N + 1]; }; #if defined(Q_CC_MSVC) @@ -114,21 +114,21 @@ template<int N> struct QConstStringData #endif #else -template<int N> struct QConstStringData +template<int N> struct QStaticStringData { - const QStringData str; - const ushort data[N + 1]; + QStringData str; + ushort data[N + 1]; }; #endif #if defined(QT_UNICODE_LITERAL_II) # define QT_UNICODE_LITERAL(str) QT_UNICODE_LITERAL_II(str) # if defined(Q_COMPILER_LAMBDA) -# define QStringLiteral(str) ([]() -> QConstStringDataPtr<sizeof(QT_UNICODE_LITERAL(str))/2 - 1> { \ +# define QStringLiteral(str) ([]() -> QStaticStringDataPtr<sizeof(QT_UNICODE_LITERAL(str))/2 - 1> { \ enum { Size = sizeof(QT_UNICODE_LITERAL(str))/2 - 1 }; \ - static const QConstStringData<Size> qstring_literal = \ - { { Q_REFCOUNT_INITIALIZER(-1), Size, 0, 0, { 0 } }, QT_UNICODE_LITERAL(str) }; \ - QConstStringDataPtr<Size> holder = { &qstring_literal }; \ + static const QStaticStringData<Size> qstring_literal = \ + { { Q_REFCOUNT_INITIALIZE_STATIC, Size, 0, 0, { 0 } }, QT_UNICODE_LITERAL(str) }; \ + QStaticStringDataPtr<Size> holder = { &qstring_literal }; \ return holder; }()) # elif defined(Q_CC_GNU) @@ -139,9 +139,9 @@ template<int N> struct QConstStringData # define QStringLiteral(str) \ __extension__ ({ \ enum { Size = sizeof(QT_UNICODE_LITERAL(str))/2 - 1 }; \ - static const QConstStringData<Size> qstring_literal = \ - { { Q_REFCOUNT_INITIALIZER(-1), Size, 0, 0, { 0 } }, QT_UNICODE_LITERAL(str) }; \ - QConstStringDataPtr<Size> holder = { &qstring_literal }; \ + static const QStaticStringData<Size> qstring_literal = \ + { { Q_REFCOUNT_INITIALIZE_STATIC, Size, 0, 0, { 0 } }, QT_UNICODE_LITERAL(str) }; \ + QStaticStringDataPtr<Size> holder = { &qstring_literal }; \ holder; }) # endif #endif @@ -587,9 +587,9 @@ public: QString(int size, Qt::Initialization); template <int n> - inline QString(const QConstStringData<n> &dd) : d(const_cast<QStringData *>(&dd.str)) {} + inline QString(const QStaticStringData<n> &dd) : d(const_cast<QStringData *>(&dd.str)) {} template <int N> - Q_DECL_CONSTEXPR inline QString(QConstStringDataPtr<N> dd) : d(const_cast<QStringData *>(&dd.ptr->str)) {} + Q_DECL_CONSTEXPR inline QString(QStaticStringDataPtr<N> dd) : d(const_cast<QStringData *>(&dd.ptr->str)) {} private: #if defined(QT_NO_CAST_FROM_ASCII) && !defined(Q_NO_DECLARED_NOT_DEFINED) @@ -601,8 +601,8 @@ private: QString &operator=(const QByteArray &a); #endif - static const QConstStringData<1> shared_null; - static const QConstStringData<1> shared_empty; + static const QStaticStringData<1> shared_null; + static const QStaticStringData<1> shared_empty; Data *d; inline QString(Data *dd, int /*dummy*/) : d(dd) {} diff --git a/src/corelib/tools/qstringbuilder.h b/src/corelib/tools/qstringbuilder.h index a9b8c973b1..016e37fb38 100644 --- a/src/corelib/tools/qstringbuilder.h +++ b/src/corelib/tools/qstringbuilder.h @@ -249,9 +249,9 @@ template <> struct QConcatenable<QString> : private QAbstractConcatenable #endif }; -template <int N> struct QConcatenable<QConstStringDataPtr<N> > : private QAbstractConcatenable +template <int N> struct QConcatenable<QStaticStringDataPtr<N> > : private QAbstractConcatenable { - typedef QConstStringDataPtr<N> type; + typedef QStaticStringDataPtr<N> type; typedef QString ConvertTo; enum { ExactSize = true }; static int size(const type &) { return N; } @@ -363,9 +363,9 @@ template <> struct QConcatenable<QByteArray> : private QAbstractConcatenable } }; -template <int N> struct QConcatenable<QConstByteArrayDataPtr<N> > : private QAbstractConcatenable +template <int N> struct QConcatenable<QStaticByteArrayDataPtr<N> > : private QAbstractConcatenable { - typedef QConstByteArrayDataPtr<N> type; + typedef QStaticByteArrayDataPtr<N> type; typedef QByteArray ConvertTo; enum { ExactSize = false }; static int size(const type &) { return N; } diff --git a/src/corelib/tools/qvector.cpp b/src/corelib/tools/qvector.cpp index 95775d4bd8..59ca11179b 100644 --- a/src/corelib/tools/qvector.cpp +++ b/src/corelib/tools/qvector.cpp @@ -54,7 +54,7 @@ static inline int alignmentThreshold() return 2 * sizeof(void*); } -const QVectorData QVectorData::shared_null = { Q_REFCOUNT_INITIALIZER(-1), 0, 0, true, false, 0 }; +const QVectorData QVectorData::shared_null = { Q_REFCOUNT_INITIALIZE_STATIC, 0, 0, false, 0 }; QVectorData *QVectorData::malloc(int sizeofTypedData, int size, int sizeofT, QVectorData *init) { diff --git a/src/corelib/tools/qvector.h b/src/corelib/tools/qvector.h index 2e8abcad25..51364df91d 100644 --- a/src/corelib/tools/qvector.h +++ b/src/corelib/tools/qvector.h @@ -70,13 +70,11 @@ struct Q_CORE_EXPORT QVectorData int size; #if defined(QT_ARCH_SPARC) && defined(Q_CC_GNU) && defined(__LP64__) && defined(QT_BOOTSTRAPPED) // workaround for bug in gcc 3.4.2 - uint sharable; uint capacity; uint reserved; #else - uint sharable : 1; uint capacity : 1; - uint reserved : 30; + uint reserved : 31; #endif static const QVectorData shared_null; @@ -120,7 +118,19 @@ public: inline QVector() : d(const_cast<QVectorData *>(&QVectorData::shared_null)) { } explicit QVector(int size); QVector(int size, const T &t); - inline QVector(const QVector<T> &v) : d(v.d) { d->ref.ref(); if (!d->sharable) detach_helper(); } + inline QVector(const QVector<T> &v) + { + if (v.d->ref.ref()) { + d = v.d; + } else { + d = const_cast<QVectorData *>(&QVectorData::shared_null); + realloc(0, v.d->alloc); + qCopy(v.p->array, v.p->array + v.d->size, p->array); + d->size = v.d->size; + d->capacity = v.d->capacity; + } + } + inline ~QVector() { if (!d) return; if (!d->ref.deref()) free(p); } QVector<T> &operator=(const QVector<T> &v); #ifdef Q_COMPILER_RVALUE_REFS @@ -144,9 +154,18 @@ public: void reserve(int size); inline void squeeze() { realloc(d->size, d->size); d->capacity = 0; } - inline void detach() { if (d->ref != 1) detach_helper(); } - inline bool isDetached() const { return d->ref == 1; } - inline void setSharable(bool sharable) { if (!sharable) detach(); if (d != &QVectorData::shared_null) d->sharable = sharable; } + inline void detach() { if (!isDetached()) detach_helper(); } + inline bool isDetached() const { return !d->ref.isShared(); } + inline void setSharable(bool sharable) + { + if (sharable == d->ref.isSharable()) + return; + if (!sharable) + detach(); + if (d != &QVectorData::shared_null) + d->ref.setSharable(sharable); + } + inline bool isSharedWith(const QVector<T> &other) const { return d == other.d; } inline T *data() { detach(); return p->array; } @@ -337,7 +356,7 @@ void QVector<T>::detach_helper() { realloc(d->size, d->alloc); } template <typename T> void QVector<T>::reserve(int asize) -{ if (asize > d->alloc) realloc(d->size, asize); if (d->ref == 1) d->capacity = 1; } +{ if (asize > d->alloc) realloc(d->size, asize); if (isDetached()) d->capacity = 1; } template <typename T> void QVector<T>::resize(int asize) { realloc(asize, (asize > d->alloc || (!d->capacity && asize < d->size && asize < (d->alloc >> 1))) ? @@ -389,13 +408,10 @@ inline void QVector<T>::replace(int i, const T &t) template <typename T> QVector<T> &QVector<T>::operator=(const QVector<T> &v) { - QVectorData *o = v.d; - o->ref.ref(); - if (!d->ref.deref()) - free(p); - d = o; - if (!d->sharable) - detach_helper(); + if (v.d != d) { + QVector<T> tmp(v); + tmp.swap(*this); + } return *this; } @@ -411,9 +427,8 @@ template <typename T> QVector<T>::QVector(int asize) { d = malloc(asize); - d->ref = 1; + d->ref.initializeOwned(); d->alloc = d->size = asize; - d->sharable = true; d->capacity = false; if (QTypeInfo<T>::isComplex) { T* b = p->array; @@ -429,9 +444,8 @@ template <typename T> QVector<T>::QVector(int asize, const T &t) { d = malloc(asize); - d->ref = 1; + d->ref.initializeOwned(); d->alloc = d->size = asize; - d->sharable = true; d->capacity = false; T* i = p->array + d->size; while (i != p->array) @@ -443,9 +457,8 @@ template <typename T> QVector<T>::QVector(std::initializer_list<T> args) { d = malloc(int(args.size())); - d->ref = 1; + d->ref.initializeOwned(); d->alloc = d->size = int(args.size()); - d->sharable = true; d->capacity = false; T* i = p->array + d->size; auto it = args.end(); @@ -477,7 +490,7 @@ void QVector<T>::realloc(int asize, int aalloc) union { QVectorData *d; Data *p; } x; x.d = d; - if (QTypeInfo<T>::isComplex && asize < d->size && d->ref == 1 ) { + if (QTypeInfo<T>::isComplex && asize < d->size && isDetached()) { // call the destructor on all objects that need to be // destroyed when shrinking pOld = p->array + d->size; @@ -488,13 +501,13 @@ void QVector<T>::realloc(int asize, int aalloc) } } - if (aalloc != d->alloc || d->ref != 1) { + if (aalloc != d->alloc || !isDetached()) { // (re)allocate memory if (QTypeInfo<T>::isStatic) { x.d = malloc(aalloc); Q_CHECK_PTR(x.p); x.d->size = 0; - } else if (d->ref != 1) { + } else if (!isDetached()) { x.d = malloc(aalloc); Q_CHECK_PTR(x.p); if (QTypeInfo<T>::isComplex) { @@ -515,9 +528,8 @@ void QVector<T>::realloc(int asize, int aalloc) QT_RETHROW; } } - x.d->ref = 1; + x.d->ref.initializeOwned(); x.d->alloc = aalloc; - x.d->sharable = true; x.d->capacity = d->capacity; x.d->reserved = 0; } @@ -572,7 +584,7 @@ Q_OUTOFLINE_TEMPLATE T QVector<T>::value(int i, const T &defaultValue) const template <typename T> void QVector<T>::append(const T &t) { - if (d->ref != 1 || d->size + 1 > d->alloc) { + if (!isDetached() || d->size + 1 > d->alloc) { const T copy(t); realloc(d->size, (d->size + 1 > d->alloc) ? QVectorData::grow(sizeOfTypedData(), d->size + 1, sizeof(T), QTypeInfo<T>::isStatic) @@ -596,7 +608,7 @@ typename QVector<T>::iterator QVector<T>::insert(iterator before, size_type n, c int offset = int(before - p->array); if (n != 0) { const T copy(t); - if (d->ref != 1 || d->size + n > d->alloc) + if (!isDetached() || d->size + n > d->alloc) realloc(d->size, QVectorData::grow(sizeOfTypedData(), d->size + n, sizeof(T), QTypeInfo<T>::isStatic)); if (QTypeInfo<T>::isStatic) { diff --git a/src/corelib/tools/tools.pri b/src/corelib/tools/tools.pri index 13b597d513..2ec32fd9e9 100644 --- a/src/corelib/tools/tools.pri +++ b/src/corelib/tools/tools.pri @@ -2,6 +2,8 @@ HEADERS += \ tools/qalgorithms.h \ + tools/qarraydata.h \ + tools/qarraydataops.h \ tools/qbitarray.h \ tools/qbytearray.h \ tools/qbytearraymatcher.h \ @@ -55,6 +57,7 @@ HEADERS += \ SOURCES += \ + tools/qarraydata.cpp \ tools/qbitarray.cpp \ tools/qbytearray.cpp \ tools/qbytearraymatcher.cpp \ diff --git a/tests/auto/corelib/tools/qarraydata/qarraydata.pro b/tests/auto/corelib/tools/qarraydata/qarraydata.pro new file mode 100644 index 0000000000..8e368117fa --- /dev/null +++ b/tests/auto/corelib/tools/qarraydata/qarraydata.pro @@ -0,0 +1,5 @@ +TARGET = tst_qarraydata +SOURCES += tst_qarraydata.cpp +HEADERS += simplevector.h +QT = core testlib +CONFIG += testcase parallel_test diff --git a/tests/auto/corelib/tools/qarraydata/simplevector.h b/tests/auto/corelib/tools/qarraydata/simplevector.h new file mode 100644 index 0000000000..a1eb2dac48 --- /dev/null +++ b/tests/auto/corelib/tools/qarraydata/simplevector.h @@ -0,0 +1,329 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + + +#ifndef QARRAY_TEST_SIMPLE_VECTOR_H +#define QARRAY_TEST_SIMPLE_VECTOR_H + +#include <QtCore/qarraydata.h> +#include <QtCore/qarraydatapointer.h> + +#include <algorithm> + +template <class T> +struct SimpleVector +{ +private: + typedef QTypedArrayData<T> Data; + +public: + typedef T value_type; + typedef typename Data::iterator iterator; + typedef typename Data::const_iterator const_iterator; + + SimpleVector() + { + } + + SimpleVector(size_t n, const T &t) + : d(Data::allocate(n)) + { + if (n) + d->copyAppend(n, t); + } + + SimpleVector(const T *begin, const T *end) + : d(Data::allocate(end - begin)) + { + if (end - begin) + d->copyAppend(begin, end); + } + + explicit SimpleVector(Data *ptr) + : d(ptr) + { + } + + bool empty() const { return d->size == 0; } + bool isNull() const { return d.isNull(); } + bool isEmpty() const { return this->empty(); } + + bool isStatic() const { return d->ref.isStatic(); } + bool isShared() const { return d->ref.isShared(); } + bool isSharedWith(const SimpleVector &other) const { return d == other.d; } + bool isSharable() const { return d->ref.isSharable(); } + + void setSharable(bool sharable) { d.setSharable(sharable); } + + size_t size() const { return d->size; } + size_t capacity() const { return d->alloc; } + + iterator begin() { detach(); return d->begin(); } + iterator end() { detach(); return d->end(); } + + const_iterator begin() const { return d->begin(); } + const_iterator end() const { return d->end(); } + + const_iterator constBegin() const { return begin(); } + const_iterator constEnd() const { return end(); } + + T &operator[](size_t i) { Q_ASSERT(i < size_t(d->size)); detach(); return begin()[i]; } + T &at(size_t i) { Q_ASSERT(i < size_t(d->size)); detach(); return begin()[i]; } + + const T &operator[](size_t i) const { Q_ASSERT(i < size_t(d->size)); return begin()[i]; } + const T &at(size_t i) const { Q_ASSERT(i < size_t(d->size)); return begin()[i]; } + + T &front() + { + Q_ASSERT(!isEmpty()); + detach(); + return *begin(); + } + + T &back() + { + Q_ASSERT(!isEmpty()); + detach(); + return *(end() - 1); + } + + const T &front() const + { + Q_ASSERT(!isEmpty()); + return *begin(); + } + + const T &back() const + { + Q_ASSERT(!isEmpty()); + return *(end() - 1); + } + + void reserve(size_t n) + { + if (n == 0) + return; + + if (n <= capacity()) { + if (d->capacityReserved) + return; + if (!d->ref.isShared()) { + d->capacityReserved = 1; + return; + } + } + + SimpleVector detached(Data::allocate(n, + d->detachFlags() | Data::CapacityReserved)); + detached.d->copyAppend(constBegin(), constEnd()); + detached.swap(*this); + } + + void prepend(const_iterator first, const_iterator last) + { + if (!d->size) { + append(first, last); + return; + } + + if (first == last) + return; + + T *const begin = d->begin(); + if (d->ref.isShared() + || capacity() - size() < size_t(last - first)) { + SimpleVector detached(Data::allocate( + qMax(capacity(), size() + (last - first)), + d->detachFlags())); + + detached.d->copyAppend(first, last); + detached.d->copyAppend(begin, begin + d->size); + detached.swap(*this); + + return; + } + + d->insert(begin, first, last); + } + + void append(const_iterator first, const_iterator last) + { + if (first == last) + return; + + if (d->ref.isShared() + || capacity() - size() < size_t(last - first)) { + SimpleVector detached(Data::allocate( + qMax(capacity(), size() + (last - first)), + d->detachFlags())); + + if (d->size) { + const T *const begin = constBegin(); + detached.d->copyAppend(begin, begin + d->size); + } + detached.d->copyAppend(first, last); + detached.swap(*this); + + return; + } + + d->copyAppend(first, last); + } + + void insert(int position, const_iterator first, const_iterator last) + { + if (position < 0) + position += d->size + 1; + + if (position <= 0) { + prepend(first, last); + return; + } + + if (size_t(position) >= size()) { + append(first, last); + return; + } + + if (first == last) + return; + + T *const begin = d->begin(); + T *const where = begin + position; + const T *const end = begin + d->size; + if (d->ref.isShared() + || capacity() - size() < size_t(last - first)) { + SimpleVector detached(Data::allocate( + qMax(capacity(), size() + (last - first)), + d->detachFlags())); + + if (position) + detached.d->copyAppend(begin, where); + detached.d->copyAppend(first, last); + detached.d->copyAppend(where, end); + detached.swap(*this); + + return; + } + + if ((first >= where && first < end) + || (last > where && last <= end)) { + // Copy overlapping data first and only then shuffle it into place + T *start = d->begin() + position; + T *middle = d->end(); + + d->copyAppend(first, last); + std::rotate(start, middle, d->end()); + + return; + } + + d->insert(where, first, last); + } + + void swap(SimpleVector &other) + { + qSwap(d, other.d); + } + + void clear() + { + d.clear(); + } + + void detach() + { + d.detach(); + } + +private: + QArrayDataPointer<T> d; +}; + +template <class T> +bool operator==(const SimpleVector<T> &lhs, const SimpleVector<T> &rhs) +{ + if (lhs.isSharedWith(rhs)) + return true; + if (lhs.size() != rhs.size()) + return false; + return std::equal(lhs.begin(), lhs.end(), rhs.begin()); +} + +template <class T> +bool operator!=(const SimpleVector<T> &lhs, const SimpleVector<T> &rhs) +{ + return !(lhs == rhs); +} + +template <class T> +bool operator<(const SimpleVector<T> &lhs, const SimpleVector<T> &rhs) +{ + return std::lexicographical_compare(lhs.begin(), lhs.end(), rhs.begin(), rhs.end()); +} + +template <class T> +bool operator>(const SimpleVector<T> &lhs, const SimpleVector<T> &rhs) +{ + return rhs < lhs; +} + +template <class T> +bool operator<=(const SimpleVector<T> &lhs, const SimpleVector<T> &rhs) +{ + return !(rhs < lhs); +} + +template <class T> +bool operator>=(const SimpleVector<T> &lhs, const SimpleVector<T> &rhs) +{ + return !(lhs < rhs); +} + +namespace std { + template <class T> + void swap(SimpleVector<T> &v1, SimpleVector<T> &v2) + { + v1.swap(v2); + } +} + +#endif // include guard diff --git a/tests/auto/corelib/tools/qarraydata/tst_qarraydata.cpp b/tests/auto/corelib/tools/qarraydata/tst_qarraydata.cpp new file mode 100644 index 0000000000..90c865c9e7 --- /dev/null +++ b/tests/auto/corelib/tools/qarraydata/tst_qarraydata.cpp @@ -0,0 +1,1135 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + + +#include <QtTest/QtTest> +#include <QtCore/QString> +#include <QtCore/qarraydata.h> + +#include "simplevector.h" + +struct SharedNullVerifier +{ + SharedNullVerifier() + { + Q_ASSERT(QArrayData::shared_null.ref.isStatic()); + Q_ASSERT(QArrayData::shared_null.ref.isShared()); + Q_ASSERT(QArrayData::shared_null.ref.isSharable()); + } +}; + +// This is meant to verify/ensure that shared_null is not being dynamically +// initialized and stays away from the order-of-static-initialization fiasco. +// +// Of course, if this was to fail, qmake and the build should have crashed and +// burned before we ever got to this point :-) +SharedNullVerifier globalInit; + +class tst_QArrayData : public QObject +{ + Q_OBJECT + +private slots: + void referenceCounting(); + void sharedNullEmpty(); + void staticData(); + void simpleVector(); + void allocate_data(); + void allocate(); + void alignment_data(); + void alignment(); + void typedData(); + void gccBug43247(); + void arrayOps(); + void setSharable_data(); + void setSharable(); +}; + +template <class T> const T &const_(const T &t) { return t; } + +void tst_QArrayData::referenceCounting() +{ + { + // Reference counting initialized to 1 (owned) + QArrayData array = { { Q_BASIC_ATOMIC_INITIALIZER(1) }, 0, 0, 0, 0 }; + + QCOMPARE(int(array.ref), 1); + + QVERIFY(!array.ref.isStatic()); + QVERIFY(array.ref.isSharable()); + + QVERIFY(array.ref.ref()); + QCOMPARE(int(array.ref), 2); + + QVERIFY(array.ref.deref()); + QCOMPARE(int(array.ref), 1); + + QVERIFY(array.ref.ref()); + QCOMPARE(int(array.ref), 2); + + QVERIFY(array.ref.deref()); + QCOMPARE(int(array.ref), 1); + + QVERIFY(!array.ref.deref()); + QCOMPARE(int(array.ref), 0); + + // Now would be a good time to free/release allocated data + } + + { + // Reference counting initialized to 0 (non-sharable) + QArrayData array = { { Q_BASIC_ATOMIC_INITIALIZER(0) }, 0, 0, 0, 0 }; + + QCOMPARE(int(array.ref), 0); + + QVERIFY(!array.ref.isStatic()); + QVERIFY(!array.ref.isSharable()); + + QVERIFY(!array.ref.ref()); + // Reference counting fails, data should be copied + QCOMPARE(int(array.ref), 0); + + QVERIFY(!array.ref.deref()); + QCOMPARE(int(array.ref), 0); + + // Free/release data + } + + { + // Reference counting initialized to -1 (static read-only data) + QArrayData array = { Q_REFCOUNT_INITIALIZE_STATIC, 0, 0, 0, 0 }; + + QCOMPARE(int(array.ref), -1); + + QVERIFY(array.ref.isStatic()); + QVERIFY(array.ref.isSharable()); + + QVERIFY(array.ref.ref()); + QCOMPARE(int(array.ref), -1); + + QVERIFY(array.ref.deref()); + QCOMPARE(int(array.ref), -1); + } +} + +void tst_QArrayData::sharedNullEmpty() +{ + QArrayData *null = const_cast<QArrayData *>(&QArrayData::shared_null); + QArrayData *empty = QArrayData::allocate(1, Q_ALIGNOF(QArrayData), 0); + + QVERIFY(null->ref.isStatic()); + QVERIFY(null->ref.isSharable()); + QVERIFY(null->ref.isShared()); + + QVERIFY(empty->ref.isStatic()); + QVERIFY(empty->ref.isSharable()); + QVERIFY(empty->ref.isShared()); + + QCOMPARE(int(null->ref), -1); + QCOMPARE(int(empty->ref), -1); + + QVERIFY(null->ref.ref()); + QVERIFY(empty->ref.ref()); + + QCOMPARE(int(null->ref), -1); + QCOMPARE(int(empty->ref), -1); + + QVERIFY(null->ref.deref()); + QVERIFY(empty->ref.deref()); + + QCOMPARE(int(null->ref), -1); + QCOMPARE(int(empty->ref), -1); + + QVERIFY(null != empty); + + QCOMPARE(null->size, 0); + QCOMPARE(null->alloc, 0u); + QCOMPARE(null->capacityReserved, 0u); + + QCOMPARE(empty->size, 0); + QCOMPARE(empty->alloc, 0u); + QCOMPARE(empty->capacityReserved, 0u); +} + +void tst_QArrayData::staticData() +{ + QStaticArrayData<char, 10> charArray = { + Q_STATIC_ARRAY_DATA_HEADER_INITIALIZER(char, 10), + { 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j' } + }; + QStaticArrayData<int, 10> intArray = { + Q_STATIC_ARRAY_DATA_HEADER_INITIALIZER(int, 10), + { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 } + }; + QStaticArrayData<double, 10> doubleArray = { + Q_STATIC_ARRAY_DATA_HEADER_INITIALIZER(double, 10), + { 0.f, 1.f, 2.f, 3.f, 4.f, 5.f, 6.f, 7.f, 8.f, 9.f } + }; + + QCOMPARE(charArray.header.size, 10); + QCOMPARE(intArray.header.size, 10); + QCOMPARE(doubleArray.header.size, 10); + + QCOMPARE(charArray.header.data(), reinterpret_cast<void *>(&charArray.data)); + QCOMPARE(intArray.header.data(), reinterpret_cast<void *>(&intArray.data)); + QCOMPARE(doubleArray.header.data(), reinterpret_cast<void *>(&doubleArray.data)); +} + +void tst_QArrayData::simpleVector() +{ + QArrayData data0 = { Q_REFCOUNT_INITIALIZE_STATIC, 0, 0, 0, 0 }; + QStaticArrayData<int, 7> data1 = { + Q_STATIC_ARRAY_DATA_HEADER_INITIALIZER(int, 7), + { 0, 1, 2, 3, 4, 5, 6 } + }; + + int array[] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 }; + + SimpleVector<int> v1; + SimpleVector<int> v2(v1); + SimpleVector<int> v3(static_cast<QTypedArrayData<int> *>(&data0)); + SimpleVector<int> v4(static_cast<QTypedArrayData<int> *>(&data1.header)); + SimpleVector<int> v5(static_cast<QTypedArrayData<int> *>(&data0)); + SimpleVector<int> v6(static_cast<QTypedArrayData<int> *>(&data1.header)); + SimpleVector<int> v7(10, 5); + SimpleVector<int> v8(array, array + sizeof(array)/sizeof(*array)); + + v3 = v1; + v1.swap(v3); + v4.clear(); + + QVERIFY(v1.isNull()); + QVERIFY(v2.isNull()); + QVERIFY(v3.isNull()); + QVERIFY(!v4.isNull()); + QVERIFY(!v5.isNull()); + QVERIFY(!v6.isNull()); + QVERIFY(!v7.isNull()); + QVERIFY(!v8.isNull()); + + QVERIFY(v1.isEmpty()); + QVERIFY(v2.isEmpty()); + QVERIFY(v3.isEmpty()); + QVERIFY(v4.isEmpty()); + QVERIFY(v5.isEmpty()); + QVERIFY(!v6.isEmpty()); + QVERIFY(!v7.isEmpty()); + QVERIFY(!v8.isEmpty()); + + QCOMPARE(v1.size(), size_t(0)); + QCOMPARE(v2.size(), size_t(0)); + QCOMPARE(v3.size(), size_t(0)); + QCOMPARE(v4.size(), size_t(0)); + QCOMPARE(v5.size(), size_t(0)); + QCOMPARE(v6.size(), size_t(7)); + QCOMPARE(v7.size(), size_t(10)); + QCOMPARE(v8.size(), size_t(10)); + + QCOMPARE(v1.capacity(), size_t(0)); + QCOMPARE(v2.capacity(), size_t(0)); + QCOMPARE(v3.capacity(), size_t(0)); + QCOMPARE(v4.capacity(), size_t(0)); + QCOMPARE(v5.capacity(), size_t(0)); + // v6.capacity() is unspecified, for now + QVERIFY(v7.capacity() >= size_t(10)); + QVERIFY(v8.capacity() >= size_t(10)); + + QVERIFY(v1.isStatic()); + QVERIFY(v2.isStatic()); + QVERIFY(v3.isStatic()); + QVERIFY(v4.isStatic()); + QVERIFY(v5.isStatic()); + QVERIFY(v6.isStatic()); + QVERIFY(!v7.isStatic()); + QVERIFY(!v8.isStatic()); + + QVERIFY(v1.isShared()); + QVERIFY(v2.isShared()); + QVERIFY(v3.isShared()); + QVERIFY(v4.isShared()); + QVERIFY(v5.isShared()); + QVERIFY(v6.isShared()); + QVERIFY(!v7.isShared()); + QVERIFY((SimpleVector<int>(v7), v7.isShared())); + QVERIFY(!v7.isShared()); + QVERIFY(!v8.isShared()); + + QVERIFY(v1.isSharable()); + QVERIFY(v2.isSharable()); + QVERIFY(v3.isSharable()); + QVERIFY(v4.isSharable()); + QVERIFY(v5.isSharable()); + QVERIFY(v6.isSharable()); + QVERIFY(v7.isSharable()); + QVERIFY(v8.isSharable()); + + QVERIFY(v1.isSharedWith(v2)); + QVERIFY(v1.isSharedWith(v3)); + QVERIFY(!v1.isSharedWith(v4)); + QVERIFY(!v1.isSharedWith(v5)); + QVERIFY(!v1.isSharedWith(v6)); + + QVERIFY(v1.constBegin() == v1.constEnd()); + QVERIFY(v4.constBegin() == v4.constEnd()); + QVERIFY(v6.constBegin() + v6.size() == v6.constEnd()); + QVERIFY(v7.constBegin() + v7.size() == v7.constEnd()); + QVERIFY(v8.constBegin() + v8.size() == v8.constEnd()); + + QVERIFY(v1 == v2); + QVERIFY(v1 == v3); + QVERIFY(v1 == v4); + QVERIFY(v1 == v5); + QVERIFY(!(v1 == v6)); + + QVERIFY(v1 != v6); + QVERIFY(v4 != v6); + QVERIFY(v5 != v6); + QVERIFY(!(v1 != v5)); + + QVERIFY(v1 < v6); + QVERIFY(!(v6 < v1)); + QVERIFY(v6 > v1); + QVERIFY(!(v1 > v6)); + QVERIFY(v1 <= v6); + QVERIFY(!(v6 <= v1)); + QVERIFY(v6 >= v1); + QVERIFY(!(v1 >= v6)); + + { + SimpleVector<int> temp(v6); + + QCOMPARE(const_(v6).front(), 0); + QCOMPARE(const_(v6).back(), 6); + + QVERIFY(temp.isShared()); + QVERIFY(temp.isSharedWith(v6)); + + QCOMPARE(temp.front(), 0); + QCOMPARE(temp.back(), 6); + + // Detached + QVERIFY(!temp.isShared()); + const int *const tempBegin = temp.begin(); + + for (size_t i = 0; i < v6.size(); ++i) { + QCOMPARE(const_(v6)[i], int(i)); + QCOMPARE(const_(v6).at(i), int(i)); + QCOMPARE(&const_(v6)[i], &const_(v6).at(i)); + + QCOMPARE(const_(v8)[i], const_(v6)[i]); + + QCOMPARE(temp[i], int(i)); + QCOMPARE(temp.at(i), int(i)); + QCOMPARE(&temp[i], &temp.at(i)); + } + + // A single detach should do + QCOMPARE(temp.begin(), tempBegin); + } + + { + int count = 0; + Q_FOREACH (int value, v7) { + QCOMPARE(value, 5); + ++count; + } + + QCOMPARE(count, 10); + } + + { + int count = 0; + Q_FOREACH (int value, v8) { + QCOMPARE(value, count); + ++count; + } + + QCOMPARE(count, 10); + } + + v5 = v6; + QVERIFY(v5.isSharedWith(v6)); + QVERIFY(!v1.isSharedWith(v5)); + + v1.swap(v6); + QVERIFY(v6.isNull()); + QVERIFY(v1.isSharedWith(v5)); + + { + using std::swap; + swap(v1, v6); + QVERIFY(v5.isSharedWith(v6)); + QVERIFY(!v1.isSharedWith(v5)); + } + + v1.prepend(array, array + sizeof(array)/sizeof(array[0])); + QCOMPARE(v1.size(), size_t(10)); + QVERIFY(v1 == v8); + + v6 = v1; + QVERIFY(v1.isSharedWith(v6)); + + v1.prepend(array, array + sizeof(array)/sizeof(array[0])); + QVERIFY(!v1.isSharedWith(v6)); + QCOMPARE(v1.size(), size_t(20)); + QCOMPARE(v6.size(), size_t(10)); + + for (int i = 0; i < 20; ++i) + QCOMPARE(v1[i], v6[i % 10]); + + v1.clear(); + + v1.append(array, array + sizeof(array)/sizeof(array[0])); + QCOMPARE(v1.size(), size_t(10)); + QVERIFY(v1 == v8); + + v6 = v1; + QVERIFY(v1.isSharedWith(v6)); + + v1.append(array, array + sizeof(array)/sizeof(array[0])); + QVERIFY(!v1.isSharedWith(v6)); + QCOMPARE(v1.size(), size_t(20)); + QCOMPARE(v6.size(), size_t(10)); + + for (int i = 0; i < 20; ++i) + QCOMPARE(v1[i], v6[i % 10]); + + v1.insert(0, v6.constBegin(), v6.constEnd()); + QCOMPARE(v1.size(), size_t(30)); + + v6 = v1; + QVERIFY(v1.isSharedWith(v6)); + + v1.insert(10, v6.constBegin(), v6.constEnd()); + QVERIFY(!v1.isSharedWith(v6)); + QCOMPARE(v1.size(), size_t(60)); + QCOMPARE(v6.size(), size_t(30)); + + for (int i = 0; i < 30; ++i) + QCOMPARE(v6[i], v8[i % 10]); + + v1.insert(v1.size(), v6.constBegin(), v6.constEnd()); + QCOMPARE(v1.size(), size_t(90)); + + v1.insert(-1, v8.constBegin(), v8.constEnd()); + QCOMPARE(v1.size(), size_t(100)); + + v1.insert(-11, v8.constBegin(), v8.constEnd()); + QCOMPARE(v1.size(), size_t(110)); + + v1.insert(-200, v8.constBegin(), v8.constEnd()); + QCOMPARE(v1.size(), size_t(120)); + + for (int i = 0; i < 120; ++i) + QCOMPARE(v1[i], v8[i % 10]); + + { + v7.setSharable(true); + QVERIFY(v7.isSharable()); + + SimpleVector<int> copy1(v7); + QVERIFY(copy1.isSharedWith(v7)); + + v7.setSharable(false); + QVERIFY(!v7.isSharable()); + + QVERIFY(!copy1.isSharedWith(v7)); + QCOMPARE(v7.size(), copy1.size()); + for (size_t i = 0; i < copy1.size(); ++i) + QCOMPARE(v7[i], copy1[i]); + + SimpleVector<int> clone(v7); + QVERIFY(!clone.isSharedWith(v7)); + QCOMPARE(clone.size(), copy1.size()); + for (size_t i = 0; i < copy1.size(); ++i) + QCOMPARE(clone[i], copy1[i]); + + v7.setSharable(true); + QVERIFY(v7.isSharable()); + + SimpleVector<int> copy2(v7); + QVERIFY(copy2.isSharedWith(v7)); + } + + { + SimpleVector<int> null; + SimpleVector<int> empty(0, 5); + + QVERIFY(null.isSharable()); + QVERIFY(empty.isSharable()); + + null.setSharable(true); + empty.setSharable(true); + + QVERIFY(null.isSharable()); + QVERIFY(empty.isSharable()); + + QVERIFY(null.isEmpty()); + QVERIFY(empty.isEmpty()); + + null.setSharable(false); + empty.setSharable(false); + + QVERIFY(!null.isSharable()); + QVERIFY(!empty.isSharable()); + + QVERIFY(null.isEmpty()); + QVERIFY(empty.isEmpty()); + + null.setSharable(true); + empty.setSharable(true); + + QVERIFY(null.isSharable()); + QVERIFY(empty.isSharable()); + + QVERIFY(null.isEmpty()); + QVERIFY(empty.isEmpty()); + } +} + +struct Deallocator +{ + Deallocator(size_t objectSize, size_t alignment) + : objectSize(objectSize) + , alignment(alignment) + { + } + + ~Deallocator() + { + Q_FOREACH (QArrayData *data, headers) + QArrayData::deallocate(data, objectSize, alignment); + } + + size_t objectSize; + size_t alignment; + QVector<QArrayData *> headers; +}; + +Q_DECLARE_METATYPE(const QArrayData *) +Q_DECLARE_METATYPE(QArrayData::AllocateOptions) + +void tst_QArrayData::allocate_data() +{ + QTest::addColumn<size_t>("objectSize"); + QTest::addColumn<size_t>("alignment"); + QTest::addColumn<QArrayData::AllocateOptions>("allocateOptions"); + QTest::addColumn<bool>("isCapacityReserved"); + QTest::addColumn<bool>("isSharable"); + QTest::addColumn<const QArrayData *>("commonEmpty"); + + struct { + char const *typeName; + size_t objectSize; + size_t alignment; + } types[] = { + { "char", sizeof(char), Q_ALIGNOF(char) }, + { "short", sizeof(short), Q_ALIGNOF(short) }, + { "void *", sizeof(void *), Q_ALIGNOF(void *) } + }; + + QArrayData *shared_empty = QArrayData::allocate(0, Q_ALIGNOF(QArrayData), 0); + QArrayData *unsharable_empty = QArrayData::allocate(0, Q_ALIGNOF(QArrayData), 0, QArrayData::Unsharable); + + QVERIFY(shared_empty); + QVERIFY(unsharable_empty); + + struct { + char const *description; + QArrayData::AllocateOptions allocateOptions; + bool isCapacityReserved; + bool isSharable; + const QArrayData *commonEmpty; + } options[] = { + { "Default", QArrayData::Default, false, true, shared_empty }, + { "Reserved", QArrayData::CapacityReserved, true, true, shared_empty }, + { "Reserved | Unsharable", + QArrayData::CapacityReserved | QArrayData::Unsharable, true, false, + unsharable_empty }, + { "Unsharable", QArrayData::Unsharable, false, false, unsharable_empty }, + }; + + for (size_t i = 0; i < sizeof(types)/sizeof(types[0]); ++i) + for (size_t j = 0; j < sizeof(options)/sizeof(options[0]); ++j) + QTest::newRow(qPrintable( + QLatin1String(types[i].typeName) + + QLatin1String(": ") + + QLatin1String(options[j].description))) + << types[i].objectSize << types[i].alignment + << options[j].allocateOptions << options[j].isCapacityReserved + << options[j].isSharable << options[j].commonEmpty; +} + +void tst_QArrayData::allocate() +{ + QFETCH(size_t, objectSize); + QFETCH(size_t, alignment); + QFETCH(QArrayData::AllocateOptions, allocateOptions); + QFETCH(bool, isCapacityReserved); + QFETCH(bool, isSharable); + QFETCH(const QArrayData *, commonEmpty); + + // Minimum alignment that can be requested is that of QArrayData. + // Typically, this alignment is sizeof(void *) and ensured by malloc. + size_t minAlignment = qMax(alignment, Q_ALIGNOF(QArrayData)); + + // Shared Empty + QCOMPARE(QArrayData::allocate(objectSize, minAlignment, 0, + QArrayData::AllocateOptions(allocateOptions)), commonEmpty); + + Deallocator keeper(objectSize, minAlignment); + keeper.headers.reserve(1024); + + for (int capacity = 1; capacity <= 1024; capacity <<= 1) { + QArrayData *data = QArrayData::allocate(objectSize, minAlignment, + capacity, QArrayData::AllocateOptions(allocateOptions)); + keeper.headers.append(data); + + QCOMPARE(data->size, 0); + QVERIFY(data->alloc >= uint(capacity)); + QCOMPARE(data->capacityReserved, uint(isCapacityReserved)); + QCOMPARE(data->ref.isSharable(), isSharable); + + // Check that the allocated array can be used. Best tested with a + // memory checker, such as valgrind, running. + ::memset(data->data(), 'A', objectSize * capacity); + } +} + +class Unaligned +{ + char dummy[8]; +}; + +void tst_QArrayData::alignment_data() +{ + QTest::addColumn<size_t>("alignment"); + + for (int i = 1; i < 10; ++i) { + size_t alignment = 1u << i; + QTest::newRow(qPrintable(QString::number(alignment))) << alignment; + } +} + +void tst_QArrayData::alignment() +{ + QFETCH(size_t, alignment); + + // Minimum alignment that can be requested is that of QArrayData. + // Typically, this alignment is sizeof(void *) and ensured by malloc. + size_t minAlignment = qMax(alignment, Q_ALIGNOF(QArrayData)); + + Deallocator keeper(sizeof(Unaligned), minAlignment); + keeper.headers.reserve(100); + + for (int i = 0; i < 100; ++i) { + QArrayData *data = QArrayData::allocate(sizeof(Unaligned), + minAlignment, 8, QArrayData::Default); + keeper.headers.append(data); + + QVERIFY(data); + QCOMPARE(data->size, 0); + QVERIFY(data->alloc >= uint(8)); + + // These conditions should hold as long as header and array are + // allocated together + QVERIFY(data->offset >= qptrdiff(sizeof(QArrayData))); + QVERIFY(data->offset <= qptrdiff(sizeof(QArrayData) + + minAlignment - Q_ALIGNOF(QArrayData))); + + // Data is aligned + QCOMPARE(quintptr(data->data()) % alignment, quintptr(0u)); + + // Check that the allocated array can be used. Best tested with a + // memory checker, such as valgrind, running. + ::memset(data->data(), 'A', sizeof(Unaligned) * 8); + } +} + +void tst_QArrayData::typedData() +{ + QStaticArrayData<int, 10> data = { + Q_STATIC_ARRAY_DATA_HEADER_INITIALIZER(int, 10), + { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 } + }; + QCOMPARE(data.header.size, 10); + + { + QTypedArrayData<int> *array = + static_cast<QTypedArrayData<int> *>(&data.header); + QCOMPARE(array->data(), data.data); + + int j = 0; + for (QTypedArrayData<int>::iterator iter = array->begin(); + iter != array->end(); ++iter, ++j) + QCOMPARE(iter, data.data + j); + QCOMPARE(j, 10); + } + + { + const QTypedArrayData<int> *array = + static_cast<const QTypedArrayData<int> *>(&data.header); + + QCOMPARE(array->data(), data.data); + + int j = 0; + for (QTypedArrayData<int>::const_iterator iter = array->begin(); + iter != array->end(); ++iter, ++j) + QCOMPARE(iter, data.data + j); + QCOMPARE(j, 10); + } + + { + QTypedArrayData<int> *null = QTypedArrayData<int>::sharedNull(); + QTypedArrayData<int> *empty = QTypedArrayData<int>::allocate(0); + + QVERIFY(null != empty); + + QCOMPARE(null->size, 0); + QCOMPARE(empty->size, 0); + + QCOMPARE(null->begin(), null->end()); + QCOMPARE(empty->begin(), empty->end()); + } + + + { + Deallocator keeper(sizeof(char), + Q_ALIGNOF(QTypedArrayData<char>::AlignmentDummy)); + QArrayData *array = QTypedArrayData<char>::allocate(10, false); + keeper.headers.append(array); + + QVERIFY(array); + QCOMPARE(array->size, 0); + QCOMPARE(array->alloc, 10u); + + // Check that the allocated array can be used. Best tested with a + // memory checker, such as valgrind, running. + ::memset(array->data(), 0, 10 * sizeof(char)); + + keeper.headers.clear(); + QTypedArrayData<short>::deallocate(array); + + QVERIFY(true); + } + + { + Deallocator keeper(sizeof(short), + Q_ALIGNOF(QTypedArrayData<short>::AlignmentDummy)); + QArrayData *array = QTypedArrayData<short>::allocate(10, false); + keeper.headers.append(array); + + QVERIFY(array); + QCOMPARE(array->size, 0); + QCOMPARE(array->alloc, 10u); + + // Check that the allocated array can be used. Best tested with a + // memory checker, such as valgrind, running. + ::memset(array->data(), 0, 10 * sizeof(short)); + + keeper.headers.clear(); + QTypedArrayData<short>::deallocate(array); + + QVERIFY(true); + } + + { + Deallocator keeper(sizeof(double), + Q_ALIGNOF(QTypedArrayData<double>::AlignmentDummy)); + QArrayData *array = QTypedArrayData<double>::allocate(10, false); + keeper.headers.append(array); + + QVERIFY(array); + QCOMPARE(array->size, 0); + QCOMPARE(array->alloc, 10u); + + // Check that the allocated array can be used. Best tested with a + // memory checker, such as valgrind, running. + ::memset(array->data(), 0, 10 * sizeof(double)); + + keeper.headers.clear(); + QTypedArrayData<double>::deallocate(array); + + QVERIFY(true); + } +} + +void tst_QArrayData::gccBug43247() +{ + // This test tries to verify QArrayData is not affected by GCC optimizer + // bug #43247. + // Reported on GCC 4.4.3, Linux, affects QVector + + QTest::ignoreMessage(QtDebugMsg, "GCC Optimization bug #43247 not triggered (3)"); + QTest::ignoreMessage(QtDebugMsg, "GCC Optimization bug #43247 not triggered (4)"); + QTest::ignoreMessage(QtDebugMsg, "GCC Optimization bug #43247 not triggered (5)"); + QTest::ignoreMessage(QtDebugMsg, "GCC Optimization bug #43247 not triggered (6)"); + QTest::ignoreMessage(QtDebugMsg, "GCC Optimization bug #43247 not triggered (7)"); + + SimpleVector<int> array(10, 0); + // QVector<int> vector(10, 0); + + for (int i = 0; i < 10; ++i) { + if (i >= 3 && i < 8) + qDebug("GCC Optimization bug #43247 not triggered (%i)", i); + + // When access to data is implemented through an array of size 1, this + // line lets the compiler assume i == 0, and the conditional above is + // skipped. + QVERIFY(array.at(i) == 0); + // QVERIFY(vector.at(i) == 0); + } +} + +struct CountedObject +{ + CountedObject() + : id(liveCount++) + { + } + + CountedObject(const CountedObject &other) + : id(other.id) + { + ++liveCount; + } + + ~CountedObject() + { + --liveCount; + } + + CountedObject &operator=(const CountedObject &other) + { + id = other.id; + return *this; + } + + struct LeakChecker + { + LeakChecker() + : previousLiveCount(liveCount) + { + } + + ~LeakChecker() + { + QCOMPARE(liveCount, previousLiveCount); + } + + private: + const size_t previousLiveCount; + }; + + size_t id; // not unique + static size_t liveCount; +}; + +size_t CountedObject::liveCount = 0; + +void tst_QArrayData::arrayOps() +{ + CountedObject::LeakChecker leakChecker; Q_UNUSED(leakChecker) + + const int intArray[5] = { 80, 101, 100, 114, 111 }; + const QString stringArray[5] = { + QLatin1String("just"), + QLatin1String("for"), + QLatin1String("testing"), + QLatin1String("a"), + QLatin1String("vector") + }; + const CountedObject objArray[5]; + + QVERIFY(!QTypeInfo<int>::isComplex && !QTypeInfo<int>::isStatic); + QVERIFY(QTypeInfo<QString>::isComplex && !QTypeInfo<QString>::isStatic); + QVERIFY(QTypeInfo<CountedObject>::isComplex && QTypeInfo<CountedObject>::isStatic); + + QCOMPARE(CountedObject::liveCount, size_t(5)); + for (size_t i = 0; i < 5; ++i) + QCOMPARE(objArray[i].id, i); + + //////////////////////////////////////////////////////////////////////////// + // copyAppend (I) + SimpleVector<int> vi(intArray, intArray + 5); + SimpleVector<QString> vs(stringArray, stringArray + 5); + SimpleVector<CountedObject> vo(objArray, objArray + 5); + + QCOMPARE(CountedObject::liveCount, size_t(10)); + for (int i = 0; i < 5; ++i) { + QCOMPARE(vi[i], intArray[i]); + QVERIFY(vs[i].isSharedWith(stringArray[i])); + QCOMPARE(vo[i].id, objArray[i].id); + } + + //////////////////////////////////////////////////////////////////////////// + // destroyAll + vi.clear(); + vs.clear(); + vo.clear(); + + QCOMPARE(CountedObject::liveCount, size_t(5)); + + //////////////////////////////////////////////////////////////////////////// + // copyAppend (II) + int referenceInt = 7; + QString referenceString = QLatin1String("reference"); + CountedObject referenceObject; + + vi = SimpleVector<int>(5, referenceInt); + vs = SimpleVector<QString>(5, referenceString); + vo = SimpleVector<CountedObject>(5, referenceObject); + + QCOMPARE(vi.size(), size_t(5)); + QCOMPARE(vs.size(), size_t(5)); + QCOMPARE(vo.size(), size_t(5)); + + QCOMPARE(CountedObject::liveCount, size_t(11)); + for (int i = 0; i < 5; ++i) { + QCOMPARE(vi[i], referenceInt); + QVERIFY(vs[i].isSharedWith(referenceString)); + QCOMPARE(vo[i].id, referenceObject.id); + } + + //////////////////////////////////////////////////////////////////////////// + // insert + vi.reserve(30); + vs.reserve(30); + vo.reserve(30); + + QCOMPARE(vi.size(), size_t(5)); + QCOMPARE(vs.size(), size_t(5)); + QCOMPARE(vo.size(), size_t(5)); + + QVERIFY(vi.capacity() >= 30); + QVERIFY(vs.capacity() >= 30); + QVERIFY(vo.capacity() >= 30); + + // Displace as many elements as array is extended by + vi.insert(0, intArray, intArray + 5); + vs.insert(0, stringArray, stringArray + 5); + vo.insert(0, objArray, objArray + 5); + + QCOMPARE(vi.size(), size_t(10)); + QCOMPARE(vs.size(), size_t(10)); + QCOMPARE(vo.size(), size_t(10)); + + // Displace more elements than array is extended by + vi.insert(0, intArray, intArray + 5); + vs.insert(0, stringArray, stringArray + 5); + vo.insert(0, objArray, objArray + 5); + + QCOMPARE(vi.size(), size_t(15)); + QCOMPARE(vs.size(), size_t(15)); + QCOMPARE(vo.size(), size_t(15)); + + // Displace less elements than array is extended by + vi.insert(5, vi.constBegin(), vi.constEnd()); + vs.insert(5, vs.constBegin(), vs.constEnd()); + vo.insert(5, vo.constBegin(), vo.constEnd()); + + QCOMPARE(vi.size(), size_t(30)); + QCOMPARE(vs.size(), size_t(30)); + QCOMPARE(vo.size(), size_t(30)); + + QCOMPARE(CountedObject::liveCount, size_t(36)); + for (int i = 0; i < 15; ++i) { + QCOMPARE(vi[i], intArray[i % 5]); + QVERIFY(vs[i].isSharedWith(stringArray[i % 5])); + QCOMPARE(vo[i].id, objArray[i % 5].id); + } + + for (int i = 15; i < 20; ++i) { + QCOMPARE(vi[i], referenceInt); + QVERIFY(vs[i].isSharedWith(referenceString)); + QCOMPARE(vo[i].id, referenceObject.id); + } + + for (int i = 20; i < 25; ++i) { + QCOMPARE(vi[i], intArray[i % 5]); + QVERIFY(vs[i].isSharedWith(stringArray[i % 5])); + QCOMPARE(vo[i].id, objArray[i % 5].id); + } + + for (int i = 25; i < 30; ++i) { + QCOMPARE(vi[i], referenceInt); + QVERIFY(vs[i].isSharedWith(referenceString)); + QCOMPARE(vo[i].id, referenceObject.id); + } +} + +Q_DECLARE_METATYPE(QArrayDataPointer<int>) + +static inline bool arrayIsFilledWith(const QArrayDataPointer<int> &array, + int fillValue, size_t size) +{ + const int *iter = array->begin(); + const int *const end = array->end(); + + for (size_t i = 0; i < size; ++i, ++iter) + if (*iter != fillValue) + return false; + + if (iter != end) + return false; + + return true; +} + +void tst_QArrayData::setSharable_data() +{ + QTest::addColumn<QArrayDataPointer<int> >("array"); + QTest::addColumn<size_t>("size"); + QTest::addColumn<size_t>("capacity"); + QTest::addColumn<bool>("isCapacityReserved"); + QTest::addColumn<int>("fillValue"); + + QArrayDataPointer<int> null; + QArrayDataPointer<int> empty; empty.clear(); + + static QStaticArrayData<int, 10> staticArrayData = { + Q_STATIC_ARRAY_DATA_HEADER_INITIALIZER(int, 10), + { 3, 3, 3, 3, 3, 3, 3, 3, 3, 3 } + }; + + QArrayDataPointer<int> emptyReserved(QTypedArrayData<int>::allocate(5, + QArrayData::CapacityReserved)); + QArrayDataPointer<int> nonEmpty(QTypedArrayData<int>::allocate(10, + QArrayData::Default)); + QArrayDataPointer<int> nonEmptyReserved(QTypedArrayData<int>::allocate(15, + QArrayData::CapacityReserved)); + QArrayDataPointer<int> staticArray( + static_cast<QTypedArrayData<int> *>(&staticArrayData.header)); + + nonEmpty->copyAppend(5, 1); + nonEmptyReserved->copyAppend(7, 2); + + QTest::newRow("shared-null") << null << size_t(0) << size_t(0) << false << 0; + QTest::newRow("shared-empty") << empty << size_t(0) << size_t(0) << false << 0; + // unsharable-empty implicitly tested in shared-empty + QTest::newRow("empty-reserved") << emptyReserved << size_t(0) << size_t(5) << true << 0; + QTest::newRow("non-empty") << nonEmpty << size_t(5) << size_t(10) << false << 1; + QTest::newRow("non-empty-reserved") << nonEmptyReserved << size_t(7) << size_t(15) << true << 2; + QTest::newRow("static-array") << staticArray << size_t(10) << size_t(0) << false << 3; +} + +void tst_QArrayData::setSharable() +{ + QFETCH(QArrayDataPointer<int>, array); + QFETCH(size_t, size); + QFETCH(size_t, capacity); + QFETCH(bool, isCapacityReserved); + QFETCH(int, fillValue); + + QVERIFY(array->ref.isShared()); // QTest has a copy + QVERIFY(array->ref.isSharable()); + + QCOMPARE(size_t(array->size), size); + QCOMPARE(size_t(array->alloc), capacity); + QCOMPARE(bool(array->capacityReserved), isCapacityReserved); + QVERIFY(arrayIsFilledWith(array, fillValue, size)); + + // shared-null becomes shared-empty, may otherwise detach + array.setSharable(true); + + QVERIFY(array->ref.isSharable()); + QVERIFY(arrayIsFilledWith(array, fillValue, size)); + + { + QArrayDataPointer<int> copy(array); + QVERIFY(array->ref.isShared()); + QVERIFY(array->ref.isSharable()); + QCOMPARE(copy.data(), array.data()); + } + + // Unshare, must detach + array.setSharable(false); + + // Immutability (alloc == 0) is lost on detach + if (capacity == 0 && size != 0) + capacity = size; + + QVERIFY(!array->ref.isShared()); + QVERIFY(!array->ref.isSharable()); + + QCOMPARE(size_t(array->size), size); + QCOMPARE(size_t(array->alloc), capacity); + QCOMPARE(bool(array->capacityReserved), isCapacityReserved); + QVERIFY(arrayIsFilledWith(array, fillValue, size)); + + { + QArrayDataPointer<int> copy(array); + QVERIFY(!array->ref.isShared()); + QVERIFY(!array->ref.isSharable()); + + // Null/empty is always shared + QCOMPARE(copy->ref.isShared(), !(size || isCapacityReserved)); + QVERIFY(copy->ref.isSharable()); + + QCOMPARE(size_t(copy->size), size); + QCOMPARE(size_t(copy->alloc), capacity); + QCOMPARE(bool(copy->capacityReserved), isCapacityReserved); + QVERIFY(arrayIsFilledWith(copy, fillValue, size)); + } + + // Make sharable, again + array.setSharable(true); + + QCOMPARE(array->ref.isShared(), !(size || isCapacityReserved)); + QVERIFY(array->ref.isSharable()); + + QCOMPARE(size_t(array->size), size); + QCOMPARE(size_t(array->alloc), capacity); + QCOMPARE(bool(array->capacityReserved), isCapacityReserved); + QVERIFY(arrayIsFilledWith(array, fillValue, size)); + + { + QArrayDataPointer<int> copy(array); + QVERIFY(array->ref.isShared()); + QCOMPARE(copy.data(), array.data()); + } + + QCOMPARE(array->ref.isShared(), !(size || isCapacityReserved)); + QVERIFY(array->ref.isSharable()); +} + +QTEST_APPLESS_MAIN(tst_QArrayData) +#include "tst_qarraydata.moc" diff --git a/tests/auto/corelib/tools/qvector/tst_qvector.cpp b/tests/auto/corelib/tools/qvector/tst_qvector.cpp index 3c885f25fc..bed2e25d31 100644 --- a/tests/auto/corelib/tools/qvector/tst_qvector.cpp +++ b/tests/auto/corelib/tools/qvector/tst_qvector.cpp @@ -85,6 +85,8 @@ private slots: void initializeList(); void const_shared_null(); + void setSharable_data(); + void setSharable(); }; void tst_QVector::constructors() const @@ -946,5 +948,97 @@ void tst_QVector::const_shared_null() QVERIFY(!v2.isDetached()); } +Q_DECLARE_METATYPE(QVector<int>); + +void tst_QVector::setSharable_data() +{ + QTest::addColumn<QVector<int> >("vector"); + QTest::addColumn<int>("size"); + QTest::addColumn<int>("capacity"); + QTest::addColumn<bool>("isCapacityReserved"); + + QVector<int> null; + QVector<int> empty(0, 5); + QVector<int> emptyReserved; + QVector<int> nonEmpty; + QVector<int> nonEmptyReserved; + + emptyReserved.reserve(10); + nonEmptyReserved.reserve(15); + + nonEmpty << 0 << 1 << 2 << 3 << 4; + nonEmptyReserved << 0 << 1 << 2 << 3 << 4 << 5 << 6; + + QVERIFY(emptyReserved.capacity() >= 10); + QVERIFY(nonEmptyReserved.capacity() >= 15); + + QTest::newRow("null") << null << 0 << 0 << false; + QTest::newRow("empty") << empty << 0 << 0 << false; + QTest::newRow("empty, Reserved") << emptyReserved << 0 << 10 << true; + QTest::newRow("non-empty") << nonEmpty << 5 << 0 << false; + QTest::newRow("non-empty, Reserved") << nonEmptyReserved << 7 << 15 << true; +} + +void tst_QVector::setSharable() +{ + QFETCH(QVector<int>, vector); + QFETCH(int, size); + QFETCH(int, capacity); + QFETCH(bool, isCapacityReserved); + + QVERIFY(!vector.isDetached()); // Shared with QTest + + vector.setSharable(true); + + QCOMPARE(vector.size(), size); + if (isCapacityReserved) + QVERIFY2(vector.capacity() >= capacity, + qPrintable(QString("Capacity is %1, expected at least %2.") + .arg(vector.capacity()) + .arg(capacity))); + + { + QVector<int> copy(vector); + + QVERIFY(!copy.isDetached()); + QVERIFY(copy.isSharedWith(vector)); + } + + vector.setSharable(false); + QVERIFY(vector.isDetached() || vector.isSharedWith(QVector<int>())); + + { + QVector<int> copy(vector); + + QVERIFY(copy.isDetached() || copy.isSharedWith(QVector<int>())); + QCOMPARE(copy.size(), size); + if (isCapacityReserved) + QVERIFY2(copy.capacity() >= capacity, + qPrintable(QString("Capacity is %1, expected at least %2.") + .arg(vector.capacity()) + .arg(capacity))); + QCOMPARE(copy, vector); + } + + vector.setSharable(true); + + { + QVector<int> copy(vector); + + QVERIFY(!copy.isDetached()); + QVERIFY(copy.isSharedWith(vector)); + } + + for (int i = 0; i < vector.size(); ++i) + QCOMPARE(vector[i], i); + + QCOMPARE(vector.size(), size); + if (isCapacityReserved) + QVERIFY2(vector.capacity() >= capacity, + qPrintable(QString("Capacity is %1, expected at least %2.") + .arg(vector.capacity()) + .arg(capacity))); +} + QTEST_APPLESS_MAIN(tst_QVector) #include "tst_qvector.moc" diff --git a/tests/auto/corelib/tools/tools.pro b/tests/auto/corelib/tools/tools.pro index 930799e3b3..e2002a98b6 100644 --- a/tests/auto/corelib/tools/tools.pro +++ b/tests/auto/corelib/tools/tools.pro @@ -1,6 +1,7 @@ TEMPLATE=subdirs SUBDIRS=\ qalgorithms \ + qarraydata \ qbitarray \ qbytearray \ qbytearraymatcher \ diff --git a/tests/benchmarks/corelib/tools/qvector/qrawvector.h b/tests/benchmarks/corelib/tools/qvector/qrawvector.h index 14fe9ca6c1..3bd75164d5 100644 --- a/tests/benchmarks/corelib/tools/qvector/qrawvector.h +++ b/tests/benchmarks/corelib/tools/qvector/qrawvector.h @@ -289,10 +289,9 @@ public: QVector<T> mutateToVector() { Data *d = toBase(m_begin); - d->ref = 1; + d->ref.initializeOwned(); d->alloc = m_alloc; d->size = m_size; - d->sharable = 0; d->capacity = 0; QVector<T> v; |