189 lines
3.7 KiB
C++
189 lines
3.7 KiB
C++
#ifndef VOXEL_STRUCT_DB_H
|
|
#define VOXEL_STRUCT_DB_H
|
|
|
|
#include <core/error_macros.h>
|
|
#include <vector>
|
|
|
|
// Stores uniquely-identified structs in a packed array.
|
|
// Always use the IDs if you want to store a reference somewhere. Addresses aren't stable.
|
|
template <typename T>
|
|
class StructDB {
|
|
private:
|
|
struct Slot {
|
|
T data;
|
|
uint16_t version = 0;
|
|
bool valid = false;
|
|
};
|
|
|
|
public:
|
|
static const uint16_t MAX_INDEX = 0xffff;
|
|
static const uint16_t MAX_VERSION = 0xffff;
|
|
|
|
static uint16_t get_index(uint32_t id) {
|
|
return id & 0xffff;
|
|
}
|
|
|
|
static uint16_t get_version(uint32_t id) {
|
|
return (id >> 16) & 0xffff;
|
|
}
|
|
|
|
static uint32_t make_id(uint16_t index, uint16_t version) {
|
|
return index | (version << 16);
|
|
}
|
|
|
|
inline T &get(uint32_t id) {
|
|
const uint16_t i = get_index(id);
|
|
CRASH_COND(i >= _slots.size());
|
|
const uint16_t v = get_version(id);
|
|
Slot &s = _slots[i];
|
|
CRASH_COND(s.version != v);
|
|
CRASH_COND(s.valid == false);
|
|
return s.data;
|
|
}
|
|
|
|
inline const T &get(uint32_t id) const {
|
|
const uint16_t i = get_index(id);
|
|
CRASH_COND(i >= _slots.size());
|
|
const uint16_t v = get_version(id);
|
|
const Slot &s = _slots[i];
|
|
CRASH_COND(s.version != v);
|
|
CRASH_COND(s.valid == false);
|
|
return s.data;
|
|
}
|
|
|
|
inline T *try_get(uint32_t id) {
|
|
const uint16_t i = get_index(id);
|
|
if (i >= _slots.size()) {
|
|
return nullptr;
|
|
}
|
|
const uint16_t v = get_version(id);
|
|
Slot &s = _slots[i];
|
|
if (s.version != v || s.valid == false) {
|
|
return nullptr;
|
|
}
|
|
return &s.data;
|
|
}
|
|
|
|
inline const T *try_get(uint32_t id) const {
|
|
const uint16_t i = get_index(id);
|
|
if (i >= _slots.size()) {
|
|
return nullptr;
|
|
}
|
|
const uint16_t v = get_version(id);
|
|
const Slot &s = _slots[i];
|
|
if (s.version != v) {
|
|
return nullptr;
|
|
}
|
|
return &s.data;
|
|
}
|
|
|
|
inline uint32_t create(T data) {
|
|
uint16_t i = find_free_index();
|
|
uint16_t v;
|
|
if (i == _slots.size()) {
|
|
CRASH_COND(i == MAX_INDEX);
|
|
v = 0;
|
|
Slot s;
|
|
s.data = data;
|
|
s.valid = true;
|
|
s.version = v;
|
|
_slots.push_back(s);
|
|
} else {
|
|
Slot &s = _slots[i];
|
|
CRASH_COND(s.version == MAX_VERSION);
|
|
s.data = data;
|
|
s.valid = true;
|
|
v = s.version + 1;
|
|
s.version = v;
|
|
}
|
|
return make_id(i, v);
|
|
}
|
|
|
|
inline void destroy(uint32_t id) {
|
|
const uint16_t i = get_index(id);
|
|
CRASH_COND(i >= _slots.size());
|
|
Slot &s = _slots[i];
|
|
CRASH_COND(s.valid == false);
|
|
s.data = T();
|
|
s.valid = false;
|
|
}
|
|
|
|
inline bool is_valid(uint32_t id) const {
|
|
const uint16_t i = get_index(id);
|
|
if (i >= _slots.size()) {
|
|
return false;
|
|
}
|
|
const Slot &s = _slots[i];
|
|
return s.valid && s.version == get_version(id);
|
|
}
|
|
|
|
inline uint32_t count() const {
|
|
uint32_t c = 0;
|
|
for (size_t i = 0; i < _slots.size(); ++i) {
|
|
const Slot &s = _slots[i];
|
|
if (s.valid) {
|
|
++c;
|
|
}
|
|
}
|
|
return c;
|
|
}
|
|
|
|
template <typename F>
|
|
inline void for_each(F f) {
|
|
for (size_t i = 0; i < _slots.size(); ++i) {
|
|
Slot &s = _slots[i];
|
|
if (s.valid) {
|
|
f(s.data);
|
|
}
|
|
}
|
|
}
|
|
|
|
template <typename F>
|
|
inline void for_each(F f) const {
|
|
for (size_t i = 0; i < _slots.size(); ++i) {
|
|
const Slot &s = _slots[i];
|
|
if (s.valid) {
|
|
f(s.data);
|
|
}
|
|
}
|
|
}
|
|
|
|
template <typename F>
|
|
inline void for_each_with_id(F f) {
|
|
for (size_t i = 0; i < _slots.size(); ++i) {
|
|
Slot &s = _slots[i];
|
|
if (s.valid) {
|
|
f(s.data, make_id(i, s.version));
|
|
}
|
|
}
|
|
}
|
|
|
|
template <typename F>
|
|
inline void for_each_with_id(F f) const {
|
|
for (size_t i = 0; i < _slots.size(); ++i) {
|
|
const Slot &s = _slots[i];
|
|
if (s.valid) {
|
|
f(s.data, make_id(i, s.version));
|
|
}
|
|
}
|
|
}
|
|
|
|
inline void clear() {
|
|
_slots.clear();
|
|
}
|
|
|
|
private:
|
|
inline size_t find_free_index() const {
|
|
for (size_t i = 0; i < _slots.size(); ++i) {
|
|
if (_slots[i].valid == false) {
|
|
return i;
|
|
}
|
|
}
|
|
return _slots.size();
|
|
}
|
|
|
|
std::vector<Slot> _slots;
|
|
};
|
|
|
|
#endif // VOXEL_STRUCT_DB_H
|