// SPDX-FileCopyrightText: 2002-2024 PCSX2 Dev Team // SPDX-License-Identifier: GPL-3.0+ #pragma once #include "common/Assertions.h" #include #include #include #include #include template class FixedHeapArray { public: using value_type = T; using size_type = std::size_t; using difference_type = std::ptrdiff_t; using reference = T&; using const_reference = const T&; using pointer = T*; using const_pointer = const T*; using this_type = FixedHeapArray; FixedHeapArray() { allocate(); } FixedHeapArray(const this_type& copy) { allocate(); std::copy(copy.cbegin(), copy.cend(), begin()); } FixedHeapArray(this_type&& move) { m_data = move.m_data; move.m_data = nullptr; } ~FixedHeapArray() { deallocate(); } size_type size() const { return SIZE; } size_type capacity() const { return SIZE; } bool empty() const { return false; } pointer begin() { return m_data; } pointer end() { return m_data + SIZE; } const_pointer data() const { return m_data; } pointer data() { return m_data; } const_pointer cbegin() const { return m_data; } const_pointer cend() const { return m_data + SIZE; } const_reference operator[](size_type index) const { assert(index < SIZE); return m_data[index]; } reference operator[](size_type index) { assert(index < SIZE); return m_data[index]; } const_reference front() const { return m_data[0]; } const_reference back() const { return m_data[SIZE - 1]; } reference front() { return m_data[0]; } reference back() { return m_data[SIZE - 1]; } void fill(const_reference value) { std::fill(begin(), end(), value); } void swap(this_type& move) { std::swap(m_data, move.m_data); } this_type& operator=(const this_type& rhs) { std::copy(begin(), end(), rhs.cbegin()); return *this; } this_type& operator=(this_type&& move) { deallocate(); m_data = move.m_data; move.m_data = nullptr; return *this; } #define RELATIONAL_OPERATOR(op) \ bool operator op(const this_type& rhs) const \ { \ for (size_type i = 0; i < SIZE; i++) \ { \ if (!(m_data[i] op rhs.m_data[i])) \ return false; \ } \ } RELATIONAL_OPERATOR(==); RELATIONAL_OPERATOR(!=); RELATIONAL_OPERATOR(<); RELATIONAL_OPERATOR(<=); RELATIONAL_OPERATOR(>); RELATIONAL_OPERATOR(>=); #undef RELATIONAL_OPERATOR private: void allocate() { if constexpr (ALIGNMENT > 0) { #ifdef _MSC_VER m_data = static_cast(_aligned_malloc(SIZE * sizeof(T), ALIGNMENT)); if (!m_data) pxFailRel("Memory allocation failed."); #else if (posix_memalign(reinterpret_cast(&m_data), ALIGNMENT, SIZE * sizeof(T)) != 0) pxFailRel("Memory allocation failed."); #endif } else { m_data = static_cast(std::malloc(SIZE * sizeof(T))); if (!m_data) pxFailRel("Memory allocation failed."); } } void deallocate() { if constexpr (ALIGNMENT > 0) { #ifdef _MSC_VER _aligned_free(m_data); #else std::free(m_data); #endif } else { std::free(m_data); } } T* m_data; }; template class DynamicHeapArray { static_assert(std::is_trivially_copyable_v, "T is trivially copyable"); static_assert(std::is_standard_layout_v, "T is standard layout"); public: using value_type = T; using size_type = std::size_t; using difference_type = std::ptrdiff_t; using reference = T&; using const_reference = const T&; using pointer = T*; using const_pointer = const T*; using this_type = DynamicHeapArray; DynamicHeapArray() : m_data(nullptr) , m_size(0) { } DynamicHeapArray(size_t size) { internal_resize(size, nullptr, 0); } DynamicHeapArray(const T* begin, const T* end) { const size_t size = reinterpret_cast(end) - reinterpret_cast(begin); if (size > 0) { internal_resize(size / sizeof(T), nullptr, 0); std::memcpy(m_data, begin, size); } else { m_data = nullptr; m_size = 0; } } DynamicHeapArray(const T* begin, size_t count) { if (count > 0) { internal_resize(count, nullptr, 0); std::memcpy(m_data, begin, sizeof(T) * count); } else { m_data = nullptr; m_size = 0; } } DynamicHeapArray(const this_type& copy) { if (copy.m_size > 0) { internal_resize(copy.m_size, nullptr, 0); std::memcpy(m_data, copy.m_data, sizeof(T) * copy.m_size); } else { m_data = nullptr; m_size = 0; } } DynamicHeapArray(this_type&& move) { m_data = move.m_data; m_size = move.m_size; move.m_data = nullptr; move.m_size = 0; } ~DynamicHeapArray() { internal_deallocate(); } size_type size() const { return m_size; } size_type capacity() const { return m_size; } bool empty() const { return (m_size == 0); } pointer begin() { return m_data; } pointer end() { return m_data + m_size; } const_pointer data() const { return m_data; } pointer data() { return m_data; } const_pointer cbegin() const { return m_data; } const_pointer cend() const { return m_data + m_size; } const_reference operator[](size_type index) const { assert(index < m_size); return m_data[index]; } reference operator[](size_type index) { assert(index < m_size); return m_data[index]; } const_reference front() const { return m_data[0]; } const_reference back() const { return m_data[m_size - 1]; } reference front() { return m_data[0]; } reference back() { return m_data[m_size - 1]; } void fill(const_reference value) { std::fill(begin(), end(), value); } void swap(this_type& move) { std::swap(m_data, move.m_data); std::swap(m_size, move.m_size); } void resize(size_t new_size) { internal_resize(new_size, m_data, m_size); } void deallocate() { internal_deallocate(); m_data = nullptr; m_size = 0; } void assign(const T* begin, const T* end) { const size_t size = reinterpret_cast(end) - reinterpret_cast(begin); const size_t count = size / sizeof(T); if (count > 0) { if (m_size != count) { internal_deallocate(); internal_resize(count, nullptr, 0); } std::memcpy(m_data, begin, size); } else { internal_deallocate(); m_data = nullptr; m_size = 0; } } void assign(const T* begin, size_t count) { if (count > 0) { if (m_size != count) { internal_deallocate(); internal_resize(count, nullptr, 0); } std::memcpy(m_data, begin, sizeof(T) * count); } else { internal_deallocate(); m_data = nullptr; m_size = 0; } } void assign(const this_type& copy) { assign(copy.m_data, copy.m_size); } void assign(this_type&& move) { internal_deallocate(); m_data = move.m_data; m_size = move.m_size; move.m_data = nullptr; move.m_size = 0; } this_type& operator=(const this_type& rhs) { assign(rhs); return *this; } this_type& operator=(this_type&& move) { assign(std::move(move)); return *this; } #define RELATIONAL_OPERATOR(op, size_op) \ bool operator op(const this_type& rhs) const \ { \ if (m_size != rhs.m_size) \ return m_size size_op rhs.m_size; \ for (size_type i = 0; i < m_size; i++) \ { \ if (!(m_data[i] op rhs.m_data[i])) \ return false; \ } \ } RELATIONAL_OPERATOR(==, !=); RELATIONAL_OPERATOR(!=, ==); RELATIONAL_OPERATOR(<, <); RELATIONAL_OPERATOR(<=, <=); RELATIONAL_OPERATOR(>, >); RELATIONAL_OPERATOR(>=, >=); #undef RELATIONAL_OPERATOR private: void internal_resize(size_t size, T* prev_ptr, size_t prev_size) { if constexpr (alignment > 0) { #ifdef _MSC_VER m_data = static_cast(_aligned_realloc(prev_ptr, size * sizeof(T), alignment)); if (!m_data) pxFailRel("Memory allocation failed."); #else if (posix_memalign(reinterpret_cast(&m_data), alignment, size * sizeof(T)) != 0) pxFailRel("Memory allocation failed."); if (prev_ptr) { std::memcpy(m_data, prev_ptr, std::min(size, prev_size) * sizeof(T)); std::free(prev_ptr); } #endif } else { m_data = static_cast(std::realloc(prev_ptr, size * sizeof(T))); if (!m_data) pxFailRel("Memory allocation failed."); } m_size = size; } void internal_deallocate() { if constexpr (alignment > 0) { #ifdef _MSC_VER _aligned_free(m_data); #else std::free(m_data); #endif } else { std::free(m_data); } } T* m_data; size_t m_size; };