rpcs3/rpcs3/Emu/RSX/Common/simple_array.hpp
2025-04-27 15:07:26 +03:00

472 lines
7.8 KiB
C++

#pragma once
#include <util/types.hpp>
#include <functional>
#include <algorithm>
namespace rsx
{
template <typename Ty>
requires std::is_trivially_destructible_v<Ty>
struct simple_array
{
public:
using iterator = Ty*;
using const_iterator = const Ty*;
using value_type = Ty;
private:
static constexpr u32 _local_capacity = std::max<u32>(64u / sizeof(Ty), 1u);
char _local_storage[_local_capacity * sizeof(Ty)];
u32 _capacity = _local_capacity;
Ty* _data = _local_capacity ? reinterpret_cast<Ty*>(_local_storage) : nullptr;
u32 _size = 0;
inline u64 offset(const_iterator pos)
{
return (_data) ? u64(pos - _data) : 0ull;
}
bool is_local_storage() const
{
return _data == reinterpret_cast<const Ty*>(_local_storage);
}
public:
simple_array() = default;
simple_array(u32 initial_size)
{
reserve(initial_size);
_size = initial_size;
}
simple_array(u32 initial_size, const Ty val)
{
reserve(initial_size);
_size = initial_size;
for (u32 n = 0; n < initial_size; ++n)
{
_data[n] = val;
}
}
simple_array(const std::initializer_list<Ty>& args)
{
reserve(::size32(args));
for (const auto& arg : args)
{
push_back(arg);
}
}
simple_array(const simple_array& other)
{
resize(other._size);
if (_size)
{
std::memcpy(_data, other._data, size_bytes());
}
}
simple_array(simple_array&& other) noexcept
{
swap(other);
}
simple_array& operator=(const simple_array& other)
{
if (&other != this)
{
resize(other._size);
if (_size)
{
std::memcpy(_data, other._data, size_bytes());
}
}
return *this;
}
simple_array& operator=(simple_array&& other) noexcept
{
swap(other);
return *this;
}
~simple_array()
{
if (_data)
{
if (!is_local_storage())
{
free(_data);
}
_data = nullptr;
_size = _capacity = 0;
}
}
void swap(simple_array<Ty>& that) noexcept
{
if (!_size && !that._size)
{
// NOP. Surprisingly common
return;
}
const auto _this_is_local = is_local_storage();
const auto _that_is_local = that.is_local_storage();
if (!_this_is_local && !_that_is_local)
{
std::swap(_capacity, that._capacity);
std::swap(_size, that._size);
std::swap(_data, that._data);
return;
}
if (!_size)
{
*this = that;
that.clear();
return;
}
if (!that._size)
{
that = *this;
clear();
return;
}
if (_this_is_local != _that_is_local)
{
// Mismatched usage of the stack storage.
rsx::simple_array<Ty> tmp{ *this };
*this = that;
that = tmp;
return;
}
// Use memcpy to allow compiler optimizations
Ty _stack_alloc[_local_capacity];
std::memcpy(_stack_alloc, that._data, that.size_bytes());
std::memcpy(that._data, _data, size_bytes());
std::memcpy(_data, _stack_alloc, that.size_bytes());
std::swap(_size, that._size);
}
void reserve(u32 size)
{
if (_capacity >= size)
{
return;
}
if (is_local_storage())
{
// Switch to heap storage
_data = static_cast<Ty*>(std::malloc(sizeof(Ty) * size));
std::memcpy(static_cast<void*>(_data), _local_storage, size_bytes());
}
else
{
// Extend heap storage
ensure(_data = static_cast<Ty*>(std::realloc(_data, sizeof(Ty) * size))); // "realloc() failed!"
}
_capacity = size;
}
template <typename T> requires UnsignedInt<T>
void resize(T size)
{
const auto new_size = static_cast<u32>(size);
reserve(new_size);
_size = new_size;
}
void push_back(const Ty& val)
{
if (_size >= _capacity)
{
reserve(_capacity + 16);
}
_data[_size++] = val;
}
void push_back(Ty&& val)
{
if (_size >= _capacity)
{
reserve(_capacity + 16);
}
_data[_size++] = val;
}
template <typename... Args>
void emplace_back(Args&&... args)
{
if (_size >= _capacity)
{
reserve(_capacity + 16);
}
std::construct_at(&_data[_size++], std::forward<Args&&>(args)...);
}
Ty pop_back()
{
return _data[--_size];
}
iterator insert(iterator pos, const Ty& val)
{
ensure(pos >= _data);
const auto _loc = offset(pos);
if (_size >= _capacity)
{
reserve(_capacity + 16);
pos = _data + _loc;
}
if (_loc >= _size)
{
_data[_size++] = val;
return pos;
}
AUDIT(_loc < _size);
const auto remaining = (_size - _loc);
memmove(pos + 1, pos, remaining * sizeof(Ty));
*pos = val;
_size++;
return pos;
}
iterator insert(iterator pos, Ty&& val)
{
ensure(pos >= _data);
const auto _loc = offset(pos);
if (_size >= _capacity)
{
reserve(_capacity + 16);
pos = _data + _loc;
}
if (_loc >= _size)
{
_data[_size++] = val;
return pos;
}
AUDIT(_loc < _size);
const u32 remaining = (_size - _loc);
memmove(pos + 1, pos, remaining * sizeof(Ty));
*pos = val;
_size++;
return pos;
}
void clear()
{
_size = 0;
}
bool empty() const
{
return _size == 0;
}
u32 size() const
{
return _size;
}
u64 size_bytes() const
{
return _size * sizeof(Ty);
}
u32 capacity() const
{
return _capacity;
}
Ty& operator[] (u32 index)
{
return _data[index];
}
const Ty& operator[] (u32 index) const
{
return _data[index];
}
Ty* data()
{
return _data;
}
const Ty* data() const
{
return _data;
}
Ty& back()
{
return _data[_size - 1];
}
const Ty& back() const
{
return _data[_size - 1];
}
Ty& front()
{
return _data[0];
}
const Ty& front() const
{
return _data[0];
}
iterator begin()
{
return _data;
}
iterator end()
{
return _data ? _data + _size : nullptr;
}
const_iterator begin() const
{
return _data;
}
const_iterator end() const
{
return _data ? _data + _size : nullptr;
}
bool any(std::predicate<const Ty&> auto predicate) const
{
for (auto it = begin(); it != end(); ++it)
{
if (std::invoke(predicate, *it))
{
return true;
}
}
return false;
}
bool erase_if(std::predicate<const Ty&> auto predicate)
{
if (!_size)
{
return false;
}
bool ret = false;
for (auto ptr = _data, last = _data + _size - 1; ptr <= last;)
{
if (predicate(*ptr))
{
ret = true;
if (ptr == last)
{
// Popping the last entry from list. Just set the new size and exit
_size--;
break;
}
// Move item to the end of the list and shrink by 1
std::memcpy(ptr, last, sizeof(Ty));
_size--;
last--;
// Retest the same ptr which now has the previous tail item
continue;
}
ptr++;
}
return ret;
}
simple_array<Ty>& sort(std::predicate<const Ty&, const Ty&> auto predicate)
{
if (_size < 2)
{
return *this;
}
std::sort(begin(), end(), predicate);
return *this;
}
template <typename F, typename U = std::invoke_result_t<F, const Ty&>>
requires (std::is_invocable_v<F, const Ty&> && std::is_trivially_destructible_v<U>)
simple_array<U> map(F&& xform) const
{
simple_array<U> result;
result.reserve(size());
for (auto it = begin(); it != end(); ++it)
{
result.push_back(xform(*it));
}
return result;
}
template <typename F, typename U = std::invoke_result_t<F, const Ty&>>
requires (std::is_invocable_v<F, const Ty&> && !std::is_trivially_destructible_v<U>)
std::vector<U> map(F&& xform) const
{
std::vector<U> result;
result.reserve(size());
for (auto it = begin(); it != end(); ++it)
{
result.push_back(xform(*it));
}
return result;
}
template <typename F, typename U>
requires std::is_invocable_r_v<U, F, const U&, const Ty&>
U reduce(U initial_value, F&& reducer) const
{
U accumulate = initial_value;
for (auto it = begin(); it != end(); ++it)
{
accumulate = reducer(accumulate, *it);
}
return accumulate;
}
};
}