diff --git a/src/corelib/global/qt_pch.h b/src/corelib/global/qt_pch.h index 0dfd6c745f..1663d9392f 100644 --- a/src/corelib/global/qt_pch.h +++ b/src/corelib/global/qt_pch.h @@ -67,7 +67,7 @@ #include #include #include -#include /* All moc genereated code has this include */ +//#include /* All moc genereated code has this include */ #include #include #include diff --git a/src/corelib/text/qbytearraylist.h b/src/corelib/text/qbytearraylist.h index 4b6c926960..e113c6059c 100644 --- a/src/corelib/text/qbytearraylist.h +++ b/src/corelib/text/qbytearraylist.h @@ -80,8 +80,10 @@ public: inline QByteArray join(char sep) const { return QtPrivate::QByteArrayList_join(self(), &sep, 1); } +#if 0 inline int indexOf(const char *needle, int from = 0) const { return QtPrivate::QByteArrayList_indexOf(self(), needle, from); } +#endif private: typedef QVector Self; diff --git a/src/corelib/tools/qarraydataops.h b/src/corelib/tools/qarraydataops.h index dbd535fa3e..f3de6b65a9 100644 --- a/src/corelib/tools/qarraydataops.h +++ b/src/corelib/tools/qarraydataops.h @@ -150,7 +150,7 @@ struct QPodArrayOps this->size += (e - b); } - void insert(T *where, size_t n, T t) + void insert(T *where, size_t n, parameter_type t) { Q_ASSERT(!this->isShared()); Q_ASSERT(where >= this->begin() && where < this->end()); // Use copyAppend at end @@ -163,6 +163,19 @@ struct QPodArrayOps *where++ = t; } + void insert(T *where, T &&t) + { + Q_ASSERT(!this->isShared()); + Q_ASSERT(where >= this->begin() && where <= this->end()); + Q_ASSERT(this->allocatedCapacity() - this->size >= 1); + + ::memmove(static_cast(where + 1), static_cast(where), + (static_cast(this->end()) - where) * sizeof(T)); + this->size += 1; + new (where) T(std::move(t)); + } + + void erase(T *b, T *e) { Q_ASSERT(this->isMutable()); @@ -369,7 +382,7 @@ struct QGenericArrayOps } } - void insert(T *where, size_t n, T t) + void insert(T *where, size_t n, parameter_type t) { Q_ASSERT(!this->isShared()); Q_ASSERT(where >= this->begin() && where <= this->end()); @@ -431,6 +444,33 @@ struct QGenericArrayOps } } + void insert(T *where, T &&t) + { + Q_ASSERT(!this->isShared()); + Q_ASSERT(where >= this->begin() && where <= this->end()); + Q_ASSERT(this->allocatedCapacity() - this->size >= 1); + + // Array may be truncated at where in case of exceptions + T *const end = this->end(); + + if (where != end) { + // Move elements in array + T *readIter = end - 1; + T *writeIter = end; + new (writeIter) T(std::move(*readIter)); + while (readIter > where) { + --readIter; + --writeIter; + *writeIter = std::move(*readIter); + } + *where = std::move(t); + } else { + new (where) T(std::move(t)); + } + + ++this->size; + } + void erase(T *b, T *e) { Q_ASSERT(this->isMutable()); @@ -554,7 +594,7 @@ struct QMovableArrayOps this->size += (e - b); } - void insert(T *where, size_t n, T t) + void insert(T *where, size_t n, parameter_type t) { Q_ASSERT(!this->isShared()); Q_ASSERT(where >= this->begin() && where <= this->end()); @@ -618,6 +658,9 @@ struct QMovableArrayOps this->size += n; } + // use moving insert + using QGenericArrayOps::insert; + void erase(T *b, T *e) { Q_ASSERT(this->isMutable()); @@ -627,7 +670,7 @@ struct QMovableArrayOps struct Mover { - Mover(T *&start, const T *finish, uint &sz) + Mover(T *&start, const T *finish, int &sz) : destination(start) , source(start) , n(finish - start) @@ -644,7 +687,7 @@ struct QMovableArrayOps T *&destination; const T *const source; size_t n; - uint &size; + int &size; } mover(e, this->end(), this->size); // destroy the elements we're erasing diff --git a/src/corelib/tools/qarraydatapointer.h b/src/corelib/tools/qarraydatapointer.h index 9103064bd9..3d850c0144 100644 --- a/src/corelib/tools/qarraydatapointer.h +++ b/src/corelib/tools/qarraydatapointer.h @@ -215,7 +215,7 @@ protected: T *ptr; public: - uint size; + int size; }; template diff --git a/src/corelib/tools/qvector.h b/src/corelib/tools/qvector.h index 330bf8bb98..640809cd14 100644 --- a/src/corelib/tools/qvector.h +++ b/src/corelib/tools/qvector.h @@ -1,6 +1,7 @@ /**************************************************************************** ** ** Copyright (C) 2016 The Qt Company Ltd. +** Copyright (C) 2019 Intel Corporation ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the QtCore module of the Qt Toolkit. @@ -40,22 +41,14 @@ #ifndef QVECTOR_H #define QVECTOR_H -#include -#include -#include -#include +#include +#include #include -#include +#include -#include +#include +#include #include -#if QT_VERSION < QT_VERSION_CHECK(6,0,0) -#include -#endif -#include -#include - -#include QT_BEGIN_NAMESPACE @@ -79,85 +72,233 @@ class QVector #endif { typedef QTypedArrayData Data; - Data *d; + typedef QArrayDataOps DataOps; + typedef QArrayDataPointer DataPointer; + + DataPointer d; template friend int QtPrivate::indexOf(const QVector &list, const U &u, int from); template friend int QtPrivate::lastIndexOf(const QVector &list, const U &u, int from); public: - inline QVector() noexcept : d(Data::sharedNull()) { } - explicit QVector(int size); - QVector(int size, const T &t); - inline QVector(const QVector &v); - inline ~QVector() { if (!d->deref()) freeData(d); } - QVector &operator=(const QVector &v); - QVector(QVector &&other) noexcept : d(other.d) { other.d = Data::sharedNull(); } - QVector &operator=(QVector &&other) noexcept - { QVector moved(std::move(other)); swap(moved); return *this; } - void swap(QVector &other) noexcept { qSwap(d, other.d); } - inline QVector(std::initializer_list args); - QVector &operator=(std::initializer_list args); - template = true> - inline QVector(InputIterator first, InputIterator last); - explicit QVector(QArrayDataPointerRef ref) noexcept : d(ref.ptr) {} + typedef T Type; + typedef T value_type; + typedef value_type *pointer; + typedef const value_type *const_pointer; + typedef value_type &reference; + typedef const value_type &const_reference; + typedef int size_type; + typedef qptrdiff difference_type; + typedef typename Data::iterator iterator; + typedef typename Data::const_iterator const_iterator; + typedef iterator Iterator; + typedef const_iterator ConstIterator; + typedef std::reverse_iterator reverse_iterator; + typedef std::reverse_iterator const_reverse_iterator; + // ### The line below triggers too earely instantiation of QTypeInfo in qvariant.h + //typedef typename DataOps::parameter_type parameter_type; + typedef const_reference parameter_type; - bool operator==(const QVector &v) const; - inline bool operator!=(const QVector &v) const { return !(*this == v); } - - inline int size() const { return d->size; } - - inline bool isEmpty() const { return d->size == 0; } - - void resize(int size); - - inline int capacity() const { return d->constAllocatedCapacity(); } - void reserve(int size); - inline void squeeze() +private: + void resize_internal(int i, Qt::Initialization); + bool isValidIterator(const_iterator i) const + { + const std::less less = {}; + return !less(d->end(), i) && !less(i, d->begin()); + } + QVector(DataPointer dd) noexcept + : d(dd) { - if (d->size < int(d->allocatedCapacity())) { - if (!d->size) { - *this = QVector(); - return; - } - realloc(d->size, d->detachFlags()); - } } - inline void detach(); - inline bool isDetached() const { return !d->isShared(); } +public: + inline QVector() noexcept { } + explicit QVector(int size) + : d(Data::allocate(size)) + { + if (size) + d->appendInitialize(size); + } + QVector(int size, const T &t) + : d(Data::allocate(size)) + { + if (size) + d->copyAppend(size, t); + } + + inline QVector(const QVector &other) noexcept : d(other.d) {} + QVector(QVector &&other) noexcept : d(std::move(other.d)) {} + inline QVector(std::initializer_list args) + : d(Data::allocate(args.size())) + { + if (args.size()) + d->copyAppend(args.begin(), args.end()); + } + + ~QVector() /*noexcept(std::is_nothrow_destructible::value)*/ {} + QVector &operator=(const QVector &other) { d = other.d; return *this; } + QVector &operator=(QVector &&other) noexcept(std::is_nothrow_destructible::value) + { + d = std::move(other.d); + return *this; + } + QVector &operator=(std::initializer_list args) + { + d = DataPointer(Data::allocate(args.size())); + if (args.size()) + d->copyAppend(args.begin(), args.end()); + return *this; + } + template = true> + QVector(InputIterator i1, InputIterator i2) + : d(Data::allocate(std::distance(i1, i2))) + { + if (std::distance(i1, i2)) + d->copyAppend(i1, i2); + } + + template = true> + QVector(InputIterator i1, InputIterator i2) + : QVector() + { + QtPrivate::reserveIfForwardIterator(this, i1, i2); + std::copy(i1, i2, std::back_inserter(*this)); + } + + void swap(QVector &other) noexcept { qSwap(d, other.d); } + + friend bool operator==(const QVector &l, const QVector &r) + { + if (l.size() != r.size()) + return false; + if (l.begin() == r.begin()) + return true; + + // do element-by-element comparison + return l.d->compare(l.begin(), r.begin(), l.size()); + } + friend bool operator!=(const QVector &l, const QVector &r) + { + return !(l == r); + } + + int size() const noexcept { return int(d->size); } + int count() const noexcept { return size(); } + int length() const noexcept { return size(); } + + inline bool isEmpty() const noexcept { return d->size == 0; } + + void resize(int size) + { + resize_internal(size, Qt::Uninitialized); + if (size > this->size()) + d->appendInitialize(size); + } + void resize(int size, parameter_type c) + { + resize_internal(size, Qt::Uninitialized); + if (size > this->size()) + d->copyAppend(size - this->size(), c); + } + + inline int capacity() const { return int(d->constAllocatedCapacity()); } + void reserve(int size); + inline void squeeze(); + + void detach() { d.detach(); } + bool isDetached() const noexcept { return !d->isShared(); } inline bool isSharedWith(const QVector &other) const { return d == other.d; } - 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(); + pointer data() { detach(); return d->data(); } + const_pointer data() const noexcept { return d->data(); } + const_pointer constData() const noexcept { return d->data(); } + void clear() { + if (!size()) + return; + if (d->needsDetach()) { + // must allocate memory + DataPointer detached(Data::allocate(d.allocatedCapacity(), d->detachFlags())); + d.swap(detached); + } else { + d->truncate(0); + } + } - const T &at(int i) const; - T &operator[](int i); - const T &operator[](int i) const; - void append(const T &t); - void append(T &&t); - inline void append(const QVector &l) { *this += l; } + const_reference at(int i) const noexcept + { + Q_ASSERT_X(size_t(i) < size_t(d->size), "QVector::at", "index out of range"); + return data()[i]; + } + reference operator[](int i) + { + Q_ASSERT_X(size_t(i) < size_t(d->size), "QVector::operator[]", "index out of range"); + detach(); + return data()[i]; + } + const_reference operator[](int i) const noexcept { return at(i); } + void append(const_reference t) + { append(const_iterator(std::addressof(t)), const_iterator(std::addressof(t)) + 1); } + void append(const_iterator i1, const_iterator i2); + void append(value_type &&t); + void append(const QVector &l) { append(l.constBegin(), l.constEnd()); } void prepend(T &&t); void prepend(const T &t); - void insert(int i, T &&t); - void insert(int i, const T &t); - void insert(int i, int n, const T &t); - void replace(int i, const T &t); - void remove(int i); - void remove(int i, int n); - inline void removeFirst() { Q_ASSERT(!isEmpty()); erase(d->begin()); } - inline void removeLast(); - T takeFirst() { Q_ASSERT(!isEmpty()); T r = std::move(first()); removeFirst(); return r; } - T takeLast() { Q_ASSERT(!isEmpty()); T r = std::move(last()); removeLast(); return r; } + iterator insert(int i, parameter_type t) + { return insert(i, 1, t); } + iterator insert(int i, int n, parameter_type t); + iterator insert(const_iterator before, parameter_type t) + { + Q_ASSERT_X(isValidIterator(before), "QVector::insert", "The specified iterator argument 'before' is invalid"); + return insert(before, 1, t); + } + iterator insert(const_iterator before, int n, parameter_type t) + { + Q_ASSERT_X(isValidIterator(before), "QVector::insert", "The specified iterator argument 'before' is invalid"); + return insert(std::distance(constBegin(), before), n, t); + } + inline iterator insert(const_iterator before, T &&t) + { + Q_ASSERT_X(isValidIterator(before), "QVector::insert", "The specified iterator argument 'before' is invalid"); + return insert(std::distance(constBegin(), before), std::move(t)); + } + iterator insert(int i, T &&t); +#if 0 + template< class InputIt > + iterator insert( const_iterator pos, InputIt first, InputIt last ); + iterator insert( const_iterator pos, std::initializer_list ilist ); +#endif + void replace(int i, const T &t) + { + Q_ASSERT_X(i >= 0 && i < d->size, "QVector::replace", "index out of range"); + const T copy(t); + data()[i] = copy; + } + void replace(int i, T &&t) + { + Q_ASSERT_X(i >= 0 && i < d->size, "QVector::replace", "index out of range"); + const T copy(std::move(t)); + data()[i] = std::move(copy); + } - QVector &fill(const T &t, int size = -1); + void remove(int i, int n = 1); + void removeFirst() { Q_ASSERT(!isEmpty()); remove(0); } + void removeLast() { Q_ASSERT(!isEmpty()); remove(size() - 1); } + value_type takeFirst() { Q_ASSERT(!isEmpty()); value_type v = std::move(first()); remove(0); return v; } + value_type takeLast() { Q_ASSERT(!isEmpty()); value_type v = std::move(last()); remove(size() - 1); return v; } - int indexOf(const T &t, int from = 0) const; - int lastIndexOf(const T &t, int from = -1) const; - bool contains(const T &t) const; - int count(const T &t) const; + QVector &fill(parameter_type t, int size = -1); + + int indexOf(const T &t, int from = 0) const noexcept; + int lastIndexOf(const T &t, int from = -1) const noexcept; + bool contains(const T &t) const noexcept + { + return indexOf(t) != -1; + } + int count(const T &t) const noexcept + { + return int(std::count(&*cbegin(), &*cend(), t)); + } // QList compatibility void removeAt(int i) { remove(i); } @@ -166,10 +307,10 @@ public: const const_iterator ce = this->cend(), cit = std::find(this->cbegin(), ce, t); if (cit == ce) return 0; + int index = cit - this->cbegin(); // next operation detaches, so ce, cit, t may become invalidated: const T tCopy = t; - const int firstFoundIdx = std::distance(this->cbegin(), cit); - const iterator e = end(), it = std::remove(begin() + firstFoundIdx, e, tCopy); + const iterator e = end(), it = std::remove(begin() + index, e, tCopy); const int result = std::distance(it, e); erase(it, e); return result; @@ -182,7 +323,6 @@ public: remove(i); return true; } - int length() const { return size(); } T takeAt(int i) { T t = std::move((*this)[i]); remove(i); return t; } void move(int from, int to) { @@ -199,32 +339,26 @@ public: } // STL-style - typedef typename Data::iterator iterator; - typedef typename Data::const_iterator const_iterator; - typedef std::reverse_iterator reverse_iterator; - typedef std::reverse_iterator const_reverse_iterator; - inline iterator begin() { detach(); return d->begin(); } - inline const_iterator begin() const noexcept { return d->constBegin(); } - inline const_iterator cbegin() const noexcept { return d->constBegin(); } - inline const_iterator constBegin() const noexcept { return d->constBegin(); } - inline iterator end() { detach(); return d->end(); } - inline const_iterator end() const noexcept { return d->constEnd(); } - inline const_iterator cend() const noexcept { return d->constEnd(); } - inline const_iterator constEnd() const noexcept { return d->constEnd(); } + iterator begin() { detach(); return d->begin(); } + iterator end() { detach(); return d->end(); } + + const_iterator begin() const noexcept { return d->constBegin(); } + const_iterator end() const noexcept { return d->constEnd(); } + const_iterator cbegin() const noexcept { return d->constBegin(); } + const_iterator cend() const noexcept { return d->constEnd(); } + const_iterator constBegin() const noexcept { return d->constBegin(); } + const_iterator constEnd() const noexcept { return d->constEnd(); } reverse_iterator rbegin() { return reverse_iterator(end()); } reverse_iterator rend() { return reverse_iterator(begin()); } const_reverse_iterator rbegin() const noexcept { return const_reverse_iterator(end()); } const_reverse_iterator rend() const noexcept { return const_reverse_iterator(begin()); } const_reverse_iterator crbegin() const noexcept { return const_reverse_iterator(end()); } const_reverse_iterator crend() const noexcept { return const_reverse_iterator(begin()); } - iterator insert(iterator before, int n, const T &x); - inline iterator insert(iterator before, const T &x) { return insert(before, 1, x); } - inline iterator insert(iterator before, T &&x); + iterator erase(iterator begin, iterator end); inline iterator erase(iterator pos) { return erase(pos, pos+1); } // more Qt - inline int count() const { return d->size; } inline T& first() { Q_ASSERT(!isEmpty()); return *begin(); } inline const T &first() const { Q_ASSERT(!isEmpty()); return *begin(); } inline const T &constFirst() const { Q_ASSERT(!isEmpty()); return *begin(); } @@ -235,7 +369,7 @@ public: inline bool endsWith(const T &t) const { return !isEmpty() && last() == t; } QVector mid(int pos, int len = -1) const; - T value(int i) const; + T value(int i) const { return value(i, T()); } T value(int i, const T &defaultValue) const; void swapItemsAt(int i, int j) { @@ -246,15 +380,6 @@ public: } // STL compatibility - typedef T value_type; - typedef value_type* pointer; - typedef const value_type* const_pointer; - typedef value_type& reference; - typedef const value_type& const_reference; - typedef qptrdiff difference_type; - typedef iterator Iterator; - typedef const_iterator ConstIterator; - typedef int size_type; inline void push_back(const T &t) { append(t); } void push_back(T &&t) { append(std::move(t)); } void push_front(T &&t) { prepend(std::move(t)); } @@ -263,14 +388,14 @@ public: void pop_front() { removeFirst(); } inline bool empty() const { return d->size == 0; } - inline T& front() { return first(); } + inline reference front() { return first(); } inline const_reference front() const { return first(); } inline reference back() { return last(); } inline const_reference back() const { return last(); } void shrink_to_fit() { squeeze(); } // comfort - QVector &operator+=(const QVector &l); + QVector &operator+=(const QVector &l) { append(l.cbegin(), l.cend()); return *this; } inline QVector operator+(const QVector &l) const { QVector n = *this; n += l; return n; } inline QVector &operator+=(const T &t) @@ -284,34 +409,12 @@ public: inline QVector &operator<<(T &&t) { append(std::move(t)); return *this; } -#if QT_VERSION < QT_VERSION_CHECK(6,0,0) - Q_DECL_DEPRECATED_X("Use QVector(vector.begin(), vector.end()) instead.") - static inline QVector fromStdVector(const std::vector &vector) - { return QVector(vector.begin(), vector.end()); } - Q_DECL_DEPRECATED_X("Use std::vector(vector.begin(), vector.end()) instead.") - inline std::vector toStdVector() const - { return std::vector(d->begin(), d->end()); } -#endif - // Consider deprecating in 6.4 or later static QVector fromList(const QVector &list) { return list; } QVector toList() const { return *this; } static inline QVector fromVector(const QVector &vector) { return vector; } inline QVector toVector() const { return *this; } - -private: - void realloc(int alloc, QArrayData::ArrayOptions options); - void freeData(Data *d); - void defaultConstruct(T *from, T *to); - void copyConstruct(const T *srcFrom, const T *srcTo, T *dstFrom); - void destruct(T *from, T *to); - bool isValidIterator(const iterator &i) const - { - const std::less less = {}; - return !less(d->end(), i) && !less(i, d->begin()); - } - class AlignmentDummy { Data header; T array[1]; }; }; #if defined(__cpp_deduction_guides) && __cpp_deduction_guides >= 201606 @@ -321,153 +424,86 @@ template QVector; #endif -#ifdef Q_CC_MSVC -// behavior change: an object of POD type constructed with an initializer of the form () -// will be default-initialized -# pragma warning ( push ) -# pragma warning ( disable : 4345 ) -# pragma warning(disable : 4127) // conditional expression is constant -#endif - template -void QVector::defaultConstruct(T *from, T *to) +inline void QVector::resize_internal(int newSize, Qt::Initialization) { - if (QTypeInfo::isComplex) { - while (from != to) { - new (from++) T(); + Q_ASSERT(newSize >= 0); + + if (d->needsDetach() || newSize > capacity()) { + // must allocate memory + DataPointer detached(Data::allocate(d->detachCapacity(newSize), + d->detachFlags())); + if (size() && newSize) { + detached->copyAppend(constBegin(), constBegin() + qMin(newSize, size())); } - } else { - ::memset(static_cast(from), 0, (to - from) * sizeof(T)); + d.swap(detached); } -} -template -void QVector::copyConstruct(const T *srcFrom, const T *srcTo, T *dstFrom) -{ - if (QTypeInfo::isComplex) { - while (srcFrom != srcTo) - new (dstFrom++) T(*srcFrom++); - } else { - ::memcpy(static_cast(dstFrom), static_cast(srcFrom), (srcTo - srcFrom) * sizeof(T)); - } -} - -template -void QVector::destruct(T *from, T *to) -{ - if (QTypeInfo::isComplex) { - while (from != to) { - from++->~T(); - } - } -} - -template -inline QVector::QVector(const QVector &v) -{ - if (v.d->ref()) { - d = v.d; - } else { - if (v.d->flags & Data::CapacityReserved) { - d = Data::allocate(v.d->allocatedCapacity()).first; - Q_CHECK_PTR(d); - d->flags |= Data::CapacityReserved; - } else { - d = Data::allocate(v.d->size).first; - Q_CHECK_PTR(d); - } - if (v.d->size) { - copyConstruct(v.d->begin(), v.d->end(), d->begin()); - d->size = v.d->size; - } - } -} - -#if defined(Q_CC_MSVC) -#pragma warning( pop ) -#endif - -template -void QVector::detach() -{ - // ### check whether this is still required - if (d->isStatic()) - return; - - if (d->needsDetach()) { - realloc(d->allocatedCapacity(), d->detachFlags()); - Q_ASSERT(isDetached()); - } + if (newSize < size()) + d->truncate(newSize); } template void QVector::reserve(int asize) { - if (asize > int(d->allocatedCapacity())) - realloc(asize, typename Data::ArrayOptions(d->flags | Data::CapacityReserved)); - else if (isDetached()) - d->flags |= Data::CapacityReserved; - Q_ASSERT(int(d->allocatedCapacity()) >= asize); + // capacity() == 0 for immutable data, so this will force a detaching below + if (asize <= capacity()) { + if (d->flags() & Data::CapacityReserved) + return; // already reserved, don't shrink + if (!d->isShared()) { + // accept current allocation, don't shrink + d->flags() |= Data::CapacityReserved; + return; + } + } + + DataPointer detached(Data::allocate(qMax(asize, size()), + d->detachFlags() | Data::CapacityReserved)); + detached->copyAppend(constBegin(), constEnd()); + d.swap(detached); } template -void QVector::resize(int asize) +inline void QVector::squeeze() { - if (asize == d->size) - return detach(); - int oldAlloc = d->allocatedCapacity(); - if (asize > oldAlloc || !isDetached()) { // there is not enough space - QArrayData::ArrayOptions opt = d->detachFlags(); - if (asize > oldAlloc) - opt |= QArrayData::GrowsForward; - realloc(qMax(oldAlloc, asize), opt); + if (d->needsDetach() || size() != capacity()) { + // must allocate memory + DataPointer detached(Data::allocate(size(), d->detachFlags() & ~Data::CapacityReserved)); + if (size()) { + detached->copyAppend(constBegin(), constEnd()); + } + d.swap(detached); } - if (asize < d->size) - destruct(begin() + asize, end()); - else - defaultConstruct(end(), begin() + asize); - d->size = asize; } -template -inline void QVector::clear() -{ - if (!d->size) - return; - destruct(begin(), end()); - d->size = 0; -} -template -inline const T &QVector::at(int i) const -{ Q_ASSERT_X(i >= 0 && i < d->size, "QVector::at", "index out of range"); - return d->begin()[i]; } -template -inline const T &QVector::operator[](int i) const -{ Q_ASSERT_X(i >= 0 && i < d->size, "QVector::operator[]", "index out of range"); - return d->begin()[i]; } -template -inline T &QVector::operator[](int i) -{ Q_ASSERT_X(i >= 0 && i < d->size, "QVector::operator[]", "index out of range"); - return data()[i]; } -template -inline void QVector::insert(int i, const T &t) -{ Q_ASSERT_X(i >= 0 && i <= d->size, "QVector::insert", "index out of range"); - insert(begin() + i, 1, t); } -template -inline void QVector::insert(int i, int n, const T &t) -{ Q_ASSERT_X(i >= 0 && i <= d->size, "QVector::insert", "index out of range"); - insert(begin() + i, n, t); } -template -inline void QVector::insert(int i, T &&t) -{ Q_ASSERT_X(i >= 0 && i <= d->size, "QVector::insert", "index out of range"); - insert(begin() + i, std::move(t)); } + template inline void QVector::remove(int i, int n) -{ Q_ASSERT_X(i >= 0 && n >= 0 && i + n <= d->size, "QVector::remove", "index out of range"); - erase(d->begin() + i, d->begin() + i + n); } -template -inline void QVector::remove(int i) -{ Q_ASSERT_X(i >= 0 && i < d->size, "QVector::remove", "index out of range"); - erase(d->begin() + i, d->begin() + i + 1); } +{ + Q_ASSERT_X(size_t(i) + size_t(n) <= size_t(d->size), "QVector::remove", "index out of range"); + Q_ASSERT_X(n >= 0, "QVector::remove", "invalid count"); + + if (n == 0) + return; + + const size_t newSize = size() - n; + if (d->needsDetach() || + ((d->flags() & Data::CapacityReserved) == 0 + && newSize < d->allocatedCapacity()/2)) { + // allocate memory + DataPointer detached(Data::allocate(d->detachCapacity(newSize), + d->detachFlags() & ~(Data::GrowsBackwards | Data::GrowsForward))); + const_iterator where = constBegin() + i; + if (newSize) { + detached->copyAppend(constBegin(), where); + detached->copyAppend(where + n, constEnd()); + } + d.swap(detached); + } else { + // we're detached and we can just move data around + d->erase(d->begin() + i, d->begin() + i + n); + } +} + template inline void QVector::prepend(const T &t) { insert(begin(), 1, t); } @@ -475,304 +511,106 @@ template inline void QVector::prepend(T &&t) { insert(begin(), std::move(t)); } -template -inline void QVector::replace(int i, const T &t) -{ - Q_ASSERT_X(i >= 0 && i < d->size, "QVector::replace", "index out of range"); - const T copy(t); - data()[i] = copy; -} - -template -QVector &QVector::operator=(const QVector &v) -{ - if (v.d != d) { - QVector tmp(v); - tmp.swap(*this); - } - return *this; -} - -template -QVector::QVector(int asize) -{ - Q_ASSERT_X(asize >= 0, "QVector::QVector", "Size must be greater than or equal to 0."); - if (Q_LIKELY(asize > 0)) { - d = Data::allocate(asize).first; - Q_CHECK_PTR(d); - d->size = asize; - defaultConstruct(d->begin(), d->end()); - } else { - d = Data::sharedNull(); - } -} - -template -QVector::QVector(int asize, const T &t) -{ - Q_ASSERT_X(asize >= 0, "QVector::QVector", "Size must be greater than or equal to 0."); - if (asize > 0) { - d = Data::allocate(asize).first; - Q_CHECK_PTR(d); - d->size = asize; - T* i = d->end(); - while (i != d->begin()) - new (--i) T(t); - } else { - d = Data::sharedNull(); - } -} - -#if defined(Q_CC_MSVC) -QT_WARNING_PUSH -QT_WARNING_DISABLE_MSVC(4127) // conditional expression is constant -#endif // Q_CC_MSVC - -template -QVector::QVector(std::initializer_list args) -{ - if (args.size() > 0) { - d = Data::allocate(args.size()).first; - Q_CHECK_PTR(d); - // std::initializer_list::iterator is guaranteed to be - // const T* ([support.initlist]/1), so can be memcpy'ed away from by copyConstruct - copyConstruct(args.begin(), args.end(), d->begin()); - d->size = int(args.size()); - } else { - d = Data::sharedNull(); - } -} - -template -QVector &QVector::operator=(std::initializer_list args) -{ - QVector tmp(args); - tmp.swap(*this); - return *this; -} - -#if defined(Q_CC_MSVC) -QT_WARNING_POP -#endif // Q_CC_MSVC - -template -template > -QVector::QVector(InputIterator first, InputIterator last) - : QVector() -{ - QtPrivate::reserveIfForwardIterator(this, first, last); - std::copy(first, last, std::back_inserter(*this)); -} - -template -void QVector::freeData(Data *x) -{ - destruct(x->begin(), x->end()); - Data::deallocate(x); -} - -#if defined(Q_CC_MSVC) -QT_WARNING_PUSH -QT_WARNING_DISABLE_MSVC(4127) // conditional expression is constant -#endif - template -void QVector::realloc(int aalloc, QArrayData::ArrayOptions options) +inline T QVector::value(int i, const T &defaultValue) const { - Q_ASSERT(aalloc >= d->size); - Data *x = d; - - const bool isShared = d->isShared(); - - QT_TRY { - // allocate memory - auto pair = Data::allocate(aalloc, options); - x = pair.first; - Q_CHECK_PTR(x); - // aalloc is bigger then 0 so it is not [un]sharedEmpty - Q_ASSERT(!x->isStatic()); - x->size = d->size; - - T *srcBegin = d->begin(); - T *srcEnd = d->end(); - T *dst = x->begin(); - - if (!QTypeInfoQuery::isRelocatable || (isShared && QTypeInfo::isComplex)) { - QT_TRY { - if (isShared || !std::is_nothrow_move_constructible::value) { - // we can not move the data, we need to copy construct it - while (srcBegin != srcEnd) - new (dst++) T(*srcBegin++); - } else { - while (srcBegin != srcEnd) - new (dst++) T(std::move(*srcBegin++)); - } - } QT_CATCH (...) { - // destruct already copied objects - destruct(x->begin(), dst); - QT_RETHROW; - } - } else { - ::memcpy(static_cast(dst), static_cast(srcBegin), (srcEnd - srcBegin) * sizeof(T)); - dst += srcEnd - srcBegin; - } - - } QT_CATCH (...) { - Data::deallocate(x); - QT_RETHROW; - } - - Q_ASSERT(d != x); - if (!d->deref()) { - if (!QTypeInfoQuery::isRelocatable || !aalloc || (isShared && QTypeInfo::isComplex)) { - // data was copy constructed, we need to call destructors - // or if !alloc we did nothing to the old 'd'. - freeData(d); - } else { - Data::deallocate(d); - } - } - d = x; - - Q_ASSERT(d->data()); - Q_ASSERT(uint(d->size) <= d->allocatedCapacity()); - Q_ASSERT(d != Data::sharedNull()); - Q_ASSERT(d->allocatedCapacity() >= uint(aalloc)); -} - -#if defined(Q_CC_MSVC) -QT_WARNING_POP -#endif - -template -Q_OUTOFLINE_TEMPLATE T QVector::value(int i) const -{ - if (uint(i) >= uint(d->size)) { - return T(); - } - return d->begin()[i]; -} -template -Q_OUTOFLINE_TEMPLATE T QVector::value(int i, const T &defaultValue) const -{ - return uint(i) >= uint(d->size) ? defaultValue : d->begin()[i]; + return size_t(i) < size_t(d->size) ? at(i) : defaultValue; } template -void QVector::append(const T &t) +inline void QVector::append(const_iterator i1, const_iterator i2) { - const bool isTooSmall = d->size >= int(d->allocatedCapacity()); - QArrayData::ArrayOptions opt = d->detachFlags(); - if (!isDetached() || isTooSmall) { - T copy(t); + if (i1 == i2) + return; + const size_t newSize = size() + std::distance(i1, i2); + if (d->needsDetach() || newSize > d->allocatedCapacity()) { + DataPointer detached(Data::allocate(d->detachCapacity(newSize), + d->detachFlags() | Data::GrowsForward)); + detached->copyAppend(constBegin(), constEnd()); + detached->copyAppend(i1, i2); + d.swap(detached); + } else { + // we're detached and we can just move data around + d->copyAppend(i1, i2); + } +} + +template +inline void QVector::append(value_type &&t) +{ + const size_t newSize = size() + 1; + const bool isTooSmall = newSize > d->allocatedCapacity(); + const bool isOverlapping = std::addressof(*d->begin()) <= std::addressof(t) + && std::addressof(t) < std::addressof(*d->end()); + if (isTooSmall || d->needsDetach() || Q_UNLIKELY(isOverlapping)) { + typename Data::ArrayOptions flags = d->detachFlags(); if (isTooSmall) - opt |= QArrayData::GrowsForward; - realloc(isTooSmall ? d->size + 1 : d->allocatedCapacity(), opt); - - if (QTypeInfo::isComplex) - new (d->end()) T(std::move(copy)); - else - *d->end() = std::move(copy); - + flags |= Data::GrowsForward; + DataPointer detached(Data::allocate(d->detachCapacity(newSize), flags)); + detached->copyAppend(constBegin(), constEnd()); + detached->moveAppend(std::addressof(t), std::addressof(t) + 1); + d.swap(detached); } else { - if (QTypeInfo::isComplex) - new (d->end()) T(t); - else - *d->end() = t; + // we're detached and we can just move data around + d->moveAppend(std::addressof(t), std::addressof(t) + 1); } - ++d->size; } template -void QVector::append(T &&t) +inline typename QVector::iterator +QVector::insert(int i, int n, parameter_type t) { - const bool isTooSmall = uint(d->size + 1) > d->allocatedCapacity(); - if (!isDetached() || isTooSmall) { - QArrayData::ArrayOptions opt(isTooSmall ? QArrayData::GrowsForward : QArrayData::DefaultAllocationFlags); - realloc(isTooSmall ? d->size + 1 : d->allocatedCapacity(), opt); - } + Q_ASSERT_X(size_t(i) <= size_t(d->size), "QVector::insert", "index out of range"); - new (d->end()) T(std::move(t)); + // we don't have a quick exit for n == 0 + // it's not worth wasting CPU cycles for that - ++d->size; -} + const size_t newSize = size() + n; + if (d->needsDetach() || newSize > d->allocatedCapacity()) { + typename Data::ArrayOptions flags = d->detachFlags() | Data::GrowsForward; + if (size_t(i) <= newSize / 4) + flags |= Data::GrowsBackwards; -template -void QVector::removeLast() -{ - Q_ASSERT(!isEmpty()); - Q_ASSERT(d->allocatedCapacity()); - - if (d->needsDetach()) - detach(); - --d->size; - if (QTypeInfo::isComplex) - (d->data() + d->size)->~T(); -} - -template -typename QVector::iterator QVector::insert(iterator before, size_type n, const T &t) -{ - Q_ASSERT_X(isValidIterator(before), "QVector::insert", "The specified iterator argument 'before' is invalid"); - - const auto offset = std::distance(d->begin(), before); - if (n != 0) { - const T copy(t); - if (!isDetached() || d->size + n > int(d->allocatedCapacity())) - realloc(d->size + n, QArrayData::GrowsForward); - if (!QTypeInfoQuery::isRelocatable) { - T *b = d->end(); - T *i = d->end() + n; - while (i != b) - new (--i) T; - i = d->end(); - T *j = i + n; - b = d->begin() + offset; - while (i != b) - *--j = *--i; - i = b+n; - while (i != b) - *--i = copy; - } else { - T *b = d->begin() + offset; - T *i = b + n; - memmove(static_cast(i), static_cast(b), (d->size - offset) * sizeof(T)); - while (i != b) - new (--i) T(copy); - } - d->size += n; - } - return d->begin() + offset; -} - -template -typename QVector::iterator QVector::insert(iterator before, T &&t) -{ - Q_ASSERT_X(isValidIterator(before), "QVector::insert", "The specified iterator argument 'before' is invalid"); - - const auto offset = std::distance(d->begin(), before); - if (!isDetached() || d->size + 1 > int(d->allocatedCapacity())) - realloc(d->size + 1, QArrayData::GrowsForward); - if (!QTypeInfoQuery::isRelocatable) { - T *i = d->end(); - T *j = i + 1; - T *b = d->begin() + offset; - // The new end-element needs to be constructed, the rest must be move assigned - if (i != b) { - new (--j) T(std::move(*--i)); - while (i != b) - *--j = std::move(*--i); - *b = std::move(t); - } else { - new (b) T(std::move(t)); - } + DataPointer detached(Data::allocate(d->detachCapacity(newSize), flags)); + const_iterator where = constBegin() + i; + detached->copyAppend(constBegin(), where); + detached->copyAppend(n, t); + detached->copyAppend(where, constEnd()); + d.swap(detached); } else { - T *b = d->begin() + offset; - memmove(static_cast(b + 1), static_cast(b), (d->size - offset) * sizeof(T)); - new (b) T(std::move(t)); + // we're detached and we can just move data around + if (i == size()) { + d->copyAppend(n, t); + } else { + T copy(t); + d->insert(d.begin() + i, n, copy); + } } - d->size += 1; - return d->begin() + offset; + return d.begin() + i; +} + +template +inline typename QVector::iterator +QVector::insert(int i, T &&t) +{ + Q_ASSERT_X(size_t(i) <= size_t(d->size), "QVector::insert", "index out of range"); + + const size_t newSize = size() + 1; + if (d->needsDetach() || newSize > d->allocatedCapacity()) { + typename Data::ArrayOptions flags = d->detachFlags() | Data::GrowsForward; + if (size_t(i) <= newSize / 4) + flags |= Data::GrowsBackwards; + + DataPointer detached(Data::allocate(d->detachCapacity(newSize), flags)); + const_iterator where = constBegin() + i; + detached->copyAppend(constBegin(), where); + detached->moveAppend(std::addressof(t), std::addressof(t) + 1); + detached->copyAppend(where, constEnd()); + d.swap(detached); + } else { + d->insert(d.begin() + i, std::move(t)); + } + return d.begin() + i; } template @@ -780,102 +618,33 @@ typename QVector::iterator QVector::erase(iterator abegin, iterator aend) { Q_ASSERT_X(isValidIterator(abegin), "QVector::erase", "The specified iterator argument 'abegin' is invalid"); Q_ASSERT_X(isValidIterator(aend), "QVector::erase", "The specified iterator argument 'aend' is invalid"); + Q_ASSERT(aend >= abegin); - const auto itemsToErase = aend - abegin; + // d.begin() so we don't detach just yet + int i = std::distance(d.begin(), abegin); + int n = std::distance(abegin, aend); + remove(i, n); - if (!itemsToErase) - return abegin; - - Q_ASSERT(abegin >= d->begin()); - Q_ASSERT(aend <= d->end()); - Q_ASSERT(abegin <= aend); - - const auto itemsUntouched = abegin - d->begin(); - - // FIXME we could do a proper realloc, which copy constructs only needed data. - // FIXME we are about to delete data - maybe it is good time to shrink? - // FIXME the shrink is also an issue in removeLast, that is just a copy + reduce of this. - if (d->allocatedCapacity()) { - detach(); - abegin = d->begin() + itemsUntouched; - aend = abegin + itemsToErase; - if (!QTypeInfoQuery::isRelocatable) { - iterator moveBegin = abegin + itemsToErase; - iterator moveEnd = d->end(); - while (moveBegin != moveEnd) { - if (QTypeInfo::isComplex) - static_cast(abegin)->~T(); - new (abegin++) T(*moveBegin++); - } - if (abegin < d->end()) { - // destroy rest of instances - destruct(abegin, d->end()); - } - } else { - destruct(abegin, aend); - // QTBUG-53605: static_cast masks clang errors of the form - // error: destination for this 'memmove' call is a pointer to class containing a dynamic class - // FIXME maybe use std::is_polymorphic (as soon as allowed) to avoid the memmove - memmove(static_cast(abegin), static_cast(aend), - (d->size - itemsToErase - itemsUntouched) * sizeof(T)); - } - d->size -= int(itemsToErase); - } - return d->begin() + itemsUntouched; + return d.begin() + i; } template -bool QVector::operator==(const QVector &v) const +inline QVector &QVector::fill(parameter_type t, int newSize) { - if (d == v.d) - return true; - if (d->size != v.d->size) - return false; - const T *vb = v.d->begin(); - const T *b = d->begin(); - const T *e = d->end(); - return std::equal(b, e, QT_MAKE_CHECKED_ARRAY_ITERATOR(vb, v.d->size)); -} - -template -QVector &QVector::fill(const T &from, int asize) -{ - const T copy(from); - resize(asize < 0 ? d->size : asize); - if (d->size) { - T *i = d->end(); - T *b = d->begin(); - while (i != b) - *--i = copy; - } - return *this; -} - -template -QVector &QVector::operator+=(const QVector &l) -{ - if (d->size == 0) { - *this = l; + if (newSize == -1) + newSize = size(); + if (d->needsDetach() || newSize > capacity()) { + // must allocate memory + DataPointer detached(Data::allocate(d->detachCapacity(newSize), + d->detachFlags())); + detached->copyAppend(newSize, t); + d.swap(detached); } else { - uint newSize = d->size + l.d->size; - const bool isTooSmall = newSize > d->allocatedCapacity(); - if (!isDetached() || isTooSmall) { - QArrayData::ArrayOptions opt(isTooSmall ? d->flags | QArrayData::GrowsForward : d->flags); - realloc(isTooSmall ? newSize : d->allocatedCapacity(), opt); - } - - if (l.d->size) { - T *w = d->begin() + newSize; - T *i = l.d->end(); - T *b = l.d->begin(); - while (i != b) { - if (QTypeInfo::isComplex) - new (--w) T(*--i); - else - *--w = *--i; - } - d->size = newSize; - } + // we're detached + const T copy(t); + d->assign(d.begin(), d.begin() + qMin(size(), newSize), t); + if (newSize > size()) + d->copyAppend(newSize - size(), copy); } return *this; } @@ -916,54 +685,35 @@ int lastIndexOf(const QVector &vector, const U &u, int from) } template -int QVector::indexOf(const T &t, int from) const +int QVector::indexOf(const T &t, int from) const noexcept { return QtPrivate::indexOf(*this, t, from); } template -int QVector::lastIndexOf(const T &t, int from) const +int QVector::lastIndexOf(const T &t, int from) const noexcept { return QtPrivate::lastIndexOf(*this, t, from); } template -bool QVector::contains(const T &t) const -{ - const T *b = d->begin(); - const T *e = d->end(); - return std::find(b, e, t) != e; -} - -template -int QVector::count(const T &t) const -{ - const T *b = d->begin(); - const T *e = d->end(); - return int(std::count(b, e, t)); -} - -template -Q_OUTOFLINE_TEMPLATE QVector QVector::mid(int pos, int len) const +inline QVector QVector::mid(int pos, int len) const { using namespace QtPrivate; - switch (QContainerImplHelper::mid(d->size, &pos, &len)) { + switch (QContainerImplHelper::mid(d.size, &pos, &len)) { case QContainerImplHelper::Null: case QContainerImplHelper::Empty: - return QVector(); + return QVector(); case QContainerImplHelper::Full: return *this; case QContainerImplHelper::Subset: break; } - QVector midResult; - midResult.realloc(len, QArrayData::DefaultAllocationFlags); - T *srcFrom = d->begin() + pos; - T *srcTo = d->begin() + pos + len; - midResult.copyConstruct(srcFrom, srcTo, midResult.data()); - midResult.d->size = len; - return midResult; + // Allocate memory + DataPointer copied(Data::allocate(len)); + copied->copyAppend(constBegin() + pos, constBegin() + pos + len); + return copied; } Q_DECLARE_SEQUENTIAL_ITERATOR(Vector) diff --git a/src/corelib/tools/tools.pri b/src/corelib/tools/tools.pri index 40c84157cd..a1b243dd5f 100644 --- a/src/corelib/tools/tools.pri +++ b/src/corelib/tools/tools.pri @@ -47,7 +47,6 @@ HEADERS += \ tools/qvector.h \ tools/qversionnumber.h - SOURCES += \ tools/qarraydata.cpp \ tools/qbitarray.cpp \ diff --git a/src/sql/kernel/qsqlindex.cpp b/src/sql/kernel/qsqlindex.cpp index 60b0ef6965..5781f24b5c 100644 --- a/src/sql/kernel/qsqlindex.cpp +++ b/src/sql/kernel/qsqlindex.cpp @@ -44,9 +44,6 @@ QT_BEGIN_NAMESPACE -// ### Qt 6: remove the static assertion, the 'sorts' field was changed from QList to QVector in Qt 5.6 -Q_STATIC_ASSERT((sizeof(QList) == sizeof(QVector))); - /*! \class QSqlIndex \brief The QSqlIndex class provides functions to manipulate and diff --git a/tests/auto/corelib/tools/qvector/tst_qvector.cpp b/tests/auto/corelib/tools/qvector/tst_qvector.cpp index ed952954cd..7a69e844d4 100644 --- a/tests/auto/corelib/tools/qvector/tst_qvector.cpp +++ b/tests/auto/corelib/tools/qvector/tst_qvector.cpp @@ -104,7 +104,7 @@ private: static void check(const State state1, const State state2) { - QCOMPARE(state1, state2); + QCOMPARE(int(state1), int(state2)); } }; @@ -174,7 +174,7 @@ private: { // check if c object has been moved QCOMPARE(c, c->that); - QCOMPARE(c->state, Constructed); + QCOMPARE(int(c->state), int(Constructed)); } }; QAtomicInt Custom::counter = 0; @@ -724,16 +724,12 @@ void tst_QVector::capacity() const myvec.remove(3); myvec.remove(3); myvec.remove(3); - // TODO: is this a safe assumption? presumably it won't release memory until shrink(), but can we asser that is true? - QVERIFY(myvec.capacity() >= 6); myvec.squeeze(); QVERIFY(myvec.capacity() >= 3); myvec.remove(0); myvec.remove(0); myvec.remove(0); - // TODO: as above note - QVERIFY(myvec.capacity() >= 3); myvec.squeeze(); QVERIFY(myvec.capacity() == 0); } diff --git a/tests/auto/other/toolsupport/tst_toolsupport.cpp b/tests/auto/other/toolsupport/tst_toolsupport.cpp index ab7bca8322..2440b4a5e9 100644 --- a/tests/auto/other/toolsupport/tst_toolsupport.cpp +++ b/tests/auto/other/toolsupport/tst_toolsupport.cpp @@ -97,14 +97,14 @@ void tst_toolsupport::offsets_data() { QTestData &data = QTest::newRow("sizeof(QObjectData)") << sizeof(QObjectData); - data << 28 << 48; // vptr + 3 ptr + 2 int + ptr + data << 36 << 64; // vptr + 2 ptr + (2*ptr + int) + 2 int + ptr } #if RUN_MEMBER_OFFSET_TEST { QTestData &data = QTest::newRow("QObjectPrivate::extraData") << pmm_to_offsetof(&QObjectPrivate::extraData); - data << 28 << 48; // sizeof(QObjectData) + data << 36 << 64; // sizeof(QObjectData) } { @@ -126,9 +126,9 @@ void tst_toolsupport::offsets_data() #ifdef Q_PROCESSOR_X86 // x86 32-bit has weird alignment rules. Refer to QtPrivate::AlignOf in // qglobal.h for more details. - data << 152 << 224; + data << 176 << 272; #else - data << 156 << 224; + data << 180 << 272; #endif } #endif