29 static constexpr uint32_t MaxNameLength = 256;
33 using FuncCtor = void(
void*, uint32_t);
34 using FuncDtor = void(
void*, uint32_t);
35 using FuncFrom = void(
void*,
void*, uint32_t, uint32_t, uint32_t, uint32_t);
36 using FuncCopy = void(
void*,
const void*, uint32_t, uint32_t, uint32_t, uint32_t);
37 using FuncMove = void(
void*,
void*, uint32_t, uint32_t, uint32_t, uint32_t);
38 using FuncSwap = void(
void*,
void*, uint32_t, uint32_t, uint32_t, uint32_t);
39 using FuncCmp = bool(
const void*,
const void*);
41 using FuncSave = void(
ser::serializer&,
const void*, uint32_t, uint32_t, uint32_t);
42 using FuncLoad = void(
ser::serializer&,
void*, uint32_t, uint32_t, uint32_t);
59 DataStorageType storageType = DataStorageType::Table;
63 const uint8_t* pSoaSizes =
nullptr;
67 RuntimeTypeKind typeKind = RuntimeTypeKind::Struct;
69 RuntimePrimitiveKind primitiveKind = RuntimePrimitiveKind::None;
71 FuncCtor* funcCtor =
nullptr;
72 FuncMove* funcMoveCtor =
nullptr;
73 FuncCopy* funcCopyCtor =
nullptr;
74 FuncDtor* funcDtor =
nullptr;
75 FuncCopy* funcCopy =
nullptr;
76 FuncMove* funcMove =
nullptr;
77 FuncSwap* funcSwap =
nullptr;
78 FuncCmp* funcCmp =
nullptr;
79 FuncSave* funcSave =
nullptr;
80 FuncLoad* funcLoad =
nullptr;
90 uint8_t soaSizes[meta::StructToTupleMaxTypes];
97 FuncCtor* func_ctor{};
99 FuncMove* func_move_ctor{};
101 FuncCopy* func_copy_ctor{};
103 FuncDtor* func_dtor{};
105 FuncCopy* func_copy{};
107 FuncMove* func_move{};
109 FuncSwap* func_swap{};
113 FuncSave* func_save{};
115 FuncLoad* func_load{};
117 RuntimeTypeKind typeKind = RuntimeTypeKind::Struct;
119 RuntimePrimitiveKind primitiveKind = RuntimePrimitiveKind::None;
123 #if GAIA_ENABLE_ADD_DEL_HOOKS
125 FuncOnAdd* func_add{};
127 FuncOnDel* func_del{};
129 #if GAIA_ENABLE_SET_HOOKS
131 FuncOnSet* func_set{};
149 ctor_move(
void* pDst,
void* pSrc, uint32_t idxDst, uint32_t idxSrc, uint32_t sizeDst, uint32_t sizeSrc)
const {
150 GAIA_ASSERT(pSrc !=
nullptr && pDst !=
nullptr);
151 GAIA_ASSERT(pSrc != pDst || idxSrc != idxDst);
152 if (func_move_ctor !=
nullptr) {
153 func_move_ctor(pDst, pSrc, idxDst, idxSrc, sizeDst, sizeSrc);
157 if (comp.soa() != 0 || comp.size() == 0)
160 auto* pD = (uint8_t*)pDst + ((uintptr_t)comp.size() * idxDst);
161 auto* pS = (uint8_t*)pSrc + ((uintptr_t)comp.size() * idxSrc);
162 memmove((
void*)pD, (
const void*)pS, comp.size());
166 void* pDst,
const void* pSrc, uint32_t idxDst, uint32_t idxSrc, uint32_t sizeDst, uint32_t sizeSrc)
const {
167 GAIA_ASSERT(pSrc !=
nullptr && pDst !=
nullptr);
168 GAIA_ASSERT(pSrc != pDst || idxSrc != idxDst);
169 if (func_copy_ctor !=
nullptr) {
170 func_copy_ctor(pDst, pSrc, idxDst, idxSrc, sizeDst, sizeSrc);
174 if (comp.soa() != 0 || comp.size() == 0)
177 auto* pD = (uint8_t*)pDst + ((uintptr_t)comp.size() * idxDst);
178 auto* pS = (
const uint8_t*)pSrc + ((uintptr_t)comp.size() * idxSrc);
179 memcpy((
void*)pD, (
const void*)pS, comp.size());
182 void dtor(
void* pSrc)
const {
183 if (func_dtor !=
nullptr)
188 copy(
void* pDst,
const void* pSrc, uint32_t idxDst, uint32_t idxSrc, uint32_t sizeDst, uint32_t sizeSrc)
const {
189 GAIA_ASSERT(pSrc !=
nullptr && pDst !=
nullptr);
190 GAIA_ASSERT(pSrc != pDst || idxSrc != idxDst);
191 if (func_copy !=
nullptr) {
192 func_copy(pDst, pSrc, idxDst, idxSrc, sizeDst, sizeSrc);
196 if (comp.soa() != 0 || comp.size() == 0)
199 auto* pD = (uint8_t*)pDst + ((uintptr_t)comp.size() * idxDst);
200 auto* pS = (
const uint8_t*)pSrc + ((uintptr_t)comp.size() * idxSrc);
201 memcpy((
void*)pD, (
const void*)pS, comp.size());
204 void move(
void* pDst,
void* pSrc, uint32_t idxDst, uint32_t idxSrc, uint32_t sizeDst, uint32_t sizeSrc)
const {
205 GAIA_ASSERT(pSrc !=
nullptr && pDst !=
nullptr);
206 GAIA_ASSERT(pSrc != pDst || idxSrc != idxDst);
207 if (func_move !=
nullptr) {
208 func_move(pDst, pSrc, idxDst, idxSrc, sizeDst, sizeSrc);
212 if (comp.soa() != 0 || comp.size() == 0)
215 auto* pD = (uint8_t*)pDst + ((uintptr_t)comp.size() * idxDst);
216 auto* pS = (uint8_t*)pSrc + ((uintptr_t)comp.size() * idxSrc);
217 memmove((
void*)pD, (
const void*)pS, comp.size());
221 swap(
void* pLeft,
void* pRight, uint32_t idxLeft, uint32_t idxRight, uint32_t sizeDst, uint32_t sizeSrc)
const {
222 GAIA_ASSERT(pLeft !=
nullptr && pRight !=
nullptr);
223 if (func_swap !=
nullptr) {
224 func_swap(pLeft, pRight, idxLeft, idxRight, sizeDst, sizeSrc);
228 if (comp.soa() != 0 || comp.size() == 0)
231 auto* l = (uint8_t*)pLeft + ((uintptr_t)comp.size() * idxLeft);
232 auto* r = (uint8_t*)pRight + ((uintptr_t)comp.size() * idxRight);
233 GAIA_FOR(comp.size()) {
234 const auto tmp = l[i];
240 bool cmp(
const void* pLeft,
const void* pRight)
const {
241 GAIA_ASSERT(pLeft != pRight);
242 if (func_cmp !=
nullptr)
243 return func_cmp(pLeft, pRight);
245 if (comp.soa() != 0 || comp.size() == 0)
248 return memcmp(pLeft, pRight, comp.size()) == 0;
251 void save(ser::serializer& serializer,
const void* pSrc, uint32_t from, uint32_t to, uint32_t cap)
const {
252 GAIA_ASSERT(serializer.valid() && pSrc !=
nullptr && from < to && to <= cap);
253 if (func_save !=
nullptr) {
254 func_save(serializer, pSrc, from, to, cap);
258 if (comp.soa() != 0 || comp.size() == 0)
261 const auto* pBase = (
const uint8_t*)pSrc;
262 GAIA_FOR2(from, to) {
263 const auto* p = pBase + ((uintptr_t)comp.size() * i);
264 serializer.save_raw((
const void*)p, comp.size(), ser::serialization_type_id::trivial_wrapper);
268 void load(ser::serializer& serializer,
void* pDst, uint32_t from, uint32_t to, uint32_t cap)
const {
269 GAIA_ASSERT(serializer.valid() && pDst !=
nullptr && from < to && to <= cap);
270 if (func_load !=
nullptr) {
271 func_load(serializer, pDst, from, to, cap);
275 if (comp.soa() != 0 || comp.size() == 0)
278 auto* pBase = (uint8_t*)pDst;
279 GAIA_FOR2(from, to) {
280 auto* p = pBase + ((uintptr_t)comp.size() * i);
281 serializer.load_raw((
void*)p, comp.size(), ser::serialization_type_id::trivial_wrapper);
285 void clear_fields() {
289 GAIA_NODISCARD
bool has_fields()
const {
290 return !fields.empty();
293 GAIA_NODISCARD
static uint32_t field_element_count(
const RuntimeFieldDesc& field)
noexcept {
294 return field.count == 0 ? 1U : field.count;
297 GAIA_NODISCARD
static uint32_t field_element_count(
const RuntimeField& field)
noexcept {
298 return field.count == 0 ? 1U : field.count;
301 GAIA_NODISCARD
static uint32_t primitive_type_size(Entity type)
noexcept {
302 ser::serialization_type_id
id = ser::serialization_type_id::ignore;
303 if (!runtime_primitive_serialization_type(type,
id))
305 return ser::serialization_type_size(
id, 0);
308 GAIA_NODISCARD
bool add_field(
const RuntimeFieldDesc& desc, uint32_t typeSize = 0) {
309 if (desc.name.empty() || desc.name.size() >= MaxNameLength)
311 if (desc.type == EntityBad)
314 const auto elemSize = typeSize != 0 ? typeSize : primitive_type_size(desc.type);
318 const auto elemCount = field_element_count(desc);
319 const auto end = (uint64_t)desc.offset + (uint64_t)elemSize * (uint64_t)elemCount;
320 if (end > comp.size())
323 for (
auto& field: fields) {
324 if (strncmp(field.name, desc.name.data(), desc.name.size()) == 0 && field.name[desc.name.size()] == 0) {
325 field.type = desc.type;
326 field.offset = desc.offset;
327 field.count = desc.count;
332 RuntimeField field{};
333 memcpy((
void*)field.name, (
const void*)desc.name.data(), desc.name.size());
334 field.name[desc.name.size()] = 0;
335 field.type = desc.type;
336 field.offset = desc.offset;
337 field.count = desc.count;
338 fields.push_back(field);
342 GAIA_NODISCARD
bool field(util::str_view fieldName,
const RuntimeField** ppField)
const {
343 GAIA_ASSERT(ppField !=
nullptr);
345 if (fieldName.empty() || fieldName.size() >= MaxNameLength)
348 for (
const auto& field: fields) {
349 if (strncmp(field.name, fieldName.data(), fieldName.size()) == 0 && field.name[fieldName.size()] == 0) {
357 GAIA_NODISCARD uint32_t field_count() const noexcept {
358 return (uint32_t)fields.size();
361 void clear_runtime_fields() {
370 const Hooks& hooks()
const {
376 GAIA_NODISCARD uint32_t calc_new_mem_offset(uint32_t addr,
size_t cnt)
const noexcept {
377 if (comp.soa() == 0) {
378 addr = (uint32_t)mem::detail::get_aligned_byte_offset(addr, comp.alig(), comp.size(), cnt);
380 GAIA_FOR(comp.soa()) {
381 addr = (uint32_t)mem::detail::get_aligned_byte_offset(addr, comp.alig(), soaSizes[i], cnt);
386 addr += comp.soa() * 12;
392 template <
typename T>
393 GAIA_NODISCARD
static uint32_t init_type_name(
char (&nameTmp)[MaxNameLength]) {
394 auto ct_name = detail::ComponentDesc<T>::name();
403 auto nameTmpLen = (uint32_t)ct_name.size();
404 GAIA_ASSERT(nameTmpLen < MaxNameLength);
405 memcpy((
void*)nameTmp, (
const void*)ct_name.data(), nameTmpLen);
406 nameTmp[ct_name.size()] = 0;
408 auto strip_prefix = [&](
const char* prefix, uint32_t prefixLen) {
409 if (nameTmpLen <= prefixLen || strncmp(nameTmp, prefix, prefixLen) != 0)
412 memmove(nameTmp, nameTmp + prefixLen, nameTmpLen - prefixLen + 1);
413 nameTmpLen -= prefixLen;
416 strip_prefix(
"const ", 6);
418 const uint32_t NSubstrings = 3;
419 const char* to_remove[NSubstrings] = {
"class ",
"struct ",
"enum "};
420 const uint32_t to_remove_len[NSubstrings] = {6, 7, 5};
421 GAIA_FOR(NSubstrings) {
422 strip_prefix(to_remove[i], to_remove_len[i]);
425 while (nameTmpLen > 0) {
426 const auto ch = nameTmp[nameTmpLen - 1];
427 if (ch !=
' ' && ch !=
'&' && ch !=
'*')
430 nameTmp[--nameTmpLen] = 0;
433 if (nameTmpLen > 6 && strncmp(nameTmp + nameTmpLen - 6,
" const", 6) == 0) {
435 nameTmp[nameTmpLen] = 0;
440 GAIA_FOR(NSubstrings) {
441 const auto* str = to_remove[i];
442 const auto len = to_remove_len[i];
445 while ((pos = strstr(pos, str)) !=
nullptr) {
446 const bool isBoundary = pos == nameTmp || pos[-1] ==
'<' || pos[-1] ==
',' || pos[-1] ==
' ';
452 const auto tailMaxLen = (size_t)(MaxNameLength - (uint32_t)(pos + len - nameTmp));
453 memmove(pos, pos + len, GAIA_STRLEN(pos + len, tailMaxLen) + 1);
461 static void init_name(SymbolLookupKey& nameOut, util::str_view nameView) {
462 char* name = mem::AllocHelper::alloc<char>(nameView.size() + 1);
463 memcpy((
void*)name, (
const void*)nameView.data(), nameView.size());
464 name[nameView.size()] = 0;
465 nameOut = SymbolLookupKey(name, nameView.size(), 1);
469 template <
typename T>
470 GAIA_NODISCARD
static ComponentCacheItem* create(Entity entity) {
471 static_assert(core::is_raw_v<T>);
473 constexpr auto componentSize = detail::ComponentDesc<T>::size();
475 componentSize < Component::MaxComponentSizeInBytes,
476 "Trying to register a component larger than the maximum allowed component size! In the future this "
477 "restriction won't apply to components not stored inside archetype chunks.");
479 char nameTmp[MaxNameLength];
480 const auto nameTmpLen = init_type_name<T>(nameTmp);
482 uint8_t soaSizes[meta::StructToTupleMaxTypes]{};
483 const auto desc = detail::ComponentDesc<T>::make(
485 return create(entity, desc);
488 GAIA_NODISCARD
static ComponentCacheItem* create(Entity entity,
const ecs::ComponentDesc& desc) {
489 GAIA_ASSERT(!desc.name.empty());
490 GAIA_ASSERT(desc.name.size() < MaxNameLength);
491 GAIA_ASSERT(desc.size < Component::MaxComponentSizeInBytes);
492 GAIA_ASSERT((desc.size == 0 && desc.alig == 0) || (desc.alig > 0 && desc.alig < Component::MaxAlignment));
493 GAIA_ASSERT(desc.soa <= meta::StructToTupleMaxTypes);
495 auto* cci =
new ComponentCacheItem();
496 cci->entity = entity;
497 cci->comp = Component(entity.id(), desc.soa, desc.size, desc.alig, desc.storageType);
498 cci->hashLookup = desc.hashLookup.hash != 0
500 : ComponentLookupHash{core::calculate_hash64(desc.name.data(), desc.name.size())};
502 if (desc.soa > 0 && desc.pSoaSizes !=
nullptr) {
503 GAIA_FOR(desc.soa) cci->soaSizes[i] = desc.pSoaSizes[i];
506 init_name(cci->name, desc.name);
508 cci->func_ctor = desc.funcCtor;
509 cci->func_move_ctor = desc.funcMoveCtor;
510 cci->func_copy_ctor = desc.funcCopyCtor;
511 cci->func_dtor = desc.funcDtor;
512 cci->func_copy = desc.funcCopy;
513 cci->func_move = desc.funcMove;
514 cci->func_swap = desc.funcSwap;
515 cci->func_cmp = desc.funcCmp;
516 cci->func_save = desc.funcSave;
517 cci->func_load = desc.funcLoad;
518 cci->typeKind = desc.typeKind;
519 cci->primitiveKind = desc.primitiveKind;
524 GAIA_NODISCARD static ComponentCacheItem* create(Entity entity, const ComponentCacheItemCtx& ctx) {
525 ecs::ComponentDesc desc{};
526 desc.name = ctx.name;
527 desc.size = ctx.size;
528 desc.alig = ctx.alig;
529 desc.storageType = ctx.storageType;
531 desc.pSoaSizes = ctx.pSoaSizes;
532 desc.hashLookup = ctx.hashLookup;
533 desc.typeKind = ctx.typeKind;
534 desc.primitiveKind = ctx.primitiveKind;
535 desc.funcCtor = ctx.funcCtor;
536 desc.funcMoveCtor = ctx.funcMoveCtor;
537 desc.funcCopyCtor = ctx.funcCopyCtor;
538 desc.funcDtor = ctx.funcDtor;
539 desc.funcCopy = ctx.funcCopy;
540 desc.funcMove = ctx.funcMove;
541 desc.funcSwap = ctx.funcSwap;
542 desc.funcCmp = ctx.funcCmp;
543 desc.funcSave = ctx.funcSave;
544 desc.funcLoad = ctx.funcLoad;
545 return create(entity, desc);
548 static void destroy(ComponentCacheItem* pItem) {
549 if (pItem ==
nullptr)
552 if (pItem->name.str() !=
nullptr && pItem->name.owned()) {
553 mem::AllocHelper::free((
void*)pItem->name.str());