summaryrefslogtreecommitdiffstats
path: root/src/corelib/tools/qvarlengtharray.h
diff options
context:
space:
mode:
Diffstat (limited to 'src/corelib/tools/qvarlengtharray.h')
-rw-r--r--src/corelib/tools/qvarlengtharray.h307
1 files changed, 182 insertions, 125 deletions
diff --git a/src/corelib/tools/qvarlengtharray.h b/src/corelib/tools/qvarlengtharray.h
index fd3d4ffbf2..afc345d3be 100644
--- a/src/corelib/tools/qvarlengtharray.h
+++ b/src/corelib/tools/qvarlengtharray.h
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2021 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$
-**
-****************************************************************************/
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#ifndef QVARLENGTHARRAY_H
#define QVARLENGTHARRAY_H
@@ -54,7 +18,7 @@
#include <algorithm>
#include <initializer_list>
#include <iterator>
-#include <memory>
+#include <QtCore/q20memory.h>
#include <new>
#include <string.h>
@@ -88,7 +52,8 @@ protected:
qsizetype s; // size
void *ptr; // data
- Q_ALWAYS_INLINE constexpr void verify(qsizetype pos = 0, qsizetype n = 1) const
+ Q_ALWAYS_INLINE constexpr void verify([[maybe_unused]] qsizetype pos = 0,
+ [[maybe_unused]] qsizetype n = 1) const
{
Q_ASSERT(pos >= 0);
Q_ASSERT(pos <= size());
@@ -96,6 +61,11 @@ protected:
Q_ASSERT(n <= size() - pos);
}
+ struct free_deleter {
+ void operator()(void *p) const noexcept { free(p); }
+ };
+ using malloced_ptr = std::unique_ptr<void, free_deleter>;
+
public:
using size_type = qsizetype;
@@ -203,20 +173,35 @@ public:
template <typename Predicate>
qsizetype removeIf(Predicate pred);
+ void clear()
+ {
+ if constexpr (QTypeInfo<T>::isComplex)
+ std::destroy_n(data(), size());
+ s = 0;
+ }
+
iterator erase(const_iterator begin, const_iterator end);
iterator erase(const_iterator pos) { return erase(pos, pos + 1); }
+ static constexpr qsizetype max_size() noexcept
+ {
+ // -1 to deal with the pointer one-past-the-end
+ return (QtPrivate::MaxAllocSize / sizeof(T)) - 1;
+ }
+
size_t hash(size_t seed) const noexcept(QtPrivate::QNothrowHashable_v<T>)
{
return qHashRange(begin(), end(), seed);
}
protected:
+ void growBy(qsizetype prealloc, void *array, qsizetype increment)
+ { reallocate_impl(prealloc, array, size(), (std::max)(size() * 2, size() + increment)); }
template <typename...Args>
reference emplace_back_impl(qsizetype prealloc, void *array, Args&&...args)
{
if (size() == capacity()) // ie. size() != 0
- reallocate_impl(prealloc, array, size(), size() << 1);
- reference r = *new (end()) T(std::forward<Args>(args)...);
+ growBy(prealloc, array, 1);
+ reference r = *q20::construct_at(end(), std::forward<Args>(args)...);
++s;
return r;
}
@@ -238,8 +223,35 @@ protected:
void append_impl(qsizetype prealloc, void *array, const T *buf, qsizetype n);
void reallocate_impl(qsizetype prealloc, void *array, qsizetype size, qsizetype alloc);
+ void resize_impl(qsizetype prealloc, void *array, qsizetype sz, const T &v)
+ {
+ if (QtPrivate::q_points_into_range(&v, begin(), end())) {
+ resize_impl(prealloc, array, sz, T(v));
+ return;
+ }
+ reallocate_impl(prealloc, array, sz, qMax(sz, capacity()));
+ while (size() < sz) {
+ q20::construct_at(data() + size(), v);
+ ++s;
+ }
+ }
void resize_impl(qsizetype prealloc, void *array, qsizetype sz)
- { reallocate_impl(prealloc, array, sz, qMax(sz, capacity())); }
+ {
+ reallocate_impl(prealloc, array, sz, qMax(sz, capacity()));
+ if constexpr (QTypeInfo<T>::isComplex) {
+ // call default constructor for new objects (which can throw)
+ while (size() < sz) {
+ q20::construct_at(data() + size());
+ ++s;
+ }
+ } else {
+ s = sz;
+ }
+ }
+
+ void assign_impl(qsizetype prealloc, void *array, qsizetype n, const T &t);
+ template <typename Iterator>
+ void assign_impl(qsizetype prealloc, void *array, Iterator first, Iterator last);
bool isValidIterator(const const_iterator &i) const
{
@@ -251,16 +263,29 @@ protected:
// Prealloc = 256 by default, specified in qcontainerfwd.h
template<class T, qsizetype Prealloc>
class QVarLengthArray
- : public QVLABase<T>, // ### Qt 7: swap base class order
+#if QT_VERSION >= QT_VERSION_CHECK(7,0,0) || defined(QT_BOOTSTRAPPED)
+ : public QVLAStorage<sizeof(T), alignof(T), Prealloc>,
+ public QVLABase<T>
+#else
+ : public QVLABase<T>,
public QVLAStorage<sizeof(T), alignof(T), Prealloc>
+#endif
{
template <class S, qsizetype Prealloc2>
friend class QVarLengthArray;
using Base = QVLABase<T>;
using Storage = QVLAStorage<sizeof(T), alignof(T), Prealloc>;
+ static_assert(Prealloc > 0, "QVarLengthArray Prealloc must be greater than 0.");
static_assert(std::is_nothrow_destructible_v<T>, "Types with throwing destructors are not supported in Qt containers.");
using Base::verify;
+
+ template <typename U>
+ using if_copyable = std::enable_if_t<std::is_copy_constructible_v<U>, bool>;
+ template <typename InputIterator>
+ using if_input_iterator = QtPrivate::IfIsInputIterator<InputIterator>;
public:
+ static constexpr qsizetype PreallocatedSize = Prealloc;
+
using size_type = typename Base::size_type;
using value_type = typename Base::value_type;
using pointer = typename Base::pointer;
@@ -283,6 +308,15 @@ public:
inline explicit QVarLengthArray(qsizetype size);
+#ifndef Q_QDOC
+ template <typename U = T, if_copyable<U> = true>
+#endif
+ explicit QVarLengthArray(qsizetype sz, const T &v)
+ : QVarLengthArray{}
+ {
+ resize(sz, v);
+ }
+
QVarLengthArray(const QVarLengthArray &other)
: QVarLengthArray{}
{
@@ -312,7 +346,7 @@ public:
{
}
- template <typename InputIterator, QtPrivate::IfIsInputIterator<InputIterator> = true>
+ template <typename InputIterator, if_input_iterator<InputIterator> = true>
inline QVarLengthArray(InputIterator first, InputIterator last)
: QVarLengthArray()
{
@@ -359,9 +393,7 @@ public:
QVarLengthArray<T, Prealloc> &operator=(std::initializer_list<T> list)
{
- resize(qsizetype(list.size()));
- std::copy(list.begin(), list.end(),
- QT_MAKE_CHECKED_ARRAY_ITERATOR(begin(), size()));
+ assign(list);
return *this;
}
@@ -371,6 +403,7 @@ public:
}
#ifdef Q_QDOC
inline qsizetype size() const { return this->s; }
+ static constexpr qsizetype max_size() noexcept { return QVLABase<T>::max_size(); }
#endif
using Base::size;
inline qsizetype count() const { return size(); }
@@ -393,7 +426,15 @@ public:
}
bool isEmpty() const { return empty(); }
void resize(qsizetype sz) { Base::resize_impl(Prealloc, this->array, sz); }
+#ifndef Q_QDOC
+ template <typename U = T, if_copyable<U> = true>
+#endif
+ void resize(qsizetype sz, const T &v)
+ { Base::resize_impl(Prealloc, this->array, sz, v); }
+ using Base::clear;
+#ifdef Q_QDOC
inline void clear() { resize(0); }
+#endif
void squeeze() { reallocate(size(), size()); }
using Base::capacity;
@@ -468,6 +509,15 @@ public:
void insert(qsizetype i, T &&t);
void insert(qsizetype i, const T &t);
void insert(qsizetype i, qsizetype n, const T &t);
+
+ QVarLengthArray &assign(qsizetype n, const T &t)
+ { Base::assign_impl(Prealloc, this->array, n, t); return *this; }
+ template <typename InputIterator, if_input_iterator<InputIterator> = true>
+ QVarLengthArray &assign(InputIterator first, InputIterator last)
+ { Base::assign_impl(Prealloc, this->array, first, last); return *this; }
+ QVarLengthArray &assign(std::initializer_list<T> list)
+ { assign(list.begin(), list.end()); return *this; }
+
#ifdef Q_QDOC
void replace(qsizetype i, const T &t);
void remove(qsizetype i, qsizetype n = 1);
@@ -632,23 +682,22 @@ QVarLengthArray(InputIterator, InputIterator) -> QVarLengthArray<ValueType>;
template <class T, qsizetype Prealloc>
Q_INLINE_TEMPLATE QVarLengthArray<T, Prealloc>::QVarLengthArray(qsizetype asize)
+ : QVarLengthArray()
{
- this->s = asize;
- static_assert(Prealloc > 0, "QVarLengthArray Prealloc must be greater than 0.");
- Q_ASSERT_X(size() >= 0, "QVarLengthArray::QVarLengthArray()", "Size must be greater than or equal to 0.");
- if (size() > Prealloc) {
- this->ptr = malloc(size() * sizeof(T));
- Q_CHECK_PTR(data());
- this->a = size();
- } else {
- this->ptr = this->array;
- this->a = Prealloc;
- }
- if constexpr (QTypeInfo<T>::isComplex) {
- T *i = end();
- while (i != begin())
- new (--i) T;
+ Q_ASSERT_X(asize >= 0, "QVarLengthArray::QVarLengthArray(qsizetype)",
+ "Size must be greater than or equal to 0.");
+
+ // historically, this ctor worked for non-copyable/non-movable T, so keep it working, why not?
+ // resize(asize) // this requires a movable or copyable T, can't use, need to do it by hand
+
+ if (asize > Prealloc) {
+ this->ptr = malloc(asize * sizeof(T));
+ Q_CHECK_PTR(this->ptr);
+ this->a = asize;
}
+ if constexpr (QTypeInfo<T>::isComplex)
+ std::uninitialized_default_construct_n(data(), asize);
+ this->s = asize;
}
template <class T>
@@ -709,7 +758,7 @@ Q_OUTOFLINE_TEMPLATE void QVLABase<T>::append_impl(qsizetype prealloc, void *arr
const qsizetype asize = size() + increment;
if (asize >= capacity())
- reallocate_impl(prealloc, array, size(), qMax(size() * 2, asize));
+ growBy(prealloc, array, increment);
if constexpr (QTypeInfo<T>::isComplex)
std::uninitialized_copy_n(abuf, increment, end());
@@ -720,6 +769,61 @@ Q_OUTOFLINE_TEMPLATE void QVLABase<T>::append_impl(qsizetype prealloc, void *arr
}
template <class T>
+Q_OUTOFLINE_TEMPLATE void QVLABase<T>::assign_impl(qsizetype prealloc, void *array, qsizetype n, const T &t)
+{
+ Q_ASSERT(n >= 0);
+ if (n > capacity()) {
+ reallocate_impl(prealloc, array, 0, capacity()); // clear
+ resize_impl(prealloc, array, n, t);
+ } else {
+ auto mid = (std::min)(n, size());
+ std::fill(data(), data() + mid, t);
+ std::uninitialized_fill(data() + mid, data() + n, t);
+ s = n;
+ erase(data() + n, data() + size());
+ }
+}
+
+template <class T>
+template <typename Iterator>
+Q_OUTOFLINE_TEMPLATE void QVLABase<T>::assign_impl(qsizetype prealloc, void *array, Iterator first, Iterator last)
+{
+ // This function only provides the basic exception guarantee.
+ constexpr bool IsFwdIt =
+ std::is_convertible_v<typename std::iterator_traits<Iterator>::iterator_category,
+ std::forward_iterator_tag>;
+ if constexpr (IsFwdIt) {
+ const qsizetype n = std::distance(first, last);
+ if (n > capacity())
+ reallocate_impl(prealloc, array, 0, n); // clear & reserve n
+ }
+
+ auto dst = begin();
+ const auto dend = end();
+ while (true) {
+ if (first == last) { // ran out of elements to assign
+ std::destroy(dst, dend);
+ break;
+ }
+ if (dst == dend) { // ran out of existing elements to overwrite
+ if constexpr (IsFwdIt) {
+ dst = std::uninitialized_copy(first, last, dst);
+ break;
+ } else {
+ do {
+ emplace_back_impl(prealloc, array, *first);
+ } while (++first != last);
+ return; // size() is already correct (and dst invalidated)!
+ }
+ }
+ *dst = *first; // overwrite existing element
+ ++dst;
+ ++first;
+ }
+ this->s = dst - begin();
+}
+
+template <class T>
Q_OUTOFLINE_TEMPLATE void QVLABase<T>::reallocate_impl(qsizetype prealloc, void *array, qsizetype asize, qsizetype aalloc)
{
Q_ASSERT(aalloc >= asize);
@@ -728,13 +832,10 @@ Q_OUTOFLINE_TEMPLATE void QVLABase<T>::reallocate_impl(qsizetype prealloc, void
qsizetype osize = size();
const qsizetype copySize = qMin(asize, osize);
- Q_ASSUME(copySize >= 0);
+ Q_ASSERT(copySize >= 0);
if (aalloc != capacity()) {
- struct free_deleter {
- void operator()(void *p) const noexcept { free(p); }
- };
- std::unique_ptr<void, free_deleter> guard;
+ QVLABaseBase::malloced_ptr guard;
void *newPtr;
qsizetype newA;
if (aalloc > prealloc) {
@@ -764,17 +865,6 @@ Q_OUTOFLINE_TEMPLATE void QVLABase<T>::reallocate_impl(qsizetype prealloc, void
if (oldPtr != reinterpret_cast<T *>(array) && oldPtr != data())
free(oldPtr);
-
- if constexpr (QTypeInfo<T>::isComplex) {
- // call default constructor for new objects (which can throw)
- while (size() < asize) {
- new (data() + size()) T;
- ++s;
- }
- } else {
- s = asize;
- }
-
}
template <class T>
@@ -842,29 +932,12 @@ Q_OUTOFLINE_TEMPLATE auto QVLABase<T>::emplace_impl(qsizetype prealloc, void *ar
Q_ASSERT(size() <= capacity());
Q_ASSERT(capacity() > 0);
- qsizetype offset = qsizetype(before - cbegin());
- if (size() == capacity())
- reallocate_impl(prealloc, array, size(), size() * 2);
- if constexpr (!QTypeInfo<T>::isRelocatable) {
- T *b = begin() + offset;
- T *i = end();
- T *j = i + 1;
- // 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 = T(std::forward<Args>(args)...);
- } else {
- new (b) T(std::forward<Args>(args)...);
- }
- } else {
- T *b = begin() + offset;
- memmove(static_cast<void *>(b + 1), static_cast<const void *>(b), (size() - offset) * sizeof(T));
- new (b) T(std::forward<Args>(args)...);
- }
- this->s += 1;
- return data() + offset;
+ const qsizetype offset = qsizetype(before - cbegin());
+ emplace_back_impl(prealloc, array, std::forward<Args>(args)...);
+ const auto b = begin() + offset;
+ const auto e = end();
+ QtPrivate::q_rotate(b, e - 1, e);
+ return b;
}
template <class T>
@@ -872,28 +945,12 @@ Q_OUTOFLINE_TEMPLATE auto QVLABase<T>::insert_impl(qsizetype prealloc, void *arr
{
Q_ASSERT_X(isValidIterator(before), "QVarLengthArray::insert", "The specified const_iterator argument 'before' is invalid");
- qsizetype offset = qsizetype(before - cbegin());
- if (n != 0) {
- const T copy(t); // `t` could alias an element in [begin(), end()[
- resize_impl(prealloc, array, size() + n);
- if constexpr (!QTypeInfo<T>::isRelocatable) {
- T *b = begin() + offset;
- T *j = end();
- T *i = j - n;
- while (i != b)
- *--j = *--i;
- i = b + n;
- while (i != b)
- *--i = copy;
- } else {
- T *b = begin() + offset;
- T *i = b + n;
- memmove(static_cast<void *>(i), static_cast<const void *>(b), (size() - offset - n) * sizeof(T));
- while (i != b)
- new (--i) T(copy);
- }
- }
- return data() + offset;
+ const qsizetype offset = qsizetype(before - cbegin());
+ resize_impl(prealloc, array, size() + n, t);
+ const auto b = begin() + offset;
+ const auto e = end();
+ QtPrivate::q_rotate(b, e - n, e);
+ return b;
}
template <class T>