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 */