2#include "gaia/config/config.h"
9#include "gaia/cnt/map.h"
10#include "gaia/core/hashing_string.h"
11#include "gaia/ecs/component.h"
12#include "gaia/ecs/component_cache_item.h"
13#include "gaia/ecs/component_desc.h"
14#include "gaia/ecs/id.h"
15#include "gaia/meta/type_info.h"
16#include "gaia/util/logging.h"
17#include "gaia/util/str.h"
25 class GAIA_API ComponentCache final {
28 struct ResolvedLookupEntry {
29 const ComponentCacheItem* pItem =
nullptr;
34 cnt::map<uint32_t, const ComponentCacheItem*> m_compByEntityId;
36 SymbolTable m_symbols;
38 cnt::map<ComponentLookupHash, const ComponentCacheItem*> m_compByTypeHash;
41 cnt::map<ComponentCacheItem::SymbolLookupKey, const ComponentCacheItem*> m_compBySymbol;
44 cnt::map<ComponentCacheItem::SymbolLookupKey, ResolvedLookupEntry> m_compByPath;
47 cnt::map<ComponentCacheItem::SymbolLookupKey, ResolvedLookupEntry> m_compByShortSymbol;
56 m_compByTypeHash.clear();
57 m_compBySymbol.clear();
59 m_compByShortSymbol.clear();
61 for (
auto [entityId, pItem]: m_compByEntityId) {
63 ComponentCacheItem::destroy(
const_cast<ComponentCacheItem*
>(pItem));
66 m_compByEntityId.clear();
70 GAIA_NODISCARD
static bool is_internal_symbol(util::str_view symbol)
noexcept {
71 constexpr char InternalPrefix[] =
"gaia::ecs::";
72 constexpr uint32_t InternalPrefixLen = (uint32_t)(
sizeof(InternalPrefix) - 1);
73 return symbol.size() >= InternalPrefixLen && memcmp(symbol.data(), InternalPrefix, InternalPrefixLen) == 0;
76 static ComponentCacheItem::SymbolLookupKey short_name_key(
const ComponentCacheItem& item)
noexcept {
77 const auto symbol = item.symbol_name();
78 const auto* pName = symbol.data();
79 const auto len = symbol.size();
80 for (uint32_t i = len; i > 1; --i) {
81 const auto idx = i - 1;
82 if (pName[idx] !=
':' || pName[idx - 1] !=
':')
85 return ComponentCacheItem::SymbolLookupKey(pName + idx + 1, len - idx - 1, 0);
88 return ComponentCacheItem::SymbolLookupKey();
91 GAIA_NODISCARD
static util::str_view short_symbol_view(
const ComponentCacheItem& item)
noexcept {
92 const auto symbol = item.symbol_name();
93 if (is_internal_symbol(symbol))
96 const auto shortName = short_name_key(item);
97 if (shortName.str() ==
nullptr || shortName.len() == 0)
100 return util::str_view(shortName.str(), shortName.len());
103 GAIA_NODISCARD
static ComponentCacheItem::SymbolLookupKey lookup_key(util::str_view value)
noexcept {
104 return value.empty() ? ComponentCacheItem::SymbolLookupKey()
105 : ComponentCacheItem::SymbolLookupKey(value.data(), value.size(), 0);
108 GAIA_NODISCARD
static util::str_view normalize_name_view(
const char* name, uint32_t len = 0) noexcept {
112 const auto l = len == 0 ? (uint32_t)GAIA_STRLEN(name, ComponentCacheItem::MaxNameLength) : len;
113 GAIA_ASSERT(l < ComponentCacheItem::MaxNameLength);
114 return util::str_view(name, l);
117#if GAIA_ASSERT_ENABLED
120 void validate_runtime_type(
const ecs::ComponentDesc& desc)
const noexcept {
121 const auto& runtimeType = desc.runtimeType;
122 ser::serialization_type_id type = ser::serialization_type_id::ignore;
123 if (runtimeType.typeKind == RuntimeTypeKind::Enum || runtimeType.typeKind == RuntimeTypeKind::Bitmask) {
124 GAIA_ASSERT(runtime_primitive_serialization_type(runtimeType.underlyingType, type));
125 GAIA_ASSERT(runtimeType.elementType == EntityBad);
126 GAIA_ASSERT(runtimeType.elementCount == 0);
127 GAIA_ASSERT(runtimeType.opaqueAsType == EntityBad);
131 if (runtimeType.typeKind == RuntimeTypeKind::Array) {
132 GAIA_ASSERT(runtimeType.underlyingType == EntityBad);
133 GAIA_ASSERT(runtimeType.elementType != EntityBad);
134 GAIA_ASSERT(runtimeType.elementCount > 0);
135 GAIA_ASSERT(runtimeType.fields ==
nullptr);
136 GAIA_ASSERT(runtimeType.fieldCount == 0);
137 GAIA_ASSERT(runtimeType.constants ==
nullptr);
138 GAIA_ASSERT(runtimeType.constantCount == 0);
139 GAIA_ASSERT(runtimeType.opaqueAsType == EntityBad);
140 const auto* pElementType = find(runtimeType.elementType);
141 GAIA_ASSERT(pElementType !=
nullptr);
142 const auto elementSize = pElementType !=
nullptr ? pElementType->comp.size() : 0;
143 const auto elementAlignment = pElementType !=
nullptr ? pElementType->comp.alig() : 0;
144 GAIA_ASSERT(elementSize > 0);
145 if (elementSize > 0) {
146 GAIA_ASSERT(runtimeType.elementCount <= UINT32_MAX / elementSize);
147 GAIA_ASSERT(desc.size == elementSize * runtimeType.elementCount);
149 GAIA_ASSERT(desc.alig == elementAlignment);
153 if (runtimeType.typeKind == RuntimeTypeKind::Vector) {
154 GAIA_ASSERT(runtimeType.underlyingType == EntityBad);
155 GAIA_ASSERT(runtimeType.elementType != EntityBad);
156 GAIA_ASSERT(runtimeType.elementCount == 0);
157 GAIA_ASSERT(runtimeType.fields ==
nullptr);
158 GAIA_ASSERT(runtimeType.fieldCount == 0);
159 GAIA_ASSERT(runtimeType.constants ==
nullptr);
160 GAIA_ASSERT(runtimeType.constantCount == 0);
161 GAIA_ASSERT(runtimeType.opaqueAsType == EntityBad);
162 if (runtimeType.sequenceAdapter ==
nullptr) {
163 GAIA_ASSERT(desc.size == 0);
164 GAIA_ASSERT(desc.soa == 0);
166 GAIA_ASSERT(runtimeType.sequenceAdapter->count !=
nullptr);
167 GAIA_ASSERT(runtimeType.sequenceAdapter->element !=
nullptr);
169 GAIA_ASSERT(find(runtimeType.elementType) !=
nullptr);
170 #if GAIA_JSON_ENABLED
171 GAIA_ASSERT(runtimeType.jsonEncoding != RuntimeJsonEncoding::Utf8String || runtimeType.elementType == Char8);
175 #if GAIA_JSON_ENABLED
176 GAIA_ASSERT(runtimeType.jsonEncoding == RuntimeJsonEncoding::Default);
179 if (runtimeType.typeKind == RuntimeTypeKind::Opaque) {
180 GAIA_ASSERT(runtimeType.underlyingType == EntityBad);
181 GAIA_ASSERT(runtimeType.elementType == EntityBad);
182 GAIA_ASSERT(runtimeType.elementCount == 0);
183 GAIA_ASSERT(runtimeType.fields ==
nullptr);
184 GAIA_ASSERT(runtimeType.fieldCount == 0);
185 GAIA_ASSERT(runtimeType.constants ==
nullptr);
186 GAIA_ASSERT(runtimeType.constantCount == 0);
187 GAIA_ASSERT(runtimeType.opaqueAsType != EntityBad);
188 if (runtimeType.opaqueAdapter !=
nullptr)
189 GAIA_ASSERT(runtimeType.opaqueAdapter->project !=
nullptr);
190 GAIA_ASSERT(find(runtimeType.opaqueAsType) !=
nullptr);
194 GAIA_ASSERT(runtimeType.underlyingType == EntityBad);
195 GAIA_ASSERT(runtimeType.elementType == EntityBad);
196 GAIA_ASSERT(runtimeType.elementCount == 0);
197 GAIA_ASSERT(runtimeType.opaqueAsType == EntityBad);
198 GAIA_ASSERT(runtimeType.sequenceAdapter ==
nullptr);
199 GAIA_ASSERT(runtimeType.opaqueAdapter ==
nullptr);
204 void validate_runtime_fields(
const ecs::ComponentDesc& desc)
const noexcept {
205 const auto& runtimeType = desc.runtimeType;
206 if (runtimeType.fieldCount == 0)
209 GAIA_ASSERT(runtimeType.fields !=
nullptr);
210 if (runtimeType.fields ==
nullptr)
213 GAIA_FOR(runtimeType.fieldCount) {
214 const auto& field = runtimeType.fields[i];
215 GAIA_ASSERT(!field.name.empty());
216 GAIA_ASSERT(field.name.size() < ComponentCacheItem::MaxNameLength);
217 GAIA_ASSERT(field.unit.size() < RuntimeFieldDesc::MaxUnitLength);
218 GAIA_ASSERT(field.type != EntityBad);
220 (field.flags & (RuntimeFieldFlag_HasMinimum | RuntimeFieldFlag_HasMaximum)) !=
221 (RuntimeFieldFlag_HasMinimum | RuntimeFieldFlag_HasMaximum) ||
222 field.minimum <= field.maximum);
223 GAIA_ASSERT((field.flags & RuntimeFieldFlag_HasStep) == 0 || field.step > 0.0);
224 #if GAIA_JSON_ENABLED
226 field.jsonEncoding != RuntimeJsonEncoding::Utf8String || (field.type == Char8 && field.count != 0));
229 const auto* pType = find(field.type);
230 GAIA_ASSERT(pType !=
nullptr);
231 GAIA_ASSERT(pType ==
nullptr || pType->typeKind != RuntimeTypeKind::Array || field.count == 0);
232 const auto elementSize = pType !=
nullptr ? pType->comp.size() : 0;
233 GAIA_ASSERT(elementSize > 0);
234 const auto elementCount = ComponentCacheItem::field_element_count(field);
235 GAIA_ASSERT(elementCount > 0);
236 GAIA_ASSERT(field.offset <= desc.size);
237 const auto availableSize = field.offset <= desc.size ? desc.size - field.offset : 0;
238 GAIA_ASSERT(elementSize <= availableSize);
239 GAIA_ASSERT(elementCount <= availableSize / elementSize);
241 GAIA_FOR2_(i + 1, runtimeType.fieldCount, otherIdx) {
242 const auto& other = runtimeType.fields[otherIdx];
243 const bool sameLen = field.name.size() == other.name.size();
244 const bool sameName = sameLen && field.name.data() !=
nullptr && other.name.data() !=
nullptr &&
245 strncmp(field.name.data(), other.name.data(), field.name.size()) == 0;
246 GAIA_ASSERT(!sameName);
253 static void validate_runtime_constants(
const ecs::ComponentDesc& desc)
noexcept {
254 const auto& runtimeType = desc.runtimeType;
255 if (runtimeType.constantCount == 0)
258 GAIA_ASSERT(runtimeType.typeKind == RuntimeTypeKind::Enum || runtimeType.typeKind == RuntimeTypeKind::Bitmask);
259 GAIA_ASSERT(runtimeType.constants !=
nullptr);
260 if (runtimeType.constants ==
nullptr)
263 GAIA_FOR(runtimeType.constantCount) {
264 const auto& constant = runtimeType.constants[i];
265 GAIA_ASSERT(!constant.name.empty());
266 GAIA_ASSERT(constant.name.size() < ComponentCacheItem::MaxNameLength);
268 GAIA_FOR2_(i + 1, runtimeType.constantCount, otherIdx) {
269 const auto& other = runtimeType.constants[otherIdx];
270 const bool sameLen = constant.name.size() == other.name.size();
271 const bool sameName = sameLen && constant.name.data() !=
nullptr && other.name.data() !=
nullptr &&
272 strncmp(constant.name.data(), other.name.data(), constant.name.size()) == 0;
273 GAIA_ASSERT(!sameName);
274 GAIA_ASSERT(constant.value != other.value);
283 validate_typed_runtime_type(
const ComponentCacheItem& item,
const RuntimeTypeDesc& runtimeType)
const noexcept {
284 ecs::ComponentDesc desc{};
285 desc.size = item.comp.size();
286 desc.alig = item.comp.alig();
287 desc.storageType = item.comp.storage_type();
288 desc.soa = item.comp.soa();
289 desc.pSoaSizes = item.soaSizes;
290 desc.runtimeType = runtimeType;
291 validate_runtime_type(desc);
292 validate_runtime_fields(desc);
293 validate_runtime_constants(desc);
297 static bool build_default_path(util::str& out, util::str_view symbol) {
299 bool changed =
false;
300 out.reserve(symbol.size());
301 for (uint32_t i = 0; i < symbol.size(); ++i) {
302 if (i + 1 < symbol.size() && symbol.data()[i] ==
':' && symbol.data()[i + 1] ==
':') {
309 out.append(symbol.data()[i]);
323 static bool build_scoped_path(util::str& out, util::str_view scopePath, util::str_view leaf) {
325 if (scopePath.empty() || leaf.empty())
328 out.reserve(scopePath.size() + 1 + leaf.size());
329 out.append(scopePath);
338 static void initialize_names(ComponentCacheItem& item, util::str_view scopePath = {}) {
340 const auto symbol = item.symbol_name();
341 if (is_internal_symbol(symbol))
344 const auto shortName = short_name_key(item);
345 const bool hasShortName =
346 shortName.str() !=
nullptr && shortName.len() != 0 && shortName.len() != symbol.size();
347 const auto leaf = hasShortName ? util::str_view{shortName.str(), shortName.len()} : symbol;
349 if (!build_scoped_path(item.path, scopePath, leaf))
350 (void)build_default_path(item.path, symbol);
353 static void add_lookup_mapping(
354 cnt::map<ComponentCacheItem::SymbolLookupKey, ResolvedLookupEntry>& map, util::str_view view,
355 const ComponentCacheItem& item) {
359 const auto key = lookup_key(view);
360 const auto it = map.find(key);
361 if (it == map.end()) {
362 map.emplace(key, ResolvedLookupEntry{&item, 1});
366 auto& entry = it->second;
367 GAIA_ASSERT(entry.matches != 0);
371 void add_path_mapping(
const ComponentCacheItem& item) {
372 add_lookup_mapping(m_compByPath, item.path.view(), item);
375 static void push_unique_entity(cnt::darray<Entity>& out, Entity entity) {
376 if (entity == EntityBad)
379 for (
const auto existing: out) {
380 if (existing == entity)
384 out.push_back(entity);
387 void remove_path_mapping(
const ComponentCacheItem& item) {
388 const auto pathValue = item.path.view();
389 if (pathValue.empty())
392 const auto key = lookup_key(pathValue);
393 const auto it = m_compByPath.find(key);
394 if (it == m_compByPath.end())
397 auto& entry = it->second;
398 GAIA_ASSERT(entry.matches != 0);
400 if (entry.matches == 1) {
401 GAIA_ASSERT(entry.pItem == &item);
402 m_compByPath.erase(it);
406 const auto remainingMatches = entry.matches - 1;
407 if (entry.pItem != &item) {
408 entry.matches = remainingMatches;
412 const ComponentCacheItem* pReplacement =
nullptr;
413 for (
const auto& [entityId, pItem]: m_compByEntityId) {
415 GAIA_ASSERT(pItem !=
nullptr);
419 if (pItem->path.view() != pathValue)
422 pReplacement = pItem;
426 GAIA_ASSERT(pReplacement !=
nullptr);
428 m_compByPath.erase(it);
429 if (pReplacement !=
nullptr)
430 m_compByPath.emplace(
431 lookup_key(pReplacement->path.view()), ResolvedLookupEntry{pReplacement, remainingMatches});
437 void add_name_mappings(ComponentCacheItem& item, util::str_view scopePath) {
438 m_compBySymbol.emplace(lookup_key(item.symbol_name()), &item);
439 initialize_names(item, scopePath);
440 add_path_mapping(item);
441 add_lookup_mapping(m_compByShortSymbol, short_symbol_view(item), item);
448 ComponentCacheItem& add_item(
const ComponentCacheItem* pItem, util::str_view scopePath) {
449 GAIA_ASSERT(pItem !=
nullptr);
450 GAIA_ASSERT(pItem->entity.id() == pItem->comp.id());
451 m_compByEntityId.emplace(pItem->entity.id(), pItem);
453 auto& item = *
const_cast<ComponentCacheItem*
>(pItem);
454 item.m_ownerCache =
this;
455 add_name_mappings(item, scopePath);
462 GAIA_NODISCARD
const ComponentCacheItem* find_item(ComponentLookupHash hash)
const noexcept {
463 const auto it = m_compByTypeHash.find(hash);
464 return it != m_compByTypeHash.end() ? it->second :
nullptr;
470 template <
typename T>
471 void add_hash_mapping(
const ComponentCacheItem& item) {
472 const auto hash = detail::ComponentDesc<T>::hash_lookup();
473 const auto it = m_compByTypeHash.find(hash);
474 if (it == m_compByTypeHash.end()) {
475 m_compByTypeHash.emplace(hash, &item);
479 GAIA_ASSERT(it->second == &item);
483 ComponentCache() =
default;
489 ComponentCache(ComponentCache&&) =
delete;
490 ComponentCache(
const ComponentCache&) =
delete;
491 ComponentCache& operator=(ComponentCache&&) =
delete;
492 ComponentCache& operator=(
const ComponentCache&) =
delete;
496 template <
typename T>
497 GAIA_NODISCARD GAIA_FORCEINLINE
const ComponentCacheItem& add(Entity entity, util::str_view scopePath = {}) {
498 static_assert(!is_pair<T>::value);
499 GAIA_ASSERT(!entity.pair());
501 const auto* pItem = find_item(detail::ComponentDesc<T>::hash_lookup());
502 if (pItem !=
nullptr)
505 const auto* pNewItem = ComponentCacheItem::create<T>(entity, m_symbols);
506 GAIA_ASSERT(entity.id() == pNewItem->comp.id());
507 const auto& item = add_item(pNewItem, scopePath);
508 add_hash_mapping<T>(item);
519 template <
typename T>
520 GAIA_NODISCARD GAIA_FORCEINLINE
const ComponentCacheItem&
521 add(Entity entity,
const RuntimeTypeDesc& runtimeType, util::str_view scopePath = {}) {
522 static_assert(!is_pair<T>::value);
523 GAIA_ASSERT(!entity.pair());
525 const auto* pItem = find_item(detail::ComponentDesc<T>::hash_lookup());
526 if (pItem !=
nullptr)
529 const auto* pNewItem = ComponentCacheItem::create<T>(entity, m_symbols, runtimeType);
530 GAIA_ASSERT(entity.id() == pNewItem->comp.id());
531#if GAIA_ASSERT_ENABLED
532 validate_typed_runtime_type(*pNewItem, runtimeType);
534 const auto& item = add_item(pNewItem, scopePath);
535 add_hash_mapping<T>(item);
544 GAIA_NODISCARD ComponentCacheItem&
545 add(Entity entity,
const ecs::ComponentDesc& desc, util::str_view scopePath = {}) {
546 GAIA_ASSERT(entity != EntityBad);
547 GAIA_ASSERT(!entity.pair());
548 GAIA_ASSERT(!desc.name.empty());
549 GAIA_ASSERT(desc.name.size() < ComponentCacheItem::MaxNameLength);
552 const auto* pExisting = symbol(desc.name);
553 if (pExisting !=
nullptr)
554 return *find(pExisting->entity);
557#if GAIA_ASSERT_ENABLED
558 validate_runtime_type(desc);
559 validate_runtime_fields(desc);
560 validate_runtime_constants(desc);
563 const auto* pItem = ComponentCacheItem::create(entity, m_symbols, desc);
564 GAIA_ASSERT(entity.id() == pItem->comp.id());
565 return add_item(pItem, scopePath);
571 GAIA_NODISCARD
const ComponentCacheItem* find(Entity entity)
const noexcept {
575 const auto it = m_compByEntityId.find(entity.id());
576 return it != m_compByEntityId.end() ? it->second :
nullptr;
583 GAIA_NODISCARD
const ComponentCacheItem* find_pair_payload(Entity pair)
const noexcept {
587 const auto relationIt = m_compByEntityId.find(pair.id());
588 const auto* pRelation = relationIt != m_compByEntityId.end() ? relationIt->second :
nullptr;
589 if (pRelation !=
nullptr && pRelation->comp.size() != 0U)
592 const auto targetIt = m_compByEntityId.find(pair.gen());
593 const auto* pTarget = targetIt != m_compByEntityId.end() ? targetIt->second :
nullptr;
594 return pTarget !=
nullptr && pTarget->comp.size() != 0U ? pTarget : pRelation;
600 GAIA_NODISCARD ComponentCacheItem* find(Entity entity)
noexcept {
601 return const_cast<ComponentCacheItem*
>(
const_cast<const ComponentCache*
>(
this)->find(entity));
608 GAIA_NODISCARD
const ComponentCacheItem& get(Entity entity)
const noexcept {
609 GAIA_ASSERT(!entity.pair());
610 const auto* pItem = find(entity);
611 GAIA_ASSERT(pItem !=
nullptr);
619 GAIA_NODISCARD ComponentCacheItem& get(Entity entity)
noexcept {
620 auto* pItem = find(entity);
621 GAIA_ASSERT(pItem !=
nullptr);
629 GAIA_NODISCARD util::str_view symbol_name(
const ComponentCacheItem& item)
const noexcept {
630 return item.symbol_name();
636 GAIA_NODISCARD util::str_view path_name(
const ComponentCacheItem& item)
const noexcept {
637 return item.path.view();
645 bool path(ComponentCacheItem& item, util::str_view name)
noexcept {
646 if (path_name(item) == name)
650 remove_path_mapping(item);
655 if (name.size() >= ComponentCacheItem::MaxNameLength)
658 remove_path_mapping(item);
659 item.path.assign(name);
660 add_path_mapping(item);
670 bool path(ComponentCacheItem& item,
const char* name, uint32_t len = 0) noexcept {
671 return path(item, normalize_name_view(name, len));
677 GAIA_NODISCARD
const ComponentCacheItem* symbol(util::str_view name)
const noexcept {
678 GAIA_ASSERT(name.size() < ComponentCacheItem::MaxNameLength);
679 const auto it = m_compBySymbol.find(lookup_key(name));
680 return it != m_compBySymbol.end() ? it->second :
nullptr;
687 GAIA_NODISCARD
const ComponentCacheItem* symbol(
const char* name, uint32_t len = 0) const noexcept {
688 GAIA_ASSERT(name !=
nullptr);
689 return symbol(normalize_name_view(name, len));
695 GAIA_NODISCARD
const ComponentCacheItem* path(util::str_view name)
const noexcept {
696 GAIA_ASSERT(name.size() < ComponentCacheItem::MaxNameLength);
697 const auto it = m_compByPath.find(lookup_key(name));
698 if (it == m_compByPath.end())
701 return it->second.matches == 1 ? it->second.pItem :
nullptr;
708 GAIA_NODISCARD
const ComponentCacheItem* path(
const char* name, uint32_t len = 0) const noexcept {
709 GAIA_ASSERT(name !=
nullptr);
710 return path(normalize_name_view(name, len));
717 GAIA_NODISCARD
const ComponentCacheItem* short_symbol(util::str_view name)
const noexcept {
718 GAIA_ASSERT(name.size() < ComponentCacheItem::MaxNameLength);
719 const auto it = m_compByShortSymbol.find(lookup_key(name));
720 if (it == m_compByShortSymbol.end())
723 return it->second.matches == 1 ? it->second.pItem :
nullptr;
731 GAIA_NODISCARD
const ComponentCacheItem* short_symbol(
const char* name, uint32_t len = 0) const noexcept {
732 GAIA_ASSERT(name !=
nullptr);
733 return short_symbol(normalize_name_view(name, len));
740 void add_path_matches(cnt::darray<Entity>& out, util::str_view name)
const {
744 const auto it = m_compByPath.find(lookup_key(name));
745 if (it == m_compByPath.end())
748 const auto& entry = it->second;
749 GAIA_ASSERT(entry.matches != 0);
751 if (entry.matches == 1) {
752 GAIA_ASSERT(entry.pItem !=
nullptr);
753 push_unique_entity(out, entry.pItem->entity);
757 for (
const auto& [entityId, pItem]: m_compByEntityId) {
759 GAIA_ASSERT(pItem !=
nullptr);
760 if (pItem->path.view() == name)
761 push_unique_entity(out, pItem->entity);
769 template <
typename T>
770 GAIA_NODISCARD
const ComponentCacheItem* find() const noexcept {
771 static_assert(!is_pair<T>::value);
772 return find_item(detail::ComponentDesc<T>::hash_lookup());
778 template <
typename T>
779 GAIA_NODISCARD
const ComponentCacheItem& get() const noexcept {
780 static_assert(!is_pair<T>::value);
781 const auto* pItem = find<T>();
782 GAIA_ASSERT(pItem !=
nullptr);
791 static ComponentCacheItem::Hooks& hooks(
const ComponentCacheItem& cacheItem)
noexcept {
792 return const_cast<ComponentCacheItem&
>(cacheItem).hooks();
798 const auto registeredTypes = m_compByEntityId.size();
799 GAIA_LOG_N(
"Registered components: %u", registeredTypes);
801 auto logDesc = [](
const ComponentCacheItem& item) {
802 const auto symbol = item.symbol_name();
804 " hash:%016" PRIx64
", size:%3u B, align:%3u B, [%u:%u] %.*s [%s]", item.hashLookup.hash,
805 item.comp.size(), item.comp.alig(), item.entity.id(), item.entity.gen(), (
int)symbol.size(),
806 symbol.data(), EntityKindString[item.entity.kind()]);
808 for (
const auto& [entityId, pItem]: m_compByEntityId) {
810 GAIA_ASSERT(pItem !=
nullptr);