#pragma once
|
|
|
|
#include "gp_config.hpp"
|
|
|
|
#include "gp/algorithms/move.hpp"
|
|
#include "gp/containers/array.hpp"
|
|
#include "gp/utils/iterator.hpp"
|
|
|
|
#include <stddef.h>
|
|
|
|
namespace gp{
|
|
/**
|
|
* @brief An array that has static indexes that are not affected by deletions or new additions
|
|
*
|
|
* @tparam T The stored type
|
|
* @tparam _capacity The total capacity of the array
|
|
*/
|
|
template<typename T, size_t _capacity>
|
|
class indexed_array{
|
|
size_t data_top = 0;
|
|
size_t available_indexes_top = 0;
|
|
size_t remove_top = 0;
|
|
|
|
gp::array<char, sizeof(T)*_capacity> data_table;
|
|
size_t available_indexes[_capacity];
|
|
size_t translation_table[_capacity];
|
|
size_t reverse_translation_table[_capacity];
|
|
size_t remove_table[_capacity];
|
|
public:
|
|
indexed_array() {}
|
|
|
|
template<typename U>
|
|
size_t push(U&& value) {
|
|
size_t index;
|
|
|
|
gp_config::assertion(data_top+1 != _capacity, "Indexed array capacity exceeded");
|
|
|
|
if(available_indexes_top) {
|
|
available_indexes_top--;
|
|
index = available_indexes[available_indexes_top];
|
|
} else {
|
|
index = data_top;
|
|
}
|
|
|
|
new(&(data_table.as_buffer().template cast<T>()[data_top])) T(gp::forward<U>(value));
|
|
translation_table[index] = data_top;
|
|
reverse_translation_table[data_top] = index;
|
|
|
|
++data_top;
|
|
|
|
return index;
|
|
}
|
|
|
|
void pop(size_t idx) {
|
|
size_t v_idx = translation_table[idx];
|
|
|
|
available_indexes[available_indexes_top] = idx;
|
|
++available_indexes_top;
|
|
|
|
translation_table[idx] = -1;
|
|
--data_top;
|
|
if(v_idx < data_top) {
|
|
size_t u_idx = reverse_translation_table[data_top];
|
|
data_table.as_buffer().template cast<T>()[v_idx] = gp::move(reinterpret_cast<T&>(data_table[data_top]));
|
|
::operator delete(&data_table.as_buffer().template cast<T>()[data_top], &(data_table.as_buffer().template cast<T>()[data_top]));
|
|
data_table.as_buffer().template cast<T>()[data_top].~T();
|
|
translation_table[u_idx] = v_idx;
|
|
reverse_translation_table[v_idx] = u_idx;
|
|
}
|
|
}
|
|
|
|
void reset() {
|
|
auto it = data_table;
|
|
auto end = data_table+data_top;
|
|
while(it != end) {
|
|
::operator delete(it, it);
|
|
++it;
|
|
}
|
|
data_top = 0;
|
|
available_indexes_top = 0;
|
|
remove_top = 0;
|
|
}
|
|
|
|
void mark_internal_for_removal(size_t i_idx) {
|
|
remove_table[remove_top] = reverse_translation_table[i_idx];
|
|
++remove_top;
|
|
}
|
|
|
|
void mark_for_removal(size_t idx) {
|
|
remove_table[remove_top] = idx;
|
|
++remove_top;
|
|
}
|
|
|
|
void sweep_removed() {
|
|
auto it = remove_table;
|
|
auto end = remove_table+remove_top;
|
|
while(it != end) {
|
|
pop(*it);
|
|
++it;
|
|
}
|
|
}
|
|
|
|
bool has(size_t idx) {
|
|
if(idx > data_top) return false;
|
|
if(translation_table[idx] == -1) return false;
|
|
return true;
|
|
}
|
|
|
|
template<typename fn_t>
|
|
void foreach(fn_t function) {
|
|
for(size_t i = 0; i < data_top; ++i) {
|
|
function(reverse_translation_table[i], operator[](reverse_translation_table[i]));
|
|
}
|
|
}
|
|
|
|
pointer_iterator<T> begin()
|
|
{
|
|
return data_table.as_buffer().template cast<T>().begin();
|
|
}
|
|
|
|
pointer_iterator<T> end()
|
|
{
|
|
return data_table.as_buffer().template cast<T>().begin()+data_top;
|
|
}
|
|
|
|
const_pointer_iterator<T> cbegin()
|
|
{
|
|
return data_table.as_buffer().template cast<T>().cbegin();
|
|
}
|
|
|
|
const_pointer_iterator<T> cend()
|
|
{
|
|
return data_table.as_buffer().template cast<T>().cbegin()+data_top;
|
|
}
|
|
|
|
size_t size() {
|
|
return data_top;
|
|
}
|
|
|
|
size_t capacity() {
|
|
return _capacity;
|
|
}
|
|
|
|
T& operator[](size_t idx) {
|
|
gp_config::assertion(idx < data_top, "Bad indexed array access");
|
|
return data_table.as_buffer().template cast<T>()[translation_table[idx]];
|
|
}
|
|
};
|
|
}
|