blob: e10b7236d6571af5c8fb14fb9b8ccc275a4d4d8b [file] [log] [blame]
/****************************************************************************
**
** Copyright (C) 2014 Klaralvdalens Datakonsult AB (KDAB).
** Contact: https://www.qt.io/licensing/
**
** This file is part of the Qt3D 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 QT3DCORE_QCIRCULARBUFFER_H
#define QT3DCORE_QCIRCULARBUFFER_H
//
// W A R N I N G
// -------------
//
// This file is not part of the Qt API. It exists for the convenience
// of other Qt classes. This header file may change from version to
// version without notice, or even be removed.
//
// We mean it.
//
#include <Qt3DCore/qt3dcore_global.h>
#include <QtCore/QtGlobal>
#include <QtCore/qlist.h>
#include <QtCore/qpair.h>
#include <QtCore/qshareddata.h>
#include <QtCore/qvector.h>
#include <algorithm>
#include <iterator>
#include <limits>
#include <memory>
#include <new>
#ifdef Q_COMPILER_INITIALIZER_LISTS
# include <initializer_list>
#endif
QT_BEGIN_NAMESPACE
namespace Qt3DCore {
class CircularBufferData : public QSharedData
{
protected:
CircularBufferData()
: data(nullptr),
capacity(0),
size(0),
first(-1),
last(-1)
{}
~CircularBufferData()
{
// Release the raw memory
deallocate(data);
}
int wraparound(int index) const
{
index = index < capacity ? index : (index - capacity);
Q_ASSERT(index < capacity); // make sure that wrapping around once was enough
return index;
}
void *allocate(int count, size_t sizeOfT)
{ return operator new[](count * sizeOfT); }
void deallocate(void *p)
{ operator delete[](p); }
void *data; // Array of the actual data
public:
int capacity; // Size of the m_data array
int size; // Number of elements of m_data actually used
int first; // Index in m_data of the first element of the circular buffer
int last; // Index in m_data of the last element of the circular buffer
};
template <typename T>
class TypedCircularBufferData : public CircularBufferData
{
template <typename InputIterator>
explicit TypedCircularBufferData(InputIterator f, InputIterator l, std::input_iterator_tag) Q_DECL_EQ_DELETE;
public:
TypedCircularBufferData() : CircularBufferData() {}
template <typename ForwardIterator>
explicit TypedCircularBufferData(ForwardIterator f, ForwardIterator l, std::forward_iterator_tag)
{
const int n = int(std::distance(f, l));
CircularBufferData::data = allocate(n);
std::uninitialized_copy(f, l, data());
first = 0;
last = n - 1;
size = capacity = n;
}
~TypedCircularBufferData()
{
if (QTypeInfo<T>::isComplex && size != 0) {
// The type is complex so we manually call the destructor for each item
// since we used the placement new operator to instantiate them
if (first <= last) {
// Destroy items from first to last
T *b = data() + first;
T *i = b + size;
while (i-- != b)
i->~T();
} else {
// Destroy items at end of data array
T *b = data() + first;
T *i = data() + capacity;
while (i-- != b)
i->~T();
// Destroy items at beginning of data array
b = data();
i = b + last;
while (i-- != b)
i->~T();
}
}
}
using CircularBufferData::wraparound;
T *allocate(int count) { return static_cast<T*>(CircularBufferData::allocate(count, sizeof(T))); }
using CircularBufferData::deallocate;
T *data() const { return static_cast<T*>(CircularBufferData::data); }
void setData(T *newdata) { CircularBufferData::data = static_cast<void*>(newdata); }
};
template <typename T>
class QCircularBuffer
{
typedef TypedCircularBufferData<T> Data;
public:
typedef QPair<T*,int> array_range;
typedef QPair<const T*,int> const_array_range;
typedef array_range ArrayRange;
typedef const_array_range ConstArrayRange;
QCircularBuffer()
: d(new Data())
{}
explicit QCircularBuffer(int amount);
explicit QCircularBuffer(int amount, const T &val);
explicit QCircularBuffer(int amount, int initialSize, const T &value);
#ifdef Q_COMPILER_INITIALIZER_LISTS
QCircularBuffer(std::initializer_list<T> list)
: d(new Data(list.begin(), list.end(), std::random_access_iterator_tag()))
{}
#endif
template <typename ForwardIterator>
explicit QCircularBuffer(ForwardIterator f, ForwardIterator l)
: d(new Data(f, l, typename std::iterator_traits<ForwardIterator>::iterator_category()))
{}
QCircularBuffer(const QCircularBuffer<T> &other)
: d(other.d)
{}
void swap(QCircularBuffer &other) { d.swap(other.d); }
QCircularBuffer<T> &operator = (const QCircularBuffer<T> &other)
{
d = other.d;
return *this;
}
~QCircularBuffer() {}
class iterator
{
public:
typedef std::random_access_iterator_tag iterator_category;
typedef ptrdiff_t difference_type;
typedef T value_type;
typedef T *pointer;
typedef T &reference;
Q_DECL_CONSTEXPR iterator() : buffer(nullptr), index(-1) {}
iterator(QCircularBuffer<T> *buf, int idx)
: buffer(buf), index(idx)
{}
T &operator*() const { return (*buffer)[ index ]; }
T *operator->() const { return &(*buffer)[index]; }
T &operator[](int j) const { return (*buffer)[ index + j ]; }
bool operator==(const iterator &other) const
{
return (buffer == other.buffer && index == other.index);
}
bool operator!=(const iterator &other) const
{
return (buffer != other.buffer || index != other.index);
}
bool operator<(const iterator &other) const
{
Q_ASSERT_X(buffer == other.buffer, "QCircularBuffer<T>::iterator::operator<", "iterators use different buffers");
return index < other.index;
}
bool operator<=(const iterator &other) const
{
Q_ASSERT_X(buffer == other.buffer, "QCircularBuffer<T>::iterator::operator<=", "iterators use different buffers");
return index <= other.index;
}
bool operator>(const iterator &other) const
{
Q_ASSERT_X(buffer == other.buffer, "QCircularBuffer<T>::iterator::operator>", "iterators use different buffers");
return index > other.index;
}
bool operator>=(const iterator &other) const
{
Q_ASSERT_X(buffer == other.buffer, "QCircularBuffer<T>::iterator::operator>=", "iterators use different buffers");
return index >= other.index;
}
iterator &operator++() { ++index; return *this; }
iterator operator++(int)
{
iterator ans = *this;
++index;
return ans;
}
iterator &operator--() { --index; return *this; }
iterator operator--(int)
{
iterator ans = *this;
--index;
return ans;
}
iterator &operator+=(int j) { index += j; return *this; }
iterator &operator-=(int j) { index -= j; return *this; }
iterator operator+(int j) const { return iterator(buffer, index + j); }
iterator operator-(int j) const { return iterator(buffer, index - j); }
int operator-(iterator other) const
{
Q_ASSERT_X(buffer == other.buffer, "QCircularBuffer<T>::iterator::operator-", "iterators use different buffers");
return index - other.index;
}
private:
QCircularBuffer<T> *buffer;
int index;
friend class QCircularBuffer;
};
friend class iterator;
class const_iterator
{
public:
typedef std::random_access_iterator_tag iterator_category;
typedef ptrdiff_t difference_type;
typedef T value_type;
typedef const T *pointer;
typedef const T &reference;
Q_DECL_CONSTEXPR const_iterator() : buffer(nullptr), index(-1) {}
const_iterator(const QCircularBuffer<T> *buff, int idx)
: buffer(buff), index(idx)
{}
const_iterator(const iterator &other)
: buffer(other.buffer), index(other.index)
{}
const T &operator*() const { return buffer->at(index); }
const T *operator->() const { return &buffer->at(index); }
const T &operator[](int j) const { return buffer->at(index + j); }
bool operator==(const const_iterator &other) const
{
Q_ASSERT_X(buffer == other.buffer, "QCircularBuffer<T>::const_iterator::operator==", "iterators use different buffers");
return index == other.index;
}
bool operator!=(const const_iterator &other) const
{
Q_ASSERT_X(buffer == other.buffer, "QCircularBuffer<T>::const_iterator::operator!=", "iterators use different buffers");
return index != other.index;
}
bool operator<(const const_iterator &other) const
{
Q_ASSERT_X(buffer == other.buffer, "QCircularBuffer<T>::const_iterator::operator<", "iterators use different buffers");
return index < other.index;
}
bool operator<=(const const_iterator &other) const
{
Q_ASSERT_X(buffer == other.buffer, "QCircularBuffer<T>::const_iterator::operator<=", "iterators use different buffers");
return index <= other.index;
}
bool operator>(const const_iterator &other) const
{
Q_ASSERT_X(buffer == other.buffer, "QCircularBuffer<T>::const_iterator::operator>", "iterators use different buffers");
return index > other.index;
}
bool operator>=(const const_iterator &other) const
{
Q_ASSERT_X(buffer == other.buffer, "QCircularBuffer<T>::const_iterator::operator>=", "iterators use different buffers");
return index >= other.index;
}
const_iterator &operator++() { ++index; return *this; }
const_iterator operator++(int)
{
const_iterator ans = *this;
++index;
return ans;
}
const_iterator &operator--() { --index; return *this; }
const_iterator operator--(int)
{
const_iterator ans = *this;
--index;
return ans;
}
const_iterator &operator+=(int j) { index += j; return *this; }
const_iterator &operator-=(int j) { index -= j; return *this; }
const_iterator operator+(int j) const { return const_iterator(buffer, index + j); }
const_iterator operator-(int j) const { return const_iterator(buffer, index - j); }
int operator-(const_iterator other) const
{
Q_ASSERT_X(buffer == other.buffer, "QCircularBuffer<T>::const_iterator::operator-", "iterators use different buffers");
return index - other.index;
}
private:
const QCircularBuffer<T> *buffer;
int index;
friend class QCircularBuffer;
};
friend class const_iterator;
typedef std::reverse_iterator<iterator> reverse_iterator;
typedef std::reverse_iterator<const_iterator> const_reverse_iterator;
iterator begin() { return iterator(this, 0); }
const_iterator begin() const { return const_iterator(this, 0); }
const_iterator cbegin() const { return const_iterator(this, 0); }
reverse_iterator rbegin() { return reverse_iterator(end()); }
const_reverse_iterator rbegin() const { return const_reverse_iterator(end()); }
const_reverse_iterator crbegin() const { return const_reverse_iterator(end()); }
const_iterator constBegin() const { return const_iterator(this, 0); }
iterator end() { return iterator(this, d->size); }
const_iterator end() const { return const_iterator(this, d->size); }
const_iterator cend() const { return const_iterator(this, d->size); }
reverse_iterator rend() { return reverse_iterator(begin()); }
const_reverse_iterator rend() const { return const_reverse_iterator(begin()); }
const_reverse_iterator crend() const { return const_reverse_iterator(begin()); }
const_iterator constEnd() const { return const_iterator(this, d->size); }
iterator insert(const_iterator before, int number, const T &val)
{
insert(before.index, number, val);
return iterator(this, before.index);
}
iterator insert(const_iterator before, const T &val) { return insert(before, 1, val); }
iterator erase(const_iterator b, const_iterator e)
{
int number = e - b;
remove(b.index, number);
return iterator(this, e.index - number);
}
iterator erase(const_iterator pos) { return erase(pos, pos + 1); }
// 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 ptrdiff_t difference_type;
typedef iterator Iterator;
typedef const_iterator ConstIterator;
typedef int size_type;
void push_back(const T &t) { append(t); }
void push_front(const T &t) { prepend(t); }
void pop_back() { Q_ASSERT(!isEmpty()); erase(end() - 1); }
void pop_front() { Q_ASSERT(!isEmpty()); erase(begin()); }
bool empty() const { return isEmpty(); }
T &front() { return first(); }
const T &front() const { return first(); }
T &back() { return last(); }
const T &back() const { return last(); }
QAtomicInt refCount() const { return d->ref; }
void append(const T &val);
const T &at(int i) const
{
Q_ASSERT_X(i >= 0 && i < d->size, "QCircularBuffer<T>::at", "index out of range");
return d->data()[d->wraparound(d->first + i)];
}
const T &operator[](int i) const
{
Q_ASSERT_X(i >= 0 && i < d->size, "QCircularBuffer<T>::operator[]", "index out of range");
return d->data()[d->wraparound(d->first + i)];
}
T &operator[](int i)
{
d.detach();
Q_ASSERT_X(i >= 0 && i < d->size, "QCircularBuffer<T>::operator[]", "index out of range");
return d->data()[d->wraparound(d->first + i)];
}
int capacity() const { return d->capacity; }
void clear() { *this = QCircularBuffer<T>(d->capacity); }
bool contains(const T &val) const;
int count(const T &val) const;
int count() const { return size(); }
array_range data()
{
d.detach();
if (d->size == 0)
return array_range(nullptr, 0);
if (!isLinearised())
linearise();
return array_range(d->data() + d->first, d->last - d->first + 1);
}
const_array_range data() const { return constData(); }
const_array_range constData() const
{
if (!isLinearised() || d->size == 0)
return const_array_range(nullptr, 0);
return const_array_range(d->data() + d->first, d->last - d->first + 1);
}
array_range dataOne()
{
d.detach();
if (d->size == 0)
return array_range(nullptr, 0);
if (isLinearised())
return array_range(d->data() + d->first, d->last - d->first + 1);
else
return array_range(d->data() + d->first, d->capacity - d->first);
}
const_array_range dataOne() const { return constDataOne(); }
const_array_range constDataOne() const
{
if (d->size == 0)
return const_array_range(nullptr, 0);
if (isLinearised())
return const_array_range(d->data() + d->first, d->last - d->first + 1);
else
return const_array_range(d->data() + d->first, d->capacity - d->first);
}
array_range dataTwo()
{
d.detach();
if (d->size == 0 || isLinearised())
return array_range(nullptr, 0);
return array_range(d->data(), d->last + 1);
}
const_array_range dataTwo() const { return constDataTwo(); }
const_array_range constDataTwo() const
{
if (d->size == 0 || isLinearised())
return const_array_range(nullptr, 0);
return const_array_range(d->data(), d->last + 1);
}
bool endsWith(const T &val) const { return !isEmpty() && last() == val; }
QCircularBuffer<T> &fill(const T &val, int number = -1);
T &first() { Q_ASSERT(!isEmpty()); d.detach(); return d->data()[ d->first ]; }
const T &first() const { Q_ASSERT(!isEmpty()); return d->data()[ d->first ]; }
int freeSize() const { return sizeAvailable(); }
static QCircularBuffer<T> fromList(const QList<T> &list)
{ return QCircularBuffer(list.begin(), list.end()); }
static QCircularBuffer<T> fromVector(const QVector<T> &vector)
{ return QCircularBuffer(vector.begin(), vector.end()); }
int indexOf(const T &val, int from = 0) const;
void insert(int i, const T &val) { insert(i, 1, val); }
void insert(int i, int number, const T &val);
bool isEmpty() const { return d->size == 0; }
bool isFull() const { return d->size == d->capacity; }
bool isLinearised() const { return (d->last >= d->first); }
T &last() { Q_ASSERT(!isEmpty()); return d->data()[ d->last ]; }
const T &last() const { Q_ASSERT(!isEmpty()); return d->data()[ d->last ]; }
int lastIndexOf(const T &val, int from = -1) const;
void linearise()
{
if (!isLinearised()) {
QCircularBuffer linearized(this->cbegin(), this->cend());
swap(linearized);
}
}
void prepend(const T &val);
void remove(int i) { remove(i, 1); }
void remove(int i, int number);
void replace(int i, const T &val)
{
Q_ASSERT_X(i >= 0 && i < d->size, "QCircularBuffer<T>::replace", "index out of range");
const T copy(val);
(*this)[ i ] = copy;
}
void reserve(int amount) { setCapacity(amount); }
void resize(int newSize);
void setCapacity(int amount);
int size() const { return d->size; }
Q_DECL_CONSTEXPR int max_size() const { return std::numeric_limits<size_type>::max(); }
int sizeAvailable() const { return d->capacity - d->size; }
void squeeze() { setCapacity(size()); }
bool startsWith(const T &val) const { return !isEmpty() && first() == val; }
QList<T> toList() const;
QVector<T> toVector() const;
T value(int i) const
{
if (i < 0 || i >= d->size)
return T();
return at(i);
}
T value(int i, const T &defaultValue) const
{
if (i < 0 || i >= d->size)
return defaultValue;
return at(i);
}
QCircularBuffer<T> &operator+=(const T &other) { append(other); return *this; }
QCircularBuffer<T> &operator+=(const QCircularBuffer<T> &other);
QCircularBuffer<T> &operator+=(const QVector<T> &other);
QCircularBuffer<T> &operator+=(const QList<T> &other);
QCircularBuffer<T> &operator<<(const T &other) { append(other); return *this; }
QCircularBuffer<T> &operator<<(const QCircularBuffer<T> &other) { *this += other; return *this; }
QCircularBuffer<T> &operator<<(const QVector<T> &other) { *this += other; return *this; }
QCircularBuffer<T> &operator<<(const QList<T> &other) { *this += other; return *this; }
inline bool isSharedWith(const QCircularBuffer &other) const { return d == other.d; }
private:
QExplicitlySharedDataPointer<Data> d;
};
template <typename T>
QCircularBuffer<T> operator+(const QCircularBuffer<T> &lhs, const QCircularBuffer<T> &rhs);
template <typename T>
inline bool operator==(const QCircularBuffer<T> &lhs, const QCircularBuffer<T> &rhs)
{ return lhs.isSharedWith(rhs) || (lhs.size() == rhs.size() && std::equal(lhs.begin(), lhs.end(), rhs.begin())); }
template <typename T>
inline bool operator!=(const QCircularBuffer<T> &lhs, const QCircularBuffer<T> &rhs)
{ return !operator==(lhs, rhs); }
template <typename T>
inline void swap(QCircularBuffer<T> &lhs, QCircularBuffer<T> &rhs)
{ lhs.swap(rhs); }
template <typename T>
inline bool operator< (const QCircularBuffer<T> &lhs, const QCircularBuffer<T> &rhs)
{ return std::lexicographical_compare(lhs.begin(), lhs.end(), rhs.begin(), rhs.end()); }
template <typename T>
inline bool operator> (const QCircularBuffer<T> &lhs, const QCircularBuffer<T> &rhs)
{ return operator<(rhs, lhs); }
template <typename T>
inline bool operator>=(const QCircularBuffer<T> &lhs, const QCircularBuffer<T> &rhs)
{ return !operator<(lhs, rhs); }
template <typename T>
inline bool operator<=(const QCircularBuffer<T> &lhs, const QCircularBuffer<T> &rhs)
{ return !operator>(lhs, rhs); }
// out-of-line function implementations:
#ifndef Q_QDOC
template <typename T>
QCircularBuffer<T>::QCircularBuffer(int amount)
: d(new Data())
{
// Allocate some raw memory
d->setData(d->allocate(amount));
d->capacity = amount;
// Initialize memory block to zero
memset(d->data(), 0, amount * sizeof(T));
}
template <typename T>
QCircularBuffer<T>::QCircularBuffer(int amount, const T &val)
: d(new Data())
{
// Allocate some raw memory
d->setData(d->allocate(amount));
d->capacity = amount;
// Initialize the objects. In this case we always use the placement new operator
T *b = d->data();
T *i = b + d->capacity;
while (i != b)
new (--i) T(val);
d->first = 0;
d->last = d->capacity - 1;
d->size = d->capacity;
}
template <typename T>
QCircularBuffer<T>::QCircularBuffer(int amount, int initialSize, const T &val)
: d(new Data())
{
Q_ASSERT_X(amount >= initialSize, "QCircularBuffer<T>::QCircularBuffer(int capacity, int size, const T &value)", "size is greater than capacity");
// Allocate some raw memory
d->setData(d->allocate(amount));
d->capacity = amount;
// Initialize the objects that need to be set to the specified value.
// In this case we always use the placement new operator
T *b = d->data();
T *i = b + initialSize;
while (i != b)
new (--i) T(val);
// Initialize the remaining memory to zero
memset(d->data() + initialSize, 0, (amount - initialSize) * sizeof(T));
d->first = 0;
d->last = initialSize - 1;
d->size = initialSize;
}
template <typename T>
void QCircularBuffer<T>::append(const T &val)
{
// If we have no capacity we do nothing
if (!d->capacity)
return;
d.detach();
if (d->size == d->capacity) {
// Buffer is full. Overwrite earliest item and rotate
d->data()[ d->first ] = val;
d->first = d->wraparound(++d->first);
d->last = d->wraparound(++d->last);
} else if (d->size != 0) {
// Buffer is partially full. Append data to end of array using appropriate method
int index = d->wraparound(d->first + d->size);
if (QTypeInfo<T>::isComplex)
new (d->data() + index) T(val);
else
d->data()[ index ] = val;
++d->size;
++d->last;
} else {
// Buffer is empty. Append data to end of array using appropriate method
d->size = 1;
d->first = d->last = 0;
if (QTypeInfo<T>::isComplex)
new (d->data() + d->first) T(val);
else
d->data()[ d->first ] = val;
}
}
template <typename T>
bool QCircularBuffer<T>::contains(const T &val) const
{
if (isLinearised()) {
T *b = d->data() + d->first;
T *i = b + d->size;
while (i != b)
if (*--i == val)
return true;
return false;
} else {
// Check the array from m_first to the end
T *b = d->data() + d->first;
T *i = d->data() + d->capacity;
while (i != b)
if (*--i == val)
return true;
// Check array from 0 to m_end
b = d->data();
i = d->data() + d->last + 1;
while (i != b)
if (*--i == val)
return true;
return false;
}
}
template <typename T>
int QCircularBuffer<T>::count(const T &val) const
{
int c = 0;
if (isLinearised()) {
T *b = d->data() + d->first;
T *i = b + d->size;
while (i != b)
if (*--i == val)
++c;
} else {
// Check the array from m_first to the end
T *b = d->data() + d->first;
T *i = d->data() + d->capacity;
while (i != b)
if (*--i == val)
++c;
// Check array from 0 to m_end
b = d->data();
i = d->data() + d->last + 1;
while (i != b)
if (*--i == val)
++c;
}
return c;
}
template <typename T>
QCircularBuffer<T> &QCircularBuffer<T>::fill(const T &val, int number)
{
Q_ASSERT_X(d->capacity >= number, "QCircularBuffer<T>::fill", "size is greater than capacity");
const T copy(val);
d.detach();
int oldSize = d->size;
d->size = (number < 0 ? d->size : number);
d->first = (number == 0 ? -1 : 0);
d->last = d->size - 1;
// Copy item into array size times
if (d->size) {
T *b = d->data();
T *i = d->data() + d->size;
while (i != b)
*--i = copy;
}
if (d->size < oldSize) {
// Cleanup old items beyond end of new array
T *b = d->data() + d->size;
T *i = d->data() + oldSize;
while (i-- != b) {
i->~T();
//TODO: Optimize to a single memset call
memset(i, 0, sizeof(T));
}
}
return *this;
}
template <typename T>
int QCircularBuffer<T>::indexOf(const T &val, int from) const
{
Q_ASSERT_X(from < d->size, "QCircularBuffer<T>::indexOf", "from is greater than last valid index");
if (from < 0)
from = qMax(from + d->size, 0);
else if (from >= d->size)
from = d->size - 1;
for (int i = from; i < d->size; ++i)
if (at(i) == val)
return i;
return -1;
}
template <typename T>
void QCircularBuffer<T>::insert(int i, int number, const T &val)
{
Q_ASSERT_X(i >= 0 && i <= d->size, "QCircularBuffer<T>::insert", "index out of range");
d.detach();
int freeCapacity = d->capacity - d->size;
// Calculate number of elements that will actually be inserted. This
// depends upon where the insertion has been requested and any spare
// capacity left in the buffer. This is because elements at higher
// indices will be pushed to the right and will potentially wrap around
// to overwrite earlier elements.
int numToInsert = qMin(number, i + freeCapacity);
// Calculate the number of elements at the beginning of the buffer that
// will be overwritten
int numToOverwrite = qMin(i, qMax(0, number - freeCapacity));
// Decide which way to shift to minimize the amount of copying required.
if (i < d->size / 2) {
// Inserting in lower half of buffer so we shift earlier items down
// Shift data at the bottom end down. This may only be a subset if some
// of the early data is to be overwritten.
if (QTypeInfo<T>::isStatic) {
int b = d->first + numToOverwrite;
int e = d->first + i - 1;
for (int j = b; j <= e; ++j) {
int srcIndex = j % d->capacity;
int dstIndex = (j - numToInsert + d->capacity) % d->capacity;
T *src = d->data() + srcIndex;
T *dst = d->data() + dstIndex;
new (dst) T(*src);
}
} else {
// We have a movable type so a simple memcopy (or maybe two or
// three) will suffice to shift the data at the bottom end
int numToMove = i - numToOverwrite;
if (numToMove > 0) {
int srcBegin = (d->first + numToOverwrite) % d->capacity;
int srcEnd = (d->first + i - 1) % d->capacity;
int dstBegin = (srcBegin - numToInsert + d->capacity) % d->capacity;
int dstEnd = (srcEnd - numToInsert + d->capacity) % d->capacity;
// Do we have any wrap-around problems to deal with?
bool srcRegionWraps = (srcEnd < srcBegin);
bool dstRegionWraps = (dstEnd < dstBegin);
if (!srcRegionWraps && dstRegionWraps) {
// Destination region wraps so do the move in two steps
int wrapCount = abs(srcBegin - numToInsert);
memmove(d->data() + d->capacity - wrapCount, d->data() + srcBegin, wrapCount * sizeof(T));
memmove(d->data(), d->data() + srcBegin + wrapCount, (numToMove - wrapCount) * sizeof(T));
} else if (srcRegionWraps && !dstRegionWraps) {
// Source region wraps so do the move in two steps
int wrapCount = d->capacity - srcBegin;
memmove(d->data() + dstBegin, d->data() + d->capacity - wrapCount, wrapCount * sizeof(T));
memmove(d->data() + dstBegin + numToInsert, d->data(), (numToMove - wrapCount) * sizeof(T));
} else if (srcRegionWraps && dstRegionWraps) {
// Source and destination regions wrap so we have to do this in three steps
int srcWrapCount = d->capacity - srcBegin;
memmove(d->data() + dstBegin, d->data() + d->capacity - srcWrapCount, srcWrapCount * sizeof(T));
memmove(d->data() + d->capacity - numToInsert, d->data(), numToInsert * sizeof(T));
memmove(d->data(), d->data() + numToInsert, (numToMove - srcWrapCount - numToInsert) * sizeof(T));
} else {
// No wrap around - do a single memmove
memmove(d->data() + dstBegin, d->data() + srcBegin, numToMove * sizeof(T));
}
}
}
// Insert the new items
int e = d->first + i;
int b = e - numToInsert;
for (int j = b; j < e; ++j) {
T *p = d->data() + ((j + d->capacity) % d->capacity);
new (p) T(val);
}
// Adjust the first, last and size indices as needed.
// NB. The last index never changes in this regime.
d->size += qMin(number, freeCapacity);
d->first = (d->first - (numToInsert - numToOverwrite) + d->capacity) % d->capacity;
} else {
// Inserting in upper half of buffer so we shift later items up
// Shift data at the top end up which may or may not overwrite some
// of the earliest data
if (QTypeInfo<T>::isStatic) {
int b = d->first + d->size - 1;
int e = d->first + i;
for (int j = b; j >= e; j--) {
int srcIndex = j % d->capacity;
int dstIndex = (j + numToInsert) % d->capacity;
T *src = d->data() + srcIndex;
T *dst = d->data() + dstIndex;
new (dst) T(*src);
}
} else {
// We have a movable type so a simple memcopy (or maybe two or
// three) will suffice to shift the data at the top end
int numToMove = d->size - i;
if (numToMove > 0) {
int srcBegin = (d->first + i) % d->capacity;
int srcEnd = d->last;
int dstBegin = (srcBegin + numToInsert) % d->capacity;
int dstEnd = (srcEnd + numToInsert) % d->capacity;
// Do we have any wrap-around problems to deal with?
bool srcRegionWraps = (srcEnd < srcBegin);
bool dstRegionWraps = (dstEnd < dstBegin);
if (!srcRegionWraps && dstRegionWraps) {
// Destination region wraps so do the move in two steps
int wrapCount = srcEnd + numToInsert - d->capacity + 1;
memmove(d->data(), d->data() + srcEnd - wrapCount + 1, wrapCount * sizeof(T));
memmove(d->data() + dstBegin, d->data() + srcBegin, (numToMove - wrapCount) * sizeof(T));
} else if (srcRegionWraps && !dstRegionWraps) {
// Source region wraps so do the move in two steps
int wrapCount = d->last + 1;
memmove(d->data() + numToInsert, d->data(), wrapCount * sizeof(T));
memmove(d->data() + dstBegin, d->data() + srcBegin, (numToMove - wrapCount) * sizeof(T));
} else if (srcRegionWraps && dstRegionWraps) {
// Source and destination regions wrap so we have to do this in three steps
int srcWrapCount = d->last + 1;
memmove(d->data() + numToInsert, d->data(), srcWrapCount * sizeof(T));
memmove(d->data(), d->data() + d->capacity - numToInsert, numToInsert * sizeof(T));
memmove(d->data() + dstBegin, d->data() + srcBegin, (numToMove - srcWrapCount - numToInsert) * sizeof(T));
} else {
// No wrap around - do a single memmove
memmove(d->data() + dstBegin, d->data() + srcBegin, numToMove * sizeof(T));
}
}
}
// Insert the new items
for (int j = d->first + i; j < d->first + i + numToInsert; ++j) {
T *p = d->data() + (j % d->capacity);
new (p) T(val);
}
// Adjust the first, last and size indices as needed
d->size += qMin(number, freeCapacity);
d->first = (d->first + numToOverwrite) % d->capacity;
d->last = (d->last + numToInsert) % d->capacity;
}
}
template <typename T>
int QCircularBuffer<T>::lastIndexOf(const T &val, int from) const
{
if (from < 0)
from = qMax(from + d->size, 0);
else if (from >= d->size)
from = d->size - 1;
for (int i = from; i >= 0; --i)
if (at(i) == val)
return i;
return -1;
}
template <typename T>
void QCircularBuffer<T>::prepend(const T &val)
{
// If we have no capacity we do nothing
if (!d->capacity)
return;
d.detach();
if (d->size == d->capacity) {
// Buffer is full. Overwrite last item and rotate
d->data()[ d->last ] = val;
d->first = (--d->first + d->capacity) % d->capacity;
d->last = (--d->last + d->capacity) % d->capacity;
} else if (d->size != 0) {
// Buffer is partially full. Prepend data to start of array using appropriate method
d->first = (--d->first + d->capacity) % d->capacity;
++d->size;
if (QTypeInfo<T>::isComplex)
new (d->data() + d->first) T(val);
else
d->data()[ d->first ] = val;
} else {
// Buffer is empty. Prepend data to start of array using appropriate method
d->size = 1;
d->first = d->last = d->capacity - 1;
if (QTypeInfo<T>::isComplex)
new (d->data() + d->first) T(val);
else
d->data()[ d->first ] = val;
}
}
template <typename T>
void QCircularBuffer<T>::remove(int i, int number)
{
Q_ASSERT_X(i >= 0 && number > 0 && i + number <= d->size, "QCircularBuffer<T>::remove", "index out of range");
d.detach();
// HACK (it actually makes sense, but requires some more thinking)
if ( i == 0 && !QTypeInfo<T>::isComplex ) {
d->first = d->wraparound( d->first + number );
d->size -= number;
return;
}
// Calculate the number of items that need to be moved downward
int numToMoveDown = d->size - number - i;
int numToMoveUp = i;
if (numToMoveDown < numToMoveUp) {
// Move higher items down
int numToMove = numToMoveDown;
if (QTypeInfo<T>::isComplex) {
// Copy items down from higher positions
int b = d->first + i;
int e = b + numToMove;
for (int j = b; j < e ; ++j) {
T *src = d->data() + ((j + number) % d->capacity);
T *dst = d->data() + (j % d->capacity);
new (dst) T(*src);
}
// Clean up items at end of buffer
for (int j = d->last; j > d->last - number; --j) {
T *p = d->data() + ((j + d->capacity) % d->capacity);
p->~T();
//TODO: Optimize to a single memset call
memset(p, 0, sizeof(T));
}
} else {
if (isLinearised()) {
// With a linearised buffer we can do a simple move and removal of items
memmove(d->data() + d->last - numToMove - number + 1, d->data() + d->last - numToMove + 1, numToMove * sizeof(T));
memset(d->data() + d->last - number + 1, 0, number * sizeof(T));
} else {
// With a non-linearised buffer we need to be careful of wrapping issues
int srcBegin = (d->last - numToMove + 1 + d->capacity) % d->capacity;
int srcEnd = d->last;
int dstBegin = (d->first + i) % d->capacity;
int dstEnd = (dstBegin + numToMove - 1) % d->capacity;
bool srcRegionWraps = (srcEnd < srcBegin);
bool dstRegionWraps = (dstEnd < dstBegin);
if (srcRegionWraps && !dstRegionWraps) {
// Source region wraps so do the move in two steps
int wrapCount = d->capacity - srcBegin;
memmove(d->data() + dstBegin, d->data() + srcBegin, wrapCount * sizeof(T));
memmove(d->data() + dstBegin + wrapCount, d->data(), (numToMove - wrapCount) * sizeof(T));
} else if (!srcRegionWraps && dstRegionWraps) {
// Destination region wraps so do the move in two steps
int wrapCount = number - srcBegin;
memmove(d->data() + d->capacity - wrapCount, d->data() + srcBegin, wrapCount * sizeof(T));
memmove(d->data(), d->data() + srcBegin + wrapCount, (numToMove - wrapCount) * sizeof(T));
} else if (srcRegionWraps && dstRegionWraps) {
// Source and destination regions wrap so we have to do this in three steps
int srcWrapCount = d->capacity - srcBegin;
memmove(d->data() + dstBegin, d->data() + srcBegin, srcWrapCount * sizeof(T));
memmove(d->data() + dstBegin + srcWrapCount, d->data(), number * sizeof(T));
memmove(d->data(), d->data() + number, (numToMove - srcWrapCount - number) * sizeof(T));
} else {
// No wrap around, so we can do this in one hit
memmove(d->data() + dstBegin, d->data() + srcBegin, numToMove * sizeof(T));
}
// We potentially have a disjoint region that needs zeroing
int zeroStart = (d->last - number + d->capacity + 1) % d->capacity;
int zeroEnd = d->last;
if (zeroEnd < zeroStart) {
// Region to be zeroed wraps. Do it in two steps.
memset(d->data(), 0, (d->last + 1) * sizeof(T));
memset(d->data() + zeroStart, 0, (number - d->last - 1) * sizeof(T));
} else {
// Region to be zeroed is contiguous
memset(d->data() + zeroStart, 0, number * sizeof(T));
}
}
}
// Adjust the indices
d->size -= number;
d->last = (d->last - number + d->capacity) % d->capacity;
} else {
// Move lower items up
int numToMove = numToMoveUp;
if (QTypeInfo<T>::isComplex) {
// Copy items up from lower positions
int b = d->first + i - 1;
int e = b - numToMove;
for (int j = b; j > e ; --j) {
T *src = d->data() + ((j + d->capacity) % d->capacity);
T *dst = d->data() + ((j + d->capacity + number) % d->capacity);
new (dst) T(*src);
}
// Clean up items at start of buffer
for (int j = d->first; j < d->first + number; ++j) {
T *p = d->data() + (j % d->capacity);
p->~T();
//TODO: Optimize to a single memset call
memset(p, 0, sizeof(T));
}
} else {
if (isLinearised()) {
// With a linearised buffer we can do a simple move and removal of items
memmove(d->data() + d->first + number, d->data() + d->first, numToMove * sizeof(T));
memset(d->data() + d->first, 0, number * sizeof(T));
} else {
// With a non-linearised buffer we need to be careful of wrapping issues
int srcBegin = d->first;
int srcEnd = (srcBegin + numToMove - 1) % d->capacity;
int dstBegin = (srcBegin + number) % d->capacity;
int dstEnd = (dstBegin + numToMove - 1) % d->capacity;
bool srcRegionWraps = (srcEnd < srcBegin);
bool dstRegionWraps = (dstEnd < dstBegin);
if (srcRegionWraps && !dstRegionWraps) {
// Source region wraps so do the move in two steps
int wrapCount = srcEnd + 1;
memmove(d->data() + dstEnd - wrapCount + 1, d->data(), wrapCount * sizeof(T));
memmove(d->data() + dstBegin, d->data() + srcBegin, (numToMove - wrapCount) * sizeof(T));
} else if (!srcRegionWraps && dstRegionWraps) {
// Destination region wraps so do the move in two steps
int wrapCount = dstEnd + 1;
memmove(d->data(), d->data() + srcEnd - wrapCount + 1, wrapCount * sizeof(T));
memmove(d->data() + dstBegin, d->data() + srcBegin, (numToMove - wrapCount) * sizeof(T));
} else if (srcRegionWraps && dstRegionWraps) {
// Source and destination regions wrap so we have to do this in three steps
int srcWrapCount = srcEnd + 1;
memmove(d->data() + dstEnd - srcWrapCount + 1, d->data(), srcWrapCount * sizeof(T));
memmove(d->data(), d->data() + d->capacity - number, number * sizeof(T));
memmove(d->data() + dstBegin, d->data() + srcBegin, (numToMove - srcWrapCount - number) * sizeof(T));
} else {
// No wrap around, so we can do this in one hit
memmove(d->data() + dstBegin, d->data() + srcBegin, numToMove * sizeof(T));
}
// We potentially have a disjoint region that needs zeroing
int zeroStart = d->first;
int zeroEnd = (zeroStart + number - 1) % d->capacity;
if (zeroEnd < zeroStart) {
// Region to be zeroed wraps. Do it in two steps.
memset(d->data() + zeroStart, 0, (d->capacity - d->first) * sizeof(T));
memset(d->data(), 0, (number - d->capacity + d->first) * sizeof(T));
} else {
// Region to be zeroed is contiguous
memset(d->data() + zeroStart, 0, number * sizeof(T));
}
}
}
// Adjust the indices
d->size -= number;
d->first = (d->first + number) % d->capacity;
}
}
template <typename T>
void QCircularBuffer<T>::setCapacity(int amount)
{
if (amount == d->capacity)
return;
d.detach();
// Allocate some new raw memory
T *newData = d->allocate(amount);
// How many items can we copy across?
int newSize = qMin(d->size, amount);
if (QTypeInfo<T>::isComplex) {
// Copy across the elements from the original array
for (int i = 0; i < newSize; ++i) {
T *src = d->data() + ((d->first + i) % d->capacity);
T *dst = newData + i;
new (dst) T(*src);
}
// Destroy the original items.
// The type is complex so we manually call the destructor for each item
// since we used the placement new operator to instantiate them
T *b = d->data();
T *i = b + d->capacity;
while (i-- != b)
i->~T();
} else {
// Copy across the elements from the original array. The source region
// potentially wraps so we may have to do this in one or two steps
if (isLinearised()) {
memmove(newData, d->data() + d->first, newSize * sizeof(T));
} else {
int step1Size = qMin(newSize, d->capacity - d->first);
memmove(newData, d->data() + d->first, step1Size * sizeof(T));
int step2Size = qMax(0, qMin(newSize - d->capacity + d->first, d->last + 1));
memmove(newData + step1Size, d->data(), step2Size * sizeof(T));
}
}
// Initialize any memory outside of the valid buffer (ie the unused items)
memset(newData + newSize, 0, (amount - newSize) * sizeof(T));
// Release the raw memory for the old array
d->deallocate(d->data());
// Assign the new memory to be our buffer data and fix indices
d->setData(newData);
d->capacity = amount;
d->first = 0;
d->size = newSize;
d->last = d->size - 1;
}
template <typename T>
void QCircularBuffer<T>::resize(int newSize)
{
Q_ASSERT_X(newSize >= 0 && newSize <= d->capacity, "QCircularBuffer<T>::resize", "size out of range");
d.detach();
if (newSize < d->size) {
remove(newSize, d->size - newSize);
} else if (newSize > d->size) {
const T t = T();
insert(d->size, newSize - d->size, t);
}
}
template <typename T>
QCircularBuffer<T> &QCircularBuffer<T>::operator+=(const QCircularBuffer<T> &other)
{
d.detach();
// How many items do we need to copy? No point in ever copying across a number
// greater than capacity
int numToCopy = qMin(other.size(), d->capacity);
int offset = other.size() - numToCopy;
for (int i = 0; i < numToCopy; ++i)
append(other.at(offset + i));
return *this;
}
template <typename T>
QCircularBuffer<T> &QCircularBuffer<T>::operator+=(const QVector<T> &other)
{
d.detach();
// How many items do we need to copy? No point in ever copying across a number
// greater than capacity
int numToCopy = qMin(other.size(), d->capacity);
int offset = other.size() - numToCopy;
for (int i = 0; i < numToCopy; ++i)
append(other.at(offset + i));
return *this;
}
template <typename T>
QCircularBuffer<T> &QCircularBuffer<T>::operator+=(const QList<T> &other)
{
d.detach();
// How many items do we need to copy? No point in ever copying across a number
// greater than capacity
int numToCopy = qMin(other.size(), d->capacity);
int offset = other.size() - numToCopy;
for (int i = 0; i < numToCopy; ++i)
append(other.at(offset + i));
return *this;
}
template <typename T>
QList<T> QCircularBuffer<T>::toList() const
{
QList<T> list;
list.reserve(size());
for (int i = 0; i < size(); ++i)
list.append(at(i));
return list;
}
template <typename T>
QVector<T> QCircularBuffer<T>::toVector() const
{
QVector<T> vector;
vector.reserve(size());
for (int i = 0; i < size(); ++i)
vector.append(at(i));
return vector;
}
template <typename T>
QCircularBuffer<T> operator+(const QCircularBuffer<T> &lhs, const QCircularBuffer<T> &rhs)
{
QCircularBuffer<T> circ(lhs.size() + rhs.size());
for (int i = 0; i < lhs.size(); ++i)
circ.append(lhs.at(i));
for (int i = 0; i < rhs.size(); ++i)
circ.append(rhs.at(i));
return circ;
}
#endif // Q_QDOC
Q_DECLARE_SEQUENTIAL_ITERATOR(CircularBuffer)
Q_DECLARE_MUTABLE_SEQUENTIAL_ITERATOR(CircularBuffer)
} //Qt3D
QT_END_NAMESPACE
#endif // QT3DCORE_QCIRCULARBUFFER_H