| 1 | /**************************************************************************** |
| 2 | ** |
| 3 | ** Copyright (C) 2014 Klaralvdalens Datakonsult AB (KDAB). |
| 4 | ** Contact: https://www.qt.io/licensing/ |
| 5 | ** |
| 6 | ** This file is part of the Qt3D module of the Qt Toolkit. |
| 7 | ** |
| 8 | ** $QT_BEGIN_LICENSE:LGPL$ |
| 9 | ** Commercial License Usage |
| 10 | ** Licensees holding valid commercial Qt licenses may use this file in |
| 11 | ** accordance with the commercial license agreement provided with the |
| 12 | ** Software or, alternatively, in accordance with the terms contained in |
| 13 | ** a written agreement between you and The Qt Company. For licensing terms |
| 14 | ** and conditions see https://www.qt.io/terms-conditions. For further |
| 15 | ** information use the contact form at https://www.qt.io/contact-us. |
| 16 | ** |
| 17 | ** GNU Lesser General Public License Usage |
| 18 | ** Alternatively, this file may be used under the terms of the GNU Lesser |
| 19 | ** General Public License version 3 as published by the Free Software |
| 20 | ** Foundation and appearing in the file LICENSE.LGPL3 included in the |
| 21 | ** packaging of this file. Please review the following information to |
| 22 | ** ensure the GNU Lesser General Public License version 3 requirements |
| 23 | ** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. |
| 24 | ** |
| 25 | ** GNU General Public License Usage |
| 26 | ** Alternatively, this file may be used under the terms of the GNU |
| 27 | ** General Public License version 2.0 or (at your option) the GNU General |
| 28 | ** Public license version 3 or any later version approved by the KDE Free |
| 29 | ** Qt Foundation. The licenses are as published by the Free Software |
| 30 | ** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 |
| 31 | ** included in the packaging of this file. Please review the following |
| 32 | ** information to ensure the GNU General Public License requirements will |
| 33 | ** be met: https://www.gnu.org/licenses/gpl-2.0.html and |
| 34 | ** https://www.gnu.org/licenses/gpl-3.0.html. |
| 35 | ** |
| 36 | ** $QT_END_LICENSE$ |
| 37 | ** |
| 38 | ****************************************************************************/ |
| 39 | |
| 40 | #ifndef QT3DCORE_QCIRCULARBUFFER_H |
| 41 | #define QT3DCORE_QCIRCULARBUFFER_H |
| 42 | |
| 43 | // |
| 44 | // W A R N I N G |
| 45 | // ------------- |
| 46 | // |
| 47 | // This file is not part of the Qt API. It exists for the convenience |
| 48 | // of other Qt classes. This header file may change from version to |
| 49 | // version without notice, or even be removed. |
| 50 | // |
| 51 | // We mean it. |
| 52 | // |
| 53 | |
| 54 | #include <Qt3DCore/qt3dcore_global.h> |
| 55 | #include <QtCore/QtGlobal> |
| 56 | #include <QtCore/qlist.h> |
| 57 | #include <QtCore/qpair.h> |
| 58 | #include <QtCore/qshareddata.h> |
| 59 | #include <QtCore/qvector.h> |
| 60 | |
| 61 | #include <algorithm> |
| 62 | #include <iterator> |
| 63 | #include <limits> |
| 64 | #include <memory> |
| 65 | #include <new> |
| 66 | |
| 67 | |
| 68 | #ifdef Q_COMPILER_INITIALIZER_LISTS |
| 69 | # include <initializer_list> |
| 70 | #endif |
| 71 | |
| 72 | QT_BEGIN_NAMESPACE |
| 73 | |
| 74 | namespace Qt3DCore { |
| 75 | |
| 76 | class CircularBufferData : public QSharedData |
| 77 | { |
| 78 | protected: |
| 79 | CircularBufferData() |
| 80 | : data(nullptr), |
| 81 | capacity(0), |
| 82 | size(0), |
| 83 | first(-1), |
| 84 | last(-1) |
| 85 | {} |
| 86 | |
| 87 | ~CircularBufferData() |
| 88 | { |
| 89 | // Release the raw memory |
| 90 | deallocate(p: data); |
| 91 | } |
| 92 | |
| 93 | int wraparound(int index) const |
| 94 | { |
| 95 | index = index < capacity ? index : (index - capacity); |
| 96 | Q_ASSERT(index < capacity); // make sure that wrapping around once was enough |
| 97 | return index; |
| 98 | } |
| 99 | |
| 100 | void *allocate(int count, size_t sizeOfT) |
| 101 | { return operator new[](count * sizeOfT); } |
| 102 | void deallocate(void *p) |
| 103 | { operator delete[](p); } |
| 104 | |
| 105 | void *data; // Array of the actual data |
| 106 | public: |
| 107 | int capacity; // Size of the m_data array |
| 108 | int size; // Number of elements of m_data actually used |
| 109 | int first; // Index in m_data of the first element of the circular buffer |
| 110 | int last; // Index in m_data of the last element of the circular buffer |
| 111 | }; |
| 112 | |
| 113 | template <typename T> |
| 114 | class TypedCircularBufferData : public CircularBufferData |
| 115 | { |
| 116 | template <typename InputIterator> |
| 117 | explicit TypedCircularBufferData(InputIterator f, InputIterator l, std::input_iterator_tag) Q_DECL_EQ_DELETE; |
| 118 | public: |
| 119 | TypedCircularBufferData() : CircularBufferData() {} |
| 120 | template <typename ForwardIterator> |
| 121 | explicit TypedCircularBufferData(ForwardIterator f, ForwardIterator l, std::forward_iterator_tag) |
| 122 | { |
| 123 | const int n = int(std::distance(f, l)); |
| 124 | CircularBufferData::data = allocate(count: n); |
| 125 | std::uninitialized_copy(f, l, data()); |
| 126 | first = 0; |
| 127 | last = n - 1; |
| 128 | size = capacity = n; |
| 129 | } |
| 130 | ~TypedCircularBufferData() |
| 131 | { |
| 132 | if (QTypeInfo<T>::isComplex && size != 0) { |
| 133 | // The type is complex so we manually call the destructor for each item |
| 134 | // since we used the placement new operator to instantiate them |
| 135 | if (first <= last) { |
| 136 | // Destroy items from first to last |
| 137 | T *b = data() + first; |
| 138 | T *i = b + size; |
| 139 | while (i-- != b) |
| 140 | i->~T(); |
| 141 | } else { |
| 142 | // Destroy items at end of data array |
| 143 | T *b = data() + first; |
| 144 | T *i = data() + capacity; |
| 145 | while (i-- != b) |
| 146 | i->~T(); |
| 147 | |
| 148 | // Destroy items at beginning of data array |
| 149 | b = data(); |
| 150 | i = b + last; |
| 151 | while (i-- != b) |
| 152 | i->~T(); |
| 153 | } |
| 154 | } |
| 155 | |
| 156 | } |
| 157 | |
| 158 | using CircularBufferData::wraparound; |
| 159 | T *allocate(int count) { return static_cast<T*>(CircularBufferData::allocate(count, sizeOfT: sizeof(T))); } |
| 160 | using CircularBufferData::deallocate; |
| 161 | T *data() const { return static_cast<T*>(CircularBufferData::data); } |
| 162 | void setData(T *newdata) { CircularBufferData::data = static_cast<void*>(newdata); } |
| 163 | }; |
| 164 | |
| 165 | template <typename T> |
| 166 | class QCircularBuffer |
| 167 | { |
| 168 | typedef TypedCircularBufferData<T> Data; |
| 169 | public: |
| 170 | typedef QPair<T*,int> array_range; |
| 171 | typedef QPair<const T*,int> const_array_range; |
| 172 | typedef array_range ArrayRange; |
| 173 | typedef const_array_range ConstArrayRange; |
| 174 | |
| 175 | QCircularBuffer() |
| 176 | : d(new Data()) |
| 177 | {} |
| 178 | |
| 179 | explicit QCircularBuffer(int amount); |
| 180 | explicit QCircularBuffer(int amount, const T &val); |
| 181 | explicit QCircularBuffer(int amount, int initialSize, const T &value); |
| 182 | #ifdef Q_COMPILER_INITIALIZER_LISTS |
| 183 | QCircularBuffer(std::initializer_list<T> list) |
| 184 | : d(new Data(list.begin(), list.end(), std::random_access_iterator_tag())) |
| 185 | {} |
| 186 | #endif |
| 187 | template <typename ForwardIterator> |
| 188 | explicit QCircularBuffer(ForwardIterator f, ForwardIterator l) |
| 189 | : d(new Data(f, l, typename std::iterator_traits<ForwardIterator>::iterator_category())) |
| 190 | {} |
| 191 | |
| 192 | QCircularBuffer(const QCircularBuffer<T> &other) |
| 193 | : d(other.d) |
| 194 | {} |
| 195 | |
| 196 | void swap(QCircularBuffer &other) { d.swap(other.d); } |
| 197 | |
| 198 | QCircularBuffer<T> &operator = (const QCircularBuffer<T> &other) |
| 199 | { |
| 200 | d = other.d; |
| 201 | return *this; |
| 202 | } |
| 203 | |
| 204 | ~QCircularBuffer() {} |
| 205 | |
| 206 | class iterator |
| 207 | { |
| 208 | public: |
| 209 | typedef std::random_access_iterator_tag iterator_category; |
| 210 | typedef ptrdiff_t difference_type; |
| 211 | typedef T value_type; |
| 212 | typedef T *pointer; |
| 213 | typedef T &reference; |
| 214 | |
| 215 | Q_DECL_CONSTEXPR iterator() : buffer(nullptr), index(-1) {} |
| 216 | iterator(QCircularBuffer<T> *buf, int idx) |
| 217 | : buffer(buf), index(idx) |
| 218 | {} |
| 219 | |
| 220 | T &operator*() const { return (*buffer)[ index ]; } |
| 221 | T *operator->() const { return &(*buffer)[index]; } |
| 222 | T &operator[](int j) const { return (*buffer)[ index + j ]; } |
| 223 | |
| 224 | bool operator==(const iterator &other) const |
| 225 | { |
| 226 | return (buffer == other.buffer && index == other.index); |
| 227 | } |
| 228 | |
| 229 | bool operator!=(const iterator &other) const |
| 230 | { |
| 231 | return (buffer != other.buffer || index != other.index); |
| 232 | } |
| 233 | |
| 234 | bool operator<(const iterator &other) const |
| 235 | { |
| 236 | Q_ASSERT_X(buffer == other.buffer, "QCircularBuffer<T>::iterator::operator<" , "iterators use different buffers" ); |
| 237 | return index < other.index; |
| 238 | } |
| 239 | |
| 240 | bool operator<=(const iterator &other) const |
| 241 | { |
| 242 | Q_ASSERT_X(buffer == other.buffer, "QCircularBuffer<T>::iterator::operator<=" , "iterators use different buffers" ); |
| 243 | return index <= other.index; |
| 244 | } |
| 245 | |
| 246 | bool operator>(const iterator &other) const |
| 247 | { |
| 248 | Q_ASSERT_X(buffer == other.buffer, "QCircularBuffer<T>::iterator::operator>" , "iterators use different buffers" ); |
| 249 | return index > other.index; |
| 250 | } |
| 251 | |
| 252 | bool operator>=(const iterator &other) const |
| 253 | { |
| 254 | Q_ASSERT_X(buffer == other.buffer, "QCircularBuffer<T>::iterator::operator>=" , "iterators use different buffers" ); |
| 255 | return index >= other.index; |
| 256 | } |
| 257 | |
| 258 | iterator &operator++() { ++index; return *this; } |
| 259 | iterator operator++(int) |
| 260 | { |
| 261 | iterator ans = *this; |
| 262 | ++index; |
| 263 | return ans; |
| 264 | } |
| 265 | |
| 266 | iterator &operator--() { --index; return *this; } |
| 267 | iterator operator--(int) |
| 268 | { |
| 269 | iterator ans = *this; |
| 270 | --index; |
| 271 | return ans; |
| 272 | } |
| 273 | |
| 274 | iterator &operator+=(int j) { index += j; return *this; } |
| 275 | iterator &operator-=(int j) { index -= j; return *this; } |
| 276 | iterator operator+(int j) const { return iterator(buffer, index + j); } |
| 277 | iterator operator-(int j) const { return iterator(buffer, index - j); } |
| 278 | int operator-(iterator other) const |
| 279 | { |
| 280 | Q_ASSERT_X(buffer == other.buffer, "QCircularBuffer<T>::iterator::operator-" , "iterators use different buffers" ); |
| 281 | return index - other.index; |
| 282 | } |
| 283 | |
| 284 | private: |
| 285 | QCircularBuffer<T> *buffer; |
| 286 | int index; |
| 287 | friend class QCircularBuffer; |
| 288 | }; |
| 289 | friend class iterator; |
| 290 | |
| 291 | class const_iterator |
| 292 | { |
| 293 | public: |
| 294 | typedef std::random_access_iterator_tag iterator_category; |
| 295 | typedef ptrdiff_t difference_type; |
| 296 | typedef T value_type; |
| 297 | typedef const T *pointer; |
| 298 | typedef const T &reference; |
| 299 | |
| 300 | Q_DECL_CONSTEXPR const_iterator() : buffer(nullptr), index(-1) {} |
| 301 | const_iterator(const QCircularBuffer<T> *buff, int idx) |
| 302 | : buffer(buff), index(idx) |
| 303 | {} |
| 304 | const_iterator(const iterator &other) |
| 305 | : buffer(other.buffer), index(other.index) |
| 306 | {} |
| 307 | |
| 308 | const T &operator*() const { return buffer->at(index); } |
| 309 | const T *operator->() const { return &buffer->at(index); } |
| 310 | const T &operator[](int j) const { return buffer->at(index + j); } |
| 311 | |
| 312 | bool operator==(const const_iterator &other) const |
| 313 | { |
| 314 | Q_ASSERT_X(buffer == other.buffer, "QCircularBuffer<T>::const_iterator::operator==" , "iterators use different buffers" ); |
| 315 | return index == other.index; |
| 316 | } |
| 317 | |
| 318 | bool operator!=(const const_iterator &other) const |
| 319 | { |
| 320 | Q_ASSERT_X(buffer == other.buffer, "QCircularBuffer<T>::const_iterator::operator!=" , "iterators use different buffers" ); |
| 321 | return index != other.index; |
| 322 | } |
| 323 | |
| 324 | bool operator<(const const_iterator &other) const |
| 325 | { |
| 326 | Q_ASSERT_X(buffer == other.buffer, "QCircularBuffer<T>::const_iterator::operator<" , "iterators use different buffers" ); |
| 327 | return index < other.index; |
| 328 | } |
| 329 | |
| 330 | bool operator<=(const const_iterator &other) const |
| 331 | { |
| 332 | Q_ASSERT_X(buffer == other.buffer, "QCircularBuffer<T>::const_iterator::operator<=" , "iterators use different buffers" ); |
| 333 | return index <= other.index; |
| 334 | } |
| 335 | |
| 336 | bool operator>(const const_iterator &other) const |
| 337 | { |
| 338 | Q_ASSERT_X(buffer == other.buffer, "QCircularBuffer<T>::const_iterator::operator>" , "iterators use different buffers" ); |
| 339 | return index > other.index; |
| 340 | } |
| 341 | |
| 342 | bool operator>=(const const_iterator &other) const |
| 343 | { |
| 344 | Q_ASSERT_X(buffer == other.buffer, "QCircularBuffer<T>::const_iterator::operator>=" , "iterators use different buffers" ); |
| 345 | return index >= other.index; |
| 346 | } |
| 347 | |
| 348 | const_iterator &operator++() { ++index; return *this; } |
| 349 | const_iterator operator++(int) |
| 350 | { |
| 351 | const_iterator ans = *this; |
| 352 | ++index; |
| 353 | return ans; |
| 354 | } |
| 355 | |
| 356 | const_iterator &operator--() { --index; return *this; } |
| 357 | const_iterator operator--(int) |
| 358 | { |
| 359 | const_iterator ans = *this; |
| 360 | --index; |
| 361 | return ans; |
| 362 | } |
| 363 | |
| 364 | const_iterator &operator+=(int j) { index += j; return *this; } |
| 365 | const_iterator &operator-=(int j) { index -= j; return *this; } |
| 366 | const_iterator operator+(int j) const { return const_iterator(buffer, index + j); } |
| 367 | const_iterator operator-(int j) const { return const_iterator(buffer, index - j); } |
| 368 | int operator-(const_iterator other) const |
| 369 | { |
| 370 | Q_ASSERT_X(buffer == other.buffer, "QCircularBuffer<T>::const_iterator::operator-" , "iterators use different buffers" ); |
| 371 | return index - other.index; |
| 372 | } |
| 373 | |
| 374 | private: |
| 375 | const QCircularBuffer<T> *buffer; |
| 376 | int index; |
| 377 | friend class QCircularBuffer; |
| 378 | }; |
| 379 | friend class const_iterator; |
| 380 | |
| 381 | typedef std::reverse_iterator<iterator> reverse_iterator; |
| 382 | typedef std::reverse_iterator<const_iterator> const_reverse_iterator; |
| 383 | |
| 384 | iterator begin() { return iterator(this, 0); } |
| 385 | const_iterator begin() const { return const_iterator(this, 0); } |
| 386 | const_iterator cbegin() const { return const_iterator(this, 0); } |
| 387 | reverse_iterator rbegin() { return reverse_iterator(end()); } |
| 388 | const_reverse_iterator rbegin() const { return const_reverse_iterator(end()); } |
| 389 | const_reverse_iterator crbegin() const { return const_reverse_iterator(end()); } |
| 390 | const_iterator constBegin() const { return const_iterator(this, 0); } |
| 391 | iterator end() { return iterator(this, d->size); } |
| 392 | const_iterator end() const { return const_iterator(this, d->size); } |
| 393 | const_iterator cend() const { return const_iterator(this, d->size); } |
| 394 | reverse_iterator rend() { return reverse_iterator(begin()); } |
| 395 | const_reverse_iterator rend() const { return const_reverse_iterator(begin()); } |
| 396 | const_reverse_iterator crend() const { return const_reverse_iterator(begin()); } |
| 397 | const_iterator constEnd() const { return const_iterator(this, d->size); } |
| 398 | iterator insert(const_iterator before, int number, const T &val) |
| 399 | { |
| 400 | insert(before.index, number, val); |
| 401 | return iterator(this, before.index); |
| 402 | } |
| 403 | iterator insert(const_iterator before, const T &val) { return insert(before, 1, val); } |
| 404 | iterator erase(const_iterator b, const_iterator e) |
| 405 | { |
| 406 | int number = e - b; |
| 407 | remove(b.index, number); |
| 408 | return iterator(this, e.index - number); |
| 409 | } |
| 410 | iterator erase(const_iterator pos) { return erase(pos, pos + 1); } |
| 411 | |
| 412 | // STL compatibility |
| 413 | typedef T value_type; |
| 414 | typedef value_type *pointer; |
| 415 | typedef const value_type *const_pointer; |
| 416 | typedef value_type &reference; |
| 417 | typedef const value_type &const_reference; |
| 418 | typedef ptrdiff_t difference_type; |
| 419 | typedef iterator Iterator; |
| 420 | typedef const_iterator ConstIterator; |
| 421 | typedef int size_type; |
| 422 | |
| 423 | void push_back(const T &t) { append(val: t); } |
| 424 | void push_front(const T &t) { prepend(val: t); } |
| 425 | void pop_back() { Q_ASSERT(!isEmpty()); erase(end() - 1); } |
| 426 | void pop_front() { Q_ASSERT(!isEmpty()); erase(begin()); } |
| 427 | bool empty() const { return isEmpty(); } |
| 428 | T &front() { return first(); } |
| 429 | const T &front() const { return first(); } |
| 430 | T &back() { return last(); } |
| 431 | const T &back() const { return last(); } |
| 432 | |
| 433 | QAtomicInt refCount() const { return d->ref; } |
| 434 | |
| 435 | void append(const T &val); |
| 436 | |
| 437 | const T &at(int i) const |
| 438 | { |
| 439 | Q_ASSERT_X(i >= 0 && i < d->size, "QCircularBuffer<T>::at" , "index out of range" ); |
| 440 | return d->data()[d->wraparound(d->first + i)]; |
| 441 | } |
| 442 | |
| 443 | const T &operator[](int i) const |
| 444 | { |
| 445 | Q_ASSERT_X(i >= 0 && i < d->size, "QCircularBuffer<T>::operator[]" , "index out of range" ); |
| 446 | return d->data()[d->wraparound(d->first + i)]; |
| 447 | } |
| 448 | |
| 449 | T &operator[](int i) |
| 450 | { |
| 451 | d.detach(); |
| 452 | Q_ASSERT_X(i >= 0 && i < d->size, "QCircularBuffer<T>::operator[]" , "index out of range" ); |
| 453 | return d->data()[d->wraparound(d->first + i)]; |
| 454 | } |
| 455 | |
| 456 | int capacity() const { return d->capacity; } |
| 457 | |
| 458 | void clear() { *this = QCircularBuffer<T>(d->capacity); } |
| 459 | |
| 460 | bool contains(const T &val) const; |
| 461 | int count(const T &val) const; |
| 462 | int count() const { return size(); } |
| 463 | |
| 464 | array_range data() |
| 465 | { |
| 466 | d.detach(); |
| 467 | if (d->size == 0) |
| 468 | return array_range(nullptr, 0); |
| 469 | if (!isLinearised()) |
| 470 | linearise(); |
| 471 | return array_range(d->data() + d->first, d->last - d->first + 1); |
| 472 | } |
| 473 | const_array_range data() const { return constData(); } |
| 474 | const_array_range constData() const |
| 475 | { |
| 476 | if (!isLinearised() || d->size == 0) |
| 477 | return const_array_range(nullptr, 0); |
| 478 | return const_array_range(d->data() + d->first, d->last - d->first + 1); |
| 479 | } |
| 480 | |
| 481 | array_range dataOne() |
| 482 | { |
| 483 | d.detach(); |
| 484 | if (d->size == 0) |
| 485 | return array_range(nullptr, 0); |
| 486 | if (isLinearised()) |
| 487 | return array_range(d->data() + d->first, d->last - d->first + 1); |
| 488 | else |
| 489 | return array_range(d->data() + d->first, d->capacity - d->first); |
| 490 | } |
| 491 | const_array_range dataOne() const { return constDataOne(); } |
| 492 | const_array_range constDataOne() const |
| 493 | { |
| 494 | if (d->size == 0) |
| 495 | return const_array_range(nullptr, 0); |
| 496 | if (isLinearised()) |
| 497 | return const_array_range(d->data() + d->first, d->last - d->first + 1); |
| 498 | else |
| 499 | return const_array_range(d->data() + d->first, d->capacity - d->first); |
| 500 | } |
| 501 | |
| 502 | array_range dataTwo() |
| 503 | { |
| 504 | d.detach(); |
| 505 | if (d->size == 0 || isLinearised()) |
| 506 | return array_range(nullptr, 0); |
| 507 | return array_range(d->data(), d->last + 1); |
| 508 | } |
| 509 | const_array_range dataTwo() const { return constDataTwo(); } |
| 510 | const_array_range constDataTwo() const |
| 511 | { |
| 512 | if (d->size == 0 || isLinearised()) |
| 513 | return const_array_range(nullptr, 0); |
| 514 | return const_array_range(d->data(), d->last + 1); |
| 515 | } |
| 516 | |
| 517 | bool endsWith(const T &val) const { return !isEmpty() && last() == val; } |
| 518 | QCircularBuffer<T> &fill(const T &val, int number = -1); |
| 519 | T &first() { Q_ASSERT(!isEmpty()); d.detach(); return d->data()[ d->first ]; } |
| 520 | const T &first() const { Q_ASSERT(!isEmpty()); return d->data()[ d->first ]; } |
| 521 | int freeSize() const { return sizeAvailable(); } |
| 522 | |
| 523 | static QCircularBuffer<T> fromList(const QList<T> &list) |
| 524 | { return QCircularBuffer(list.begin(), list.end()); } |
| 525 | static QCircularBuffer<T> fromVector(const QVector<T> &vector) |
| 526 | { return QCircularBuffer(vector.begin(), vector.end()); } |
| 527 | |
| 528 | int indexOf(const T &val, int from = 0) const; |
| 529 | void insert(int i, const T &val) { insert(i, 1, val); } |
| 530 | void insert(int i, int number, const T &val); |
| 531 | bool isEmpty() const { return d->size == 0; } |
| 532 | bool isFull() const { return d->size == d->capacity; } |
| 533 | bool isLinearised() const { return (d->last >= d->first); } |
| 534 | T &last() { Q_ASSERT(!isEmpty()); return d->data()[ d->last ]; } |
| 535 | const T &last() const { Q_ASSERT(!isEmpty()); return d->data()[ d->last ]; } |
| 536 | int lastIndexOf(const T &val, int from = -1) const; |
| 537 | void linearise() |
| 538 | { |
| 539 | if (!isLinearised()) { |
| 540 | QCircularBuffer linearized(this->cbegin(), this->cend()); |
| 541 | swap(other&: linearized); |
| 542 | } |
| 543 | } |
| 544 | |
| 545 | void prepend(const T &val); |
| 546 | void remove(int i) { remove(i, 1); } |
| 547 | void remove(int i, int number); |
| 548 | |
| 549 | void replace(int i, const T &val) |
| 550 | { |
| 551 | Q_ASSERT_X(i >= 0 && i < d->size, "QCircularBuffer<T>::replace" , "index out of range" ); |
| 552 | const T copy(val); |
| 553 | (*this)[ i ] = copy; |
| 554 | } |
| 555 | |
| 556 | void reserve(int amount) { setCapacity(amount); } |
| 557 | void resize(int newSize); |
| 558 | void setCapacity(int amount); |
| 559 | int size() const { return d->size; } |
| 560 | Q_DECL_CONSTEXPR int max_size() const { return std::numeric_limits<size_type>::max(); } |
| 561 | int sizeAvailable() const { return d->capacity - d->size; } |
| 562 | void squeeze() { setCapacity(size()); } |
| 563 | bool startsWith(const T &val) const { return !isEmpty() && first() == val; } |
| 564 | |
| 565 | QList<T> toList() const; |
| 566 | QVector<T> toVector() const; |
| 567 | |
| 568 | T value(int i) const |
| 569 | { |
| 570 | if (i < 0 || i >= d->size) |
| 571 | return T(); |
| 572 | return at(i); |
| 573 | } |
| 574 | |
| 575 | T value(int i, const T &defaultValue) const |
| 576 | { |
| 577 | if (i < 0 || i >= d->size) |
| 578 | return defaultValue; |
| 579 | return at(i); |
| 580 | } |
| 581 | |
| 582 | QCircularBuffer<T> &operator+=(const T &other) { append(val: other); return *this; } |
| 583 | QCircularBuffer<T> &operator+=(const QCircularBuffer<T> &other); |
| 584 | QCircularBuffer<T> &operator+=(const QVector<T> &other); |
| 585 | QCircularBuffer<T> &operator+=(const QList<T> &other); |
| 586 | |
| 587 | QCircularBuffer<T> &operator<<(const T &other) { append(val: other); return *this; } |
| 588 | QCircularBuffer<T> &operator<<(const QCircularBuffer<T> &other) { *this += other; return *this; } |
| 589 | QCircularBuffer<T> &operator<<(const QVector<T> &other) { *this += other; return *this; } |
| 590 | QCircularBuffer<T> &operator<<(const QList<T> &other) { *this += other; return *this; } |
| 591 | |
| 592 | inline bool isSharedWith(const QCircularBuffer &other) const { return d == other.d; } |
| 593 | |
| 594 | private: |
| 595 | QExplicitlySharedDataPointer<Data> d; |
| 596 | }; |
| 597 | |
| 598 | template <typename T> |
| 599 | QCircularBuffer<T> operator+(const QCircularBuffer<T> &lhs, const QCircularBuffer<T> &rhs); |
| 600 | |
| 601 | template <typename T> |
| 602 | inline bool operator==(const QCircularBuffer<T> &lhs, const QCircularBuffer<T> &rhs) |
| 603 | { return lhs.isSharedWith(rhs) || (lhs.size() == rhs.size() && std::equal(lhs.begin(), lhs.end(), rhs.begin())); } |
| 604 | |
| 605 | template <typename T> |
| 606 | inline bool operator!=(const QCircularBuffer<T> &lhs, const QCircularBuffer<T> &rhs) |
| 607 | { return !operator==(lhs, rhs); } |
| 608 | |
| 609 | template <typename T> |
| 610 | inline void swap(QCircularBuffer<T> &lhs, QCircularBuffer<T> &rhs) |
| 611 | { lhs.swap(rhs); } |
| 612 | |
| 613 | template <typename T> |
| 614 | inline bool operator< (const QCircularBuffer<T> &lhs, const QCircularBuffer<T> &rhs) |
| 615 | { return std::lexicographical_compare(lhs.begin(), lhs.end(), rhs.begin(), rhs.end()); } |
| 616 | |
| 617 | template <typename T> |
| 618 | inline bool operator> (const QCircularBuffer<T> &lhs, const QCircularBuffer<T> &rhs) |
| 619 | { return operator<(rhs, lhs); } |
| 620 | |
| 621 | template <typename T> |
| 622 | inline bool operator>=(const QCircularBuffer<T> &lhs, const QCircularBuffer<T> &rhs) |
| 623 | { return !operator<(lhs, rhs); } |
| 624 | |
| 625 | template <typename T> |
| 626 | inline bool operator<=(const QCircularBuffer<T> &lhs, const QCircularBuffer<T> &rhs) |
| 627 | { return !operator>(lhs, rhs); } |
| 628 | |
| 629 | // out-of-line function implementations: |
| 630 | |
| 631 | #ifndef Q_QDOC |
| 632 | |
| 633 | template <typename T> |
| 634 | QCircularBuffer<T>::QCircularBuffer(int amount) |
| 635 | : d(new Data()) |
| 636 | { |
| 637 | // Allocate some raw memory |
| 638 | d->setData(d->allocate(amount)); |
| 639 | d->capacity = amount; |
| 640 | |
| 641 | // Initialize memory block to zero |
| 642 | memset(d->data(), 0, amount * sizeof(T)); |
| 643 | } |
| 644 | |
| 645 | template <typename T> |
| 646 | QCircularBuffer<T>::QCircularBuffer(int amount, const T &val) |
| 647 | : d(new Data()) |
| 648 | { |
| 649 | // Allocate some raw memory |
| 650 | d->setData(d->allocate(amount)); |
| 651 | d->capacity = amount; |
| 652 | |
| 653 | // Initialize the objects. In this case we always use the placement new operator |
| 654 | T *b = d->data(); |
| 655 | T *i = b + d->capacity; |
| 656 | while (i != b) |
| 657 | new (--i) T(val); |
| 658 | d->first = 0; |
| 659 | d->last = d->capacity - 1; |
| 660 | d->size = d->capacity; |
| 661 | } |
| 662 | |
| 663 | template <typename T> |
| 664 | QCircularBuffer<T>::QCircularBuffer(int amount, int initialSize, const T &val) |
| 665 | : d(new Data()) |
| 666 | { |
| 667 | Q_ASSERT_X(amount >= initialSize, "QCircularBuffer<T>::QCircularBuffer(int capacity, int size, const T &value)" , "size is greater than capacity" ); |
| 668 | |
| 669 | // Allocate some raw memory |
| 670 | d->setData(d->allocate(amount)); |
| 671 | d->capacity = amount; |
| 672 | |
| 673 | // Initialize the objects that need to be set to the specified value. |
| 674 | // In this case we always use the placement new operator |
| 675 | T *b = d->data(); |
| 676 | T *i = b + initialSize; |
| 677 | while (i != b) |
| 678 | new (--i) T(val); |
| 679 | |
| 680 | // Initialize the remaining memory to zero |
| 681 | memset(d->data() + initialSize, 0, (amount - initialSize) * sizeof(T)); |
| 682 | |
| 683 | d->first = 0; |
| 684 | d->last = initialSize - 1; |
| 685 | d->size = initialSize; |
| 686 | } |
| 687 | |
| 688 | template <typename T> |
| 689 | void QCircularBuffer<T>::append(const T &val) |
| 690 | { |
| 691 | // If we have no capacity we do nothing |
| 692 | if (!d->capacity) |
| 693 | return; |
| 694 | d.detach(); |
| 695 | if (d->size == d->capacity) { |
| 696 | // Buffer is full. Overwrite earliest item and rotate |
| 697 | d->data()[ d->first ] = val; |
| 698 | d->first = d->wraparound(++d->first); |
| 699 | d->last = d->wraparound(++d->last); |
| 700 | } else if (d->size != 0) { |
| 701 | // Buffer is partially full. Append data to end of array using appropriate method |
| 702 | int index = d->wraparound(d->first + d->size); |
| 703 | if (QTypeInfo<T>::isComplex) |
| 704 | new (d->data() + index) T(val); |
| 705 | else |
| 706 | d->data()[ index ] = val; |
| 707 | ++d->size; |
| 708 | ++d->last; |
| 709 | } else { |
| 710 | // Buffer is empty. Append data to end of array using appropriate method |
| 711 | d->size = 1; |
| 712 | d->first = d->last = 0; |
| 713 | if (QTypeInfo<T>::isComplex) |
| 714 | new (d->data() + d->first) T(val); |
| 715 | else |
| 716 | d->data()[ d->first ] = val; |
| 717 | } |
| 718 | } |
| 719 | |
| 720 | template <typename T> |
| 721 | bool QCircularBuffer<T>::contains(const T &val) const |
| 722 | { |
| 723 | if (isLinearised()) { |
| 724 | T *b = d->data() + d->first; |
| 725 | T *i = b + d->size; |
| 726 | while (i != b) |
| 727 | if (*--i == val) |
| 728 | return true; |
| 729 | return false; |
| 730 | } else { |
| 731 | // Check the array from m_first to the end |
| 732 | T *b = d->data() + d->first; |
| 733 | T *i = d->data() + d->capacity; |
| 734 | while (i != b) |
| 735 | if (*--i == val) |
| 736 | return true; |
| 737 | |
| 738 | // Check array from 0 to m_end |
| 739 | b = d->data(); |
| 740 | i = d->data() + d->last + 1; |
| 741 | while (i != b) |
| 742 | if (*--i == val) |
| 743 | return true; |
| 744 | |
| 745 | return false; |
| 746 | } |
| 747 | } |
| 748 | |
| 749 | template <typename T> |
| 750 | int QCircularBuffer<T>::count(const T &val) const |
| 751 | { |
| 752 | int c = 0; |
| 753 | if (isLinearised()) { |
| 754 | T *b = d->data() + d->first; |
| 755 | T *i = b + d->size; |
| 756 | while (i != b) |
| 757 | if (*--i == val) |
| 758 | ++c; |
| 759 | } else { |
| 760 | // Check the array from m_first to the end |
| 761 | T *b = d->data() + d->first; |
| 762 | T *i = d->data() + d->capacity; |
| 763 | while (i != b) |
| 764 | if (*--i == val) |
| 765 | ++c; |
| 766 | |
| 767 | // Check array from 0 to m_end |
| 768 | b = d->data(); |
| 769 | i = d->data() + d->last + 1; |
| 770 | while (i != b) |
| 771 | if (*--i == val) |
| 772 | ++c; |
| 773 | } |
| 774 | return c; |
| 775 | } |
| 776 | |
| 777 | template <typename T> |
| 778 | QCircularBuffer<T> &QCircularBuffer<T>::fill(const T &val, int number) |
| 779 | { |
| 780 | Q_ASSERT_X(d->capacity >= number, "QCircularBuffer<T>::fill" , "size is greater than capacity" ); |
| 781 | const T copy(val); |
| 782 | d.detach(); |
| 783 | int oldSize = d->size; |
| 784 | d->size = (number < 0 ? d->size : number); |
| 785 | d->first = (number == 0 ? -1 : 0); |
| 786 | d->last = d->size - 1; |
| 787 | |
| 788 | // Copy item into array size times |
| 789 | if (d->size) { |
| 790 | T *b = d->data(); |
| 791 | T *i = d->data() + d->size; |
| 792 | while (i != b) |
| 793 | *--i = copy; |
| 794 | } |
| 795 | |
| 796 | if (d->size < oldSize) { |
| 797 | // Cleanup old items beyond end of new array |
| 798 | T *b = d->data() + d->size; |
| 799 | T *i = d->data() + oldSize; |
| 800 | while (i-- != b) { |
| 801 | i->~T(); |
| 802 | //TODO: Optimize to a single memset call |
| 803 | memset(i, 0, sizeof(T)); |
| 804 | } |
| 805 | } |
| 806 | |
| 807 | return *this; |
| 808 | } |
| 809 | |
| 810 | template <typename T> |
| 811 | int QCircularBuffer<T>::indexOf(const T &val, int from) const |
| 812 | { |
| 813 | Q_ASSERT_X(from < d->size, "QCircularBuffer<T>::indexOf" , "from is greater than last valid index" ); |
| 814 | if (from < 0) |
| 815 | from = qMax(from + d->size, 0); |
| 816 | else if (from >= d->size) |
| 817 | from = d->size - 1; |
| 818 | for (int i = from; i < d->size; ++i) |
| 819 | if (at(i) == val) |
| 820 | return i; |
| 821 | return -1; |
| 822 | } |
| 823 | |
| 824 | template <typename T> |
| 825 | void QCircularBuffer<T>::insert(int i, int number, const T &val) |
| 826 | { |
| 827 | Q_ASSERT_X(i >= 0 && i <= d->size, "QCircularBuffer<T>::insert" , "index out of range" ); |
| 828 | d.detach(); |
| 829 | int freeCapacity = d->capacity - d->size; |
| 830 | |
| 831 | // Calculate number of elements that will actually be inserted. This |
| 832 | // depends upon where the insertion has been requested and any spare |
| 833 | // capacity left in the buffer. This is because elements at higher |
| 834 | // indices will be pushed to the right and will potentially wrap around |
| 835 | // to overwrite earlier elements. |
| 836 | int numToInsert = qMin(a: number, b: i + freeCapacity); |
| 837 | |
| 838 | // Calculate the number of elements at the beginning of the buffer that |
| 839 | // will be overwritten |
| 840 | int numToOverwrite = qMin(a: i, b: qMax(a: 0, b: number - freeCapacity)); |
| 841 | |
| 842 | // Decide which way to shift to minimize the amount of copying required. |
| 843 | if (i < d->size / 2) { |
| 844 | // Inserting in lower half of buffer so we shift earlier items down |
| 845 | |
| 846 | // Shift data at the bottom end down. This may only be a subset if some |
| 847 | // of the early data is to be overwritten. |
| 848 | if (QTypeInfo<T>::isStatic) { |
| 849 | int b = d->first + numToOverwrite; |
| 850 | int e = d->first + i - 1; |
| 851 | for (int j = b; j <= e; ++j) { |
| 852 | int srcIndex = j % d->capacity; |
| 853 | int dstIndex = (j - numToInsert + d->capacity) % d->capacity; |
| 854 | T *src = d->data() + srcIndex; |
| 855 | T *dst = d->data() + dstIndex; |
| 856 | |
| 857 | new (dst) T(*src); |
| 858 | } |
| 859 | } else { |
| 860 | // We have a movable type so a simple memcopy (or maybe two or |
| 861 | // three) will suffice to shift the data at the bottom end |
| 862 | int numToMove = i - numToOverwrite; |
| 863 | if (numToMove > 0) { |
| 864 | int srcBegin = (d->first + numToOverwrite) % d->capacity; |
| 865 | int srcEnd = (d->first + i - 1) % d->capacity; |
| 866 | int dstBegin = (srcBegin - numToInsert + d->capacity) % d->capacity; |
| 867 | int dstEnd = (srcEnd - numToInsert + d->capacity) % d->capacity; |
| 868 | |
| 869 | // Do we have any wrap-around problems to deal with? |
| 870 | bool srcRegionWraps = (srcEnd < srcBegin); |
| 871 | bool dstRegionWraps = (dstEnd < dstBegin); |
| 872 | if (!srcRegionWraps && dstRegionWraps) { |
| 873 | // Destination region wraps so do the move in two steps |
| 874 | int wrapCount = abs(x: srcBegin - numToInsert); |
| 875 | memmove(d->data() + d->capacity - wrapCount, d->data() + srcBegin, wrapCount * sizeof(T)); |
| 876 | memmove(d->data(), d->data() + srcBegin + wrapCount, (numToMove - wrapCount) * sizeof(T)); |
| 877 | } else if (srcRegionWraps && !dstRegionWraps) { |
| 878 | // Source region wraps so do the move in two steps |
| 879 | int wrapCount = d->capacity - srcBegin; |
| 880 | memmove(d->data() + dstBegin, d->data() + d->capacity - wrapCount, wrapCount * sizeof(T)); |
| 881 | memmove(d->data() + dstBegin + numToInsert, d->data(), (numToMove - wrapCount) * sizeof(T)); |
| 882 | } else if (srcRegionWraps && dstRegionWraps) { |
| 883 | // Source and destination regions wrap so we have to do this in three steps |
| 884 | int srcWrapCount = d->capacity - srcBegin; |
| 885 | memmove(d->data() + dstBegin, d->data() + d->capacity - srcWrapCount, srcWrapCount * sizeof(T)); |
| 886 | memmove(d->data() + d->capacity - numToInsert, d->data(), numToInsert * sizeof(T)); |
| 887 | memmove(d->data(), d->data() + numToInsert, (numToMove - srcWrapCount - numToInsert) * sizeof(T)); |
| 888 | } else { |
| 889 | // No wrap around - do a single memmove |
| 890 | memmove(d->data() + dstBegin, d->data() + srcBegin, numToMove * sizeof(T)); |
| 891 | } |
| 892 | } |
| 893 | } |
| 894 | |
| 895 | // Insert the new items |
| 896 | int e = d->first + i; |
| 897 | int b = e - numToInsert; |
| 898 | for (int j = b; j < e; ++j) { |
| 899 | T *p = d->data() + ((j + d->capacity) % d->capacity); |
| 900 | new (p) T(val); |
| 901 | } |
| 902 | |
| 903 | // Adjust the first, last and size indices as needed. |
| 904 | // NB. The last index never changes in this regime. |
| 905 | d->size += qMin(a: number, b: freeCapacity); |
| 906 | d->first = (d->first - (numToInsert - numToOverwrite) + d->capacity) % d->capacity; |
| 907 | } else { |
| 908 | // Inserting in upper half of buffer so we shift later items up |
| 909 | |
| 910 | // Shift data at the top end up which may or may not overwrite some |
| 911 | // of the earliest data |
| 912 | if (QTypeInfo<T>::isStatic) { |
| 913 | int b = d->first + d->size - 1; |
| 914 | int e = d->first + i; |
| 915 | for (int j = b; j >= e; j--) { |
| 916 | int srcIndex = j % d->capacity; |
| 917 | int dstIndex = (j + numToInsert) % d->capacity; |
| 918 | T *src = d->data() + srcIndex; |
| 919 | T *dst = d->data() + dstIndex; |
| 920 | |
| 921 | new (dst) T(*src); |
| 922 | } |
| 923 | } else { |
| 924 | // We have a movable type so a simple memcopy (or maybe two or |
| 925 | // three) will suffice to shift the data at the top end |
| 926 | int numToMove = d->size - i; |
| 927 | if (numToMove > 0) { |
| 928 | int srcBegin = (d->first + i) % d->capacity; |
| 929 | int srcEnd = d->last; |
| 930 | int dstBegin = (srcBegin + numToInsert) % d->capacity; |
| 931 | int dstEnd = (srcEnd + numToInsert) % d->capacity; |
| 932 | |
| 933 | // Do we have any wrap-around problems to deal with? |
| 934 | bool srcRegionWraps = (srcEnd < srcBegin); |
| 935 | bool dstRegionWraps = (dstEnd < dstBegin); |
| 936 | if (!srcRegionWraps && dstRegionWraps) { |
| 937 | // Destination region wraps so do the move in two steps |
| 938 | int wrapCount = srcEnd + numToInsert - d->capacity + 1; |
| 939 | memmove(d->data(), d->data() + srcEnd - wrapCount + 1, wrapCount * sizeof(T)); |
| 940 | memmove(d->data() + dstBegin, d->data() + srcBegin, (numToMove - wrapCount) * sizeof(T)); |
| 941 | } else if (srcRegionWraps && !dstRegionWraps) { |
| 942 | // Source region wraps so do the move in two steps |
| 943 | int wrapCount = d->last + 1; |
| 944 | memmove(d->data() + numToInsert, d->data(), wrapCount * sizeof(T)); |
| 945 | memmove(d->data() + dstBegin, d->data() + srcBegin, (numToMove - wrapCount) * sizeof(T)); |
| 946 | } else if (srcRegionWraps && dstRegionWraps) { |
| 947 | // Source and destination regions wrap so we have to do this in three steps |
| 948 | int srcWrapCount = d->last + 1; |
| 949 | memmove(d->data() + numToInsert, d->data(), srcWrapCount * sizeof(T)); |
| 950 | memmove(d->data(), d->data() + d->capacity - numToInsert, numToInsert * sizeof(T)); |
| 951 | memmove(d->data() + dstBegin, d->data() + srcBegin, (numToMove - srcWrapCount - numToInsert) * sizeof(T)); |
| 952 | } else { |
| 953 | // No wrap around - do a single memmove |
| 954 | memmove(d->data() + dstBegin, d->data() + srcBegin, numToMove * sizeof(T)); |
| 955 | } |
| 956 | } |
| 957 | } |
| 958 | |
| 959 | // Insert the new items |
| 960 | for (int j = d->first + i; j < d->first + i + numToInsert; ++j) { |
| 961 | T *p = d->data() + (j % d->capacity); |
| 962 | new (p) T(val); |
| 963 | } |
| 964 | |
| 965 | // Adjust the first, last and size indices as needed |
| 966 | d->size += qMin(a: number, b: freeCapacity); |
| 967 | d->first = (d->first + numToOverwrite) % d->capacity; |
| 968 | d->last = (d->last + numToInsert) % d->capacity; |
| 969 | } |
| 970 | } |
| 971 | |
| 972 | template <typename T> |
| 973 | int QCircularBuffer<T>::lastIndexOf(const T &val, int from) const |
| 974 | { |
| 975 | if (from < 0) |
| 976 | from = qMax(from + d->size, 0); |
| 977 | else if (from >= d->size) |
| 978 | from = d->size - 1; |
| 979 | for (int i = from; i >= 0; --i) |
| 980 | if (at(i) == val) |
| 981 | return i; |
| 982 | return -1; |
| 983 | } |
| 984 | |
| 985 | template <typename T> |
| 986 | void QCircularBuffer<T>::prepend(const T &val) |
| 987 | { |
| 988 | // If we have no capacity we do nothing |
| 989 | if (!d->capacity) |
| 990 | return; |
| 991 | |
| 992 | d.detach(); |
| 993 | if (d->size == d->capacity) { |
| 994 | // Buffer is full. Overwrite last item and rotate |
| 995 | d->data()[ d->last ] = val; |
| 996 | d->first = (--d->first + d->capacity) % d->capacity; |
| 997 | d->last = (--d->last + d->capacity) % d->capacity; |
| 998 | } else if (d->size != 0) { |
| 999 | // Buffer is partially full. Prepend data to start of array using appropriate method |
| 1000 | d->first = (--d->first + d->capacity) % d->capacity; |
| 1001 | ++d->size; |
| 1002 | if (QTypeInfo<T>::isComplex) |
| 1003 | new (d->data() + d->first) T(val); |
| 1004 | else |
| 1005 | d->data()[ d->first ] = val; |
| 1006 | } else { |
| 1007 | // Buffer is empty. Prepend data to start of array using appropriate method |
| 1008 | d->size = 1; |
| 1009 | d->first = d->last = d->capacity - 1; |
| 1010 | if (QTypeInfo<T>::isComplex) |
| 1011 | new (d->data() + d->first) T(val); |
| 1012 | else |
| 1013 | d->data()[ d->first ] = val; |
| 1014 | } |
| 1015 | } |
| 1016 | |
| 1017 | template <typename T> |
| 1018 | void QCircularBuffer<T>::remove(int i, int number) |
| 1019 | { |
| 1020 | Q_ASSERT_X(i >= 0 && number > 0 && i + number <= d->size, "QCircularBuffer<T>::remove" , "index out of range" ); |
| 1021 | d.detach(); |
| 1022 | |
| 1023 | // HACK (it actually makes sense, but requires some more thinking) |
| 1024 | if ( i == 0 && !QTypeInfo<T>::isComplex ) { |
| 1025 | d->first = d->wraparound( d->first + number ); |
| 1026 | d->size -= number; |
| 1027 | return; |
| 1028 | } |
| 1029 | |
| 1030 | // Calculate the number of items that need to be moved downward |
| 1031 | int numToMoveDown = d->size - number - i; |
| 1032 | int numToMoveUp = i; |
| 1033 | |
| 1034 | if (numToMoveDown < numToMoveUp) { |
| 1035 | // Move higher items down |
| 1036 | int numToMove = numToMoveDown; |
| 1037 | |
| 1038 | if (QTypeInfo<T>::isComplex) { |
| 1039 | // Copy items down from higher positions |
| 1040 | int b = d->first + i; |
| 1041 | int e = b + numToMove; |
| 1042 | for (int j = b; j < e ; ++j) { |
| 1043 | T *src = d->data() + ((j + number) % d->capacity); |
| 1044 | T *dst = d->data() + (j % d->capacity); |
| 1045 | new (dst) T(*src); |
| 1046 | } |
| 1047 | |
| 1048 | // Clean up items at end of buffer |
| 1049 | for (int j = d->last; j > d->last - number; --j) { |
| 1050 | T *p = d->data() + ((j + d->capacity) % d->capacity); |
| 1051 | p->~T(); |
| 1052 | //TODO: Optimize to a single memset call |
| 1053 | memset(p, 0, sizeof(T)); |
| 1054 | } |
| 1055 | } else { |
| 1056 | if (isLinearised()) { |
| 1057 | // With a linearised buffer we can do a simple move and removal of items |
| 1058 | memmove(d->data() + d->last - numToMove - number + 1, d->data() + d->last - numToMove + 1, numToMove * sizeof(T)); |
| 1059 | memset(d->data() + d->last - number + 1, 0, number * sizeof(T)); |
| 1060 | } else { |
| 1061 | // With a non-linearised buffer we need to be careful of wrapping issues |
| 1062 | int srcBegin = (d->last - numToMove + 1 + d->capacity) % d->capacity; |
| 1063 | int srcEnd = d->last; |
| 1064 | int dstBegin = (d->first + i) % d->capacity; |
| 1065 | int dstEnd = (dstBegin + numToMove - 1) % d->capacity; |
| 1066 | |
| 1067 | bool srcRegionWraps = (srcEnd < srcBegin); |
| 1068 | bool dstRegionWraps = (dstEnd < dstBegin); |
| 1069 | if (srcRegionWraps && !dstRegionWraps) { |
| 1070 | // Source region wraps so do the move in two steps |
| 1071 | int wrapCount = d->capacity - srcBegin; |
| 1072 | memmove(d->data() + dstBegin, d->data() + srcBegin, wrapCount * sizeof(T)); |
| 1073 | memmove(d->data() + dstBegin + wrapCount, d->data(), (numToMove - wrapCount) * sizeof(T)); |
| 1074 | } else if (!srcRegionWraps && dstRegionWraps) { |
| 1075 | // Destination region wraps so do the move in two steps |
| 1076 | int wrapCount = number - srcBegin; |
| 1077 | memmove(d->data() + d->capacity - wrapCount, d->data() + srcBegin, wrapCount * sizeof(T)); |
| 1078 | memmove(d->data(), d->data() + srcBegin + wrapCount, (numToMove - wrapCount) * sizeof(T)); |
| 1079 | } else if (srcRegionWraps && dstRegionWraps) { |
| 1080 | // Source and destination regions wrap so we have to do this in three steps |
| 1081 | int srcWrapCount = d->capacity - srcBegin; |
| 1082 | memmove(d->data() + dstBegin, d->data() + srcBegin, srcWrapCount * sizeof(T)); |
| 1083 | memmove(d->data() + dstBegin + srcWrapCount, d->data(), number * sizeof(T)); |
| 1084 | memmove(d->data(), d->data() + number, (numToMove - srcWrapCount - number) * sizeof(T)); |
| 1085 | } else { |
| 1086 | // No wrap around, so we can do this in one hit |
| 1087 | memmove(d->data() + dstBegin, d->data() + srcBegin, numToMove * sizeof(T)); |
| 1088 | } |
| 1089 | |
| 1090 | // We potentially have a disjoint region that needs zeroing |
| 1091 | int zeroStart = (d->last - number + d->capacity + 1) % d->capacity; |
| 1092 | int zeroEnd = d->last; |
| 1093 | if (zeroEnd < zeroStart) { |
| 1094 | // Region to be zeroed wraps. Do it in two steps. |
| 1095 | memset(d->data(), 0, (d->last + 1) * sizeof(T)); |
| 1096 | memset(d->data() + zeroStart, 0, (number - d->last - 1) * sizeof(T)); |
| 1097 | } else { |
| 1098 | // Region to be zeroed is contiguous |
| 1099 | memset(d->data() + zeroStart, 0, number * sizeof(T)); |
| 1100 | } |
| 1101 | } |
| 1102 | } |
| 1103 | |
| 1104 | // Adjust the indices |
| 1105 | d->size -= number; |
| 1106 | d->last = (d->last - number + d->capacity) % d->capacity; |
| 1107 | } else { |
| 1108 | // Move lower items up |
| 1109 | int numToMove = numToMoveUp; |
| 1110 | |
| 1111 | if (QTypeInfo<T>::isComplex) { |
| 1112 | // Copy items up from lower positions |
| 1113 | int b = d->first + i - 1; |
| 1114 | int e = b - numToMove; |
| 1115 | for (int j = b; j > e ; --j) { |
| 1116 | T *src = d->data() + ((j + d->capacity) % d->capacity); |
| 1117 | T *dst = d->data() + ((j + d->capacity + number) % d->capacity); |
| 1118 | new (dst) T(*src); |
| 1119 | } |
| 1120 | |
| 1121 | // Clean up items at start of buffer |
| 1122 | for (int j = d->first; j < d->first + number; ++j) { |
| 1123 | T *p = d->data() + (j % d->capacity); |
| 1124 | p->~T(); |
| 1125 | //TODO: Optimize to a single memset call |
| 1126 | memset(p, 0, sizeof(T)); |
| 1127 | } |
| 1128 | } else { |
| 1129 | if (isLinearised()) { |
| 1130 | // With a linearised buffer we can do a simple move and removal of items |
| 1131 | memmove(d->data() + d->first + number, d->data() + d->first, numToMove * sizeof(T)); |
| 1132 | memset(d->data() + d->first, 0, number * sizeof(T)); |
| 1133 | } else { |
| 1134 | // With a non-linearised buffer we need to be careful of wrapping issues |
| 1135 | int srcBegin = d->first; |
| 1136 | int srcEnd = (srcBegin + numToMove - 1) % d->capacity; |
| 1137 | int dstBegin = (srcBegin + number) % d->capacity; |
| 1138 | int dstEnd = (dstBegin + numToMove - 1) % d->capacity; |
| 1139 | |
| 1140 | bool srcRegionWraps = (srcEnd < srcBegin); |
| 1141 | bool dstRegionWraps = (dstEnd < dstBegin); |
| 1142 | if (srcRegionWraps && !dstRegionWraps) { |
| 1143 | // Source region wraps so do the move in two steps |
| 1144 | int wrapCount = srcEnd + 1; |
| 1145 | memmove(d->data() + dstEnd - wrapCount + 1, d->data(), wrapCount * sizeof(T)); |
| 1146 | memmove(d->data() + dstBegin, d->data() + srcBegin, (numToMove - wrapCount) * sizeof(T)); |
| 1147 | } else if (!srcRegionWraps && dstRegionWraps) { |
| 1148 | // Destination region wraps so do the move in two steps |
| 1149 | int wrapCount = dstEnd + 1; |
| 1150 | memmove(d->data(), d->data() + srcEnd - wrapCount + 1, wrapCount * sizeof(T)); |
| 1151 | memmove(d->data() + dstBegin, d->data() + srcBegin, (numToMove - wrapCount) * sizeof(T)); |
| 1152 | } else if (srcRegionWraps && dstRegionWraps) { |
| 1153 | // Source and destination regions wrap so we have to do this in three steps |
| 1154 | int srcWrapCount = srcEnd + 1; |
| 1155 | memmove(d->data() + dstEnd - srcWrapCount + 1, d->data(), srcWrapCount * sizeof(T)); |
| 1156 | memmove(d->data(), d->data() + d->capacity - number, number * sizeof(T)); |
| 1157 | memmove(d->data() + dstBegin, d->data() + srcBegin, (numToMove - srcWrapCount - number) * sizeof(T)); |
| 1158 | } else { |
| 1159 | // No wrap around, so we can do this in one hit |
| 1160 | memmove(d->data() + dstBegin, d->data() + srcBegin, numToMove * sizeof(T)); |
| 1161 | } |
| 1162 | |
| 1163 | // We potentially have a disjoint region that needs zeroing |
| 1164 | int zeroStart = d->first; |
| 1165 | int zeroEnd = (zeroStart + number - 1) % d->capacity; |
| 1166 | if (zeroEnd < zeroStart) { |
| 1167 | // Region to be zeroed wraps. Do it in two steps. |
| 1168 | memset(d->data() + zeroStart, 0, (d->capacity - d->first) * sizeof(T)); |
| 1169 | memset(d->data(), 0, (number - d->capacity + d->first) * sizeof(T)); |
| 1170 | } else { |
| 1171 | // Region to be zeroed is contiguous |
| 1172 | memset(d->data() + zeroStart, 0, number * sizeof(T)); |
| 1173 | } |
| 1174 | } |
| 1175 | } |
| 1176 | |
| 1177 | // Adjust the indices |
| 1178 | d->size -= number; |
| 1179 | d->first = (d->first + number) % d->capacity; |
| 1180 | } |
| 1181 | } |
| 1182 | |
| 1183 | template <typename T> |
| 1184 | void QCircularBuffer<T>::setCapacity(int amount) |
| 1185 | { |
| 1186 | if (amount == d->capacity) |
| 1187 | return; |
| 1188 | |
| 1189 | d.detach(); |
| 1190 | // Allocate some new raw memory |
| 1191 | T *newData = d->allocate(amount); |
| 1192 | |
| 1193 | // How many items can we copy across? |
| 1194 | int newSize = qMin(d->size, amount); |
| 1195 | |
| 1196 | if (QTypeInfo<T>::isComplex) { |
| 1197 | // Copy across the elements from the original array |
| 1198 | for (int i = 0; i < newSize; ++i) { |
| 1199 | T *src = d->data() + ((d->first + i) % d->capacity); |
| 1200 | T *dst = newData + i; |
| 1201 | new (dst) T(*src); |
| 1202 | } |
| 1203 | |
| 1204 | // Destroy the original items. |
| 1205 | // The type is complex so we manually call the destructor for each item |
| 1206 | // since we used the placement new operator to instantiate them |
| 1207 | T *b = d->data(); |
| 1208 | T *i = b + d->capacity; |
| 1209 | while (i-- != b) |
| 1210 | i->~T(); |
| 1211 | } else { |
| 1212 | // Copy across the elements from the original array. The source region |
| 1213 | // potentially wraps so we may have to do this in one or two steps |
| 1214 | if (isLinearised()) { |
| 1215 | memmove(newData, d->data() + d->first, newSize * sizeof(T)); |
| 1216 | } else { |
| 1217 | int step1Size = qMin(newSize, d->capacity - d->first); |
| 1218 | memmove(newData, d->data() + d->first, step1Size * sizeof(T)); |
| 1219 | int step2Size = qMax(0, qMin(newSize - d->capacity + d->first, d->last + 1)); |
| 1220 | memmove(newData + step1Size, d->data(), step2Size * sizeof(T)); |
| 1221 | } |
| 1222 | } |
| 1223 | |
| 1224 | // Initialize any memory outside of the valid buffer (ie the unused items) |
| 1225 | memset(newData + newSize, 0, (amount - newSize) * sizeof(T)); |
| 1226 | |
| 1227 | // Release the raw memory for the old array |
| 1228 | d->deallocate(d->data()); |
| 1229 | |
| 1230 | // Assign the new memory to be our buffer data and fix indices |
| 1231 | d->setData(newData); |
| 1232 | d->capacity = amount; |
| 1233 | d->first = 0; |
| 1234 | d->size = newSize; |
| 1235 | d->last = d->size - 1; |
| 1236 | } |
| 1237 | |
| 1238 | template <typename T> |
| 1239 | void QCircularBuffer<T>::resize(int newSize) |
| 1240 | { |
| 1241 | Q_ASSERT_X(newSize >= 0 && newSize <= d->capacity, "QCircularBuffer<T>::resize" , "size out of range" ); |
| 1242 | d.detach(); |
| 1243 | if (newSize < d->size) { |
| 1244 | remove(newSize, d->size - newSize); |
| 1245 | } else if (newSize > d->size) { |
| 1246 | const T t = T(); |
| 1247 | insert(d->size, newSize - d->size, t); |
| 1248 | } |
| 1249 | } |
| 1250 | |
| 1251 | template <typename T> |
| 1252 | QCircularBuffer<T> &QCircularBuffer<T>::operator+=(const QCircularBuffer<T> &other) |
| 1253 | { |
| 1254 | d.detach(); |
| 1255 | // How many items do we need to copy? No point in ever copying across a number |
| 1256 | // greater than capacity |
| 1257 | int numToCopy = qMin(other.size(), d->capacity); |
| 1258 | int offset = other.size() - numToCopy; |
| 1259 | for (int i = 0; i < numToCopy; ++i) |
| 1260 | append(val: other.at(offset + i)); |
| 1261 | return *this; |
| 1262 | } |
| 1263 | |
| 1264 | template <typename T> |
| 1265 | QCircularBuffer<T> &QCircularBuffer<T>::operator+=(const QVector<T> &other) |
| 1266 | { |
| 1267 | d.detach(); |
| 1268 | // How many items do we need to copy? No point in ever copying across a number |
| 1269 | // greater than capacity |
| 1270 | int numToCopy = qMin(other.size(), d->capacity); |
| 1271 | int offset = other.size() - numToCopy; |
| 1272 | for (int i = 0; i < numToCopy; ++i) |
| 1273 | append(val: other.at(offset + i)); |
| 1274 | return *this; |
| 1275 | } |
| 1276 | |
| 1277 | template <typename T> |
| 1278 | QCircularBuffer<T> &QCircularBuffer<T>::operator+=(const QList<T> &other) |
| 1279 | { |
| 1280 | d.detach(); |
| 1281 | // How many items do we need to copy? No point in ever copying across a number |
| 1282 | // greater than capacity |
| 1283 | int numToCopy = qMin(other.size(), d->capacity); |
| 1284 | int offset = other.size() - numToCopy; |
| 1285 | for (int i = 0; i < numToCopy; ++i) |
| 1286 | append(val: other.at(offset + i)); |
| 1287 | return *this; |
| 1288 | } |
| 1289 | |
| 1290 | template <typename T> |
| 1291 | QList<T> QCircularBuffer<T>::toList() const |
| 1292 | { |
| 1293 | QList<T> list; |
| 1294 | list.reserve(size()); |
| 1295 | for (int i = 0; i < size(); ++i) |
| 1296 | list.append(at(i)); |
| 1297 | return list; |
| 1298 | } |
| 1299 | |
| 1300 | template <typename T> |
| 1301 | QVector<T> QCircularBuffer<T>::toVector() const |
| 1302 | { |
| 1303 | QVector<T> vector; |
| 1304 | vector.reserve(size()); |
| 1305 | for (int i = 0; i < size(); ++i) |
| 1306 | vector.append(at(i)); |
| 1307 | return vector; |
| 1308 | } |
| 1309 | |
| 1310 | template <typename T> |
| 1311 | QCircularBuffer<T> operator+(const QCircularBuffer<T> &lhs, const QCircularBuffer<T> &rhs) |
| 1312 | { |
| 1313 | QCircularBuffer<T> circ(lhs.size() + rhs.size()); |
| 1314 | for (int i = 0; i < lhs.size(); ++i) |
| 1315 | circ.append(lhs.at(i)); |
| 1316 | for (int i = 0; i < rhs.size(); ++i) |
| 1317 | circ.append(rhs.at(i)); |
| 1318 | return circ; |
| 1319 | } |
| 1320 | |
| 1321 | #endif // Q_QDOC |
| 1322 | |
| 1323 | Q_DECLARE_SEQUENTIAL_ITERATOR(CircularBuffer) |
| 1324 | Q_DECLARE_MUTABLE_SEQUENTIAL_ITERATOR(CircularBuffer) |
| 1325 | |
| 1326 | } //Qt3D |
| 1327 | |
| 1328 | QT_END_NAMESPACE |
| 1329 | |
| 1330 | #endif // QT3DCORE_QCIRCULARBUFFER_H |
| 1331 | |