mirror of https://github.com/PCSX2/pcsx2.git
278 lines
8.0 KiB
C
278 lines
8.0 KiB
C
|
/*
|
||
|
* Copyright (C) 2017-2017 Alessandro Vetere
|
||
|
* Copyright (C) 2007-2009 Gabest
|
||
|
* http://www.gabest.org
|
||
|
*
|
||
|
* This Program is free software; you can redistribute it and/or modify
|
||
|
* it under the terms of the GNU General Public License as published by
|
||
|
* the Free Software Foundation; either version 2, or (at your option)
|
||
|
* any later version.
|
||
|
*
|
||
|
* This Program is distributed in the hope that it will be useful,
|
||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||
|
* GNU General Public License for more details.
|
||
|
*
|
||
|
* You should have received a copy of the GNU General Public License
|
||
|
* along with GNU Make; see the file COPYING. If not, write to
|
||
|
* the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA USA.
|
||
|
* http://www.gnu.org/copyleft/gpl.html
|
||
|
*
|
||
|
*/
|
||
|
|
||
|
#pragma once
|
||
|
|
||
|
template <class T>
|
||
|
struct Element {
|
||
|
T data;
|
||
|
uint16 next_index;
|
||
|
uint16 prev_index;
|
||
|
};
|
||
|
|
||
|
template <class T>
|
||
|
class FastListIterator;
|
||
|
|
||
|
template <class T>
|
||
|
class FastList {
|
||
|
friend class FastListIterator<T>;
|
||
|
private:
|
||
|
// The index of the first element of the list is m_buffer[0].next_index
|
||
|
// The first Element<T> of the list has prev_index equal to 0
|
||
|
// The index of the last element of the list is m_buffer[0].prev_index
|
||
|
// The last Element<T> of the list has next_index equal to 0
|
||
|
// All the other Element<T> of the list are chained by next_index and prev_index
|
||
|
// m_buffer has dynamic size m_capacity
|
||
|
// Due to m_buffer reallocation, the pointers to Element<T> stored into the array
|
||
|
// are invalidated every time Grow() is executed. But FastListIterator<T> is
|
||
|
// index based, not pointer based, and the elements are copied in order on Grow(),
|
||
|
// so there is no iterator invalidation (which is an index invalidation) until
|
||
|
// the relevant iterator (or the index alone) are erased from the list.
|
||
|
// m_buffer[0] is always present as auxiliary Element<T> of the list
|
||
|
Element<T>* m_buffer;
|
||
|
uint16 m_capacity;
|
||
|
uint16 m_free_indexes_stack_top;
|
||
|
// m_free_indexes_stack has dynamic size (m_capacity - 1)
|
||
|
// m_buffer indexes that are free to be used are stacked here
|
||
|
uint16* m_free_indexes_stack;
|
||
|
|
||
|
public:
|
||
|
__forceinline FastList() {
|
||
|
m_buffer = nullptr;
|
||
|
clear();
|
||
|
}
|
||
|
|
||
|
__forceinline ~FastList() {
|
||
|
_aligned_free(m_buffer);
|
||
|
}
|
||
|
|
||
|
void clear() {
|
||
|
// Initialize m_capacity to 4 so we avoid to Grow() on initial insertions
|
||
|
// The code doesn't break if this value is changed with anything from 1 to USHRT_MAX
|
||
|
m_capacity = 4;
|
||
|
|
||
|
// Initialize m_buffer and m_free_indexes_stack as a contiguous block of memory starting at m_buffer
|
||
|
// This should increase cache locality and reduce memory fragmentation
|
||
|
_aligned_free(m_buffer);
|
||
|
m_buffer = (Element<T>*)_aligned_malloc(m_capacity * sizeof(Element<T>) + (m_capacity - 1) * sizeof(uint16), 64);
|
||
|
m_free_indexes_stack = (uint16*)&m_buffer[m_capacity];
|
||
|
|
||
|
// Initialize m_buffer[0], data field is unused but initialized using default T constructor
|
||
|
m_buffer[0] = { T(), 0, 0 };
|
||
|
|
||
|
// m_free_indexes_stack top index is 0, bottom index is m_capacity - 2
|
||
|
m_free_indexes_stack_top = 0;
|
||
|
|
||
|
// m_buffer index 0 is reserved for auxiliary element
|
||
|
for (uint16 i = 0; i < m_capacity - 1; i++) {
|
||
|
m_free_indexes_stack[i] = i + 1;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Insert the element in front of the list and return its position in m_buffer
|
||
|
__forceinline uint16 InsertFront(const T& data) {
|
||
|
if (Full()) {
|
||
|
Grow();
|
||
|
}
|
||
|
|
||
|
// Pop a free index from the stack
|
||
|
const uint16 free_index = m_free_indexes_stack[m_free_indexes_stack_top++];
|
||
|
m_buffer[free_index].data = data;
|
||
|
ListInsertFront(free_index);
|
||
|
return free_index;
|
||
|
}
|
||
|
|
||
|
__forceinline void push_front(const T& data) {
|
||
|
InsertFront(data);
|
||
|
}
|
||
|
|
||
|
__forceinline const T& back() const {
|
||
|
return m_buffer[LastIndex()].data;
|
||
|
}
|
||
|
|
||
|
__forceinline void pop_back() {
|
||
|
EraseIndex(LastIndex());
|
||
|
}
|
||
|
|
||
|
__forceinline uint16 size() const {
|
||
|
return m_free_indexes_stack_top;
|
||
|
}
|
||
|
|
||
|
__forceinline bool empty() const {
|
||
|
return size() == 0;
|
||
|
}
|
||
|
|
||
|
__forceinline void EraseIndex(const uint16 index) {
|
||
|
ListRemove(index);
|
||
|
m_free_indexes_stack[--m_free_indexes_stack_top] = index;
|
||
|
}
|
||
|
|
||
|
__forceinline void MoveFront(const uint16 index) {
|
||
|
if (FirstIndex() != index) {
|
||
|
ListRemove(index);
|
||
|
ListInsertFront(index);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
__forceinline const FastListIterator<T> begin() const {
|
||
|
return FastListIterator<T>(this, FirstIndex());
|
||
|
}
|
||
|
|
||
|
__forceinline const FastListIterator<T> end() const {
|
||
|
return FastListIterator<T>(this, 0);
|
||
|
}
|
||
|
|
||
|
__forceinline FastListIterator<T> erase(FastListIterator<T> i) {
|
||
|
EraseIndex(i.Index());
|
||
|
return ++i;
|
||
|
}
|
||
|
|
||
|
private:
|
||
|
// Accessed by FastListIterator<T> using class friendship
|
||
|
__forceinline const T& Data(const uint16 index) const {
|
||
|
return m_buffer[index].data;
|
||
|
}
|
||
|
|
||
|
// Accessed by FastListIterator<T> using class friendship
|
||
|
__forceinline uint16 NextIndex(const uint16 index) const {
|
||
|
return m_buffer[index].next_index;
|
||
|
}
|
||
|
|
||
|
// Accessed by FastListIterator<T> using class friendship
|
||
|
__forceinline uint16 PrevIndex(const uint16 index) const {
|
||
|
return m_buffer[index].prev_index;
|
||
|
}
|
||
|
|
||
|
__forceinline uint16 FirstIndex() const {
|
||
|
return m_buffer[0].next_index;
|
||
|
}
|
||
|
|
||
|
__forceinline uint16 LastIndex() const {
|
||
|
return m_buffer[0].prev_index;
|
||
|
}
|
||
|
|
||
|
__forceinline bool Full() const {
|
||
|
// The minus one is due to the presence of the auxiliary element
|
||
|
return size() == m_capacity - 1;
|
||
|
}
|
||
|
|
||
|
__forceinline void ListInsertFront(const uint16 index) {
|
||
|
// Update prev / next indexes to add m_buffer[index] to the chain
|
||
|
Element<T>& head = m_buffer[0];
|
||
|
m_buffer[index].prev_index = 0;
|
||
|
m_buffer[index].next_index = head.next_index;
|
||
|
m_buffer[head.next_index].prev_index = index;
|
||
|
head.next_index = index;
|
||
|
}
|
||
|
|
||
|
__forceinline void ListRemove(const uint16 index) {
|
||
|
// Update prev / next indexes to remove m_buffer[index] from the chain
|
||
|
const Element<T>& to_remove = m_buffer[index];
|
||
|
m_buffer[to_remove.prev_index].next_index = to_remove.next_index;
|
||
|
m_buffer[to_remove.next_index].prev_index = to_remove.prev_index;
|
||
|
}
|
||
|
|
||
|
void Grow() {
|
||
|
if (m_capacity == USHRT_MAX) {
|
||
|
throw std::runtime_error("FastList size maxed out at USHRT_MAX (65535) elements, cannot grow futhermore.");
|
||
|
}
|
||
|
|
||
|
const uint16 new_capacity = m_capacity <= (USHRT_MAX / 2) ? (m_capacity * 2) : USHRT_MAX;
|
||
|
|
||
|
Element<T>* new_buffer = (Element<T>*)_aligned_malloc(new_capacity * sizeof(Element<T>) + (new_capacity - 1) * sizeof(uint16), 64);
|
||
|
uint16* new_free_indexes_stack = (uint16*)&new_buffer[new_capacity];
|
||
|
|
||
|
memcpy(new_buffer, m_buffer, m_capacity * sizeof(Element<T>));
|
||
|
memcpy(new_free_indexes_stack, m_free_indexes_stack, (m_capacity - 1) * sizeof(uint16));
|
||
|
|
||
|
_aligned_free(m_buffer);
|
||
|
|
||
|
m_buffer = new_buffer;
|
||
|
m_free_indexes_stack = new_free_indexes_stack;
|
||
|
|
||
|
// Initialize the additional space in the stack
|
||
|
for (uint16 i = m_capacity - 1; i < new_capacity - 1; i++) {
|
||
|
m_free_indexes_stack[i] = i + 1;
|
||
|
}
|
||
|
|
||
|
m_capacity = new_capacity;
|
||
|
}
|
||
|
};
|
||
|
|
||
|
|
||
|
template <class T>
|
||
|
// This iterator is const_iterator
|
||
|
class FastListIterator
|
||
|
{
|
||
|
private:
|
||
|
const FastList<T>* m_fastlist;
|
||
|
uint16 m_index;
|
||
|
|
||
|
public:
|
||
|
__forceinline FastListIterator(const FastList<T>* fastlist, const uint16 index) {
|
||
|
m_fastlist = fastlist;
|
||
|
m_index = index;
|
||
|
}
|
||
|
|
||
|
__forceinline bool operator!=(const FastListIterator<T>& other) const {
|
||
|
return (m_index != other.m_index);
|
||
|
}
|
||
|
|
||
|
__forceinline bool operator==(const FastListIterator<T>& other) const {
|
||
|
return (m_index == other.m_index);
|
||
|
}
|
||
|
|
||
|
// Prefix increment
|
||
|
__forceinline const FastListIterator<T>& operator++() {
|
||
|
m_index = m_fastlist->NextIndex(m_index);
|
||
|
return *this;
|
||
|
}
|
||
|
|
||
|
// Postfix increment
|
||
|
__forceinline const FastListIterator<T> operator++(int) {
|
||
|
FastListIterator<T> copy(*this);
|
||
|
++(*this);
|
||
|
return copy;
|
||
|
}
|
||
|
|
||
|
// Prefix decrement
|
||
|
__forceinline const FastListIterator<T>& operator--() {
|
||
|
m_index = m_fastlist->PrevIndex(m_index);
|
||
|
return *this;
|
||
|
}
|
||
|
|
||
|
// Postfix decrement
|
||
|
__forceinline const FastListIterator<T> operator--(int) {
|
||
|
FastListIterator<T> copy(*this);
|
||
|
--(*this);
|
||
|
return copy;
|
||
|
}
|
||
|
|
||
|
__forceinline const T& operator*() const {
|
||
|
return m_fastlist->Data(m_index);
|
||
|
}
|
||
|
|
||
|
__forceinline uint16 Index() const {
|
||
|
return m_index;
|
||
|
}
|
||
|
};
|