Created
September 22, 2025 16:30
-
-
Save Pikachuxxxx/97528ea701d38c7d20afb1f736c0ebfe to your computer and use it in GitHub Desktop.
Custom Arrays for Razix Engine
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| #pragma once | |
| namespace Razix { | |
| // Forward declarations | |
| template<typename T, size_t N> | |
| class FixedArray; | |
| template<typename T> | |
| class DynamicArray; | |
| template<typename T, size_t SmallSize = 16> | |
| class SmallArray; | |
| //============================================================================= | |
| // FixedArray - Stack allocated, compile-time size | |
| //============================================================================= | |
| template<typename T, size_t N> | |
| class FixedArray | |
| { | |
| public: | |
| using value_type = T; | |
| using size_type = size_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*; | |
| private: | |
| alignas(T) char m_data[sizeof(T) * N]; | |
| size_type m_size = 0; | |
| public: | |
| // Constructors | |
| FixedArray() = default; | |
| FixedArray(std::initializer_list<T> init) | |
| { | |
| for (const auto& item: init) { | |
| if (m_size < N) { | |
| push_back(item); | |
| } | |
| } | |
| } | |
| explicit FixedArray(size_type count, const T& value = T{}) | |
| { | |
| resize(count, value); | |
| } | |
| // Destructor | |
| ~FixedArray() | |
| { | |
| clear(); | |
| } | |
| // Copy constructor | |
| FixedArray(const FixedArray& other) | |
| : m_size(other.m_size) | |
| { | |
| for (size_type i = 0; i < m_size; ++i) { | |
| new (data() + i) T(other[i]); | |
| } | |
| } | |
| // Move constructor | |
| FixedArray(FixedArray&& other) noexcept | |
| : m_size(other.m_size) | |
| { | |
| for (size_type i = 0; i < m_size; ++i) { | |
| new (data() + i) T(std::move(other[i])); | |
| } | |
| other.clear(); | |
| } | |
| // Assignment operators | |
| FixedArray& operator=(const FixedArray& other) | |
| { | |
| if (this != &other) { | |
| clear(); | |
| m_size = other.m_size; | |
| for (size_type i = 0; i < m_size; ++i) { | |
| new (data() + i) T(other[i]); | |
| } | |
| } | |
| return *this; | |
| } | |
| FixedArray& operator=(FixedArray&& other) noexcept | |
| { | |
| if (this != &other) { | |
| clear(); | |
| m_size = other.m_size; | |
| for (size_type i = 0; i < m_size; ++i) { | |
| new (data() + i) T(std::move(other[i])); | |
| } | |
| other.clear(); | |
| } | |
| return *this; | |
| } | |
| // Element access | |
| reference operator[](size_type index) | |
| { | |
| return data()[index]; | |
| } | |
| const_reference operator[](size_type index) const | |
| { | |
| return data()[index]; | |
| } | |
| reference at(size_type index) | |
| { | |
| if (index >= m_size) { | |
| // Handle bounds error - could throw or assert | |
| return data()[0]; // Fallback | |
| } | |
| return data()[index]; | |
| } | |
| const_reference at(size_type index) const | |
| { | |
| if (index >= m_size) { | |
| return data()[0]; // Fallback | |
| } | |
| return data()[index]; | |
| } | |
| reference front() { return data()[0]; } | |
| const_reference front() const { return data()[0]; } | |
| reference back() { return data()[m_size - 1]; } | |
| const_reference back() const { return data()[m_size - 1]; } | |
| // Iterators | |
| iterator begin() { return data(); } | |
| const_iterator begin() const { return data(); } | |
| const_iterator cbegin() const { return data(); } | |
| iterator end() { return data() + m_size; } | |
| const_iterator end() const { return data() + m_size; } | |
| const_iterator cend() const { return data() + m_size; } | |
| // Capacity | |
| bool empty() const { return m_size == 0; } | |
| size_type size() const { return m_size; } | |
| constexpr size_type max_size() const { return N; } | |
| constexpr size_type capacity() const { return N; } | |
| // Modifiers | |
| void clear() | |
| { | |
| for (size_type i = 0; i < m_size; ++i) { | |
| data()[i].~T(); | |
| } | |
| m_size = 0; | |
| } | |
| void push_back(const T& value) | |
| { | |
| if (m_size < N) { | |
| new (data() + m_size) T(value); | |
| ++m_size; | |
| } | |
| } | |
| void push_back(T&& value) | |
| { | |
| if (m_size < N) { | |
| new (data() + m_size) T(std::move(value)); | |
| ++m_size; | |
| } | |
| } | |
| template<typename... Args> | |
| reference emplace_back(Args&&... args) | |
| { | |
| if (m_size < N) { | |
| T* ptr = new (data() + m_size) T(std::forward<Args>(args)...); | |
| ++m_size; | |
| return *ptr; | |
| } | |
| return back(); // Fallback | |
| } | |
| void pop_back() | |
| { | |
| if (m_size > 0) { | |
| --m_size; | |
| data()[m_size].~T(); | |
| } | |
| } | |
| void resize(size_type count, const T& value = T{}) | |
| { | |
| if (count > N) count = N; | |
| if (count > m_size) { | |
| for (size_type i = m_size; i < count; ++i) { | |
| new (data() + i) T(value); | |
| } | |
| } else if (count < m_size) { | |
| for (size_type i = count; i < m_size; ++i) { | |
| data()[i].~T(); | |
| } | |
| } | |
| m_size = count; | |
| } | |
| // Data access | |
| pointer data() { return reinterpret_cast<T*>(m_data); } | |
| const_pointer data() const { return reinterpret_cast<const T*>(m_data); } | |
| }; | |
| //============================================================================= | |
| // DynamicArray - Heap allocated, runtime resizable | |
| //============================================================================= | |
| template<typename T> | |
| class DynamicArray | |
| { | |
| public: | |
| using value_type = T; | |
| using size_type = size_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*; | |
| private: | |
| T* m_data = nullptr; | |
| size_type m_size = 0; | |
| size_type m_capacity = 0; | |
| static constexpr size_type initial_capacity = 4; | |
| static constexpr double growth_factor = 1.5; | |
| void reallocate(size_type new_capacity) | |
| { | |
| T* new_data = static_cast<T*>(operator new(new_capacity * sizeof(T))); | |
| // Move existing elements | |
| for (size_type i = 0; i < m_size; ++i) { | |
| new (new_data + i) T(std::move(m_data[i])); | |
| m_data[i].~T(); | |
| } | |
| operator delete(m_data); | |
| m_data = new_data; | |
| m_capacity = new_capacity; | |
| } | |
| public: | |
| // Constructors | |
| DynamicArray() = default; | |
| explicit DynamicArray(size_type count, const T& value = T{}) | |
| { | |
| resize(count, value); | |
| } | |
| DynamicArray(std::initializer_list<T> init) | |
| { | |
| reserve(init.size()); | |
| for (const auto& item: init) { | |
| push_back(item); | |
| } | |
| } | |
| // Destructor | |
| ~DynamicArray() | |
| { | |
| clear(); | |
| operator delete(m_data); | |
| } | |
| // Copy constructor | |
| DynamicArray(const DynamicArray& other) | |
| { | |
| reserve(other.m_size); | |
| for (size_type i = 0; i < other.m_size; ++i) { | |
| push_back(other[i]); | |
| } | |
| } | |
| // Move constructor | |
| DynamicArray(DynamicArray&& other) noexcept | |
| : m_data(other.m_data), m_size(other.m_size), m_capacity(other.m_capacity) | |
| { | |
| other.m_data = nullptr; | |
| other.m_size = 0; | |
| other.m_capacity = 0; | |
| } | |
| // Assignment operators | |
| DynamicArray& operator=(const DynamicArray& other) | |
| { | |
| if (this != &other) { | |
| clear(); | |
| reserve(other.m_size); | |
| for (size_type i = 0; i < other.m_size; ++i) { | |
| push_back(other[i]); | |
| } | |
| } | |
| return *this; | |
| } | |
| DynamicArray& operator=(DynamicArray&& other) noexcept | |
| { | |
| if (this != &other) { | |
| clear(); | |
| operator delete(m_data); | |
| m_data = other.m_data; | |
| m_size = other.m_size; | |
| m_capacity = other.m_capacity; | |
| other.m_data = nullptr; | |
| other.m_size = 0; | |
| other.m_capacity = 0; | |
| } | |
| return *this; | |
| } | |
| // Element access (same as FixedArray) | |
| reference operator[](size_type index) { return m_data[index]; } | |
| const_reference operator[](size_type index) const { return m_data[index]; } | |
| reference at(size_type index) | |
| { | |
| if (index >= m_size) { | |
| return m_data[0]; // Fallback | |
| } | |
| return m_data[index]; | |
| } | |
| const_reference at(size_type index) const | |
| { | |
| if (index >= m_size) { | |
| return m_data[0]; // Fallback | |
| } | |
| return m_data[index]; | |
| } | |
| reference front() { return m_data[0]; } | |
| const_reference front() const { return m_data[0]; } | |
| reference back() { return m_data[m_size - 1]; } | |
| const_reference back() const { return m_data[m_size - 1]; } | |
| // Iterators | |
| iterator begin() { return m_data; } | |
| const_iterator begin() const { return m_data; } | |
| const_iterator cbegin() const { return m_data; } | |
| iterator end() { return m_data + m_size; } | |
| const_iterator end() const { return m_data + m_size; } | |
| const_iterator cend() const { return m_data + m_size; } | |
| // Capacity | |
| bool empty() const { return m_size == 0; } | |
| size_type size() const { return m_size; } | |
| size_type capacity() const { return m_capacity; } | |
| void reserve(size_type new_capacity) | |
| { | |
| if (new_capacity > m_capacity) { | |
| reallocate(new_capacity); | |
| } | |
| } | |
| void shrink_to_fit() | |
| { | |
| if (m_size < m_capacity) { | |
| reallocate(m_size); | |
| } | |
| } | |
| // Modifiers | |
| void clear() | |
| { | |
| for (size_type i = 0; i < m_size; ++i) { | |
| m_data[i].~T(); | |
| } | |
| m_size = 0; | |
| } | |
| void push_back(const T& value) | |
| { | |
| if (m_size >= m_capacity) { | |
| size_type new_capacity = m_capacity == 0 ? initial_capacity | |
| : static_cast<size_type>(m_capacity * growth_factor); | |
| reallocate(new_capacity); | |
| } | |
| new (m_data + m_size) T(value); | |
| ++m_size; | |
| } | |
| void push_back(T&& value) | |
| { | |
| if (m_size >= m_capacity) { | |
| size_type new_capacity = m_capacity == 0 ? initial_capacity | |
| : static_cast<size_type>(m_capacity * growth_factor); | |
| reallocate(new_capacity); | |
| } | |
| new (m_data + m_size) T(std::move(value)); | |
| ++m_size; | |
| } | |
| template<typename... Args> | |
| reference emplace_back(Args&&... args) | |
| { | |
| if (m_size >= m_capacity) { | |
| size_type new_capacity = m_capacity == 0 ? initial_capacity | |
| : static_cast<size_type>(m_capacity * growth_factor); | |
| reallocate(new_capacity); | |
| } | |
| T* ptr = new (m_data + m_size) T(std::forward<Args>(args)...); | |
| ++m_size; | |
| return *ptr; | |
| } | |
| void pop_back() | |
| { | |
| if (m_size > 0) { | |
| --m_size; | |
| m_data[m_size].~T(); | |
| } | |
| } | |
| void resize(size_type count, const T& value = T{}) | |
| { | |
| if (count > m_capacity) { | |
| reserve(count); | |
| } | |
| if (count > m_size) { | |
| for (size_type i = m_size; i < count; ++i) { | |
| new (m_data + i) T(value); | |
| } | |
| } else if (count < m_size) { | |
| for (size_type i = count; i < m_size; ++i) { | |
| m_data[i].~T(); | |
| } | |
| } | |
| m_size = count; | |
| } | |
| // Data access | |
| pointer data() { return m_data; } | |
| const_pointer data() const { return m_data; } | |
| }; | |
| //============================================================================= | |
| // SmallArray - Small buffer optimization | |
| //============================================================================= | |
| template<typename T, size_t SmallSize> | |
| class SmallArray | |
| { | |
| public: | |
| using value_type = T; | |
| using size_type = size_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*; | |
| private: | |
| union | |
| { | |
| alignas(T) char m_small_data[sizeof(T) * SmallSize]; | |
| T* m_large_data; | |
| }; | |
| size_type m_size = 0; | |
| size_type m_capacity = SmallSize; | |
| bool m_is_small = true; | |
| static constexpr double growth_factor = 1.5; | |
| void reallocate(size_type new_capacity) | |
| { | |
| T* new_data = static_cast<T*>(operator new(new_capacity * sizeof(T))); | |
| T* old_data = data(); | |
| // Move existing elements | |
| for (size_type i = 0; i < m_size; ++i) { | |
| new (new_data + i) T(std::move(old_data[i])); | |
| old_data[i].~T(); | |
| } | |
| if (!m_is_small) { | |
| operator delete(m_large_data); | |
| } | |
| m_large_data = new_data; | |
| m_capacity = new_capacity; | |
| m_is_small = false; | |
| } | |
| public: | |
| // Constructors | |
| SmallArray() = default; | |
| explicit SmallArray(size_type count, const T& value = T{}) | |
| { | |
| resize(count, value); | |
| } | |
| SmallArray(std::initializer_list<T> init) | |
| { | |
| if (init.size() > SmallSize) { | |
| reserve(init.size()); | |
| } | |
| for (const auto& item: init) { | |
| push_back(item); | |
| } | |
| } | |
| // Destructor | |
| ~SmallArray() | |
| { | |
| clear(); | |
| if (!m_is_small) { | |
| operator delete(m_large_data); | |
| } | |
| } | |
| // Copy constructor | |
| SmallArray(const SmallArray& other) | |
| { | |
| if (other.m_size > SmallSize) { | |
| reserve(other.m_size); | |
| } | |
| for (size_type i = 0; i < other.m_size; ++i) { | |
| push_back(other[i]); | |
| } | |
| } | |
| // Move constructor | |
| SmallArray(SmallArray&& other) noexcept | |
| { | |
| if (other.m_is_small) { | |
| m_size = other.m_size; | |
| m_capacity = SmallSize; | |
| m_is_small = true; | |
| for (size_type i = 0; i < m_size; ++i) { | |
| new (small_data() + i) T(std::move(other.small_data()[i])); | |
| } | |
| } else { | |
| m_large_data = other.m_large_data; | |
| m_size = other.m_size; | |
| m_capacity = other.m_capacity; | |
| m_is_small = false; | |
| other.m_large_data = nullptr; | |
| other.m_size = 0; | |
| other.m_capacity = SmallSize; | |
| other.m_is_small = true; | |
| } | |
| } | |
| // Assignment operators | |
| SmallArray& operator=(const SmallArray& other) | |
| { | |
| if (this != &other) { | |
| clear(); | |
| if (!m_is_small) { | |
| operator delete(m_large_data); | |
| m_is_small = true; | |
| m_capacity = SmallSize; | |
| } | |
| if (other.m_size > SmallSize) { | |
| reserve(other.m_size); | |
| } | |
| for (size_type i = 0; i < other.m_size; ++i) { | |
| push_back(other[i]); | |
| } | |
| } | |
| return *this; | |
| } | |
| SmallArray& operator=(SmallArray&& other) noexcept | |
| { | |
| if (this != &other) { | |
| clear(); | |
| if (!m_is_small) { | |
| operator delete(m_large_data); | |
| } | |
| if (other.m_is_small) { | |
| m_size = other.m_size; | |
| m_capacity = SmallSize; | |
| m_is_small = true; | |
| for (size_type i = 0; i < m_size; ++i) { | |
| new (small_data() + i) T(std::move(other.small_data()[i])); | |
| } | |
| } else { | |
| m_large_data = other.m_large_data; | |
| m_size = other.m_size; | |
| m_capacity = other.m_capacity; | |
| m_is_small = false; | |
| other.m_large_data = nullptr; | |
| other.m_size = 0; | |
| other.m_capacity = SmallSize; | |
| other.m_is_small = true; | |
| } | |
| } | |
| return *this; | |
| } | |
| // Element access | |
| reference operator[](size_type index) { return data()[index]; } | |
| const_reference operator[](size_type index) const { return data()[index]; } | |
| reference at(size_type index) | |
| { | |
| if (index >= m_size) { | |
| return data()[0]; // Fallback | |
| } | |
| return data()[index]; | |
| } | |
| const_reference at(size_type index) const | |
| { | |
| if (index >= m_size) { | |
| return data()[0]; // Fallback | |
| } | |
| return data()[index]; | |
| } | |
| reference front() { return data()[0]; } | |
| const_reference front() const { return data()[0]; } | |
| reference back() { return data()[m_size - 1]; } | |
| const_reference back() const { return data()[m_size - 1]; } | |
| // Iterators | |
| iterator begin() { return data(); } | |
| const_iterator begin() const { return data(); } | |
| const_iterator cbegin() const { return data(); } | |
| iterator end() { return data() + m_size; } | |
| const_iterator end() const { return data() + m_size; } | |
| const_iterator cend() const { return data() + m_size; } | |
| // Capacity | |
| bool empty() const { return m_size == 0; } | |
| size_type size() const { return m_size; } | |
| size_type capacity() const { return m_capacity; } | |
| bool is_small() const { return m_is_small; } | |
| void reserve(size_type new_capacity) | |
| { | |
| if (new_capacity > m_capacity) { | |
| reallocate(new_capacity); | |
| } | |
| } | |
| void shrink_to_fit() | |
| { | |
| if (!m_is_small && m_size <= SmallSize) { | |
| T* old_data = m_large_data; | |
| for (size_type i = 0; i < m_size; ++i) { | |
| new (small_data() + i) T(std::move(old_data[i])); | |
| old_data[i].~T(); | |
| } | |
| operator delete(old_data); | |
| m_is_small = true; | |
| m_capacity = SmallSize; | |
| } else if (!m_is_small && m_size < m_capacity) { | |
| reallocate(m_size); | |
| } | |
| } | |
| // Modifiers | |
| void clear() | |
| { | |
| T* ptr = data(); | |
| for (size_type i = 0; i < m_size; ++i) { | |
| ptr[i].~T(); | |
| } | |
| m_size = 0; | |
| } | |
| void push_back(const T& value) | |
| { | |
| if (m_size >= m_capacity) { | |
| size_type new_capacity = static_cast<size_type>(m_capacity * growth_factor); | |
| reallocate(new_capacity); | |
| } | |
| new (data() + m_size) T(value); | |
| ++m_size; | |
| } | |
| void push_back(T&& value) | |
| { | |
| if (m_size >= m_capacity) { | |
| size_type new_capacity = static_cast<size_type>(m_capacity * growth_factor); | |
| reallocate(new_capacity); | |
| } | |
| new (data() + m_size) T(std::move(value)); | |
| ++m_size; | |
| } | |
| template<typename... Args> | |
| reference emplace_back(Args&&... args) | |
| { | |
| if (m_size >= m_capacity) { | |
| size_type new_capacity = static_cast<size_type>(m_capacity * growth_factor); | |
| reallocate(new_capacity); | |
| } | |
| T* ptr = new (data() + m_size) T(std::forward<Args>(args)...); | |
| ++m_size; | |
| return *ptr; | |
| } | |
| void pop_back() | |
| { | |
| if (m_size > 0) { | |
| --m_size; | |
| data()[m_size].~T(); | |
| } | |
| } | |
| void resize(size_type count, const T& value = T{}) | |
| { | |
| if (count > m_capacity) { | |
| reserve(count); | |
| } | |
| if (count > m_size) { | |
| for (size_type i = m_size; i < count; ++i) { | |
| new (data() + i) T(value); | |
| } | |
| } else if (count < m_size) { | |
| for (size_type i = count; i < m_size; ++i) { | |
| data()[i].~T(); | |
| } | |
| } | |
| m_size = count; | |
| } | |
| // Data access | |
| pointer data() | |
| { | |
| return m_is_small ? small_data() : m_large_data; | |
| } | |
| const_pointer data() const | |
| { | |
| return m_is_small ? small_data() : m_large_data; | |
| } | |
| private: | |
| T* small_data() | |
| { | |
| return reinterpret_cast<T*>(m_small_data); | |
| } | |
| const T* small_data() const | |
| { | |
| return reinterpret_cast<const T*>(m_small_data); | |
| } | |
| }; | |
| } // namespace Razix |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment