co_ecs 0.9.0
Cobalt ECS
Loading...
Searching...
No Matches
handle.hpp
Go to the documentation of this file.
1#pragma once
2
3#include <atomic>
4#include <cstdint>
5#include <numeric>
6#include <vector>
7
8namespace co_ecs::detail {
9
12template<typename Tag>
13class handle {
14public:
15 using id_t = std::uint32_t;
16 using generation_t = std::uint32_t;
17
19 static constexpr auto invalid_id = std::numeric_limits<id_t>::max();
20
22 static constexpr auto invalid_generation = std::numeric_limits<generation_t>::max();
23
26 [[nodiscard]] static constexpr handle invalid() noexcept {
27 return handle{ invalid_id, invalid_generation };
28 }
29
31 constexpr handle() = default;
32
36 explicit constexpr handle(id_t id, generation_t generation = 0) noexcept : _id(id), _generation(generation) {
37 }
38
41 [[nodiscard]] constexpr auto valid() const noexcept -> bool {
42 return *this != handle::invalid();
43 }
44
47 [[nodiscard]] constexpr auto id() const noexcept {
48 return _id;
49 }
50
53 [[nodiscard]] constexpr auto generation() const noexcept {
54 return _generation;
55 }
56
60 [[nodiscard]] constexpr auto operator<=>(const handle& rhs) const = default;
61
62private:
63 id_t _id{ invalid_id };
64 generation_t _generation{ invalid_generation };
65};
66
69template<typename H>
70class handle_pool {
71public:
74 [[nodiscard]] constexpr auto create() -> H {
75 if (!_free_ids.empty()) {
76 auto id = _free_ids.back();
77 _free_ids.pop_back();
78 _free_cursor.store(_free_ids.size(), std::memory_order::relaxed);
79 return H{ id, _generations[id] };
80 }
81 auto handle = H{ _next_id.fetch_add(1, std::memory_order::relaxed) };
82 _generations.emplace_back();
83 return handle;
84 };
85
89 [[nodiscard]] constexpr auto alive(H handle) const noexcept -> bool {
90 if (handle.id() < _generations.size()) {
91 return _generations[handle.id()] == handle.generation();
92 }
93 return false;
94 }
95
98 constexpr void recycle(H handle) {
99 if (!alive(handle)) {
100 return;
101 }
102 _generations[handle.id()]++;
103 _free_ids.push_back(handle.id());
104 _free_cursor.fetch_add(1, std::memory_order::relaxed);
105 }
106
110 constexpr auto reserve() -> H {
111 auto n = _free_cursor.fetch_sub(1, std::memory_order::relaxed);
112 if (n > 0) {
113 auto id = _free_ids[n - 1];
114 return H{ id, _generations[id] };
115 }
116 auto handle = H{ _next_id.fetch_add(1, std::memory_order::relaxed) };
117 return handle;
118 }
119
121 constexpr void flush() {
122 auto free_cursor = _free_cursor.load(std::memory_order::relaxed);
123
124 while (free_cursor < 0) {
125 _generations.emplace_back();
126 free_cursor++;
127 }
128
129 while (_free_ids.size() > free_cursor) {
130 _free_ids.pop_back();
131 }
132
133 _free_cursor.store(free_cursor, std::memory_order::relaxed);
134 }
135
136private:
137 std::atomic<typename H::id_t> _next_id{};
138 std::atomic<std::int64_t> _free_cursor{};
139 std::vector<typename H::generation_t> _generations;
140 std::vector<typename H::id_t> _free_ids;
141};
142
143} // namespace co_ecs::detail
Definition component.hpp:17