2#include "gaia/config/config.h"
8#include "gaia/cnt/darray.h"
9#include "gaia/cnt/sarray.h"
10#include "gaia/cnt/sarray_ext.h"
11#include "gaia/cnt/set.h"
12#include "gaia/config/profiler.h"
13#include "gaia/core/utility.h"
14#include "gaia/ecs/api.h"
15#include "gaia/ecs/archetype.h"
16#include "gaia/ecs/archetype_common.h"
17#include "gaia/ecs/id.h"
18#include "gaia/ecs/query_common.h"
19#include "gaia/ecs/query_mask.h"
20#include "gaia/ecs/query_match_stamps.h"
21#include "gaia/ser/ser_binary.h"
22#include "gaia/util/str.h"
26 using EntityToArchetypeMap = cnt::map<EntityLookupKey, ComponentIndexEntryArray>;
33 enum class MatchingStyle { Simple, Wildcard, Complex };
39 const void* pData =
nullptr;
42 FetchByKeyFn fetchByKey =
nullptr;
44 GAIA_NODISCARD
bool empty()
const {
45 return fetchByKey ==
nullptr;
53 return fetchByKey(pData, arr, ent, key);
56 GAIA_NODISCARD uint32_t revision(
const EntityLookupKey& key)
const {
61 return it ==
pVersions->end() ? 0 : it->second;
121 GAIA_ASSERT(key != EntityBadLookupKey);
123 const auto it = map.find(key);
124 if (it == map.end() || it->second.empty())
127 return std::span(it->second.data(), it->second.size());
132 GAIA_ASSERT(src != EntityBad);
134 return fetch_archetypes_for_select(map, arr, ent, EntityLookupKey(src));
140 GAIA_ASSERT(key != EntityBadLookupKey);
142 const auto it = core::find_if(map, [&](
const auto& item) {
143 return item.matches(key);
145 if (it == map.end() || arr.empty())
153 GAIA_ASSERT(src != EntityBad);
155 return fetch_archetypes_for_select(map, arr, ent, EntityLookupKey(src));
160 return fetch_archetypes_for_select(*(
const EntityToArchetypeMap*)pData, arr, ent, key);
165 return fetch_archetypes_for_select(*(
const SingleArchetypeLookup*)pData, arr, ent, key);
168 inline ArchetypeLookupView
169 make_archetype_lookup_view(
const EntityToArchetypeMap& map,
const EntityToArchetypeVersionMap& versions) {
170 return ArchetypeLookupView{&map, &versions, fetch_archetypes_for_select_from_map};
173 inline ArchetypeLookupView make_archetype_lookup_view(
const SingleArchetypeLookup& map) {
174 return ArchetypeLookupView{&map,
nullptr, fetch_archetypes_for_select_from_single};
178 enum class EOpcode : uint8_t {
211 Var_Term_All_Src_Bind,
217 Var_Search_SelectAll,
219 Var_Search_SelectOtherOr,
220 Var_Search_SelectOtherOrBind,
222 Var_Search_SelectAny,
223 Var_Search_MaybeFinalize,
225 Var_Final_Require_Or,
230 using VmLabel = uint16_t;
247 EOpcode opcode = EOpcode::Src_Never;
252 EOpcode sourceOpcode = EOpcode::Src_Never;
266 GAIA_NODISCARD
bool empty()
const {
272 uint16_t selectAllPc = (uint16_t)-1;
273 uint16_t selectOrPc = (uint16_t)-1;
274 uint16_t selectOtherOrPc = (uint16_t)-1;
275 uint16_t selectOtherOrBindPc = (uint16_t)-1;
276 uint16_t beginAnyPc = (uint16_t)-1;
277 uint16_t selectAnyPc = (uint16_t)-1;
278 uint16_t maybeFinalizePc = (uint16_t)-1;
279 uint16_t initialAllMask = 0;
280 uint16_t initialOrMask = 0;
281 uint16_t initialAnyMask = 0;
282 uint16_t allBegin = 0;
283 uint16_t allCheckBegin = 0;
284 uint16_t allCount = 0;
285 uint16_t orBegin = 0;
286 uint16_t orCheckBegin = 0;
287 uint16_t orCount = 0;
288 uint16_t anyBegin = 0;
289 uint16_t anyCheckBegin = 0;
290 uint16_t anyCount = 0;
291 uint16_t notBegin = 0;
292 uint16_t notCount = 0;
293 uint8_t orVarMask = 0;
302 uint16_t mainOpsCount = 0;
327 uint8_t varMaskOr = 0;
328 uint8_t varMaskNot = 0;
329 uint8_t varMaskAny = 0;
331 GAIA_NODISCARD
bool has_src_terms()
const {
335 GAIA_NODISCARD
bool has_variable_terms()
const {
339 GAIA_NODISCARD
bool has_id_terms()
const {
344 enum class EVarProgramTermSet : uint8_t { None, All, Or, Any, Not };
347 EVarProgramTermSet termSet;
350 static constexpr auto VarProgramOpcodeFirst = EOpcode::Var_Term_All_Check;
351 static constexpr auto VarProgramOpcodeLast = EOpcode::Var_Final_Success;
353 {EVarProgramTermSet::All},
354 {EVarProgramTermSet::All},
355 {EVarProgramTermSet::All},
356 {EVarProgramTermSet::Or},
357 {EVarProgramTermSet::Or},
358 {EVarProgramTermSet::Any},
359 {EVarProgramTermSet::Any},
360 {EVarProgramTermSet::Not},
361 {EVarProgramTermSet::None},
362 {EVarProgramTermSet::None},
363 {EVarProgramTermSet::None},
364 {EVarProgramTermSet::None},
365 {EVarProgramTermSet::None},
366 {EVarProgramTermSet::None},
367 {EVarProgramTermSet::None},
368 {EVarProgramTermSet::Not},
369 {EVarProgramTermSet::None},
370 {EVarProgramTermSet::Or},
371 {EVarProgramTermSet::None},
375 sizeof(VarProgramOpcodeMetaTable) /
sizeof(VarProgramOpcodeMetaTable[0]) ==
376 (uint32_t)VarProgramOpcodeLast - (uint32_t)VarProgramOpcodeFirst + 1u,
377 "VarProgramOpcodeMetaTable out of sync with EOpcode variable micro-op range.");
379 GAIA_NODISCARD
inline const VarProgramOpcodeMeta& var_program_opcode_meta(EOpcode opcode) {
380 GAIA_ASSERT((uint32_t)opcode >= (uint32_t)VarProgramOpcodeFirst);
381 GAIA_ASSERT((uint32_t)opcode <= (uint32_t)VarProgramOpcodeLast);
382 return VarProgramOpcodeMetaTable[(uint32_t)opcode - (uint32_t)VarProgramOpcodeFirst];
385 GAIA_NODISCARD
inline uint8_t src_term_cost(
const QueryCompileCtx::SourceTermOp& termOp) {
386 const bool depth1 = termOp.term.travDepth == 1;
387 switch (termOp.opcode) {
388 case EOpcode::Src_Never:
390 case EOpcode::Src_Self:
392 case EOpcode::Src_Up:
393 case EOpcode::Src_Down:
394 return depth1 ? 2 : 4;
395 case EOpcode::Src_UpDown:
396 return depth1 ? 3 : 5;
402 GAIA_NODISCARD
inline uint8_t bound_match_id_cost(Entity queryId) {
404 return (!is_variable(queryId) && queryId.id() != All.id()) ? 1u : 3u;
407 cost += (!is_variable((EntityId)queryId.id()) && queryId.id() != All.id()) ? 1u : 3u;
408 cost += (!is_variable((EntityId)queryId.gen()) && queryId.gen() != All.id()) ? 1u : 3u;
412 GAIA_NODISCARD
inline uint8_t bound_term_cost(
const QueryCompileCtx::VarTermOp& termOp) {
413 uint8_t cost = bound_match_id_cost(termOp.term.id);
414 if (termOp.term.src != EntityBad)
415 cost = (uint8_t)(cost + src_term_cost({termOp.sourceOpcode, termOp.term}));
419 GAIA_NODISCARD
inline uint8_t search_term_cost(
const QueryCompileCtx::VarTermOp& termOp) {
420 uint8_t cost = bound_term_cost(termOp);
421 if (termOp.term.src != EntityBad) {
422 const bool srcIsVar = is_variable(EntityId(termOp.term.src.id()));
423 cost = (uint8_t)(cost + (srcIsVar ? 32u : 8u));
428 template <
typename ProgramOpsArray>
429 inline void sort_program_ops_by_cost(ProgramOpsArray& ops) {
430 const auto cnt = (uint32_t)ops.size();
434 for (uint32_t i = 1; i < cnt; ++i) {
435 const auto key = ops[i];
439 const auto prev = ops[j - 1];
440 if (prev.cost < key.cost)
442 if (prev.cost == key.cost && (uint8_t)prev.opcode < (uint8_t)key.opcode)
444 if (prev.cost == key.cost && prev.opcode == key.opcode && prev.arg <= key.arg)
454 template <
typename SourceTermsArray>
455 inline void sort_src_terms_by_cost(SourceTermsArray& terms) {
456 const auto cnt = (uint32_t)terms.size();
460 for (uint32_t i = 1; i < cnt; ++i) {
462 const auto keyCost = src_term_cost(key);
465 while (j > 0 && src_term_cost(terms[j - 1]) > keyCost) {
466 terms[j] = terms[j - 1];
475 program_ops(
const QueryCompileCtx& comp,
const QueryCompileCtx::VarProgram& program) {
476 GAIA_ASSERT((uint32_t)program.begin + (uint32_t)program.count <= (uint32_t)comp.ops.size());
477 return {comp.ops.data() + program.begin, program.count};
480 inline uint32_t handle_last_archetype_match(
481 QueryArchetypeCacheIndexMap* pCont, EntityLookupKey entityKey, uint32_t srcArchetypeCnt,
482 uint32_t srcRevision) {
483 if (pCont ==
nullptr)
486 const auto cache_it = pCont->find(entityKey);
487 uint32_t lastMatchedIdx = 0;
488 if (cache_it == pCont->end())
489 pCont->emplace(entityKey, QueryArchetypeCacheCursor{srcArchetypeCnt, srcRevision});
491 auto& cursor = cache_it->second;
492 if (cursor.revision == srcRevision)
493 lastMatchedIdx = cursor.index;
494 cursor.index = srcArchetypeCnt;
495 cursor.revision = srcRevision;
497 return lastMatchedIdx;
502 static bool check_mask(
const QueryMask& maskArchetype,
const QueryMask& maskQuery) {
503 return match_entity_mask(maskArchetype, maskQuery);
505 static void restart([[maybe_unused]] uint32_t& idx) {}
506 static bool can_continue(
bool hasMatch) {
509 static bool eval(uint32_t expectedMatches, uint32_t totalMatches) {
510 return expectedMatches == totalMatches;
512 static uint32_t handle_last_match(
514 return handle_last_archetype_match(
520 static bool check_mask(
const QueryMask& maskArchetype,
const QueryMask& maskQuery) {
521 return match_entity_mask(maskArchetype, maskQuery);
523 static void restart(uint32_t& idx) {
527 static bool can_continue([[maybe_unused]]
bool hasMatch) {
530 static bool eval(uint32_t expectedMatches, uint32_t totalMatches) {
531 (void)expectedMatches;
532 return totalMatches > 0;
534 static uint32_t handle_last_match(
536 return handle_last_archetype_match(
542 static bool check_mask(
const QueryMask& maskArchetype,
const QueryMask& maskQuery) {
543 return !match_entity_mask(maskArchetype, maskQuery);
545 static void restart(uint32_t& idx) {
548 static bool can_continue(
bool hasMatch) {
551 static bool eval(uint32_t expectedMatches, uint32_t totalMatches) {
552 (void)expectedMatches;
553 return totalMatches == 0;
555 static uint32_t handle_last_match(
557 return handle_last_archetype_match(
562 GAIA_NODISCARD
inline bool is_archetype_marked(
const MatchingCtx& ctx,
const Archetype* pArchetype) {
566 const auto sid = (uint32_t)pArchetype->id();
567 if (!stamps.has(sid))
577 const auto sid = (uint32_t)pArchetype->id();
583 inline void add_all_archetypes(MatchingCtx& ctx) {
584 for (
const auto* pArchetype: ctx.allArchetypes) {
585 if (is_archetype_marked(ctx, pArchetype))
588 mark_archetype_match(ctx, pArchetype);
592 template <
typename OpKind>
593 inline bool match_inter_eval_matches(uint32_t queryIdMarches, uint32_t& outMatches) {
594 const bool hadAnyMatches = queryIdMarches > 0;
598 if (!OpKind::can_continue(hadAnyMatches))
603 outMatches += (uint32_t)hadAnyMatches;
615 template <
typename OpKind,
typename CmpFunc>
616 GAIA_NODISCARD
inline bool match_inter(EntitySpan queryIds, EntitySpan archetypeIds, CmpFunc func) {
617 const auto archetypeIdsCnt = (uint32_t)archetypeIds.size();
618 const auto queryIdsCnt = (uint32_t)queryIds.size();
621 uint32_t indices[2]{};
622 uint32_t matches = 0;
649 while (indices[0] < queryIdsCnt) {
650 const auto idInQuery = queryIds[indices[0]];
653 if (idInQuery == All || idInQuery.id() == Is.id())
656 uint32_t queryIdMatches = 0;
657 while (indices[1] < archetypeIdsCnt) {
658 const auto idInArchetype = archetypeIds[indices[1]];
661 const auto res = func(idInQuery, idInArchetype);
671 if (!match_inter_eval_matches<OpKind>(queryIdMatches, matches))
680 if (!match_inter_eval_matches<OpKind>(queryIdMatches, matches))
687 OpKind::restart(indices[1]);
693 return OpKind::eval(queryIdsCnt, matches);
701 return {idInQuery == idInArchetype};
704 GAIA_NODISCARD
inline IdCmpResult cmp_ids_pairs(
Entity idInQuery,
Entity idInArchetype) {
705 if (idInQuery.pair()) {
707 if (idInQuery ==
Pair(All, All))
715 if (idInQuery.gen() == All.id())
716 return {idInQuery.id() == idInArchetype.id()};
723 if (idInQuery.id() == All.id())
724 return {idInQuery.gen() == idInArchetype.gen()};
728 return cmp_ids(idInQuery, idInArchetype);
731 GAIA_NODISCARD
inline IdCmpResult
732 cmp_ids_is(
const World& w,
const Archetype& archetype, Entity idInQuery, Entity idInArchetype) {
734 if (idInQuery.pair() && idInQuery.id() == Is.id()) {
735 auto archetypeIds = archetype.ids_view();
737 idInQuery.gen() == idInArchetype.id() ||
738 as_relations_trav_if(w, idInQuery, [&](Entity relation) {
739 const auto idx = core::get_index(archetypeIds, relation);
746 return cmp_ids(idInQuery, idInArchetype);
749 GAIA_NODISCARD
inline IdCmpResult
750 cmp_ids_is_pairs(
const World& w,
const Archetype& archetype, Entity idInQuery, Entity idInArchetype) {
751 if (idInQuery.pair()) {
753 if (idInQuery == Pair(All, All))
757 if (idInQuery.id() == Is.id()) {
759 if (idInArchetype == idInQuery)
762 const auto eQ = pair_tgt(w, idInQuery);
763 if (eQ == idInArchetype)
768 if (idInArchetype.id() == Is.id()) {
769 const auto eA = pair_tgt(w, idInArchetype);
773 return {as_relations_trav_if(w, eQ, [eA](Entity relation) {
774 return eA == relation;
779 auto archetypeIds = archetype.ids_view();
780 return {as_relations_trav_if(w, eQ, [&archetypeIds](Entity relation) {
783 const auto idx = core::get_index(archetypeIds, relation);
794 if (idInQuery.id() == All.id()) {
795 if (idInQuery.gen() == idInArchetype.gen())
799 if (archetype.pairs_is() > 0) {
800 auto archetypeIds = archetype.ids_view();
802 const auto e = pair_tgt(w, idInQuery);
803 return {as_relations_trav_if(w, e, [&](Entity relation) {
806 const auto idx = core::get_index(archetypeIds, relation);
821 if (idInQuery.gen() == All.id()) {
822 return {idInQuery.id() == idInArchetype.id()};
827 return cmp_ids(idInQuery, idInArchetype);
836 template <
typename OpKind>
837 GAIA_NODISCARD
inline bool match_res(
const Archetype& archetype, EntitySpan queryIds) {
840 if (archetype.pairs() == 0) {
841 return match_inter<OpKind>(
842 queryIds, archetype.ids_view(),
844 [](Entity idInQuery, Entity idInArchetype) {
845 return cmp_ids(idInQuery, idInArchetype);
850 return match_inter<OpKind>(
851 queryIds, archetype.ids_view(),
853 [](Entity idInQuery, Entity idInArchetype) {
854 return cmp_ids_pairs(idInQuery, idInArchetype);
864 template <
typename OpKind>
865 GAIA_NODISCARD
inline bool match_res_as(
const World& w,
const Archetype& archetype, EntitySpan queryIds) {
867 if (archetype.pairs() == 0) {
868 return match_inter<OpKind>(
869 queryIds, archetype.ids_view(),
871 [&](Entity idInQuery, Entity idInArchetype) {
872 return cmp_ids_is(w, archetype, idInQuery, idInArchetype);
876 return match_inter<OpKind>(
877 queryIds, archetype.ids_view(),
879 [&](Entity idInQuery, Entity idInArchetype) {
880 return cmp_ids_is_pairs(w, archetype, idInQuery, idInArchetype);
884 GAIA_NODISCARD
inline bool match_single_id_on_archetype(
const World& w,
const Archetype& archetype, Entity
id) {
885 const Entity ids[1] = {
id};
886 return match_res_as<OpOr>(w, archetype, EntitySpan{ids, 1});
889 GAIA_NODISCARD
inline bool match_single_id_on_archetype_exact(
const Archetype& archetype, Entity
id) {
890 const Entity ids[1] = {
id};
891 return match_res<OpOr>(archetype, EntitySpan{ids, 1});
894 GAIA_NODISCARD
inline EOpcode src_opcode_from_term(
const QueryTerm& term) {
895 const bool includeSelf = query_trav_has(term.travKind, QueryTravKind::Self);
896 const bool includeUp = query_trav_has(term.travKind, QueryTravKind::Up) && term.entTrav != EntityBad;
897 const bool includeDown = query_trav_has(term.travKind, QueryTravKind::Down) && term.entTrav != EntityBad;
898 if (!includeSelf && !includeUp && !includeDown)
899 return EOpcode::Src_Never;
900 if (includeSelf && !includeUp && !includeDown)
901 return EOpcode::Src_Self;
902 if (includeUp && includeDown)
903 return EOpcode::Src_UpDown;
905 return EOpcode::Src_Up;
906 return EOpcode::Src_Down;
910 uint32_t queueIdx = 0;
911 uint32_t childIdx = 0;
912 uint32_t childLevel = 0;
913 uint32_t upDepth = 0;
915 bool initialized =
false;
916 bool selfEmitted =
false;
917 Entity upSource = EntityBad;
924 void reset_runtime_state() {
931 upSource = EntityBad;
940 GAIA_NODISCARD
inline bool next_lookup_src_cursor(
944 template <
typename Func>
945 GAIA_NODISCARD
inline bool
946 each_lookup_src(
const World& w, EOpcode opcode,
const QueryTerm& term,
Entity sourceEntity, Func&& func) {
948 Entity source = EntityBad;
949 while (next_lookup_src_cursor(w, opcode, term, sourceEntity, cursor, source)) {
957 template <
typename Func>
958 GAIA_NODISCARD
inline bool
959 each_lookup_src(
const World& w,
const QueryTerm& term, Entity sourceEntity, Func&& func) {
960 return each_lookup_src(w, src_opcode_from_term(term), term, sourceEntity, GAIA_FWD(func));
963 GAIA_NODISCARD
inline bool next_lookup_src_cursor_up(
964 const World& w,
const QueryTerm& term, Entity sourceEntity, SourceLookupCursor& cursor, Entity& outSource,
966 if (!valid(w, sourceEntity))
969 const uint32_t maxDepth =
970 term.travDepth == QueryTermOptions::TravDepthUnlimited ? MAX_TRAV_DEPTH : (uint32_t)term.travDepth;
972 if (!cursor.initialized) {
973 cursor.initialized =
true;
974 cursor.upSource = sourceEntity;
977 if (includeSelf && !cursor.selfEmitted) {
978 cursor.selfEmitted =
true;
979 outSource = sourceEntity;
983 while (cursor.upDepth < maxDepth) {
984 const auto next = target(w, cursor.upSource, term.entTrav);
985 if (next == EntityBad || next == cursor.upSource)
988 cursor.upSource = next;
997 GAIA_NODISCARD
inline bool next_lookup_src_cursor_down(
998 const World& w,
const QueryTerm& term, Entity sourceEntity, SourceLookupCursor& cursor, Entity& outSource,
1000 if (!valid(w, sourceEntity))
1003 const uint32_t maxDepth =
1004 term.travDepth == QueryTermOptions::TravDepthUnlimited ? MAX_TRAV_DEPTH : (uint32_t)term.travDepth;
1006 if (!cursor.initialized) {
1007 cursor.initialized =
true;
1008 cursor.queue.push_back(sourceEntity);
1009 cursor.levels.push_back(0);
1010 cursor.visited.insert(EntityLookupKey(sourceEntity));
1013 if (includeSelf && !cursor.selfEmitted) {
1014 cursor.selfEmitted =
true;
1015 outSource = sourceEntity;
1020 if (cursor.childIdx < cursor.children.size()) {
1021 const auto child = cursor.children[cursor.childIdx++];
1022 cursor.queue.push_back(child);
1023 cursor.levels.push_back(cursor.childLevel);
1028 bool loadedChildren =
false;
1029 while (cursor.queueIdx < cursor.queue.size()) {
1030 const auto source = cursor.queue[cursor.queueIdx];
1031 const auto level = cursor.levels[cursor.queueIdx];
1033 if (level >= maxDepth)
1036 cursor.children.clear();
1037 cursor.childIdx = 0;
1038 cursor.childLevel = level + 1;
1039 sources(w, term.entTrav, source, [&](Entity next) {
1040 const auto key = EntityLookupKey(next);
1041 const auto ins = cursor.visited.insert(key);
1045 cursor.children.push_back(next);
1048 core::sort(cursor.children, [](Entity left, Entity right) {
1049 return left.id() < right.id();
1052 if (!cursor.children.empty()) {
1053 loadedChildren =
true;
1058 if (!loadedChildren)
1063 GAIA_NODISCARD
inline bool next_lookup_src_cursor(
1064 const World& w, EOpcode opcode,
const QueryTerm& term, Entity sourceEntity, SourceLookupCursor& cursor,
1065 Entity& outSource) {
1066 const bool includeSelf = query_trav_has(term.travKind, QueryTravKind::Self);
1067 const bool unlimitedTraversal =
1068 term.travDepth == QueryTermOptions::TravDepthUnlimited && term.entTrav != EntityBad;
1070 if (unlimitedTraversal &&
1071 (opcode == EOpcode::Src_Up || opcode == EOpcode::Src_Down || opcode == EOpcode::Src_UpDown)) {
1072 if (!valid(w, sourceEntity))
1075 if (!cursor.initialized)
1076 cursor.initialized =
true;
1078 if (includeSelf && !cursor.selfEmitted) {
1079 cursor.selfEmitted =
true;
1080 outSource = sourceEntity;
1085 if (cursor.cachedSources.data() != cachedSources.data() ||
1086 cursor.cachedSources.size() != cachedSources.size()) {
1087 cursor.cachedSources = cachedSources;
1088 cursor.queueIdx = 0;
1091 if (cursor.queueIdx < cursor.cachedSources.size()) {
1092 outSource = cursor.cachedSources[cursor.queueIdx++];
1099 if (opcode == EOpcode::Src_Up) {
1100 return adv_cached_src(targets_trav_cache(w, term.entTrav, sourceEntity));
1103 if (opcode == EOpcode::Src_Down) {
1104 return adv_cached_src(sources_bfs_trav_cache(w, term.entTrav, sourceEntity));
1107 if (cursor.phase == 0) {
1108 if (adv_cached_src(targets_trav_cache(w, term.entTrav, sourceEntity)))
1112 cursor.cachedSources = {};
1113 cursor.queueIdx = 0;
1116 return adv_cached_src(sources_bfs_trav_cache(w, term.entTrav, sourceEntity));
1120 case EOpcode::Src_Never:
1122 case EOpcode::Src_Self:
1123 if (cursor.initialized || !valid(w, sourceEntity))
1125 cursor.initialized =
true;
1126 outSource = sourceEntity;
1128 case EOpcode::Src_Up:
1129 return next_lookup_src_cursor_up(w, term, sourceEntity, cursor, outSource, includeSelf);
1130 case EOpcode::Src_Down:
1131 return next_lookup_src_cursor_down(w, term, sourceEntity, cursor, outSource, includeSelf);
1132 case EOpcode::Src_UpDown:
1133 if (cursor.phase == 0) {
1134 if (next_lookup_src_cursor_up(w, term, sourceEntity, cursor, outSource, includeSelf))
1136 cursor.reset_runtime_state();
1139 return next_lookup_src_cursor_down(w, term, sourceEntity, cursor, outSource,
false);
1146 GAIA_NODISCARD
inline bool match_src_term(
const World& w,
const QueryTerm& term, EOpcode opcode) {
1147 auto match_src_entity = [&](Entity source) {
1148 if (!valid(w, source))
1151 auto* pArchetype = archetype_from_entity(w, source);
1152 if (pArchetype ==
nullptr)
1155 return match_single_id_on_archetype(w, *pArchetype, term.id);
1158 return each_lookup_src(w, opcode, term, term.src, match_src_entity);
1161 GAIA_NODISCARD
inline bool match_src_term(
const World& w,
const QueryTerm& term) {
1162 return match_src_term(w, term, src_opcode_from_term(term));
1172 uint32_t sourceArchetypeIdx = 0;
1173 uint32_t sourceChunkIdx = 0;
1174 uint32_t sourceEntityIdx = 0;
1175 Entity source = EntityBad;
1179 GAIA_NODISCARD
inline bool is_var_entity(
Entity entity) {
1180 return is_variable(EntityId(entity.id()));
1183 GAIA_NODISCARD
inline uint32_t var_index(
Entity varEntity) {
1184 GAIA_ASSERT(is_var_entity(varEntity));
1185 return (uint32_t)(varEntity.id() - Var0.id());
1188 GAIA_NODISCARD
inline bool var_is_bound(
const VarBindings& vars,
Entity varEntity) {
1189 const auto idx = var_index(varEntity);
1190 return (vars.mask & (uint8_t(1) << idx)) != 0;
1193 GAIA_NODISCARD
inline bool bind_var(VarBindings& vars, Entity varEntity, Entity value) {
1194 const auto idx = var_index(varEntity);
1195 const auto bit = (uint8_t(1) << idx);
1196 if ((vars.mask & bit) != 0)
1197 return vars.values[idx].id() == value.id();
1199 vars.values[idx] = value;
1204 GAIA_NODISCARD
inline bool match_token(VarBindings& vars, Entity token, Entity value,
bool pairSide) {
1205 if (pairSide && token.id() == All.id())
1208 if (!is_var_entity(token))
1209 return token.id() == value.id();
1211 return bind_var(vars, token, value);
1215 Entity token = EntityBad;
1216 Entity matchValue = EntityBad;
1217 bool concrete =
false;
1218 bool needsBind =
false;
1222 uint32_t matchId = 0;
1223 uint8_t bindVarIdx = 0xff;
1224 bool concrete =
false;
1225 bool needsBind =
false;
1230 out.token = queryToken;
1232 if (queryToken == EntityBad)
1235 if (is_var_entity(queryToken)) {
1236 if (!var_is_bound(vars, queryToken)) {
1237 out.needsBind =
true;
1241 out.matchValue = vars.values[var_index(queryToken)];
1242 out.concrete = out.matchValue.id() != All.id();
1246 if (queryToken.id() == All.id())
1249 out.matchValue = queryToken;
1250 out.concrete =
true;
1256 GAIA_NODISCARD
inline RawMatchToken resolve_raw_pair_match_token(Entity queryToken,
const VarBindings& vars) {
1257 RawMatchToken out{};
1259 if (queryToken == EntityBad || queryToken.id() == All.id())
1262 if (is_var_entity(queryToken)) {
1263 out.bindVarIdx = (uint8_t)var_index(queryToken);
1264 if ((vars.mask & (uint8_t(1) << out.bindVarIdx)) == 0) {
1265 out.needsBind =
true;
1269 out.matchId = vars.values[out.bindVarIdx].id();
1270 out.concrete = out.matchId != All.id();
1274 out.matchId = queryToken.id();
1275 out.concrete =
true;
1279 GAIA_NODISCARD
inline uint32_t count_pair_id_matches_limited(
1280 const World& w,
const Archetype& archetype, Entity queryId,
const VarBindings& varsIn, uint32_t limit) {
1281 GAIA_ASSERT(limit > 0);
1282 GAIA_ASSERT(queryId.pair());
1284 const auto queryRel = pair_rel(w, queryId);
1285 const auto queryTgt = pair_tgt(w, queryId);
1286 if (queryRel == EntityBad || queryTgt == EntityBad)
1289 const auto rel = resolve_raw_pair_match_token(queryRel, varsIn);
1290 const auto tgt = resolve_raw_pair_match_token(queryTgt, varsIn);
1291 const bool sameUnboundVar = rel.needsBind && tgt.needsBind && rel.bindVarIdx == tgt.bindVarIdx;
1295 if (!rel.needsBind && !tgt.needsBind && !sameUnboundVar) {
1296 const auto matchPair = Pair(
1297 rel.concrete ? Entity((EntityId)rel.matchId, 0,
true,
false, EntityKind::EK_Gen) : All,
1298 tgt.concrete ? Entity((EntityId)tgt.matchId, 0, true, false, EntityKind::EK_Gen) : All);
1299 const auto count = archetype.pair_matches(matchPair);
1300 return count < limit ? count : limit;
1304 auto archetypeIds = archetype.ids_view();
1305 const auto cnt = (uint32_t)archetypeIds.size();
1307 const auto idInArchetype = archetypeIds[i];
1308 if (!idInArchetype.pair())
1310 if (rel.concrete && idInArchetype.id() != rel.matchId)
1312 if (tgt.concrete && idInArchetype.gen() != tgt.matchId)
1314 if (sameUnboundVar && idInArchetype.id() != idInArchetype.gen())
1325 template <
typename Func>
1326 GAIA_NODISCARD
inline bool each_term_match(
1327 const World& w,
const Archetype& candidateArchetype,
const QueryCompileCtx::VarTermOp& termOp,
1328 const VarBindings& varsIn, Func&& func);
1330 GAIA_NODISCARD
inline bool next_id_match_cursor(
1331 const World& w,
const Archetype& archetype, Entity queryId,
const VarBindings& varsIn, uint32_t& idIdx,
1332 VarBindings& outVars) {
1333 auto archetypeIds = archetype.ids_view();
1334 const auto cnt = (uint32_t)archetypeIds.size();
1336 if (!queryId.pair()) {
1337 for (uint32_t i = idIdx; i < cnt; ++i) {
1338 const auto idInArchetype = archetypeIds[i];
1339 if (idInArchetype.pair())
1342 const auto value = id_entity(w, idInArchetype);
1343 if (value == EntityBad)
1347 if (!match_token(vars, queryId, value,
false))
1358 const auto queryRel = pair_rel(w, queryId);
1359 const auto queryTgt = pair_tgt(w, queryId);
1360 if (queryRel == EntityBad || queryTgt == EntityBad)
1362 const auto rel = resolve_pair_query_token(queryRel, varsIn);
1363 const auto tgt = resolve_pair_query_token(queryTgt, varsIn);
1365 for (uint32_t i = idIdx; i < cnt; ++i) {
1366 const auto idInArchetype = archetypeIds[i];
1367 if (!idInArchetype.pair())
1370 if (rel.concrete && idInArchetype.id() != rel.matchValue.id())
1372 if (tgt.concrete && idInArchetype.gen() != tgt.matchValue.id())
1376 if (rel.needsBind) {
1377 const auto relValue = pair_rel(w, idInArchetype);
1378 if (relValue == EntityBad)
1380 if (!match_token(vars, rel.token, relValue,
true))
1383 if (tgt.needsBind) {
1384 const auto tgtValue = pair_tgt(w, idInArchetype);
1385 if (tgtValue == EntityBad)
1387 if (!match_token(vars, tgt.token, tgtValue,
true))
1399 GAIA_NODISCARD
inline bool next_term_match_cursor(
1400 const World& w,
const Archetype& archetype,
const QueryCompileCtx::VarTermOp& termOp,
1401 const VarBindings& varsIn, VarTermMatchCursor& cursor, VarBindings& outVars) {
1402 const auto& term = termOp.term;
1403 if (term.src == EntityBad)
1404 return next_id_match_cursor(w, archetype, term.id, varsIn, cursor.idIdx, outVars);
1406 auto sourceEntity = term.src;
1407 if (is_var_entity(sourceEntity)) {
1408 if (!var_is_bound(varsIn, sourceEntity))
1410 sourceEntity = varsIn.values[var_index(sourceEntity)];
1414 if (cursor.source != EntityBad) {
1415 auto* pSrcArchetype = archetype_from_entity(w, cursor.source);
1416 if (pSrcArchetype !=
nullptr &&
1417 next_id_match_cursor(w, *pSrcArchetype, term.id, varsIn, cursor.idIdx, outVars))
1421 cursor.source = EntityBad;
1424 Entity nextSource = EntityBad;
1425 if (!next_lookup_src_cursor(w, termOp.sourceOpcode, term, sourceEntity, cursor.sourceCursor, nextSource))
1428 cursor.source = nextSource;
1432 GAIA_NODISCARD
inline bool term_has_match_bound(
1433 const World& w,
const Archetype& candidateArchetype,
const QueryCompileCtx::VarTermOp& termOp,
1434 const VarBindings& vars);
1436 GAIA_NODISCARD
inline bool term_has_match(
1437 const World& w,
const Archetype& archetype,
const QueryCompileCtx::VarTermOp& termOp,
1438 const VarBindings& varsIn) {
1439 if ((uint8_t)(termOp.varMask & ~varsIn.mask) == 0)
1440 return term_has_match_bound(w, archetype, termOp, varsIn);
1442 return each_term_match(w, archetype, termOp, varsIn, [&](
const VarBindings&) {
1447 GAIA_NODISCARD
inline uint32_t count_term_matches_limited(
1448 const World& w,
const Archetype& archetype,
const QueryCompileCtx::VarTermOp& termOp,
1449 const VarBindings& varsIn, uint32_t limit) {
1450 GAIA_ASSERT(limit > 0);
1452 if ((uint8_t)(termOp.varMask & ~varsIn.mask) == 0)
1453 return term_has_match_bound(w, archetype, termOp, varsIn) ? 1u : 0u;
1455 if (termOp.term.src == EntityBad && termOp.term.id.pair())
1456 return count_pair_id_matches_limited(w, archetype, termOp.term.id, varsIn, limit);
1459 (void)each_term_match(w, archetype, termOp, varsIn, [&](
const VarBindings&) {
1461 return count >= limit;
1466 GAIA_NODISCARD
inline bool has_concrete_match_id(Entity queryId) {
1467 if (!queryId.pair())
1468 return !is_variable(queryId) && queryId.id() != All.id();
1470 return !is_variable((EntityId)queryId.id()) && queryId.id() != All.id() &&
1471 !is_variable((EntityId)queryId.gen()) && queryId.gen() != All.id();
1474 GAIA_NODISCARD
inline bool
1475 match_id_bound(
const World& w,
const Archetype& archetype, Entity queryId,
const VarBindings& vars) {
1476 auto archetypeIds = archetype.ids_view();
1477 const auto cnt = (uint32_t)archetypeIds.size();
1479 if (!queryId.pair()) {
1480 Entity queryToken = queryId;
1481 if (is_var_entity(queryToken)) {
1482 if (!var_is_bound(vars, queryToken))
1484 queryToken = vars.values[var_index(queryToken)];
1488 const auto idInArchetype = archetypeIds[i];
1489 if (idInArchetype.pair())
1492 const auto value = id_entity(w, idInArchetype);
1493 if (value == EntityBad)
1495 if (queryToken.id() != value.id())
1504 auto queryRel = pair_rel(w, queryId);
1505 auto queryTgt = pair_tgt(w, queryId);
1506 if (queryRel == EntityBad || queryTgt == EntityBad)
1509 if (is_var_entity(queryRel)) {
1510 if (!var_is_bound(vars, queryRel))
1512 queryRel = vars.values[var_index(queryRel)];
1515 if (is_var_entity(queryTgt)) {
1516 if (!var_is_bound(vars, queryTgt))
1518 queryTgt = vars.values[var_index(queryTgt)];
1521 const bool relIsConcrete = queryRel.id() != All.id();
1522 const bool tgtIsConcrete = queryTgt.id() != All.id();
1524 if (relIsConcrete || tgtIsConcrete) {
1526 archetype.pair_matches(Pair(relIsConcrete ? queryRel : All, tgtIsConcrete ? queryTgt : All));
1530 if (relIsConcrete && tgtIsConcrete)
1535 const auto idInArchetype = archetypeIds[i];
1536 if (!idInArchetype.pair())
1539 if (relIsConcrete && idInArchetype.id() != queryRel.id())
1541 if (tgtIsConcrete && idInArchetype.gen() != queryTgt.id())
1544 if (!relIsConcrete) {
1545 const auto rel = pair_rel(w, idInArchetype);
1546 if (rel == EntityBad)
1549 if (!tgtIsConcrete) {
1550 const auto tgt = pair_tgt(w, idInArchetype);
1551 if (tgt == EntityBad)
1561 GAIA_NODISCARD
inline bool next_self_src_var_match_cursor(
1562 const MatchingCtx& ctx,
const QueryCompileCtx::VarTermOp& termOp,
const VarBindings& varsIn,
1563 VarTermMatchCursor& cursor, VarBindings& outVars) {
1564 GAIA_ASSERT(ctx.pWorld !=
nullptr);
1565 GAIA_ASSERT(is_var_entity(termOp.term.src));
1566 GAIA_ASSERT(!var_is_bound(varsIn, termOp.term.src));
1567 GAIA_ASSERT(termOp.sourceOpcode == EOpcode::Src_Self);
1570 for (; cursor.sourceArchetypeIdx < sourceRecords.size(); ++cursor.sourceArchetypeIdx) {
1571 const auto* pSrcArchetype = sourceRecords[cursor.sourceArchetypeIdx].pArchetype;
1572 if (pSrcArchetype ==
nullptr)
1574 if (!idsPreFiltered && !match_single_id_on_archetype(*ctx.pWorld, *pSrcArchetype, termOp.term.id))
1577 const auto& chunks = pSrcArchetype->chunks();
1578 for (; cursor.sourceChunkIdx < chunks.size(); ++cursor.sourceChunkIdx) {
1579 const auto* pChunk = chunks[cursor.sourceChunkIdx];
1580 if (pChunk ==
nullptr || pChunk->empty())
1583 const auto entities = pChunk->entity_view();
1584 for (; cursor.sourceEntityIdx < entities.size(); ++cursor.sourceEntityIdx) {
1585 const auto entity = entities[cursor.sourceEntityIdx];
1587 if (!bind_var(vars, termOp.term.src, entity))
1591 ++cursor.sourceEntityIdx;
1595 cursor.sourceEntityIdx = 0;
1598 cursor.sourceChunkIdx = 0;
1605 for (; cursor.sourceArchetypeIdx < sourceArchetypes.size(); ++cursor.sourceArchetypeIdx) {
1606 const auto* pSrcArchetype = sourceArchetypes[cursor.sourceArchetypeIdx];
1607 if (pSrcArchetype ==
nullptr)
1609 if (!match_single_id_on_archetype(*ctx.pWorld, *pSrcArchetype, termOp.term.id))
1612 const auto& chunks = pSrcArchetype->chunks();
1613 for (; cursor.sourceChunkIdx < chunks.size(); ++cursor.sourceChunkIdx) {
1614 const auto* pChunk = chunks[cursor.sourceChunkIdx];
1615 if (pChunk ==
nullptr || pChunk->empty())
1618 const auto entities = pChunk->entity_view();
1619 for (; cursor.sourceEntityIdx < entities.size(); ++cursor.sourceEntityIdx) {
1620 const auto entity = entities[cursor.sourceEntityIdx];
1622 if (!bind_var(vars, termOp.term.src, entity))
1626 ++cursor.sourceEntityIdx;
1630 cursor.sourceEntityIdx = 0;
1633 cursor.sourceChunkIdx = 0;
1639 if (!ctx.archetypeLookup.empty()) {
1640 const auto sourceArchetypes =
1641 ctx.archetypeLookup.fetch(ctx.allArchetypes, termOp.term.id, EntityLookupKey(termOp.term.id));
1642 if (adv_matches(sourceArchetypes,
true))
1644 }
else if (adv_matches_all(ctx.allArchetypes))
1650 GAIA_NODISCARD
inline bool next_src_var_match_cursor_inverse(
1651 const MatchingCtx& ctx,
const QueryCompileCtx::VarTermOp& termOp,
const VarBindings& varsIn,
1652 VarTermMatchCursor& cursor, VarBindings& outVars, EOpcode inverseOpcode) {
1653 GAIA_ASSERT(ctx.pWorld !=
nullptr);
1654 GAIA_ASSERT(is_var_entity(termOp.term.src));
1655 GAIA_ASSERT(!var_is_bound(varsIn, termOp.term.src));
1657 inverseOpcode == EOpcode::Src_Up || inverseOpcode == EOpcode::Src_Down ||
1658 inverseOpcode == EOpcode::Src_UpDown);
1661 for (; cursor.sourceArchetypeIdx < sourceRecords.size(); ++cursor.sourceArchetypeIdx) {
1662 const auto* pSrcArchetype = sourceRecords[cursor.sourceArchetypeIdx].pArchetype;
1663 if (pSrcArchetype ==
nullptr)
1665 if (!idsPreFiltered && !match_single_id_on_archetype(*ctx.pWorld, *pSrcArchetype, termOp.term.id))
1668 const auto& chunks = pSrcArchetype->chunks();
1669 for (; cursor.sourceChunkIdx < chunks.size(); ++cursor.sourceChunkIdx) {
1670 const auto* pChunk = chunks[cursor.sourceChunkIdx];
1671 if (pChunk ==
nullptr || pChunk->empty())
1674 const auto entities = pChunk->entity_view();
1675 while (cursor.sourceEntityIdx < entities.size()) {
1676 if (cursor.source == EntityBad) {
1677 cursor.source = entities[cursor.sourceEntityIdx];
1678 cursor.sourceCursor = {};
1681 Entity candidate = EntityBad;
1682 if (next_lookup_src_cursor(
1683 *ctx.pWorld, inverseOpcode, termOp.term, cursor.source, cursor.sourceCursor, candidate)) {
1685 if (!bind_var(vars, termOp.term.src, candidate))
1692 cursor.source = EntityBad;
1693 ++cursor.sourceEntityIdx;
1696 cursor.sourceEntityIdx = 0;
1699 cursor.sourceChunkIdx = 0;
1706 for (; cursor.sourceArchetypeIdx < sourceArchetypes.size(); ++cursor.sourceArchetypeIdx) {
1707 const auto* pSrcArchetype = sourceArchetypes[cursor.sourceArchetypeIdx];
1708 if (pSrcArchetype ==
nullptr)
1710 if (!match_single_id_on_archetype(*ctx.pWorld, *pSrcArchetype, termOp.term.id))
1713 const auto& chunks = pSrcArchetype->chunks();
1714 for (; cursor.sourceChunkIdx < chunks.size(); ++cursor.sourceChunkIdx) {
1715 const auto* pChunk = chunks[cursor.sourceChunkIdx];
1716 if (pChunk ==
nullptr || pChunk->empty())
1719 const auto entities = pChunk->entity_view();
1720 while (cursor.sourceEntityIdx < entities.size()) {
1721 if (cursor.source == EntityBad) {
1722 cursor.source = entities[cursor.sourceEntityIdx];
1723 cursor.sourceCursor = {};
1726 Entity candidate = EntityBad;
1727 if (next_lookup_src_cursor(
1728 *ctx.pWorld, inverseOpcode, termOp.term, cursor.source, cursor.sourceCursor, candidate)) {
1730 if (!bind_var(vars, termOp.term.src, candidate))
1737 cursor.source = EntityBad;
1738 ++cursor.sourceEntityIdx;
1741 cursor.sourceEntityIdx = 0;
1744 cursor.sourceChunkIdx = 0;
1750 if (!ctx.archetypeLookup.empty()) {
1751 const auto sourceArchetypes =
1752 ctx.archetypeLookup.fetch(ctx.allArchetypes, termOp.term.id, EntityLookupKey(termOp.term.id));
1753 if (adv_matches(sourceArchetypes,
true))
1755 }
else if (adv_matches_all(ctx.allArchetypes))
1761 GAIA_NODISCARD
inline bool next_up_src_var_match_cursor(
1762 const MatchingCtx& ctx,
const QueryCompileCtx::VarTermOp& termOp,
const VarBindings& varsIn,
1763 VarTermMatchCursor& cursor, VarBindings& outVars) {
1764 GAIA_ASSERT(ctx.pWorld !=
nullptr);
1765 GAIA_ASSERT(is_var_entity(termOp.term.src));
1766 GAIA_ASSERT(!var_is_bound(varsIn, termOp.term.src));
1767 GAIA_ASSERT(termOp.sourceOpcode == EOpcode::Src_Up);
1768 return next_src_var_match_cursor_inverse(ctx, termOp, varsIn, cursor, outVars, EOpcode::Src_Down);
1771 GAIA_NODISCARD
inline bool next_down_src_var_match_cursor(
1772 const MatchingCtx& ctx,
const QueryCompileCtx::VarTermOp& termOp,
const VarBindings& varsIn,
1773 VarTermMatchCursor& cursor, VarBindings& outVars) {
1774 GAIA_ASSERT(termOp.sourceOpcode == EOpcode::Src_Down);
1775 return next_src_var_match_cursor_inverse(ctx, termOp, varsIn, cursor, outVars, EOpcode::Src_Up);
1778 GAIA_NODISCARD
inline bool next_updown_src_var_match_cursor(
1779 const MatchingCtx& ctx,
const QueryCompileCtx::VarTermOp& termOp,
const VarBindings& varsIn,
1780 VarTermMatchCursor& cursor, VarBindings& outVars) {
1781 GAIA_ASSERT(termOp.sourceOpcode == EOpcode::Src_UpDown);
1782 return next_src_var_match_cursor_inverse(ctx, termOp, varsIn, cursor, outVars, EOpcode::Src_UpDown);
1785 GAIA_NODISCARD
inline bool next_term_match_cursor(
1786 const MatchingCtx& ctx,
const Archetype& archetype,
const QueryCompileCtx::VarTermOp& termOp,
1787 const VarBindings& varsIn, VarTermMatchCursor& cursor, VarBindings& outVars) {
1788 const auto& term = termOp.term;
1789 const bool hasUnboundVar =
1790 term.src != EntityBad && is_var_entity(term.src) && !var_is_bound(varsIn, term.src);
1791 if (hasUnboundVar && termOp.sourceOpcode == EOpcode::Src_Self) {
1792 return next_self_src_var_match_cursor(ctx, termOp, varsIn, cursor, outVars);
1794 if (hasUnboundVar && termOp.sourceOpcode == EOpcode::Src_Up) {
1795 return next_up_src_var_match_cursor(ctx, termOp, varsIn, cursor, outVars);
1797 if (hasUnboundVar && termOp.sourceOpcode == EOpcode::Src_Down) {
1798 return next_down_src_var_match_cursor(ctx, termOp, varsIn, cursor, outVars);
1800 if (hasUnboundVar && termOp.sourceOpcode == EOpcode::Src_UpDown) {
1801 return next_updown_src_var_match_cursor(ctx, termOp, varsIn, cursor, outVars);
1804 return next_term_match_cursor(*ctx.pWorld, archetype, termOp, varsIn, cursor, outVars);
1807 GAIA_NODISCARD
inline bool term_has_match_bound(
1808 const World& w,
const Archetype& candidateArchetype,
const QueryCompileCtx::VarTermOp& termOp,
1809 const VarBindings& vars) {
1810 const auto& term = termOp.term;
1811 auto match_on_archetype = [&](
const Archetype& archetype) {
1812 return match_id_bound(w, archetype, term.id, vars);
1815 if (term.src == EntityBad)
1816 return match_on_archetype(candidateArchetype);
1818 auto sourceEntity = term.src;
1819 if (is_var_entity(sourceEntity)) {
1820 if (!var_is_bound(vars, sourceEntity))
1822 sourceEntity = vars.values[var_index(sourceEntity)];
1825 return each_lookup_src(w, termOp.sourceOpcode, term, sourceEntity, [&](Entity source) {
1826 auto* pSrcArchetype = archetype_from_entity(w, source);
1827 if (pSrcArchetype == nullptr)
1829 if (!match_on_archetype(*pSrcArchetype))
1836 template <
typename Func>
1837 GAIA_NODISCARD
inline bool each_id_match(
1838 const World& w,
const Archetype& archetype, Entity queryId,
const VarBindings& varsIn, Func&& func) {
1839 auto archetypeIds = archetype.ids_view();
1840 const auto cnt = (uint32_t)archetypeIds.size();
1842 if (!queryId.pair()) {
1844 const auto idInArchetype = archetypeIds[i];
1845 if (idInArchetype.pair())
1848 const auto value = id_entity(w, idInArchetype);
1849 if (value == EntityBad)
1853 if (!match_token(vars, queryId, value,
false))
1862 const auto queryRel = pair_rel(w, queryId);
1863 const auto queryTgt = pair_tgt(w, queryId);
1864 if (queryRel == EntityBad || queryTgt == EntityBad)
1866 const auto rel = resolve_pair_query_token(queryRel, varsIn);
1867 const auto tgt = resolve_pair_query_token(queryTgt, varsIn);
1870 const auto idInArchetype = archetypeIds[i];
1871 if (!idInArchetype.pair())
1874 if (rel.concrete && idInArchetype.id() != rel.matchValue.id())
1876 if (tgt.concrete && idInArchetype.gen() != tgt.matchValue.id())
1879 if (!rel.needsBind && !tgt.needsBind) {
1886 if (rel.needsBind) {
1887 const auto relValue = pair_rel(w, idInArchetype);
1888 if (relValue == EntityBad)
1890 if (!match_token(vars, rel.token, relValue,
true))
1893 if (tgt.needsBind) {
1894 const auto tgtValue = pair_tgt(w, idInArchetype);
1895 if (tgtValue == EntityBad)
1897 if (!match_token(vars, tgt.token, tgtValue,
true))
1908 template <
typename Func>
1909 GAIA_NODISCARD
inline bool each_term_match(
1910 const World& w,
const Archetype& candidateArchetype,
const QueryCompileCtx::VarTermOp& termOp,
1911 const VarBindings& varsIn, Func&& func) {
1912 const auto& term = termOp.term;
1913 auto&& matchFunc = GAIA_FWD(func);
1914 auto each_on_src = [&](Entity sourceEntity,
const VarBindings& vars) {
1915 return each_lookup_src(w, termOp.sourceOpcode, term, sourceEntity, [&](Entity source) {
1916 auto* pSrcArchetype = archetype_from_entity(w, source);
1917 if (pSrcArchetype == nullptr)
1920 return each_id_match(w, *pSrcArchetype, term.id, vars, matchFunc);
1924 if (term.src == EntityBad)
1925 return each_id_match(w, candidateArchetype, term.id, varsIn, matchFunc);
1927 if (is_var_entity(term.src)) {
1928 if (!var_is_bound(varsIn, term.src))
1931 const auto source = varsIn.values[var_index(term.src)];
1932 return each_on_src(source, varsIn);
1935 return each_on_src(term.src, varsIn);
1938 template <
typename OpKind, MatchingStyle Style>
1940 if constexpr (Style != MatchingStyle::Complex) {
1941 if (ctx.idsToMatch.size() == 1) {
1942 for (
const auto& entry: records) {
1943 const auto* pArchetype = entry.pArchetype;
1944 if (is_archetype_marked(ctx, pArchetype))
1946#if GAIA_USE_PARTITIONED_BLOOM_FILTER >= 0
1947 if constexpr (Style == MatchingStyle::Simple) {
1948 if (!OpKind::check_mask(pArchetype->queryMask(), ctx.queryMask))
1952 mark_archetype_match(ctx, pArchetype);
1958 if constexpr (Style == MatchingStyle::Complex) {
1959 for (
const auto& record: records) {
1960 const auto* pArchetype = record.pArchetype;
1961 if (is_archetype_marked(ctx, pArchetype))
1964 if (!match_res_as<OpKind>(*ctx.pWorld, *pArchetype, ctx.idsToMatch))
1967 mark_archetype_match(ctx, pArchetype);
1970#if GAIA_USE_PARTITIONED_BLOOM_FILTER >= 0
1971 else if constexpr (Style == MatchingStyle::Simple) {
1972 for (
const auto& record: records) {
1973 const auto* pArchetype = record.pArchetype;
1974 if (is_archetype_marked(ctx, pArchetype))
1978 if (!OpKind::check_mask(pArchetype->queryMask(), ctx.queryMask))
1981 if (!match_res<OpKind>(*pArchetype, ctx.idsToMatch))
1984 mark_archetype_match(ctx, pArchetype);
1989 for (
const auto& record: records) {
1990 const auto* pArchetype = record.pArchetype;
1991 if (is_archetype_marked(ctx, pArchetype))
1994 if (!match_res<OpKind>(*pArchetype, ctx.idsToMatch))
1997 mark_archetype_match(ctx, pArchetype);
2002 template <
typename OpKind, MatchingStyle Style>
2004 if constexpr (Style == MatchingStyle::Complex) {
2005 for (
const auto* pArchetype: archetypes) {
2006 if (is_archetype_marked(ctx, pArchetype))
2009 if (!match_res_as<OpKind>(*ctx.pWorld, *pArchetype, ctx.idsToMatch))
2012 mark_archetype_match(ctx, pArchetype);
2015#if GAIA_USE_PARTITIONED_BLOOM_FILTER >= 0
2016 else if constexpr (Style == MatchingStyle::Simple) {
2017 for (
const auto* pArchetype: archetypes) {
2018 if (is_archetype_marked(ctx, pArchetype))
2021 if (!OpKind::check_mask(pArchetype->queryMask(), ctx.queryMask))
2024 if (!match_res<OpKind>(*pArchetype, ctx.idsToMatch))
2027 mark_archetype_match(ctx, pArchetype);
2032 for (
const auto* pArchetype: archetypes) {
2033 if (is_archetype_marked(ctx, pArchetype))
2036 if (!match_res<OpKind>(*pArchetype, ctx.idsToMatch))
2039 mark_archetype_match(ctx, pArchetype);
2044 template <
typename OpKind, MatchingStyle Style>
2045 inline void match_archetype_inter(
2047 const uint32_t lookupRevision = ctx.archetypeLookup.revision(entityKey);
2048 uint32_t lastMatchedIdx = OpKind::handle_last_match(ctx, entityKey, (uint32_t)records.size(), lookupRevision);
2049 if (lastMatchedIdx >= records.size())
2052 auto recordsNew =
std::span(&records[lastMatchedIdx], records.size() - lastMatchedIdx);
2053 match_archetype_inter<OpKind, Style>(ctx, recordsNew);
2056 template <
typename OpKind, MatchingStyle Style>
2059 const uint32_t lookupRevision = ctx.archetypeLookup.revision(entityKey);
2060 uint32_t lastMatchedIdx =
2061 OpKind::handle_last_match(ctx, entityKey, (uint32_t)archetypes.size(), lookupRevision);
2062 if (lastMatchedIdx >= archetypes.size())
2065 auto archetypesNew =
std::span(&archetypes[lastMatchedIdx], archetypes.size() - lastMatchedIdx);
2066 match_archetype_inter<OpKind, Style>(ctx, archetypesNew);
2069 template <MatchingStyle Style>
2070 inline void match_archetype_all(MatchingCtx& ctx) {
2071 if constexpr (Style == MatchingStyle::Complex) {
2074 if (ctx.ent.id() == Is.id()) {
2075 ctx.ent = EntityBad;
2076 match_archetype_inter<OpAll, Style>(ctx, EntityBadLookupKey, ctx.allArchetypes);
2078 auto entityKey = EntityLookupKey(ctx.ent);
2080 auto archetypes = ctx.archetypeLookup.fetch(ctx.allArchetypes, ctx.ent, entityKey);
2081 if (archetypes.empty())
2084 match_archetype_inter<OpAll, Style>(ctx, entityKey, archetypes);
2087 auto entityKey = EntityLookupKey(ctx.ent);
2091 auto archetypes = ctx.archetypeLookup.fetch(ctx.allArchetypes, ctx.ent, entityKey);
2092 if (archetypes.empty())
2095 match_archetype_inter<OpAll, Style>(ctx, entityKey, archetypes);
2099 template <MatchingStyle Style>
2100 inline void match_archetype_or(MatchingCtx& ctx) {
2101 EntityLookupKey entityKey(ctx.ent);
2106 auto archetypes = ctx.archetypeLookup.fetch(ctx.allArchetypes, ctx.ent, entityKey);
2107 if (archetypes.empty())
2110 match_archetype_inter<OpOr, Style>(ctx, entityKey, archetypes);
2113 inline void match_archetype_or_as(MatchingCtx& ctx) {
2114 EntityLookupKey entityKey = EntityBadLookupKey;
2119 if (ctx.ent.id() == Is.id()) {
2120 ctx.ent = EntityBad;
2121 match_archetype_inter<OpOr, MatchingStyle::Complex>(ctx, entityKey, ctx.allArchetypes);
2123 entityKey = EntityLookupKey(ctx.ent);
2125 auto archetypes = ctx.archetypeLookup.fetch(ctx.allArchetypes, ctx.ent, entityKey);
2126 if (archetypes.empty())
2129 match_archetype_inter<OpOr, MatchingStyle::Complex>(ctx, entityKey, archetypes);
2133 template <MatchingStyle Style>
2134 inline void match_archetype_no_2(MatchingCtx& ctx) {
2138 if constexpr (Style == MatchingStyle::Complex) {
2139 for (uint32_t i = 0; i < ctx.pMatchesArr->size();) {
2140 const auto* pArchetype = (*ctx.pMatchesArr)[i];
2142 if (match_res_as<OpNo>(*ctx.pWorld, *pArchetype, ctx.idsToMatch)) {
2147 core::swap_erase(*ctx.pMatchesArr, i);
2150#if GAIA_USE_PARTITIONED_BLOOM_FILTER >= 0
2151 else if constexpr (Style == MatchingStyle::Simple) {
2152 for (uint32_t i = 0; i < ctx.pMatchesArr->size();) {
2153 const auto* pArchetype = (*ctx.pMatchesArr)[i];
2156 if (OpNo::check_mask(pArchetype->queryMask(), ctx.queryMask))
2159 if (match_res<OpNo>(*pArchetype, ctx.idsToMatch)) {
2164 core::swap_erase(*ctx.pMatchesArr, i);
2169 for (uint32_t i = 0; i < ctx.pMatchesArr->size();) {
2170 const auto* pArchetype = (*ctx.pMatchesArr)[i];
2172 if (match_res<OpNo>(*pArchetype, ctx.idsToMatch)) {
2177 core::swap_erase(*ctx.pMatchesArr, i);
2182 template <
typename OpKind, MatchingStyle Style,
bool WildcardWithAsFallback = false>
2183 inline void filter_current_matches(MatchingCtx& ctx, EntitySpan idsToMatch) {
2184 if constexpr (Style == MatchingStyle::Complex) {
2185 for (uint32_t i = 0; i < ctx.pMatchesArr->size();) {
2186 const auto* pArchetype = (*ctx.pMatchesArr)[i];
2187 if (match_res_as<OpKind>(*ctx.pWorld, *pArchetype, idsToMatch)) {
2192 core::swap_erase(*ctx.pMatchesArr, i);
2195#if GAIA_USE_PARTITIONED_BLOOM_FILTER >= 0
2196 else if constexpr (Style == MatchingStyle::Simple) {
2197 for (uint32_t i = 0; i < ctx.pMatchesArr->size();) {
2198 const auto* pArchetype = (*ctx.pMatchesArr)[i];
2199 if (OpKind::check_mask(pArchetype->queryMask(), ctx.queryMask) &&
2200 match_res<OpKind>(*pArchetype, idsToMatch)) {
2205 core::swap_erase(*ctx.pMatchesArr, i);
2210 for (uint32_t i = 0; i < ctx.pMatchesArr->size();) {
2211 const auto* pArchetype = (*ctx.pMatchesArr)[i];
2212 if (match_res<OpKind>(*pArchetype, idsToMatch) ||
2213 (WildcardWithAsFallback && match_res_as<OpKind>(*ctx.pWorld, *pArchetype, idsToMatch))) {
2218 core::swap_erase(*ctx.pMatchesArr, i);
2223 template <MatchingStyle Style>
2224 GAIA_NODISCARD
inline bool exec_not_impl(
const QueryCompileCtx& comp, MatchingCtx& ctx) {
2225 ctx.idsToMatch =
std::span{comp.ids_not.data(), comp.ids_not.size()};
2227 if (ctx.targetEntities.empty()) {
2229 if (ctx.pMatchesArr->empty()) {
2232 match_archetype_inter<detail::OpNo, Style>(ctx, EntityBadLookupKey, ctx.allArchetypes);
2234 match_archetype_no_2<Style>(ctx);
2238 if (ctx.pMatchesArr->empty())
2239 match_archetype_inter<detail::OpNo, Style>(ctx, ctx.allArchetypes);
2241 match_archetype_no_2<Style>(ctx);
2247 template <MatchingStyle Style>
2248 GAIA_NODISCARD
inline bool exec_all_impl(
const QueryCompileCtx& comp, MatchingCtx& ctx) {
2249 ctx.ent = comp.ids_all[0];
2250 ctx.idsToMatch =
std::span{comp.ids_all.data(), comp.ids_all.size()};
2252 if (ctx.targetEntities.empty())
2253 match_archetype_all<Style>(ctx);
2255 match_archetype_inter<OpAll, Style>(ctx, ctx.allArchetypes);
2258 return !ctx.pMatchesArr->empty();
2261 template <MatchingStyle Style>
2262 GAIA_NODISCARD
inline bool exec_or_noall_impl(
const QueryCompileCtx& comp, MatchingCtx& ctx) {
2266 const auto cnt = comp.ids_or.size();
2269 ctx.ent = comp.ids_or[i];
2270 const Entity idsToMatchData[1] = {ctx.ent};
2271 ctx.idsToMatch = EntitySpan{idsToMatchData, 1};
2273 if constexpr (Style == MatchingStyle::Complex)
2274 match_archetype_or_as(ctx);
2276 match_archetype_or<Style>(ctx);
2282 template <MatchingStyle Style>
2283 GAIA_NODISCARD
inline bool exec_or_withall_impl(
const QueryCompileCtx& comp, MatchingCtx& ctx) {
2287 ctx.idsToMatch =
std::span{comp.ids_or.data(), comp.ids_or.size()};
2289 if constexpr (Style == MatchingStyle::Complex)
2290 filter_current_matches<OpOr, MatchingStyle::Complex>(ctx, ctx.idsToMatch);
2291 else if constexpr (Style == MatchingStyle::Simple)
2292 filter_current_matches<OpOr, MatchingStyle::Simple>(ctx, ctx.idsToMatch);
2294 filter_current_matches<OpOr, MatchingStyle::Wildcard, true>(ctx, ctx.idsToMatch);
2299 template <
typename SourceTermsArray>
2300 GAIA_NODISCARD
inline const QueryCompileCtx::SourceTermOp&
2301 get_src_term_op(
const QueryCompileCtx& comp,
const MatchingCtx& ctx,
const SourceTermsArray& terms) {
2302 const auto& stackItem = comp.ops[ctx.pc];
2303 GAIA_ASSERT(stackItem.arg < terms.size());
2304 return terms[stackItem.arg];
2312 static constexpr uint32_t OpcodeArgLimit = 256u;
2314 MAX_ITEMS_IN_QUERY <= OpcodeArgLimit,
2315 "CompiledOp::arg is uint8_t. Increase arg width if query term capacity grows above 256.");
2320 static const char* opcode_name(detail::EOpcode opcode) {
2321 static const char* s_names[] = {
2346 "term_all_src_bind",
2355 "search_other_or_bind",
2358 "search_maybe_finalize",
2365 sizeof(s_names) /
sizeof(s_names[0]) == (uint32_t)detail::EOpcode::Var_Final_Success + 1u,
2366 "Opcode name table out of sync with EOpcode.");
2367 return s_names[(uint32_t)opcode];
2370 GAIA_NODISCARD
static bool opcode_has_arg(detail::EOpcode opcode) {
2371 return opcode == detail::EOpcode::Src_AllTerm ||
2372 opcode == detail::EOpcode::Src_NotTerm ||
2373 opcode == detail::EOpcode::Src_OrTerm;
2376 static void add_uint(
util::str& out, uint32_t value) {
2378 const auto len = GAIA_STRFMT(buffer,
sizeof(buffer),
"%u", value);
2379 GAIA_ASSERT(len >= 0);
2380 out.
append(buffer, (uint32_t)len);
2383 static void add_cstr(
util::str& out,
const char* value) {
2384 GAIA_ASSERT(value !=
nullptr);
2385 out.
append(value, (uint32_t)GAIA_STRLEN(value, 64));
2388 static void add_id_expr(
util::str& out,
const World& world, EntityId
id) {
2389 if (is_variable(
id)) {
2391 add_uint(out, (uint32_t)(
id - Var0.id()));
2395 if (
id == All.id()) {
2400 const auto entity = entity_from_id(world,
id);
2401 if (entity != EntityBad)
2402 add_entity_expr(out, world, entity);
2405 add_uint(out, (uint32_t)
id);
2410 if (entity == EntityBad) {
2415 if (entity.pair()) {
2417 add_id_expr(out, world, (EntityId)entity.id());
2419 add_id_expr(out, world, (EntityId)entity.gen());
2424 if (is_variable(EntityId(entity.id()))) {
2426 add_uint(out, (uint32_t)(entity.id() - Var0.id()));
2430 if (entity.id() == All.id()) {
2435 const auto name = entity_name(world, entity);
2436 if (!name.empty()) {
2437 out.
append(name.data(), name.size());
2441 add_uint(out, entity.id());
2443 add_uint(out, entity.gen());
2447 add_entity_expr(out, world, term.
id);
2449 if (term.
src == EntityBad)
2452 add_entity_expr(out, world, term.
src);
2455 if (term.
entTrav != EntityBad) {
2457 add_entity_expr(out, world, term.
entTrav);
2459 if (term.
travDepth == QueryTermOptions::TravDepthUnlimited)
2462 add_uint(out, (uint32_t)term.
travDepth);
2471 add_cstr(out, title);
2473 add_uint(out, (uint32_t)ids.size());
2476 const auto cnt = (uint32_t)ids.size();
2481 add_entity_expr(out, world, ids[i]);
2486 static void add_src_terms_section(
2489 const World& world) {
2493 add_cstr(out, title);
2495 add_uint(out, (uint32_t)terms.size());
2498 const auto cnt = (uint32_t)terms.size();
2503 add_cstr(out, opcode_name(terms[i].opcode));
2505 add_term_expr(out, world, terms[i].term);
2510 static void add_var_terms_section(
2516 add_cstr(out, title);
2518 add_uint(out, (uint32_t)terms.size());
2521 const auto cnt = (uint32_t)terms.size();
2526 add_cstr(out, opcode_name(terms[i].sourceOpcode));
2528 add_term_expr(out, world, terms[i].term);
2535 switch (detail::var_program_opcode_meta(op.
opcode).termSet) {
2536 case detail::EVarProgramTermSet::None:
2539 case detail::EVarProgramTermSet::Or:
2541 case detail::EVarProgramTermSet::Any:
2543 case detail::EVarProgramTermSet::Not:
2545 case detail::EVarProgramTermSet::All:
2551 static void add_var_program_ops_section(
2557 add_cstr(out, title);
2559 add_uint(out, (uint32_t)ops.size());
2562 const auto cnt = (uint32_t)ops.size();
2564 const auto& op = ops[i];
2568 add_cstr(out, opcode_name(op.
opcode));
2569 if (detail::var_program_opcode_meta(op.
opcode).termSet != detail::EVarProgramTermSet::None) {
2571 add_uint(out, (uint32_t)op.
arg);
2573 add_uint(out, (uint32_t)op.
cost);
2575 add_term_expr(out, world, var_program_op_term(comp, op));
2578 add_uint(out, (uint32_t)op.
pc_ok);
2580 add_uint(out, (uint32_t)op.
pc_fail);
2589 out.
append(
"var_exec: ");
2607 [[maybe_unused]]
const auto len = GAIA_STRFMT(title,
sizeof(title),
"varp%u", i);
2608 GAIA_ASSERT(len > 0);
2609 add_var_program_ops_section(out, title, detail::program_ops(comp, step.program), comp, world);
2617 GAIA_FOR(MaxVarCnt) {
2618 const auto bit = (uint8_t(1) << i);
2619 if ((vars.mask & bit) == 0)
2626 GAIA_NODISCARD
static uint8_t
2630 if (detail::is_var_entity(term.
src) && !detail::var_is_bound(vars, term.
src))
2631 mask |= (uint8_t(1) << detail::var_index(term.
src));
2633 if (!term.
id.pair()) {
2634 const auto idEnt = id_entity(world, term.
id);
2635 if (detail::is_var_entity(idEnt) && !detail::var_is_bound(vars, idEnt))
2636 mask |= (uint8_t(1) << detail::var_index(idEnt));
2640 const auto relEnt = pair_rel(world, term.
id);
2641 if (detail::is_var_entity(relEnt) && !detail::var_is_bound(vars, relEnt))
2642 mask |= (uint8_t(1) << detail::var_index(relEnt));
2644 const auto tgtEnt = pair_tgt(world, term.
id);
2645 if (detail::is_var_entity(tgtEnt) && !detail::var_is_bound(vars, tgtEnt))
2646 mask |= (uint8_t(1) << detail::var_index(tgtEnt));
2651 GAIA_NODISCARD
bool eval_variable_terms_program_on_archetype(
2655 return match_search_program_on_archetype(ctx, archetype, programStep, orAlreadySatisfied);
2661 case detail::EOpcode::Var_Term_Or_Check:
2662 case detail::EOpcode::Var_Term_Or_Bind:
2663 case detail::EOpcode::Var_Final_Or_Check:
2665 case detail::EOpcode::Var_Term_Any_Check:
2666 case detail::EOpcode::Var_Term_Any_Bind:
2668 case detail::EOpcode::Var_Term_Not:
2669 case detail::EOpcode::Var_Final_Not_Check:
2671 case detail::EOpcode::Var_Term_All_Check:
2672 case detail::EOpcode::Var_Term_All_Bind:
2673 case detail::EOpcode::Var_Term_All_Src_Bind:
2681 GAIA_NODISCARD
bool select_next_pending_search_all_term(
2683 uint16_t pendingMask,
const detail::VarBindings& vars, uint32_t& outLocalIdx, uint32_t& outPc,
2684 bool preferBoundTerms =
true)
const {
2685 outLocalIdx = (uint32_t)-1;
2686 outPc = (uint32_t)-1;
2687 uint32_t firstReadyLocalIdx = (uint32_t)-1;
2688 uint32_t firstReadyPc = (uint32_t)-1;
2690 for (uint32_t localIdx = 0; localIdx < search.allCount; ++localIdx) {
2691 const auto bit = (uint16_t)(uint16_t(1) << localIdx);
2692 if ((pendingMask & bit) == 0)
2695 const auto bindPc = (uint32_t)search.allBegin + localIdx;
2696 const auto& bindOp = programOps[bindPc];
2697 const auto& termOp = search_program_term_op(bindOp);
2698 if (detail::is_var_entity(termOp.term.src) && !detail::var_is_bound(vars, termOp.term.src) &&
2699 bindOp.opcode != detail::EOpcode::Var_Term_All_Src_Bind)
2702 const bool bindsNewVars = (uint8_t)(termOp.varMask & ~vars.mask) != 0;
2703 const auto pc = bindsNewVars ? bindPc : (uint32_t)search.allCheckBegin + localIdx;
2704 if (preferBoundTerms && !bindsNewVars) {
2705 outLocalIdx = localIdx;
2710 if (firstReadyLocalIdx == (uint32_t)-1) {
2711 firstReadyLocalIdx = localIdx;
2716 if (firstReadyLocalIdx == (uint32_t)-1)
2719 outLocalIdx = firstReadyLocalIdx;
2720 outPc = firstReadyPc;
2724 GAIA_NODISCARD
bool select_next_pending_search_or_term(
2726 uint16_t pendingMask, uint16_t pendingCheckMask,
const detail::VarBindings& vars,
bool preferBoundTerms,
2727 bool requireNewBindings, uint32_t& outLocalIdx, uint32_t& outPc)
const {
2728 outLocalIdx = (uint32_t)-1;
2729 outPc = (uint32_t)-1;
2730 if (requireNewBindings && (uint8_t)(search.orVarMask & ~vars.mask) == 0)
2733 uint32_t firstReadyLocalIdx = (uint32_t)-1;
2734 uint32_t firstReadyPc = (uint32_t)-1;
2736 for (uint32_t localIdx = 0; localIdx < search.orCount; ++localIdx) {
2737 const auto bit = (uint16_t)(uint16_t(1) << localIdx);
2738 if ((pendingMask & bit) == 0)
2741 const auto bindPc = (uint32_t)search.orBegin + localIdx;
2742 const auto& bindOp = programOps[bindPc];
2743 const auto& termOp = search_program_term_op(bindOp);
2744 if (detail::is_var_entity(termOp.term.src) && !detail::var_is_bound(vars, termOp.term.src))
2747 const bool bindsNewVars = (uint8_t)(termOp.varMask & ~vars.mask) != 0;
2748 if (requireNewBindings) {
2751 outLocalIdx = localIdx;
2756 if (!bindsNewVars && (pendingCheckMask & bit) == 0)
2759 const auto pc = bindsNewVars ? bindPc : (uint32_t)search.orCheckBegin + localIdx;
2760 if (preferBoundTerms && !bindsNewVars) {
2761 outLocalIdx = localIdx;
2766 if (firstReadyLocalIdx == (uint32_t)-1) {
2767 firstReadyLocalIdx = localIdx;
2772 if (firstReadyLocalIdx == (uint32_t)-1)
2775 outLocalIdx = firstReadyLocalIdx;
2776 outPc = firstReadyPc;
2780 GAIA_NODISCARD
bool select_next_pending_search_any_term(
2782 uint16_t pendingMask,
const detail::VarBindings& vars, uint32_t& outLocalIdx, uint32_t& outPc)
const {
2783 outLocalIdx = (uint32_t)-1;
2784 outPc = (uint32_t)-1;
2785 uint32_t firstReadyBindingLocalIdx = (uint32_t)-1;
2786 uint32_t firstReadyBindingPc = (uint32_t)-1;
2788 for (uint32_t localIdx = 0; localIdx < search.anyCount; ++localIdx) {
2789 const auto bit = (uint16_t)(uint16_t(1) << localIdx);
2790 if ((pendingMask & bit) == 0)
2793 const auto bindPc = (uint32_t)search.anyBegin + localIdx;
2794 const auto& bindOp = programOps[bindPc];
2795 const auto& termOp = search_program_term_op(bindOp);
2796 if (detail::is_var_entity(termOp.term.src) && !detail::var_is_bound(vars, termOp.term.src))
2799 const bool bindsNewVars = (uint8_t)(termOp.varMask & ~vars.mask) != 0;
2800 if (!bindsNewVars) {
2801 outLocalIdx = localIdx;
2802 outPc = (uint32_t)search.anyCheckBegin + localIdx;
2806 if (firstReadyBindingLocalIdx == (uint32_t)-1) {
2807 firstReadyBindingLocalIdx = localIdx;
2808 firstReadyBindingPc = bindPc;
2812 if (firstReadyBindingLocalIdx == (uint32_t)-1)
2815 outLocalIdx = firstReadyBindingLocalIdx;
2816 outPc = firstReadyBindingPc;
2820 GAIA_NODISCARD int32_t select_best_pending_search_term(
2823 uint32_t& outBestIdx)
const {
2824 constexpr uint32_t MatchProbeLimit = 64;
2825 outBestIdx = (uint32_t)-1;
2826 uint32_t bestMatchCnt = MatchProbeLimit + 1;
2828 for (uint32_t localIdx = 0; localIdx < count; ++localIdx) {
2829 const auto i = (uint32_t)begin + localIdx;
2830 const auto bit = (uint16_t)(uint16_t(1) << i);
2831 if ((pendingMask & bit) == 0)
2834 const auto& termOp = search_program_term_op(programOps[i]);
2835 if (detail::is_var_entity(termOp.term.src) && !detail::var_is_bound(vars, termOp.term.src))
2838 const auto matchCnt =
2839 detail::count_term_matches_limited(*ctx.
pWorld, archetype, termOp, vars, bestMatchCnt);
2843 if (outBestIdx == (uint32_t)-1 || matchCnt < bestMatchCnt) {
2845 bestMatchCnt = matchCnt;
2846 if (bestMatchCnt == 1)
2851 return outBestIdx == (uint32_t)-1 ? 0 : 1;
2854 GAIA_NODISCARD
bool can_skip_pending_search_all(
2857 const auto anyVarMask = m_compCtx.varMaskAny;
2858 for (uint32_t localIdx = 0; localIdx < search.allCount; ++localIdx) {
2859 const auto i = (uint32_t)search.allBegin + localIdx;
2860 const auto bit = (uint16_t(1) << i);
2861 if ((pendingAllMask & bit) == 0)
2864 const auto& termOp = search_program_term_op(programOps[i]);
2865 const auto missingMask = (uint8_t)(termOp.varMask & ~vars.mask);
2866 if (missingMask == 0)
2868 if ((missingMask & ~anyVarMask) != 0)
2875 GAIA_NODISCARD
bool match_search_program_on_archetype(
2878 using namespace detail;
2880 static constexpr uint16_t BacktrackPC = (uint16_t)-1;
2882 struct SearchProgramState {
2884 uint16_t pendingAllMask = 0;
2885 uint16_t pendingOrMask = 0;
2886 uint16_t pendingFinalOrMask = 0;
2887 uint16_t pendingAnyMask = 0;
2888 uint16_t pc = BacktrackPC;
2889 uint8_t termOpIdx = 0xff;
2890 uint8_t bestOrIdx = 0xff;
2891 uint8_t scanIdx = 0;
2892 bool orMatched =
false;
2893 bool anyMatched =
false;
2894 bool currentAnyMatched =
false;
2897 struct SearchBacktrackFrame {
2898 SearchProgramState state{};
2899 VarBindings varsBase{};
2900 VarTermMatchCursor cursor{};
2903 const auto& program = programStep.program;
2904 const auto& search = programStep.search;
2905 const auto programOps = detail::program_ops(m_compCtx, program);
2906 if (programOps.empty())
2908 GAIA_ASSERT(search.selectAllPc != BacktrackPC);
2909 GAIA_ASSERT(search.selectOrPc != BacktrackPC);
2910 GAIA_ASSERT(search.selectOtherOrPc != BacktrackPC);
2911 GAIA_ASSERT(search.selectOtherOrBindPc != BacktrackPC);
2912 GAIA_ASSERT(search.beginAnyPc != BacktrackPC);
2913 GAIA_ASSERT(search.selectAnyPc != BacktrackPC);
2914 GAIA_ASSERT(search.maybeFinalizePc != BacktrackPC);
2916 const auto is_term_ready = [&](
const detail::CompiledOp& op,
const VarBindings& vars) {
2917 const auto& termOp = search_program_term_op(op);
2918 return !is_var_entity(termOp.term.src) || var_is_bound(vars, termOp.term.src) ||
2919 op.
opcode == EOpcode::Var_Term_All_Src_Bind;
2922 const auto can_binding_satisfy_pending_or = [&](
const SearchProgramState& state,
2923 const VarBindings& nextVars) {
2924 if (state.orMatched || search.orCount == 0 || state.pendingOrMask == 0)
2927 bool hasDeferredOr =
false;
2928 for (uint32_t localIdx = 0; localIdx < search.orCount; ++localIdx) {
2929 const auto bit = (uint16_t)(uint16_t(1) << localIdx);
2930 if ((state.pendingOrMask & bit) == 0)
2933 const auto bindPc = (uint32_t)search.orBegin + localIdx;
2934 const auto& bindOp = programOps[bindPc];
2935 const auto& termOp = search_program_term_op(bindOp);
2936 const auto missingMaskBefore = (uint8_t)(termOp.varMask & ~state.vars.mask);
2937 const auto missingMaskAfter = (uint8_t)(termOp.varMask & ~nextVars.mask);
2938 if (missingMaskAfter != 0) {
2939 hasDeferredOr =
true;
2943 if (missingMaskBefore == 0 && (state.pendingFinalOrMask & bit) == 0)
2946 if (term_has_match(*ctx.
pWorld, archetype, termOp, nextVars))
2950 return hasDeferredOr;
2953 const auto adv_after_search_term_success = [&](SearchProgramState& state,
const detail::CompiledOp& op,
2954 VarBindings nextVars) {
2955 const auto bit = (uint16_t)(uint16_t(1) << state.termOpIdx);
2956 state.vars = nextVars;
2958 case EOpcode::Var_Term_All_Check:
2959 case EOpcode::Var_Term_All_Bind:
2960 case EOpcode::Var_Term_All_Src_Bind:
2961 state.pendingAllMask = (uint16_t)(state.pendingAllMask & ~bit);
2962 state.pc = op.
pc_ok;
2964 case EOpcode::Var_Term_Or_Check:
2965 case EOpcode::Var_Term_Or_Bind:
2966 state.pendingOrMask = (uint16_t)(state.pendingOrMask & ~(uint16_t(1) << state.termOpIdx));
2967 state.pendingFinalOrMask = (uint16_t)(state.pendingFinalOrMask & ~(uint16_t(1) << state.termOpIdx));
2968 state.orMatched =
true;
2969 state.pc = op.
pc_ok;
2971 case EOpcode::Var_Term_Any_Check:
2972 case EOpcode::Var_Term_Any_Bind:
2973 state.pendingAnyMask = (uint16_t)(state.pendingAnyMask & ~(uint16_t(1) << state.termOpIdx));
2974 state.anyMatched =
true;
2975 state.currentAnyMatched =
true;
2976 state.pc = op.
pc_ok;
2980 state.pc = BacktrackPC;
2985 const auto handle_search_term_exhausted = [&](SearchProgramState& state,
const detail::CompiledOp& op) {
2986 if (op.
opcode == EOpcode::Var_Term_Any_Check || op.
opcode == EOpcode::Var_Term_Any_Bind) {
2987 state.pendingAnyMask = (uint16_t)(state.pendingAnyMask & ~(uint16_t(1) << state.termOpIdx));
2992 const auto try_enter_search_term = [&](SearchProgramState& state,
2994 const auto& op = programOps[state.pc];
2995 const auto& termOp = search_program_term_op(op);
2996 SearchBacktrackFrame frame{};
2997 frame.state = state;
2998 frame.varsBase = state.vars;
3000 VarBindings nextVars{};
3002 if (!detail::next_term_match_cursor(ctx, archetype, termOp, frame.varsBase, frame.cursor, nextVars))
3004 if (can_binding_satisfy_pending_or(state, nextVars))
3008 if (op.
opcode == EOpcode::Var_Term_Any_Check || op.
opcode == EOpcode::Var_Term_Any_Bind) {
3009 frame.state.anyMatched =
true;
3010 frame.state.currentAnyMatched =
true;
3013 stack.push_back(GAIA_MOV(frame));
3014 adv_after_search_term_success(state, op, nextVars);
3018 const auto backtrack = [&](SearchProgramState& state,
3020 while (!stack.empty()) {
3021 auto& frame = stack.back();
3022 const auto resumeState = frame.state;
3023 const auto& op = programOps[resumeState.pc];
3024 const auto& termOp = search_program_term_op(op);
3025 VarBindings nextVars{};
3027 if (detail::next_term_match_cursor(ctx, archetype, termOp, frame.varsBase, frame.cursor, nextVars)) {
3028 if (op.
opcode == EOpcode::Var_Term_Any_Check || op.
opcode == EOpcode::Var_Term_Any_Bind) {
3029 frame.state.anyMatched =
true;
3030 frame.state.currentAnyMatched =
true;
3033 state = frame.state;
3034 adv_after_search_term_success(state, op, nextVars);
3039 state = resumeState;
3040 handle_search_term_exhausted(state, op);
3041 if (state.pc != BacktrackPC)
3049 SearchProgramState state{};
3050 state.vars = make_initial_var_bindings(ctx);
3051 state.pendingAllMask = search.initialAllMask;
3052 state.pendingOrMask = search.initialOrMask;
3053 state.pendingFinalOrMask = search.initialOrMask;
3054 state.pendingAnyMask = search.initialAnyMask;
3055 state.pc = search.selectAllPc;
3058 if (state.pc == BacktrackPC) {
3059 if (!backtrack(state, stack))
3063 const auto& op = programOps[state.pc];
3065 case EOpcode::Var_Search_SelectAll: {
3066 if (state.pendingAllMask == 0) {
3071 if (search.orCount == 0 && search.anyCount == 0) {
3072 uint32_t nextAllLocalIdx = (uint32_t)-1;
3073 uint32_t nextAllPc = (uint32_t)-1;
3074 if (select_next_pending_search_all_term(
3075 programOps, search, state.pendingAllMask, state.vars, nextAllLocalIdx, nextAllPc)) {
3076 const auto bindPc = (uint32_t)search.allBegin + nextAllLocalIdx;
3077 if (nextAllPc != bindPc) {
3078 state.termOpIdx = (uint8_t)nextAllLocalIdx;
3079 state.pc = (uint16_t)nextAllPc;
3084 uint32_t bestAllIdx = (uint32_t)-1;
3085 const auto allSel = select_best_pending_search_term(
3086 ctx, archetype, programOps, search.allBegin, search.allCount, state.pendingAllMask, state.vars,
3089 if (!backtrack(state, stack))
3095 state.termOpIdx = (uint8_t)bestAllIdx;
3096 state.pc = (uint16_t)bestAllIdx;
3100 uint32_t nextAllLocalIdx = (uint32_t)-1;
3101 uint32_t nextAllPc = (uint32_t)-1;
3102 if (select_next_pending_search_all_term(
3103 programOps, search, state.pendingAllMask, state.vars, nextAllLocalIdx, nextAllPc)) {
3104 state.termOpIdx = (uint8_t)nextAllLocalIdx;
3105 state.pc = (uint16_t)nextAllPc;
3113 case EOpcode::Var_Search_SelectOr: {
3114 if (!state.orMatched && search.anyCount == 0 && (uint8_t)(search.orVarMask & ~state.vars.mask) == 0) {
3115 state.bestOrIdx = 0xff;
3117 state.pc = search.maybeFinalizePc;
3121 if (state.orMatched && (uint8_t)(search.orVarMask & ~state.vars.mask) == 0) {
3122 state.bestOrIdx = 0xff;
3124 state.pc = search.beginAnyPc;
3128 uint32_t nextOrLocalIdx = (uint32_t)-1;
3129 uint32_t nextOrPc = (uint32_t)-1;
3130 if (select_next_pending_search_or_term(
3131 programOps, search, state.pendingOrMask, state.pendingFinalOrMask, state.vars, !state.orMatched,
3132 state.orMatched, nextOrLocalIdx, nextOrPc)) {
3133 state.bestOrIdx = (uint8_t)nextOrLocalIdx;
3135 state.termOpIdx = state.bestOrIdx;
3136 state.pc = (uint16_t)nextOrPc;
3140 state.bestOrIdx = 0xff;
3145 case EOpcode::Var_Search_SelectOtherOr: {
3146 if (state.orMatched && (uint8_t)(search.orVarMask & ~state.vars.mask) == 0) {
3148 state.pc = search.beginAnyPc;
3153 while (state.scanIdx < search.orCount) {
3154 const auto localIdx = (uint32_t)state.scanIdx++;
3155 if (localIdx == state.bestOrIdx)
3158 const auto bit = (uint16_t)(uint16_t(1) << localIdx);
3159 if ((state.pendingOrMask & bit) == 0)
3161 const auto bindPc = (uint32_t)search.orBegin + localIdx;
3162 if (!is_term_ready(programOps[bindPc], state.vars))
3165 const bool bindsNewVars =
3166 (uint8_t)(search_program_term_op(programOps[bindPc]).varMask & ~state.vars.mask) != 0;
3167 if (state.orMatched) {
3171 if (!bindsNewVars && (state.pendingFinalOrMask & bit) == 0)
3177 state.termOpIdx = (uint8_t)localIdx;
3178 state.pc = (uint16_t)((uint32_t)search.orCheckBegin + localIdx);
3189 case EOpcode::Var_Search_SelectOtherOrBind: {
3190 if (state.orMatched) {
3196 for (uint32_t localIdx = state.scanIdx; localIdx < search.orCount; ++localIdx) {
3197 if (localIdx == state.bestOrIdx)
3200 const auto bit = (uint16_t)(uint16_t(1) << localIdx);
3201 if ((state.pendingOrMask & bit) == 0)
3204 const auto bindPc = (uint32_t)search.orBegin + localIdx;
3205 if (!is_term_ready(programOps[bindPc], state.vars))
3208 const auto& termOp = search_program_term_op(programOps[bindPc]);
3209 const bool bindsNewVars = (uint8_t)(termOp.varMask & ~state.vars.mask) != 0;
3213 state.scanIdx = (uint8_t)(localIdx + 1u);
3214 state.termOpIdx = (uint8_t)localIdx;
3215 state.pc = (uint16_t)bindPc;
3224 case EOpcode::Var_Search_BeginAny:
3225 state.anyMatched =
false;
3227 state.currentAnyMatched =
false;
3228 state.pc = op.
pc_ok;
3230 case EOpcode::Var_Search_SelectAny: {
3231 uint32_t nextAnyLocalIdx = (uint32_t)-1;
3232 uint32_t nextAnyPc = (uint32_t)-1;
3233 const bool found = select_next_pending_search_any_term(
3234 programOps, search, state.pendingAnyMask, state.vars, nextAnyLocalIdx, nextAnyPc);
3236 state.termOpIdx = (uint8_t)nextAnyLocalIdx;
3237 state.currentAnyMatched =
false;
3238 state.pc = (uint16_t)nextAnyPc;
3246 case EOpcode::Var_Search_MaybeFinalize:
3247 if (!state.anyMatched &&
3248 can_skip_pending_search_all(programOps, search, state.pendingAllMask, state.vars))
3249 state.pc = op.
pc_ok;
3250 else if (op.
pc_fail != BacktrackPC)
3252 else if (!backtrack(state, stack))
3255 case EOpcode::Var_Term_All_Check:
3256 if (term_has_match(*ctx.
pWorld, archetype, search_program_term_op(op), state.vars))
3257 adv_after_search_term_success(state, op, state.vars);
3259 handle_search_term_exhausted(state, op);
3260 if (state.pc == BacktrackPC && !backtrack(state, stack))
3264 case EOpcode::Var_Term_All_Bind:
3265 case EOpcode::Var_Term_All_Src_Bind:
3266 if (!try_enter_search_term(state, stack)) {
3267 handle_search_term_exhausted(state, op);
3268 if (state.pc == BacktrackPC && !backtrack(state, stack))
3272 case EOpcode::Var_Term_Or_Check:
3273 case EOpcode::Var_Term_Or_Bind:
3274 case EOpcode::Var_Term_Any_Check:
3275 case EOpcode::Var_Term_Any_Bind: {
3276 const auto& termOp = search_program_term_op(op);
3277 const bool bindsNewVars = (uint8_t)(termOp.varMask & ~state.vars.mask) != 0;
3278 if (!bindsNewVars) {
3279 if (term_has_match(*ctx.
pWorld, archetype, termOp, state.vars))
3280 adv_after_search_term_success(state, op, state.vars);
3282 if (op.
opcode == EOpcode::Var_Term_Or_Check || op.
opcode == EOpcode::Var_Term_Or_Bind)
3283 state.pendingFinalOrMask =
3284 (uint16_t)(state.pendingFinalOrMask & ~(uint16_t(1) << state.termOpIdx));
3285 handle_search_term_exhausted(state, op);
3286 if (state.pc == BacktrackPC && !backtrack(state, stack))
3292 if (!try_enter_search_term(state, stack)) {
3293 handle_search_term_exhausted(state, op);
3294 if (state.pc == BacktrackPC && !backtrack(state, stack))
3299 case EOpcode::Var_Final_Not_Check:
3300 if (term_has_match(*ctx.
pWorld, archetype, search_program_term_op(op), state.vars)) {
3301 if (!backtrack(state, stack))
3304 state.pc = op.
pc_ok;
3306 case EOpcode::Var_Final_Require_Or:
3307 if (orAlreadySatisfied || state.orMatched || search.orCount == 0)
3308 state.pc = op.
pc_ok;
3309 else if (op.
pc_fail != BacktrackPC)
3311 else if (!backtrack(state, stack))
3314 case EOpcode::Var_Final_Or_Check:
3315 if ((state.pendingFinalOrMask & (uint16_t(1) << op.
arg)) == 0)
3317 else if (term_has_match(*ctx.
pWorld, archetype, search_program_term_op(op), state.vars))
3318 state.pc = op.
pc_ok;
3320 state.pendingFinalOrMask = (uint16_t)(state.pendingFinalOrMask & ~(uint16_t(1) << op.
arg));
3321 if (op.
pc_fail != BacktrackPC)
3323 else if (!backtrack(state, stack))
3327 case EOpcode::Var_Final_Success:
3337 void filter_variable_terms(
MatchingCtx& ctx, VarEvalFunc evalFunc)
const {
3338 if (!m_compCtx.has_variable_terms())
3341 const bool orAlreadySatisfied = !m_compCtx.
ids_or.empty() || ctx.
skipOr;
3343 constexpr uint32_t FilterChunkSize = 64;
3345 uint32_t writeIdx = 0;
3347 const auto flush_filtered = [&]() {
3348 for (
const auto* pFiltered: filtered) {
3354 GAIA_FOR(sourceCnt) {
3356 const bool matched = (this->*evalFunc)(ctx, *pArchetype, orAlreadySatisfied);
3360 filtered.push_back(pArchetype);
3361 if (filtered.size() != FilterChunkSize)
3367 if (!filtered.empty())
3373 const auto cnt = (detail::VmLabel)m_compCtx.ops.size();
3376 m_compCtx.ops.push_back(GAIA_MOV(op));
3381 const auto cnt = add_op(GAIA_MOV(op));
3382 m_compCtx.ops[cnt].pc_fail = (detail::VmLabel)-1;
3386 GAIA_NODISCARD
static uint8_t opcode_arg(uint32_t idx) {
3387 GAIA_ASSERT(idx < OpcodeArgLimit);
3388 return (uint8_t)idx;
3391 template <
typename SourceTermsArray>
3392 void emit_src_gate_terms(
const SourceTermsArray& terms, detail::EOpcode opcode) {
3393 const auto cnt = (uint32_t)terms.size();
3397 op.
arg = opcode_arg(i);
3398 (void)add_gate_op(GAIA_MOV(op));
3402 void emit_src_or_terms(
bool hasOrFallback) {
3405 const auto srcOrCnt = (uint32_t)m_compCtx.
terms_or_src.size();
3406 GAIA_FOR(srcOrCnt) {
3408 op.
opcode = detail::EOpcode::Src_OrTerm;
3409 op.
arg = opcode_arg(i);
3410 orSrcOpLabels.push_back(add_op(GAIA_MOV(op)));
3413 const auto orExitPc = (detail::VmLabel)m_compCtx.ops.size();
3414 for (
const auto opLabel: orSrcOpLabels)
3415 m_compCtx.ops[opLabel].pc_ok = orExitPc;
3417 const auto lastIdx = (uint32_t)orSrcOpLabels.size() - 1u;
3419 m_compCtx.ops[orSrcOpLabels[i]].pc_fail = orSrcOpLabels[i + 1];
3422 m_compCtx.ops[orSrcOpLabels[lastIdx]].pc_fail = hasOrFallback ? orExitPc : (detail::VmLabel)-1;
3425 GAIA_NODISCARD
static detail::EOpcode pick_all_opcode(
bool isSimple,
bool isAs) {
3427 return detail::EOpcode::All_Simple;
3429 return detail::EOpcode::All_Complex;
3430 return detail::EOpcode::All_Wildcard;
3433 GAIA_NODISCARD
static detail::EOpcode pick_or_opcode(
bool hasAllTerms,
bool isSimple,
bool isAs) {
3436 return detail::EOpcode::Or_NoAll_Simple;
3438 return detail::EOpcode::Or_NoAll_Complex;
3439 return detail::EOpcode::Or_NoAll_Wildcard;
3443 return detail::EOpcode::Or_WithAll_Simple;
3445 return detail::EOpcode::Or_WithAll_Complex;
3446 return detail::EOpcode::Or_WithAll_Wildcard;
3449 GAIA_NODISCARD
static detail::EOpcode pick_not_opcode(
bool isSimple,
bool isAs) {
3451 return detail::EOpcode::Not_Simple;
3453 return detail::EOpcode::Not_Complex;
3454 return detail::EOpcode::Not_Wildcard;
3459 GAIA_NODISCARD
bool op_all_simple(
MatchingCtx& ctx)
const {
3460 GAIA_PROF_SCOPE(vm::op_and_simple);
3461 return detail::exec_all_impl<MatchingStyle::Simple>(m_compCtx, ctx);
3464 GAIA_NODISCARD
bool op_all_wildcard(
MatchingCtx& ctx)
const {
3465 GAIA_PROF_SCOPE(vm::op_and_wildcard);
3466 return detail::exec_all_impl<MatchingStyle::Wildcard>(m_compCtx, ctx);
3469 GAIA_NODISCARD
bool op_all_complex(
MatchingCtx& ctx)
const {
3470 GAIA_PROF_SCOPE(vm::op_and_complex);
3471 return detail::exec_all_impl<MatchingStyle::Complex>(m_compCtx, ctx);
3474 GAIA_NODISCARD
bool op_or_noall_simple(
MatchingCtx& ctx)
const {
3475 GAIA_PROF_SCOPE(vm::op_or);
3476 return detail::exec_or_noall_impl<MatchingStyle::Simple>(m_compCtx, ctx);
3479 GAIA_NODISCARD
bool op_or_noall_wildcard(
MatchingCtx& ctx)
const {
3480 GAIA_PROF_SCOPE(vm::op_or);
3481 return detail::exec_or_noall_impl<MatchingStyle::Wildcard>(m_compCtx, ctx);
3484 GAIA_NODISCARD
bool op_or_noall_complex(
MatchingCtx& ctx)
const {
3485 GAIA_PROF_SCOPE(vm::op_or);
3486 return detail::exec_or_noall_impl<MatchingStyle::Complex>(m_compCtx, ctx);
3489 GAIA_NODISCARD
bool op_or_withall_simple(
MatchingCtx& ctx)
const {
3490 GAIA_PROF_SCOPE(vm::op_or);
3491 return detail::exec_or_withall_impl<MatchingStyle::Simple>(m_compCtx, ctx);
3494 GAIA_NODISCARD
bool op_or_withall_wildcard(
MatchingCtx& ctx)
const {
3495 GAIA_PROF_SCOPE(vm::op_or);
3496 return detail::exec_or_withall_impl<MatchingStyle::Wildcard>(m_compCtx, ctx);
3499 GAIA_NODISCARD
bool op_or_withall_complex(
MatchingCtx& ctx)
const {
3500 GAIA_PROF_SCOPE(vm::op_or);
3501 return detail::exec_or_withall_impl<MatchingStyle::Complex>(m_compCtx, ctx);
3504 GAIA_NODISCARD
bool op_not_simple(
MatchingCtx& ctx)
const {
3505 GAIA_PROF_SCOPE(vm::op_not);
3506 return detail::exec_not_impl<MatchingStyle::Simple>(m_compCtx, ctx);
3509 GAIA_NODISCARD
bool op_not_wildcard(
MatchingCtx& ctx)
const {
3510 GAIA_PROF_SCOPE(vm::op_not);
3511 return detail::exec_not_impl<MatchingStyle::Wildcard>(m_compCtx, ctx);
3514 GAIA_NODISCARD
bool op_not_complex(
MatchingCtx& ctx)
const {
3515 GAIA_PROF_SCOPE(vm::op_not);
3516 return detail::exec_not_impl<MatchingStyle::Complex>(m_compCtx, ctx);
3519 GAIA_NODISCARD
bool op_seed_all(
MatchingCtx& ctx)
const {
3520 GAIA_PROF_SCOPE(vm::op_seed_all);
3521 detail::add_all_archetypes(ctx);
3525 GAIA_NODISCARD
bool op_var_filter(
MatchingCtx& ctx)
const {
3526 GAIA_PROF_SCOPE(vm::op_var_filter);
3528 filter_variable_terms(ctx, &VirtualMachine::eval_variable_terms_program_on_archetype);
3532 GAIA_NODISCARD
bool op_src_all_term(
MatchingCtx& ctx)
const {
3533 GAIA_PROF_SCOPE(vm::op_src_all);
3534 const auto& termOp = detail::get_src_term_op(m_compCtx, ctx, m_compCtx.
terms_all_src);
3535 return detail::match_src_term(*ctx.
pWorld, termOp.term, termOp.opcode);
3538 GAIA_NODISCARD
bool op_src_not_term(
MatchingCtx& ctx)
const {
3539 GAIA_PROF_SCOPE(vm::op_src_not);
3540 const auto& termOp = detail::get_src_term_op(m_compCtx, ctx, m_compCtx.
terms_not_src);
3541 return !detail::match_src_term(*ctx.
pWorld, termOp.term, termOp.opcode);
3544 GAIA_NODISCARD
bool op_src_or_term(
MatchingCtx& ctx)
const {
3545 GAIA_PROF_SCOPE(vm::op_src_or);
3546 const auto& termOp = detail::get_src_term_op(m_compCtx, ctx, m_compCtx.
terms_or_src);
3547 const bool matched = detail::match_src_term(*ctx.
pWorld, termOp.term, termOp.opcode);
3552 if (m_compCtx.
ids_all.empty())
3553 detail::add_all_archetypes(ctx);
3557 static constexpr OpcodeFunc OpcodeFuncs[] = {
3558 &VirtualMachine::op_all_simple,
3559 &VirtualMachine::op_all_wildcard,
3560 &VirtualMachine::op_all_complex,
3561 &VirtualMachine::op_or_noall_simple,
3562 &VirtualMachine::op_or_noall_wildcard,
3563 &VirtualMachine::op_or_noall_complex,
3564 &VirtualMachine::op_or_withall_simple,
3565 &VirtualMachine::op_or_withall_wildcard,
3566 &VirtualMachine::op_or_withall_complex,
3567 &VirtualMachine::op_not_simple,
3568 &VirtualMachine::op_not_wildcard,
3569 &VirtualMachine::op_not_complex,
3570 &VirtualMachine::op_seed_all,
3571 &VirtualMachine::op_var_filter,
3572 &VirtualMachine::op_src_all_term,
3573 &VirtualMachine::op_src_not_term,
3574 &VirtualMachine::op_src_or_term
3577 sizeof(OpcodeFuncs) /
sizeof(OpcodeFuncs[0]) == (uint32_t)detail::EOpcode::Src_Never,
3578 "OpcodeFuncs must contain all executable opcodes.");
3581 const auto opcodeIdx = (uint32_t)stackItem.
opcode;
3582 GAIA_ASSERT(opcodeIdx < (uint32_t)detail::EOpcode::Src_Never);
3583 return (this->*OpcodeFuncs[opcodeIdx])(ctx);
3591 out.
append(
"main_ops: ");
3592 add_uint(out, (uint32_t)m_compCtx.mainOpsCount);
3595 const auto opsCnt = (uint32_t)m_compCtx.mainOpsCount;
3597 const auto& op = m_compCtx.ops[i];
3601 add_cstr(out, opcode_name(op.
opcode));
3602 if (opcode_has_arg(op.
opcode)) {
3604 add_uint(out, op.
arg);
3607 add_uint(out, op.
pc_ok);
3620 add_src_terms_section(out,
"src_all", m_compCtx.
terms_all_src, world);
3621 add_src_terms_section(out,
"src_or", m_compCtx.
terms_or_src, world);
3622 add_src_terms_section(out,
"src_not", m_compCtx.
terms_not_src, world);
3624 add_var_terms_section(out,
"var_all", m_compCtx.
terms_all_var, world);
3625 add_var_terms_section(out,
"var_or", m_compCtx.
terms_or_var, world);
3626 add_var_terms_section(out,
"var_not", m_compCtx.
terms_not_var, world);
3627 add_var_terms_section(out,
"var_any", m_compCtx.
terms_any_var, world);
3628 add_var_program_exec_section(out, m_compCtx);
3629 add_var_program_sections(out, m_compCtx, world);
3638 GAIA_PROF_SCOPE(vm::compile);
3639 (void)entityToArchetypeMap;
3640 (void)allArchetypes;
3643 m_compCtx.
ids_or.clear();
3653 m_compCtx.varMaskOr = 0;
3654 m_compCtx.varMaskNot = 0;
3655 m_compCtx.varMaskAny = 0;
3657 m_compCtx.mainOpsCount = 0;
3658 m_compCtx.ops.clear();
3660 auto& data = queryCtx.data;
3661 GAIA_ASSERT(queryCtx.w !=
nullptr);
3662 const auto& world = *queryCtx.w;
3663 const bool hasEntityFilterTerms = data.deps.has_dep_flag(QueryCtx::DependencyHasEntityFilterTerms);
3664 auto isAdjunctDirectTerm = [&](
const QueryTerm& term) {
3665 if (term.
src != EntityBad || term.
entTrav != EntityBad || term_has_variables(term))
3668 const auto id = term.
id;
3669 return (
id.
pair() && world_is_exclusive_dont_fragment_relation(world, pair_rel(world,
id))) ||
3670 (!
id.
pair() && world_is_non_fragmenting_out_of_line_component(world,
id));
3675 QueryTermSpan terms_or = terms.subspan(data.firstOr, data.firstNot - data.firstOr);
3676 QueryTermSpan terms_not = terms.subspan(data.firstNot, data.firstAny - data.firstNot);
3680 if (!terms_all.empty()) {
3681 GAIA_PROF_SCOPE(vm::compile_all);
3683 const auto cnt = terms_all.size();
3685 auto& p = terms_all[i];
3686 if (isAdjunctDirectTerm(p))
3688 if (term_has_variables(p)) {
3690 m_compCtx.
terms_all_var.push_back({detail::src_opcode_from_term(p), p, varMask});
3695 if (p.src == EntityBad) {
3696 m_compCtx.
ids_all.push_back(p.id);
3699 m_compCtx.
terms_all_src.push_back({detail::src_opcode_from_term(p), p});
3704 if (!terms_or.empty()) {
3705 GAIA_PROF_SCOPE(vm::compile_or);
3707 const auto cnt = terms_or.size();
3709 auto& p = terms_or[i];
3710 if (p.src == EntityBad && hasEntityFilterTerms)
3712 if (term_has_variables(p)) {
3714 m_compCtx.
terms_or_var.push_back({detail::src_opcode_from_term(p), p, varMask});
3715 m_compCtx.varMaskOr |= varMask;
3719 if (p.src == EntityBad)
3720 m_compCtx.
ids_or.push_back(p.id);
3722 m_compCtx.
terms_or_src.push_back({detail::src_opcode_from_term(p), p});
3727 if (!terms_not.empty()) {
3728 GAIA_PROF_SCOPE(vm::compile_not);
3730 const auto cnt = terms_not.size();
3732 auto& p = terms_not[i];
3733 if (isAdjunctDirectTerm(p))
3735 if (term_has_variables(p)) {
3737 m_compCtx.
terms_not_var.push_back({detail::src_opcode_from_term(p), p, varMask});
3738 m_compCtx.varMaskNot |= varMask;
3742 if (p.src == EntityBad)
3743 m_compCtx.
ids_not.push_back(p.id);
3745 m_compCtx.
terms_not_src.push_back({detail::src_opcode_from_term(p), p});
3750 if (!terms_any.empty()) {
3751 GAIA_PROF_SCOPE(vm::compile_any);
3753 const auto cnt = terms_any.size();
3755 auto& p = terms_any[i];
3756 if (!term_has_variables(p))
3759 m_compCtx.
terms_any_var.push_back({detail::src_opcode_from_term(p), p, varMask});
3760 m_compCtx.varMaskAny |= varMask;
3765 detail::sort_src_terms_by_cost(m_compCtx.
terms_or_src);
3768 constexpr uint32_t VarSearchProgramOpCapacity = MAX_ITEMS_IN_QUERY * 3u + 8u;
3772 auto init_var_search_program = [&]() {
3773 varSearchProgramOps.clear();
3784 const auto allVarCnt = (uint32_t)m_compCtx.
terms_all_var.size();
3785 GAIA_FOR(allVarCnt) {
3786 const auto cost = detail::search_term_cost(m_compCtx.
terms_all_var[i]);
3787 const auto srcVarBit =
3789 ? (uint8_t)(uint8_t(1) << detail::var_index(m_compCtx.
terms_all_var[i].term.src))
3791 const auto canBindFromSelfSource =
3792 m_compCtx.
terms_all_var[i].sourceOpcode == detail::EOpcode::Src_Self &&
3793 detail::is_var_entity(m_compCtx.
terms_all_var[i].term.src) &&
3795 (uint8_t)(m_compCtx.
terms_all_var[i].varMask & m_compCtx.varMaskAny) == 0;
3796 const auto canBindFromUpSource =
3797 m_compCtx.
terms_all_var[i].sourceOpcode == detail::EOpcode::Src_Up &&
3798 detail::is_var_entity(m_compCtx.
terms_all_var[i].term.src) &&
3800 (uint8_t)(m_compCtx.
terms_all_var[i].varMask & m_compCtx.varMaskAny) == 0;
3801 const auto canBindFromDownSource =
3802 m_compCtx.
terms_all_var[i].sourceOpcode == detail::EOpcode::Src_Down &&
3803 detail::is_var_entity(m_compCtx.
terms_all_var[i].term.src) &&
3805 (uint8_t)(m_compCtx.
terms_all_var[i].varMask & m_compCtx.varMaskAny) == 0;
3806 const auto canBindFromUpDownSource =
3807 m_compCtx.
terms_all_var[i].sourceOpcode == detail::EOpcode::Src_UpDown &&
3808 detail::is_var_entity(m_compCtx.
terms_all_var[i].term.src) &&
3810 (uint8_t)(m_compCtx.
terms_all_var[i].varMask & m_compCtx.varMaskAny) == 0;
3812 canBindFromSelfSource || canBindFromUpSource || canBindFromDownSource || canBindFromUpDownSource
3813 ? detail::EOpcode::Var_Term_All_Src_Bind
3814 : detail::EOpcode::Var_Term_All_Bind;
3815 searchAllBindOps.push_back({opcode, 0, 0, (uint8_t)i, cost});
3816 searchAllCheckOps.push_back({detail::EOpcode::Var_Term_All_Check, 0, 0, (uint8_t)i, cost});
3818 detail::sort_program_ops_by_cost(searchAllBindOps);
3819 detail::sort_program_ops_by_cost(searchAllCheckOps);
3821 const auto orVarCnt = (uint32_t)m_compCtx.
terms_or_var.size();
3822 GAIA_FOR(orVarCnt) {
3823 const auto cost = detail::search_term_cost(m_compCtx.
terms_or_var[i]);
3824 searchOrBindOps.push_back({detail::EOpcode::Var_Term_Or_Bind, 0, 0, (uint8_t)i, cost});
3825 searchOrCheckOps.push_back({detail::EOpcode::Var_Term_Or_Check, 0, 0, (uint8_t)i, cost});
3826 finalOrCheckOps.push_back({detail::EOpcode::Var_Final_Or_Check, 0, 0, (uint8_t)i, cost});
3827 varSearchMeta.orVarMask = (uint8_t)(varSearchMeta.orVarMask | m_compCtx.
terms_or_var[i].varMask);
3829 detail::sort_program_ops_by_cost(searchOrBindOps);
3830 detail::sort_program_ops_by_cost(searchOrCheckOps);
3831 detail::sort_program_ops_by_cost(finalOrCheckOps);
3833 const auto anyVarCnt = (uint32_t)m_compCtx.
terms_any_var.size();
3834 GAIA_FOR(anyVarCnt) {
3835 const auto cost = detail::search_term_cost(m_compCtx.
terms_any_var[i]);
3836 searchAnyBindOps.push_back({detail::EOpcode::Var_Term_Any_Bind, 0, 0, (uint8_t)i, cost});
3837 searchAnyCheckOps.push_back({detail::EOpcode::Var_Term_Any_Check, 0, 0, (uint8_t)i, cost});
3839 detail::sort_program_ops_by_cost(searchAnyBindOps);
3840 detail::sort_program_ops_by_cost(searchAnyCheckOps);
3842 const auto notVarCnt = (uint32_t)m_compCtx.
terms_not_var.size();
3843 GAIA_FOR(notVarCnt) {
3844 finalNotOps.push_back(
3845 {detail::EOpcode::Var_Final_Not_Check, 0, 0, (uint8_t)i,
3848 detail::sort_program_ops_by_cost(finalNotOps);
3850 for (
const auto& op: searchAllBindOps)
3851 varSearchProgramOps.push_back(op);
3852 for (
const auto& op: searchOrBindOps)
3853 varSearchProgramOps.push_back(op);
3854 for (
const auto& op: searchAnyBindOps)
3855 varSearchProgramOps.push_back(op);
3857 varSearchMeta.allBegin = 0;
3858 varSearchMeta.allCount = (uint16_t)searchAllBindOps.size();
3859 varSearchMeta.orBegin = varSearchMeta.allCount;
3860 varSearchMeta.orCount = (uint16_t)searchOrBindOps.size();
3861 varSearchMeta.anyBegin = (uint16_t)(varSearchMeta.orBegin + varSearchMeta.orCount);
3862 varSearchMeta.anyCount = (uint16_t)searchAnyBindOps.size();
3863 varSearchMeta.notBegin = 0;
3864 varSearchMeta.notCount = 0;
3866 const auto termOpsCnt = (uint16_t)varSearchProgramOps.size();
3867 const auto selectAllPc = termOpsCnt;
3868 const auto selectOrPc = (uint16_t)(termOpsCnt + 1u);
3869 const auto selectOtherOrPc = (uint16_t)(termOpsCnt + 2u);
3870 const auto selectOtherOrBindPc = (uint16_t)(termOpsCnt + 3u);
3871 const auto beginAnyPc = (uint16_t)(termOpsCnt + 4u);
3872 const auto selectAnyPc = (uint16_t)(termOpsCnt + 5u);
3873 const auto maybeFinalizePc = (uint16_t)(termOpsCnt + 6u);
3874 const auto allCheckBegin = (uint16_t)(termOpsCnt + 7u);
3875 const auto orCheckBegin = (uint16_t)(allCheckBegin + searchAllCheckOps.size());
3876 const auto anyCheckBegin = (uint16_t)(orCheckBegin + searchOrCheckOps.size());
3877 const auto finalNotBegin = (uint16_t)(anyCheckBegin + searchAnyCheckOps.size());
3878 const auto finalRequireOrPc = (uint16_t)(finalNotBegin + finalNotOps.size());
3879 const auto finalOrCheckBegin = (uint16_t)(finalRequireOrPc + 1u);
3880 const auto finalSuccessPc = (uint16_t)(finalOrCheckBegin + finalOrCheckOps.size());
3881 const auto finalBegin = !finalNotOps.empty() ? finalNotBegin : finalRequireOrPc;
3882 const auto backtrackPc = (detail::VmLabel)-1;
3884 for (
auto& op: varSearchProgramOps) {
3886 case detail::EOpcode::Var_Term_All_Bind:
3887 case detail::EOpcode::Var_Term_All_Src_Bind:
3888 op.
pc_ok = selectAllPc;
3891 case detail::EOpcode::Var_Term_Or_Bind:
3892 op.
pc_ok = selectAllPc;
3893 op.
pc_fail = selectOtherOrBindPc;
3895 case detail::EOpcode::Var_Term_Any_Bind:
3896 op.
pc_ok = selectAllPc;
3904 varSearchProgramOps.push_back({detail::EOpcode::Var_Search_SelectAll, selectAllPc, selectOrPc, 0, 0});
3905 varSearchProgramOps.push_back({detail::EOpcode::Var_Search_SelectOr, selectOrPc, selectOtherOrPc, 0, 0});
3906 varSearchProgramOps.push_back(
3907 {detail::EOpcode::Var_Search_SelectOtherOr, selectOtherOrPc, selectOtherOrBindPc, 0, 0});
3908 varSearchProgramOps.push_back(
3909 {detail::EOpcode::Var_Search_SelectOtherOrBind, selectOtherOrBindPc, beginAnyPc, 0, 0});
3910 varSearchProgramOps.push_back({detail::EOpcode::Var_Search_BeginAny, selectAnyPc, backtrackPc, 0, 0});
3911 varSearchProgramOps.push_back({detail::EOpcode::Var_Search_SelectAny, selectAnyPc, maybeFinalizePc, 0, 0});
3912 varSearchProgramOps.push_back({detail::EOpcode::Var_Search_MaybeFinalize, finalBegin, backtrackPc, 0, 0});
3913 for (
auto op: searchAllCheckOps) {
3914 op.
pc_ok = selectAllPc;
3916 varSearchProgramOps.push_back(op);
3918 for (
auto op: searchOrCheckOps) {
3919 op.
pc_ok = selectAllPc;
3920 op.
pc_fail = selectOtherOrBindPc;
3921 varSearchProgramOps.push_back(op);
3923 for (
auto op: searchAnyCheckOps) {
3924 op.
pc_ok = selectAllPc;
3926 varSearchProgramOps.push_back(op);
3928 for (uint32_t i = 0; i < finalNotOps.size(); ++i) {
3929 auto op = finalNotOps[i];
3930 op.
pc_ok = (i + 1u < finalNotOps.size()) ? (uint16_t)(finalNotBegin + i + 1u) : finalRequireOrPc;
3932 varSearchProgramOps.push_back(op);
3934 varSearchProgramOps.push_back(
3935 {detail::EOpcode::Var_Final_Require_Or, finalSuccessPc,
3936 searchOrCheckOps.empty() ? backtrackPc : finalOrCheckBegin, 0, 0});
3937 for (uint32_t i = 0; i < finalOrCheckOps.size(); ++i) {
3938 auto op = finalOrCheckOps[i];
3939 op.
pc_ok = finalSuccessPc;
3940 op.
pc_fail = (i + 1u < finalOrCheckOps.size()) ? (uint16_t)(finalOrCheckBegin + i + 1u) : backtrackPc;
3941 varSearchProgramOps.push_back(op);
3943 varSearchProgramOps.push_back({detail::EOpcode::Var_Final_Success, finalSuccessPc, backtrackPc, 0, 0});
3945 varSearchMeta.selectAllPc = selectAllPc;
3946 varSearchMeta.selectOrPc = selectOrPc;
3947 varSearchMeta.selectOtherOrPc = selectOtherOrPc;
3948 varSearchMeta.selectOtherOrBindPc = selectOtherOrBindPc;
3949 varSearchMeta.beginAnyPc = beginAnyPc;
3950 varSearchMeta.selectAnyPc = selectAnyPc;
3951 varSearchMeta.maybeFinalizePc = maybeFinalizePc;
3952 varSearchMeta.allCheckBegin = allCheckBegin;
3953 varSearchMeta.orCheckBegin = orCheckBegin;
3954 varSearchMeta.anyCheckBegin = anyCheckBegin;
3955 varSearchMeta.notBegin = finalNotBegin;
3956 varSearchMeta.notCount = (uint16_t)finalNotOps.size();
3958 auto init_mask = [](uint16_t begin, uint16_t count) {
3960 for (uint16_t i = 0; i < count; ++i)
3961 mask = (uint16_t)(mask | (uint16_t(1) << (begin + i)));
3965 varSearchMeta.initialAllMask = init_mask(varSearchMeta.allBegin, varSearchMeta.allCount);
3966 varSearchMeta.initialOrMask = init_mask(0, varSearchMeta.orCount);
3967 varSearchMeta.initialAnyMask = init_mask(0, varSearchMeta.anyCount);
3970 create_opcodes(queryCtx);
3972 init_var_search_program();
3980 GAIA_ASSERT(m_compCtx.ops.size() <= UINT16_MAX);
3981 program.begin = (uint16_t)m_compCtx.ops.size();
3982 program.count = (uint16_t)ops.size();
3983 for (
const auto& op: ops)
3984 m_compCtx.ops.push_back(op);
3989 if (m_compCtx.has_variable_terms()) {
3990 const auto program = emit_flat_program(
3992 if (!program.empty())
3993 m_compCtx.
var_programs.push_back({program, varSearchMeta});
3997 void create_opcodes(
QueryCtx& queryCtx) {
3998 const bool isSimple = (queryCtx.data.
flags & QueryCtx::QueryFlags::Complex) == 0U;
4001 uint16_t preservedProgramBase = 0;
4003 preservedProgramOffsets.clear();
4006 preservedProgramBase = m_compCtx.
var_programs[0].program.begin;
4007 GAIA_ASSERT(preservedProgramBase <= m_compCtx.ops.size());
4008 const auto preservedCnt = (uint32_t)m_compCtx.ops.size() - (uint32_t)preservedProgramBase;
4009 preservedVarOps.reserve(preservedCnt);
4010 for (uint32_t i = 0; i < preservedCnt; ++i)
4011 preservedVarOps.push_back(m_compCtx.ops[(uint32_t)preservedProgramBase + i]);
4013 for (
const auto& step: m_compCtx.var_programs) {
4014 GAIA_ASSERT(step.program.begin >= preservedProgramBase);
4015 preservedProgramOffsets.push_back((uint16_t)(step.program.begin - preservedProgramBase));
4019 m_compCtx.ops.clear();
4023 emit_src_gate_terms(m_compCtx.
terms_all_src, detail::EOpcode::Src_AllTerm);
4027 emit_src_gate_terms(m_compCtx.
terms_not_src, detail::EOpcode::Src_NotTerm);
4031 const bool hasOrFallback = !m_compCtx.
ids_or.empty() || !m_compCtx.
terms_or_var.empty();
4032 emit_src_or_terms(hasOrFallback);
4036 if (!m_compCtx.has_id_terms() &&
4037 (m_compCtx.has_src_terms() || m_compCtx.has_variable_terms() ||
4038 queryCtx.data.
deps.has_dep_flag(QueryCtx::DependencyHasEntityFilterTerms))) {
4039 detail::CompiledOp op{};
4040 op.
opcode = detail::EOpcode::Seed_All;
4041 (void)add_op(GAIA_MOV(op));
4045 if (!m_compCtx.
ids_all.empty()) {
4046 detail::CompiledOp op{};
4047 op.
opcode = pick_all_opcode(isSimple, isAs);
4048 (void)add_gate_op(GAIA_MOV(op));
4052 if (!m_compCtx.
ids_or.empty()) {
4053 detail::CompiledOp op{};
4054 op.
opcode = pick_or_opcode(!m_compCtx.
ids_all.empty(), isSimple, isAs);
4055 (void)add_op(GAIA_MOV(op));
4059 if (!m_compCtx.
ids_not.empty()) {
4060 detail::CompiledOp op{};
4061 op.
opcode = pick_not_opcode(isSimple, isAs);
4062 (void)add_op(GAIA_MOV(op));
4066 if (m_compCtx.has_variable_terms()) {
4067 detail::CompiledOp op{};
4068 op.
opcode = detail::EOpcode::Var_Filter;
4069 (void)add_gate_op(GAIA_MOV(op));
4072 m_compCtx.mainOpsCount = (uint16_t)m_compCtx.ops.size();
4074 if (!preservedVarOps.empty()) {
4075 const auto newProgramBase = (uint16_t)m_compCtx.ops.size();
4076 for (
const auto& op: preservedVarOps)
4077 m_compCtx.ops.push_back(op);
4079 GAIA_ASSERT(preservedProgramOffsets.size() == m_compCtx.
var_programs.size());
4080 const auto programCnt = (uint32_t)m_compCtx.
var_programs.size();
4081 GAIA_FOR(programCnt)
4082 m_compCtx.
var_programs[i].program.begin = (uint16_t)(newProgramBase + preservedProgramOffsets[i]);
4086 queryCtx.data.
flags &= ~QueryCtx::QueryFlags::Recompile;
4089 GAIA_NODISCARD
bool is_compiled()
const {
4090 return !m_compCtx.ops.empty();
4093 GAIA_NODISCARD uint32_t op_count()
const {
4094 return (uint32_t)m_compCtx.ops.size();
4097 GAIA_NODISCARD uint64_t op_signature()
const {
4098 uint64_t hash = 1469598103934665603ull;
4099 for (
const auto& op: m_compCtx.ops) {
4100 const uint64_t packed =
4101 (uint64_t)(uint8_t)op.
opcode |
4102 ((uint64_t)op.
pc_ok << 8u) |
4103 ((uint64_t)op.
pc_fail << 24u) |
4104 ((uint64_t)op.
arg << 40u) |
4105 ((uint64_t)op.
cost << 48u);
4107 hash *= 1099511628211ull;
4114 GAIA_PROF_SCOPE(vm::exec);
4116 if (m_compCtx.mainOpsCount == 0)
4123 auto& stackItem = m_compCtx.ops[ctx.
pc];
4124 GAIA_ASSERT((uint32_t)stackItem.
opcode < (uint32_t)detail::EOpcode::Src_Never);
4125 const bool ret = exec_opcode(stackItem, ctx);
4127 }
while (ctx.
pc < m_compCtx.mainOpsCount);
Array with variable size of elements of type.
Definition darray_impl.h:25
Definition span_impl.h:99
Definition archetype.h:83
Wrapper for two Entities forming a relationship pair.
Definition id.h:529
Wrapper for two types forming a relationship pair. Depending on what types are used to form a pair it...
Definition id.h:224
Compiles query terms into matching bytecode and evaluates that bytecode against archetypes....
Definition vm.h:2311
void exec(MatchingCtx &ctx)
Executes compiled opcodes.
Definition vm.h:4113
void compile(const EntityToArchetypeMap &entityToArchetypeMap, std::span< const Archetype * > allArchetypes, QueryCtx &queryCtx)
Transforms inputs into virtual machine opcodes.
Definition vm.h:3635
Checks if endianess was detected correctly at compile-time.
Definition bitset.h:9
constexpr uint32_t BadIndex
Sentinel index value returned by helpers when a lookup fails.
Definition utility.h:20
Definition query_match_stamps.h:13
Hashmap lookup structure used for Entity.
Definition id.h:468
uint32_t as_mask_0
Mask for items with Is relationship pair. If the id is a pair, the first part (id) is written here.
Definition query_common.h:756
uint32_t as_mask_1
Mask for items with Is relationship pair. If the id is a pair, the second part (gen) is written here.
Definition query_common.h:759
Dependencies deps
Explicit dependency metadata derived from query shape.
Definition query_common.h:788
uint16_t flags
Query flags.
Definition query_common.h:772
Definition query_common.h:564
Internal representation of QueryInput.
Definition query_common.h:445
Entity id
Queried id.
Definition query_common.h:447
uint8_t travDepth
Maximum number of traversal steps.
Definition query_common.h:455
Entity entTrav
Optional traversal relation for source lookups.
Definition query_common.h:451
Entity src
Source of where the queried id is looked up at.
Definition query_common.h:449
const EntityToArchetypeVersionMap * pVersions
Optional lookup-bucket revision table used to validate cached incremental cursors.
Definition vm.h:41
QueryArchetypeCacheIndexMap * pLastMatchedArchetypeIdx_Not
Idx of the last matched archetype against the NOT opcode.
Definition vm.h:88
EntitySpan idsToMatch
List of entity ids in a query to consider.
Definition vm.h:112
Entity ent
Entity to match.
Definition vm.h:110
cnt::sarray< Entity, MaxVarCnt > varBindings
Runtime variable bindings (Var0..Var7) provided by the query.
Definition vm.h:100
ArchetypeMatchStamps * pMatchesStampByArchetypeId
Per-archetype stamp table for O(1) dedup in hot loops.
Definition vm.h:80
std::span< const Archetype * > allArchetypes
Array of all archetypes in the world.
Definition vm.h:76
uint32_t matchesVersion
Current dedup version used with pMatchesStampByArchetypeId.
Definition vm.h:82
QueryArchetypeCacheIndexMap * pLastMatchedArchetypeIdx_All
Idx of the last matched archetype against the ALL opcode.
Definition vm.h:84
bool skipOr
OR group was already satisfied by source terms.
Definition vm.h:104
uint32_t pc
Current stack position (program counter)
Definition vm.h:114
EntitySpan targetEntities
Entities for which we run the VM. If empty, we run against all of them.
Definition vm.h:72
QueryMask queryMask
Mask for speeding up simple query matching.
Definition vm.h:90
cnt::darr< const Archetype * > * pMatchesArr
Array of already matches archetypes. Reset before each exec().
Definition vm.h:78
uint32_t as_mask_0
Mask for items with Is relationship pair. If the id is a pair, the first part (id) is written here.
Definition vm.h:93
const World * pWorld
World.
Definition vm.h:70
QueryArchetypeCacheIndexMap * pLastMatchedArchetypeIdx_Or
Idx of the last matched archetype against the OR opcode.
Definition vm.h:86
ArchetypeLookupView archetypeLookup
entity -> archetypes lookup used to seed structural candidate archetypes
Definition vm.h:74
uint16_t flags
Flags copied over from QueryCtx::Data.
Definition vm.h:98
uint8_t varBindingMask
Bitmask of bindings set in varBindings.
Definition vm.h:102
uint32_t as_mask_1
Mask for items with Is relationship pair. If the id is a pair, the second part (gen) is written here.
Definition vm.h:96
VmLabel pc_ok
Stack position to go to if the opcode returns true.
Definition vm.h:236
VmLabel pc_fail
Stack position to go to if the opcode returns false.
Definition vm.h:238
EOpcode opcode
Opcode to execute.
Definition vm.h:234
uint8_t cost
Optional planner-side cost used for sorting compiled micro-op plans.
Definition vm.h:242
uint8_t arg
Optional opcode argument (e.g. index to a source term array).
Definition vm.h:240
cnt::sarray_ext< SourceTermOp, MAX_ITEMS_IN_QUERY > terms_not_src
Source lookup terms for NOT.
Definition vm.h:314
cnt::sarray_ext< Entity, MAX_ITEMS_IN_QUERY > ids_or
Array of ops that can be evaluated with a OR opcode.
Definition vm.h:306
cnt::sarray_ext< VarTermOp, MAX_ITEMS_IN_QUERY > terms_any_var
Variable terms for ANY.
Definition vm.h:322
uint8_t varMaskAll
Variable masks (Var0..Var7) used by variable terms.
Definition vm.h:326
cnt::sarray_ext< VarTermOp, MAX_ITEMS_IN_QUERY > terms_not_var
Variable terms for NOT.
Definition vm.h:320
cnt::sarray_ext< VarProgramStep, MaxVarCnt > var_programs
Variable programs.
Definition vm.h:324
cnt::sarray_ext< SourceTermOp, MAX_ITEMS_IN_QUERY > terms_all_src
Source lookup terms for ALL.
Definition vm.h:310
cnt::sarray_ext< Entity, MAX_ITEMS_IN_QUERY > ids_all
Array of ops that can be evaluated with a ALL opcode.
Definition vm.h:304
cnt::sarray_ext< VarTermOp, MAX_ITEMS_IN_QUERY > terms_or_var
Variable terms for OR.
Definition vm.h:318
cnt::sarray_ext< Entity, MAX_ITEMS_IN_QUERY > ids_not
Array of ops that can be evaluated with a NOT opcode.
Definition vm.h:308
cnt::sarray_ext< SourceTermOp, MAX_ITEMS_IN_QUERY > terms_or_src
Source lookup terms for OR.
Definition vm.h:312
cnt::sarray_ext< VarTermOp, MAX_ITEMS_IN_QUERY > terms_all_var
Variable terms for ALL.
Definition vm.h:316
Lightweight owning string container with explicit length semantics (no implicit null terminator).
Definition str.h:331
void append(const char *data, uint32_t size)
Appends size characters from data.
Definition str.h:389
void reserve(uint32_t len)
Reserves capacity for at least len characters.
Definition str.h:358