summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--src/corelib/tools/qarraydata.cpp100
-rw-r--r--src/corelib/tools/qarraydata.h166
-rw-r--r--src/corelib/tools/qarraydataops.h324
-rw-r--r--src/corelib/tools/qarraydatapointer.h200
-rw-r--r--src/corelib/tools/qbytearray.cpp20
-rw-r--r--src/corelib/tools/qbytearray.h32
-rw-r--r--src/corelib/tools/qhash.cpp4
-rw-r--r--src/corelib/tools/qlinkedlist.cpp2
-rw-r--r--src/corelib/tools/qlinkedlist.h2
-rw-r--r--src/corelib/tools/qlist.cpp6
-rw-r--r--src/corelib/tools/qmap.cpp4
-rw-r--r--src/corelib/tools/qrefcount.h51
-rw-r--r--src/corelib/tools/qstring.cpp20
-rw-r--r--src/corelib/tools/qstring.h46
-rw-r--r--src/corelib/tools/qstringbuilder.h8
-rw-r--r--src/corelib/tools/qvector.cpp2
-rw-r--r--src/corelib/tools/qvector.h68
-rw-r--r--src/corelib/tools/tools.pri3
-rw-r--r--tests/auto/corelib/tools/qarraydata/qarraydata.pro5
-rw-r--r--tests/auto/corelib/tools/qarraydata/simplevector.h329
-rw-r--r--tests/auto/corelib/tools/qarraydata/tst_qarraydata.cpp1135
-rw-r--r--tests/auto/corelib/tools/qvector/tst_qvector.cpp94
-rw-r--r--tests/auto/corelib/tools/tools.pro1
-rw-r--r--tests/benchmarks/corelib/tools/qvector/qrawvector.h3
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;