diff options
Diffstat (limited to 'src/corelib/tools')
24 files changed, 1895 insertions, 584 deletions
diff --git a/src/corelib/tools/qarraydata.cpp b/src/corelib/tools/qarraydata.cpp new file mode 100644 index 0000000000..8498d0e4d5 --- /dev/null +++ b/src/corelib/tools/qarraydata.cpp @@ -0,0 +1,111 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/ +** +** 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> +#include <QtCore/private/qtools_p.h> + +#include <stdlib.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, AllocationOptions options) +{ + // Alignment is a power of two + Q_ASSERT(alignment >= Q_ALIGNOF(QArrayData) + && !(alignment & (alignment - 1))); + + // Don't allocate empty headers + if (!(options & RawData) && !capacity) + return !(options & Unsharable) + ? const_cast<QArrayData *>(&qt_array_empty) + : const_cast<QArrayData *>(&qt_array_unsharable_empty); + + size_t headerSize = sizeof(QArrayData); + + // Allocate extra (alignment - Q_ALIGNOF(QArrayData)) padding bytes so we + // can properly align the data array. This assumes malloc is able to + // provide appropriate alignment for the header -- as it should! + // Padding is skipped when allocating a header for RawData. + if (!(options & RawData)) + headerSize += (alignment - Q_ALIGNOF(QArrayData)); + + // Allocate additional space if array is growing + if (options & Grow) + capacity = qAllocMore(objectSize * capacity, headerSize) / objectSize; + + size_t allocSize = headerSize + objectSize * capacity; + + QArrayData *header = static_cast<QArrayData *>(::malloc(allocSize)); + 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; + + ::free(data); +} + +QT_END_NAMESPACE diff --git a/src/corelib/tools/qarraydata.h b/src/corelib/tools/qarraydata.h new file mode 100644 index 0000000000..351a75aade --- /dev/null +++ b/src/corelib/tools/qarraydata.h @@ -0,0 +1,275 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/ +** +** 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> +#include <string.h> + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +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; + } + + // This refers to array data mutability, not "header data" represented by + // data members in QArrayData. Shared data (array and header) must still + // follow COW principles. + bool isMutable() const + { + return alloc != 0; + } + + enum AllocationOption { + CapacityReserved = 0x1, + Unsharable = 0x2, + RawData = 0x4, + Grow = 0x8, + + Default = 0 + }; + + Q_DECLARE_FLAGS(AllocationOptions, AllocationOption) + + AllocationOptions detachFlags() const + { + AllocationOptions result; + if (!ref.isSharable()) + result |= Unsharable; + if (capacityReserved) + result |= CapacityReserved; + return result; + } + + AllocationOptions cloneFlags() const + { + AllocationOptions result; + if (capacityReserved) + result |= CapacityReserved; + return result; + } + + static QArrayData *allocate(size_t objectSize, size_t alignment, + size_t capacity, AllocationOptions 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::AllocationOptions) + +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, + AllocationOptions 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 *fromRawData(const T *data, size_t n, + AllocationOptions options = Default) + { + QTypedArrayData *result = allocate(0, options | RawData); + if (result) { + Q_ASSERT(!result->ref.isShared()); // No shared empty, please! + + result->offset = reinterpret_cast<const char *>(data) + - reinterpret_cast<const char *>(result); + result->size = n; + } + return result; + } + + 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]; +}; + +// Support for returning QArrayDataPointer<T> from functions +template <class T> +struct QArrayDataPointerRef +{ + QTypedArrayData<T> *ptr; +}; + +#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) } \ + /**/ + +//////////////////////////////////////////////////////////////////////////////// +// Q_ARRAY_LITERAL + +// The idea here is to place a (read-only) copy of header and array data in an +// mmappable portion of the executable (typically, .rodata section). This is +// accomplished by hiding a static const instance of QStaticArrayData, which is +// POD. + +#if defined(Q_COMPILER_VARIADIC_MACROS) +#if defined(Q_COMPILER_LAMBDA) +// Hide array inside a lambda +#define Q_ARRAY_LITERAL(Type, ...) \ + ([]() -> QArrayDataPointerRef<Type> { \ + /* MSVC 2010 Doesn't support static variables in a lambda, but */ \ + /* happily accepts them in a static function of a lambda-local */ \ + /* struct :-) */ \ + struct StaticWrapper { \ + static QArrayDataPointerRef<Type> get() \ + { \ + Q_ARRAY_LITERAL_IMPL(Type, __VA_ARGS__) \ + return ref; \ + } \ + }; \ + return StaticWrapper::get(); \ + }()) \ + /**/ +#elif defined(Q_CC_GNU) +// Hide array within GCC's __extension__ {( )} block +#define Q_ARRAY_LITERAL(Type, ...) \ + __extension__ ({ \ + Q_ARRAY_LITERAL_IMPL(Type, __VA_ARGS__) \ + ref; \ + }) \ + /**/ +#endif +#endif // defined(Q_COMPILER_VARIADIC_MACROS) + +#if defined(Q_ARRAY_LITERAL) +#define Q_ARRAY_LITERAL_IMPL(Type, ...) \ + union { Type type_must_be_POD; } dummy; Q_UNUSED(dummy) \ + \ + /* Portable compile-time array size computation */ \ + Type data[] = { __VA_ARGS__ }; Q_UNUSED(data) \ + enum { Size = sizeof(data) / sizeof(data[0]) }; \ + \ + static const QStaticArrayData<Type, Size> literal = { \ + Q_STATIC_ARRAY_DATA_HEADER_INITIALIZER(Type, Size), { __VA_ARGS__ } }; \ + \ + QArrayDataPointerRef<Type> ref = \ + { static_cast<QTypedArrayData<Type> *>( \ + const_cast<QArrayData *>(&literal.header)) }; \ + /**/ +#else +// As a fallback, memory is allocated and data copied to the heap. + +// The fallback macro does NOT use variadic macros and does NOT support +// variable number of arguments. It is suitable for char arrays. + +namespace QtPrivate { + template <class T, size_t N> + inline QArrayDataPointerRef<T> qMakeArrayLiteral(const T (&array)[N]) + { + union { T type_must_be_POD; } dummy; Q_UNUSED(dummy) + + QArrayDataPointerRef<T> result = { QTypedArrayData<T>::allocate(N) }; + Q_CHECK_PTR(result.ptr); + + ::memcpy(result.ptr->data(), array, N * sizeof(T)); + result.ptr->size = N; + + return result; + } +} + +#define Q_ARRAY_LITERAL(Type, Array) \ + QT_PREPEND_NAMESPACE(QtPrivate::qMakeArrayLiteral)<Type>( Array ) +#endif // !defined(Q_ARRAY_LITERAL) + +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..1b8ed3372d --- /dev/null +++ b/src/corelib/tools/qarraydataops.h @@ -0,0 +1,322 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/ +** +** 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 + +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.atomic.load() == 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.atomic.load() == 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 *&it) + : iter(&it) + , end(it) + { + } + + 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 *start, T *finish, size_t diff) + : begin(start) + , end(finish) + , displace(diff) + { + ::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 *w) : where(w) {} + + 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..f5ad53aa54 --- /dev/null +++ b/src/corelib/tools/qarraydatapointer.h @@ -0,0 +1,220 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/ +** +** 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 + +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) + { + Q_CHECK_PTR(ptr); + } + + QArrayDataPointer(QArrayDataPointerRef<T> ref) + : d(ref.ptr) + { + } + + QArrayDataPointer &operator=(const QArrayDataPointer &other) + { + QArrayDataPointer tmp(other); + this->swap(tmp); + return *this; + } + +#ifdef Q_COMPILER_RVALUE_REFS + QArrayDataPointer(QArrayDataPointer &&other) + : d(other.d) + { + other.d = Data::sharedNull(); + } + + QArrayDataPointer &operator=(QArrayDataPointer &&other) + { + this->swap(other); + return *this; + } +#endif + + 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) + { + // Can't call setSharable on static read-only data, like shared_null + // and the internal shared-empties. + 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::sharedNull(); + } + + bool detach() + { + if (!d->isMutable() || d->ref.isShared()) { + Data *copy = clone(d->detachFlags()); + QArrayDataPointer old(d); + d = copy; + return true; + } + + return false; + } + +private: + Data *clone(QArrayData::AllocationOptions 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 b6719c9536..ac936b1d0a 100644 --- a/src/corelib/tools/qbytearray.cpp +++ b/src/corelib/tools/qbytearray.cpp @@ -57,7 +57,7 @@ #include <string.h> #include <stdlib.h> -#define IS_RAW_DATA(d) ((d)->offset != 0) +#define IS_RAW_DATA(d) ((d)->offset != sizeof(QByteArrayData)) QT_BEGIN_NAMESPACE @@ -69,24 +69,25 @@ int qFindByteArray( int qAllocMore(int alloc, int extra) { - if (alloc == 0 && extra == 0) - return 0; - const int page = 1 << 12; - int nalloc; - alloc += extra; - if (alloc < 1<<6) { - nalloc = (1<<3) + ((alloc >>3) << 3); - } else { - // don't do anything if the loop will overflow signed int. - if (alloc >= INT_MAX/2) - return INT_MAX; - nalloc = (alloc < page) ? 1 << 3 : page; - while (nalloc < alloc) { - if (nalloc <= 0) - return INT_MAX; - nalloc *= 2; - } - } + Q_ASSERT(alloc >= 0 && extra >= 0); + Q_ASSERT(alloc < (1 << 30) - extra); + + unsigned nalloc = alloc + extra; + + // Round up to next power of 2 + + // Assuming container is growing, always overshoot + //--nalloc; + + nalloc |= nalloc >> 1; + nalloc |= nalloc >> 2; + nalloc |= nalloc >> 4; + nalloc |= nalloc >> 8; + nalloc |= nalloc >> 16; + ++nalloc; + + Q_ASSERT(nalloc > unsigned(alloc + extra)); + return nalloc - extra; } @@ -554,7 +555,7 @@ QByteArray qUncompress(const uchar* data, int nbytes) } d.take(); // realloc was successful d.reset(p); - d->offset = 0; + d->offset = sizeof(QByteArrayData); int res = ::uncompress((uchar*)d->data(), &len, (uchar*)data+4, nbytes-4); @@ -576,11 +577,11 @@ 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; - d->offset = 0; + d->offset = sizeof(QByteArrayData); d->data()[len] = 0; return QByteArray(d.take(), 0, 0); @@ -614,10 +615,10 @@ static inline char qToLower(char c) return c; } -const QConstByteArrayData<1> QByteArray::shared_null = { { Q_REFCOUNT_INITIALIZER(-1), - 0, 0, 0, { 0 } }, { 0 } }; -const QConstByteArrayData<1> QByteArray::shared_empty = { { Q_REFCOUNT_INITIALIZER(-1), - 0, 0, 0, { 0 } }, { 0 } }; +const QStaticByteArrayData<1> QByteArray::shared_null = { { Q_REFCOUNT_INITIALIZE_STATIC, + 0, 0, 0, sizeof(QByteArrayData) }, { 0 } }; +const QStaticByteArrayData<1> QByteArray::shared_empty = { { Q_REFCOUNT_INITIALIZE_STATIC, + 0, 0, 0, sizeof(QByteArrayData) }, { 0 } }; /*! \class QByteArray @@ -904,7 +905,7 @@ QByteArray &QByteArray::operator=(const char *str) x = const_cast<Data *>(&shared_empty.ba); } else { int len = qstrlen(str); - if (d->ref != 1 || len > int(d->alloc) || (len < d->size && len < int(d->alloc) >> 1)) + if (d->ref.isShared() || len > int(d->alloc) || (len < d->size && len < int(d->alloc) >> 1)) realloc(len); x = d; memcpy(x->data(), str, len + 1); // include null terminator @@ -1289,38 +1290,16 @@ void QByteArray::chop(int n) \sa isEmpty() */ -/*! \fn QByteArray::QByteArray(const char *str) - - Constructs a byte array initialized with the string \a str. - - QByteArray makes a deep copy of the string data. -*/ - -QByteArray::QByteArray(const char *str) -{ - if (!str) { - d = const_cast<Data *>(&shared_null.ba); - } else if (!*str) { - d = const_cast<Data *>(&shared_empty.ba); - } else { - int len = qstrlen(str); - d = static_cast<Data *>(malloc(sizeof(Data) + len + 1)); - Q_CHECK_PTR(d); - d->ref = 1; - d->size = len; - d->alloc = len; - d->capacityReserved = false; - d->offset = 0; - memcpy(d->data(), str, len+1); // include null terminator - } -} - /*! Constructs a byte array containing the first \a size bytes of array \a data. If \a data is 0, a null byte array is constructed. + If \a size is negative, \a data is assumed to point to a nul-terminated + string and its length is determined dynamically. The terminating + nul-character is not considered part of the byte array. + QByteArray makes a deep copy of the string data. \sa fromRawData() @@ -1330,18 +1309,22 @@ QByteArray::QByteArray(const char *data, int size) { if (!data) { d = const_cast<Data *>(&shared_null.ba); - } else if (size <= 0) { - d = const_cast<Data *>(&shared_empty.ba); } else { - d = static_cast<Data *>(malloc(sizeof(Data) + size + 1)); - Q_CHECK_PTR(d); - d->ref = 1; - d->size = size; - d->alloc = size; - d->capacityReserved = false; - d->offset = 0; - memcpy(d->data(), data, size); - d->data()[size] = '\0'; + if (size < 0) + size = strlen(data); + if (!size) { + d = const_cast<Data *>(&shared_empty.ba); + } else { + d = static_cast<Data *>(malloc(sizeof(Data) + size + 1)); + Q_CHECK_PTR(d); + d->ref.initializeOwned(); + d->size = size; + d->alloc = size; + d->capacityReserved = false; + d->offset = sizeof(QByteArrayData); + memcpy(d->data(), data, size); + d->data()[size] = '\0'; + } } } @@ -1359,11 +1342,11 @@ 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; - d->offset = 0; + d->offset = sizeof(QByteArrayData); memset(d->data(), ch, size); d->data()[size] = '\0'; } @@ -1379,11 +1362,11 @@ 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; - d->offset = 0; + d->offset = sizeof(QByteArrayData); d->data()[size] = '\0'; } @@ -1405,7 +1388,7 @@ void QByteArray::resize(int size) if (size < 0) size = 0; - if (d->offset && d->ref == 1 && size < d->size) { + if (IS_RAW_DATA(d) && !d->ref.isShared() && size < d->size) { d->size = size; return; } @@ -1426,15 +1409,15 @@ 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; - x->offset = 0; + x->offset = sizeof(QByteArrayData); x->data()[size] = '\0'; d = x; } else { - if (d->ref != 1 || size > int(d->alloc) + if (d->ref.isShared() || size > int(d->alloc) || (!d->capacityReserved && size < d->size && size < int(d->alloc) >> 1)) realloc(qAllocMore(size, sizeof(Data))); if (int(d->alloc) >= size) { @@ -1465,14 +1448,14 @@ QByteArray &QByteArray::fill(char ch, int size) void QByteArray::realloc(int alloc) { - if (d->ref != 1 || d->offset) { + if (d->ref.isShared() || IS_RAW_DATA(d)) { 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; - x->offset = 0; + x->offset = sizeof(QByteArrayData); ::memcpy(x->data(), d->data(), x->size); x->data()[x->size] = '\0'; if (!d->ref.deref()) @@ -1482,7 +1465,7 @@ void QByteArray::realloc(int alloc) Data *x = static_cast<Data *>(::realloc(d, sizeof(Data) + alloc + 1)); Q_CHECK_PTR(x); x->alloc = alloc; - x->offset = 0; + x->offset = sizeof(QByteArrayData); d = x; } } @@ -1504,7 +1487,7 @@ void QByteArray::expand(int i) QByteArray QByteArray::nulTerminated() const { // is this fromRawData? - if (!d->offset) + if (!IS_RAW_DATA(d)) return *this; // no, then we're sure we're zero terminated QByteArray copy(*this); @@ -1566,7 +1549,7 @@ QByteArray &QByteArray::prepend(const char *str) QByteArray &QByteArray::prepend(const char *str, int len) { if (str) { - if (d->ref != 1 || d->size + len > int(d->alloc)) + if (d->ref.isShared() || d->size + len > int(d->alloc)) realloc(qAllocMore(d->size + len, sizeof(Data))); memmove(d->data()+len, d->data(), d->size); memcpy(d->data(), str, len); @@ -1584,7 +1567,7 @@ QByteArray &QByteArray::prepend(const char *str, int len) QByteArray &QByteArray::prepend(char ch) { - if (d->ref != 1 || d->size + 1 > int(d->alloc)) + if (d->ref.isShared() || d->size + 1 > int(d->alloc)) realloc(qAllocMore(d->size + 1, sizeof(Data))); memmove(d->data()+1, d->data(), d->size); d->data()[0] = ch; @@ -1622,7 +1605,7 @@ QByteArray &QByteArray::append(const QByteArray &ba) if ((d == &shared_null.ba || d == &shared_empty.ba) && !IS_RAW_DATA(ba.d)) { *this = ba; } else if (ba.d != &shared_null.ba) { - if (d->ref != 1 || d->size + ba.d->size > int(d->alloc)) + if (d->ref.isShared() || d->size + ba.d->size > int(d->alloc)) realloc(qAllocMore(d->size + ba.d->size, sizeof(Data))); memcpy(d->data() + d->size, ba.d->data(), ba.d->size); d->size += ba.d->size; @@ -1656,7 +1639,7 @@ QByteArray& QByteArray::append(const char *str) { if (str) { int len = qstrlen(str); - if (d->ref != 1 || d->size + len > int(d->alloc)) + if (d->ref.isShared() || d->size + len > int(d->alloc)) realloc(qAllocMore(d->size + len, sizeof(Data))); memcpy(d->data() + d->size, str, len + 1); // include null terminator d->size += len; @@ -1681,7 +1664,7 @@ QByteArray &QByteArray::append(const char *str, int len) if (len < 0) len = qstrlen(str); if (str && len) { - if (d->ref != 1 || d->size + len > int(d->alloc)) + if (d->ref.isShared() || d->size + len > int(d->alloc)) realloc(qAllocMore(d->size + len, sizeof(Data))); memcpy(d->data() + d->size, str, len); // include null terminator d->size += len; @@ -1698,7 +1681,7 @@ QByteArray &QByteArray::append(const char *str, int len) QByteArray& QByteArray::append(char ch) { - if (d->ref != 1 || d->size + 1 > int(d->alloc)) + if (d->ref.isShared() || d->size + 1 > int(d->alloc)) realloc(qAllocMore(d->size + 1, sizeof(Data))); d->data()[d->size++] = ch; d->data()[d->size] = '\0'; @@ -3889,11 +3872,11 @@ 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; - x->offset = data - (x->d + sizeof(qptrdiff)); + x->offset = data - reinterpret_cast<char *>(x); } return QByteArray(x, 0, 0); } @@ -3914,14 +3897,14 @@ QByteArray QByteArray::fromRawData(const char *data, int size) */ QByteArray &QByteArray::setRawData(const char *data, uint size) { - if (d->ref != 1 || d->alloc) { + if (d->ref.isShared() || d->alloc) { *this = fromRawData(data, size); } else { if (data) { d->size = size; - d->offset = data - (d->d + sizeof(qptrdiff)); + d->offset = data - reinterpret_cast<char *>(d); } else { - d->offset = 0; + d->offset = sizeof(QByteArrayData); d->size = 0; *d->data() = 0; } diff --git a/src/corelib/tools/qbytearray.h b/src/corelib/tools/qbytearray.h index 09c43988fd..bd3a4a8444 100644 --- a/src/corelib/tools/qbytearray.h +++ b/src/corelib/tools/qbytearray.h @@ -124,32 +124,31 @@ struct QByteArrayData int size; uint alloc : 31; uint capacityReserved : 1; - union { - qptrdiff offset; // will always work as we add/subtract from a ushort ptr - char d[sizeof(qptrdiff)]; - }; - inline char *data() { return d + sizeof(qptrdiff) + offset; } - inline const char *data() const { return d + sizeof(qptrdiff) + offset; } + + qptrdiff offset; + + inline char *data() { return reinterpret_cast<char *>(this) + offset; } + inline const char *data() const { return reinterpret_cast<const char *>(this) + 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, sizeof(QByteArrayData) }, str }; \ + QStaticByteArrayDataPtr<Size> holder = { &qbytearray_literal }; \ return holder; }()) #elif defined(Q_CC_GNU) @@ -160,9 +159,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, sizeof(QByteArrayData) }, str }; \ + QStaticByteArrayDataPtr<Size> holder = { &qbytearray_literal }; \ holder; }) #endif @@ -180,8 +179,7 @@ private: public: inline QByteArray(); - QByteArray(const char *); - QByteArray(const char *, int size); + QByteArray(const char *, int size = -1); QByteArray(int size, char c); QByteArray(int size, Qt::Initialization); inline QByteArray(const QByteArray &); @@ -207,10 +205,8 @@ public: void squeeze(); #ifndef QT_NO_CAST_FROM_BYTEARRAY -#if QT_DEPRECATED_SINCE(5, 0) - QT_DEPRECATED operator const char *() const { return constData(); } - QT_DEPRECATED operator const void *() const { return constData(); } -#endif + operator const char *() const; + operator const void *() const; #endif char *data(); const char *data() const; @@ -379,16 +375,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.ba)) {} 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); @@ -417,6 +413,12 @@ inline char QByteArray::operator[](uint i) const inline bool QByteArray::isEmpty() const { return d->size == 0; } +#ifndef QT_NO_CAST_FROM_BYTEARRAY +inline QByteArray::operator const char *() const +{ return d->data(); } +inline QByteArray::operator const void *() const +{ return d->data(); } +#endif inline char *QByteArray::data() { detach(); return d->data(); } inline const char *QByteArray::data() const @@ -424,9 +426,9 @@ inline const char *QByteArray::data() const inline const char *QByteArray::constData() const { return d->data(); } inline void QByteArray::detach() -{ if (d->ref != 1 || d->offset) realloc(d->size); } +{ if (d->ref.isShared() || (d->offset != sizeof(QByteArrayData))) realloc(d->size); } inline bool QByteArray::isDetached() const -{ return d->ref == 1; } +{ return !d->ref.isShared(); } inline QByteArray::QByteArray(const QByteArray &a) : d(a.d) { d->ref.ref(); } @@ -435,7 +437,7 @@ inline int QByteArray::capacity() const inline void QByteArray::reserve(int asize) { - if (d->ref != 1 || asize > int(d->alloc)) + if (d->ref.isShared() || asize > int(d->alloc)) realloc(asize); if (!d->capacityReserved) { @@ -446,11 +448,12 @@ inline void QByteArray::reserve(int asize) inline void QByteArray::squeeze() { - if (d->ref > 1 || d->size < int(d->alloc)) + if (d->ref.isShared() || d->size < int(d->alloc)) realloc(d->size); if (d->capacityReserved) { - // cannot set unconditionally, since d could be the shared_null/shared_empty (which is const) + // cannot set unconditionally, since d could be shared_null or + // otherwise static. d->capacityReserved = false; } } diff --git a/src/corelib/tools/qhash.cpp b/src/corelib/tools/qhash.cpp index d5703e8b2a..897de77f2e 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/qhash.h b/src/corelib/tools/qhash.h index 9e4007c26e..91d015c394 100644 --- a/src/corelib/tools/qhash.h +++ b/src/corelib/tools/qhash.h @@ -288,8 +288,8 @@ public: void reserve(int size); inline void squeeze() { reserve(1); } - inline void detach() { if (d->ref != 1) detach_helper(); } - inline bool isDetached() const { return d->ref == 1; } + inline void detach() { if (d->ref.isShared()) detach_helper(); } + inline bool isDetached() const { return !d->ref.isShared(); } inline void setSharable(bool sharable) { if (!sharable) detach(); if (d != &QHashData::shared_null) d->sharable = sharable; } inline bool isSharedWith(const QHash<Key, T> &other) const { return d == other.d; } diff --git a/src/corelib/tools/qlinkedlist.cpp b/src/corelib/tools/qlinkedlist.cpp index b31ef3e5e9..6e66f804c0 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 2e6d05ac35..28f190c7fa 100644 --- a/src/corelib/tools/qlinkedlist.h +++ b/src/corelib/tools/qlinkedlist.h @@ -94,8 +94,8 @@ public: inline int size() const { return d->size; } inline void detach() - { if (d->ref != 1) detach_helper(); } - inline bool isDetached() const { return d->ref == 1; } + { if (d->ref.isShared()) detach_helper(); } + inline bool isDetached() const { return !d->ref.isShared(); } inline void setSharable(bool sharable) { if (!sharable) detach(); if (d != &QLinkedListData::shared_null) d->sharable = sharable; } inline bool isSharedWith(const QLinkedList<T> &other) const { return d == other.d; } @@ -241,8 +241,6 @@ private: template <typename T> inline QLinkedList<T>::~QLinkedList() { - if (!d) - return; if (!d->ref.deref()) free(d); } @@ -252,7 +250,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; @@ -265,6 +263,7 @@ void QLinkedList<T>::detach_helper() copy = copy->n; } QT_CATCH(...) { copy->n = x.e; + Q_ASSERT(!x.d->ref.deref()); // Don't trigger assert in free free(x.d); QT_RETHROW; } @@ -281,14 +280,13 @@ void QLinkedList<T>::free(QLinkedListData *x) { Node *y = reinterpret_cast<Node*>(x); Node *i = y->n; - if (x->ref == 0) { - while(i != y) { - Node *n = i; - i = i->n; - delete n; - } - delete x; + Q_ASSERT(x->ref.atomic.load() == 0); + while (i != y) { + Node *n = i; + i = i->n; + delete n; } + delete x; } template <typename T> diff --git a/src/corelib/tools/qlist.cpp b/src/corelib/tools/qlist.cpp index 263045a25d..9ee4c0a797 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, { 0 } }; static int grow(int size) { @@ -87,8 +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->sharable = true; + t->ref.initializeOwned(); t->alloc = alloc; // The space reservation algorithm's optimization is biased towards appending: // Something which looks like an append will put the data at the beginning, @@ -129,8 +128,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->sharable = true; + t->ref.initializeOwned(); t->alloc = alloc; if (!alloc) { t->begin = 0; @@ -146,7 +144,7 @@ QListData::Data *QListData::detach(int alloc) void QListData::realloc(int alloc) { - Q_ASSERT(d->ref == 1); + Q_ASSERT(!d->ref.isShared()); Data *x = static_cast<Data *>(::realloc(d, DataHeaderSize + alloc * sizeof(void *))); Q_CHECK_PTR(x); @@ -159,7 +157,7 @@ void QListData::realloc(int alloc) // ensures that enough space is available to append n elements void **QListData::append(int n) { - Q_ASSERT(d->ref == 1); + Q_ASSERT(!d->ref.isShared()); int e = d->end; if (e + n > d->alloc) { int b = d->begin; @@ -190,7 +188,7 @@ void **QListData::append(const QListData& l) void **QListData::prepend() { - Q_ASSERT(d->ref == 1); + Q_ASSERT(!d->ref.isShared()); if (d->begin == 0) { if (d->end >= d->alloc / 3) realloc(grow(d->alloc + 1)); @@ -208,7 +206,7 @@ void **QListData::prepend() void **QListData::insert(int i) { - Q_ASSERT(d->ref == 1); + Q_ASSERT(!d->ref.isShared()); if (i <= 0) return prepend(); int size = d->end - d->begin; @@ -247,7 +245,7 @@ void **QListData::insert(int i) void QListData::remove(int i) { - Q_ASSERT(d->ref == 1); + Q_ASSERT(!d->ref.isShared()); i += d->begin; if (i - d->begin < d->end - i) { if (int offset = i - d->begin) @@ -262,7 +260,7 @@ void QListData::remove(int i) void QListData::remove(int i, int n) { - Q_ASSERT(d->ref == 1); + Q_ASSERT(!d->ref.isShared()); i += d->begin; int middle = i + n/2; if (middle - d->begin < d->end - middle) { @@ -278,7 +276,7 @@ void QListData::remove(int i, int n) void QListData::move(int from, int to) { - Q_ASSERT(d->ref == 1); + Q_ASSERT(!d->ref.isShared()); if (from == to) return; @@ -318,7 +316,7 @@ void QListData::move(int from, int to) void **QListData::erase(void **xi) { - Q_ASSERT(d->ref == 1); + Q_ASSERT(!d->ref.isShared()); int i = xi - (d->array + d->begin); remove(i); return d->array + d->begin + i; diff --git a/src/corelib/tools/qlist.h b/src/corelib/tools/qlist.h index bf6933732c..bc3350f42b 100644 --- a/src/corelib/tools/qlist.h +++ b/src/corelib/tools/qlist.h @@ -71,7 +71,6 @@ struct Q_CORE_EXPORT QListData { struct Data { QtPrivate::RefCount ref; int alloc, begin, end; - uint sharable : 1; void *array[1]; }; enum { DataHeaderSize = sizeof(Data) - sizeof(void *) }; @@ -114,7 +113,7 @@ class QList public: inline QList() : d(const_cast<QListData::Data *>(&QListData::shared_null)) { } - inline QList(const QList<T> &l) : d(l.d) { d->ref.ref(); if (!d->sharable) detach_helper(); } + QList(const QList<T> &l); ~QList(); QList<T> &operator=(const QList<T> &l); #ifdef Q_COMPILER_RVALUE_REFS @@ -132,17 +131,25 @@ public: inline int size() const { return p.size(); } - inline void detach() { if (d->ref != 1) detach_helper(); } + inline void detach() { if (d->ref.isShared()) detach_helper(); } inline void detachShared() { // The "this->" qualification is needed for GCCE. - if (d->ref != 1 && this->d != &QListData::shared_null) + if (d->ref.isShared() && this->d != &QListData::shared_null) detach_helper(); } - inline bool isDetached() const { return d->ref == 1; } - inline void setSharable(bool sharable) { if (!sharable) detach(); if (d != &QListData::shared_null) d->sharable = sharable; } + inline bool isDetached() const { return !d->ref.isShared(); } + inline void setSharable(bool sharable) + { + if (sharable == d->ref.isSharable()) + return; + if (!sharable) + detach(); + if (d != &QListData::shared_null) + d->ref.setSharable(sharable); + } inline bool isSharedWith(const QList<T> &other) const { return d == other.d; } inline bool isEmpty() const { return p.isEmpty(); } @@ -419,13 +426,8 @@ template <typename T> Q_INLINE_TEMPLATE QList<T> &QList<T>::operator=(const QList<T> &l) { if (d != l.d) { - QListData::Data *o = l.d; - o->ref.ref(); - if (!d->ref.deref()) - dealloc(d); - d = o; - if (!d->sharable) - detach_helper(); + QList<T> tmp(l); + tmp.swap(*this); } return *this; } @@ -478,7 +480,7 @@ template <typename T> Q_OUTOFLINE_TEMPLATE void QList<T>::reserve(int alloc) { if (d->alloc < alloc) { - if (d->ref != 1) + if (d->ref.isShared()) detach_helper(alloc); else p.realloc(alloc); @@ -488,7 +490,7 @@ Q_OUTOFLINE_TEMPLATE void QList<T>::reserve(int alloc) template <typename T> Q_OUTOFLINE_TEMPLATE void QList<T>::append(const T &t) { - if (d->ref != 1) { + if (d->ref.isShared()) { Node *n = detach_helper_grow(INT_MAX, 1); QT_TRY { node_construct(n, t); @@ -522,7 +524,7 @@ Q_OUTOFLINE_TEMPLATE void QList<T>::append(const T &t) template <typename T> inline void QList<T>::prepend(const T &t) { - if (d->ref != 1) { + if (d->ref.isShared()) { Node *n = detach_helper_grow(0, 1); QT_TRY { node_construct(n, t); @@ -556,7 +558,7 @@ inline void QList<T>::prepend(const T &t) template <typename T> inline void QList<T>::insert(int i, const T &t) { - if (d->ref != 1) { + if (d->ref.isShared()) { Node *n = detach_helper_grow(i, 1); QT_TRY { node_construct(n, t); @@ -708,6 +710,28 @@ Q_OUTOFLINE_TEMPLATE void QList<T>::detach_helper() } template <typename T> +Q_OUTOFLINE_TEMPLATE QList<T>::QList(const QList<T> &l) + : d(l.d) +{ + if (!d->ref.ref()) { + p.detach(d->alloc); + + struct Cleanup + { + Cleanup(QListData::Data *d) : d_(d) {} + ~Cleanup() { if (d_) qFree(d_); } + + QListData::Data *d_; + } tryCatch(d); + + node_copy(reinterpret_cast<Node *>(p.begin()), + reinterpret_cast<Node *>(p.end()), + reinterpret_cast<Node *>(l.p.begin())); + tryCatch.d_ = 0; + } +} + +template <typename T> Q_OUTOFLINE_TEMPLATE QList<T>::~QList() { if (!d->ref.deref()) @@ -802,7 +826,7 @@ Q_OUTOFLINE_TEMPLATE QList<T> &QList<T>::operator+=(const QList<T> &l) if (isEmpty()) { *this = l; } else { - Node *n = (d->ref != 1) + Node *n = (d->ref.isShared()) ? detach_helper_grow(INT_MAX, l.size()) : reinterpret_cast<Node *>(p.append(l.p)); QT_TRY { diff --git a/src/corelib/tools/qmap.cpp b/src/corelib/tools/qmap.cpp index 103d074941..c922d7aab0 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/qmap.h b/src/corelib/tools/qmap.h index 515b60bea6..dc358a8106 100644 --- a/src/corelib/tools/qmap.h +++ b/src/corelib/tools/qmap.h @@ -179,7 +179,7 @@ public: inline QMap() : d(const_cast<QMapData *>(&QMapData::shared_null)) { } inline QMap(const QMap<Key, T> &other) : d(other.d) { d->ref.ref(); if (!d->sharable) detach(); } - inline ~QMap() { if (!d) return; if (!d->ref.deref()) freeData(d); } + inline ~QMap() { if (!d->ref.deref()) freeData(d); } QMap<Key, T> &operator=(const QMap<Key, T> &other); #ifdef Q_COMPILER_RVALUE_REFS @@ -199,8 +199,8 @@ public: inline bool isEmpty() const { return d->size == 0; } - inline void detach() { if (d->ref != 1) detach_helper(); } - inline bool isDetached() const { return d->ref == 1; } + inline void detach() { if (d->ref.isShared()) detach_helper(); } + inline bool isDetached() const { return !d->ref.isShared(); } inline void setSharable(bool sharable) { if (!sharable) detach(); if (d != &QMapData::shared_null) d->sharable = sharable; } inline bool isSharedWith(const QMap<Key, T> &other) const { return d == other.d; } inline void setInsertInOrder(bool ordered) { if (ordered) detach(); if (d != &QMapData::shared_null) d->insertInOrder = ordered; } diff --git a/src/corelib/tools/qrefcount.h b/src/corelib/tools/qrefcount.h index bc673214fa..16b1d339b3 100644 --- a/src/corelib/tools/qrefcount.h +++ b/src/corelib/tools/qrefcount.h @@ -55,37 +55,61 @@ 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(); } - inline bool operator==(int value) const - { return atomic.load() == value; } - inline bool operator!=(int value) const - { return atomic.load() != value; } - inline bool operator!() const - { 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; } + 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); + } + + 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/qregularexpression.h b/src/corelib/tools/qregularexpression.h index 3ca83c9e27..57cb29035b 100644 --- a/src/corelib/tools/qregularexpression.h +++ b/src/corelib/tools/qregularexpression.h @@ -239,8 +239,6 @@ Q_DECLARE_TYPEINFO(QRegularExpressionMatchIterator, Q_MOVABLE_TYPE); QT_END_NAMESPACE -Q_DECLARE_METATYPE(QRegularExpression) - QT_END_HEADER #endif // QT_NO_REGEXP diff --git a/src/corelib/tools/qsharedpointer.cpp b/src/corelib/tools/qsharedpointer.cpp index 77c3d1e2cb..58c62f0a3e 100644 --- a/src/corelib/tools/qsharedpointer.cpp +++ b/src/corelib/tools/qsharedpointer.cpp @@ -1380,47 +1380,14 @@ Q_GLOBAL_STATIC(KnownPointers, knownPointers) QT_BEGIN_NAMESPACE namespace QtSharedPointer { - Q_CORE_EXPORT void internalSafetyCheckAdd(const volatile void *); - Q_CORE_EXPORT void internalSafetyCheckRemove(const volatile void *); Q_AUTOTEST_EXPORT void internalSafetyCheckCleanCheck(); } /*! \internal */ -void QtSharedPointer::internalSafetyCheckAdd(const volatile void *) +void QtSharedPointer::internalSafetyCheckAdd(const void *d_ptr, const volatile void *ptr) { - // Qt 4.5 compatibility - // this function is broken by design, so it was replaced with internalSafetyCheckAdd2 - // - // it's broken because we tracked the pointers added and - // removed from QSharedPointer, converted to void*. - // That is, this is supposed to track the "top-of-object" pointer in - // case of multiple inheritance. - // - // However, it doesn't work well in some compilers: - // if you create an object with a class of type A and the last reference - // is dropped of type B, then the value passed to internalSafetyCheckRemove could - // be different than was added. That would leave dangling addresses. - // - // So instead, we track the pointer by the d-pointer instead. -} - -/*! - \internal -*/ -void QtSharedPointer::internalSafetyCheckRemove(const volatile void *) -{ - // Qt 4.5 compatibility - // see comments above -} - -/*! - \internal -*/ -void QtSharedPointer::internalSafetyCheckAdd2(const void *d_ptr, const volatile void *ptr) -{ - // see comments above for the rationale for this function KnownPointers *const kp = knownPointers(); if (!kp) return; // end-game: the application is being destroyed already @@ -1453,7 +1420,7 @@ void QtSharedPointer::internalSafetyCheckAdd2(const void *d_ptr, const volatile /*! \internal */ -void QtSharedPointer::internalSafetyCheckRemove2(const void *d_ptr) +void QtSharedPointer::internalSafetyCheckRemove(const void *d_ptr) { KnownPointers *const kp = knownPointers(); if (!kp) diff --git a/src/corelib/tools/qsharedpointer_impl.h b/src/corelib/tools/qsharedpointer_impl.h index 58010dd8d9..81ce17889e 100644 --- a/src/corelib/tools/qsharedpointer_impl.h +++ b/src/corelib/tools/qsharedpointer_impl.h @@ -105,8 +105,8 @@ namespace QtSharedPointer { template <class X, class Y> QSharedPointer<X> copyAndSetPointer(X * ptr, const QSharedPointer<Y> &src); // used in debug mode to verify the reuse of pointers - Q_CORE_EXPORT void internalSafetyCheckAdd2(const void *, const volatile void *); - Q_CORE_EXPORT void internalSafetyCheckRemove2(const void *); + Q_CORE_EXPORT void internalSafetyCheckAdd(const void *, const volatile void *); + Q_CORE_EXPORT void internalSafetyCheckRemove(const void *); template <class T, typename Klass, typename RetVal> inline void executeDeleter(T *t, RetVal (Klass:: *memberDeleter)()) @@ -247,7 +247,7 @@ namespace QtSharedPointer { } static void safetyCheckDeleter(ExternalRefCountData *self) { - internalSafetyCheckRemove2(self); + internalSafetyCheckRemove(self); deleter(self); } @@ -290,7 +290,7 @@ namespace QtSharedPointer { } static void safetyCheckDeleter(ExternalRefCountData *self) { - internalSafetyCheckRemove2(self); + internalSafetyCheckRemove(self); deleter(self); } @@ -374,7 +374,7 @@ namespace QtSharedPointer { Basic<T>::internalConstruct(ptr); if (ptr) d->setQObjectShared(ptr, true); #ifdef QT_SHAREDPOINTER_TRACK_POINTERS - if (ptr) internalSafetyCheckAdd2(d, ptr); + if (ptr) internalSafetyCheckAdd(d, ptr); #endif } diff --git a/src/corelib/tools/qstring.cpp b/src/corelib/tools/qstring.cpp index 083abcbaad..878403fe75 100644 --- a/src/corelib/tools/qstring.cpp +++ b/src/corelib/tools/qstring.cpp @@ -41,6 +41,7 @@ #include "qstringlist.h" #include "qregexp.h" +#include "qregularexpression.h" #include "qunicodetables_p.h" #ifndef QT_NO_TEXTCODEC #include <qtextcodec.h> @@ -94,6 +95,8 @@ #define ULLONG_MAX quint64_C(18446744073709551615) #endif +#define IS_RAW_DATA(d) ((d)->offset != sizeof(QStringData)) + QT_BEGIN_NAMESPACE #ifdef QT_USE_ICU @@ -793,8 +796,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, sizeof(QStringData) }, { 0 } }; +const QStaticStringData<1> QString::shared_empty = { { Q_REFCOUNT_INITIALIZE_STATIC, 0, 0, false, sizeof(QStringData) }, { 0 } }; int QString::grow(int size) { @@ -1015,63 +1018,44 @@ int QString::toUcs4_helper(const ushort *uc, int length, uint *out) Constructs a string initialized with the first \a size characters of the QChar array \a unicode. + If \a unicode is 0, a null string is constructed. + + If \a size is negative, \a unicode is assumed to point to a nul-terminated + array and its length is determined dynamically. The terminating + nul-character is not considered part of the string. + QString makes a deep copy of the string data. The unicode data is copied as is and the Byte Order Mark is preserved if present. + + \sa fromRawData() */ QString::QString(const QChar *unicode, int size) { if (!unicode) { d = const_cast<Data *>(&shared_null.str); - } else if (size <= 0) { - d = const_cast<Data *>(&shared_empty.str); } else { - d = (Data*) ::malloc(sizeof(Data)+(size+1)*sizeof(QChar)); - Q_CHECK_PTR(d); - d->ref = 1; - d->size = size; - d->alloc = (uint) size; - d->capacityReserved = false; - d->offset = 0; - memcpy(d->data(), unicode, size * sizeof(QChar)); - d->data()[size] = '\0'; + if (size < 0) { + size = 0; + while (unicode[size] != 0) + ++size; + } + if (!size) { + d = const_cast<Data *>(&shared_empty.str); + } else { + d = (Data*) ::malloc(sizeof(Data)+(size+1)*sizeof(QChar)); + Q_CHECK_PTR(d); + d->ref.initializeOwned(); + d->size = size; + d->alloc = (uint) size; + d->capacityReserved = false; + d->offset = sizeof(QStringData); + memcpy(d->data(), unicode, size * sizeof(QChar)); + d->data()[size] = '\0'; + } } } /*! - \since 4.7 - - Constructs a string initialized with the characters of the QChar array - \a unicode, which must be terminated with a 0. - - QString makes a deep copy of the string data. The unicode data is copied as - is and the Byte Order Mark is preserved if present. -*/ -QString::QString(const QChar *unicode) -{ - if (!unicode) { - d = const_cast<Data *>(&shared_null.str); - } else { - int size = 0; - while (unicode[size] != 0) - ++size; - if (!size) { - d = const_cast<Data *>(&shared_empty.str); - } else { - d = (Data*) ::malloc(sizeof(Data)+(size+1)*sizeof(QChar)); - Q_CHECK_PTR(d); - d->ref = 1; - d->size = size; - d->alloc = (uint) size; - d->capacityReserved = false; - d->offset = 0; - memcpy(d->data(), unicode, size * sizeof(QChar)); - d->data()[size] = '\0'; - } - } -} - - -/*! Constructs a string of the given \a size with every character set to \a ch. @@ -1084,11 +1068,11 @@ 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; - d->offset = 0; + d->offset = sizeof(QStringData); d->data()[size] = '\0'; ushort *i = d->data() + size; ushort *b = d->data(); @@ -1108,11 +1092,11 @@ 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; - d->offset = 0; + d->offset = sizeof(QStringData); d->data()[size] = '\0'; } @@ -1130,11 +1114,11 @@ 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; - d->offset = 0; + d->offset = sizeof(QStringData); d->data()[0] = ch.unicode(); d->data()[1] = '\0'; } @@ -1232,7 +1216,7 @@ void QString::resize(int size) if (size < 0) size = 0; - if (d->offset && d->ref == 1 && size < d->size) { + if (IS_RAW_DATA(d) && !d->ref.isShared() && size < d->size) { d->size = size; return; } @@ -1243,7 +1227,7 @@ void QString::resize(int size) QString::free(d); d = x; } else { - if (d->ref != 1 || size > int(d->alloc) || + if (d->ref.isShared() || size > int(d->alloc) || (!d->capacityReserved && size < d->size && size < int(d->alloc) >> 1)) realloc(grow(size)); if (int(d->alloc) >= size) { @@ -1306,14 +1290,14 @@ void QString::resize(int size) // ### Qt 5: rename reallocData() to avoid confusion. 197625 void QString::realloc(int alloc) { - if (d->ref != 1 || d->offset) { + if (d->ref.isShared() || IS_RAW_DATA(d)) { 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; - x->offset =0; + x->offset = sizeof(QStringData); ::memcpy(x->data(), d->data(), x->size * sizeof(QChar)); x->data()[x->size] = 0; if (!d->ref.deref()) @@ -1324,7 +1308,7 @@ void QString::realloc(int alloc) Q_CHECK_PTR(p); d = p; d->alloc = alloc; - d->offset = 0; + d->offset = sizeof(QStringData); } } @@ -1536,7 +1520,7 @@ QString &QString::append(const QString &str) if (d == &shared_null.str) { operator=(str); } else { - if (d->ref != 1 || d->size + str.d->size > int(d->alloc)) + if (d->ref.isShared() || d->size + str.d->size > int(d->alloc)) realloc(grow(d->size + str.d->size)); memcpy(d->data() + d->size, str.d->data(), str.d->size * sizeof(QChar)); d->size += str.d->size; @@ -1556,7 +1540,7 @@ QString &QString::append(const QLatin1String &str) const uchar *s = (const uchar *)str.latin1(); if (s) { int len = str.size(); - if (d->ref != 1 || d->size + len > int(d->alloc)) + if (d->ref.isShared() || d->size + len > int(d->alloc)) realloc(grow(d->size + len)); ushort *i = d->data() + d->size; while ((*i++ = *s++)) @@ -1599,7 +1583,7 @@ QString &QString::append(const QLatin1String &str) */ QString &QString::append(QChar ch) { - if (d->ref != 1 || d->size + 1 > int(d->alloc)) + if (d->ref.isShared() || d->size + 1 > int(d->alloc)) realloc(grow(d->size + 1)); d->data()[d->size++] = ch.unicode(); d->data()[d->size] = '\0'; @@ -1758,6 +1742,18 @@ QString &QString::remove(QChar ch, Qt::CaseSensitivity cs) */ /*! + \fn QString &QString::remove(const QRegularExpression &re) + \since 5.0 + + Removes every occurrence of the regular expression \a re in the + string, and returns a reference to the string. For example: + + \snippet doc/src/snippets/qstring/main.cpp 96 + + \sa indexOf(), lastIndexOf(), replace() +*/ + +/*! \fn QString &QString::replace(int position, int n, const QString &after) Replaces \a n characters beginning at index \a position with @@ -2940,6 +2936,138 @@ QString& QString::replace(const QRegExp &rx, const QString &after) } #endif +#ifndef QT_NO_REGEXP +#ifndef QT_BOOTSTRAPPED +/*! + \overload replace() + \since 5.0 + + Replaces every occurrence of the regular expression \a re in the + string with \a after. Returns a reference to the string. For + example: + + \snippet doc/src/snippets/qstring/main.cpp 87 + + For regular expressions containing capturing groups, + occurrences of \bold{\\1}, \bold{\\2}, ..., in \a after are replaced + with the string captured by the corresponding capturing group. + + \snippet doc/src/snippets/qstring/main.cpp 88 + + \sa indexOf(), lastIndexOf(), remove(), QRegularExpression, QRegularExpressionMatch +*/ +QString &QString::replace(const QRegularExpression &re, const QString &after) +{ + if (!re.isValid()) { + qWarning("QString::replace: invalid QRegularExpresssion object"); + return *this; + } + + const QString copy(*this); + QRegularExpressionMatchIterator iterator = re.globalMatch(copy); + if (!iterator.hasNext()) // no matches at all + return *this; + + realloc(); + + int numCaptures = re.captureCount(); + + // 1. build the backreferences vector, holding where the backreferences + // are in the replacement string + QVector<QStringCapture> backReferences; + const int al = after.length(); + const QChar *ac = after.unicode(); + + for (int i = 0; i < al - 1; i++) { + if (ac[i] == QLatin1Char('\\')) { + int no = ac[i + 1].digitValue(); + if (no > 0 && no <= numCaptures) { + QStringCapture backReference; + backReference.pos = i; + backReference.len = 2; + + if (i < al - 2) { + int secondDigit = ac[i + 2].digitValue(); + if (secondDigit != -1 && ((no * 10) + secondDigit) <= numCaptures) { + no = (no * 10) + secondDigit; + ++backReference.len; + } + } + + backReference.no = no; + backReferences.append(backReference); + } + } + } + + // 2. iterate on the matches. For every match, copy in chunks + // - the part before the match + // - the after string, with the proper replacements for the backreferences + + int newLength = 0; // length of the new string, with all the replacements + int lastEnd = 0; + QVector<QStringRef> chunks; + while (iterator.hasNext()) { + QRegularExpressionMatch match = iterator.next(); + int len; + // add the part before the match + len = match.capturedStart() - lastEnd; + if (len > 0) { + chunks << copy.midRef(lastEnd, len); + newLength += len; + } + + lastEnd = 0; + // add the after string, with replacements for the backreferences + foreach (const QStringCapture &backReference, backReferences) { + // part of "after" before the backreference + len = backReference.pos - lastEnd; + if (len > 0) { + chunks << after.midRef(lastEnd, len); + newLength += len; + } + + // backreference itself + len = match.capturedLength(backReference.no); + if (len > 0) { + chunks << copy.midRef(match.capturedStart(backReference.no), len); + newLength += len; + } + + lastEnd = backReference.pos + backReference.len; + } + + // add the last part of the after string + len = after.length() - lastEnd; + if (len > 0) { + chunks << after.midRef(lastEnd, len); + newLength += len; + } + + lastEnd = match.capturedEnd(); + } + + // 3. trailing string after the last match + if (copy.length() > lastEnd) { + chunks << copy.midRef(lastEnd); + newLength += copy.length() - lastEnd; + } + + // 4. assemble the chunks together + resize(newLength); + int i = 0; + QChar *uc = data(); + foreach (const QStringRef &chunk, chunks) { + int len = chunk.length(); + memcpy(uc + i, chunk.unicode(), len * sizeof(QChar)); + i += len; + } + + return *this; +} +#endif // QT_BOOTSTRAPPED +#endif // QT_NO_REGEXP + /*! Returns the number of (potentially overlapping) occurrences of the string \a str in this string. @@ -3139,6 +3267,118 @@ int QString::count(const QRegExp& rx) const } #endif // QT_NO_REGEXP +#ifndef QT_NO_REGEXP +#ifndef QT_BOOTSTRAPPED +/*! + \overload indexOf() + \since 5.0 + + Returns the index position of the first match of the regular + expression \a re in the string, searching forward from index + position \a from. Returns -1 if \a re didn't match anywhere. + + Example: + + \snippet doc/src/snippets/qstring/main.cpp 93 +*/ +int QString::indexOf(const QRegularExpression& re, int from) const +{ + if (!re.isValid()) { + qWarning("QString::indexOf: invalid QRegularExpresssion object"); + return -1; + } + + QRegularExpressionMatch match = re.match(*this, from); + if (match.hasMatch()) + return match.capturedStart(); + + return -1; +} + +/*! + \overload lastIndexOf() + \since 5.0 + + Returns the index position of the last match of the regular + expression \a re in the string, which starts before the index + position \a from. Returns -1 if \a re didn't match anywhere. + + Example: + + \snippet doc/src/snippets/qstring/main.cpp 94 +*/ +int QString::lastIndexOf(const QRegularExpression &re, int from) const +{ + if (!re.isValid()) { + qWarning("QString::lastIndexOf: invalid QRegularExpresssion object"); + return -1; + } + + int endpos = (from < 0) ? (size() + from + 1) : (from + 1); + + QRegularExpressionMatchIterator iterator = re.globalMatch(*this); + int lastIndex = -1; + while (iterator.hasNext()) { + QRegularExpressionMatch match = iterator.next(); + int start = match.capturedStart(); + if (start < endpos) + lastIndex = start; + else + break; + } + + return lastIndex; +} + +/*! \overload contains() + \since 5.0 + + Returns true if the regular expression \a re matches somewhere in + this string; otherwise returns false. +*/ +bool QString::contains(const QRegularExpression &re) const +{ + if (!re.isValid()) { + qWarning("QString::contains: invalid QRegularExpresssion object"); + return false; + } + QRegularExpressionMatch match = re.match(*this); + return match.hasMatch(); +} + +/*! + \overload count() + \since 5.0 + + Returns the number of times the regular expression \a re matches + in the string. + + This function counts overlapping matches, so in the example + below, there are four instances of "ana" or "ama": + + \snippet doc/src/snippets/qstring/main.cpp 95 +*/ +int QString::count(const QRegularExpression &re) const +{ + if (!re.isValid()) { + qWarning("QString::count: invalid QRegularExpresssion object"); + return 0; + } + int count = 0; + int index = -1; + int len = length(); + while (index < len - 1) { + QRegularExpressionMatch match = re.match(*this, index + 1); + if (!match.hasMatch()) + break; + index = match.capturedStart(); + count++; + } + return count; +} +#endif // QT_BOOTSTRAPPED +#endif // QT_NO_REGEXP + /*! \fn int QString::count() const \overload count() @@ -3266,6 +3506,49 @@ public: QString string; }; +static QString extractSections(const QList<qt_section_chunk> §ions, + int start, + int end, + QString::SectionFlags flags) +{ + if (start < 0) + start += sections.count(); + if (end < 0) + end += sections.count(); + + QString ret; + int x = 0; + int first_i = start, last_i = end; + for (int i = 0; x <= end && i < sections.size(); ++i) { + const qt_section_chunk §ion = sections.at(i); + const bool empty = (section.length == section.string.length()); + if (x >= start) { + if (x == start) + first_i = i; + if (x == end) + last_i = i; + if (x != start) + ret += section.string; + else + ret += section.string.mid(section.length); + } + if (!empty || !(flags & QString::SectionSkipEmpty)) + x++; + } + + if ((flags & QString::SectionIncludeLeadingSep) && first_i < sections.size()) { + const qt_section_chunk §ion = sections.at(first_i); + ret.prepend(section.string.left(section.length)); + } + + if ((flags & QString::SectionIncludeTrailingSep) && last_i+1 <= sections.size()-1) { + const qt_section_chunk §ion = sections.at(last_i+1); + ret += section.string.left(section.length); + } + + return ret; +} + /*! \overload section() @@ -3299,41 +3582,57 @@ QString QString::section(const QRegExp ®, int start, int end, SectionFlags fl } sections.append(qt_section_chunk(last_len, QString(uc + last_m, n - last_m))); - if(start < 0) - start += sections.count(); - if(end < 0) - end += sections.count(); + return extractSections(sections, start, end, flags); +} +#endif - QString ret; - int x = 0; - int first_i = start, last_i = end; - for (int i = 0; x <= end && i < sections.size(); ++i) { - const qt_section_chunk §ion = sections.at(i); - const bool empty = (section.length == section.string.length()); - if (x >= start) { - if(x == start) - first_i = i; - if(x == end) - last_i = i; - if(x != start) - ret += section.string; - else - ret += section.string.mid(section.length); - } - if (!empty || !(flags & SectionSkipEmpty)) - x++; - } - if((flags & SectionIncludeLeadingSep) && first_i < sections.size()) { - const qt_section_chunk §ion = sections.at(first_i); - ret.prepend(section.string.left(section.length)); +#ifndef QT_NO_REGEXP +#ifndef QT_BOOTSTRAPPED +/*! + \overload section() + \since 5.0 + + This string is treated as a sequence of fields separated by the + regular expression, \a re. + + \snippet doc/src/snippets/qstring/main.cpp 89 + + \warning Using this QRegularExpression version is much more expensive than + the overloaded string and character versions. + + \sa split() simplified() +*/ +QString QString::section(const QRegularExpression &re, int start, int end, SectionFlags flags) const +{ + if (!re.isValid()) { + qWarning("QString::section: invalid QRegularExpression object"); + return QString(); } - if((flags & SectionIncludeTrailingSep) && last_i+1 <= sections.size()-1) { - const qt_section_chunk §ion = sections.at(last_i+1); - ret += section.string.left(section.length); + + const QChar *uc = unicode(); + if (!uc) + return QString(); + + QRegularExpression sep(re); + if (flags & SectionCaseInsensitiveSeps) + sep.setPatternOptions(sep.patternOptions() | QRegularExpression::CaseInsensitiveOption); + + QList<qt_section_chunk> sections; + int n = length(), m = 0, last_m = 0, last_len = 0; + QRegularExpressionMatchIterator iterator = sep.globalMatch(*this); + while (iterator.hasNext()) { + QRegularExpressionMatch match = iterator.next(); + m = match.capturedStart(); + sections.append(qt_section_chunk(last_len, QString(uc + last_m, m - last_m))); + last_m = m; + last_len = match.capturedLength(); } - return ret; + sections.append(qt_section_chunk(last_len, QString(uc + last_m, n - last_m))); + + return extractSections(sections, start, end, flags); } -#endif +#endif // QT_BOOTSTRAPPED +#endif // QT_NO_REGEXP /*! Returns a substring that contains the \a n leftmost characters @@ -3390,15 +3689,17 @@ QString QString::right(int n) const QString QString::mid(int position, int n) const { - if (d == &shared_null.str || position > d->size) + if (position > d->size) return QString(); - if (n < 0) - n = d->size - position; if (position < 0) { + if (n < 0 || n + position >= d->size) + return *this; + if (n + position <= 0) + return QString(); + n += position; position = 0; - } - if (n + position > d->size) + } else if (n < 0 || n > d->size - position) n = d->size - position; if (position == 0 && n == d->size) return *this; @@ -3753,11 +4054,11 @@ 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; - d->offset = 0; + d->offset = sizeof(QStringData); d->data()[size] = '\0'; ushort *dst = d->data(); /* SIMD: @@ -4765,7 +5066,7 @@ int QString::localeAwareCompare_helper(const QChar *data1, int length1, const ushort *QString::utf16() const { - if (d->offset) + if (IS_RAW_DATA(d)) const_cast<QString*>(this)->realloc(); // ensure '\\0'-termination for ::fromRawData strings return d->data(); } @@ -6092,6 +6393,62 @@ QStringList QString::split(const QRegExp &rx, SplitBehavior behavior) const } #endif +#ifndef QT_NO_REGEXP +#ifndef QT_BOOTSTRAPPED +/*! + \overload + \since 5.0 + + Splits the string into substrings wherever the regular expression + \a re matches, and returns the list of those strings. If \a re + does not match anywhere in the string, split() returns a + single-element list containing this string. + + Here's an example where we extract the words in a sentence + using one or more whitespace characters as the separator: + + \snippet doc/src/snippets/qstring/main.cpp 90 + + Here's a similar example, but this time we use any sequence of + non-word characters as the separator: + + \snippet doc/src/snippets/qstring/main.cpp 91 + + Here's a third example where we use a zero-length assertion, + \bold{\\b} (word boundary), to split the string into an + alternating sequence of non-word and word tokens: + + \snippet doc/src/snippets/qstring/main.cpp 92 + + \sa QStringList::join(), section() +*/ +QStringList QString::split(const QRegularExpression &re, SplitBehavior behavior) const +{ + QStringList list; + if (!re.isValid()) { + qWarning("QString::split: invalid QRegularExpression object"); + return list; + } + + int start = 0; + int end = 0; + QRegularExpressionMatchIterator iterator = re.globalMatch(*this); + while (iterator.hasNext()) { + QRegularExpressionMatch match = iterator.next(); + end = match.capturedStart(); + if (start != end || behavior == KeepEmptyParts) + list.append(mid(start, end - start)); + start = match.capturedEnd(); + } + + if (start != size() || behavior == KeepEmptyParts) + list.append(mid(start)); + + return list; +} +#endif // QT_BOOTSTRAPPED +#endif // QT_NO_REGEXP + /*! \enum QString::NormalizationForm @@ -7067,11 +7424,11 @@ 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; - x->offset = (const ushort *)unicode - (x->d + sizeof(qptrdiff)/sizeof(ushort)); + x->offset = reinterpret_cast<const char *>(unicode) - reinterpret_cast<char *>(x); } return QString(x, 0); } @@ -7092,14 +7449,14 @@ QString QString::fromRawData(const QChar *unicode, int size) */ QString &QString::setRawData(const QChar *unicode, int size) { - if (d->ref != 1 || d->alloc) { + if (d->ref.isShared() || d->alloc) { *this = fromRawData(unicode, size); } else { if (unicode) { d->size = size; - d->offset = (const ushort *)unicode - (d->d + sizeof(qptrdiff)/sizeof(ushort)); + d->offset = reinterpret_cast<const char *>(unicode) - reinterpret_cast<char *>(d); } else { - d->offset = 0; + d->offset = sizeof(QStringData); d->size = 0; } } @@ -7179,6 +7536,17 @@ QString &QString::setRawData(const QChar *unicode, int size) \sa latin1() */ +/*! \fn QLatin1String::QLatin1String(const QByteArray &str) + + Constructs a QLatin1String object that stores \a str. + + The string data is \e not copied. The caller must be able to + guarantee that \a str will not be deleted or modified as long as + the QLatin1String object exists. + + \sa latin1() +*/ + /*! \fn const char *QLatin1String::latin1() const Returns the Latin-1 string stored in this object. @@ -8059,15 +8427,17 @@ QStringRef QString::rightRef(int n) const QStringRef QString::midRef(int position, int n) const { - if (d == &shared_null.str || position > d->size) + if (position > d->size) return QStringRef(); - if (n < 0) - n = d->size - position; if (position < 0) { + if (n < 0 || n + position >= d->size) + return QStringRef(this, 0, d->size); + if (n + position <= 0) + return QStringRef(); + n += position; position = 0; - } - if (n + position > d->size) + } else if (n < 0 || n > d->size - position) n = d->size - position; return QStringRef(this, position, n); } diff --git a/src/corelib/tools/qstring.h b/src/corelib/tools/qstring.h index 4d02fbe66d..de5cd2cfa9 100644 --- a/src/corelib/tools/qstring.h +++ b/src/corelib/tools/qstring.h @@ -64,6 +64,7 @@ QT_BEGIN_NAMESPACE class QCharRef; class QRegExp; +class QRegularExpression; class QStringList; class QTextCodec; class QLatin1String; @@ -75,25 +76,24 @@ struct QStringData { int size; uint alloc : 31; uint capacityReserved : 1; - union { - qptrdiff offset; // will always work as we add/subtract from a ushort ptr - ushort d[sizeof(qptrdiff)/sizeof(ushort)]; - }; - inline ushort *data() { return d + sizeof(qptrdiff)/sizeof(ushort) + offset; } - inline const ushort *data() const { return d + sizeof(qptrdiff)/sizeof(ushort) + offset; } + + qptrdiff offset; + + inline ushort *data() { return reinterpret_cast<ushort *>(reinterpret_cast<char *>(this) + offset); } + inline const ushort *data() const { return reinterpret_cast<const ushort *>(reinterpret_cast<const char *>(this) + 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 @@ -102,10 +102,10 @@ template<int N> struct QConstStringData || (defined(__SIZEOF_WCHAR_T__) && __SIZEOF_WCHAR_T__ == 2) \ || (!defined(__SIZEOF_WCHAR_T__) && 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) @@ -115,21 +115,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, sizeof(QStringData) }, QT_UNICODE_LITERAL(str) }; \ + QStaticStringDataPtr<Size> holder = { &qstring_literal }; \ return holder; }()) # elif defined(Q_CC_GNU) @@ -140,9 +140,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, sizeof(QStringData) }, QT_UNICODE_LITERAL(str) }; \ + QStaticStringDataPtr<Size> holder = { &qstring_literal }; \ holder; }) # endif #endif @@ -160,8 +160,7 @@ public: typedef QStringData Data; inline QString(); - QString(const QChar *unicode, int size); // Qt5: don't cap size < 0 - explicit QString(const QChar *unicode); // Qt5: merge with the above + explicit QString(const QChar *unicode, int size = -1); QString(QChar c); QString(int size, QChar c); inline QString(const QLatin1String &latin1); @@ -285,6 +284,13 @@ public: inline bool contains(QRegExp &rx) const { return indexOf(rx) != -1; } #endif +#ifndef QT_NO_REGEXP + int indexOf(const QRegularExpression &re, int from = 0) const; + int lastIndexOf(const QRegularExpression &re, int from = -1) const; + bool contains(const QRegularExpression &re) const; + int count(const QRegularExpression &re) const; +#endif + enum SectionFlag { SectionDefault = 0x00, SectionSkipEmpty = 0x01, @@ -299,7 +305,9 @@ public: #ifndef QT_NO_REGEXP QString section(const QRegExp ®, int start, int end = -1, SectionFlags flags = SectionDefault) const; #endif - +#ifndef QT_NO_REGEXP + QString section(const QRegularExpression &re, int start, int end = -1, SectionFlags flags = SectionDefault) const; +#endif QString left(int n) const Q_REQUIRED_RESULT; QString right(int n) const Q_REQUIRED_RESULT; QString mid(int position, int n = -1) const Q_REQUIRED_RESULT; @@ -340,7 +348,7 @@ public: inline QString &prepend(const QLatin1String &s) { return insert(0, s); } inline QString &operator+=(QChar c) { - if (d->ref != 1 || d->size + 1 > int(d->alloc)) + if (d->ref.isShared() || d->size + 1 > int(d->alloc)) realloc(grow(d->size + 1)); d->data()[d->size++] = c.unicode(); d->data()[d->size] = '\0'; @@ -372,6 +380,11 @@ public: inline QString &remove(const QRegExp &rx) { return replace(rx, QString()); } #endif +#ifndef QT_NO_REGEXP + QString &replace(const QRegularExpression &re, const QString &after); + inline QString &remove(const QRegularExpression &re) + { return replace(re, QString()); } +#endif enum SplitBehavior { KeepEmptyParts, SkipEmptyParts }; @@ -382,7 +395,9 @@ public: #ifndef QT_NO_REGEXP QStringList split(const QRegExp &sep, SplitBehavior behavior = KeepEmptyParts) const Q_REQUIRED_RESULT; #endif - +#ifndef QT_NO_REGEXP + QStringList split(const QRegularExpression &sep, SplitBehavior behavior = KeepEmptyParts) const Q_REQUIRED_RESULT; +#endif enum NormalizationForm { NormalizationForm_D, NormalizationForm_C, @@ -592,9 +607,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) @@ -606,8 +621,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) {} @@ -649,6 +664,7 @@ class QLatin1String public: Q_DECL_CONSTEXPR inline explicit QLatin1String(const char *s) : m_size(s ? int(strlen(s)) : 0), m_data(s) {} Q_DECL_CONSTEXPR inline explicit QLatin1String(const char *s, int sz) : m_size(sz), m_data(s) {} + Q_DECL_CONSTEXPR inline explicit QLatin1String(const QByteArray &s) : m_size(strlen(s.constData())), m_data(s.constData()) {} inline const char *latin1() const { return m_data; } inline int size() const { return m_size; } @@ -709,9 +725,9 @@ inline QChar *QString::data() inline const QChar *QString::constData() const { return reinterpret_cast<const QChar*>(d->data()); } inline void QString::detach() -{ if (d->ref != 1 || d->offset) realloc(); } +{ if (d->ref.isShared() || (d->offset != sizeof(QStringData))) realloc(); } inline bool QString::isDetached() const -{ return d->ref == 1; } +{ return !d->ref.isShared(); } inline QString &QString::operator=(const QLatin1String &s) { *this = fromLatin1(s.latin1(), s.size()); @@ -874,7 +890,7 @@ inline QString::~QString() { if (!d->ref.deref()) free(d); } inline void QString::reserve(int asize) { - if (d->ref != 1 || asize > int(d->alloc)) + if (d->ref.isShared() || asize > int(d->alloc)) realloc(asize); if (!d->capacityReserved) { @@ -885,11 +901,12 @@ inline void QString::reserve(int asize) inline void QString::squeeze() { - if (d->ref > 1 || d->size < int(d->alloc)) + if (d->ref.isShared() || d->size < int(d->alloc)) realloc(); if (d->capacityReserved) { - // cannot set unconditionally, since d could be the shared_null/shared_empty (which is const) + // cannot set unconditionally, since d could be shared_null or + // otherwise static. d->capacityReserved = false; } } diff --git a/src/corelib/tools/qstringbuilder.h b/src/corelib/tools/qstringbuilder.h index e34425be7e..0eb8aa8903 100644 --- a/src/corelib/tools/qstringbuilder.h +++ b/src/corelib/tools/qstringbuilder.h @@ -219,9 +219,9 @@ template <> struct QConcatenable<QString> : private QAbstractConcatenable } }; -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; } @@ -324,9 +324,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; } @@ -338,9 +338,8 @@ template <int N> struct QConcatenable<QConstByteArrayDataPtr<N> > : private QAbs #endif static inline void appendTo(const type &ba, char *&out) { - const char *a = ba.ptr->data; - while (*a) - *out++ = *a++; + ::memcpy(out, ba.ptr->data, N); + out += N; } }; diff --git a/src/corelib/tools/qvector.cpp b/src/corelib/tools/qvector.cpp index 75c219bbc9..1a746dc061 100644 --- a/src/corelib/tools/qvector.cpp +++ b/src/corelib/tools/qvector.cpp @@ -54,15 +54,7 @@ static inline int alignmentThreshold() return 2 * sizeof(void*); } -const QVectorData QVectorData::shared_null = { Q_REFCOUNT_INITIALIZER(-1), 0, 0, true, false, 0 }; - -QVectorData *QVectorData::malloc(int sizeofTypedData, int size, int sizeofT, QVectorData *init) -{ - QVectorData* p = (QVectorData *)::malloc(sizeofTypedData + (size - 1) * sizeofT); - Q_CHECK_PTR(p); - ::memcpy(p, init, sizeofTypedData + (qMin(size, init->alloc) - 1) * sizeofT); - return p; -} +const QVectorData QVectorData::shared_null = { Q_REFCOUNT_INITIALIZE_STATIC, 0, 0, false, 0 }; QVectorData *QVectorData::allocate(int size, int alignment) { @@ -84,11 +76,9 @@ void QVectorData::free(QVectorData *x, int alignment) ::free(x); } -int QVectorData::grow(int sizeofTypedData, int size, int sizeofT, bool excessive) +int QVectorData::grow(int sizeOfHeader, int size, int sizeOfT) { - if (excessive) - return size + size / 2; - return qAllocMore(size * sizeofT, sizeofTypedData - sizeofT) / sizeofT; + return qAllocMore(size * sizeOfT, sizeOfHeader) / sizeOfT; } /*! diff --git a/src/corelib/tools/qvector.h b/src/corelib/tools/qvector.h index 4230e55ff5..c119ef43ae 100644 --- a/src/corelib/tools/qvector.h +++ b/src/corelib/tools/qvector.h @@ -65,37 +65,28 @@ QT_BEGIN_NAMESPACE struct Q_CORE_EXPORT QVectorData { QtPrivate::RefCount ref; - int alloc; int size; -#if defined(Q_PROCESSOR_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; -#endif + uint alloc : 31; + uint capacityReserved : 1; + + qptrdiff offset; + + void* data() { return reinterpret_cast<char *>(this) + this->offset; } static const QVectorData shared_null; - // ### Qt 5: rename to 'allocate()'. The current name causes problems for - // some debugges when the QVector is member of a class within an unnamed namespace. - // ### Qt 5: can be removed completely. (Ralf) - static QVectorData *malloc(int sizeofTypedData, int size, int sizeofT, QVectorData *init); static QVectorData *allocate(int size, int alignment); static QVectorData *reallocate(QVectorData *old, int newsize, int oldsize, int alignment); static void free(QVectorData *data, int alignment); - static int grow(int sizeofTypedData, int size, int sizeofT, bool excessive); + static int grow(int sizeOfHeader, int size, int sizeOfT); }; template <typename T> -struct QVectorTypedData : private QVectorData -{ // private inheritance as we must not access QVectorData member thought QVectorTypedData - // as this would break strict aliasing rules. (in the case of shared_null) - T array[1]; +struct QVectorTypedData : QVectorData +{ + T* begin() { return reinterpret_cast<T *>(this->data()); } + T* end() { return begin() + this->size; } - static inline void free(QVectorTypedData<T> *x, int alignment) { QVectorData::free(static_cast<QVectorData *>(x), alignment); } + static QVectorTypedData *sharedNull() { return static_cast<QVectorTypedData *>(const_cast<QVectorData *>(&QVectorData::shared_null)); } }; class QRegion; @@ -104,27 +95,30 @@ template <typename T> class QVector { typedef QVectorTypedData<T> Data; - union { - QVectorData *d; -#if defined(Q_CC_SUN) && (__SUNPRO_CC <= 0x550) - QVectorTypedData<T> *p; -#else - Data *p; -#endif - }; + Data *d; public: - // ### Qt 5: Consider making QVector non-shared to get at least one - // "really fast" container. See tests/benchmarks/corelib/tools/qvector/ - inline QVector() : d(const_cast<QVectorData *>(&QVectorData::shared_null)) { } + inline QVector() : d(Data::sharedNull()) { } 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() { if (!d) return; if (!d->ref.deref()) free(p); } + inline QVector(const QVector<T> &v) + { + if (v.d->ref.ref()) { + d = v.d; + } else { + d = Data::sharedNull(); + realloc(0, int(v.d->alloc)); + qCopy(v.d->begin(), v.d->end(), d->begin()); + d->size = v.d->size; + d->capacityReserved = v.d->capacityReserved; + } + } + + inline ~QVector() { if (!d->ref.deref()) free(d); } QVector<T> &operator=(const QVector<T> &v); #ifdef Q_COMPILER_RVALUE_REFS inline QVector<T> operator=(QVector<T> &&other) - { qSwap(p, other.p); return *this; } + { qSwap(d, other.d); return *this; } #endif inline void swap(QVector<T> &other) { qSwap(d, other.d); } #ifdef Q_COMPILER_INITIALIZER_LISTS @@ -139,18 +133,27 @@ public: void resize(int size); - inline int capacity() const { return d->alloc; } + inline int capacity() const { return int(d->alloc); } void reserve(int size); - inline void squeeze() { realloc(d->size, d->size); d->capacity = 0; } + inline void squeeze() { realloc(d->size, d->size); d->capacityReserved = 0; } + + 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 != Data::sharedNull()) + d->ref.setSharable(sharable); + } - 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 bool isSharedWith(const QVector<T> &other) const { return d == other.d; } - inline T *data() { detach(); return p->array; } - inline const T *data() const { return p->array; } - inline const T *constData() const { return p->array; } + inline T *data() { detach(); return d->begin(); } + inline const T *data() const { return d->begin(); } + inline const T *constData() const { return d->begin(); } void clear(); const T &at(int i) const; @@ -243,12 +246,12 @@ public: typedef T* iterator; typedef const T* const_iterator; #endif - inline iterator begin() { detach(); return p->array; } - inline const_iterator begin() const { return p->array; } - inline const_iterator constBegin() const { return p->array; } - inline iterator end() { detach(); return p->array + d->size; } - inline const_iterator end() const { return p->array + d->size; } - inline const_iterator constEnd() const { return p->array + d->size; } + inline iterator begin() { detach(); return d->begin(); } + inline const_iterator begin() const { return d->begin(); } + inline const_iterator constBegin() const { return d->begin(); } + inline iterator end() { detach(); return d->end(); } + inline const_iterator end() const { return d->end(); } + inline const_iterator constEnd() const { return d->end(); } iterator insert(iterator before, int n, const T &x); inline iterator insert(iterator before, const T &x) { return insert(before, 1, x); } iterator erase(iterator begin, iterator end); @@ -313,46 +316,49 @@ private: friend class QRegion; // Optimization for QRegion::rects() void detach_helper(); - QVectorData *malloc(int alloc); + Data *malloc(int alloc); void realloc(int size, int alloc); void free(Data *d); - int sizeOfTypedData() { - // this is more or less the same as sizeof(Data), except that it doesn't - // count the padding at the end - return reinterpret_cast<const char *>(&(reinterpret_cast<const Data *>(this))->array[1]) - reinterpret_cast<const char *>(this); + + class AlignmentDummy { QVectorData header; T array[1]; }; + + static Q_DECL_CONSTEXPR int offsetOfTypedData() + { + // (non-POD)-safe offsetof(AlignmentDummy, array) + return (sizeof(QVectorData) + (alignOfTypedData() - 1)) & ~(alignOfTypedData() - 1); } - inline int alignOfTypedData() const + static Q_DECL_CONSTEXPR int alignOfTypedData() { #ifdef Q_ALIGNOF - return qMax<int>(sizeof(void*), Q_ALIGNOF(Data)); + return Q_ALIGNOF(AlignmentDummy); #else - return 0; + return sizeof(void *); #endif } }; template <typename T> void QVector<T>::detach_helper() -{ realloc(d->size, d->alloc); } +{ realloc(d->size, int(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 > int(d->alloc)) realloc(d->size, asize); if (isDetached()) d->capacityReserved = 1; } template <typename T> void QVector<T>::resize(int asize) -{ realloc(asize, (asize > d->alloc || (!d->capacity && asize < d->size && asize < (d->alloc >> 1))) ? - QVectorData::grow(sizeOfTypedData(), asize, sizeof(T), QTypeInfo<T>::isStatic) - : d->alloc); } +{ realloc(asize, (asize > int(d->alloc) || (!d->capacityReserved && asize < d->size && asize < int(d->alloc >> 1))) ? + QVectorData::grow(offsetOfTypedData(), asize, sizeof(T)) + : int(d->alloc)); } template <typename T> inline void QVector<T>::clear() { *this = QVector<T>(); } template <typename T> inline const T &QVector<T>::at(int i) const { Q_ASSERT_X(i >= 0 && i < d->size, "QVector<T>::at", "index out of range"); - return p->array[i]; } + return d->begin()[i]; } template <typename T> inline const T &QVector<T>::operator[](int i) const { Q_ASSERT_X(i >= 0 && i < d->size, "QVector<T>::operator[]", "index out of range"); - return p->array[i]; } + return d->begin()[i]; } template <typename T> inline T &QVector<T>::operator[](int i) { Q_ASSERT_X(i >= 0 && i < d->size, "QVector<T>::operator[]", "index out of range"); @@ -388,39 +394,37 @@ 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; } template <typename T> -inline QVectorData *QVector<T>::malloc(int aalloc) +inline typename QVector<T>::Data *QVector<T>::malloc(int aalloc) { - QVectorData *vectordata = QVectorData::allocate(sizeOfTypedData() + (aalloc - 1) * sizeof(T), alignOfTypedData()); + QVectorData *vectordata = QVectorData::allocate(offsetOfTypedData() + aalloc * sizeof(T), alignOfTypedData()); Q_CHECK_PTR(vectordata); - return vectordata; + return static_cast<Data *>(vectordata); } template <typename T> QVector<T>::QVector(int asize) { d = malloc(asize); - d->ref = 1; - d->alloc = d->size = asize; - d->sharable = true; - d->capacity = false; + d->ref.initializeOwned(); + d->size = asize; + d->alloc = uint(d->size); + d->capacityReserved = false; + d->offset = offsetOfTypedData(); if (QTypeInfo<T>::isComplex) { - T* b = p->array; - T* i = p->array + d->size; + T* b = d->begin(); + T* i = d->end(); while (i != b) new (--i) T; } else { - qMemSet(p->array, 0, asize * sizeof(T)); + qMemSet(d->begin(), 0, asize * sizeof(T)); } } @@ -428,12 +432,13 @@ template <typename T> QVector<T>::QVector(int asize, const T &t) { d = malloc(asize); - d->ref = 1; - d->alloc = d->size = asize; - d->sharable = true; - d->capacity = false; - T* i = p->array + d->size; - while (i != p->array) + d->ref.initializeOwned(); + d->size = asize; + d->alloc = uint(d->size); + d->capacityReserved = false; + d->offset = offsetOfTypedData(); + T* i = d->end(); + while (i != d->begin()) new (--i) T(t); } @@ -442,14 +447,22 @@ template <typename T> QVector<T>::QVector(std::initializer_list<T> args) { d = malloc(int(args.size())); - d->ref = 1; - d->alloc = d->size = int(args.size()); - d->sharable = true; - d->capacity = false; - T* i = p->array + d->size; - auto it = args.end(); - while (i != p->array) - new (--i) T(*(--it)); + d->ref.initializeOwned(); + d->size = int(args.size()); + d->alloc = uint(d->size); + d->capacityReserved = false; + d->offset = offsetOfTypedData(); + if (QTypeInfo<T>::isComplex) { + T* b = d->begin(); + T* i = d->end(); + const T* s = args.end(); + while (i != b) + new(--i) T(*--s); + } else { + // std::initializer_list<T>::iterator is guaranteed to be + // const T* ([support.initlist]/1), so can be memcpy'ed away from: + ::memcpy(d->begin(), args.begin(), args.size() * sizeof(T)); + } } #endif @@ -457,14 +470,12 @@ template <typename T> void QVector<T>::free(Data *x) { if (QTypeInfo<T>::isComplex) { - T* b = x->array; - union { QVectorData *d; Data *p; } u; - u.p = x; - T* i = b + u.d->size; + T* b = x->begin(); + T* i = b + x->size; while (i-- != b) i->~T(); } - x->free(x, alignOfTypedData()); + Data::free(x, alignOfTypedData()); } template <typename T> @@ -473,84 +484,82 @@ void QVector<T>::realloc(int asize, int aalloc) Q_ASSERT(asize <= aalloc); T *pOld; T *pNew; - union { QVectorData *d; Data *p; } x; - x.d = d; + Data *x = 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; - pNew = p->array + asize; + pOld = d->begin() + d->size; + pNew = d->begin() + asize; while (asize < d->size) { (--pOld)->~T(); d->size--; } } - if (aalloc != d->alloc || d->ref != 1) { + if (aalloc != int(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) { - x.d = malloc(aalloc); - Q_CHECK_PTR(x.p); + x = malloc(aalloc); + Q_CHECK_PTR(x); + x->size = 0; + } else if (!isDetached()) { + x = malloc(aalloc); + Q_CHECK_PTR(x); if (QTypeInfo<T>::isComplex) { - x.d->size = 0; + x->size = 0; } else { - ::memcpy(x.p, p, sizeOfTypedData() + (qMin(aalloc, d->alloc) - 1) * sizeof(T)); - x.d->size = d->size; + ::memcpy(x, d, offsetOfTypedData() + qMin(uint(aalloc), d->alloc) * sizeof(T)); + x->size = d->size; } } else { QT_TRY { - QVectorData *mem = QVectorData::reallocate(d, sizeOfTypedData() + (aalloc - 1) * sizeof(T), - sizeOfTypedData() + (d->alloc - 1) * sizeof(T), alignOfTypedData()); + QVectorData *mem = QVectorData::reallocate(d, offsetOfTypedData() + aalloc * sizeof(T), + offsetOfTypedData() + d->alloc * sizeof(T), alignOfTypedData()); Q_CHECK_PTR(mem); - x.d = d = mem; - x.d->size = d->size; + x = d = static_cast<Data *>(mem); + x->size = d->size; } QT_CATCH (const std::bad_alloc &) { - if (aalloc > d->alloc) // ignore the error in case we are just shrinking. + if (aalloc > int(d->alloc)) // ignore the error in case we are just shrinking. QT_RETHROW; } } - x.d->ref = 1; - x.d->alloc = aalloc; - x.d->sharable = true; - x.d->capacity = d->capacity; - x.d->reserved = 0; + x->ref.initializeOwned(); + x->alloc = uint(aalloc); + x->capacityReserved = d->capacityReserved; + x->offset = offsetOfTypedData(); } if (QTypeInfo<T>::isComplex) { QT_TRY { - pOld = p->array + x.d->size; - pNew = x.p->array + x.d->size; + pOld = d->begin() + x->size; + pNew = x->begin() + x->size; // copy objects from the old array into the new array const int toMove = qMin(asize, d->size); - while (x.d->size < toMove) { + while (x->size < toMove) { new (pNew++) T(*pOld++); - x.d->size++; + x->size++; } // construct all new objects when growing - while (x.d->size < asize) { + while (x->size < asize) { new (pNew++) T; - x.d->size++; + x->size++; } } QT_CATCH (...) { - free(x.p); + free(x); QT_RETHROW; } - } else if (asize > x.d->size) { + } else if (asize > x->size) { // initialize newly allocated memory to 0 - qMemSet(x.p->array + x.d->size, 0, (asize - x.d->size) * sizeof(T)); + qMemSet(x->end(), 0, (asize - x->size) * sizeof(T)); } - x.d->size = asize; + x->size = asize; - if (d != x.d) { + if (d != x) { if (!d->ref.deref()) - free(p); - d = x.d; + free(d); + d = x; } } @@ -560,31 +569,31 @@ Q_OUTOFLINE_TEMPLATE T QVector<T>::value(int i) const if (i < 0 || i >= d->size) { return T(); } - return p->array[i]; + return d->begin()[i]; } template<typename T> Q_OUTOFLINE_TEMPLATE T QVector<T>::value(int i, const T &defaultValue) const { - return ((i < 0 || i >= d->size) ? defaultValue : p->array[i]); + return ((i < 0 || i >= d->size) ? defaultValue : d->begin()[i]); } template <typename T> void QVector<T>::append(const T &t) { - if (d->ref != 1 || d->size + 1 > d->alloc) { + if (!isDetached() || d->size + 1 > int(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) - : d->alloc); + realloc(d->size, (d->size + 1 > int(d->alloc)) ? + QVectorData::grow(offsetOfTypedData(), d->size + 1, sizeof(T)) + : int(d->alloc)); if (QTypeInfo<T>::isComplex) - new (p->array + d->size) T(copy); + new (d->end()) T(copy); else - p->array[d->size] = copy; + *d->end() = copy; } else { if (QTypeInfo<T>::isComplex) - new (p->array + d->size) T(t); + new (d->end()) T(t); else - p->array[d->size] = t; + *d->end() = t; } ++d->size; } @@ -592,27 +601,26 @@ void QVector<T>::append(const T &t) template <typename T> typename QVector<T>::iterator QVector<T>::insert(iterator before, size_type n, const T &t) { - int offset = int(before - p->array); + int offset = int(before - d->begin()); if (n != 0) { const T copy(t); - if (d->ref != 1 || d->size + n > d->alloc) - realloc(d->size, QVectorData::grow(sizeOfTypedData(), d->size + n, sizeof(T), - QTypeInfo<T>::isStatic)); + if (!isDetached() || d->size + n > int(d->alloc)) + realloc(d->size, QVectorData::grow(offsetOfTypedData(), d->size + n, sizeof(T))); if (QTypeInfo<T>::isStatic) { - T *b = p->array + d->size; - T *i = p->array + d->size + n; + T *b = d->end(); + T *i = d->end() + n; while (i != b) new (--i) T; - i = p->array + d->size; + i = d->end(); T *j = i + n; - b = p->array + offset; + b = d->begin() + offset; while (i != b) *--j = *--i; i = b+n; while (i != b) *--i = copy; } else { - T *b = p->array + offset; + T *b = d->begin() + offset; T *i = b + n; memmove(i, b, (d->size - offset) * sizeof(T)); while (i != b) @@ -620,29 +628,29 @@ typename QVector<T>::iterator QVector<T>::insert(iterator before, size_type n, c } d->size += n; } - return p->array + offset; + return d->begin() + offset; } template <typename T> typename QVector<T>::iterator QVector<T>::erase(iterator abegin, iterator aend) { - int f = int(abegin - p->array); - int l = int(aend - p->array); + int f = int(abegin - d->begin()); + int l = int(aend - d->begin()); int n = l - f; detach(); if (QTypeInfo<T>::isComplex) { - qCopy(p->array+l, p->array+d->size, p->array+f); - T *i = p->array+d->size; - T* b = p->array+d->size-n; + qCopy(d->begin()+l, d->end(), d->begin()+f); + T *i = d->end(); + T* b = d->end()-n; while (i != b) { --i; i->~T(); } } else { - memmove(p->array + f, p->array + l, (d->size-l)*sizeof(T)); + memmove(d->begin() + f, d->begin() + l, (d->size-l)*sizeof(T)); } d->size -= n; - return p->array + f; + return d->begin() + f; } template <typename T> @@ -652,9 +660,9 @@ bool QVector<T>::operator==(const QVector<T> &v) const return false; if (d == v.d) return true; - T* b = p->array; + T* b = d->begin(); T* i = b + d->size; - T* j = v.p->array + d->size; + T* j = v.d->end(); while (i != b) if (!(*--i == *--j)) return false; @@ -667,8 +675,8 @@ QVector<T> &QVector<T>::fill(const T &from, int asize) const T copy(from); resize(asize < 0 ? d->size : asize); if (d->size) { - T *i = p->array + d->size; - T *b = p->array; + T *i = d->end(); + T *b = d->begin(); while (i != b) *--i = copy; } @@ -681,9 +689,9 @@ QVector<T> &QVector<T>::operator+=(const QVector &l) int newSize = d->size + l.d->size; realloc(d->size, newSize); - T *w = p->array + newSize; - T *i = l.p->array + l.d->size; - T *b = l.p->array; + T *w = d->begin() + newSize; + T *i = l.d->end(); + T *b = l.d->begin(); while (i != b) { if (QTypeInfo<T>::isComplex) new (--w) T(*--i); @@ -700,11 +708,11 @@ int QVector<T>::indexOf(const T &t, int from) const if (from < 0) from = qMax(from + d->size, 0); if (from < d->size) { - T* n = p->array + from - 1; - T* e = p->array + d->size; + T* n = d->begin() + from - 1; + T* e = d->end(); while (++n != e) if (*n == t) - return n - p->array; + return n - d->begin(); } return -1; } @@ -717,8 +725,8 @@ int QVector<T>::lastIndexOf(const T &t, int from) const else if (from >= d->size) from = d->size-1; if (from >= 0) { - T* b = p->array; - T* n = p->array + from + 1; + T* b = d->begin(); + T* n = d->begin() + from + 1; while (n != b) { if (*--n == t) return n - b; @@ -730,8 +738,8 @@ int QVector<T>::lastIndexOf(const T &t, int from) const template <typename T> bool QVector<T>::contains(const T &t) const { - T* b = p->array; - T* i = p->array + d->size; + T* b = d->begin(); + T* i = d->end(); while (i != b) if (*--i == t) return true; @@ -742,8 +750,8 @@ template <typename T> int QVector<T>::count(const T &t) const { int c = 0; - T* b = p->array; - T* i = p->array + d->size; + T* b = d->begin(); + T* i = d->end(); while (i != b) if (*--i == t) ++c; diff --git a/src/corelib/tools/tools.pri b/src/corelib/tools/tools.pri index 250789a969..ac347404fd 100644 --- a/src/corelib/tools/tools.pri +++ b/src/corelib/tools/tools.pri @@ -2,6 +2,9 @@ HEADERS += \ tools/qalgorithms.h \ + tools/qarraydata.h \ + tools/qarraydataops.h \ + tools/qarraydatapointer.h \ tools/qbitarray.h \ tools/qbytearray.h \ tools/qbytearraymatcher.h \ @@ -56,6 +59,7 @@ HEADERS += \ SOURCES += \ + tools/qarraydata.cpp \ tools/qbitarray.cpp \ tools/qbytearray.cpp \ tools/qbytearraymatcher.cpp \ |