Gaia-ECS v0.9.3
A simple and powerful entity component system
Loading...
Searching...
No Matches
component_cursor.h
1#pragma once
2#include "gaia/config/config.h"
3
4#include <cstdint>
5#include <cstring>
6
7#include "gaia/ecs/component_cache.h"
8#include "gaia/ecs/component_cache_item.h"
9#include "gaia/ecs/id.h"
10#include "gaia/util/str.h"
11
12namespace gaia {
13 namespace ecs {
14 class World;
15 void world_finish_write(World& world, Entity term, Entity entity);
16
19 enum ComponentRawViewFlags : uint32_t {
21 ComponentRawViewFlag_None = 0,
23 ComponentRawViewFlag_Valid = 1U << 0
24 };
25
36 const void* data = nullptr;
38 uint32_t size = 0;
40 uint32_t flags = ComponentRawViewFlag_None;
41
43 GAIA_NODISCARD bool valid() const noexcept {
44 return (flags & ComponentRawViewFlag_Valid) != 0;
45 }
46 };
47 static_assert(sizeof(ComponentRawView) == 16, "ComponentRawView must stay compact");
48
60 void* data = nullptr;
62 uint32_t size = 0;
64 uint32_t flags = ComponentRawViewFlag_None;
65
67 GAIA_NODISCARD bool valid() const noexcept {
68 return (flags & ComponentRawViewFlag_Valid) != 0;
69 }
70 };
71 static_assert(sizeof(ComponentRawMutView) == 16, "ComponentRawMutView must stay compact");
72
74 enum class ComponentWriteResult : uint8_t { Ok, Invalid, ReadOnly, OutOfRange };
75
83 static constexpr uint32_t MaxDepth = 32;
84
86 ComponentCursor() = default;
87
93 GAIA_NODISCARD static ComponentCursor
94 from_raw(const ComponentCache& components, Entity component, ComponentRawView view) {
95 ComponentCursor cursor{};
96 if (!view.valid())
97 return cursor;
98
99 cursor.m_components = &components;
100 cursor.m_stack[0].type = component;
101 cursor.m_stack[0].data = view.data;
102 cursor.m_stack[0].size = view.size;
103 cursor.m_valid = true;
104 return cursor;
105 }
106
114 GAIA_NODISCARD static ComponentCursor from_raw(
115 World& world, const ComponentCache& components, Entity entity, Entity component, ComponentRawMutView view) {
116 ComponentCursor cursor{};
117 if (!view.valid())
118 return cursor;
119
120 cursor.m_world = &world;
121 cursor.m_components = &components;
122 cursor.m_entity = entity;
123 cursor.m_rootType = component;
124 cursor.m_stack[0].type = component;
125 cursor.m_stack[0].data = view.data;
126 cursor.m_stack[0].mutData = view.data;
127 cursor.m_stack[0].size = view.size;
128 cursor.m_stack[0].writable = true;
129 cursor.m_valid = true;
130 return cursor;
131 }
132
134 GAIA_NODISCARD bool valid() const noexcept {
135 return m_valid;
136 }
137
139 GAIA_NODISCARD uint32_t depth() const noexcept {
140 return m_depth;
141 }
142
144 GAIA_NODISCARD Entity type() const noexcept {
145 return m_valid ? m_stack[m_depth].type : EntityBad;
146 }
147
149 GAIA_NODISCARD uint32_t size() const noexcept {
150 return m_valid ? m_stack[m_depth].size : 0;
151 }
152
154 GAIA_NODISCARD const void* ptr() const noexcept {
155 return m_valid ? m_stack[m_depth].data : nullptr;
156 }
157
159 GAIA_NODISCARD void* mut_ptr() const noexcept {
160 return m_valid ? m_stack[m_depth].mutData : nullptr;
161 }
162
164 GAIA_NODISCARD uint32_t field_count() const {
165 const auto* pItem = current_item();
166 return pItem != nullptr ? pItem->field_count() : 0;
167 }
168
172 bool field(uint32_t index) {
173 const auto* pItem = current_item();
174 if (pItem == nullptr || index >= pItem->field_count())
175 return false;
176 return descend(pItem->fields[index]);
177 }
178
183 const auto* pItem = current_item();
184 if (pItem == nullptr)
185 return false;
186
187 const RuntimeField* pField = nullptr;
188 if (!pItem->field(name, &pField) || pField == nullptr)
189 return false;
190 return descend(*pField);
191 }
192
195 bool parent() noexcept {
196 if (!m_valid || m_depth == 0)
197 return false;
198 --m_depth;
199 return true;
200 }
201
205 GAIA_NODISCARD bool get_f32(float* value) const noexcept {
206 if (value == nullptr || !m_valid || size() != sizeof(float) || ptr() == nullptr)
207 return false;
208 memcpy(value, ptr(), sizeof(float));
209 return true;
210 }
211
221 ComponentWriteResult write_bytes(const void* data, uint32_t byteCount) noexcept {
222 if (!m_valid)
223 return ComponentWriteResult::Invalid;
224 if (!m_stack[m_depth].writable || m_world == nullptr || m_entity == EntityBad || m_rootType == EntityBad)
225 return ComponentWriteResult::ReadOnly;
226 if (byteCount != size())
227 return ComponentWriteResult::OutOfRange;
228 if (byteCount == 0)
229 return ComponentWriteResult::Ok;
230 if (data == nullptr || mut_ptr() == nullptr)
231 return ComponentWriteResult::Invalid;
232
233 memcpy(mut_ptr(), data, byteCount);
234 world_finish_write(*m_world, m_rootType, m_entity);
235 return ComponentWriteResult::Ok;
236 }
237
238 private:
239 struct Scope {
240 Entity type = EntityBad;
241 const void* data = nullptr;
242 void* mutData = nullptr;
243 uint32_t size = 0;
244 bool writable = false;
245 };
246
247 World* m_world = nullptr;
248 const ComponentCache* m_components = nullptr;
249 Entity m_entity = EntityBad;
250 Entity m_rootType = EntityBad;
251 Scope m_stack[MaxDepth]{};
252 uint32_t m_depth = 0;
253 bool m_valid = false;
254
255 GAIA_NODISCARD const ComponentCacheItem* current_item() const noexcept {
256 if (!m_valid || m_components == nullptr)
257 return nullptr;
258 return m_components->find(m_stack[m_depth].type);
259 }
260
261 bool descend(const RuntimeField& field) noexcept {
262 if (!m_valid || m_components == nullptr || m_depth + 1 >= MaxDepth)
263 return false;
264
265 const auto* pType = m_components->find(field.type);
266 if (pType == nullptr)
267 return false;
268
269 const auto elemSize = pType->comp.size();
270 const auto elemCount = ComponentCacheItem::field_element_count(field);
271 const auto fieldSize64 = (uint64_t)elemSize * (uint64_t)elemCount;
272 const auto end = (uint64_t)field.offset + fieldSize64;
273 if (fieldSize64 > UINT32_MAX || end > m_stack[m_depth].size)
274 return false;
275
276 Scope next{};
277 next.type = field.type;
278 next.size = (uint32_t)fieldSize64;
279 next.writable = m_stack[m_depth].writable;
280 if (m_stack[m_depth].data != nullptr)
281 next.data = (const uint8_t*)m_stack[m_depth].data + field.offset;
282 if (m_stack[m_depth].mutData != nullptr)
283 next.mutData = (uint8_t*)m_stack[m_depth].mutData + field.offset;
284
285 m_stack[++m_depth] = next;
286 return true;
287 }
288 };
289 } // namespace ecs
290} // namespace gaia
Cache for compile-time defined components.
Definition component_cache.h:24
Definition world.h:174
Checks if endianess was detected correctly at compile-time.
Definition bitset.h:9
Stack-only cursor over raw component bytes and runtime field metadata.
Definition component_cursor.h:81
bool field(uint32_t index)
Descends into the reflected field at index.
Definition component_cursor.h:172
GAIA_NODISCARD void * mut_ptr() const noexcept
Definition component_cursor.h:159
static GAIA_NODISCARD ComponentCursor from_raw(const ComponentCache &components, Entity component, ComponentRawView view)
Creates a read-only cursor from a raw component view.
Definition component_cursor.h:94
GAIA_NODISCARD uint32_t field_count() const
Definition component_cursor.h:164
ComponentCursor()=default
Creates an invalid cursor.
bool parent() noexcept
Moves back to the parent cursor scope.
Definition component_cursor.h:195
GAIA_NODISCARD uint32_t size() const noexcept
Definition component_cursor.h:149
GAIA_NODISCARD uint32_t depth() const noexcept
Definition component_cursor.h:139
GAIA_NODISCARD bool valid() const noexcept
Definition component_cursor.h:134
GAIA_NODISCARD bool get_f32(float *value) const noexcept
Reads the current cursor bytes as a scalar f32 convenience value.
Definition component_cursor.h:205
GAIA_NODISCARD Entity type() const noexcept
Definition component_cursor.h:144
GAIA_NODISCARD const void * ptr() const noexcept
Definition component_cursor.h:154
ComponentWriteResult write_bytes(const void *data, uint32_t byteCount) noexcept
Writes exact bytes to the current cursor position.
Definition component_cursor.h:221
static constexpr uint32_t MaxDepth
Maximum nested field depth tracked by the cursor.
Definition component_cursor.h:83
static GAIA_NODISCARD ComponentCursor from_raw(World &world, const ComponentCache &components, Entity entity, Entity component, ComponentRawMutView view)
Creates a mutable cursor from a raw component view.
Definition component_cursor.h:114
bool field(util::str_view name)
Descends into the reflected field named name.
Definition component_cursor.h:182
Non-owning mutable view over raw component bytes on an entity.
Definition component_cursor.h:58
void * data
Raw component payload bytes. Null for tags and invalid views.
Definition component_cursor.h:60
uint32_t flags
Bitmask of ComponentRawViewFlags values.
Definition component_cursor.h:64
uint32_t size
Payload size in bytes.
Definition component_cursor.h:62
GAIA_NODISCARD bool valid() const noexcept
Definition component_cursor.h:67
Non-owning read-only view over raw component bytes on an entity.
Definition component_cursor.h:34
uint32_t size
Payload size in bytes.
Definition component_cursor.h:38
uint32_t flags
Bitmask of ComponentRawViewFlags values.
Definition component_cursor.h:40
GAIA_NODISCARD bool valid() const noexcept
Definition component_cursor.h:43
const void * data
Raw component payload bytes. Null for tags and invalid views.
Definition component_cursor.h:36
Definition id.h:247
Stored runtime field metadata.
Definition component_desc.h:65
Lightweight non-owning string view over a character sequence.
Definition str.h:13