1
0
Fork 0
Univerxel/src/core/data/generational.hpp

291 lines
8.9 KiB
C++

#pragma once
#include <cassert>
#include <vector>
#include <optional>
namespace data::generational {
struct id {
id(size_t index, size_t generation = 0): index(index), generation(generation) { }
id(): id(0) { }
size_t index;
size_t generation;
bool operator==(const id &i) const { return index == i.index && generation == i.generation; }
};
class allocator {
public:
allocator() { }
template <typename C>
allocator(const C& map) {
for(const auto& [key, _]: map) {
if (const auto size = entries.size(); key >= size) {
entries.resize(key + 1);
for (size_t i = size; i < entries.size(); i++) {
entries[i].is_live = false;
freed.push_back(i);
}
}
assert(!entries[key].is_live);
entries[key].is_live = true;
}
}
id alloc() {
if(freed.empty()) {
const auto idx = entries.size();
entries.emplace_back();
return id(idx);
} else {
const auto idx = freed.back();
freed.pop_back();
auto &entry = entries[idx];
assert(!entry.is_live);
entry.is_live = true;
return id(idx, ++entries[idx].generation);
}
}
bool is_live(id id) const {
return id.index < entries.size() &&
entries[id.index].is_live &&
entries[id.index].generation == id.generation;
}
bool free(id id) {
if(!is_live(id))
return false;
entries[id.index].is_live = false;
freed.push_back(id.index);
return true;
}
private:
struct entry {
bool is_live = true;
size_t generation = 0;
};
std::vector<entry> entries;
std::vector<size_t> freed;
};
template<typename T>
class vector {
public:
vector() { }
template<typename C>
vector(const C& map) {
for(const auto& [key, value]: map) {
if (const auto size = entries.size(); key >= size) {
entries.resize(key + 1);
}
entries[key].value = value;
}
for (size_t i = 0; i < entries.size(); i++) {
if(!entries[i].value.has_value())
freed.push_back(i);
}
}
id push(const T& in) {
if(freed.empty()) {
const auto idx = entries.size();
entries.emplace_back(in);
return id(idx);
} else {
const auto idx = freed.back();
freed.pop_back();
auto &entry = entries[idx];
assert(!entry.value.has_value());
entry.value = in;
return id(idx, ++entries[idx].generation);
}
}
template <typename... _Args>
id emplace(_Args &&... __args) {
if(freed.empty()) {
const auto idx = entries.size();
entries.emplace_back(std::forward<_Args>(__args)...);
return id(idx);
} else {
const auto idx = freed.back();
freed.pop_back();
auto &entry = entries[idx];
assert(!entry.value.has_value());
entry.value.emplace(std::forward<_Args>(__args)...);
return id(idx, ++entries[idx].generation);
}
}
bool put(id idx, const T& in) {
if(idx.index >= entries.size())
return false;
if(entries[idx.index].generation != idx.generation || entries[idx.index].value.has_value())
return false;
entries[idx.index].value = in;
return true;
}
T& at(id idx) {
assert(contains(idx));
return entries[idx.index].value.value();
}
bool contains(id idx) const {
return idx.index < entries.size() &&
entries[idx.index].generation == idx.generation &&
entries[idx.index].value.has_value();
}
bool free(id idx) {
if(!contains(idx))
return false;
entries[idx.index].value = std::nullopt;
freed.push_back(idx.index);
return true;
}
bool empty() const {
for(auto& v: entries) {
if(v.value.has_value())
return false;
}
return true;
}
template<typename apply>
void iter(apply fn) const {
for (size_t i = 0; i < entries.size(); i++) {
const auto &entry = entries[i];
if(entry.value.has_value()) {
fn(id(i, entry.generation), entry.value.value());
}
}
}
template<typename apply>
void for_each(apply fn) {
for (size_t i = 0; i < entries.size(); i++) {
auto &entry = entries[i];
if(entry.value.has_value()) {
fn(id(i, entry.generation), entry.value.value());
}
}
}
template<typename predicate>
bool contains(predicate fn) const {
for (size_t i = 0; i < entries.size(); i++) {
const auto &entry = entries[i];
if(entry.value.has_value() && fn(id(i, entry.generation), entry.value.value())) {
return true;
}
}
return false;
}
template<typename extractor>
void extract(extractor fn) {
for (size_t i = 0; i < entries.size(); i++) {
auto &entry = entries[i];
if(entry.value.has_value()) {
if(fn(id(i, entry.generation), entry.value.value()))
entry.value = std::nullopt;
}
}
}
template<typename extractor>
void remove(extractor fn) {
for (size_t i = 0; i < entries.size(); i++) {
auto &entry = entries[i];
if(entry.value.has_value()) {
if(fn(id(i, entry.generation), entry.value.value())) {
entry.value = std::nullopt;
freed.push_back(i);
}
}
}
}
size_t size() const {
return std::count_if(entries.begin(), entries.end(),
[](const entry &e) { return e.value.has_value(); });
}
private:
struct entry {
entry(): value(std::nullopt), generation(0) { }
entry(const T &in, size_t gen = 0):
value(in), generation(gen) { }
template <typename... _Args>
entry(_Args &&... __args): generation(0) {
value.emplace(std::forward<_Args>(__args)...);
}
std::optional<T> value;
size_t generation;
};
std::vector<entry> entries;
std::vector<size_t> freed;
};
template<typename T>
class view_vector: public std::vector<std::optional<T>> {
public:
view_vector(): std::vector<std::optional<T>>() { }
template <typename... _Args>
void set_emplace(size_t i, _Args &&... __args) {
if(i >= this->size())
this->resize(i + 1);
this->at(i).emplace(std::forward<_Args>(__args)...);
}
void set(size_t i, const T& in) {
if(i >= this->size())
this->resize(i + 1);
this->at(i) = in;
}
T* get(size_t i) {
if (i >= this->size() || !this->at(i).has_value())
return nullptr;
return &this->at(i).value();
}
template<typename apply>
void iter(apply fn) const {
for (size_t i = 0; i < this->size(); i++) {
if(this->at(i).has_value()) {
fn(i, this->at(i).value());
}
}
}
template<typename predicate>
bool contains(predicate fn) const {
for (size_t i = 0; i < this->size(); i++) {
if(this->at(i).has_value() && fn(i, this->at(i).value())) {
return true;
}
}
return false;
}
size_t count() const {
return std::count_if(this->begin(), this->end(),
[](const std::optional<T> &e) { return e.has_value(); });
}
};
}
namespace std {
template<>
struct hash<data::generational::id> {
std::size_t operator()(const data::generational::id& i) const noexcept {
return std::hash<size_t>{}(i.index);
}
};
}