/**************************************************************************** ** ** Copyright (C) 2014 Klaralvdalens Datakonsult AB (KDAB). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the Qt3D module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL3$ ** 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 http://www.qt.io/terms-conditions. For further ** information use the contact form at http://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.LGPLv3 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.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 later as published by the Free ** Software Foundation and appearing in the file LICENSE.GPL included in ** the packaging of this file. Please review the following information to ** ensure the GNU General Public License version 2.0 requirements will be ** met: http://www.gnu.org/licenses/gpl-2.0.html. ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #ifndef QT3D_QCIRCULARBUFFER_H #define QT3D_QCIRCULARBUFFER_H #include #include #include #include #include #ifdef Q_COMPILER_INITIALIZER_LISTS # include #endif #include #include #include #include #include #include QT_BEGIN_NAMESPACE namespace Qt3D { class CircularBufferData : public QSharedData { protected: CircularBufferData() : data(Q_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 class TypedCircularBufferData : public CircularBufferData { template explicit TypedCircularBufferData(InputIterator f, InputIterator l, std::input_iterator_tag) Q_DECL_EQ_DELETE; public: TypedCircularBufferData() : CircularBufferData() {} template 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::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(CircularBufferData::allocate(count, sizeof(T))); } using CircularBufferData::deallocate; T *data() const { return static_cast(CircularBufferData::data); } void setData(T *newdata) { CircularBufferData::data = static_cast(newdata); } }; template class QCircularBuffer { typedef TypedCircularBufferData Data; public: typedef QPair array_range; typedef QPair 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 list) : d(new Data(list.begin(), list.end(), std::random_access_iterator_tag())) {} #endif template explicit QCircularBuffer(ForwardIterator f, ForwardIterator l) : d(new Data(f, l, typename std::iterator_traits::iterator_category())) {} QCircularBuffer(const QCircularBuffer &other) : d(other.d) {} void swap(QCircularBuffer &other) { d.swap(other.d); } QCircularBuffer &operator = (const QCircularBuffer &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(Q_NULLPTR), index(-1) {} iterator(QCircularBuffer *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::iterator::operator<", "iterators use different buffers"); return index < other.index; } bool operator<=(const iterator &other) const { Q_ASSERT_X(buffer == other.buffer, "QCircularBuffer::iterator::operator<=", "iterators use different buffers"); return index <= other.index; } bool operator>(const iterator &other) const { Q_ASSERT_X(buffer == other.buffer, "QCircularBuffer::iterator::operator>", "iterators use different buffers"); return index > other.index; } bool operator>=(const iterator &other) const { Q_ASSERT_X(buffer == other.buffer, "QCircularBuffer::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::iterator::operator-", "iterators use different buffers"); return index - other.index; } private: QCircularBuffer *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(Q_NULLPTR), index(-1) {} const_iterator(const QCircularBuffer *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::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::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::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::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::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::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::const_iterator::operator-", "iterators use different buffers"); return index - other.index; } private: const QCircularBuffer *buffer; int index; friend class QCircularBuffer; }; friend class const_iterator; typedef std::reverse_iterator reverse_iterator; typedef std::reverse_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::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::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::operator[]", "index out of range"); return d->data()[d->wraparound(d->first + i)]; } int capacity() const { return d->capacity; } void clear() { *this = QCircularBuffer(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(Q_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(Q_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(Q_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(Q_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(Q_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(Q_NULLPTR, 0); return const_array_range(d->data(), d->last + 1); } bool endsWith(const T &val) const { return !isEmpty() && last() == val; } QCircularBuffer &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 fromList(const QList &list) { return QCircularBuffer(list.begin(), list.end()); } static QCircularBuffer fromVector(const QVector &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::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::max(); } int sizeAvailable() const { return d->capacity - d->size; } void squeeze() { setCapacity(size()); } bool startsWith(const T &val) const { return !isEmpty() && first() == val; } QList toList() const; QVector 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 &operator+=(const T &other) { append(other); return *this; } QCircularBuffer &operator+=(const QCircularBuffer &other); QCircularBuffer &operator+=(const QVector &other); QCircularBuffer &operator+=(const QList &other); QCircularBuffer &operator<<(const T &other) { append(other); return *this; } QCircularBuffer &operator<<(const QCircularBuffer &other) { *this += other; return *this; } QCircularBuffer &operator<<(const QVector &other) { *this += other; return *this; } QCircularBuffer &operator<<(const QList &other) { *this += other; return *this; } inline bool isSharedWith(const QCircularBuffer &other) const { return d == other.d; } private: QExplicitlySharedDataPointer d; }; template QCircularBuffer operator+(const QCircularBuffer &lhs, const QCircularBuffer &rhs); template inline bool operator==(const QCircularBuffer &lhs, const QCircularBuffer &rhs) { return lhs.isSharedWith(rhs) || (lhs.size() == rhs.size() && std::equal(lhs.begin(), lhs.end(), rhs.begin())); } template inline bool operator!=(const QCircularBuffer &lhs, const QCircularBuffer &rhs) { return !operator==(lhs, rhs); } template inline void swap(QCircularBuffer &lhs, QCircularBuffer &rhs) { lhs.swap(rhs); } template inline bool operator< (const QCircularBuffer &lhs, const QCircularBuffer &rhs) { return std::lexicographical_compare(lhs.begin(), lhs.end(), rhs.begin(), rhs.end()); } template inline bool operator> (const QCircularBuffer &lhs, const QCircularBuffer &rhs) { return operator<(rhs, lhs); } template inline bool operator>=(const QCircularBuffer &lhs, const QCircularBuffer &rhs) { return !operator<(lhs, rhs); } template inline bool operator<=(const QCircularBuffer &lhs, const QCircularBuffer &rhs) { return !operator>(lhs, rhs); } // out-of-line function implementations: #ifndef Q_QDOC template QCircularBuffer::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 QCircularBuffer::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 QCircularBuffer::QCircularBuffer(int amount, int initialSize, const T &val) : d(new Data()) { Q_ASSERT_X(amount >= initialSize, "QCircularBuffer::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 void QCircularBuffer::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::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::isComplex) new (d->data() + d->first) T(val); else d->data()[ d->first ] = val; } } template bool QCircularBuffer::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 int QCircularBuffer::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 QCircularBuffer &QCircularBuffer::fill(const T &val, int number) { Q_ASSERT_X(d->capacity >= number, "QCircularBuffer::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 int QCircularBuffer::indexOf(const T &val, int from) const { Q_ASSERT_X(from < d->size, "QCircularBuffer::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 void QCircularBuffer::insert(int i, int number, const T &val) { Q_ASSERT_X(i >= 0 && i <= d->size, "QCircularBuffer::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::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::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 int QCircularBuffer::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 void QCircularBuffer::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::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::isComplex) new (d->data() + d->first) T(val); else d->data()[ d->first ] = val; } } template void QCircularBuffer::remove(int i, int number) { Q_ASSERT_X(i >= 0 && number > 0 && i + number <= d->size, "QCircularBuffer::remove", "index out of range"); d.detach(); // HACK (it actually makes sense, but requires some more thinking) if ( i == 0 && !QTypeInfo::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::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::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 void QCircularBuffer::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::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 void QCircularBuffer::resize(int newSize) { Q_ASSERT_X(newSize >= 0 && newSize <= d->capacity, "QCircularBuffer::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 QCircularBuffer &QCircularBuffer::operator+=(const QCircularBuffer &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 QCircularBuffer &QCircularBuffer::operator+=(const QVector &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 QCircularBuffer &QCircularBuffer::operator+=(const QList &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 QList QCircularBuffer::toList() const { QList list; list.reserve(size()); for (int i = 0; i < size(); ++i) list.append(at(i)); return list; } template QVector QCircularBuffer::toVector() const { QVector vector; vector.reserve(size()); for (int i = 0; i < size(); ++i) vector.append(at(i)); return vector; } template QCircularBuffer operator+(const QCircularBuffer &lhs, const QCircularBuffer &rhs) { QCircularBuffer 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 // QT3D_QCIRCULARBUFFER_H