diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt index 5c76407797..ebb7efa509 100644 --- a/src/core/CMakeLists.txt +++ b/src/core/CMakeLists.txt @@ -28,6 +28,7 @@ add_files( pool_type.hpp random_func.cpp random_func.hpp + ring_buffer.hpp serialisation.cpp serialisation.hpp smallstack_type.hpp diff --git a/src/core/ring_buffer.hpp b/src/core/ring_buffer.hpp new file mode 100644 index 0000000000..241b1a4dc5 --- /dev/null +++ b/src/core/ring_buffer.hpp @@ -0,0 +1,635 @@ +/* + * 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 */