/* * 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 struct Element { T data; uint16 next_index; uint16 prev_index; }; template class FastListIterator; template class FastList { friend class FastListIterator; private: // The index of the first element of the list is m_buffer[0].next_index // The first Element 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 of the list has next_index equal to 0 // All the other Element 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 stored into the array // are invalidated every time Grow() is executed. But FastListIterator 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 of the list Element* 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*)_aligned_malloc(m_capacity * sizeof(Element) + (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 begin() const { return FastListIterator(this, FirstIndex()); } __forceinline const FastListIterator end() const { return FastListIterator(this, 0); } __forceinline FastListIterator erase(FastListIterator i) { EraseIndex(i.Index()); return ++i; } private: // Accessed by FastListIterator using class friendship __forceinline const T& Data(const uint16 index) const { return m_buffer[index].data; } // Accessed by FastListIterator using class friendship __forceinline uint16 NextIndex(const uint16 index) const { return m_buffer[index].next_index; } // Accessed by FastListIterator 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& 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& 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* new_buffer = (Element*)_aligned_malloc(new_capacity * sizeof(Element) + (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)); 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 // This iterator is const_iterator class FastListIterator { private: const FastList* m_fastlist; uint16 m_index; public: __forceinline FastListIterator(const FastList* fastlist, const uint16 index) { m_fastlist = fastlist; m_index = index; } __forceinline bool operator!=(const FastListIterator& other) const { return (m_index != other.m_index); } __forceinline bool operator==(const FastListIterator& other) const { return (m_index == other.m_index); } // Prefix increment __forceinline const FastListIterator& operator++() { m_index = m_fastlist->NextIndex(m_index); return *this; } // Postfix increment __forceinline const FastListIterator operator++(int) { FastListIterator copy(*this); ++(*this); return copy; } // Prefix decrement __forceinline const FastListIterator& operator--() { m_index = m_fastlist->PrevIndex(m_index); return *this; } // Postfix decrement __forceinline const FastListIterator operator--(int) { FastListIterator copy(*this); --(*this); return copy; } __forceinline const T& operator*() const { return m_fastlist->Data(m_index); } __forceinline uint16 Index() const { return m_index; } };