pcsx2/common/HeapArray.h

402 lines
8.2 KiB
C++

// SPDX-FileCopyrightText: 2002-2024 PCSX2 Dev Team
// SPDX-License-Identifier: GPL-3.0+
#pragma once
#include "common/Assertions.h"
#include <algorithm>
#include <cassert>
#include <cstdlib>
#include <cstring>
#include <type_traits>
template <typename T, std::size_t SIZE, std::size_t ALIGNMENT = 0>
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<T, SIZE>;
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<T*>(_aligned_malloc(SIZE * sizeof(T), ALIGNMENT));
if (!m_data)
pxFailRel("Memory allocation failed.");
#else
if (posix_memalign(reinterpret_cast<void**>(&m_data), ALIGNMENT, SIZE * sizeof(T)) != 0)
pxFailRel("Memory allocation failed.");
#endif
}
else
{
m_data = static_cast<T*>(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 <typename T, size_t alignment = 0>
class DynamicHeapArray
{
static_assert(std::is_trivially_copyable_v<T>, "T is trivially copyable");
static_assert(std::is_standard_layout_v<T>, "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<T>;
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<const char*>(end) - reinterpret_cast<const char*>(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<const char*>(end) - reinterpret_cast<const char*>(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<T*>(_aligned_realloc(prev_ptr, size * sizeof(T), alignment));
if (!m_data)
pxFailRel("Memory allocation failed.");
#else
if (posix_memalign(reinterpret_cast<void**>(&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<T*>(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;
};