Gaia-ECS v1.0.0
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;
19 void world_finish_write(World& world, Entity term, Entity entity);
20
23 enum ComponentRawViewFlags : uint32_t {
25 ComponentRawViewFlag_None = 0,
27 ComponentRawViewFlag_Valid = 1U << 0
28 };
29
40 const void* data = nullptr;
42 uint32_t size = 0;
44 uint32_t flags = ComponentRawViewFlag_None;
45
47 GAIA_NODISCARD bool valid() const noexcept {
48 return (flags & ComponentRawViewFlag_Valid) != 0;
49 }
50 };
51 static_assert(
52 sizeof(ComponentRawView) == sizeof(void*) + sizeof(uint32_t) * 2, "ComponentRawView must not contain padding");
53 static_assert(sizeof(ComponentRawView) <= 16, "ComponentRawView must stay compact");
54
66 void* data = nullptr;
68 uint32_t size = 0;
70 uint32_t flags = ComponentRawViewFlag_None;
71
73 GAIA_NODISCARD bool valid() const noexcept {
74 return (flags & ComponentRawViewFlag_Valid) != 0;
75 }
76 };
77 static_assert(
78 sizeof(ComponentRawMutView) == sizeof(void*) + sizeof(uint32_t) * 2,
79 "ComponentRawMutView must not contain padding");
80 static_assert(sizeof(ComponentRawMutView) <= 16, "ComponentRawMutView must stay compact");
81
87 ComponentRawView world_get_raw(const World& world, Entity entity, Entity component);
93 ComponentRawMutView world_mut_raw(World& world, Entity entity, Entity component);
94
101 ComponentRawView world_get_raw_field(const World& world, Entity entity, Entity component, uint32_t fieldIdx);
108 ComponentRawMutView world_mut_raw_field(World& world, Entity entity, Entity component, uint32_t fieldIdx);
109
111 enum class CursorStatus : uint8_t {
113 Ok,
115 Invalid,
117 ReadOnly,
119 TypeMismatch,
121 OutOfRange
122 };
123
126 template <typename T>
127 struct CursorResult final {
129 CursorStatus status = CursorStatus::Invalid;
131 T value{};
132
134 GAIA_NODISCARD bool ok() const noexcept {
135 return status == CursorStatus::Ok;
136 }
137
139 GAIA_NODISCARD explicit operator bool() const noexcept {
140 return ok();
141 }
142 };
143
145 template <>
146 struct CursorResult<void> final {
148 CursorStatus status = CursorStatus::Invalid;
149
151 GAIA_NODISCARD bool ok() const noexcept {
152 return status == CursorStatus::Ok;
153 }
154
156 GAIA_NODISCARD explicit operator bool() const noexcept {
157 return ok();
158 }
159 };
160
162 GAIA_NODISCARD inline bool operator==(CursorResult<void> result, CursorStatus status) noexcept {
163 return result.status == status;
164 }
165
167 GAIA_NODISCARD inline bool operator==(CursorStatus status, CursorResult<void> result) noexcept {
168 return result.status == status;
169 }
170
172 GAIA_NODISCARD inline bool operator!=(CursorResult<void> result, CursorStatus status) noexcept {
173 return result.status != status;
174 }
175
177 GAIA_NODISCARD inline bool operator!=(CursorStatus status, CursorResult<void> result) noexcept {
178 return result.status != status;
179 }
180
188 static constexpr uint32_t MaxDepth = 32;
189
190 ComponentCursor() = default;
191
197 GAIA_NODISCARD static ComponentCursor
198 from_raw(const ComponentCache& components, Entity component, ComponentRawView view) {
199 ComponentCursor cursor{};
200 if (!view.valid())
201 return cursor;
202
203 cursor.m_components = &components;
204 cursor.m_stack[0].type = component;
205 cursor.m_stack[0].data = view.data;
206 cursor.m_stack[0].size = view.size;
207 cursor.m_stack[0].elemSize = view.size;
208 cursor.m_stack[0].elemCount = 1;
209 cursor.m_valid = true;
210 return cursor;
211 }
212
220 GAIA_NODISCARD static ComponentCursor from_raw(
221 World& world, const ComponentCache& components, Entity entity, Entity component, ComponentRawMutView view) {
222 ComponentCursor cursor{};
223 if (!view.valid())
224 return cursor;
225
226 cursor.m_world = &world;
227 cursor.m_components = &components;
228 cursor.m_entity = entity;
229 cursor.m_rootType = component;
230 cursor.m_stack[0].type = component;
231 cursor.m_stack[0].data = view.data;
232 cursor.m_stack[0].mutData = view.data;
233 cursor.m_stack[0].size = view.size;
234 cursor.m_stack[0].elemSize = view.size;
235 cursor.m_stack[0].elemCount = 1;
236 cursor.m_stack[0].writable = true;
237 cursor.m_valid = true;
238 return cursor;
239 }
240
248 GAIA_NODISCARD static ComponentCursor
249 from_soa(const World& world, const ComponentCache& components, Entity entity, Entity component, uint32_t size) {
250 ComponentCursor cursor{};
251 cursor.m_readWorld = &world;
252 cursor.m_components = &components;
253 cursor.m_entity = entity;
254 cursor.m_rootType = component;
255 cursor.m_stack[0].type = component;
256 cursor.m_stack[0].size = size;
257 cursor.m_stack[0].elemSize = size;
258 cursor.m_stack[0].elemCount = 1;
259 cursor.m_rootSoa = true;
260 cursor.m_valid = true;
261 return cursor;
262 }
263
271 GAIA_NODISCARD static ComponentCursor
272 from_soa(World& world, const ComponentCache& components, Entity entity, Entity component, uint32_t size) {
273 auto cursor = from_soa((const World&)world, components, entity, component, size);
274 cursor.m_world = &world;
275 cursor.m_stack[0].writable = true;
276 return cursor;
277 }
278
280 GAIA_NODISCARD bool valid() const noexcept {
281 return m_valid;
282 }
283
285 GAIA_NODISCARD uint32_t depth() const noexcept {
286 return m_depth;
287 }
288
290 GAIA_NODISCARD Entity type() const noexcept {
291 return m_valid ? m_stack[m_depth].type : EntityBad;
292 }
293
296 GAIA_NODISCARD RuntimeTypeKind type_kind() const noexcept {
297 const auto* pItem = current_item();
298 return pItem != nullptr ? pItem->typeKind : RuntimeTypeKind::None;
299 }
300
303 GAIA_NODISCARD Entity opaque_as_type() const noexcept {
304 const auto* pItem = current_item();
305 return pItem != nullptr ? pItem->opaque_as_type() : EntityBad;
306 }
307
310 GAIA_NODISCARD Entity element_type() const noexcept {
311 const auto* pItem = current_item();
312 return pItem != nullptr ? pItem->element_type() : EntityBad;
313 }
314
317 GAIA_NODISCARD uint32_t size() const noexcept {
318 return m_valid ? m_stack[m_depth].size : 0;
319 }
320
322 GAIA_NODISCARD const void* ptr() const noexcept {
323 return m_valid ? m_stack[m_depth].data : nullptr;
324 }
325
327 GAIA_NODISCARD void* mut_ptr() const noexcept {
328 return m_valid ? m_stack[m_depth].mutData : nullptr;
329 }
330
332 GAIA_NODISCARD uint32_t field_count() const {
333 if (!m_valid || m_stack[m_depth].elemCount != 1)
334 return 0;
335 const auto* pItem = current_item();
336 if (pItem == nullptr)
337 return 0;
338 if (pItem->typeKind == RuntimeTypeKind::Opaque && pItem->opaque_adapter() != nullptr) {
339 const auto* pSemantic = opaque_semantic_item(*pItem);
340 return pSemantic != nullptr ? pSemantic->field_count() : 0;
341 }
342 return pItem->field_count();
343 }
344
347 GAIA_NODISCARD CursorResult<uint32_t> count() const noexcept {
348 CursorResult<uint32_t> result{};
349 if (!m_valid) {
350 result.status = CursorStatus::Invalid;
351 return result;
352 }
353
354 const auto& current = m_stack[m_depth];
355 if (current.elemCount > 1) {
356 result.status = CursorStatus::Ok;
357 result.value = current.elemCount;
358 return result;
359 }
360
361 const auto* pType = current_item();
362 if (pType == nullptr) {
363 result.status = CursorStatus::TypeMismatch;
364 return result;
365 }
366 if (pType->typeKind == RuntimeTypeKind::Array) {
367 result.status = CursorStatus::Ok;
368 result.value = pType->element_count();
369 return result;
370 }
371 if (pType->typeKind == RuntimeTypeKind::Vector) {
372 const auto* adapter = pType->sequence_adapter();
373 if (adapter == nullptr || adapter->count == nullptr) {
374 result.status = CursorStatus::TypeMismatch;
375 return result;
376 }
377 RuntimeSequenceScope sequence{current.type, current.data, current.mutData, current.size};
378 uint32_t value = 0;
379 if (!adapter->count(adapter->ctx, sequence, value)) {
380 result.status = CursorStatus::Invalid;
381 return result;
382 }
383 result.status = CursorStatus::Ok;
384 result.value = value;
385 return result;
386 }
387 result.status = CursorStatus::TypeMismatch;
388 return result;
389 }
390
394 bool field(uint32_t index) {
395 if (!m_valid || m_stack[m_depth].elemCount != 1)
396 return false;
397 const auto* pItem = current_item();
398 if (pItem == nullptr)
399 return false;
400
401 if (pItem->typeKind == RuntimeTypeKind::Opaque)
402 return field_opaque(index);
403
404 const RuntimeFieldDesc* pField = pItem->field(index);
405 return pField != nullptr ? descend(*pField, index) : false;
406 }
407
412 if (!m_valid || m_stack[m_depth].elemCount != 1)
413 return false;
414 const auto* pItem = current_item();
415 if (pItem == nullptr)
416 return false;
417
418 if (pItem->typeKind == RuntimeTypeKind::Opaque)
419 return field_opaque(name);
420
421 const RuntimeFieldDesc* pField = pItem->field(name);
422 if (pField == nullptr)
423 return false;
424
425 uint32_t index = 0;
426 const auto fieldCount = pItem->field_count();
427 while (index < fieldCount && pItem->field(index) != pField)
428 ++index;
429 return index < fieldCount && descend(*pField, index);
430 }
431
435 bool elem(uint32_t index) noexcept {
436 if (!m_valid || m_depth + 1 >= MaxDepth)
437 return false;
438
439 const auto& current = m_stack[m_depth];
440 Scope next{};
441 if (current.elemCount > 1) {
442 if (index >= current.elemCount || current.elemSize == 0)
443 return false;
444 if ((uint64_t)current.elemSize * ((uint64_t)index + 1) > current.size)
445 return false;
446
447 next.type = current.type;
448 next.size = current.elemSize;
449 next.elemSize = current.elemSize;
450 next.elemCount = 1;
451 next.sequenceAdapter = current.sequenceAdapter;
452 next.sequenceDepth = current.sequenceDepth;
453 next.elementDepth = current.elementDepth;
454 next.elementToken = current.elementToken;
455 next.opaqueAdapter = current.opaqueAdapter;
456 next.opaqueDepth = current.opaqueDepth;
457 next.opaqueValueDepth = current.opaqueValueDepth;
458 next.opaqueToken = current.opaqueToken;
459 next.writable = current.writable;
460 if (current.data != nullptr)
461 next.data = (const uint8_t*)current.data + (uint64_t)current.elemSize * index;
462 if (current.mutData != nullptr)
463 next.mutData = (uint8_t*)current.mutData + (uint64_t)current.elemSize * index;
464 } else {
465 const auto* pType = m_components != nullptr ? m_components->find(current.type) : nullptr;
466 if (pType == nullptr)
467 return false;
468
469 if (pType->typeKind == RuntimeTypeKind::Vector) {
470 const auto* adapter = pType->sequence_adapter();
471 if (adapter == nullptr || adapter->count == nullptr || adapter->element == nullptr)
472 return false;
473 RuntimeSequenceScope sequence{current.type, current.data, current.mutData, current.size};
474 uint32_t elemCount = 0;
475 if (!adapter->count(adapter->ctx, sequence, elemCount) || index >= elemCount)
476 return false;
477 RuntimeSequenceElement element{};
478 element.type = pType->element_type();
479 if (!adapter->element(adapter->ctx, sequence, index, element))
480 return false;
481 if (element.type == EntityBad)
482 element.type = pType->element_type();
483 if (element.type == EntityBad || element.size == 0 || element.data == nullptr)
484 return false;
485
486 next.type = element.type;
487 next.data = element.data;
488 next.mutData = element.mutData;
489 next.size = element.size;
490 next.elemSize = element.size;
491 next.elemCount = 1;
492 next.sequenceAdapter = adapter;
493 next.sequenceDepth = m_depth;
494 next.elementDepth = m_depth + 1;
495 next.elementToken = element.token;
496 next.writable = current.writable && element.mutData != nullptr;
497 } else if (pType->typeKind == RuntimeTypeKind::Array) {
498
499 const auto elemCount = pType->element_count();
500 const auto elementType = pType->element_type();
501 const auto* pElementType = m_components->find(elementType);
502 if (pElementType == nullptr || elemCount == 0 || index >= elemCount)
503 return false;
504
505 const auto elemSize = pElementType->comp.size();
506 if (elemSize == 0)
507 return false;
508 if ((uint64_t)elemSize * ((uint64_t)index + 1) > current.size)
509 return false;
510
511 next.type = elementType;
512 next.size = elemSize;
513 next.elemSize = elemSize;
514 next.elemCount = 1;
515 next.writable = current.writable;
516 if (current.data != nullptr)
517 next.data = (const uint8_t*)current.data + (uint64_t)elemSize * index;
518 if (current.mutData != nullptr)
519 next.mutData = (uint8_t*)current.mutData + (uint64_t)elemSize * index;
520 } else {
521 return false;
522 }
523 }
524
525 m_stack[++m_depth] = next;
526 return true;
527 }
528
531 CursorResult<void> resize(uint32_t count) noexcept {
532 if (!m_valid)
533 return {CursorStatus::Invalid};
534 const auto* pType = current_item();
535 if (pType == nullptr || pType->typeKind != RuntimeTypeKind::Vector)
536 return {CursorStatus::TypeMismatch};
537 const auto* adapter = pType->sequence_adapter();
538 if (adapter == nullptr || adapter->resize == nullptr)
539 return {CursorStatus::TypeMismatch};
540 if (!m_stack[m_depth].writable || m_world == nullptr || m_entity == EntityBad || m_rootType == EntityBad)
541 return {CursorStatus::ReadOnly};
542 RuntimeSequenceScope sequence{
543 m_stack[m_depth].type, m_stack[m_depth].data, m_stack[m_depth].mutData, m_stack[m_depth].size};
544 if (!adapter->resize(adapter->ctx, sequence, count))
545 return {CursorStatus::OutOfRange};
546 world_finish_write(*m_world, m_rootType, m_entity);
547 return {CursorStatus::Ok};
548 }
549
552 bool parent() noexcept {
553 if (!m_valid || m_depth == 0)
554 return false;
555 --m_depth;
556 return true;
557 }
558
561 GAIA_NODISCARD CursorResult<bool> b() const noexcept {
562 return read_primitive<bool>(Bool);
563 }
564
568 GAIA_NODISCARD CursorResult<void> b(bool value) noexcept {
569 return write_primitive(Bool, value);
570 }
571
574 GAIA_NODISCARD CursorResult<char> c8() const noexcept {
575 return read_primitive<char>(Char8);
576 }
577
581 GAIA_NODISCARD CursorResult<void> c8(char value) noexcept {
582 return write_primitive(Char8, value);
583 }
584
589 GAIA_NODISCARD CursorResult<uint32_t> c8(char* dst, uint32_t cap) noexcept {
590 return const_cast<const ComponentCursor&>(*this).c8(dst, cap);
591 }
592
597 GAIA_NODISCARD CursorResult<uint32_t> c8(char* dst, uint32_t cap) const noexcept {
598 CursorResult<uint32_t> result{};
599 const auto status = validate_primitive(Char8, false, false);
600 if (status != CursorStatus::Ok) {
601 result.status = status;
602 return result;
603 }
604 if (m_stack[m_depth].elemCount <= 1) {
605 result.status = CursorStatus::TypeMismatch;
606 return result;
607 }
608 if (dst == nullptr) {
609 result.status = CursorStatus::Invalid;
610 return result;
611 }
612 if (cap < size()) {
613 result.status = CursorStatus::OutOfRange;
614 return result;
615 }
616
617 memcpy(dst, ptr(), size());
618 result.status = CursorStatus::Ok;
619 result.value = size();
620 return result;
621 }
622
627 GAIA_NODISCARD CursorResult<void> c8(const char* src, uint32_t len) noexcept {
628 const auto status = validate_primitive(Char8, true, false);
629 if (status != CursorStatus::Ok)
630 return {status};
631 if (m_stack[m_depth].elemCount <= 1)
632 return {CursorStatus::TypeMismatch};
633 if (len > size())
634 return {CursorStatus::OutOfRange};
635 if (len != 0 && src == nullptr)
636 return {CursorStatus::Invalid};
637 if (len != 0)
638 memcpy(mut_ptr(), src, len);
639 if (!commit_current_sequence_element())
640 return {CursorStatus::Invalid};
641 world_finish_write(*m_world, m_rootType, m_entity);
642 return {CursorStatus::Ok};
643 }
644
647 GAIA_NODISCARD CursorResult<char16_t> c16() const noexcept {
648 return read_primitive<char16_t>(Char16);
649 }
650
654 GAIA_NODISCARD CursorResult<void> c16(char16_t value) noexcept {
655 return write_primitive(Char16, value);
656 }
657
660 GAIA_NODISCARD CursorResult<char32_t> c32() const noexcept {
661 return read_primitive<char32_t>(Char32);
662 }
663
667 GAIA_NODISCARD CursorResult<void> c32(char32_t value) noexcept {
668 return write_primitive(Char32, value);
669 }
670
673 GAIA_NODISCARD CursorResult<int8_t> s8() const noexcept {
674 return read_primitive<int8_t>(S8);
675 }
676
679 GAIA_NODISCARD CursorResult<void> s8(int8_t value) noexcept {
680 return write_primitive(S8, value);
681 }
682
685 GAIA_NODISCARD CursorResult<uint8_t> u8() const noexcept {
686 return read_primitive<uint8_t>(U8);
687 }
688
691 GAIA_NODISCARD CursorResult<void> u8(uint8_t value) noexcept {
692 return write_primitive(U8, value);
693 }
694
697 GAIA_NODISCARD CursorResult<int16_t> s16() const noexcept {
698 return read_primitive<int16_t>(S16);
699 }
700
703 GAIA_NODISCARD CursorResult<void> s16(int16_t value) noexcept {
704 return write_primitive(S16, value);
705 }
706
709 GAIA_NODISCARD CursorResult<uint16_t> u16() const noexcept {
710 return read_primitive<uint16_t>(U16);
711 }
712
715 GAIA_NODISCARD CursorResult<void> u16(uint16_t value) noexcept {
716 return write_primitive(U16, value);
717 }
718
721 GAIA_NODISCARD CursorResult<int32_t> s32() const noexcept {
722 return read_primitive<int32_t>(S32);
723 }
724
727 GAIA_NODISCARD CursorResult<void> s32(int32_t value) noexcept {
728 return write_primitive(S32, value);
729 }
730
733 GAIA_NODISCARD CursorResult<uint32_t> u32() const noexcept {
734 return read_primitive<uint32_t>(U32);
735 }
736
739 GAIA_NODISCARD CursorResult<void> u32(uint32_t value) noexcept {
740 return write_primitive(U32, value);
741 }
742
745 GAIA_NODISCARD CursorResult<int64_t> s64() const noexcept {
746 return read_primitive<int64_t>(S64);
747 }
748
751 GAIA_NODISCARD CursorResult<void> s64(int64_t value) noexcept {
752 return write_primitive(S64, value);
753 }
754
757 GAIA_NODISCARD CursorResult<uint64_t> u64() const noexcept {
758 return read_primitive<uint64_t>(U64);
759 }
760
763 GAIA_NODISCARD CursorResult<void> u64(uint64_t value) noexcept {
764 return write_primitive(U64, value);
765 }
766
769 GAIA_NODISCARD CursorResult<float> f32() const noexcept {
770 return read_primitive<float>(F32);
771 }
772
775 GAIA_NODISCARD CursorResult<void> f32(float value) noexcept {
776 return write_primitive(F32, value);
777 }
778
781 GAIA_NODISCARD CursorResult<double> f64() const noexcept {
782 return read_primitive<double>(F64);
783 }
784
787 GAIA_NODISCARD CursorResult<void> f64(double value) noexcept {
788 return write_primitive(F64, value);
789 }
790
798 GAIA_NODISCARD CursorResult<uint32_t> get_raw(void* data, uint32_t byteCount) const noexcept {
799 CursorResult<uint32_t> result{};
800 if (!m_valid) {
801 result.status = CursorStatus::Invalid;
802 return result;
803 }
804 if (byteCount < size()) {
805 result.status = CursorStatus::OutOfRange;
806 return result;
807 }
808 if (size() != 0 && (data == nullptr || ptr() == nullptr)) {
809 result.status = CursorStatus::Invalid;
810 return result;
811 }
812 if (size() != 0)
813 memcpy(data, ptr(), size());
814 result.status = CursorStatus::Ok;
815 result.value = size();
816 return result;
817 }
818
828 CursorResult<void> set_raw(const void* data, uint32_t byteCount) noexcept {
829 return set_raw(data, byteCount, true);
830 }
831
832 private:
833 friend class World;
834
835 CursorResult<void> set_raw(const void* data, uint32_t byteCount, bool commitWrite) noexcept {
836 if (!m_valid)
837 return {CursorStatus::Invalid};
838 if (!m_stack[m_depth].writable || m_world == nullptr || m_entity == EntityBad || m_rootType == EntityBad)
839 return {CursorStatus::ReadOnly};
840 if (byteCount != size())
841 return {CursorStatus::OutOfRange};
842 if (byteCount == 0)
843 return {CursorStatus::Ok};
844 if (data == nullptr || mut_ptr() == nullptr)
845 return {CursorStatus::Invalid};
846
847 memcpy(mut_ptr(), data, byteCount);
848 if (commitWrite) {
849 if (!commit_current_sequence_element())
850 return {CursorStatus::Invalid};
851 world_finish_write(*m_world, m_rootType, m_entity);
852 }
853 return {CursorStatus::Ok};
854 }
855
856 struct Scope {
857 Entity type = EntityBad;
858 const void* data = nullptr;
859 void* mutData = nullptr;
860 uint32_t size = 0;
861 uint32_t elemSize = 0;
862 uint32_t elemCount = 1;
863 const RuntimeSequenceAdapter* sequenceAdapter = nullptr;
864 uint32_t sequenceDepth = 0;
865 uint32_t elementDepth = 0;
866 void* elementToken = nullptr;
867 const RuntimeOpaqueAdapter* opaqueAdapter = nullptr;
868 uint32_t opaqueDepth = 0;
869 uint32_t opaqueValueDepth = 0;
870 void* opaqueToken = nullptr;
871 bool writable = false;
872 };
873
874 World* m_world = nullptr;
876 const World* m_readWorld = nullptr;
877 const ComponentCache* m_components = nullptr;
879 Entity m_entity = EntityBad;
880 Entity m_rootType = EntityBad;
881 Scope m_stack[MaxDepth]{};
882 uint32_t m_depth = 0;
883 bool m_valid = false;
885 bool m_rootSoa = false;
886
887 GAIA_NODISCARD const ComponentCacheItem* current_item() const noexcept {
888 if (!m_valid || m_components == nullptr)
889 return nullptr;
890 const auto type = m_stack[m_depth].type;
891 return type.pair() ? m_components->find_pair_payload(type) : m_components->find(type);
892 }
893
894 GAIA_NODISCARD const ComponentCacheItem* opaque_semantic_item(const ComponentCacheItem& item) const noexcept {
895 if (m_components == nullptr || item.typeKind != RuntimeTypeKind::Opaque)
896 return nullptr;
897 return m_components->find(item.opaque_as_type());
898 }
899
900 GAIA_NODISCARD static RuntimeOpaqueScope make_opaque_scope(const Scope& scope) noexcept {
901 return {scope.type, scope.data, scope.mutData, scope.size};
902 }
903
904 GAIA_NODISCARD RuntimeOpaqueValue make_opaque_value(const Scope& scope) const noexcept {
905 return {scope.type, scope.data, scope.mutData, scope.size, scope.opaqueToken};
906 }
907
908 bool project_opaque_scope(const ComponentCacheItem& item, Scope& out) const noexcept {
909 const auto* adapter = item.opaque_adapter();
910 const auto* pSemantic = opaque_semantic_item(item);
911 if (adapter == nullptr || adapter->project == nullptr || pSemantic == nullptr)
912 return false;
913 RuntimeOpaqueScope opaque = make_opaque_scope(m_stack[m_depth]);
914 RuntimeOpaqueValue value{};
915 value.type = item.opaque_as_type();
916 if (!adapter->project(adapter->ctx, opaque, value))
917 return false;
918 if (value.type == EntityBad)
919 value.type = item.opaque_as_type();
920 if (value.type != item.opaque_as_type() || value.data == nullptr || value.size != pSemantic->comp.size())
921 return false;
922
923 out = {};
924 out.type = value.type;
925 out.data = value.data;
926 out.mutData = value.mutData;
927 out.size = value.size;
928 out.elemSize = value.size;
929 out.elemCount = 1;
930 out.opaqueAdapter = adapter;
931 out.opaqueDepth = m_depth;
932 out.opaqueValueDepth = m_depth + 1;
933 out.opaqueToken = value.token;
934 out.writable = m_stack[m_depth].writable && value.mutData != nullptr;
935 return true;
936 }
937
938 bool field_opaque(uint32_t index) noexcept {
939 if (m_depth + 2 >= MaxDepth)
940 return false;
941 const auto* pItem = current_item();
942 if (pItem == nullptr)
943 return false;
944 Scope projected{};
945 if (!project_opaque_scope(*pItem, projected))
946 return false;
947 const auto* pSemantic = m_components->find(projected.type);
948 const auto* pField = pSemantic != nullptr ? pSemantic->field(index) : nullptr;
949 if (pField == nullptr)
950 return false;
951 m_stack[++m_depth] = projected;
952 if (!descend(*pField)) {
953 --m_depth;
954 return false;
955 }
956 return true;
957 }
958
959 bool field_opaque(util::str_view name) noexcept {
960 if (m_depth + 2 >= MaxDepth)
961 return false;
962 const auto* pItem = current_item();
963 if (pItem == nullptr)
964 return false;
965 Scope projected{};
966 if (!project_opaque_scope(*pItem, projected))
967 return false;
968 const auto* pSemantic = m_components->find(projected.type);
969 const auto* pField = pSemantic != nullptr ? pSemantic->field(name) : nullptr;
970 if (pField == nullptr)
971 return false;
972 m_stack[++m_depth] = projected;
973 if (!descend(*pField)) {
974 --m_depth;
975 return false;
976 }
977 return true;
978 }
979
980 GAIA_NODISCARD static RuntimeSequenceScope make_sequence_scope(const Scope& scope) noexcept {
981 return {scope.type, scope.data, scope.mutData, scope.size};
982 }
983
984 GAIA_NODISCARD RuntimeSequenceElement make_sequence_element(const Scope& scope) const noexcept {
985 return {scope.type, scope.data, scope.mutData, scope.size, scope.elementToken};
986 }
987
988 bool commit_current_sequence_element() noexcept {
989 const auto& current = m_stack[m_depth];
990 if (current.sequenceAdapter != nullptr && current.sequenceAdapter->commitElement != nullptr) {
991 if (current.sequenceDepth >= MaxDepth || current.elementDepth >= MaxDepth)
992 return false;
993 RuntimeSequenceScope sequence = make_sequence_scope(m_stack[current.sequenceDepth]);
994 RuntimeSequenceElement element = make_sequence_element(m_stack[current.elementDepth]);
995 if (!current.sequenceAdapter->commitElement(current.sequenceAdapter->ctx, sequence, element))
996 return false;
997 }
998
999 if (current.opaqueAdapter != nullptr && current.opaqueAdapter->commit != nullptr) {
1000 if (current.opaqueDepth >= MaxDepth || current.opaqueValueDepth >= MaxDepth)
1001 return false;
1002 RuntimeOpaqueScope opaque = make_opaque_scope(m_stack[current.opaqueDepth]);
1003 RuntimeOpaqueValue value = make_opaque_value(m_stack[current.opaqueValueDepth]);
1004 if (!current.opaqueAdapter->commit(current.opaqueAdapter->ctx, opaque, value))
1005 return false;
1006 }
1007 return true;
1008 }
1009
1010 GAIA_NODISCARD CursorStatus
1011 validate_primitive(Entity expectedType, bool requireWrite, bool requireScalar) const noexcept {
1012 if (!m_valid)
1013 return CursorStatus::Invalid;
1014 const auto& current = m_stack[m_depth];
1015 if (current.type != expectedType) {
1016 const auto* pCurrentType = m_components != nullptr ? m_components->find(current.type) : nullptr;
1017 if (pCurrentType == nullptr || pCurrentType->primitive_type() != expectedType)
1018 return CursorStatus::TypeMismatch;
1019 }
1020 if (requireScalar && current.elemCount != 1)
1021 return CursorStatus::TypeMismatch;
1022 if (current.size != current.elemSize * current.elemCount)
1023 return CursorStatus::OutOfRange;
1024 if (current.size == 0)
1025 return CursorStatus::TypeMismatch;
1026 if (ptr() == nullptr)
1027 return CursorStatus::Invalid;
1028 if (requireWrite) {
1029 if (!current.writable || m_world == nullptr || m_entity == EntityBad || m_rootType == EntityBad)
1030 return CursorStatus::ReadOnly;
1031 if (mut_ptr() == nullptr)
1032 return CursorStatus::Invalid;
1033 }
1034 return CursorStatus::Ok;
1035 }
1036
1037 template <typename T>
1038 GAIA_NODISCARD CursorResult<T> read_primitive(Entity expectedType) const noexcept {
1039 CursorResult<T> result{};
1040 const auto status = validate_primitive(expectedType, false, true);
1041 if (status != CursorStatus::Ok) {
1042 result.status = status;
1043 return result;
1044 }
1045 if (size() != sizeof(T)) {
1046 result.status = CursorStatus::TypeMismatch;
1047 return result;
1048 }
1049
1050 memcpy(&result.value, ptr(), sizeof(T));
1051 result.status = CursorStatus::Ok;
1052 return result;
1053 }
1054
1055 template <typename T>
1056 GAIA_NODISCARD CursorResult<void> write_primitive(Entity expectedType, const T& value) noexcept {
1057 const auto status = validate_primitive(expectedType, true, true);
1058 if (status != CursorStatus::Ok)
1059 return {status};
1060 if (size() != sizeof(T))
1061 return {CursorStatus::TypeMismatch};
1062
1063 memcpy(mut_ptr(), &value, sizeof(T));
1064 if (!commit_current_sequence_element())
1065 return {CursorStatus::Invalid};
1066 world_finish_write(*m_world, m_rootType, m_entity);
1067 return {CursorStatus::Ok};
1068 }
1069
1070 bool descend(const RuntimeFieldDesc& field, uint32_t fieldIdx = UINT32_MAX) noexcept {
1071 if (!m_valid || m_components == nullptr || m_depth + 1 >= MaxDepth)
1072 return false;
1073
1074 const auto* pType = m_components->find(field.type);
1075 if (pType == nullptr)
1076 return false;
1077
1078 Entity scopeType = field.type;
1079 uint32_t elemSize = pType->comp.size();
1080 uint32_t elemCount = ComponentCacheItem::field_element_count(field);
1081 if (pType->typeKind == RuntimeTypeKind::Array) {
1082 if (field.count != 0)
1083 return false;
1084 const auto elementType = pType->element_type();
1085 const auto* pElementType = m_components->find(elementType);
1086 if (pElementType == nullptr || pType->element_count() == 0)
1087 return false;
1088 scopeType = elementType;
1089 elemSize = pElementType->comp.size();
1090 elemCount = pType->element_count();
1091 }
1092 const auto fieldSize64 = (uint64_t)elemSize * (uint64_t)elemCount;
1093 const auto end = (uint64_t)field.offset + fieldSize64;
1094 if (fieldSize64 > UINT32_MAX || end > m_stack[m_depth].size)
1095 return false;
1096
1097 Scope next{};
1098 next.type = scopeType;
1099 next.size = (uint32_t)fieldSize64;
1100 next.elemSize = elemSize;
1101 next.elemCount = elemCount;
1102 next.sequenceAdapter = m_stack[m_depth].sequenceAdapter;
1103 next.sequenceDepth = m_stack[m_depth].sequenceDepth;
1104 next.elementDepth = m_stack[m_depth].elementDepth;
1105 next.elementToken = m_stack[m_depth].elementToken;
1106 next.opaqueAdapter = m_stack[m_depth].opaqueAdapter;
1107 next.opaqueDepth = m_stack[m_depth].opaqueDepth;
1108 next.opaqueValueDepth = m_stack[m_depth].opaqueValueDepth;
1109 next.opaqueToken = m_stack[m_depth].opaqueToken;
1110 next.writable = m_stack[m_depth].writable;
1111 if (m_rootSoa && m_depth == 0) {
1112 if (fieldIdx == UINT32_MAX || m_readWorld == nullptr)
1113 return false;
1114
1115 if (m_world != nullptr) {
1116 const auto view = world_mut_raw_field(*m_world, m_entity, m_rootType, fieldIdx);
1117 if (!view.valid() || view.size != next.size)
1118 return false;
1119 next.data = view.data;
1120 next.mutData = view.data;
1121 } else {
1122 const auto view = world_get_raw_field(*m_readWorld, m_entity, m_rootType, fieldIdx);
1123 if (!view.valid() || view.size != next.size)
1124 return false;
1125 next.data = view.data;
1126 }
1127 } else {
1128 if (m_stack[m_depth].data != nullptr)
1129 next.data = (const uint8_t*)m_stack[m_depth].data + field.offset;
1130 if (m_stack[m_depth].mutData != nullptr)
1131 next.mutData = (uint8_t*)m_stack[m_depth].mutData + field.offset;
1132 }
1133
1134 m_stack[++m_depth] = next;
1135 return true;
1136 }
1137 };
1138 } // namespace ecs
1139} // namespace gaia
Owns entities, components, archetypes, queries, observers, and systems.
Definition world.h:80
Stack-only cursor over raw component bytes and runtime field metadata.
Definition component_cursor.h:186
static GAIA_NODISCARD ComponentCursor from_soa(World &world, const ComponentCache &components, Entity entity, Entity component, uint32_t size)
Creates a mutable cursor over a non-contiguous SoA component value.
Definition component_cursor.h:272
GAIA_NODISCARD CursorResult< double > f64() const noexcept
Reads the current cursor value as an f64.
Definition component_cursor.h:781
GAIA_NODISCARD RuntimeTypeKind type_kind() const noexcept
Returns the runtime type kind at the current cursor scope.
Definition component_cursor.h:296
GAIA_NODISCARD CursorResult< uint32_t > c8(char *dst, uint32_t cap) const noexcept
Reads the current fixed c8 buffer.
Definition component_cursor.h:597
GAIA_NODISCARD CursorResult< float > f32() const noexcept
Reads the current cursor value as an f32.
Definition component_cursor.h:769
bool field(uint32_t index)
Descends into the reflected field at index.
Definition component_cursor.h:394
GAIA_NODISCARD CursorResult< uint64_t > u64() const noexcept
Reads the current cursor value as a u64.
Definition component_cursor.h:757
GAIA_NODISCARD void * mut_ptr() const noexcept
Definition component_cursor.h:327
GAIA_NODISCARD CursorResult< uint32_t > u32() const noexcept
Reads the current cursor value as a u32.
Definition component_cursor.h:733
GAIA_NODISCARD CursorResult< char16_t > c16() const noexcept
Reads the current cursor value as a scalar c16.
Definition component_cursor.h:647
GAIA_NODISCARD CursorResult< void > f64(double value) noexcept
Writes value to the current cursor value as an f64.
Definition component_cursor.h:787
CursorResult< void > resize(uint32_t count) noexcept
Resizes the current adapted dynamic sequence scope.
Definition component_cursor.h:531
GAIA_NODISCARD CursorResult< void > c8(const char *src, uint32_t len) noexcept
Writes bytes to the current fixed c8 buffer.
Definition component_cursor.h:627
GAIA_NODISCARD CursorResult< int16_t > s16() const noexcept
Reads the current cursor value as an s16.
Definition component_cursor.h:697
GAIA_NODISCARD CursorResult< uint32_t > get_raw(void *data, uint32_t byteCount) const noexcept
Copies exact bytes from the current cursor position.
Definition component_cursor.h:798
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:198
GAIA_NODISCARD CursorResult< uint8_t > u8() const noexcept
Reads the current cursor value as a u8.
Definition component_cursor.h:685
GAIA_NODISCARD uint32_t field_count() const
Definition component_cursor.h:332
bool parent() noexcept
Moves back to the parent cursor scope.
Definition component_cursor.h:552
GAIA_NODISCARD CursorResult< int8_t > s8() const noexcept
Reads the current cursor value as an s8.
Definition component_cursor.h:673
GAIA_NODISCARD CursorResult< void > c8(char value) noexcept
Writes value to the current cursor value as a scalar c8.
Definition component_cursor.h:581
GAIA_NODISCARD uint32_t size() const noexcept
Returns the current payload or field size in bytes.
Definition component_cursor.h:317
GAIA_NODISCARD CursorResult< void > b(bool value) noexcept
Writes value to the current cursor value as a bool.
Definition component_cursor.h:568
GAIA_NODISCARD CursorResult< void > f32(float value) noexcept
Writes value to the current cursor value as an f32.
Definition component_cursor.h:775
GAIA_NODISCARD CursorResult< void > c32(char32_t value) noexcept
Writes value to the current cursor value as a scalar c32.
Definition component_cursor.h:667
GAIA_NODISCARD CursorResult< bool > b() const noexcept
Reads the current cursor value as a bool.
Definition component_cursor.h:561
GAIA_NODISCARD CursorResult< uint16_t > u16() const noexcept
Reads the current cursor value as a u16.
Definition component_cursor.h:709
GAIA_NODISCARD CursorResult< char > c8() const noexcept
Reads the current cursor value as a scalar c8.
Definition component_cursor.h:574
GAIA_NODISCARD CursorResult< int64_t > s64() const noexcept
Reads the current cursor value as an s64.
Definition component_cursor.h:745
GAIA_NODISCARD uint32_t depth() const noexcept
Definition component_cursor.h:285
GAIA_NODISCARD bool valid() const noexcept
Definition component_cursor.h:280
GAIA_NODISCARD CursorResult< void > u16(uint16_t value) noexcept
Writes value to the current cursor value as a u16.
Definition component_cursor.h:715
GAIA_NODISCARD CursorResult< void > c16(char16_t value) noexcept
Writes value to the current cursor value as a scalar c16.
Definition component_cursor.h:654
GAIA_NODISCARD CursorResult< void > s64(int64_t value) noexcept
Writes value to the current cursor value as an s64.
Definition component_cursor.h:751
GAIA_NODISCARD Entity type() const noexcept
Definition component_cursor.h:290
GAIA_NODISCARD CursorResult< char32_t > c32() const noexcept
Reads the current cursor value as a scalar c32.
Definition component_cursor.h:660
GAIA_NODISCARD CursorResult< void > u32(uint32_t value) noexcept
Writes value to the current cursor value as a u32.
Definition component_cursor.h:739
GAIA_NODISCARD CursorResult< void > u64(uint64_t value) noexcept
Writes value to the current cursor value as a u64.
Definition component_cursor.h:763
static GAIA_NODISCARD ComponentCursor from_soa(const World &world, const ComponentCache &components, Entity entity, Entity component, uint32_t size)
Creates a read-only cursor over a non-contiguous SoA component value.
Definition component_cursor.h:249
GAIA_NODISCARD const void * ptr() const noexcept
Definition component_cursor.h:322
GAIA_NODISCARD CursorResult< uint32_t > count() const noexcept
Returns the element count for the current fixed or adapted sequence scope.
Definition component_cursor.h:347
bool elem(uint32_t index) noexcept
Descends into element index of the current fixed inline or named array scope.
Definition component_cursor.h:435
GAIA_NODISCARD CursorResult< int32_t > s32() const noexcept
Reads the current cursor value as an s32.
Definition component_cursor.h:721
GAIA_NODISCARD Entity element_type() const noexcept
Returns the element type for the current sequence scope, or EntityBad otherwise.
Definition component_cursor.h:310
static constexpr uint32_t MaxDepth
Maximum nested field depth tracked by the cursor.
Definition component_cursor.h:188
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:220
GAIA_NODISCARD CursorResult< void > s8(int8_t value) noexcept
Writes value to the current cursor value as an s8.
Definition component_cursor.h:679
bool field(util::str_view name)
Descends into the reflected field named name.
Definition component_cursor.h:411
GAIA_NODISCARD CursorResult< void > u8(uint8_t value) noexcept
Writes value to the current cursor value as a u8.
Definition component_cursor.h:691
CursorResult< void > set_raw(const void *data, uint32_t byteCount) noexcept
Writes exact bytes to the current cursor position.
Definition component_cursor.h:828
GAIA_NODISCARD CursorResult< uint32_t > c8(char *dst, uint32_t cap) noexcept
Reads the current fixed c8 buffer.
Definition component_cursor.h:589
GAIA_NODISCARD Entity opaque_as_type() const noexcept
Returns the semantic runtime type exposed by the current opaque scope, or EntityBad otherwise.
Definition component_cursor.h:303
GAIA_NODISCARD CursorResult< void > s16(int16_t value) noexcept
Writes value to the current cursor value as an s16.
Definition component_cursor.h:703
GAIA_NODISCARD CursorResult< void > s32(int32_t value) noexcept
Writes value to the current cursor value as an s32.
Definition component_cursor.h:727
Non-owning mutable view over raw component bytes on an entity.
Definition component_cursor.h:64
void * data
Raw component payload bytes. Null for tags and invalid views.
Definition component_cursor.h:66
uint32_t flags
Bitmask of ComponentRawViewFlags values.
Definition component_cursor.h:70
uint32_t size
Payload size in bytes.
Definition component_cursor.h:68
GAIA_NODISCARD bool valid() const noexcept
Definition component_cursor.h:73
Non-owning read-only view over raw component bytes on an entity.
Definition component_cursor.h:38
uint32_t size
Payload size in bytes.
Definition component_cursor.h:42
uint32_t flags
Bitmask of ComponentRawViewFlags values.
Definition component_cursor.h:44
GAIA_NODISCARD bool valid() const noexcept
Definition component_cursor.h:47
const void * data
Raw component payload bytes. Null for tags and invalid views.
Definition component_cursor.h:40
GAIA_NODISCARD bool ok() const noexcept
Definition component_cursor.h:151
Result of reading or writing through a ComponentCursor.
Definition component_cursor.h:127
GAIA_NODISCARD bool ok() const noexcept
Definition component_cursor.h:134
T value
Value returned by a successful read operation.
Definition component_cursor.h:131
CursorStatus status
Operation status.
Definition component_cursor.h:129
Identifier of an entity or component instance in the world. Packs the entity index,...
Definition id.h:296
InternalData data
Structured view of the packed value.
Definition id.h:328
GAIA_NODISCARD constexpr bool pair() const noexcept
Whether this id refers to a relationship pair.
Definition id.h:377
Runtime field metadata parameterized by its string representation. StringType selects borrowed regist...
Definition component_desc.h:99
Runtime sequence element returned by an adapter.
Definition component_desc.h:162
Entity type
Runtime type entity for the selected element value.
Definition component_desc.h:164
Runtime sequence adapter input scope. The byte layout is owned by the adapter.
Definition component_desc.h:150
Entity type
Runtime type entity for the selected sequence value.
Definition component_desc.h:152
Lightweight non-owning string view over a character sequence.
Definition str.h:13