blob: 62fbdb4a2ad0cd29ff081e6cc754f7545488ea73 [file] [log] [blame]
/****************************************************************************
**
** Copyright (C) 2016 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the QtCore module of the Qt Toolkit.
**
** $QT_BEGIN_LICENSE:LGPL$
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and The Qt Company. For licensing terms
** and conditions see https://www.qt.io/terms-conditions. For further
** information use the contact form at https://www.qt.io/contact-us.
**
** GNU Lesser General Public License Usage
** Alternatively, this file may be used under the terms of the GNU Lesser
** General Public License version 3 as published by the Free Software
** Foundation and appearing in the file LICENSE.LGPL3 included in the
** packaging of this file. Please review the following information to
** ensure the GNU Lesser General Public License version 3 requirements
** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU
** General Public License version 2.0 or (at your option) the GNU General
** Public license version 3 or any later version approved by the KDE Free
** Qt Foundation. The licenses are as published by the Free Software
** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
** included in the packaging of this file. Please review the following
** information to ensure the GNU General Public License requirements will
** be met: https://www.gnu.org/licenses/gpl-2.0.html and
** https://www.gnu.org/licenses/gpl-3.0.html.
**
** $QT_END_LICENSE$
**
****************************************************************************/
#ifndef QVECTOR_H
#define QVECTOR_H
#include <QtCore/qalgorithms.h>
#include <QtCore/qiterator.h>
#include <QtCore/qrefcount.h>
#include <QtCore/qarraydata.h>
#include <QtCore/qhashfunctions.h>
#include <QtCore/qcontainertools_impl.h>
#include <iterator>
#include <initializer_list>
#if QT_VERSION < QT_VERSION_CHECK(6,0,0)
#include <vector>
#endif
#include <stdlib.h>
#include <string.h>
#include <algorithm>
QT_BEGIN_NAMESPACE
template <typename T>
class QVector
{
typedef QTypedArrayData<T> Data;
Data *d;
public:
inline QVector() noexcept : d(Data::sharedNull()) { }
explicit QVector(int size);
QVector(int size, const T &t);
inline QVector(const QVector<T> &v);
inline ~QVector() { if (!d->ref.deref()) freeData(d); }
QVector<T> &operator=(const QVector<T> &v);
QVector(QVector<T> &&other) noexcept : d(other.d) { other.d = Data::sharedNull(); }
QVector<T> &operator=(QVector<T> &&other) noexcept
{ QVector moved(std::move(other)); swap(moved); return *this; }
void swap(QVector<T> &other) noexcept { qSwap(d, other.d); }
inline QVector(std::initializer_list<T> args);
QVector<T> &operator=(std::initializer_list<T> args);
template <typename InputIterator, QtPrivate::IfIsInputIterator<InputIterator> = true>
inline QVector(InputIterator first, InputIterator last);
explicit QVector(QArrayDataPointerRef<T> ref) noexcept : d(ref.ptr) {}
bool operator==(const QVector<T> &v) const;
inline bool operator!=(const QVector<T> &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 int(d->alloc); }
void reserve(int size);
inline void squeeze()
{
if (d->size < int(d->alloc)) {
if (!d->size) {
*this = QVector<T>();
return;
}
realloc(d->size);
}
if (d->capacityReserved) {
// capacity reserved in a read only memory would be useless
// this checks avoid writing to such memory.
d->capacityReserved = 0;
}
}
inline void detach();
inline bool isDetached() const { return !d->ref.isShared(); }
#if !defined(QT_NO_UNSHARABLE_CONTAINERS)
inline void setSharable(bool sharable)
{
if (sharable == d->ref.isSharable())
return;
if (!sharable)
detach();
if (d == Data::unsharableEmpty()) {
if (sharable)
d = Data::sharedNull();
} else {
d->ref.setSharable(sharable);
}
Q_ASSERT(d->ref.isSharable() == sharable);
}
#endif
inline bool isSharedWith(const QVector<T> &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();
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<T> &l) { *this += l; }
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; }
QVector<T> &fill(const T &t, int size = -1);
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;
// QList compatibility
void removeAt(int i) { remove(i); }
int removeAll(const T &t)
{
const const_iterator ce = this->cend(), cit = std::find(this->cbegin(), ce, t);
if (cit == ce)
return 0;
// 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 int result = std::distance(it, e);
erase(it, e);
return result;
}
bool removeOne(const T &t)
{
const int i = indexOf(t);
if (i < 0)
return false;
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)
{
Q_ASSERT_X(from >= 0 && from < size(), "QVector::move(int,int)", "'from' is out-of-range");
Q_ASSERT_X(to >= 0 && to < size(), "QVector::move(int,int)", "'to' is out-of-range");
if (from == to) // don't detach when no-op
return;
detach();
T * const b = d->begin();
if (from < to)
std::rotate(b + from, b + from + 1, b + to + 1);
else
std::rotate(b + to, b + from, b + from + 1);
}
// STL-style
typedef typename Data::iterator iterator;
typedef typename Data::const_iterator const_iterator;
typedef std::reverse_iterator<iterator> reverse_iterator;
typedef std::reverse_iterator<const_iterator> const_reverse_iterator;
#if !defined(QT_STRICT_ITERATORS) || defined(Q_CLANG_QDOC)
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(); }
#else
inline iterator begin(iterator = iterator()) { detach(); return d->begin(); }
inline const_iterator begin(const_iterator = const_iterator()) const noexcept { return d->constBegin(); }
inline const_iterator cbegin(const_iterator = const_iterator()) const noexcept { return d->constBegin(); }
inline const_iterator constBegin(const_iterator = const_iterator()) const noexcept { return d->constBegin(); }
inline iterator end(iterator = iterator()) { detach(); return d->end(); }
inline const_iterator end(const_iterator = const_iterator()) const noexcept { return d->constEnd(); }
inline const_iterator cend(const_iterator = const_iterator()) const noexcept { return d->constEnd(); }
inline const_iterator constEnd(const_iterator = const_iterator()) const noexcept { return d->constEnd(); }
#endif
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(); }
inline T& last() { Q_ASSERT(!isEmpty()); return *(end()-1); }
inline const T &last() const { Q_ASSERT(!isEmpty()); return *(end()-1); }
inline const T &constLast() const { Q_ASSERT(!isEmpty()); return *(end()-1); }
inline bool startsWith(const T &t) const { return !isEmpty() && first() == t; }
inline bool endsWith(const T &t) const { return !isEmpty() && last() == t; }
QVector<T> mid(int pos, int len = -1) const;
T value(int i) const;
T value(int i, const T &defaultValue) const;
void swapItemsAt(int i, int j) {
Q_ASSERT_X(i >= 0 && i < size() && j >= 0 && j < size(),
"QVector<T>::swap", "index out of range");
detach();
qSwap(d->begin()[i], d->begin()[j]);
}
// 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)); }
inline void push_front(const T &t) { prepend(t); }
void pop_back() { removeLast(); }
void pop_front() { removeFirst(); }
inline bool empty() const
{ return d->size == 0; }
inline T& 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<T> &operator+=(const QVector<T> &l);
inline QVector<T> operator+(const QVector<T> &l) const
{ QVector n = *this; n += l; return n; }
inline QVector<T> &operator+=(const T &t)
{ append(t); return *this; }
inline QVector<T> &operator<< (const T &t)
{ append(t); return *this; }
inline QVector<T> &operator<<(const QVector<T> &l)
{ *this += l; return *this; }
inline QVector<T> &operator+=(T &&t)
{ append(std::move(t)); return *this; }
inline QVector<T> &operator<<(T &&t)
{ append(std::move(t)); return *this; }
static QVector<T> fromList(const QList<T> &list);
QList<T> toList() const;
#if QT_VERSION < QT_VERSION_CHECK(6,0,0)
Q_DECL_DEPRECATED_X("Use QVector<T>(vector.begin(), vector.end()) instead.")
static inline QVector<T> fromStdVector(const std::vector<T> &vector)
{ return QVector<T>(vector.begin(), vector.end()); }
Q_DECL_DEPRECATED_X("Use std::vector<T>(vector.begin(), vector.end()) instead.")
inline std::vector<T> toStdVector() const
{ return std::vector<T>(d->begin(), d->end()); }
#endif
private:
// ### Qt6: remove methods, they are unused
void reallocData(const int size, const int alloc, QArrayData::AllocationOptions options = QArrayData::Default);
void reallocData(const int sz) { reallocData(sz, d->alloc); }
void realloc(int alloc, QArrayData::AllocationOptions options = QArrayData::Default);
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<const T*> 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
template <typename InputIterator,
typename ValueType = typename std::iterator_traits<InputIterator>::value_type,
QtPrivate::IfIsInputIterator<InputIterator> = true>
QVector(InputIterator, InputIterator) -> QVector<ValueType>;
#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 <typename T>
void QVector<T>::defaultConstruct(T *from, T *to)
{
if (QTypeInfo<T>::isComplex) {
while (from != to) {
new (from++) T();
}
} else {
::memset(static_cast<void *>(from), 0, (to - from) * sizeof(T));
}
}
template <typename T>
void QVector<T>::copyConstruct(const T *srcFrom, const T *srcTo, T *dstFrom)
{
if (QTypeInfo<T>::isComplex) {
while (srcFrom != srcTo)
new (dstFrom++) T(*srcFrom++);
} else {
::memcpy(static_cast<void *>(dstFrom), static_cast<const void *>(srcFrom), (srcTo - srcFrom) * sizeof(T));
}
}
template <typename T>
void QVector<T>::destruct(T *from, T *to)
{
if (QTypeInfo<T>::isComplex) {
while (from != to) {
from++->~T();
}
}
}
template <typename T>
inline QVector<T>::QVector(const QVector<T> &v)
{
if (v.d->ref.ref()) {
d = v.d;
} else {
if (v.d->capacityReserved) {
d = Data::allocate(v.d->alloc);
Q_CHECK_PTR(d);
d->capacityReserved = true;
} else {
d = Data::allocate(v.d->size);
Q_CHECK_PTR(d);
}
if (d->alloc) {
copyConstruct(v.d->begin(), v.d->end(), d->begin());
d->size = v.d->size;
}
}
}
#if defined(Q_CC_MSVC)
#pragma warning( pop )
#endif
template <typename T>
void QVector<T>::detach()
{
if (!isDetached()) {
#if !defined(QT_NO_UNSHARABLE_CONTAINERS)
if (!d->alloc)
d = Data::unsharableEmpty();
else
#endif
realloc(int(d->alloc));
}
Q_ASSERT(isDetached());
}
template <typename T>
void QVector<T>::reserve(int asize)
{
if (asize > int(d->alloc))
realloc(asize);
if (isDetached()
#if !defined(QT_NO_UNSHARABLE_CONTAINERS)
&& d != Data::unsharableEmpty()
#endif
)
d->capacityReserved = 1;
Q_ASSERT(capacity() >= asize);
}
template <typename T>
void QVector<T>::resize(int asize)
{
if (asize == d->size)
return detach();
if (asize > int(d->alloc) || !isDetached()) { // there is not enough space
QArrayData::AllocationOptions opt = asize > int(d->alloc) ? QArrayData::Grow : QArrayData::Default;
realloc(qMax(int(d->alloc), asize), opt);
}
if (asize < d->size)
destruct(begin() + asize, end());
else
defaultConstruct(end(), begin() + asize);
d->size = asize;
}
template <typename T>
inline void QVector<T>::clear()
{
if (!d->size)
return;
destruct(begin(), end());
d->size = 0;
}
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 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 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");
return data()[i]; }
template <typename T>
inline void QVector<T>::insert(int i, const T &t)
{ Q_ASSERT_X(i >= 0 && i <= d->size, "QVector<T>::insert", "index out of range");
insert(begin() + i, 1, t); }
template <typename T>
inline void QVector<T>::insert(int i, int n, const T &t)
{ Q_ASSERT_X(i >= 0 && i <= d->size, "QVector<T>::insert", "index out of range");
insert(begin() + i, n, t); }
template <typename T>
inline void QVector<T>::insert(int i, T &&t)
{ Q_ASSERT_X(i >= 0 && i <= d->size, "QVector<T>::insert", "index out of range");
insert(begin() + i, std::move(t)); }
template <typename T>
inline void QVector<T>::remove(int i, int n)
{ Q_ASSERT_X(i >= 0 && n >= 0 && i + n <= d->size, "QVector<T>::remove", "index out of range");
erase(d->begin() + i, d->begin() + i + n); }
template <typename T>
inline void QVector<T>::remove(int i)
{ Q_ASSERT_X(i >= 0 && i < d->size, "QVector<T>::remove", "index out of range");
erase(d->begin() + i, d->begin() + i + 1); }
template <typename T>
inline void QVector<T>::prepend(const T &t)
{ insert(begin(), 1, t); }
template <typename T>
inline void QVector<T>::prepend(T &&t)
{ insert(begin(), std::move(t)); }
template <typename T>
inline void QVector<T>::replace(int i, const T &t)
{
Q_ASSERT_X(i >= 0 && i < d->size, "QVector<T>::replace", "index out of range");
const T copy(t);
data()[i] = copy;
}
template <typename T>
QVector<T> &QVector<T>::operator=(const QVector<T> &v)
{
if (v.d != d) {
QVector<T> tmp(v);
tmp.swap(*this);
}
return *this;
}
template <typename T>
QVector<T>::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);
Q_CHECK_PTR(d);
d->size = asize;
defaultConstruct(d->begin(), d->end());
} else {
d = Data::sharedNull();
}
}
template <typename T>
QVector<T>::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);
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 <typename T>
QVector<T>::QVector(std::initializer_list<T> args)
{
if (args.size() > 0) {
d = Data::allocate(args.size());
Q_CHECK_PTR(d);
// std::initializer_list<T>::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 <typename T>
QVector<T> &QVector<T>::operator=(std::initializer_list<T> args)
{
QVector<T> tmp(args);
tmp.swap(*this);
return *this;
}
#if defined(Q_CC_MSVC)
QT_WARNING_POP
#endif // Q_CC_MSVC
template <typename T>
template <typename InputIterator, QtPrivate::IfIsInputIterator<InputIterator>>
QVector<T>::QVector(InputIterator first, InputIterator last)
: QVector()
{
QtPrivate::reserveIfForwardIterator(this, first, last);
std::copy(first, last, std::back_inserter(*this));
}
template <typename T>
void QVector<T>::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 <typename T>
void QVector<T>::reallocData(const int asize, const int aalloc, QArrayData::AllocationOptions options)
{
Q_ASSERT(asize >= 0 && asize <= aalloc);
Data *x = d;
const bool isShared = d->ref.isShared();
if (aalloc != 0) {
if (aalloc != int(d->alloc) || isShared) {
QT_TRY {
// allocate memory
x = Data::allocate(aalloc, options);
Q_CHECK_PTR(x);
// aalloc is bigger then 0 so it is not [un]sharedEmpty
#if !defined(QT_NO_UNSHARABLE_CONTAINERS)
Q_ASSERT(x->ref.isSharable() || options.testFlag(QArrayData::Unsharable));
#endif
Q_ASSERT(!x->ref.isStatic());
x->size = asize;
T *srcBegin = d->begin();
T *srcEnd = asize > d->size ? d->end() : d->begin() + asize;
T *dst = x->begin();
if (!QTypeInfoQuery<T>::isRelocatable || (isShared && QTypeInfo<T>::isComplex)) {
QT_TRY {
if (isShared || !std::is_nothrow_move_constructible<T>::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<void *>(dst), static_cast<void *>(srcBegin), (srcEnd - srcBegin) * sizeof(T));
dst += srcEnd - srcBegin;
// destruct unused / not moved data
if (asize < d->size)
destruct(d->begin() + asize, d->end());
}
if (asize > d->size) {
// construct all new objects when growing
if (!QTypeInfo<T>::isComplex) {
::memset(static_cast<void *>(dst), 0, (static_cast<T *>(x->end()) - dst) * sizeof(T));
} else {
QT_TRY {
while (dst != x->end())
new (dst++) T();
} QT_CATCH (...) {
// destruct already copied objects
destruct(x->begin(), dst);
QT_RETHROW;
}
}
}
} QT_CATCH (...) {
Data::deallocate(x);
QT_RETHROW;
}
x->capacityReserved = d->capacityReserved;
} else {
Q_ASSERT(int(d->alloc) == aalloc); // resize, without changing allocation size
Q_ASSERT(isDetached()); // can be done only on detached d
Q_ASSERT(x == d); // in this case we do not need to allocate anything
if (asize <= d->size) {
destruct(x->begin() + asize, x->end()); // from future end to current end
} else {
defaultConstruct(x->end(), x->begin() + asize); // from current end to future end
}
x->size = asize;
}
} else {
x = Data::sharedNull();
}
if (d != x) {
if (!d->ref.deref()) {
if (!QTypeInfoQuery<T>::isRelocatable || !aalloc || (isShared && QTypeInfo<T>::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->alloc);
#if !defined(QT_NO_UNSHARABLE_CONTAINERS)
Q_ASSERT(d != Data::unsharableEmpty());
#endif
Q_ASSERT(aalloc ? d != Data::sharedNull() : d == Data::sharedNull());
Q_ASSERT(d->alloc >= uint(aalloc));
Q_ASSERT(d->size == asize);
}
template<typename T>
void QVector<T>::realloc(int aalloc, QArrayData::AllocationOptions options)
{
Q_ASSERT(aalloc >= d->size);
Data *x = d;
const bool isShared = d->ref.isShared();
QT_TRY {
// allocate memory
x = Data::allocate(aalloc, options);
Q_CHECK_PTR(x);
// aalloc is bigger then 0 so it is not [un]sharedEmpty
#if !defined(QT_NO_UNSHARABLE_CONTAINERS)
Q_ASSERT(x->ref.isSharable() || options.testFlag(QArrayData::Unsharable));
#endif
Q_ASSERT(!x->ref.isStatic());
x->size = d->size;
T *srcBegin = d->begin();
T *srcEnd = d->end();
T *dst = x->begin();
if (!QTypeInfoQuery<T>::isRelocatable || (isShared && QTypeInfo<T>::isComplex)) {
QT_TRY {
if (isShared || !std::is_nothrow_move_constructible<T>::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<void *>(dst), static_cast<void *>(srcBegin), (srcEnd - srcBegin) * sizeof(T));
dst += srcEnd - srcBegin;
}
} QT_CATCH (...) {
Data::deallocate(x);
QT_RETHROW;
}
x->capacityReserved = d->capacityReserved;
Q_ASSERT(d != x);
if (!d->ref.deref()) {
if (!QTypeInfoQuery<T>::isRelocatable || !aalloc || (isShared && QTypeInfo<T>::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->alloc);
#if !defined(QT_NO_UNSHARABLE_CONTAINERS)
Q_ASSERT(d != Data::unsharableEmpty());
#endif
Q_ASSERT(d != Data::sharedNull());
Q_ASSERT(d->alloc >= uint(aalloc));
}
#if defined(Q_CC_MSVC)
QT_WARNING_POP
#endif
template<typename T>
Q_OUTOFLINE_TEMPLATE T QVector<T>::value(int i) const
{
if (uint(i) >= uint(d->size)) {
return T();
}
return d->begin()[i];
}
template<typename T>
Q_OUTOFLINE_TEMPLATE T QVector<T>::value(int i, const T &defaultValue) const
{
return uint(i) >= uint(d->size) ? defaultValue : d->begin()[i];
}
template <typename T>
void QVector<T>::append(const T &t)
{
const bool isTooSmall = uint(d->size + 1) > d->alloc;
if (!isDetached() || isTooSmall) {
T copy(t);
QArrayData::AllocationOptions opt(isTooSmall ? QArrayData::Grow : QArrayData::Default);
realloc(isTooSmall ? d->size + 1 : d->alloc, opt);
if (QTypeInfo<T>::isComplex)
new (d->end()) T(std::move(copy));
else
*d->end() = std::move(copy);
} else {
if (QTypeInfo<T>::isComplex)
new (d->end()) T(t);
else
*d->end() = t;
}
++d->size;
}
template <typename T>
void QVector<T>::append(T &&t)
{
const bool isTooSmall = uint(d->size + 1) > d->alloc;
if (!isDetached() || isTooSmall) {
QArrayData::AllocationOptions opt(isTooSmall ? QArrayData::Grow : QArrayData::Default);
realloc(isTooSmall ? d->size + 1 : d->alloc, opt);
}
new (d->end()) T(std::move(t));
++d->size;
}
template <typename T>
void QVector<T>::removeLast()
{
Q_ASSERT(!isEmpty());
Q_ASSERT(d->alloc);
if (d->ref.isShared())
detach();
--d->size;
if (QTypeInfo<T>::isComplex)
(d->data() + d->size)->~T();
}
template <typename T>
typename QVector<T>::iterator QVector<T>::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->alloc))
realloc(d->size + n, QArrayData::Grow);
if (!QTypeInfoQuery<T>::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<void *>(i), static_cast<const void *>(b), (d->size - offset) * sizeof(T));
while (i != b)
new (--i) T(copy);
}
d->size += n;
}
return d->begin() + offset;
}
template <typename T>
typename QVector<T>::iterator QVector<T>::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->alloc))
realloc(d->size + 1, QArrayData::Grow);
if (!QTypeInfoQuery<T>::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));
}
} else {
T *b = d->begin() + offset;
memmove(static_cast<void *>(b + 1), static_cast<const void *>(b), (d->size - offset) * sizeof(T));
new (b) T(std::move(t));
}
d->size += 1;
return d->begin() + offset;
}
template <typename T>
typename QVector<T>::iterator QVector<T>::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");
const auto itemsToErase = aend - abegin;
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->alloc) {
detach();
abegin = d->begin() + itemsUntouched;
aend = abegin + itemsToErase;
if (!QTypeInfoQuery<T>::isRelocatable) {
iterator moveBegin = abegin + itemsToErase;
iterator moveEnd = d->end();
while (moveBegin != moveEnd) {
if (QTypeInfo<T>::isComplex)
static_cast<T *>(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<void *> 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<void *>(abegin), static_cast<void *>(aend),
(d->size - itemsToErase - itemsUntouched) * sizeof(T));
}
d->size -= int(itemsToErase);
}
return d->begin() + itemsUntouched;
}
template <typename T>
bool QVector<T>::operator==(const QVector<T> &v) const
{
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 <typename T>
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 = d->end();
T *b = d->begin();
while (i != b)
*--i = copy;
}
return *this;
}
template <typename T>
QVector<T> &QVector<T>::operator+=(const QVector &l)
{
if (d->size == 0) {
*this = l;
} else {
uint newSize = d->size + l.d->size;
const bool isTooSmall = newSize > d->alloc;
if (!isDetached() || isTooSmall) {
QArrayData::AllocationOptions opt(isTooSmall ? QArrayData::Grow : QArrayData::Default);
realloc(isTooSmall ? newSize : d->alloc, opt);
}
if (d->alloc) {
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);
else
*--w = *--i;
}
d->size = newSize;
}
}
return *this;
}
template <typename T>
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 = d->begin() + from - 1;
T* e = d->end();
while (++n != e)
if (*n == t)
return n - d->begin();
}
return -1;
}
template <typename T>
int QVector<T>::lastIndexOf(const T &t, int from) const
{
if (from < 0)
from += d->size;
else if (from >= d->size)
from = d->size-1;
if (from >= 0) {
T* b = d->begin();
T* n = d->begin() + from + 1;
while (n != b) {
if (*--n == t)
return n - b;
}
}
return -1;
}
template <typename T>
bool QVector<T>::contains(const T &t) const
{
const T *b = d->begin();
const T *e = d->end();
return std::find(b, e, t) != e;
}
template <typename T>
int QVector<T>::count(const T &t) const
{
const T *b = d->begin();
const T *e = d->end();
return int(std::count(b, e, t));
}
template <typename T>
Q_OUTOFLINE_TEMPLATE QVector<T> QVector<T>::mid(int pos, int len) const
{
using namespace QtPrivate;
switch (QContainerImplHelper::mid(d->size, &pos, &len)) {
case QContainerImplHelper::Null:
case QContainerImplHelper::Empty:
return QVector<T>();
case QContainerImplHelper::Full:
return *this;
case QContainerImplHelper::Subset:
break;
}
QVector<T> midResult;
midResult.realloc(len);
T *srcFrom = d->begin() + pos;
T *srcTo = d->begin() + pos + len;
midResult.copyConstruct(srcFrom, srcTo, midResult.data());
midResult.d->size = len;
return midResult;
}
Q_DECLARE_SEQUENTIAL_ITERATOR(Vector)
Q_DECLARE_MUTABLE_SEQUENTIAL_ITERATOR(Vector)
template <typename T>
uint qHash(const QVector<T> &key, uint seed = 0)
noexcept(noexcept(qHashRange(key.cbegin(), key.cend(), seed)))
{
return qHashRange(key.cbegin(), key.cend(), seed);
}
template <typename T>
bool operator<(const QVector<T> &lhs, const QVector<T> &rhs)
noexcept(noexcept(std::lexicographical_compare(lhs.begin(), lhs.end(),
rhs.begin(), rhs.end())))
{
return std::lexicographical_compare(lhs.begin(), lhs.end(),
rhs.begin(), rhs.end());
}
template <typename T>
inline bool operator>(const QVector<T> &lhs, const QVector<T> &rhs)
noexcept(noexcept(lhs < rhs))
{
return rhs < lhs;
}
template <typename T>
inline bool operator<=(const QVector<T> &lhs, const QVector<T> &rhs)
noexcept(noexcept(lhs < rhs))
{
return !(lhs > rhs);
}
template <typename T>
inline bool operator>=(const QVector<T> &lhs, const QVector<T> &rhs)
noexcept(noexcept(lhs < rhs))
{
return !(lhs < rhs);
}
/*
### Qt 5:
### This needs to be removed for next releases of Qt. It is a workaround for vc++ because
### Qt exports QPolygon and QPolygonF that inherit QVector<QPoint> and
### QVector<QPointF> respectively.
*/
#if defined(Q_CC_MSVC) && !defined(QT_BUILD_CORE_LIB)
QT_BEGIN_INCLUDE_NAMESPACE
#include <QtCore/qpoint.h>
QT_END_INCLUDE_NAMESPACE
extern template class Q_CORE_EXPORT QVector<QPointF>;
extern template class Q_CORE_EXPORT QVector<QPoint>;
#endif
QVector<uint> QStringView::toUcs4() const { return QtPrivate::convertToUcs4(*this); }
QVector<QStringRef> QString::splitRef(const QString &sep, Qt::SplitBehavior behavior, Qt::CaseSensitivity cs) const
{ return splitRef(sep, _sb(behavior), cs); }
QVector<QStringRef> QString::splitRef(QChar sep, Qt::SplitBehavior behavior, Qt::CaseSensitivity cs) const
{ return splitRef(sep, _sb(behavior), cs); }
#ifndef QT_NO_REGEXP
QVector<QStringRef> QString::splitRef(const QRegExp &sep, Qt::SplitBehavior behavior) const
{ return splitRef(sep, _sb(behavior)); }
#endif
#if QT_CONFIG(regularexpression)
QVector<QStringRef> QString::splitRef(const QRegularExpression &sep, Qt::SplitBehavior behavior) const
{ return splitRef(sep, _sb(behavior)); }
#endif
QVector<QStringRef> QStringRef::split(const QString &sep, Qt::SplitBehavior behavior, Qt::CaseSensitivity cs) const
{ return split(sep, QString::_sb(behavior), cs); }
QVector<QStringRef> QStringRef::split(QChar sep, Qt::SplitBehavior behavior, Qt::CaseSensitivity cs) const
{ return split(sep, QString::_sb(behavior), cs); }
QT_END_NAMESPACE
#endif // QVECTOR_H