From c6c18c18d31cc12eec4fdb63bdf40ec81018a6b6 Mon Sep 17 00:00:00 2001 From: Gary Talent Date: Tue, 16 Aug 2022 01:30:12 -0500 Subject: [PATCH] [ox/std] Add support for custom allocators in Vector --- deps/ox/src/ox/std/vector.hpp | 225 ++++++++++++++++++++-------------- 1 file changed, 134 insertions(+), 91 deletions(-) diff --git a/deps/ox/src/ox/std/vector.hpp b/deps/ox/src/ox/std/vector.hpp index 47082cf1..a13892b1 100644 --- a/deps/ox/src/ox/std/vector.hpp +++ b/deps/ox/src/ox/std/vector.hpp @@ -8,6 +8,8 @@ #pragma once +#include "array.hpp" +#include "concepts.hpp" #include "bit.hpp" #include "error.hpp" #include "initializerlist.hpp" @@ -20,71 +22,103 @@ namespace ox { +template +struct VectorMemMap { + typename PlatSpec::size_t size = 0; + typename PlatSpec::size_t cap = 0; + typename PlatSpec::PtrType items = 0; + uint8_t allocator = 0; +}; + +template +[[nodiscard]] +constexpr auto sizeOf(const VectorMemMap &t) noexcept { + constexpr auto padding = [](std::size_t size, std::size_t al) { + return size - size % al; + }; + std::size_t size = 0; + size += sizeof(t.size); + size += padding(size, PlatSpec::alignOf(t.cap)); + size += sizeof(t.cap); + size += padding(size, PlatSpec::alignOf(t.items)); + size += sizeof(t.items); + size += padding(size, PlatSpec::alignOf(t.allocator)); + size += sizeof(t.allocator); + return size; +} + +template +[[nodiscard]] +constexpr auto alignOf(const VectorMemMap&) noexcept { + const typename PlatSpec::size_t i = 0; + return PlatSpec::alignOf(i); +} + namespace detail { -template +template struct VectorAllocator { private: - std::allocator m_allocator; - AllocAlias m_data[Size] = {}; + ox::Array, Size> m_data = {}; + Allocator m_allocator; public: constexpr VectorAllocator() noexcept = default; constexpr VectorAllocator(const VectorAllocator&) noexcept = default; constexpr VectorAllocator(VectorAllocator&&) noexcept = default; - protected: + constexpr void allocate(T **items, std::size_t cap) noexcept { // small vector optimization cannot be done it constexpr, but it doesn't really matter in constexpr if (std::is_constant_evaluated() || cap > Size) { *items = m_allocator.allocate(cap); } else { - *items = reinterpret_cast(m_data); + *items = reinterpret_cast(m_data.data()); } } constexpr void moveConstructItemsFrom(T **items, VectorAllocator &src, const std::size_t count, const std::size_t cap) noexcept { // this totally idiotic redundant check (&& count <= Size) is required to address a bug in devkitARM, // try removing it later - if (!std::is_constant_evaluated() && cap <= Size && count <= Size) { - const auto dstItems = reinterpret_cast(m_data); - const auto srcItems = reinterpret_cast(src.m_data); + if (cap <= Size && count <= Size) { + const auto dstItems = reinterpret_cast(m_data.data()); + const auto srcItems = reinterpret_cast(src.m_data.data()); for (auto i = 0u; i < count; ++i) { std::construct_at(&dstItems[i], std::move(srcItems[i])); } - *items = reinterpret_cast(m_data); + *items = reinterpret_cast(m_data.data()); } } constexpr void moveItemsFrom(T **items, VectorAllocator &src, const std::size_t count, const std::size_t cap) noexcept { // this totally idiotic redundant check (&& count <= Size) is required to address a bug in devkitARM, // try removing it later - if (!std::is_constant_evaluated() && cap <= Size && count <= Size) { - const auto dstItems = reinterpret_cast(m_data); - const auto srcItems = reinterpret_cast(src.m_data); + if (cap <= Size && count <= Size) { + const auto dstItems = reinterpret_cast(m_data.data()); + const auto srcItems = reinterpret_cast(src.m_data.data()); for (std::size_t i = 0; i < count; ++i) { dstItems[i] = std::move(srcItems[i]); } - *items = reinterpret_cast(m_data); + *items = reinterpret_cast(m_data.data()); } } constexpr void deallocate(T *items, std::size_t cap) noexcept { // small vector optimization cannot be done it constexpr, but it doesn't really matter in constexpr - if (std::is_constant_evaluated() || (items && static_cast(items) != static_cast(m_data))) { + if (std::is_constant_evaluated() || (items && static_cast(items) != static_cast(m_data.data()))) { m_allocator.deallocate(items, cap); } } }; -template -struct VectorAllocator { +template +struct VectorAllocator { private: - std::allocator m_allocator; + Allocator m_allocator; public: constexpr VectorAllocator() noexcept = default; constexpr VectorAllocator(const VectorAllocator&) noexcept = default; constexpr VectorAllocator(VectorAllocator&&) noexcept = default; - protected: + constexpr void allocate(T **items, std::size_t cap) noexcept { *items = m_allocator.allocate(cap); } @@ -107,8 +141,8 @@ struct VectorAllocator { } -template -class Vector: detail::VectorAllocator { +template> +class Vector { public: using value_type = T; @@ -223,6 +257,7 @@ class Vector: detail::VectorAllocator { std::size_t m_size = 0; std::size_t m_cap = 0; T *m_items = nullptr; + detail::VectorAllocator m_allocator; public: constexpr Vector() noexcept = default; @@ -378,61 +413,61 @@ class Vector: detail::VectorAllocator { }; -template -using VectorIt = typename Vector::template iterator; +template +using VectorIt = typename Vector::template iterator; -template -constexpr VectorIt operator+(std::size_t n, const VectorIt &a) { +template +constexpr VectorIt operator+(std::size_t n, const VectorIt &a) { return a + n; } -template -constexpr Vector::Vector(std::size_t size) noexcept { +template +constexpr Vector::Vector(std::size_t size) noexcept { m_size = size; m_cap = m_size; - this->allocate(&m_items, m_cap); + m_allocator.allocate(&m_items, m_cap); for (std::size_t i = 0; i < size; ++i) { std::construct_at(&m_items[i]); } } -template -constexpr Vector::Vector(std::initializer_list list) noexcept { +template +constexpr Vector::Vector(std::initializer_list list) noexcept { for (auto &item : list) { emplace_back(item); } } -template -constexpr Vector::Vector(const Vector &other) { +template +constexpr Vector::Vector(const Vector &other) { m_size = other.m_size; m_cap = other.m_cap; - this->allocate(&m_items, other.m_cap); + m_allocator.allocate(&m_items, other.m_cap); for (std::size_t i = 0; i < m_size; ++i) { std::construct_at(&m_items[i], other.m_items[i]); } } -template -constexpr Vector::Vector(Vector &&other) noexcept { +template +constexpr Vector::Vector(Vector &&other) noexcept { m_size = other.m_size; m_cap = other.m_cap; m_items = other.m_items; - this->moveConstructItemsFrom(&m_items, other, m_size, m_cap); + m_allocator.moveConstructItemsFrom(&m_items, other.m_allocator, m_size, m_cap); other.m_size = 0; other.m_cap = 0; other.m_items = nullptr; } -template -constexpr Vector::~Vector() { +template +constexpr Vector::~Vector() { clear(); - this->deallocate(m_items, m_cap); + m_allocator.deallocate(m_items, m_cap); m_items = nullptr; } -template -constexpr bool Vector::operator==(const Vector &other) const { +template +constexpr bool Vector::operator==(const Vector &other) const { if (m_size != other.m_size) { return false; } @@ -444,15 +479,15 @@ constexpr bool Vector::operator==(const Vector &other) const return true; } -template -constexpr Vector &Vector::operator=(const Vector &other) { +template +constexpr Vector &Vector::operator=(const Vector &other) { if (this != &other) { clear(); - this->deallocate(m_items, m_cap); + m_allocator.deallocate(m_items, m_cap); m_items = nullptr; m_size = other.m_size; m_cap = other.m_cap; - this->allocate(&m_items, other.m_cap); + m_allocator.allocate(&m_items, other.m_cap); for (std::size_t i = 0; i < m_size; i++) { std::construct_at(&m_items[i], other.m_items[i]); } @@ -460,15 +495,15 @@ constexpr Vector &Vector::operator=(cons return *this; } -template -constexpr Vector &Vector::operator=(Vector &&other) noexcept { +template +constexpr Vector &Vector::operator=(Vector &&other) noexcept { if (this != &other) { clear(); - this->deallocate(m_items, m_cap); + m_allocator.deallocate(m_items, m_cap); m_size = other.m_size; m_cap = other.m_cap; m_items = other.m_items; - this->moveItemsFrom(&m_items, other, m_size, m_cap); + m_allocator.moveItemsFrom(&m_items, other.m_allocator, m_size, m_cap); other.m_size = 0; other.m_cap = 0; other.m_items = nullptr; @@ -476,18 +511,18 @@ constexpr Vector &Vector::operator=(Vect return *this; } -template -constexpr T &Vector::operator[](std::size_t i) noexcept { +template +constexpr T &Vector::operator[](std::size_t i) noexcept { return m_items[i]; } -template -constexpr const T &Vector::operator[](std::size_t i) const noexcept { +template +constexpr const T &Vector::operator[](std::size_t i) const noexcept { return m_items[i]; } -template -Result Vector::front() noexcept { +template +Result Vector::front() noexcept { if (!m_size) { AllocAlias v; return {*reinterpret_cast(&v), OxError(1)}; @@ -495,8 +530,8 @@ Result Vector::front() noexcept { return m_items[0]; } -template -Result Vector::front() const noexcept { +template +Result Vector::front() const noexcept { if (!m_size) { AllocAlias v; return {*reinterpret_cast(&v), OxError(1)}; @@ -504,8 +539,8 @@ Result Vector::front() const noexcept { return m_items[0]; } -template -constexpr Result Vector::back() noexcept { +template +constexpr Result Vector::back() noexcept { if (!m_size) { AllocAlias v; return {*reinterpret_cast(&v), OxError(1)}; @@ -513,8 +548,8 @@ constexpr Result Vector::back() noexcept { return m_items[m_size - 1]; } -template -constexpr Result Vector::back() const noexcept { +template +constexpr Result Vector::back() const noexcept { if (!m_size) { AllocAlias v; return {*reinterpret_cast(&v), OxError(1)}; @@ -522,18 +557,18 @@ constexpr Result Vector::back() const noexcept { return m_items[m_size - 1]; } -template -constexpr std::size_t Vector::size() const noexcept { +template +constexpr std::size_t Vector::size() const noexcept { return m_size; } -template -constexpr bool Vector::empty() const noexcept { +template +constexpr bool Vector::empty() const noexcept { return !m_size; } -template -constexpr void Vector::clear() { +template +constexpr void Vector::clear() { if constexpr(is_class()) { for (std::size_t i = 0; i < m_size; ++i) { m_items[i].~T(); @@ -542,8 +577,8 @@ constexpr void Vector::clear() { m_size = 0; } -template -constexpr void Vector::resize(std::size_t size) { +template +constexpr void Vector::resize(std::size_t size) { if (m_cap < size) { expandCap(size); } @@ -559,8 +594,8 @@ constexpr void Vector::resize(std::size_t size) { m_size = size; } -template -constexpr bool Vector::contains(const T &v) const { +template +constexpr bool Vector::contains(const T &v) const { for (std::size_t i = 0; i < m_size; i++) { if (m_items[i] == v) { return true; @@ -569,8 +604,8 @@ constexpr bool Vector::contains(const T &v) const { return false; } -template -constexpr void Vector::insert(std::size_t pos, std::size_t cnt, const T &val) { +template +constexpr void Vector::insert(std::size_t pos, std::size_t cnt, const T &val) { // TODO: insert should ideally have its own expandCap if (m_size + cnt > m_cap) { expandCap(m_cap ? m_size + cnt : initialSize); @@ -588,8 +623,8 @@ constexpr void Vector::insert(std::size_t pos, std::size_t c ++m_size; } -template -constexpr void Vector::insert(std::size_t pos, const T &val) { +template +constexpr void Vector::insert(std::size_t pos, const T &val) { // TODO: insert should ideally have its own expandCap if (m_size == m_cap) { expandCap(m_cap ? m_cap * 2 : initialSize); @@ -605,19 +640,19 @@ constexpr void Vector::insert(std::size_t pos, const T &val) ++m_size; } -template +template template -constexpr T &Vector::emplace_back(Args&&... args) { +constexpr T &Vector::emplace_back(Args&&... args) { if (m_size == m_cap) { expandCap(m_cap ? m_cap * 2 : initialSize); } - auto out = std::construct_at(&m_items[m_size], forward(args)...); + auto out = std::construct_at(&m_items[m_size], ox::forward(args)...); ++m_size; return *out; } -template -constexpr void Vector::push_back(const T &item) { +template +constexpr void Vector::push_back(const T &item) { if (m_size == m_cap) { expandCap(m_cap ? m_cap * 2 : initialSize); } @@ -625,19 +660,19 @@ constexpr void Vector::push_back(const T &item) { ++m_size; } -template -constexpr void Vector::pop_back() { +template +constexpr void Vector::pop_back() { --m_size; m_items[m_size].~T(); } -template -constexpr Result::template iterator> Vector::erase(const iterator<> &pos) { +template +constexpr Result::template iterator> Vector::erase(const iterator<> &pos) { return erase(pos.offset()); } -template -constexpr Result::template iterator> Vector::erase(std::size_t pos) { +template +constexpr Result::template iterator> Vector::erase(std::size_t pos) { if (pos >= m_size) { return OxError(1, "Vector::erase failed: pos is greater than Vector size"); } @@ -649,8 +684,8 @@ constexpr Result::template iterator -constexpr Error Vector::unordered_erase(std::size_t pos) { +template +constexpr Error Vector::unordered_erase(std::size_t pos) { if (pos >= m_size) { return OxError(1); } @@ -660,20 +695,28 @@ constexpr Error Vector::unordered_erase(std::size_t pos) { return OxError(0); } -template -constexpr void Vector::expandCap(std::size_t cap) { +template +constexpr void Vector::expandCap(std::size_t cap) { const auto oldItems = m_items; const auto oldCap = m_cap; m_cap = cap; - this->allocate(&m_items, cap); + m_allocator.allocate(&m_items, cap); if (oldItems) { // move over old items const auto itRange = ox::min(cap, m_size); for (std::size_t i = 0; i < itRange; ++i) { std::construct_at(&m_items[i], std::move(oldItems[i])); oldItems[i].~T(); } - this->deallocate(oldItems, oldCap); + m_allocator.deallocate(oldItems, oldCap); } } + +template +[[nodiscard]] +constexpr auto alignOf(const Vector&) noexcept { + const typename PlatSpec::size_t i = 0; + return PlatSpec::alignOf(i); +} + }