/* * This file is part of OpenTTD. * OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2. * OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see . */ /** @file ring_buffer.hpp Resizing ring buffer implementation. */ #ifndef RING_BUFFER_HPP #define RING_BUFFER_HPP #include "alloc_type.hpp" #include "bitmath_func.hpp" /** * Self-resizing ring-buffer * * Insertion of an item invalidates existing iterators. * Erasing an item which is not at the front or the back invalidates existing iterators. */ template class ring_buffer { std::unique_ptr data; uint32 head = 0; uint32 count = 0; uint32 mask = (uint32)-1; static uint32 round_up_size(uint32 size) { if (size <= 4) return 4; uint8 bit = FindLastBit(size - 1); return 1 << (bit + 1); } class ring_buffer_iterator_base { friend class ring_buffer; protected: const ring_buffer *ring = nullptr; uint32 pos = 0; ring_buffer_iterator_base() {} ring_buffer_iterator_base(const ring_buffer *ring, uint32 pos) : ring(ring), pos(pos) {} }; public: template class ring_buffer_iterator : public ring_buffer_iterator_base { friend class ring_buffer; ring_buffer_iterator() : ring_buffer_iterator_base() {} ring_buffer_iterator(const ring_buffer *ring, uint32 pos) : ring_buffer_iterator_base(ring, pos) {} private: void next() { if (REVERSE) { --this->pos; } else { ++this->pos; } } void prev() { if (REVERSE) { ++this->pos; } else { --this->pos; } } void move(std::ptrdiff_t delta) { if (REVERSE) { this->pos -= (uint32)delta; } else { this->pos += (uint32)delta; } } public: using difference_type = std::ptrdiff_t; using value_type = T; using reference = V &; using const_reference = const V &; using pointer = V *; using const_pointer = const V *; using iterator_category = std::bidirectional_iterator_tag; reference operator *() { return *this->ring->ptr_at_pos(this->pos); } const_reference operator *() const { return *this->ring->ptr_at_pos(this->pos); } pointer operator ->() { return this->ring->ptr_at_pos(this->pos); } const_pointer operator ->() const { return this->ring->ptr_at_pos(this->pos); } /* Increment operator (postfix) */ ring_buffer_iterator operator ++(int) { ring_buffer_iterator tmp = *this; this->next(); return tmp; } /* Increment operator (prefix) */ ring_buffer_iterator &operator ++() { this->next(); return *this; } /* Decrement operator (postfix) */ ring_buffer_iterator operator --(int) { ring_buffer_iterator tmp = *this; this->prev(); return tmp; } /* Decrement operator (prefix) */ ring_buffer_iterator &operator --() { this->prev(); return *this; } bool operator ==(const ring_buffer_iterator &other) const { return (this->ring == other.ring) && (this->pos == other.pos); } bool operator !=(const ring_buffer_iterator &other) const { return !operator ==(other); } ring_buffer_iterator operator +(std::ptrdiff_t delta) const { ring_buffer_iterator tmp = *this; this->move(delta); return tmp; } ring_buffer_iterator &operator +=(std::ptrdiff_t delta) { this->move(delta); return *this; } ring_buffer_iterator operator -(std::ptrdiff_t delta) const { ring_buffer_iterator tmp = *this; this->move(-delta); return tmp; } ring_buffer_iterator &operator -=(std::ptrdiff_t delta) { this->move(-delta); return *this; } std::ptrdiff_t operator -(const ring_buffer_iterator &other) const { dbg_assert(this->ring == other.ring); if (REVERSE) { return (int32)(other.pos - this->pos); } else { return (int32)(this->pos - other.pos); } } }; typedef ring_buffer_iterator iterator; typedef ring_buffer_iterator const_iterator; typedef ring_buffer_iterator reverse_iterator; typedef ring_buffer_iterator const_reverse_iterator; ring_buffer() = default; ring_buffer(const ring_buffer &other) { if (!other.empty()) { uint32 cap = round_up_size(other.count); this->data.reset(MallocT(cap * sizeof(T))); this->mask = cap - 1; this->head = 0; this->count = other.size(); byte *ptr = this->data.get(); for (const T &item : other) { new (ptr) T(item); ptr += sizeof(T); } } } ring_buffer(ring_buffer &&other) { std::swap(this->data, other.data); std::swap(this->head, other.head); std::swap(this->count, other.count); std::swap(this->mask, other.mask); } ring_buffer& operator =(const ring_buffer &other) { if (&other != this) { this->clear(); if (!other.empty()) { if (other.size() > this->capacity()) { uint32 cap = round_up_size(other.count); this->data.reset(MallocT(cap * sizeof(T))); this->mask = cap - 1; } this->head = 0; this->count = other.count; byte *ptr = this->data.get(); for (const T &item : other) { new (ptr) T(item); ptr += sizeof(T); } } } return *this; } ring_buffer& operator =(ring_buffer &&other) { if (&other != this) { std::swap(this->data, other.data); std::swap(this->head, other.head); std::swap(this->count, other.count); std::swap(this->mask, other.mask); } return *this; } ~ring_buffer() { for (T &item : *this) { item.~T(); } } bool operator ==(const ring_buffer& other) const { if (this->count != other.count) return false; if (this->empty()) return true; auto other_iter = other.begin(); for (const T &item : *this) { if (item != *other_iter) return false; ++other_iter; } return true; } bool operator != (const ring_buffer &other) const { return !operator ==(other); } size_t size() const { return this->count; } bool empty() const { return this->count == 0; } size_t capacity() const { return this->mask + 1; }; void clear() { for (const T &item : *this) { item.~T(); } this->count = 0; this->head = 0; } private: void reallocate(uint32 new_cap) { const uint32 cap = round_up_size(new_cap); byte *new_buf = MallocT(cap * sizeof(T)); byte *pos = new_buf; for (T &item : *this) { new (pos) T(std::move(item)); item.~T(); pos += sizeof(T); } this->mask = cap - 1; this->head = 0; this->data.reset(new_buf); } void *raw_ptr_at_pos(uint32 idx) const { return this->data.get() + (sizeof(T) * (idx & this->mask)); } void *raw_ptr_at_offset(uint32 idx) const { return this->raw_ptr_at_pos(this->head + idx); } T *ptr_at_pos(uint32 idx) const { return static_cast(this->raw_ptr_at_pos(idx)); } T *ptr_at_offset(uint32 idx) const { return static_cast(this->raw_ptr_at_offset(idx)); } void *new_back_ptr() { if (this->count == this->capacity()) this->reallocate(this->count + 1); this->count++; return this->raw_ptr_at_offset(this->count - 1); } void *new_front_ptr() { if (this->count == this->capacity()) this->reallocate(this->count + 1); this->count++; this->head--; return this->raw_ptr_at_offset(0); } public: void push_back(const T &item) { new (this->new_back_ptr()) T(item); } void push_back(T &&item) { new (this->new_back_ptr()) T(std::move(item)); } template T &emplace_back(Args&&... args) { void *ptr = this->new_back_ptr(); return *(new (ptr) T(std::forward(args)...)); } void push_front(const T &item) { new (this->new_front_ptr()) T(item); } void push_front(T &&item) { new (this->new_front_ptr()) T(std::move(item)); } template T &emplace_front(Args&&... args) { void *ptr = this->new_front_ptr(); return *(new (ptr) T(std::forward(args)...)); } void pop_back() { this->count--; this->ptr_at_offset(this->count)->~T(); } void pop_front() { this->ptr_at_offset(0)->~T(); this->head++; this->count--; } iterator begin() { return iterator(this, this->head); } const_iterator begin() const { return const_iterator(this, this->head); } const_iterator cbegin() const { return const_iterator(this, this->head); } iterator end() { return iterator(this, this->head + this->count); } const_iterator end() const { return const_iterator(this, this->head + this->count); } const_iterator cend() const { return const_iterator(this, this->head + this->count); } reverse_iterator rbegin() { return reverse_iterator(this, this->head + this->count - 1); } const_reverse_iterator rbegin() const { return const_reverse_iterator(this, this->head + this->count - 1); } const_reverse_iterator crbegin() const { return const_reverse_iterator(this, this->head + this->count - 1); } reverse_iterator rend() { return reverse_iterator(this, this->head - 1); } const_reverse_iterator rend() const { return const_reverse_iterator(this, this->head - 1); } const_reverse_iterator crend() const { return const_reverse_iterator(this, this->head - 1); } T &front() { return *this->ptr_at_offset(0); } const T &front() const { return *this->ptr_at_offset(0); } T &back() { return *this->ptr_at_offset(this->count - 1); } const T &back() const { return *this->ptr_at_offset(this->count - 1); } private: uint32 setup_insert(uint32 pos, uint32 num) { if (this->count + num > (uint32)this->capacity()) { /* grow container */ const uint32 cap = round_up_size(this->count + num); byte *new_buf = MallocT(cap * sizeof(T)); byte *write_to = new_buf; const uint32 end = this->head + this->count; for (uint32 idx = this->head; idx != end; idx++) { if (idx == pos) { /* gap for inserted items */ write_to += num * sizeof(T); } T &item = *this->ptr_at_pos(idx); new (write_to) T(std::move(item)); item.~T(); write_to += sizeof(T); } uint32 res = pos - this->head; this->mask = cap - 1; this->head = 0; this->count += num; this->data.reset(new_buf); return res; } else if (pos == this->head) { /* front */ this->count += num; this->head -= num; return 0; } else if (pos == this->head + this->count) { /* back */ uint32 ret = this->count; this->count += num; return ret; } else { /* middle, move data */ if (pos - this->head < (this->count / 2)) { /* closer to the beginning, shuffle those backwards */ const uint32 new_head = this->head - num; const uint32 insert_start = pos - num; for (uint32 idx = new_head; idx != this->head; idx++) { /* Move construct to move backwards into uninitialised region */ new (this->raw_ptr_at_pos(idx)) T(std::move(*(this->ptr_at_pos(idx + num)))); } for (uint32 idx = this->head; idx != insert_start; idx++) { /* Move assign to move backwards in initialised region */ *this->ptr_at_pos(idx) = std::move(*this->ptr_at_pos(idx + num)); } for (uint32 idx = insert_start; idx != pos; idx++) { /* Destruct to leave space for inserts */ this->ptr_at_pos(idx)->~T(); } this->head = new_head; this->count += num; return insert_start; } else { /* closer to the end, shuffle those forwards */ const uint32 after_insert = pos + num; const uint32 last = this->head + this->count - 1; const uint32 new_last = last + num; for (uint32 idx = new_last; idx != last; idx--) { /* Move construct to move forwards into uninitialised region */ new (this->raw_ptr_at_pos(idx)) T(std::move(*(this->ptr_at_pos(idx - num)))); } for (uint32 idx = last; idx != after_insert; idx--) { /* Move assign to move backwards in initialised region */ *this->ptr_at_pos(idx) = std::move(*this->ptr_at_pos(idx - num)); } for (uint32 idx = after_insert; idx != pos; idx--) { /* Destruct to leave space for inserts */ this->ptr_at_pos(idx)->~T(); } this->count += num; return pos; } } } public: template iterator emplace(ring_buffer_iterator_base pos, Args&&... args) { dbg_assert(pos.ring == this); uint32 new_pos = this->setup_insert(pos.pos, 1); new (this->raw_ptr_at_pos(new_pos)) T(std::forward(args)...); return iterator(this, new_pos); } iterator insert(ring_buffer_iterator_base pos, const T& value) { return this->emplace(pos, value); } iterator insert(ring_buffer_iterator_base pos, T&& value) { return this->emplace(pos, std::move(value)); } void reserve(size_t new_cap) { if (new_cap <= this->capacity()) return; this->reallocate((uint32)new_cap); } void resize(size_t new_size) { if (new_size < this->size()) { for (uint32 i = (uint32)new_size; i != this->count; i++) { this->ptr_at_offset(i)->~T(); } } else if (new_size > this->size()) { if (new_size > this->capacity()) { this->reallocate((uint32)new_size); } for (uint32 i = this->count; i != (uint32)new_size; i++) { new (this->raw_ptr_at_offset(i)) T(); } } this->count = (uint32)new_size; } void shrink_to_fit() { if (this->empty()) { this->clear(); this->data.reset(); this->mask = (uint32)-1; } else if (round_up_size(this->count) < this->capacity()) { this->reallocate(this->count); } } T &operator[](size_t index) { return *this->ptr_at_offset((uint32)index); } const T &operator[](size_t index) const { return *this->ptr_at_offset((uint32)index); } }; #endif /* RING_BUFFER_HPP */