From 8e7de22df5d6445393fa8e3f5997f3031cea7de9 Mon Sep 17 00:00:00 2001 From: Eidolon Date: Fri, 13 Dec 2024 10:44:19 -0600 Subject: [PATCH] Add srb2::Vector --- src/core/CMakeLists.txt | 2 + src/core/vector.cpp | 58 +++++++ src/core/vector.hpp | 363 ++++++++++++++++++++++++++++++++++++++++ 3 files changed, 423 insertions(+) create mode 100644 src/core/vector.cpp create mode 100644 src/core/vector.hpp diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt index a5d0b521a..fbae2392a 100644 --- a/src/core/CMakeLists.txt +++ b/src/core/CMakeLists.txt @@ -5,4 +5,6 @@ target_sources(SRB2SDL2 PRIVATE static_vec.hpp thread_pool.cpp thread_pool.h + vector.cpp + vector.hpp ) diff --git a/src/core/vector.cpp b/src/core/vector.cpp new file mode 100644 index 000000000..e0e832f88 --- /dev/null +++ b/src/core/vector.cpp @@ -0,0 +1,58 @@ +// DR. ROBOTNIK'S RING RACERS +//----------------------------------------------------------------------------- +// Copyright (C) 2025 by Ronald "Eidolon" Kinard +// Copyright (C) 2025 by Kart Krew +// +// This program is free software distributed under the +// terms of the GNU General Public License, version 2. +// See the 'LICENSE' file for more details. +//----------------------------------------------------------------------------- + +#include "vector.hpp" + +#include +#include +#include + +namespace srb2 +{ + +AbstractVector::GrowResult AbstractVector::_realloc_mem(void* data, size_t size, size_t old_cap, size_t elem_size, Move move, size_t cap) noexcept +{ + GrowResult ret; + std::allocator allocator; + + if (old_cap == 0) + { + cap = std::max(cap, 1); + ret.data = allocator.allocate(cap * elem_size + sizeof(std::max_align_t)); + ret.cap = cap; + return ret; + } + + cap = std::max(cap, old_cap * 2); + ret.data = allocator.allocate(cap * elem_size + sizeof(std::max_align_t)); + (move)(ret.data, data, size); + allocator.deallocate(reinterpret_cast(data), old_cap * elem_size + sizeof(std::max_align_t)); + ret.cap = cap; + return ret; +} + +void AbstractVector::_free_mem(void* data, size_t cap, size_t elem_size) noexcept +{ + std::allocator allocator; + allocator.deallocate(reinterpret_cast(data), cap * elem_size + sizeof(std::max_align_t)); +} + +template class Vector; +template class Vector; +template class Vector; +template class Vector; +template class Vector; +template class Vector; +template class Vector; +template class Vector; +template class Vector; +template class Vector; + +} // namespace srb2 diff --git a/src/core/vector.hpp b/src/core/vector.hpp new file mode 100644 index 000000000..a7c0f9bda --- /dev/null +++ b/src/core/vector.hpp @@ -0,0 +1,363 @@ +// DR. ROBOTNIK'S RING RACERS +//----------------------------------------------------------------------------- +// Copyright (C) 2025 by Ronald "Eidolon" Kinard +// Copyright (C) 2025 by Kart Krew +// +// This program is free software distributed under the +// terms of the GNU General Public License, version 2. +// See the 'LICENSE' file for more details. +//----------------------------------------------------------------------------- + +#ifndef SRB2_CORE_VEC_HPP +#define SRB2_CORE_VEC_HPP + +#include +#include +#include +#include +#include +#include +#include + +namespace srb2 +{ + +class AbstractVector +{ +protected: + size_t size_; + size_t capacity_; + size_t elem_size_; + void* data_; + + using Move = void(*)(void* dst, void* src, size_t size); + + template + static void _move(void* dst, void* src, size_t size) noexcept + { + T* d = (T*)dst; + T* s = (T*)src; + for (size_t i = 0; i < size; i++) + { + new (&d[i]) T(std::move(s[i])); + } + } + + struct GrowResult + { + void* data; + size_t cap; + }; + static GrowResult _realloc_mem(void* data, size_t size, size_t old_cap, size_t elem_size, Move move, size_t cap) noexcept; + static void _free_mem(void* data, size_t cap, size_t elem_size) noexcept; + + template + void _reserve_mem(size_t c) noexcept + { + if (c > capacity_) + { + GrowResult r = _realloc_mem(data_, size_, capacity_, elem_size_, _move, c); + data_ = r.data; + capacity_ = r.cap; + } + } + + constexpr AbstractVector() : size_(0), capacity_(0), elem_size_(0), data_(nullptr) {} +}; + +template +class Vector : AbstractVector +{ +public: + // iter traits + using value_type = T; + using size_type = size_t; + using difference_type = ptrdiff_t; + using reference = T&; + using const_reference = const T&; + using pointer = T*; + using const_pointer = const T*; + using iterator = T*; + using const_iterator = const T*; + using reverse_iterator = std::reverse_iterator; + using const_reverse_iterator = std::reverse_iterator; + + Vector() : AbstractVector() + { + elem_size_ = sizeof(T); + } + Vector(const Vector& rhs) : Vector() + { + *this = rhs; + } + Vector(Vector&& rhs) noexcept : AbstractVector() + { + elem_size_ = sizeof(T); + *this = std::move(rhs); + } + ~Vector() + { + if (data_) + { + for (size_type i = 0; i < size_; i++) + { + ((T*)(data_))[(size_ - i - 1)].~T(); + } + _free_mem(data_, capacity_, elem_size_); + } + data_ = nullptr; + } + + explicit Vector(size_type capacity) : AbstractVector() + { + elem_size_ = sizeof(T); + _reserve_mem(capacity); + } + Vector(std::initializer_list l) : AbstractVector() + { + elem_size_ = sizeof(T); + if (l.size() == 0) + { + return; + } + + _reserve_mem(l.size()); + for (auto itr = l.begin(); itr != l.end(); itr++) + { + emplace_back(*itr); + } + } + + template < + typename It, + typename std::enable_if_t< + std::is_constructible_v< + T, + typename std::iterator_traits::reference + >, + int + > = 0 + > + Vector(It begin, It end) : AbstractVector() + { + elem_size_ = sizeof(T); + for (auto itr = begin; itr != end; ++itr) + { + push_back(*itr); + } + } + + Vector& operator=(const Vector& rhs) + { + for (auto itr = rhs.begin(); itr != rhs.end(); itr++) + { + push_back(*itr); + } + return *this; + } + + Vector& operator=(Vector&& rhs) noexcept + { + std::swap(size_, rhs.size_); + std::swap(capacity_, rhs.capacity_); + std::swap(data_, rhs.data_); + return *this; + } + + constexpr size_type size() const noexcept { return size_; } + constexpr size_type capacity() const noexcept { return capacity_; } + constexpr bool empty() const noexcept { return size_ == 0; } + + constexpr T* data() noexcept { return (T*) data_; } + constexpr const T* data() const noexcept { return (const T*) data_; } + constexpr T* begin() noexcept { return data(); } + constexpr const T* begin() const noexcept { return data(); } + constexpr T* end() noexcept { return data() + size(); } + constexpr const T* end() const noexcept { return data() + size(); } + constexpr const T* cbegin() const noexcept { return data(); } + constexpr const T* cend() const noexcept { return end(); } + constexpr auto rbegin() noexcept { return std::reverse_iterator(end()); } + constexpr auto rbegin() const noexcept { return std::reverse_iterator(end()); } + constexpr auto rend() noexcept { return std::reverse_iterator(begin()); } + constexpr auto rend() const noexcept { return std::reverse_iterator(begin()); } + constexpr auto crbegin() const noexcept { return rbegin(); } + constexpr auto crend() const noexcept { return rend(); } + T& front() noexcept { return data()[0]; } + const T& front() const noexcept { return data()[0]; } + T& back() noexcept { return data()[size() - 1]; } + const T& back() const noexcept { return data()[size() - 1]; } + + void push_back(const T& t) + { + reserve(size() + 1); + new (&data()[size()]) T(t); + size_++; + } + + void push_back(T&& t) + { + reserve(size() + 1); + new (&data()[size()]) T(std::move(t)); + size_++; + } + + void pop_back() + { + T* end = &data()[size() - 1]; + end->~T(); + size_--; + } + + void clear() { for (auto& x : *this) x.~T(); size_ = 0; } + + void reserve(size_type c) + { + _reserve_mem(c); + } + + void resize(size_type s) + { + resize(s, T()); + } + + void resize(size_type s, const T& value) + { + if (s <= size()) + { + auto itr_begin = rbegin(); + auto itr_end = std::prev(rend(), s); + size_t count_destroyed = 0; + for (auto itr = itr_begin; itr != itr_end; itr++) + { + itr->~T(); + count_destroyed++; + } + size_ = s; + } + else + { + reserve(s); + size_type oldsize = size(); + size_ = s; + for (auto itr = std::next(begin(), oldsize); itr != end(); itr++) + { + try + { + new (itr) T(value); + } + catch (...) + { + size_ = oldsize; + std::rethrow_exception(std::current_exception()); + } + } + } + } + + const T& at(size_type i) const { if (i >= size()) throw std::out_of_range("index out of range"); return data()[i]; } + T& at(size_type i) { if (i >= size()) throw std::out_of_range("index out of range"); return data()[i]; } + + T& operator[](size_type i) { return data()[i]; } + const T& operator[](size_type i) const { return data()[i]; } + + iterator erase(const_iterator first) { return erase(first, first + 1); } + iterator erase(const_iterator first, const_iterator last) + { + iterator firstm = const_cast(first); + iterator lastm = const_cast(last); + if (first == last) return firstm; + + auto diff = last - first; + if (last != end()) std::move(lastm, end(), firstm); + resize(size_ - diff); + + return firstm; + } + + iterator insert(const_iterator pos, const T& value) + { + return insert(pos, (size_type)1, std::forward(value)); + } + + iterator insert(const_iterator pos, T&& value) + { + size_type oldsize = size(); + difference_type offs = pos - data(); + push_back(std::move(value)); + std::rotate(data() + offs, data() + oldsize, data() + size()); + return data() + offs; + } + + iterator insert(const_iterator pos, size_type count, const T& value) + { + size_type oldsize = size(); + difference_type offs = pos - data(); + reserve(oldsize + count); + T* d = data(); + for (uint32_t i = 0; i < count; i++) + { + // must be copy-initialized; + // value is currently uninitialized + new ((T*)(&d[oldsize + i])) T(value); + } + size_ = oldsize + count; + std::rotate(d + offs, d + oldsize, d + (oldsize + count)); + return data() + offs; + } + + template < + class InputIt, + typename std::enable_if_t< + std::is_constructible< + T, + typename std::iterator_traits::reference + >::value, + int + > = 0 + > + iterator insert(const_iterator pos, InputIt first, InputIt last) + { + size_type oldsize = size(); + difference_type offs = pos - data(); + + size_type count = 0; + while (first != last) + { + push_back(*first); + ++first; + ++count; + } + std::rotate(data() + offs, data() + oldsize, data() + (oldsize + count)); + return data() + offs; + } + + template + iterator emplace(const_iterator position, A&&... args) + { + return insert(position, T(std::forward(args)...)); + } + + template + T& emplace_back(A&&... args) + { + reserve(size() + 1); + new (&data()[size()]) T(std::forward(args)...); + size_++; + return (*this)[size_ - 1]; + } +}; + +extern template class Vector; +extern template class Vector; +extern template class Vector; +extern template class Vector; +extern template class Vector; +extern template class Vector; +extern template class Vector; +extern template class Vector; +extern template class Vector; +extern template class Vector; + +} // namespace srb2 + +#endif // SRB2_CORE_VEC_HPP