From 9777e8e76b8cb9473eafc69ea935bb4912b4173e Mon Sep 17 00:00:00 2001 From: Jordan Woyak Date: Sat, 25 Jan 2025 19:59:18 -0600 Subject: [PATCH] Common: Make SmallVector work with non-standard-layout types. --- Source/Core/Common/SmallVector.h | 98 +++++++++++++++++++++++++------- 1 file changed, 79 insertions(+), 19 deletions(-) diff --git a/Source/Core/Common/SmallVector.h b/Source/Core/Common/SmallVector.h index df50b0c115..d06bcd1cfa 100644 --- a/Source/Core/Common/SmallVector.h +++ b/Source/Core/Common/SmallVector.h @@ -3,8 +3,8 @@ #pragma once #include +#include #include -#include #include namespace Common @@ -14,39 +14,99 @@ namespace Common template class SmallVector final { - static_assert(std::is_standard_layout_v == true, "Type must be a standard layout type"); - public: + using value_type = T; + SmallVector() = default; - explicit SmallVector(size_t size) : m_size(size) {} + explicit SmallVector(size_t new_size) { resize(new_size); } - void push_back(const T& x) { m_array[m_size++] = x; } - void push_back(T&& x) { m_array[m_size++] = std::move(x); } + ~SmallVector() { clear(); } - template - T& emplace_back(Args&&... args) + SmallVector(const SmallVector& other) { - return m_array[m_size++] = T{std::forward(args)...}; + for (auto& value : other) + emplace_back(value); } - T& operator[](size_t i) { return m_array[i]; } - const T& operator[](size_t i) const { return m_array[i]; } + SmallVector& operator=(const SmallVector& rhs) + { + clear(); + for (auto& value : rhs) + emplace_back(value); + return *this; + } - auto data() { return m_array.data(); } - auto begin() { return m_array.begin(); } - auto end() { return m_array.begin() + m_size; } + SmallVector(SmallVector&& other) + { + for (auto& value : other) + emplace_back(std::move(value)); + other.clear(); + } - auto data() const { return m_array.data(); } - auto begin() const { return m_array.begin(); } - auto end() const { return m_array.begin() + m_size; } + SmallVector& operator=(SmallVector&& rhs) + { + clear(); + for (auto& value : rhs) + emplace_back(std::move(value)); + rhs.clear(); + return *this; + } + void push_back(const value_type& x) { emplace_back(x); } + void push_back(value_type&& x) { emplace_back(std::move(x)); } + + template + value_type& emplace_back(Args&&... args) + { + assert(m_size < MaxSize); + return *::new (&m_array[m_size++ * sizeof(value_type)]) value_type{std::forward(args)...}; + } + + void pop_back() + { + assert(m_size > 0); + std::destroy_at(data() + --m_size); + } + + value_type& operator[](size_t i) + { + assert(i < m_size); + return data()[i]; + } + const value_type& operator[](size_t i) const + { + assert(i < m_size); + return data()[i]; + } + + auto data() { return std::launder(reinterpret_cast(m_array.data())); } + auto begin() { return data(); } + auto end() { return data() + m_size; } + + auto data() const { return std::launder(reinterpret_cast(m_array.data())); } + auto begin() const { return data(); } + auto end() const { return data() + m_size; } + + size_t capacity() const { return MaxSize; } size_t size() const { return m_size; } + bool empty() const { return m_size == 0; } - void clear() { m_size = 0; } + void resize(size_t new_size) + { + assert(new_size <= MaxSize); + + while (size() < new_size) + emplace_back(); + + while (size() > new_size) + pop_back(); + } + + void clear() { resize(0); } private: - std::array m_array{}; + alignas(value_type) std::array m_array; size_t m_size = 0; };