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;
40 FetchByKeyFn fetchByKey =
nullptr;
42 GAIA_NODISCARD
bool empty()
const {
43 return fetchByKey ==
nullptr;
51 return fetchByKey(pData, arr, ent, key);
111 GAIA_ASSERT(key != EntityBadLookupKey);
113 const auto it = map.find(key);
114 if (it == map.end() || it->second.empty())
117 return std::span(it->second.data(), it->second.size());
122 GAIA_ASSERT(src != EntityBad);
124 return fetch_archetypes_for_select(map, arr, ent, EntityLookupKey(src));
130 GAIA_ASSERT(key != EntityBadLookupKey);
132 const auto it = core::find_if(map, [&](
const auto& item) {
133 return item.matches(key);
135 if (it == map.end() || arr.empty())
143 GAIA_ASSERT(src != EntityBad);
145 return fetch_archetypes_for_select(map, arr, ent, EntityLookupKey(src));
150 return fetch_archetypes_for_select(*(
const EntityToArchetypeMap*)pData, arr, ent, key);
155 return fetch_archetypes_for_select(*(
const SingleArchetypeLookup*)pData, arr, ent, key);
158 inline ArchetypeLookupView make_archetype_lookup_view(
const EntityToArchetypeMap& map) {
159 return ArchetypeLookupView{&map, fetch_archetypes_for_select_from_map};
162 inline ArchetypeLookupView make_archetype_lookup_view(
const SingleArchetypeLookup& map) {
163 return ArchetypeLookupView{&map, fetch_archetypes_for_select_from_single};
167 enum class EOpcode : uint8_t {
200 Var_Term_All_Src_Bind,
206 Var_Search_SelectAll,
208 Var_Search_SelectOtherOr,
209 Var_Search_SelectOtherOrBind,
211 Var_Search_SelectAny,
212 Var_Search_MaybeFinalize,
214 Var_Final_Require_Or,
219 using VmLabel = uint16_t;
236 EOpcode opcode = EOpcode::Src_Never;
241 EOpcode sourceOpcode = EOpcode::Src_Never;
255 GAIA_NODISCARD
bool empty()
const {
261 uint16_t selectAllPc = (uint16_t)-1;
262 uint16_t selectOrPc = (uint16_t)-1;
263 uint16_t selectOtherOrPc = (uint16_t)-1;
264 uint16_t selectOtherOrBindPc = (uint16_t)-1;
265 uint16_t beginAnyPc = (uint16_t)-1;
266 uint16_t selectAnyPc = (uint16_t)-1;
267 uint16_t maybeFinalizePc = (uint16_t)-1;
268 uint16_t initialAllMask = 0;
269 uint16_t initialOrMask = 0;
270 uint16_t initialAnyMask = 0;
271 uint16_t allBegin = 0;
272 uint16_t allCheckBegin = 0;
273 uint16_t allCount = 0;
274 uint16_t orBegin = 0;
275 uint16_t orCheckBegin = 0;
276 uint16_t orCount = 0;
277 uint16_t anyBegin = 0;
278 uint16_t anyCheckBegin = 0;
279 uint16_t anyCount = 0;
280 uint16_t notBegin = 0;
281 uint16_t notCount = 0;
282 uint8_t orVarMask = 0;
291 uint16_t mainOpsCount = 0;
316 uint8_t varMaskOr = 0;
317 uint8_t varMaskNot = 0;
318 uint8_t varMaskAny = 0;
320 GAIA_NODISCARD
bool has_src_terms()
const {
324 GAIA_NODISCARD
bool has_variable_terms()
const {
328 GAIA_NODISCARD
bool has_id_terms()
const {
333 enum class EVarProgramTermSet : uint8_t { None, All, Or, Any, Not };
336 EVarProgramTermSet termSet;
339 static constexpr auto VarProgramOpcodeFirst = EOpcode::Var_Term_All_Check;
340 static constexpr auto VarProgramOpcodeLast = EOpcode::Var_Final_Success;
342 {EVarProgramTermSet::All},
343 {EVarProgramTermSet::All},
344 {EVarProgramTermSet::All},
345 {EVarProgramTermSet::Or},
346 {EVarProgramTermSet::Or},
347 {EVarProgramTermSet::Any},
348 {EVarProgramTermSet::Any},
349 {EVarProgramTermSet::Not},
350 {EVarProgramTermSet::None},
351 {EVarProgramTermSet::None},
352 {EVarProgramTermSet::None},
353 {EVarProgramTermSet::None},
354 {EVarProgramTermSet::None},
355 {EVarProgramTermSet::None},
356 {EVarProgramTermSet::None},
357 {EVarProgramTermSet::Not},
358 {EVarProgramTermSet::None},
359 {EVarProgramTermSet::Or},
360 {EVarProgramTermSet::None},
364 sizeof(VarProgramOpcodeMetaTable) /
sizeof(VarProgramOpcodeMetaTable[0]) ==
365 (uint32_t)VarProgramOpcodeLast - (uint32_t)VarProgramOpcodeFirst + 1u,
366 "VarProgramOpcodeMetaTable out of sync with EOpcode variable micro-op range.");
368 GAIA_NODISCARD
inline const VarProgramOpcodeMeta& var_program_opcode_meta(EOpcode opcode) {
369 GAIA_ASSERT((uint32_t)opcode >= (uint32_t)VarProgramOpcodeFirst);
370 GAIA_ASSERT((uint32_t)opcode <= (uint32_t)VarProgramOpcodeLast);
371 return VarProgramOpcodeMetaTable[(uint32_t)opcode - (uint32_t)VarProgramOpcodeFirst];
374 GAIA_NODISCARD
inline uint8_t src_term_cost(
const QueryCompileCtx::SourceTermOp& termOp) {
375 const bool depth1 = termOp.term.travDepth == 1;
376 switch (termOp.opcode) {
377 case EOpcode::Src_Never:
379 case EOpcode::Src_Self:
381 case EOpcode::Src_Up:
382 case EOpcode::Src_Down:
383 return depth1 ? 2 : 4;
384 case EOpcode::Src_UpDown:
385 return depth1 ? 3 : 5;
391 GAIA_NODISCARD
inline uint8_t bound_match_id_cost(Entity queryId) {
393 return (!is_variable(queryId) && queryId.id() != All.id()) ? 1u : 3u;
396 cost += (!is_variable((EntityId)queryId.id()) && queryId.id() != All.id()) ? 1u : 3u;
397 cost += (!is_variable((EntityId)queryId.gen()) && queryId.gen() != All.id()) ? 1u : 3u;
401 GAIA_NODISCARD
inline uint8_t bound_term_cost(
const QueryCompileCtx::VarTermOp& termOp) {
402 uint8_t cost = bound_match_id_cost(termOp.term.id);
403 if (termOp.term.src != EntityBad)
404 cost = (uint8_t)(cost + src_term_cost({termOp.sourceOpcode, termOp.term}));
408 GAIA_NODISCARD
inline uint8_t search_term_cost(
const QueryCompileCtx::VarTermOp& termOp) {
409 uint8_t cost = bound_term_cost(termOp);
410 if (termOp.term.src != EntityBad) {
411 const bool srcIsVar = is_variable(EntityId(termOp.term.src.id()));
412 cost = (uint8_t)(cost + (srcIsVar ? 32u : 8u));
417 template <
typename ProgramOpsArray>
418 inline void sort_program_ops_by_cost(ProgramOpsArray& ops) {
419 const auto cnt = (uint32_t)ops.size();
423 for (uint32_t i = 1; i < cnt; ++i) {
424 const auto key = ops[i];
428 const auto prev = ops[j - 1];
429 if (prev.cost < key.cost)
431 if (prev.cost == key.cost && (uint8_t)prev.opcode < (uint8_t)key.opcode)
433 if (prev.cost == key.cost && prev.opcode == key.opcode && prev.arg <= key.arg)
443 template <
typename SourceTermsArray>
444 inline void sort_src_terms_by_cost(SourceTermsArray& terms) {
445 const auto cnt = (uint32_t)terms.size();
449 for (uint32_t i = 1; i < cnt; ++i) {
451 const auto keyCost = src_term_cost(key);
454 while (j > 0 && src_term_cost(terms[j - 1]) > keyCost) {
455 terms[j] = terms[j - 1];
464 program_ops(
const QueryCompileCtx& comp,
const QueryCompileCtx::VarProgram& program) {
465 GAIA_ASSERT((uint32_t)program.begin + (uint32_t)program.count <= (uint32_t)comp.ops.size());
466 return {comp.ops.data() + program.begin, program.count};
469 inline uint32_t handle_last_archetype_match(
470 QueryArchetypeCacheIndexMap* pCont, EntityLookupKey entityKey, uint32_t srcArchetypeCnt) {
471 if (pCont ==
nullptr)
474 const auto cache_it = pCont->find(entityKey);
475 uint32_t lastMatchedIdx = 0;
476 if (cache_it == pCont->end())
477 pCont->emplace(entityKey, srcArchetypeCnt);
479 lastMatchedIdx = cache_it->second;
480 cache_it->second = srcArchetypeCnt;
482 return lastMatchedIdx;
487 static bool check_mask(
const QueryMask& maskArchetype,
const QueryMask& maskQuery) {
488 return match_entity_mask(maskArchetype, maskQuery);
490 static void restart([[maybe_unused]] uint32_t& idx) {}
491 static bool can_continue(
bool hasMatch) {
494 static bool eval(uint32_t expectedMatches, uint32_t totalMatches) {
495 return expectedMatches == totalMatches;
503 static bool check_mask(
const QueryMask& maskArchetype,
const QueryMask& maskQuery) {
504 return match_entity_mask(maskArchetype, maskQuery);
506 static void restart(uint32_t& idx) {
510 static bool can_continue([[maybe_unused]]
bool hasMatch) {
513 static bool eval(uint32_t expectedMatches, uint32_t totalMatches) {
514 (void)expectedMatches;
515 return totalMatches > 0;
523 static bool check_mask(
const QueryMask& maskArchetype,
const QueryMask& maskQuery) {
524 return !match_entity_mask(maskArchetype, maskQuery);
526 static void restart(uint32_t& idx) {
529 static bool can_continue(
bool hasMatch) {
532 static bool eval(uint32_t expectedMatches, uint32_t totalMatches) {
533 (void)expectedMatches;
534 return totalMatches == 0;
541 GAIA_NODISCARD
inline bool is_archetype_marked(
const MatchingCtx& ctx,
const Archetype* pArchetype) {
545 const auto sid = (uint32_t)pArchetype->id();
546 if (!stamps.has(sid))
556 const auto sid = (uint32_t)pArchetype->id();
562 inline void add_all_archetypes(MatchingCtx& ctx) {
563 for (
const auto* pArchetype: ctx.allArchetypes) {
564 if (is_archetype_marked(ctx, pArchetype))
567 mark_archetype_match(ctx, pArchetype);
571 template <
typename OpKind>
572 inline bool match_inter_eval_matches(uint32_t queryIdMarches, uint32_t& outMatches) {
573 const bool hadAnyMatches = queryIdMarches > 0;
577 if (!OpKind::can_continue(hadAnyMatches))
582 outMatches += (uint32_t)hadAnyMatches;
594 template <
typename OpKind,
typename CmpFunc>
595 GAIA_NODISCARD
inline bool match_inter(EntitySpan queryIds, EntitySpan archetypeIds, CmpFunc func) {
596 const auto archetypeIdsCnt = (uint32_t)archetypeIds.size();
597 const auto queryIdsCnt = (uint32_t)queryIds.size();
600 uint32_t indices[2]{};
601 uint32_t matches = 0;
628 while (indices[0] < queryIdsCnt) {
629 const auto idInQuery = queryIds[indices[0]];
632 if (idInQuery == All || idInQuery.id() == Is.id())
635 uint32_t queryIdMatches = 0;
636 while (indices[1] < archetypeIdsCnt) {
637 const auto idInArchetype = archetypeIds[indices[1]];
640 const auto res = func(idInQuery, idInArchetype);
650 if (!match_inter_eval_matches<OpKind>(queryIdMatches, matches))
659 if (!match_inter_eval_matches<OpKind>(queryIdMatches, matches))
666 OpKind::restart(indices[1]);
672 return OpKind::eval(queryIdsCnt, matches);
680 return {idInQuery == idInArchetype};
683 GAIA_NODISCARD
inline IdCmpResult cmp_ids_pairs(
Entity idInQuery,
Entity idInArchetype) {
684 if (idInQuery.pair()) {
686 if (idInQuery ==
Pair(All, All))
694 if (idInQuery.gen() == All.id())
695 return {idInQuery.id() == idInArchetype.id()};
702 if (idInQuery.id() == All.id())
703 return {idInQuery.gen() == idInArchetype.gen()};
707 return cmp_ids(idInQuery, idInArchetype);
710 GAIA_NODISCARD
inline IdCmpResult
711 cmp_ids_is(
const World& w,
const Archetype& archetype, Entity idInQuery, Entity idInArchetype) {
713 if (idInQuery.pair() && idInQuery.id() == Is.id()) {
714 auto archetypeIds = archetype.ids_view();
716 idInQuery.gen() == idInArchetype.id() ||
717 as_relations_trav_if(w, idInQuery, [&](Entity relation) {
718 const auto idx = core::get_index(archetypeIds, relation);
720 return idx != BadIndex;
725 return cmp_ids(idInQuery, idInArchetype);
728 GAIA_NODISCARD
inline IdCmpResult
729 cmp_ids_is_pairs(
const World& w,
const Archetype& archetype, Entity idInQuery, Entity idInArchetype) {
730 if (idInQuery.pair()) {
732 if (idInQuery == Pair(All, All))
736 if (idInQuery.id() == Is.id()) {
738 if (idInArchetype == idInQuery)
741 const auto eQ = entity_from_id(w, idInQuery.gen());
742 if (eQ == idInArchetype)
747 if (idInArchetype.id() == Is.id()) {
748 const auto eA = entity_from_id(w, idInArchetype.gen());
752 return {as_relations_trav_if(w, eQ, [eA](Entity relation) {
753 return eA == relation;
758 auto archetypeIds = archetype.ids_view();
759 return {as_relations_trav_if(w, eQ, [&archetypeIds](Entity relation) {
762 const auto idx = core::get_index(archetypeIds, relation);
764 return idx != BadIndex;
773 if (idInQuery.id() == All.id()) {
774 if (idInQuery.gen() == idInArchetype.gen())
778 if (archetype.pairs_is() > 0) {
779 auto archetypeIds = archetype.ids_view();
781 const auto e = entity_from_id(w, idInQuery.gen());
782 return {as_relations_trav_if(w, e, [&](Entity relation) {
785 const auto idx = core::get_index(archetypeIds, relation);
787 return idx != BadIndex;
800 if (idInQuery.gen() == All.id()) {
801 return {idInQuery.id() == idInArchetype.id()};
806 return cmp_ids(idInQuery, idInArchetype);
815 template <
typename OpKind>
816 GAIA_NODISCARD
inline bool match_res(
const Archetype& archetype, EntitySpan queryIds) {
819 if (archetype.pairs() == 0) {
820 return match_inter<OpKind>(
821 queryIds, archetype.ids_view(),
823 [](Entity idInQuery, Entity idInArchetype) {
824 return cmp_ids(idInQuery, idInArchetype);
829 return match_inter<OpKind>(
830 queryIds, archetype.ids_view(),
832 [](Entity idInQuery, Entity idInArchetype) {
833 return cmp_ids_pairs(idInQuery, idInArchetype);
843 template <
typename OpKind>
844 GAIA_NODISCARD
inline bool match_res_as(
const World& w,
const Archetype& archetype, EntitySpan queryIds) {
846 if (archetype.pairs() == 0) {
847 return match_inter<OpKind>(
848 queryIds, archetype.ids_view(),
850 [&](Entity idInQuery, Entity idInArchetype) {
851 return cmp_ids_is(w, archetype, idInQuery, idInArchetype);
855 return match_inter<OpKind>(
856 queryIds, archetype.ids_view(),
858 [&](Entity idInQuery, Entity idInArchetype) {
859 return cmp_ids_is_pairs(w, archetype, idInQuery, idInArchetype);
863 GAIA_NODISCARD
inline bool match_single_id_on_archetype(
const World& w,
const Archetype& archetype, Entity
id) {
864 const Entity ids[1] = {
id};
865 return match_res_as<OpOr>(w, archetype, EntitySpan{ids, 1});
868 GAIA_NODISCARD
inline bool match_single_id_on_archetype_exact(
const Archetype& archetype, Entity
id) {
869 const Entity ids[1] = {
id};
870 return match_res<OpOr>(archetype, EntitySpan{ids, 1});
873 GAIA_NODISCARD
inline EOpcode src_opcode_from_term(
const QueryTerm& term) {
874 const bool includeSelf = query_trav_has(term.travKind, QueryTravKind::Self);
875 const bool includeUp = query_trav_has(term.travKind, QueryTravKind::Up) && term.entTrav != EntityBad;
876 const bool includeDown = query_trav_has(term.travKind, QueryTravKind::Down) && term.entTrav != EntityBad;
877 if (!includeSelf && !includeUp && !includeDown)
878 return EOpcode::Src_Never;
879 if (includeSelf && !includeUp && !includeDown)
880 return EOpcode::Src_Self;
881 if (includeUp && includeDown)
882 return EOpcode::Src_UpDown;
884 return EOpcode::Src_Up;
885 return EOpcode::Src_Down;
889 uint32_t queueIdx = 0;
890 uint32_t childIdx = 0;
891 uint32_t childLevel = 0;
892 uint32_t upDepth = 0;
894 bool initialized =
false;
895 bool selfEmitted =
false;
896 Entity upSource = EntityBad;
903 void reset_runtime_state() {
910 upSource = EntityBad;
919 GAIA_NODISCARD
inline bool next_lookup_src_cursor(
923 template <
typename Func>
924 GAIA_NODISCARD
inline bool
925 each_lookup_src(
const World& w, EOpcode opcode,
const QueryTerm& term,
Entity sourceEntity, Func&& func) {
927 Entity source = EntityBad;
928 while (next_lookup_src_cursor(w, opcode, term, sourceEntity, cursor, source)) {
936 template <
typename Func>
937 GAIA_NODISCARD
inline bool
938 each_lookup_src(
const World& w,
const QueryTerm& term, Entity sourceEntity, Func&& func) {
939 return each_lookup_src(w, src_opcode_from_term(term), term, sourceEntity, GAIA_FWD(func));
942 GAIA_NODISCARD
inline bool next_lookup_src_cursor_up(
943 const World& w,
const QueryTerm& term, Entity sourceEntity, SourceLookupCursor& cursor, Entity& outSource,
945 if (!valid(w, sourceEntity))
948 const uint32_t maxDepth =
949 term.travDepth == QueryTermOptions::TravDepthUnlimited ? MAX_TRAV_DEPTH : (uint32_t)term.travDepth;
951 if (!cursor.initialized) {
952 cursor.initialized =
true;
953 cursor.upSource = sourceEntity;
956 if (includeSelf && !cursor.selfEmitted) {
957 cursor.selfEmitted =
true;
958 outSource = sourceEntity;
962 while (cursor.upDepth < maxDepth) {
963 const auto next = target(w, cursor.upSource, term.entTrav);
964 if (next == EntityBad || next == cursor.upSource)
967 cursor.upSource = next;
976 GAIA_NODISCARD
inline bool next_lookup_src_cursor_down(
977 const World& w,
const QueryTerm& term, Entity sourceEntity, SourceLookupCursor& cursor, Entity& outSource,
979 if (!valid(w, sourceEntity))
982 const uint32_t maxDepth =
983 term.travDepth == QueryTermOptions::TravDepthUnlimited ? MAX_TRAV_DEPTH : (uint32_t)term.travDepth;
985 if (!cursor.initialized) {
986 cursor.initialized =
true;
987 cursor.queue.push_back(sourceEntity);
988 cursor.levels.push_back(0);
989 cursor.visited.insert(EntityLookupKey(sourceEntity));
992 if (includeSelf && !cursor.selfEmitted) {
993 cursor.selfEmitted =
true;
994 outSource = sourceEntity;
999 if (cursor.childIdx < cursor.children.size()) {
1000 const auto child = cursor.children[cursor.childIdx++];
1001 cursor.queue.push_back(child);
1002 cursor.levels.push_back(cursor.childLevel);
1007 bool loadedChildren =
false;
1008 while (cursor.queueIdx < cursor.queue.size()) {
1009 const auto source = cursor.queue[cursor.queueIdx];
1010 const auto level = cursor.levels[cursor.queueIdx];
1012 if (level >= maxDepth)
1015 cursor.children.clear();
1016 cursor.childIdx = 0;
1017 cursor.childLevel = level + 1;
1018 sources(w, term.entTrav, source, [&](Entity next) {
1019 const auto key = EntityLookupKey(next);
1020 const auto ins = cursor.visited.insert(key);
1024 cursor.children.push_back(next);
1027 core::sort(cursor.children, [](Entity left, Entity right) {
1028 return left.id() < right.id();
1031 if (!cursor.children.empty()) {
1032 loadedChildren =
true;
1037 if (!loadedChildren)
1042 GAIA_NODISCARD
inline bool next_lookup_src_cursor(
1043 const World& w, EOpcode opcode,
const QueryTerm& term, Entity sourceEntity, SourceLookupCursor& cursor,
1044 Entity& outSource) {
1045 const bool includeSelf = query_trav_has(term.travKind, QueryTravKind::Self);
1046 const bool unlimitedTraversal =
1047 term.travDepth == QueryTermOptions::TravDepthUnlimited && term.entTrav != EntityBad;
1049 if (unlimitedTraversal &&
1050 (opcode == EOpcode::Src_Up || opcode == EOpcode::Src_Down || opcode == EOpcode::Src_UpDown)) {
1051 if (!valid(w, sourceEntity))
1054 if (!cursor.initialized)
1055 cursor.initialized =
true;
1057 if (includeSelf && !cursor.selfEmitted) {
1058 cursor.selfEmitted =
true;
1059 outSource = sourceEntity;
1064 if (cursor.cachedSources.data() != cachedSources.data() ||
1065 cursor.cachedSources.size() != cachedSources.size()) {
1066 cursor.cachedSources = cachedSources;
1067 cursor.queueIdx = 0;
1070 if (cursor.queueIdx < cursor.cachedSources.size()) {
1071 outSource = cursor.cachedSources[cursor.queueIdx++];
1078 if (opcode == EOpcode::Src_Up) {
1079 return adv_cached_src(targets_trav_cache(w, term.entTrav, sourceEntity));
1082 if (opcode == EOpcode::Src_Down) {
1083 return adv_cached_src(sources_bfs_trav_cache(w, term.entTrav, sourceEntity));
1086 if (cursor.phase == 0) {
1087 if (adv_cached_src(targets_trav_cache(w, term.entTrav, sourceEntity)))
1091 cursor.cachedSources = {};
1092 cursor.queueIdx = 0;
1095 return adv_cached_src(sources_bfs_trav_cache(w, term.entTrav, sourceEntity));
1099 case EOpcode::Src_Never:
1101 case EOpcode::Src_Self:
1102 if (cursor.initialized || !valid(w, sourceEntity))
1104 cursor.initialized =
true;
1105 outSource = sourceEntity;
1107 case EOpcode::Src_Up:
1108 return next_lookup_src_cursor_up(w, term, sourceEntity, cursor, outSource, includeSelf);
1109 case EOpcode::Src_Down:
1110 return next_lookup_src_cursor_down(w, term, sourceEntity, cursor, outSource, includeSelf);
1111 case EOpcode::Src_UpDown:
1112 if (cursor.phase == 0) {
1113 if (next_lookup_src_cursor_up(w, term, sourceEntity, cursor, outSource, includeSelf))
1115 cursor.reset_runtime_state();
1118 return next_lookup_src_cursor_down(w, term, sourceEntity, cursor, outSource,
false);
1125 GAIA_NODISCARD
inline bool match_src_term(
const World& w,
const QueryTerm& term, EOpcode opcode) {
1126 auto match_src_entity = [&](Entity source) {
1127 if (!valid(w, source))
1130 auto* pArchetype = archetype_from_entity(w, source);
1131 if (pArchetype ==
nullptr)
1134 return match_single_id_on_archetype(w, *pArchetype, term.id);
1137 return each_lookup_src(w, opcode, term, term.src, match_src_entity);
1140 GAIA_NODISCARD
inline bool match_src_term(
const World& w,
const QueryTerm& term) {
1141 return match_src_term(w, term, src_opcode_from_term(term));
1151 uint32_t sourceArchetypeIdx = 0;
1152 uint32_t sourceChunkIdx = 0;
1153 uint32_t sourceEntityIdx = 0;
1154 Entity source = EntityBad;
1158 GAIA_NODISCARD
inline bool is_var_entity(
Entity entity) {
1159 return is_variable(EntityId(entity.id()));
1162 GAIA_NODISCARD
inline uint32_t var_index(
Entity varEntity) {
1163 GAIA_ASSERT(is_var_entity(varEntity));
1164 return (uint32_t)(varEntity.id() - Var0.id());
1167 GAIA_NODISCARD
inline bool var_is_bound(
const VarBindings& vars,
Entity varEntity) {
1168 const auto idx = var_index(varEntity);
1169 return (vars.mask & (uint8_t(1) << idx)) != 0;
1172 GAIA_NODISCARD
inline bool bind_var(VarBindings& vars, Entity varEntity, Entity value) {
1173 const auto idx = var_index(varEntity);
1174 const auto bit = (uint8_t(1) << idx);
1175 if ((vars.mask & bit) != 0)
1176 return vars.values[idx].id() == value.id();
1178 vars.values[idx] = value;
1183 GAIA_NODISCARD
inline bool match_token(VarBindings& vars, Entity token, Entity value,
bool pairSide) {
1184 if (pairSide && token.id() == All.id())
1187 if (!is_var_entity(token))
1188 return token.id() == value.id();
1190 return bind_var(vars, token, value);
1194 Entity token = EntityBad;
1195 Entity matchValue = EntityBad;
1196 bool concrete =
false;
1197 bool needsBind =
false;
1201 uint32_t matchId = 0;
1202 uint8_t bindVarIdx = 0xff;
1203 bool concrete =
false;
1204 bool needsBind =
false;
1209 out.token = queryToken;
1211 if (queryToken == EntityBad)
1214 if (is_var_entity(queryToken)) {
1215 if (!var_is_bound(vars, queryToken)) {
1216 out.needsBind =
true;
1220 out.matchValue = vars.values[var_index(queryToken)];
1221 out.concrete = out.matchValue.id() != All.id();
1225 if (queryToken.id() == All.id())
1228 out.matchValue = queryToken;
1229 out.concrete =
true;
1235 GAIA_NODISCARD
inline RawMatchToken resolve_raw_pair_match_token(Entity queryToken,
const VarBindings& vars) {
1236 RawMatchToken out{};
1238 if (queryToken == EntityBad || queryToken.id() == All.id())
1241 if (is_var_entity(queryToken)) {
1242 out.bindVarIdx = (uint8_t)var_index(queryToken);
1243 if ((vars.mask & (uint8_t(1) << out.bindVarIdx)) == 0) {
1244 out.needsBind =
true;
1248 out.matchId = vars.values[out.bindVarIdx].id();
1249 out.concrete = out.matchId != All.id();
1253 out.matchId = queryToken.id();
1254 out.concrete =
true;
1258 GAIA_NODISCARD
inline uint32_t count_pair_id_matches_limited(
1259 const World& w,
const Archetype& archetype, Entity queryId,
const VarBindings& varsIn, uint32_t limit) {
1260 GAIA_ASSERT(limit > 0);
1261 GAIA_ASSERT(queryId.pair());
1263 const auto queryRel = entity_from_id(w, queryId.id());
1264 const auto queryTgt = entity_from_id(w, queryId.gen());
1265 if (queryRel == EntityBad || queryTgt == EntityBad)
1268 const auto rel = resolve_raw_pair_match_token(queryRel, varsIn);
1269 const auto tgt = resolve_raw_pair_match_token(queryTgt, varsIn);
1270 const bool sameUnboundVar = rel.needsBind && tgt.needsBind && rel.bindVarIdx == tgt.bindVarIdx;
1274 if (!rel.needsBind && !tgt.needsBind && !sameUnboundVar) {
1275 const auto matchPair = Pair(
1276 rel.concrete ? Entity((EntityId)rel.matchId, 0,
true,
false, EntityKind::EK_Gen) : All,
1277 tgt.concrete ? Entity((EntityId)tgt.matchId, 0, true, false, EntityKind::EK_Gen) : All);
1278 const auto count = archetype.pair_matches(matchPair);
1279 return count < limit ? count : limit;
1283 auto archetypeIds = archetype.ids_view();
1284 const auto cnt = (uint32_t)archetypeIds.size();
1286 const auto idInArchetype = archetypeIds[i];
1287 if (!idInArchetype.pair())
1289 if (rel.concrete && idInArchetype.id() != rel.matchId)
1291 if (tgt.concrete && idInArchetype.gen() != tgt.matchId)
1293 if (sameUnboundVar && idInArchetype.id() != idInArchetype.gen())
1304 template <
typename Func>
1305 GAIA_NODISCARD
inline bool each_term_match(
1306 const World& w,
const Archetype& candidateArchetype,
const QueryCompileCtx::VarTermOp& termOp,
1307 const VarBindings& varsIn, Func&& func);
1309 GAIA_NODISCARD
inline bool next_id_match_cursor(
1310 const World& w,
const Archetype& archetype, Entity queryId,
const VarBindings& varsIn, uint32_t& idIdx,
1311 VarBindings& outVars) {
1312 auto archetypeIds = archetype.ids_view();
1313 const auto cnt = (uint32_t)archetypeIds.size();
1315 if (!queryId.pair()) {
1316 for (uint32_t i = idIdx; i < cnt; ++i) {
1317 const auto idInArchetype = archetypeIds[i];
1318 if (idInArchetype.pair())
1321 const auto value = entity_from_id(w, idInArchetype.id());
1322 if (value == EntityBad)
1326 if (!match_token(vars, queryId, value,
false))
1337 const auto queryRel = entity_from_id(w, queryId.id());
1338 const auto queryTgt = entity_from_id(w, queryId.gen());
1339 if (queryRel == EntityBad || queryTgt == EntityBad)
1341 const auto rel = resolve_pair_query_token(queryRel, varsIn);
1342 const auto tgt = resolve_pair_query_token(queryTgt, varsIn);
1344 for (uint32_t i = idIdx; i < cnt; ++i) {
1345 const auto idInArchetype = archetypeIds[i];
1346 if (!idInArchetype.pair())
1349 if (rel.concrete && idInArchetype.id() != rel.matchValue.id())
1351 if (tgt.concrete && idInArchetype.gen() != tgt.matchValue.id())
1355 if (rel.needsBind) {
1356 const auto relValue = entity_from_id(w, idInArchetype.id());
1357 if (relValue == EntityBad)
1359 if (!match_token(vars, rel.token, relValue,
true))
1362 if (tgt.needsBind) {
1363 const auto tgtValue = entity_from_id(w, idInArchetype.gen());
1364 if (tgtValue == EntityBad)
1366 if (!match_token(vars, tgt.token, tgtValue,
true))
1378 GAIA_NODISCARD
inline bool next_term_match_cursor(
1379 const World& w,
const Archetype& archetype,
const QueryCompileCtx::VarTermOp& termOp,
1380 const VarBindings& varsIn, VarTermMatchCursor& cursor, VarBindings& outVars) {
1381 const auto& term = termOp.term;
1382 if (term.src == EntityBad)
1383 return next_id_match_cursor(w, archetype, term.id, varsIn, cursor.idIdx, outVars);
1385 auto sourceEntity = term.src;
1386 if (is_var_entity(sourceEntity)) {
1387 if (!var_is_bound(varsIn, sourceEntity))
1389 sourceEntity = varsIn.values[var_index(sourceEntity)];
1393 if (cursor.source != EntityBad) {
1394 auto* pSrcArchetype = archetype_from_entity(w, cursor.source);
1395 if (pSrcArchetype !=
nullptr &&
1396 next_id_match_cursor(w, *pSrcArchetype, term.id, varsIn, cursor.idIdx, outVars))
1400 cursor.source = EntityBad;
1403 Entity nextSource = EntityBad;
1404 if (!next_lookup_src_cursor(w, termOp.sourceOpcode, term, sourceEntity, cursor.sourceCursor, nextSource))
1407 cursor.source = nextSource;
1411 GAIA_NODISCARD
inline bool term_has_match_bound(
1412 const World& w,
const Archetype& candidateArchetype,
const QueryCompileCtx::VarTermOp& termOp,
1413 const VarBindings& vars);
1415 GAIA_NODISCARD
inline bool term_has_match(
1416 const World& w,
const Archetype& archetype,
const QueryCompileCtx::VarTermOp& termOp,
1417 const VarBindings& varsIn) {
1418 if ((uint8_t)(termOp.varMask & ~varsIn.mask) == 0)
1419 return term_has_match_bound(w, archetype, termOp, varsIn);
1421 return each_term_match(w, archetype, termOp, varsIn, [&](
const VarBindings&) {
1426 GAIA_NODISCARD
inline uint32_t count_term_matches_limited(
1427 const World& w,
const Archetype& archetype,
const QueryCompileCtx::VarTermOp& termOp,
1428 const VarBindings& varsIn, uint32_t limit) {
1429 GAIA_ASSERT(limit > 0);
1431 if ((uint8_t)(termOp.varMask & ~varsIn.mask) == 0)
1432 return term_has_match_bound(w, archetype, termOp, varsIn) ? 1u : 0u;
1434 if (termOp.term.src == EntityBad && termOp.term.id.pair())
1435 return count_pair_id_matches_limited(w, archetype, termOp.term.id, varsIn, limit);
1438 (void)each_term_match(w, archetype, termOp, varsIn, [&](
const VarBindings&) {
1440 return count >= limit;
1445 GAIA_NODISCARD
inline bool has_concrete_match_id(Entity queryId) {
1446 if (!queryId.pair())
1447 return !is_variable(queryId) && queryId.id() != All.id();
1449 return !is_variable((EntityId)queryId.id()) && queryId.id() != All.id() &&
1450 !is_variable((EntityId)queryId.gen()) && queryId.gen() != All.id();
1453 GAIA_NODISCARD
inline bool
1454 match_id_bound(
const World& w,
const Archetype& archetype, Entity queryId,
const VarBindings& vars) {
1455 auto archetypeIds = archetype.ids_view();
1456 const auto cnt = (uint32_t)archetypeIds.size();
1458 if (!queryId.pair()) {
1459 Entity queryToken = queryId;
1460 if (is_var_entity(queryToken)) {
1461 if (!var_is_bound(vars, queryToken))
1463 queryToken = vars.values[var_index(queryToken)];
1467 const auto idInArchetype = archetypeIds[i];
1468 if (idInArchetype.pair())
1471 const auto value = entity_from_id(w, idInArchetype.id());
1472 if (value == EntityBad)
1474 if (queryToken.id() != value.id())
1483 auto queryRel = entity_from_id(w, queryId.id());
1484 auto queryTgt = entity_from_id(w, queryId.gen());
1485 if (queryRel == EntityBad || queryTgt == EntityBad)
1488 if (is_var_entity(queryRel)) {
1489 if (!var_is_bound(vars, queryRel))
1491 queryRel = vars.values[var_index(queryRel)];
1494 if (is_var_entity(queryTgt)) {
1495 if (!var_is_bound(vars, queryTgt))
1497 queryTgt = vars.values[var_index(queryTgt)];
1500 const bool relIsConcrete = queryRel.id() != All.id();
1501 const bool tgtIsConcrete = queryTgt.id() != All.id();
1503 if (relIsConcrete || tgtIsConcrete) {
1505 archetype.pair_matches(Pair(relIsConcrete ? queryRel : All, tgtIsConcrete ? queryTgt : All));
1509 if (relIsConcrete && tgtIsConcrete)
1514 const auto idInArchetype = archetypeIds[i];
1515 if (!idInArchetype.pair())
1518 if (relIsConcrete && idInArchetype.id() != queryRel.id())
1520 if (tgtIsConcrete && idInArchetype.gen() != queryTgt.id())
1523 if (!relIsConcrete) {
1524 const auto rel = entity_from_id(w, idInArchetype.id());
1525 if (rel == EntityBad)
1528 if (!tgtIsConcrete) {
1529 const auto tgt = entity_from_id(w, idInArchetype.gen());
1530 if (tgt == EntityBad)
1540 GAIA_NODISCARD
inline bool next_self_src_var_match_cursor(
1541 const MatchingCtx& ctx,
const QueryCompileCtx::VarTermOp& termOp,
const VarBindings& varsIn,
1542 VarTermMatchCursor& cursor, VarBindings& outVars) {
1543 GAIA_ASSERT(ctx.pWorld !=
nullptr);
1544 GAIA_ASSERT(is_var_entity(termOp.term.src));
1545 GAIA_ASSERT(!var_is_bound(varsIn, termOp.term.src));
1546 GAIA_ASSERT(termOp.sourceOpcode == EOpcode::Src_Self);
1549 for (; cursor.sourceArchetypeIdx < sourceRecords.size(); ++cursor.sourceArchetypeIdx) {
1550 const auto* pSrcArchetype = sourceRecords[cursor.sourceArchetypeIdx].pArchetype;
1551 if (pSrcArchetype ==
nullptr)
1553 if (!idsPreFiltered && !match_single_id_on_archetype(*ctx.pWorld, *pSrcArchetype, termOp.term.id))
1556 const auto& chunks = pSrcArchetype->chunks();
1557 for (; cursor.sourceChunkIdx < chunks.size(); ++cursor.sourceChunkIdx) {
1558 const auto* pChunk = chunks[cursor.sourceChunkIdx];
1559 if (pChunk ==
nullptr || pChunk->empty())
1562 const auto entities = pChunk->entity_view();
1563 for (; cursor.sourceEntityIdx < entities.size(); ++cursor.sourceEntityIdx) {
1564 const auto entity = entities[cursor.sourceEntityIdx];
1566 if (!bind_var(vars, termOp.term.src, entity))
1570 ++cursor.sourceEntityIdx;
1574 cursor.sourceEntityIdx = 0;
1577 cursor.sourceChunkIdx = 0;
1584 for (; cursor.sourceArchetypeIdx < sourceArchetypes.size(); ++cursor.sourceArchetypeIdx) {
1585 const auto* pSrcArchetype = sourceArchetypes[cursor.sourceArchetypeIdx];
1586 if (pSrcArchetype ==
nullptr)
1588 if (!match_single_id_on_archetype(*ctx.pWorld, *pSrcArchetype, termOp.term.id))
1591 const auto& chunks = pSrcArchetype->chunks();
1592 for (; cursor.sourceChunkIdx < chunks.size(); ++cursor.sourceChunkIdx) {
1593 const auto* pChunk = chunks[cursor.sourceChunkIdx];
1594 if (pChunk ==
nullptr || pChunk->empty())
1597 const auto entities = pChunk->entity_view();
1598 for (; cursor.sourceEntityIdx < entities.size(); ++cursor.sourceEntityIdx) {
1599 const auto entity = entities[cursor.sourceEntityIdx];
1601 if (!bind_var(vars, termOp.term.src, entity))
1605 ++cursor.sourceEntityIdx;
1609 cursor.sourceEntityIdx = 0;
1612 cursor.sourceChunkIdx = 0;
1618 if (!ctx.archetypeLookup.empty()) {
1619 const auto sourceArchetypes =
1620 ctx.archetypeLookup.fetch(ctx.allArchetypes, termOp.term.id, EntityLookupKey(termOp.term.id));
1621 if (adv_matches(sourceArchetypes,
true))
1623 }
else if (adv_matches_all(ctx.allArchetypes))
1629 GAIA_NODISCARD
inline bool next_src_var_match_cursor_inverse(
1630 const MatchingCtx& ctx,
const QueryCompileCtx::VarTermOp& termOp,
const VarBindings& varsIn,
1631 VarTermMatchCursor& cursor, VarBindings& outVars, EOpcode inverseOpcode) {
1632 GAIA_ASSERT(ctx.pWorld !=
nullptr);
1633 GAIA_ASSERT(is_var_entity(termOp.term.src));
1634 GAIA_ASSERT(!var_is_bound(varsIn, termOp.term.src));
1636 inverseOpcode == EOpcode::Src_Up || inverseOpcode == EOpcode::Src_Down ||
1637 inverseOpcode == EOpcode::Src_UpDown);
1640 for (; cursor.sourceArchetypeIdx < sourceRecords.size(); ++cursor.sourceArchetypeIdx) {
1641 const auto* pSrcArchetype = sourceRecords[cursor.sourceArchetypeIdx].pArchetype;
1642 if (pSrcArchetype ==
nullptr)
1644 if (!idsPreFiltered && !match_single_id_on_archetype(*ctx.pWorld, *pSrcArchetype, termOp.term.id))
1647 const auto& chunks = pSrcArchetype->chunks();
1648 for (; cursor.sourceChunkIdx < chunks.size(); ++cursor.sourceChunkIdx) {
1649 const auto* pChunk = chunks[cursor.sourceChunkIdx];
1650 if (pChunk ==
nullptr || pChunk->empty())
1653 const auto entities = pChunk->entity_view();
1654 while (cursor.sourceEntityIdx < entities.size()) {
1655 if (cursor.source == EntityBad) {
1656 cursor.source = entities[cursor.sourceEntityIdx];
1657 cursor.sourceCursor = {};
1660 Entity candidate = EntityBad;
1661 if (next_lookup_src_cursor(
1662 *ctx.pWorld, inverseOpcode, termOp.term, cursor.source, cursor.sourceCursor, candidate)) {
1664 if (!bind_var(vars, termOp.term.src, candidate))
1671 cursor.source = EntityBad;
1672 ++cursor.sourceEntityIdx;
1675 cursor.sourceEntityIdx = 0;
1678 cursor.sourceChunkIdx = 0;
1685 for (; cursor.sourceArchetypeIdx < sourceArchetypes.size(); ++cursor.sourceArchetypeIdx) {
1686 const auto* pSrcArchetype = sourceArchetypes[cursor.sourceArchetypeIdx];
1687 if (pSrcArchetype ==
nullptr)
1689 if (!match_single_id_on_archetype(*ctx.pWorld, *pSrcArchetype, termOp.term.id))
1692 const auto& chunks = pSrcArchetype->chunks();
1693 for (; cursor.sourceChunkIdx < chunks.size(); ++cursor.sourceChunkIdx) {
1694 const auto* pChunk = chunks[cursor.sourceChunkIdx];
1695 if (pChunk ==
nullptr || pChunk->empty())
1698 const auto entities = pChunk->entity_view();
1699 while (cursor.sourceEntityIdx < entities.size()) {
1700 if (cursor.source == EntityBad) {
1701 cursor.source = entities[cursor.sourceEntityIdx];
1702 cursor.sourceCursor = {};
1705 Entity candidate = EntityBad;
1706 if (next_lookup_src_cursor(
1707 *ctx.pWorld, inverseOpcode, termOp.term, cursor.source, cursor.sourceCursor, candidate)) {
1709 if (!bind_var(vars, termOp.term.src, candidate))
1716 cursor.source = EntityBad;
1717 ++cursor.sourceEntityIdx;
1720 cursor.sourceEntityIdx = 0;
1723 cursor.sourceChunkIdx = 0;
1729 if (!ctx.archetypeLookup.empty()) {
1730 const auto sourceArchetypes =
1731 ctx.archetypeLookup.fetch(ctx.allArchetypes, termOp.term.id, EntityLookupKey(termOp.term.id));
1732 if (adv_matches(sourceArchetypes,
true))
1734 }
else if (adv_matches_all(ctx.allArchetypes))
1740 GAIA_NODISCARD
inline bool next_up_src_var_match_cursor(
1741 const MatchingCtx& ctx,
const QueryCompileCtx::VarTermOp& termOp,
const VarBindings& varsIn,
1742 VarTermMatchCursor& cursor, VarBindings& outVars) {
1743 GAIA_ASSERT(ctx.pWorld !=
nullptr);
1744 GAIA_ASSERT(is_var_entity(termOp.term.src));
1745 GAIA_ASSERT(!var_is_bound(varsIn, termOp.term.src));
1746 GAIA_ASSERT(termOp.sourceOpcode == EOpcode::Src_Up);
1747 return next_src_var_match_cursor_inverse(ctx, termOp, varsIn, cursor, outVars, EOpcode::Src_Down);
1750 GAIA_NODISCARD
inline bool next_down_src_var_match_cursor(
1751 const MatchingCtx& ctx,
const QueryCompileCtx::VarTermOp& termOp,
const VarBindings& varsIn,
1752 VarTermMatchCursor& cursor, VarBindings& outVars) {
1753 GAIA_ASSERT(termOp.sourceOpcode == EOpcode::Src_Down);
1754 return next_src_var_match_cursor_inverse(ctx, termOp, varsIn, cursor, outVars, EOpcode::Src_Up);
1757 GAIA_NODISCARD
inline bool next_updown_src_var_match_cursor(
1758 const MatchingCtx& ctx,
const QueryCompileCtx::VarTermOp& termOp,
const VarBindings& varsIn,
1759 VarTermMatchCursor& cursor, VarBindings& outVars) {
1760 GAIA_ASSERT(termOp.sourceOpcode == EOpcode::Src_UpDown);
1761 return next_src_var_match_cursor_inverse(ctx, termOp, varsIn, cursor, outVars, EOpcode::Src_UpDown);
1764 GAIA_NODISCARD
inline bool next_term_match_cursor(
1765 const MatchingCtx& ctx,
const Archetype& archetype,
const QueryCompileCtx::VarTermOp& termOp,
1766 const VarBindings& varsIn, VarTermMatchCursor& cursor, VarBindings& outVars) {
1767 const auto& term = termOp.term;
1768 const bool hasUnboundVar =
1769 term.src != EntityBad && is_var_entity(term.src) && !var_is_bound(varsIn, term.src);
1770 if (hasUnboundVar && termOp.sourceOpcode == EOpcode::Src_Self) {
1771 return next_self_src_var_match_cursor(ctx, termOp, varsIn, cursor, outVars);
1773 if (hasUnboundVar && termOp.sourceOpcode == EOpcode::Src_Up) {
1774 return next_up_src_var_match_cursor(ctx, termOp, varsIn, cursor, outVars);
1776 if (hasUnboundVar && termOp.sourceOpcode == EOpcode::Src_Down) {
1777 return next_down_src_var_match_cursor(ctx, termOp, varsIn, cursor, outVars);
1779 if (hasUnboundVar && termOp.sourceOpcode == EOpcode::Src_UpDown) {
1780 return next_updown_src_var_match_cursor(ctx, termOp, varsIn, cursor, outVars);
1783 return next_term_match_cursor(*ctx.pWorld, archetype, termOp, varsIn, cursor, outVars);
1786 GAIA_NODISCARD
inline bool term_has_match_bound(
1787 const World& w,
const Archetype& candidateArchetype,
const QueryCompileCtx::VarTermOp& termOp,
1788 const VarBindings& vars) {
1789 const auto& term = termOp.term;
1790 auto match_on_archetype = [&](
const Archetype& archetype) {
1791 return match_id_bound(w, archetype, term.id, vars);
1794 if (term.src == EntityBad)
1795 return match_on_archetype(candidateArchetype);
1797 auto sourceEntity = term.src;
1798 if (is_var_entity(sourceEntity)) {
1799 if (!var_is_bound(vars, sourceEntity))
1801 sourceEntity = vars.values[var_index(sourceEntity)];
1804 return each_lookup_src(w, termOp.sourceOpcode, term, sourceEntity, [&](Entity source) {
1805 auto* pSrcArchetype = archetype_from_entity(w, source);
1806 if (pSrcArchetype == nullptr)
1808 if (!match_on_archetype(*pSrcArchetype))
1815 template <
typename Func>
1816 GAIA_NODISCARD
inline bool each_id_match(
1817 const World& w,
const Archetype& archetype, Entity queryId,
const VarBindings& varsIn, Func&& func) {
1818 auto archetypeIds = archetype.ids_view();
1819 const auto cnt = (uint32_t)archetypeIds.size();
1821 if (!queryId.pair()) {
1823 const auto idInArchetype = archetypeIds[i];
1824 if (idInArchetype.pair())
1827 const auto value = entity_from_id(w, idInArchetype.id());
1828 if (value == EntityBad)
1832 if (!match_token(vars, queryId, value,
false))
1841 const auto queryRel = entity_from_id(w, queryId.id());
1842 const auto queryTgt = entity_from_id(w, queryId.gen());
1843 if (queryRel == EntityBad || queryTgt == EntityBad)
1845 const auto rel = resolve_pair_query_token(queryRel, varsIn);
1846 const auto tgt = resolve_pair_query_token(queryTgt, varsIn);
1849 const auto idInArchetype = archetypeIds[i];
1850 if (!idInArchetype.pair())
1853 if (rel.concrete && idInArchetype.id() != rel.matchValue.id())
1855 if (tgt.concrete && idInArchetype.gen() != tgt.matchValue.id())
1858 if (!rel.needsBind && !tgt.needsBind) {
1865 if (rel.needsBind) {
1866 const auto relValue = entity_from_id(w, idInArchetype.id());
1867 if (relValue == EntityBad)
1869 if (!match_token(vars, rel.token, relValue,
true))
1872 if (tgt.needsBind) {
1873 const auto tgtValue = entity_from_id(w, idInArchetype.gen());
1874 if (tgtValue == EntityBad)
1876 if (!match_token(vars, tgt.token, tgtValue,
true))
1887 template <
typename Func>
1888 GAIA_NODISCARD
inline bool each_term_match(
1889 const World& w,
const Archetype& candidateArchetype,
const QueryCompileCtx::VarTermOp& termOp,
1890 const VarBindings& varsIn, Func&& func) {
1891 const auto& term = termOp.term;
1892 auto&& matchFunc = GAIA_FWD(func);
1893 auto each_on_src = [&](Entity sourceEntity,
const VarBindings& vars) {
1894 return each_lookup_src(w, termOp.sourceOpcode, term, sourceEntity, [&](Entity source) {
1895 auto* pSrcArchetype = archetype_from_entity(w, source);
1896 if (pSrcArchetype == nullptr)
1899 return each_id_match(w, *pSrcArchetype, term.id, vars, matchFunc);
1903 if (term.src == EntityBad)
1904 return each_id_match(w, candidateArchetype, term.id, varsIn, matchFunc);
1906 if (is_var_entity(term.src)) {
1907 if (!var_is_bound(varsIn, term.src))
1910 const auto source = varsIn.values[var_index(term.src)];
1911 return each_on_src(source, varsIn);
1914 return each_on_src(term.src, varsIn);
1917 template <
typename OpKind, MatchingStyle Style>
1919 if constexpr (Style != MatchingStyle::Complex) {
1920 if (ctx.idsToMatch.size() == 1) {
1921 for (
const auto& entry: records) {
1922 const auto* pArchetype = entry.pArchetype;
1923 if (is_archetype_marked(ctx, pArchetype))
1925#if GAIA_USE_PARTITIONED_BLOOM_FILTER >= 0
1926 if constexpr (Style == MatchingStyle::Simple) {
1927 if (!OpKind::check_mask(pArchetype->queryMask(), ctx.queryMask))
1931 mark_archetype_match(ctx, pArchetype);
1937 if constexpr (Style == MatchingStyle::Complex) {
1938 for (
const auto& record: records) {
1939 const auto* pArchetype = record.pArchetype;
1940 if (is_archetype_marked(ctx, pArchetype))
1943 if (!match_res_as<OpKind>(*ctx.pWorld, *pArchetype, ctx.idsToMatch))
1946 mark_archetype_match(ctx, pArchetype);
1949#if GAIA_USE_PARTITIONED_BLOOM_FILTER >= 0
1950 else if constexpr (Style == MatchingStyle::Simple) {
1951 for (
const auto& record: records) {
1952 const auto* pArchetype = record.pArchetype;
1953 if (is_archetype_marked(ctx, pArchetype))
1957 if (!OpKind::check_mask(pArchetype->queryMask(), ctx.queryMask))
1960 if (!match_res<OpKind>(*pArchetype, ctx.idsToMatch))
1963 mark_archetype_match(ctx, pArchetype);
1968 for (
const auto& record: records) {
1969 const auto* pArchetype = record.pArchetype;
1970 if (is_archetype_marked(ctx, pArchetype))
1973 if (!match_res<OpKind>(*pArchetype, ctx.idsToMatch))
1976 mark_archetype_match(ctx, pArchetype);
1981 template <
typename OpKind, MatchingStyle Style>
1983 if constexpr (Style == MatchingStyle::Complex) {
1984 for (
const auto* pArchetype: archetypes) {
1985 if (is_archetype_marked(ctx, pArchetype))
1988 if (!match_res_as<OpKind>(*ctx.pWorld, *pArchetype, ctx.idsToMatch))
1991 mark_archetype_match(ctx, pArchetype);
1994#if GAIA_USE_PARTITIONED_BLOOM_FILTER >= 0
1995 else if constexpr (Style == MatchingStyle::Simple) {
1996 for (
const auto* pArchetype: archetypes) {
1997 if (is_archetype_marked(ctx, pArchetype))
2000 if (!OpKind::check_mask(pArchetype->queryMask(), ctx.queryMask))
2003 if (!match_res<OpKind>(*pArchetype, ctx.idsToMatch))
2006 mark_archetype_match(ctx, pArchetype);
2011 for (
const auto* pArchetype: archetypes) {
2012 if (is_archetype_marked(ctx, pArchetype))
2015 if (!match_res<OpKind>(*pArchetype, ctx.idsToMatch))
2018 mark_archetype_match(ctx, pArchetype);
2023 template <
typename OpKind, MatchingStyle Style>
2024 inline void match_archetype_inter(
2026 uint32_t lastMatchedIdx = OpKind::handle_last_match(ctx, entityKey, (uint32_t)records.size());
2027 if (lastMatchedIdx >= records.size())
2030 auto recordsNew =
std::span(&records[lastMatchedIdx], records.size() - lastMatchedIdx);
2031 match_archetype_inter<OpKind, Style>(ctx, recordsNew);
2034 template <
typename OpKind, MatchingStyle Style>
2037 uint32_t lastMatchedIdx = OpKind::handle_last_match(ctx, entityKey, (uint32_t)archetypes.size());
2038 if (lastMatchedIdx >= archetypes.size())
2041 auto archetypesNew =
std::span(&archetypes[lastMatchedIdx], archetypes.size() - lastMatchedIdx);
2042 match_archetype_inter<OpKind, Style>(ctx, archetypesNew);
2045 template <MatchingStyle Style>
2046 inline void match_archetype_all(MatchingCtx& ctx) {
2047 if constexpr (Style == MatchingStyle::Complex) {
2050 if (ctx.ent.id() == Is.id()) {
2051 ctx.ent = EntityBad;
2052 match_archetype_inter<OpAll, Style>(ctx, EntityBadLookupKey, ctx.allArchetypes);
2054 auto entityKey = EntityLookupKey(ctx.ent);
2056 auto archetypes = ctx.archetypeLookup.fetch(ctx.allArchetypes, ctx.ent, entityKey);
2057 if (archetypes.empty())
2060 match_archetype_inter<OpAll, Style>(ctx, entityKey, archetypes);
2063 auto entityKey = EntityLookupKey(ctx.ent);
2067 auto archetypes = ctx.archetypeLookup.fetch(ctx.allArchetypes, ctx.ent, entityKey);
2068 if (archetypes.empty())
2071 match_archetype_inter<OpAll, Style>(ctx, entityKey, archetypes);
2075 template <MatchingStyle Style>
2076 inline void match_archetype_or(MatchingCtx& ctx) {
2077 EntityLookupKey entityKey(ctx.ent);
2082 auto archetypes = ctx.archetypeLookup.fetch(ctx.allArchetypes, ctx.ent, entityKey);
2083 if (archetypes.empty())
2086 match_archetype_inter<OpOr, Style>(ctx, entityKey, archetypes);
2089 inline void match_archetype_or_as(MatchingCtx& ctx) {
2090 EntityLookupKey entityKey = EntityBadLookupKey;
2095 if (ctx.ent.id() == Is.id()) {
2096 ctx.ent = EntityBad;
2097 match_archetype_inter<OpOr, MatchingStyle::Complex>(ctx, entityKey, ctx.allArchetypes);
2099 entityKey = EntityLookupKey(ctx.ent);
2101 auto archetypes = ctx.archetypeLookup.fetch(ctx.allArchetypes, ctx.ent, entityKey);
2102 if (archetypes.empty())
2105 match_archetype_inter<OpOr, MatchingStyle::Complex>(ctx, entityKey, archetypes);
2109 template <MatchingStyle Style>
2110 inline void match_archetype_no_2(MatchingCtx& ctx) {
2114 if constexpr (Style == MatchingStyle::Complex) {
2115 for (uint32_t i = 0; i < ctx.pMatchesArr->size();) {
2116 const auto* pArchetype = (*ctx.pMatchesArr)[i];
2118 if (match_res_as<OpNo>(*ctx.pWorld, *pArchetype, ctx.idsToMatch)) {
2123 core::swap_erase(*ctx.pMatchesArr, i);
2126#if GAIA_USE_PARTITIONED_BLOOM_FILTER >= 0
2127 else if constexpr (Style == MatchingStyle::Simple) {
2128 for (uint32_t i = 0; i < ctx.pMatchesArr->size();) {
2129 const auto* pArchetype = (*ctx.pMatchesArr)[i];
2132 if (OpNo::check_mask(pArchetype->queryMask(), ctx.queryMask))
2135 if (match_res<OpNo>(*pArchetype, ctx.idsToMatch)) {
2140 core::swap_erase(*ctx.pMatchesArr, i);
2145 for (uint32_t i = 0; i < ctx.pMatchesArr->size();) {
2146 const auto* pArchetype = (*ctx.pMatchesArr)[i];
2148 if (match_res<OpNo>(*pArchetype, ctx.idsToMatch)) {
2153 core::swap_erase(*ctx.pMatchesArr, i);
2158 template <
typename OpKind, MatchingStyle Style,
bool WildcardWithAsFallback = false>
2159 inline void filter_current_matches(MatchingCtx& ctx, EntitySpan idsToMatch) {
2160 if constexpr (Style == MatchingStyle::Complex) {
2161 for (uint32_t i = 0; i < ctx.pMatchesArr->size();) {
2162 const auto* pArchetype = (*ctx.pMatchesArr)[i];
2163 if (match_res_as<OpKind>(*ctx.pWorld, *pArchetype, idsToMatch)) {
2168 core::swap_erase(*ctx.pMatchesArr, i);
2171#if GAIA_USE_PARTITIONED_BLOOM_FILTER >= 0
2172 else if constexpr (Style == MatchingStyle::Simple) {
2173 for (uint32_t i = 0; i < ctx.pMatchesArr->size();) {
2174 const auto* pArchetype = (*ctx.pMatchesArr)[i];
2175 if (OpKind::check_mask(pArchetype->queryMask(), ctx.queryMask) &&
2176 match_res<OpKind>(*pArchetype, idsToMatch)) {
2181 core::swap_erase(*ctx.pMatchesArr, i);
2186 for (uint32_t i = 0; i < ctx.pMatchesArr->size();) {
2187 const auto* pArchetype = (*ctx.pMatchesArr)[i];
2188 if (match_res<OpKind>(*pArchetype, idsToMatch) ||
2189 (WildcardWithAsFallback && match_res_as<OpKind>(*ctx.pWorld, *pArchetype, idsToMatch))) {
2194 core::swap_erase(*ctx.pMatchesArr, i);
2199 template <MatchingStyle Style>
2200 GAIA_NODISCARD
inline bool exec_not_impl(
const QueryCompileCtx& comp, MatchingCtx& ctx) {
2201 ctx.idsToMatch =
std::span{comp.ids_not.data(), comp.ids_not.size()};
2203 if (ctx.targetEntities.empty()) {
2205 if (ctx.pMatchesArr->empty()) {
2208 match_archetype_inter<detail::OpNo, Style>(ctx, EntityBadLookupKey, ctx.allArchetypes);
2210 match_archetype_no_2<Style>(ctx);
2214 if (ctx.pMatchesArr->empty())
2215 match_archetype_inter<detail::OpNo, Style>(ctx, ctx.allArchetypes);
2217 match_archetype_no_2<Style>(ctx);
2223 template <MatchingStyle Style>
2224 GAIA_NODISCARD
inline bool exec_all_impl(
const QueryCompileCtx& comp, MatchingCtx& ctx) {
2225 ctx.ent = comp.ids_all[0];
2226 ctx.idsToMatch =
std::span{comp.ids_all.data(), comp.ids_all.size()};
2228 if (ctx.targetEntities.empty())
2229 match_archetype_all<Style>(ctx);
2231 match_archetype_inter<OpAll, Style>(ctx, ctx.allArchetypes);
2234 return !ctx.pMatchesArr->empty();
2237 template <MatchingStyle Style>
2238 GAIA_NODISCARD
inline bool exec_or_noall_impl(
const QueryCompileCtx& comp, MatchingCtx& ctx) {
2242 const auto cnt = comp.ids_or.size();
2245 ctx.ent = comp.ids_or[i];
2246 const Entity idsToMatchData[1] = {ctx.ent};
2247 ctx.idsToMatch = EntitySpan{idsToMatchData, 1};
2249 if constexpr (Style == MatchingStyle::Complex)
2250 match_archetype_or_as(ctx);
2252 match_archetype_or<Style>(ctx);
2258 template <MatchingStyle Style>
2259 GAIA_NODISCARD
inline bool exec_or_withall_impl(
const QueryCompileCtx& comp, MatchingCtx& ctx) {
2263 ctx.idsToMatch =
std::span{comp.ids_or.data(), comp.ids_or.size()};
2265 if constexpr (Style == MatchingStyle::Complex)
2266 filter_current_matches<OpOr, MatchingStyle::Complex>(ctx, ctx.idsToMatch);
2267 else if constexpr (Style == MatchingStyle::Simple)
2268 filter_current_matches<OpOr, MatchingStyle::Simple>(ctx, ctx.idsToMatch);
2270 filter_current_matches<OpOr, MatchingStyle::Wildcard, true>(ctx, ctx.idsToMatch);
2275 template <
typename SourceTermsArray>
2276 GAIA_NODISCARD
inline const QueryCompileCtx::SourceTermOp&
2277 get_src_term_op(
const QueryCompileCtx& comp,
const MatchingCtx& ctx,
const SourceTermsArray& terms) {
2278 const auto& stackItem = comp.ops[ctx.pc];
2279 GAIA_ASSERT(stackItem.arg < terms.size());
2280 return terms[stackItem.arg];
2285 static constexpr uint32_t OpcodeArgLimit = 256u;
2287 MAX_ITEMS_IN_QUERY <= OpcodeArgLimit,
2288 "CompiledOp::arg is uint8_t. Increase arg width if query term capacity grows above 256.");
2293 static const char* opcode_name(detail::EOpcode opcode) {
2294 static const char* s_names[] = {
2319 "term_all_src_bind",
2328 "search_other_or_bind",
2331 "search_maybe_finalize",
2338 sizeof(s_names) /
sizeof(s_names[0]) == (uint32_t)detail::EOpcode::Var_Final_Success + 1u,
2339 "Opcode name table out of sync with EOpcode.");
2340 return s_names[(uint32_t)opcode];
2343 GAIA_NODISCARD
static bool opcode_has_arg(detail::EOpcode opcode) {
2344 return opcode == detail::EOpcode::Src_AllTerm ||
2345 opcode == detail::EOpcode::Src_NotTerm ||
2346 opcode == detail::EOpcode::Src_OrTerm;
2349 static void add_uint(
util::str& out, uint32_t value) {
2351 const auto len = GAIA_STRFMT(buffer,
sizeof(buffer),
"%u", value);
2352 GAIA_ASSERT(len >= 0);
2353 out.
append(buffer, (uint32_t)len);
2356 static void add_cstr(
util::str& out,
const char* value) {
2357 GAIA_ASSERT(value !=
nullptr);
2358 out.
append(value, (uint32_t)GAIA_STRLEN(value, 64));
2361 static void add_id_expr(
util::str& out,
const World& world, EntityId
id) {
2362 if (is_variable(
id)) {
2364 add_uint(out, (uint32_t)(
id - Var0.id()));
2368 if (
id == All.id()) {
2373 const auto entity = entity_from_id(world,
id);
2374 if (entity != EntityBad)
2375 add_entity_expr(out, world, entity);
2378 add_uint(out, (uint32_t)
id);
2383 if (entity == EntityBad) {
2388 if (entity.pair()) {
2390 add_id_expr(out, world, (EntityId)entity.id());
2392 add_id_expr(out, world, (EntityId)entity.gen());
2397 if (is_variable(EntityId(entity.id()))) {
2399 add_uint(out, (uint32_t)(entity.id() - Var0.id()));
2403 if (entity.id() == All.id()) {
2408 const auto name = entity_name(world, entity);
2409 if (!name.empty()) {
2410 out.
append(name.data(), name.size());
2414 add_uint(out, entity.id());
2416 add_uint(out, entity.gen());
2420 add_entity_expr(out, world, term.
id);
2422 if (term.
src == EntityBad)
2425 add_entity_expr(out, world, term.
src);
2428 if (term.
entTrav != EntityBad) {
2430 add_entity_expr(out, world, term.
entTrav);
2432 if (term.
travDepth == QueryTermOptions::TravDepthUnlimited)
2435 add_uint(out, (uint32_t)term.
travDepth);
2444 add_cstr(out, title);
2446 add_uint(out, (uint32_t)ids.size());
2449 const auto cnt = (uint32_t)ids.size();
2454 add_entity_expr(out, world, ids[i]);
2459 static void add_src_terms_section(
2462 const World& world) {
2466 add_cstr(out, title);
2468 add_uint(out, (uint32_t)terms.size());
2471 const auto cnt = (uint32_t)terms.size();
2476 add_cstr(out, opcode_name(terms[i].opcode));
2478 add_term_expr(out, world, terms[i].term);
2483 static void add_var_terms_section(
2489 add_cstr(out, title);
2491 add_uint(out, (uint32_t)terms.size());
2494 const auto cnt = (uint32_t)terms.size();
2499 add_cstr(out, opcode_name(terms[i].sourceOpcode));
2501 add_term_expr(out, world, terms[i].term);
2508 switch (detail::var_program_opcode_meta(op.
opcode).termSet) {
2509 case detail::EVarProgramTermSet::None:
2512 case detail::EVarProgramTermSet::Or:
2514 case detail::EVarProgramTermSet::Any:
2516 case detail::EVarProgramTermSet::Not:
2518 case detail::EVarProgramTermSet::All:
2524 static void add_var_program_ops_section(
2530 add_cstr(out, title);
2532 add_uint(out, (uint32_t)ops.size());
2535 const auto cnt = (uint32_t)ops.size();
2537 const auto& op = ops[i];
2541 add_cstr(out, opcode_name(op.
opcode));
2542 if (detail::var_program_opcode_meta(op.
opcode).termSet != detail::EVarProgramTermSet::None) {
2544 add_uint(out, (uint32_t)op.
arg);
2546 add_uint(out, (uint32_t)op.
cost);
2548 add_term_expr(out, world, var_program_op_term(comp, op));
2551 add_uint(out, (uint32_t)op.
pc_ok);
2553 add_uint(out, (uint32_t)op.
pc_fail);
2562 out.
append(
"var_exec: ");
2580 [[maybe_unused]]
const auto len = GAIA_STRFMT(title,
sizeof(title),
"varp%u", i);
2581 GAIA_ASSERT(len > 0);
2582 add_var_program_ops_section(out, title, detail::program_ops(comp, step.program), comp, world);
2590 GAIA_FOR(MaxVarCnt) {
2591 const auto bit = (uint8_t(1) << i);
2592 if ((vars.mask & bit) == 0)
2599 GAIA_NODISCARD
static uint8_t
2603 if (detail::is_var_entity(term.
src) && !detail::var_is_bound(vars, term.
src))
2604 mask |= (uint8_t(1) << detail::var_index(term.
src));
2606 if (!term.
id.pair()) {
2607 const auto idEnt = entity_from_id(world, term.
id.id());
2608 if (detail::is_var_entity(idEnt) && !detail::var_is_bound(vars, idEnt))
2609 mask |= (uint8_t(1) << detail::var_index(idEnt));
2613 const auto relEnt = entity_from_id(world, term.
id.id());
2614 if (detail::is_var_entity(relEnt) && !detail::var_is_bound(vars, relEnt))
2615 mask |= (uint8_t(1) << detail::var_index(relEnt));
2617 const auto tgtEnt = entity_from_id(world, term.
id.gen());
2618 if (detail::is_var_entity(tgtEnt) && !detail::var_is_bound(vars, tgtEnt))
2619 mask |= (uint8_t(1) << detail::var_index(tgtEnt));
2624 GAIA_NODISCARD
bool eval_variable_terms_program_on_archetype(
2628 return match_search_program_on_archetype(ctx, archetype, programStep, orAlreadySatisfied);
2634 case detail::EOpcode::Var_Term_Or_Check:
2635 case detail::EOpcode::Var_Term_Or_Bind:
2636 case detail::EOpcode::Var_Final_Or_Check:
2638 case detail::EOpcode::Var_Term_Any_Check:
2639 case detail::EOpcode::Var_Term_Any_Bind:
2641 case detail::EOpcode::Var_Term_Not:
2642 case detail::EOpcode::Var_Final_Not_Check:
2644 case detail::EOpcode::Var_Term_All_Check:
2645 case detail::EOpcode::Var_Term_All_Bind:
2646 case detail::EOpcode::Var_Term_All_Src_Bind:
2654 GAIA_NODISCARD
bool select_next_pending_search_all_term(
2656 uint16_t pendingMask,
const detail::VarBindings& vars, uint32_t& outLocalIdx, uint32_t& outPc,
2657 bool preferBoundTerms =
true)
const {
2658 outLocalIdx = (uint32_t)-1;
2659 outPc = (uint32_t)-1;
2660 uint32_t firstReadyLocalIdx = (uint32_t)-1;
2661 uint32_t firstReadyPc = (uint32_t)-1;
2663 for (uint32_t localIdx = 0; localIdx < search.allCount; ++localIdx) {
2664 const auto bit = (uint16_t)(uint16_t(1) << localIdx);
2665 if ((pendingMask & bit) == 0)
2668 const auto bindPc = (uint32_t)search.allBegin + localIdx;
2669 const auto& bindOp = programOps[bindPc];
2670 const auto& termOp = search_program_term_op(bindOp);
2671 if (detail::is_var_entity(termOp.term.src) && !detail::var_is_bound(vars, termOp.term.src) &&
2672 bindOp.opcode != detail::EOpcode::Var_Term_All_Src_Bind)
2675 const bool bindsNewVars = (uint8_t)(termOp.varMask & ~vars.mask) != 0;
2676 const auto pc = bindsNewVars ? bindPc : (uint32_t)search.allCheckBegin + localIdx;
2677 if (preferBoundTerms && !bindsNewVars) {
2678 outLocalIdx = localIdx;
2683 if (firstReadyLocalIdx == (uint32_t)-1) {
2684 firstReadyLocalIdx = localIdx;
2689 if (firstReadyLocalIdx == (uint32_t)-1)
2692 outLocalIdx = firstReadyLocalIdx;
2693 outPc = firstReadyPc;
2697 GAIA_NODISCARD
bool select_next_pending_search_or_term(
2699 uint16_t pendingMask, uint16_t pendingCheckMask,
const detail::VarBindings& vars,
bool preferBoundTerms,
2700 bool requireNewBindings, uint32_t& outLocalIdx, uint32_t& outPc)
const {
2701 outLocalIdx = (uint32_t)-1;
2702 outPc = (uint32_t)-1;
2703 if (requireNewBindings && (uint8_t)(search.orVarMask & ~vars.mask) == 0)
2706 uint32_t firstReadyLocalIdx = (uint32_t)-1;
2707 uint32_t firstReadyPc = (uint32_t)-1;
2709 for (uint32_t localIdx = 0; localIdx < search.orCount; ++localIdx) {
2710 const auto bit = (uint16_t)(uint16_t(1) << localIdx);
2711 if ((pendingMask & bit) == 0)
2714 const auto bindPc = (uint32_t)search.orBegin + localIdx;
2715 const auto& bindOp = programOps[bindPc];
2716 const auto& termOp = search_program_term_op(bindOp);
2717 if (detail::is_var_entity(termOp.term.src) && !detail::var_is_bound(vars, termOp.term.src))
2720 const bool bindsNewVars = (uint8_t)(termOp.varMask & ~vars.mask) != 0;
2721 if (requireNewBindings) {
2724 outLocalIdx = localIdx;
2729 if (!bindsNewVars && (pendingCheckMask & bit) == 0)
2732 const auto pc = bindsNewVars ? bindPc : (uint32_t)search.orCheckBegin + localIdx;
2733 if (preferBoundTerms && !bindsNewVars) {
2734 outLocalIdx = localIdx;
2739 if (firstReadyLocalIdx == (uint32_t)-1) {
2740 firstReadyLocalIdx = localIdx;
2745 if (firstReadyLocalIdx == (uint32_t)-1)
2748 outLocalIdx = firstReadyLocalIdx;
2749 outPc = firstReadyPc;
2753 GAIA_NODISCARD
bool select_next_pending_search_any_term(
2755 uint16_t pendingMask,
const detail::VarBindings& vars, uint32_t& outLocalIdx, uint32_t& outPc)
const {
2756 outLocalIdx = (uint32_t)-1;
2757 outPc = (uint32_t)-1;
2758 uint32_t firstReadyBindingLocalIdx = (uint32_t)-1;
2759 uint32_t firstReadyBindingPc = (uint32_t)-1;
2761 for (uint32_t localIdx = 0; localIdx < search.anyCount; ++localIdx) {
2762 const auto bit = (uint16_t)(uint16_t(1) << localIdx);
2763 if ((pendingMask & bit) == 0)
2766 const auto bindPc = (uint32_t)search.anyBegin + localIdx;
2767 const auto& bindOp = programOps[bindPc];
2768 const auto& termOp = search_program_term_op(bindOp);
2769 if (detail::is_var_entity(termOp.term.src) && !detail::var_is_bound(vars, termOp.term.src))
2772 const bool bindsNewVars = (uint8_t)(termOp.varMask & ~vars.mask) != 0;
2773 if (!bindsNewVars) {
2774 outLocalIdx = localIdx;
2775 outPc = (uint32_t)search.anyCheckBegin + localIdx;
2779 if (firstReadyBindingLocalIdx == (uint32_t)-1) {
2780 firstReadyBindingLocalIdx = localIdx;
2781 firstReadyBindingPc = bindPc;
2785 if (firstReadyBindingLocalIdx == (uint32_t)-1)
2788 outLocalIdx = firstReadyBindingLocalIdx;
2789 outPc = firstReadyBindingPc;
2793 GAIA_NODISCARD int32_t select_best_pending_search_term(
2796 uint32_t& outBestIdx)
const {
2797 constexpr uint32_t MatchProbeLimit = 64;
2798 outBestIdx = (uint32_t)-1;
2799 uint32_t bestMatchCnt = MatchProbeLimit + 1;
2801 for (uint32_t localIdx = 0; localIdx < count; ++localIdx) {
2802 const auto i = (uint32_t)begin + localIdx;
2803 const auto bit = (uint16_t)(uint16_t(1) << i);
2804 if ((pendingMask & bit) == 0)
2807 const auto& termOp = search_program_term_op(programOps[i]);
2808 if (detail::is_var_entity(termOp.term.src) && !detail::var_is_bound(vars, termOp.term.src))
2811 const auto matchCnt =
2812 detail::count_term_matches_limited(*ctx.
pWorld, archetype, termOp, vars, bestMatchCnt);
2816 if (outBestIdx == (uint32_t)-1 || matchCnt < bestMatchCnt) {
2818 bestMatchCnt = matchCnt;
2819 if (bestMatchCnt == 1)
2824 return outBestIdx == (uint32_t)-1 ? 0 : 1;
2827 GAIA_NODISCARD
bool can_skip_pending_search_all(
2830 const auto anyVarMask = m_compCtx.varMaskAny;
2831 for (uint32_t localIdx = 0; localIdx < search.allCount; ++localIdx) {
2832 const auto i = (uint32_t)search.allBegin + localIdx;
2833 const auto bit = (uint16_t(1) << i);
2834 if ((pendingAllMask & bit) == 0)
2837 const auto& termOp = search_program_term_op(programOps[i]);
2838 const auto missingMask = (uint8_t)(termOp.varMask & ~vars.mask);
2839 if (missingMask == 0)
2841 if ((missingMask & ~anyVarMask) != 0)
2848 GAIA_NODISCARD
bool match_search_program_on_archetype(
2851 using namespace detail;
2853 static constexpr uint16_t BacktrackPC = (uint16_t)-1;
2855 struct SearchProgramState {
2857 uint16_t pendingAllMask = 0;
2858 uint16_t pendingOrMask = 0;
2859 uint16_t pendingFinalOrMask = 0;
2860 uint16_t pendingAnyMask = 0;
2861 uint16_t pc = BacktrackPC;
2862 uint8_t termOpIdx = 0xff;
2863 uint8_t bestOrIdx = 0xff;
2864 uint8_t scanIdx = 0;
2865 bool orMatched =
false;
2866 bool anyMatched =
false;
2867 bool currentAnyMatched =
false;
2870 struct SearchBacktrackFrame {
2871 SearchProgramState state{};
2872 VarBindings varsBase{};
2873 VarTermMatchCursor cursor{};
2876 const auto& program = programStep.program;
2877 const auto& search = programStep.search;
2878 const auto programOps = detail::program_ops(m_compCtx, program);
2879 if (programOps.empty())
2881 GAIA_ASSERT(search.selectAllPc != BacktrackPC);
2882 GAIA_ASSERT(search.selectOrPc != BacktrackPC);
2883 GAIA_ASSERT(search.selectOtherOrPc != BacktrackPC);
2884 GAIA_ASSERT(search.selectOtherOrBindPc != BacktrackPC);
2885 GAIA_ASSERT(search.beginAnyPc != BacktrackPC);
2886 GAIA_ASSERT(search.selectAnyPc != BacktrackPC);
2887 GAIA_ASSERT(search.maybeFinalizePc != BacktrackPC);
2889 const auto is_term_ready = [&](
const detail::CompiledOp& op,
const VarBindings& vars) {
2890 const auto& termOp = search_program_term_op(op);
2891 return !is_var_entity(termOp.term.src) || var_is_bound(vars, termOp.term.src) ||
2892 op.
opcode == EOpcode::Var_Term_All_Src_Bind;
2895 const auto can_binding_satisfy_pending_or = [&](
const SearchProgramState& state,
2896 const VarBindings& nextVars) {
2897 if (state.orMatched || search.orCount == 0 || state.pendingOrMask == 0)
2900 bool hasDeferredOr =
false;
2901 for (uint32_t localIdx = 0; localIdx < search.orCount; ++localIdx) {
2902 const auto bit = (uint16_t)(uint16_t(1) << localIdx);
2903 if ((state.pendingOrMask & bit) == 0)
2906 const auto bindPc = (uint32_t)search.orBegin + localIdx;
2907 const auto& bindOp = programOps[bindPc];
2908 const auto& termOp = search_program_term_op(bindOp);
2909 const auto missingMaskBefore = (uint8_t)(termOp.varMask & ~state.vars.mask);
2910 const auto missingMaskAfter = (uint8_t)(termOp.varMask & ~nextVars.mask);
2911 if (missingMaskAfter != 0) {
2912 hasDeferredOr =
true;
2916 if (missingMaskBefore == 0 && (state.pendingFinalOrMask & bit) == 0)
2919 if (term_has_match(*ctx.
pWorld, archetype, termOp, nextVars))
2923 return hasDeferredOr;
2926 const auto adv_after_search_term_success = [&](SearchProgramState& state,
const detail::CompiledOp& op,
2927 VarBindings nextVars) {
2928 const auto bit = (uint16_t)(uint16_t(1) << state.termOpIdx);
2929 state.vars = nextVars;
2931 case EOpcode::Var_Term_All_Check:
2932 case EOpcode::Var_Term_All_Bind:
2933 case EOpcode::Var_Term_All_Src_Bind:
2934 state.pendingAllMask = (uint16_t)(state.pendingAllMask & ~bit);
2935 state.pc = op.
pc_ok;
2937 case EOpcode::Var_Term_Or_Check:
2938 case EOpcode::Var_Term_Or_Bind:
2939 state.pendingOrMask = (uint16_t)(state.pendingOrMask & ~(uint16_t(1) << state.termOpIdx));
2940 state.pendingFinalOrMask = (uint16_t)(state.pendingFinalOrMask & ~(uint16_t(1) << state.termOpIdx));
2941 state.orMatched =
true;
2942 state.pc = op.
pc_ok;
2944 case EOpcode::Var_Term_Any_Check:
2945 case EOpcode::Var_Term_Any_Bind:
2946 state.pendingAnyMask = (uint16_t)(state.pendingAnyMask & ~(uint16_t(1) << state.termOpIdx));
2947 state.anyMatched =
true;
2948 state.currentAnyMatched =
true;
2949 state.pc = op.
pc_ok;
2953 state.pc = BacktrackPC;
2958 const auto handle_search_term_exhausted = [&](SearchProgramState& state,
const detail::CompiledOp& op) {
2959 if (op.
opcode == EOpcode::Var_Term_Any_Check || op.
opcode == EOpcode::Var_Term_Any_Bind) {
2960 state.pendingAnyMask = (uint16_t)(state.pendingAnyMask & ~(uint16_t(1) << state.termOpIdx));
2965 const auto try_enter_search_term = [&](SearchProgramState& state,
2967 const auto& op = programOps[state.pc];
2968 const auto& termOp = search_program_term_op(op);
2969 SearchBacktrackFrame frame{};
2970 frame.state = state;
2971 frame.varsBase = state.vars;
2973 VarBindings nextVars{};
2975 if (!detail::next_term_match_cursor(ctx, archetype, termOp, frame.varsBase, frame.cursor, nextVars))
2977 if (can_binding_satisfy_pending_or(state, nextVars))
2981 if (op.
opcode == EOpcode::Var_Term_Any_Check || op.
opcode == EOpcode::Var_Term_Any_Bind) {
2982 frame.state.anyMatched =
true;
2983 frame.state.currentAnyMatched =
true;
2986 stack.push_back(GAIA_MOV(frame));
2987 adv_after_search_term_success(state, op, nextVars);
2991 const auto backtrack = [&](SearchProgramState& state,
2993 while (!stack.empty()) {
2994 auto& frame = stack.back();
2995 const auto resumeState = frame.state;
2996 const auto& op = programOps[resumeState.pc];
2997 const auto& termOp = search_program_term_op(op);
2998 VarBindings nextVars{};
3000 if (detail::next_term_match_cursor(ctx, archetype, termOp, frame.varsBase, frame.cursor, nextVars)) {
3001 if (op.
opcode == EOpcode::Var_Term_Any_Check || op.
opcode == EOpcode::Var_Term_Any_Bind) {
3002 frame.state.anyMatched =
true;
3003 frame.state.currentAnyMatched =
true;
3006 state = frame.state;
3007 adv_after_search_term_success(state, op, nextVars);
3012 state = resumeState;
3013 handle_search_term_exhausted(state, op);
3014 if (state.pc != BacktrackPC)
3022 SearchProgramState state{};
3023 state.vars = make_initial_var_bindings(ctx);
3024 state.pendingAllMask = search.initialAllMask;
3025 state.pendingOrMask = search.initialOrMask;
3026 state.pendingFinalOrMask = search.initialOrMask;
3027 state.pendingAnyMask = search.initialAnyMask;
3028 state.pc = search.selectAllPc;
3031 if (state.pc == BacktrackPC) {
3032 if (!backtrack(state, stack))
3036 const auto& op = programOps[state.pc];
3038 case EOpcode::Var_Search_SelectAll: {
3039 if (state.pendingAllMask == 0) {
3044 if (search.orCount == 0 && search.anyCount == 0) {
3045 uint32_t nextAllLocalIdx = (uint32_t)-1;
3046 uint32_t nextAllPc = (uint32_t)-1;
3047 if (select_next_pending_search_all_term(
3048 programOps, search, state.pendingAllMask, state.vars, nextAllLocalIdx, nextAllPc)) {
3049 const auto bindPc = (uint32_t)search.allBegin + nextAllLocalIdx;
3050 if (nextAllPc != bindPc) {
3051 state.termOpIdx = (uint8_t)nextAllLocalIdx;
3052 state.pc = (uint16_t)nextAllPc;
3057 uint32_t bestAllIdx = (uint32_t)-1;
3058 const auto allSel = select_best_pending_search_term(
3059 ctx, archetype, programOps, search.allBegin, search.allCount, state.pendingAllMask, state.vars,
3062 if (!backtrack(state, stack))
3068 state.termOpIdx = (uint8_t)bestAllIdx;
3069 state.pc = (uint16_t)bestAllIdx;
3073 uint32_t nextAllLocalIdx = (uint32_t)-1;
3074 uint32_t nextAllPc = (uint32_t)-1;
3075 if (select_next_pending_search_all_term(
3076 programOps, search, state.pendingAllMask, state.vars, nextAllLocalIdx, nextAllPc)) {
3077 state.termOpIdx = (uint8_t)nextAllLocalIdx;
3078 state.pc = (uint16_t)nextAllPc;
3086 case EOpcode::Var_Search_SelectOr: {
3087 if (!state.orMatched && search.anyCount == 0 && (uint8_t)(search.orVarMask & ~state.vars.mask) == 0) {
3088 state.bestOrIdx = 0xff;
3090 state.pc = search.maybeFinalizePc;
3094 if (state.orMatched && (uint8_t)(search.orVarMask & ~state.vars.mask) == 0) {
3095 state.bestOrIdx = 0xff;
3097 state.pc = search.beginAnyPc;
3101 uint32_t nextOrLocalIdx = (uint32_t)-1;
3102 uint32_t nextOrPc = (uint32_t)-1;
3103 if (select_next_pending_search_or_term(
3104 programOps, search, state.pendingOrMask, state.pendingFinalOrMask, state.vars, !state.orMatched,
3105 state.orMatched, nextOrLocalIdx, nextOrPc)) {
3106 state.bestOrIdx = (uint8_t)nextOrLocalIdx;
3108 state.termOpIdx = state.bestOrIdx;
3109 state.pc = (uint16_t)nextOrPc;
3113 state.bestOrIdx = 0xff;
3118 case EOpcode::Var_Search_SelectOtherOr: {
3119 if (state.orMatched && (uint8_t)(search.orVarMask & ~state.vars.mask) == 0) {
3121 state.pc = search.beginAnyPc;
3126 while (state.scanIdx < search.orCount) {
3127 const auto localIdx = (uint32_t)state.scanIdx++;
3128 if (localIdx == state.bestOrIdx)
3131 const auto bit = (uint16_t)(uint16_t(1) << localIdx);
3132 if ((state.pendingOrMask & bit) == 0)
3134 const auto bindPc = (uint32_t)search.orBegin + localIdx;
3135 if (!is_term_ready(programOps[bindPc], state.vars))
3138 const bool bindsNewVars =
3139 (uint8_t)(search_program_term_op(programOps[bindPc]).varMask & ~state.vars.mask) != 0;
3140 if (state.orMatched) {
3144 if (!bindsNewVars && (state.pendingFinalOrMask & bit) == 0)
3150 state.termOpIdx = (uint8_t)localIdx;
3151 state.pc = (uint16_t)((uint32_t)search.orCheckBegin + localIdx);
3162 case EOpcode::Var_Search_SelectOtherOrBind: {
3163 if (state.orMatched) {
3169 for (uint32_t localIdx = state.scanIdx; localIdx < search.orCount; ++localIdx) {
3170 if (localIdx == state.bestOrIdx)
3173 const auto bit = (uint16_t)(uint16_t(1) << localIdx);
3174 if ((state.pendingOrMask & bit) == 0)
3177 const auto bindPc = (uint32_t)search.orBegin + localIdx;
3178 if (!is_term_ready(programOps[bindPc], state.vars))
3181 const auto& termOp = search_program_term_op(programOps[bindPc]);
3182 const bool bindsNewVars = (uint8_t)(termOp.varMask & ~state.vars.mask) != 0;
3186 state.scanIdx = (uint8_t)(localIdx + 1u);
3187 state.termOpIdx = (uint8_t)localIdx;
3188 state.pc = (uint16_t)bindPc;
3197 case EOpcode::Var_Search_BeginAny:
3198 state.anyMatched =
false;
3200 state.currentAnyMatched =
false;
3201 state.pc = op.
pc_ok;
3203 case EOpcode::Var_Search_SelectAny: {
3204 uint32_t nextAnyLocalIdx = (uint32_t)-1;
3205 uint32_t nextAnyPc = (uint32_t)-1;
3206 const bool found = select_next_pending_search_any_term(
3207 programOps, search, state.pendingAnyMask, state.vars, nextAnyLocalIdx, nextAnyPc);
3209 state.termOpIdx = (uint8_t)nextAnyLocalIdx;
3210 state.currentAnyMatched =
false;
3211 state.pc = (uint16_t)nextAnyPc;
3219 case EOpcode::Var_Search_MaybeFinalize:
3220 if (!state.anyMatched &&
3221 can_skip_pending_search_all(programOps, search, state.pendingAllMask, state.vars))
3222 state.pc = op.
pc_ok;
3223 else if (op.
pc_fail != BacktrackPC)
3225 else if (!backtrack(state, stack))
3228 case EOpcode::Var_Term_All_Check:
3229 if (term_has_match(*ctx.
pWorld, archetype, search_program_term_op(op), state.vars))
3230 adv_after_search_term_success(state, op, state.vars);
3232 handle_search_term_exhausted(state, op);
3233 if (state.pc == BacktrackPC && !backtrack(state, stack))
3237 case EOpcode::Var_Term_All_Bind:
3238 case EOpcode::Var_Term_All_Src_Bind:
3239 if (!try_enter_search_term(state, stack)) {
3240 handle_search_term_exhausted(state, op);
3241 if (state.pc == BacktrackPC && !backtrack(state, stack))
3245 case EOpcode::Var_Term_Or_Check:
3246 case EOpcode::Var_Term_Or_Bind:
3247 case EOpcode::Var_Term_Any_Check:
3248 case EOpcode::Var_Term_Any_Bind: {
3249 const auto& termOp = search_program_term_op(op);
3250 const bool bindsNewVars = (uint8_t)(termOp.varMask & ~state.vars.mask) != 0;
3251 if (!bindsNewVars) {
3252 if (term_has_match(*ctx.
pWorld, archetype, termOp, state.vars))
3253 adv_after_search_term_success(state, op, state.vars);
3255 if (op.
opcode == EOpcode::Var_Term_Or_Check || op.
opcode == EOpcode::Var_Term_Or_Bind)
3256 state.pendingFinalOrMask =
3257 (uint16_t)(state.pendingFinalOrMask & ~(uint16_t(1) << state.termOpIdx));
3258 handle_search_term_exhausted(state, op);
3259 if (state.pc == BacktrackPC && !backtrack(state, stack))
3265 if (!try_enter_search_term(state, stack)) {
3266 handle_search_term_exhausted(state, op);
3267 if (state.pc == BacktrackPC && !backtrack(state, stack))
3272 case EOpcode::Var_Final_Not_Check:
3273 if (term_has_match(*ctx.
pWorld, archetype, search_program_term_op(op), state.vars)) {
3274 if (!backtrack(state, stack))
3277 state.pc = op.
pc_ok;
3279 case EOpcode::Var_Final_Require_Or:
3280 if (orAlreadySatisfied || state.orMatched || search.orCount == 0)
3281 state.pc = op.
pc_ok;
3282 else if (op.
pc_fail != BacktrackPC)
3284 else if (!backtrack(state, stack))
3287 case EOpcode::Var_Final_Or_Check:
3288 if ((state.pendingFinalOrMask & (uint16_t(1) << op.
arg)) == 0)
3290 else if (term_has_match(*ctx.
pWorld, archetype, search_program_term_op(op), state.vars))
3291 state.pc = op.
pc_ok;
3293 state.pendingFinalOrMask = (uint16_t)(state.pendingFinalOrMask & ~(uint16_t(1) << op.
arg));
3294 if (op.
pc_fail != BacktrackPC)
3296 else if (!backtrack(state, stack))
3300 case EOpcode::Var_Final_Success:
3310 void filter_variable_terms(
MatchingCtx& ctx, VarEvalFunc evalFunc)
const {
3311 if (!m_compCtx.has_variable_terms())
3314 const bool orAlreadySatisfied = !m_compCtx.
ids_or.empty() || ctx.
skipOr;
3316 constexpr uint32_t FilterChunkSize = 64;
3318 uint32_t writeIdx = 0;
3320 const auto flush_filtered = [&]() {
3321 for (
const auto* pFiltered: filtered) {
3327 GAIA_FOR(sourceCnt) {
3329 const bool matched = (this->*evalFunc)(ctx, *pArchetype, orAlreadySatisfied);
3333 filtered.push_back(pArchetype);
3334 if (filtered.size() != FilterChunkSize)
3340 if (!filtered.empty())
3346 const auto cnt = (detail::VmLabel)m_compCtx.ops.size();
3349 m_compCtx.ops.push_back(GAIA_MOV(op));
3354 const auto cnt = add_op(GAIA_MOV(op));
3355 m_compCtx.ops[cnt].pc_fail = (detail::VmLabel)-1;
3359 GAIA_NODISCARD
static uint8_t opcode_arg(uint32_t idx) {
3360 GAIA_ASSERT(idx < OpcodeArgLimit);
3361 return (uint8_t)idx;
3364 template <
typename SourceTermsArray>
3365 void emit_src_gate_terms(
const SourceTermsArray& terms, detail::EOpcode opcode) {
3366 const auto cnt = (uint32_t)terms.size();
3370 op.
arg = opcode_arg(i);
3371 (void)add_gate_op(GAIA_MOV(op));
3375 void emit_src_or_terms(
bool hasOrFallback) {
3378 const auto srcOrCnt = (uint32_t)m_compCtx.
terms_or_src.size();
3379 GAIA_FOR(srcOrCnt) {
3381 op.
opcode = detail::EOpcode::Src_OrTerm;
3382 op.
arg = opcode_arg(i);
3383 orSrcOpLabels.push_back(add_op(GAIA_MOV(op)));
3386 const auto orExitPc = (detail::VmLabel)m_compCtx.ops.size();
3387 for (
const auto opLabel: orSrcOpLabels)
3388 m_compCtx.ops[opLabel].pc_ok = orExitPc;
3390 const auto lastIdx = (uint32_t)orSrcOpLabels.size() - 1u;
3392 m_compCtx.ops[orSrcOpLabels[i]].pc_fail = orSrcOpLabels[i + 1];
3395 m_compCtx.ops[orSrcOpLabels[lastIdx]].pc_fail = hasOrFallback ? orExitPc : (detail::VmLabel)-1;
3398 GAIA_NODISCARD
static detail::EOpcode pick_all_opcode(
bool isSimple,
bool isAs) {
3400 return detail::EOpcode::All_Simple;
3402 return detail::EOpcode::All_Complex;
3403 return detail::EOpcode::All_Wildcard;
3406 GAIA_NODISCARD
static detail::EOpcode pick_or_opcode(
bool hasAllTerms,
bool isSimple,
bool isAs) {
3409 return detail::EOpcode::Or_NoAll_Simple;
3411 return detail::EOpcode::Or_NoAll_Complex;
3412 return detail::EOpcode::Or_NoAll_Wildcard;
3416 return detail::EOpcode::Or_WithAll_Simple;
3418 return detail::EOpcode::Or_WithAll_Complex;
3419 return detail::EOpcode::Or_WithAll_Wildcard;
3422 GAIA_NODISCARD
static detail::EOpcode pick_not_opcode(
bool isSimple,
bool isAs) {
3424 return detail::EOpcode::Not_Simple;
3426 return detail::EOpcode::Not_Complex;
3427 return detail::EOpcode::Not_Wildcard;
3432 GAIA_NODISCARD
bool op_all_simple(
MatchingCtx& ctx)
const {
3433 GAIA_PROF_SCOPE(vm::op_and_simple);
3434 return detail::exec_all_impl<MatchingStyle::Simple>(m_compCtx, ctx);
3437 GAIA_NODISCARD
bool op_all_wildcard(
MatchingCtx& ctx)
const {
3438 GAIA_PROF_SCOPE(vm::op_and_wildcard);
3439 return detail::exec_all_impl<MatchingStyle::Wildcard>(m_compCtx, ctx);
3442 GAIA_NODISCARD
bool op_all_complex(
MatchingCtx& ctx)
const {
3443 GAIA_PROF_SCOPE(vm::op_and_complex);
3444 return detail::exec_all_impl<MatchingStyle::Complex>(m_compCtx, ctx);
3447 GAIA_NODISCARD
bool op_or_noall_simple(
MatchingCtx& ctx)
const {
3448 GAIA_PROF_SCOPE(vm::op_or);
3449 return detail::exec_or_noall_impl<MatchingStyle::Simple>(m_compCtx, ctx);
3452 GAIA_NODISCARD
bool op_or_noall_wildcard(
MatchingCtx& ctx)
const {
3453 GAIA_PROF_SCOPE(vm::op_or);
3454 return detail::exec_or_noall_impl<MatchingStyle::Wildcard>(m_compCtx, ctx);
3457 GAIA_NODISCARD
bool op_or_noall_complex(
MatchingCtx& ctx)
const {
3458 GAIA_PROF_SCOPE(vm::op_or);
3459 return detail::exec_or_noall_impl<MatchingStyle::Complex>(m_compCtx, ctx);
3462 GAIA_NODISCARD
bool op_or_withall_simple(
MatchingCtx& ctx)
const {
3463 GAIA_PROF_SCOPE(vm::op_or);
3464 return detail::exec_or_withall_impl<MatchingStyle::Simple>(m_compCtx, ctx);
3467 GAIA_NODISCARD
bool op_or_withall_wildcard(
MatchingCtx& ctx)
const {
3468 GAIA_PROF_SCOPE(vm::op_or);
3469 return detail::exec_or_withall_impl<MatchingStyle::Wildcard>(m_compCtx, ctx);
3472 GAIA_NODISCARD
bool op_or_withall_complex(
MatchingCtx& ctx)
const {
3473 GAIA_PROF_SCOPE(vm::op_or);
3474 return detail::exec_or_withall_impl<MatchingStyle::Complex>(m_compCtx, ctx);
3477 GAIA_NODISCARD
bool op_not_simple(
MatchingCtx& ctx)
const {
3478 GAIA_PROF_SCOPE(vm::op_not);
3479 return detail::exec_not_impl<MatchingStyle::Simple>(m_compCtx, ctx);
3482 GAIA_NODISCARD
bool op_not_wildcard(
MatchingCtx& ctx)
const {
3483 GAIA_PROF_SCOPE(vm::op_not);
3484 return detail::exec_not_impl<MatchingStyle::Wildcard>(m_compCtx, ctx);
3487 GAIA_NODISCARD
bool op_not_complex(
MatchingCtx& ctx)
const {
3488 GAIA_PROF_SCOPE(vm::op_not);
3489 return detail::exec_not_impl<MatchingStyle::Complex>(m_compCtx, ctx);
3492 GAIA_NODISCARD
bool op_seed_all(
MatchingCtx& ctx)
const {
3493 GAIA_PROF_SCOPE(vm::op_seed_all);
3494 detail::add_all_archetypes(ctx);
3498 GAIA_NODISCARD
bool op_var_filter(
MatchingCtx& ctx)
const {
3499 GAIA_PROF_SCOPE(vm::op_var_filter);
3501 filter_variable_terms(ctx, &VirtualMachine::eval_variable_terms_program_on_archetype);
3505 GAIA_NODISCARD
bool op_src_all_term(
MatchingCtx& ctx)
const {
3506 GAIA_PROF_SCOPE(vm::op_src_all);
3507 const auto& termOp = detail::get_src_term_op(m_compCtx, ctx, m_compCtx.
terms_all_src);
3508 return detail::match_src_term(*ctx.
pWorld, termOp.term, termOp.opcode);
3511 GAIA_NODISCARD
bool op_src_not_term(
MatchingCtx& ctx)
const {
3512 GAIA_PROF_SCOPE(vm::op_src_not);
3513 const auto& termOp = detail::get_src_term_op(m_compCtx, ctx, m_compCtx.
terms_not_src);
3514 return !detail::match_src_term(*ctx.
pWorld, termOp.term, termOp.opcode);
3517 GAIA_NODISCARD
bool op_src_or_term(
MatchingCtx& ctx)
const {
3518 GAIA_PROF_SCOPE(vm::op_src_or);
3519 const auto& termOp = detail::get_src_term_op(m_compCtx, ctx, m_compCtx.
terms_or_src);
3520 const bool matched = detail::match_src_term(*ctx.
pWorld, termOp.term, termOp.opcode);
3525 if (m_compCtx.
ids_all.empty())
3526 detail::add_all_archetypes(ctx);
3530 static constexpr OpcodeFunc OpcodeFuncs[] = {
3531 &VirtualMachine::op_all_simple,
3532 &VirtualMachine::op_all_wildcard,
3533 &VirtualMachine::op_all_complex,
3534 &VirtualMachine::op_or_noall_simple,
3535 &VirtualMachine::op_or_noall_wildcard,
3536 &VirtualMachine::op_or_noall_complex,
3537 &VirtualMachine::op_or_withall_simple,
3538 &VirtualMachine::op_or_withall_wildcard,
3539 &VirtualMachine::op_or_withall_complex,
3540 &VirtualMachine::op_not_simple,
3541 &VirtualMachine::op_not_wildcard,
3542 &VirtualMachine::op_not_complex,
3543 &VirtualMachine::op_seed_all,
3544 &VirtualMachine::op_var_filter,
3545 &VirtualMachine::op_src_all_term,
3546 &VirtualMachine::op_src_not_term,
3547 &VirtualMachine::op_src_or_term
3550 sizeof(OpcodeFuncs) /
sizeof(OpcodeFuncs[0]) == (uint32_t)detail::EOpcode::Src_Never,
3551 "OpcodeFuncs must contain all executable opcodes.");
3554 const auto opcodeIdx = (uint32_t)stackItem.
opcode;
3555 GAIA_ASSERT(opcodeIdx < (uint32_t)detail::EOpcode::Src_Never);
3556 return (this->*OpcodeFuncs[opcodeIdx])(ctx);
3564 out.
append(
"main_ops: ");
3565 add_uint(out, (uint32_t)m_compCtx.mainOpsCount);
3568 const auto opsCnt = (uint32_t)m_compCtx.mainOpsCount;
3570 const auto& op = m_compCtx.ops[i];
3574 add_cstr(out, opcode_name(op.
opcode));
3575 if (opcode_has_arg(op.
opcode)) {
3577 add_uint(out, op.
arg);
3580 add_uint(out, op.
pc_ok);
3593 add_src_terms_section(out,
"src_all", m_compCtx.
terms_all_src, world);
3594 add_src_terms_section(out,
"src_or", m_compCtx.
terms_or_src, world);
3595 add_src_terms_section(out,
"src_not", m_compCtx.
terms_not_src, world);
3597 add_var_terms_section(out,
"var_all", m_compCtx.
terms_all_var, world);
3598 add_var_terms_section(out,
"var_or", m_compCtx.
terms_or_var, world);
3599 add_var_terms_section(out,
"var_not", m_compCtx.
terms_not_var, world);
3600 add_var_terms_section(out,
"var_any", m_compCtx.
terms_any_var, world);
3601 add_var_program_exec_section(out, m_compCtx);
3602 add_var_program_sections(out, m_compCtx, world);
3611 GAIA_PROF_SCOPE(vm::compile);
3612 (void)entityToArchetypeMap;
3613 (void)allArchetypes;
3616 m_compCtx.
ids_or.clear();
3626 m_compCtx.varMaskOr = 0;
3627 m_compCtx.varMaskNot = 0;
3628 m_compCtx.varMaskAny = 0;
3630 m_compCtx.mainOpsCount = 0;
3631 m_compCtx.ops.clear();
3633 auto& data = queryCtx.data;
3634 GAIA_ASSERT(queryCtx.w !=
nullptr);
3635 const auto& world = *queryCtx.w;
3636 const bool hasEntityFilterTerms = data.deps.has_dep_flag(QueryCtx::DependencyHasEntityFilterTerms);
3637 auto isAdjunctDirectTerm = [&](
const QueryTerm& term) {
3638 if (term.
src != EntityBad || term.
entTrav != EntityBad || term_has_variables(term))
3641 const auto id = term.
id;
3642 return (
id.
pair() && world_is_exclusive_dont_fragment_relation(world, entity_from_id(world,
id.
id()))) ||
3643 (!
id.
pair() && world_is_non_fragmenting_out_of_line_component(world,
id));
3646 QueryTermSpan terms = data.terms_view_mut();
3647 QueryTermSpan terms_all = terms.subspan(0, data.firstOr);
3648 QueryTermSpan terms_or = terms.subspan(data.firstOr, data.firstNot - data.firstOr);
3649 QueryTermSpan terms_not = terms.subspan(data.firstNot, data.firstAny - data.firstNot);
3650 QueryTermSpan terms_any = terms.subspan(data.firstAny);
3653 if (!terms_all.empty()) {
3654 GAIA_PROF_SCOPE(vm::compile_all);
3656 const auto cnt = terms_all.size();
3658 auto& p = terms_all[i];
3659 if (isAdjunctDirectTerm(p))
3661 if (term_has_variables(p)) {
3663 m_compCtx.
terms_all_var.push_back({detail::src_opcode_from_term(p), p, varMask});
3668 if (p.src == EntityBad) {
3669 m_compCtx.
ids_all.push_back(p.id);
3672 m_compCtx.
terms_all_src.push_back({detail::src_opcode_from_term(p), p});
3677 if (!terms_or.empty()) {
3678 GAIA_PROF_SCOPE(vm::compile_or);
3680 const auto cnt = terms_or.size();
3682 auto& p = terms_or[i];
3683 if (p.src == EntityBad && hasEntityFilterTerms)
3685 if (term_has_variables(p)) {
3687 m_compCtx.
terms_or_var.push_back({detail::src_opcode_from_term(p), p, varMask});
3688 m_compCtx.varMaskOr |= varMask;
3692 if (p.src == EntityBad)
3693 m_compCtx.
ids_or.push_back(p.id);
3695 m_compCtx.
terms_or_src.push_back({detail::src_opcode_from_term(p), p});
3700 if (!terms_not.empty()) {
3701 GAIA_PROF_SCOPE(vm::compile_not);
3703 const auto cnt = terms_not.size();
3705 auto& p = terms_not[i];
3706 if (isAdjunctDirectTerm(p))
3708 if (term_has_variables(p)) {
3710 m_compCtx.
terms_not_var.push_back({detail::src_opcode_from_term(p), p, varMask});
3711 m_compCtx.varMaskNot |= varMask;
3715 if (p.src == EntityBad)
3716 m_compCtx.
ids_not.push_back(p.id);
3718 m_compCtx.
terms_not_src.push_back({detail::src_opcode_from_term(p), p});
3723 if (!terms_any.empty()) {
3724 GAIA_PROF_SCOPE(vm::compile_any);
3726 const auto cnt = terms_any.size();
3728 auto& p = terms_any[i];
3729 if (!term_has_variables(p))
3732 m_compCtx.
terms_any_var.push_back({detail::src_opcode_from_term(p), p, varMask});
3733 m_compCtx.varMaskAny |= varMask;
3738 detail::sort_src_terms_by_cost(m_compCtx.
terms_or_src);
3741 constexpr uint32_t VarSearchProgramOpCapacity = MAX_ITEMS_IN_QUERY * 3u + 8u;
3745 auto init_var_search_program = [&]() {
3746 varSearchProgramOps.clear();
3757 const auto allVarCnt = (uint32_t)m_compCtx.
terms_all_var.size();
3758 GAIA_FOR(allVarCnt) {
3759 const auto cost = detail::search_term_cost(m_compCtx.
terms_all_var[i]);
3760 const auto srcVarBit =
3762 ? (uint8_t)(uint8_t(1) << detail::var_index(m_compCtx.
terms_all_var[i].term.src))
3764 const auto canBindFromSelfSource =
3765 m_compCtx.
terms_all_var[i].sourceOpcode == detail::EOpcode::Src_Self &&
3766 detail::is_var_entity(m_compCtx.
terms_all_var[i].term.src) &&
3768 (uint8_t)(m_compCtx.
terms_all_var[i].varMask & m_compCtx.varMaskAny) == 0;
3769 const auto canBindFromUpSource =
3770 m_compCtx.
terms_all_var[i].sourceOpcode == detail::EOpcode::Src_Up &&
3771 detail::is_var_entity(m_compCtx.
terms_all_var[i].term.src) &&
3773 (uint8_t)(m_compCtx.
terms_all_var[i].varMask & m_compCtx.varMaskAny) == 0;
3774 const auto canBindFromDownSource =
3775 m_compCtx.
terms_all_var[i].sourceOpcode == detail::EOpcode::Src_Down &&
3776 detail::is_var_entity(m_compCtx.
terms_all_var[i].term.src) &&
3778 (uint8_t)(m_compCtx.
terms_all_var[i].varMask & m_compCtx.varMaskAny) == 0;
3779 const auto canBindFromUpDownSource =
3780 m_compCtx.
terms_all_var[i].sourceOpcode == detail::EOpcode::Src_UpDown &&
3781 detail::is_var_entity(m_compCtx.
terms_all_var[i].term.src) &&
3783 (uint8_t)(m_compCtx.
terms_all_var[i].varMask & m_compCtx.varMaskAny) == 0;
3785 canBindFromSelfSource || canBindFromUpSource || canBindFromDownSource || canBindFromUpDownSource
3786 ? detail::EOpcode::Var_Term_All_Src_Bind
3787 : detail::EOpcode::Var_Term_All_Bind;
3788 searchAllBindOps.push_back({opcode, 0, 0, (uint8_t)i, cost});
3789 searchAllCheckOps.push_back({detail::EOpcode::Var_Term_All_Check, 0, 0, (uint8_t)i, cost});
3791 detail::sort_program_ops_by_cost(searchAllBindOps);
3792 detail::sort_program_ops_by_cost(searchAllCheckOps);
3794 const auto orVarCnt = (uint32_t)m_compCtx.
terms_or_var.size();
3795 GAIA_FOR(orVarCnt) {
3796 const auto cost = detail::search_term_cost(m_compCtx.
terms_or_var[i]);
3797 searchOrBindOps.push_back({detail::EOpcode::Var_Term_Or_Bind, 0, 0, (uint8_t)i, cost});
3798 searchOrCheckOps.push_back({detail::EOpcode::Var_Term_Or_Check, 0, 0, (uint8_t)i, cost});
3799 finalOrCheckOps.push_back({detail::EOpcode::Var_Final_Or_Check, 0, 0, (uint8_t)i, cost});
3800 varSearchMeta.orVarMask = (uint8_t)(varSearchMeta.orVarMask | m_compCtx.
terms_or_var[i].varMask);
3802 detail::sort_program_ops_by_cost(searchOrBindOps);
3803 detail::sort_program_ops_by_cost(searchOrCheckOps);
3804 detail::sort_program_ops_by_cost(finalOrCheckOps);
3806 const auto anyVarCnt = (uint32_t)m_compCtx.
terms_any_var.size();
3807 GAIA_FOR(anyVarCnt) {
3808 const auto cost = detail::search_term_cost(m_compCtx.
terms_any_var[i]);
3809 searchAnyBindOps.push_back({detail::EOpcode::Var_Term_Any_Bind, 0, 0, (uint8_t)i, cost});
3810 searchAnyCheckOps.push_back({detail::EOpcode::Var_Term_Any_Check, 0, 0, (uint8_t)i, cost});
3812 detail::sort_program_ops_by_cost(searchAnyBindOps);
3813 detail::sort_program_ops_by_cost(searchAnyCheckOps);
3815 const auto notVarCnt = (uint32_t)m_compCtx.
terms_not_var.size();
3816 GAIA_FOR(notVarCnt) {
3817 finalNotOps.push_back(
3818 {detail::EOpcode::Var_Final_Not_Check, 0, 0, (uint8_t)i,
3821 detail::sort_program_ops_by_cost(finalNotOps);
3823 for (
const auto& op: searchAllBindOps)
3824 varSearchProgramOps.push_back(op);
3825 for (
const auto& op: searchOrBindOps)
3826 varSearchProgramOps.push_back(op);
3827 for (
const auto& op: searchAnyBindOps)
3828 varSearchProgramOps.push_back(op);
3830 varSearchMeta.allBegin = 0;
3831 varSearchMeta.allCount = (uint16_t)searchAllBindOps.size();
3832 varSearchMeta.orBegin = varSearchMeta.allCount;
3833 varSearchMeta.orCount = (uint16_t)searchOrBindOps.size();
3834 varSearchMeta.anyBegin = (uint16_t)(varSearchMeta.orBegin + varSearchMeta.orCount);
3835 varSearchMeta.anyCount = (uint16_t)searchAnyBindOps.size();
3836 varSearchMeta.notBegin = 0;
3837 varSearchMeta.notCount = 0;
3839 const auto termOpsCnt = (uint16_t)varSearchProgramOps.size();
3840 const auto selectAllPc = termOpsCnt;
3841 const auto selectOrPc = (uint16_t)(termOpsCnt + 1u);
3842 const auto selectOtherOrPc = (uint16_t)(termOpsCnt + 2u);
3843 const auto selectOtherOrBindPc = (uint16_t)(termOpsCnt + 3u);
3844 const auto beginAnyPc = (uint16_t)(termOpsCnt + 4u);
3845 const auto selectAnyPc = (uint16_t)(termOpsCnt + 5u);
3846 const auto maybeFinalizePc = (uint16_t)(termOpsCnt + 6u);
3847 const auto allCheckBegin = (uint16_t)(termOpsCnt + 7u);
3848 const auto orCheckBegin = (uint16_t)(allCheckBegin + searchAllCheckOps.size());
3849 const auto anyCheckBegin = (uint16_t)(orCheckBegin + searchOrCheckOps.size());
3850 const auto finalNotBegin = (uint16_t)(anyCheckBegin + searchAnyCheckOps.size());
3851 const auto finalRequireOrPc = (uint16_t)(finalNotBegin + finalNotOps.size());
3852 const auto finalOrCheckBegin = (uint16_t)(finalRequireOrPc + 1u);
3853 const auto finalSuccessPc = (uint16_t)(finalOrCheckBegin + finalOrCheckOps.size());
3854 const auto finalBegin = !finalNotOps.empty() ? finalNotBegin : finalRequireOrPc;
3855 const auto backtrackPc = (detail::VmLabel)-1;
3857 for (
auto& op: varSearchProgramOps) {
3859 case detail::EOpcode::Var_Term_All_Bind:
3860 case detail::EOpcode::Var_Term_All_Src_Bind:
3861 op.
pc_ok = selectAllPc;
3864 case detail::EOpcode::Var_Term_Or_Bind:
3865 op.
pc_ok = selectAllPc;
3866 op.
pc_fail = selectOtherOrBindPc;
3868 case detail::EOpcode::Var_Term_Any_Bind:
3869 op.
pc_ok = selectAllPc;
3877 varSearchProgramOps.push_back({detail::EOpcode::Var_Search_SelectAll, selectAllPc, selectOrPc, 0, 0});
3878 varSearchProgramOps.push_back({detail::EOpcode::Var_Search_SelectOr, selectOrPc, selectOtherOrPc, 0, 0});
3879 varSearchProgramOps.push_back(
3880 {detail::EOpcode::Var_Search_SelectOtherOr, selectOtherOrPc, selectOtherOrBindPc, 0, 0});
3881 varSearchProgramOps.push_back(
3882 {detail::EOpcode::Var_Search_SelectOtherOrBind, selectOtherOrBindPc, beginAnyPc, 0, 0});
3883 varSearchProgramOps.push_back({detail::EOpcode::Var_Search_BeginAny, selectAnyPc, backtrackPc, 0, 0});
3884 varSearchProgramOps.push_back({detail::EOpcode::Var_Search_SelectAny, selectAnyPc, maybeFinalizePc, 0, 0});
3885 varSearchProgramOps.push_back({detail::EOpcode::Var_Search_MaybeFinalize, finalBegin, backtrackPc, 0, 0});
3886 for (
auto op: searchAllCheckOps) {
3887 op.
pc_ok = selectAllPc;
3889 varSearchProgramOps.push_back(op);
3891 for (
auto op: searchOrCheckOps) {
3892 op.
pc_ok = selectAllPc;
3893 op.
pc_fail = selectOtherOrBindPc;
3894 varSearchProgramOps.push_back(op);
3896 for (
auto op: searchAnyCheckOps) {
3897 op.
pc_ok = selectAllPc;
3899 varSearchProgramOps.push_back(op);
3901 for (uint32_t i = 0; i < finalNotOps.size(); ++i) {
3902 auto op = finalNotOps[i];
3903 op.
pc_ok = (i + 1u < finalNotOps.size()) ? (uint16_t)(finalNotBegin + i + 1u) : finalRequireOrPc;
3905 varSearchProgramOps.push_back(op);
3907 varSearchProgramOps.push_back(
3908 {detail::EOpcode::Var_Final_Require_Or, finalSuccessPc,
3909 searchOrCheckOps.empty() ? backtrackPc : finalOrCheckBegin, 0, 0});
3910 for (uint32_t i = 0; i < finalOrCheckOps.size(); ++i) {
3911 auto op = finalOrCheckOps[i];
3912 op.
pc_ok = finalSuccessPc;
3913 op.
pc_fail = (i + 1u < finalOrCheckOps.size()) ? (uint16_t)(finalOrCheckBegin + i + 1u) : backtrackPc;
3914 varSearchProgramOps.push_back(op);
3916 varSearchProgramOps.push_back({detail::EOpcode::Var_Final_Success, finalSuccessPc, backtrackPc, 0, 0});
3918 varSearchMeta.selectAllPc = selectAllPc;
3919 varSearchMeta.selectOrPc = selectOrPc;
3920 varSearchMeta.selectOtherOrPc = selectOtherOrPc;
3921 varSearchMeta.selectOtherOrBindPc = selectOtherOrBindPc;
3922 varSearchMeta.beginAnyPc = beginAnyPc;
3923 varSearchMeta.selectAnyPc = selectAnyPc;
3924 varSearchMeta.maybeFinalizePc = maybeFinalizePc;
3925 varSearchMeta.allCheckBegin = allCheckBegin;
3926 varSearchMeta.orCheckBegin = orCheckBegin;
3927 varSearchMeta.anyCheckBegin = anyCheckBegin;
3928 varSearchMeta.notBegin = finalNotBegin;
3929 varSearchMeta.notCount = (uint16_t)finalNotOps.size();
3931 auto init_mask = [](uint16_t begin, uint16_t count) {
3933 for (uint16_t i = 0; i < count; ++i)
3934 mask = (uint16_t)(mask | (uint16_t(1) << (begin + i)));
3938 varSearchMeta.initialAllMask = init_mask(varSearchMeta.allBegin, varSearchMeta.allCount);
3939 varSearchMeta.initialOrMask = init_mask(0, varSearchMeta.orCount);
3940 varSearchMeta.initialAnyMask = init_mask(0, varSearchMeta.anyCount);
3943 create_opcodes(queryCtx);
3945 init_var_search_program();
3953 GAIA_ASSERT(m_compCtx.ops.size() <= UINT16_MAX);
3954 program.begin = (uint16_t)m_compCtx.ops.size();
3955 program.count = (uint16_t)ops.size();
3956 for (
const auto& op: ops)
3957 m_compCtx.ops.push_back(op);
3962 if (m_compCtx.has_variable_terms()) {
3963 const auto program = emit_flat_program(
3965 if (!program.empty())
3966 m_compCtx.
var_programs.push_back({program, varSearchMeta});
3970 void create_opcodes(
QueryCtx& queryCtx) {
3971 const bool isSimple = (queryCtx.data.
flags & QueryCtx::QueryFlags::Complex) == 0U;
3974 uint16_t preservedProgramBase = 0;
3976 preservedProgramOffsets.clear();
3979 preservedProgramBase = m_compCtx.
var_programs[0].program.begin;
3980 GAIA_ASSERT(preservedProgramBase <= m_compCtx.ops.size());
3981 const auto preservedCnt = (uint32_t)m_compCtx.ops.size() - (uint32_t)preservedProgramBase;
3982 preservedVarOps.reserve(preservedCnt);
3983 for (uint32_t i = 0; i < preservedCnt; ++i)
3984 preservedVarOps.push_back(m_compCtx.ops[(uint32_t)preservedProgramBase + i]);
3986 for (
const auto& step: m_compCtx.var_programs) {
3987 GAIA_ASSERT(step.program.begin >= preservedProgramBase);
3988 preservedProgramOffsets.push_back((uint16_t)(step.program.begin - preservedProgramBase));
3992 m_compCtx.ops.clear();
3996 emit_src_gate_terms(m_compCtx.
terms_all_src, detail::EOpcode::Src_AllTerm);
4000 emit_src_gate_terms(m_compCtx.
terms_not_src, detail::EOpcode::Src_NotTerm);
4004 const bool hasOrFallback = !m_compCtx.
ids_or.empty() || !m_compCtx.
terms_or_var.empty();
4005 emit_src_or_terms(hasOrFallback);
4009 if (!m_compCtx.has_id_terms() &&
4010 (m_compCtx.has_src_terms() || m_compCtx.has_variable_terms() ||
4011 queryCtx.data.
deps.has_dep_flag(QueryCtx::DependencyHasEntityFilterTerms))) {
4012 detail::CompiledOp op{};
4013 op.
opcode = detail::EOpcode::Seed_All;
4014 (void)add_op(GAIA_MOV(op));
4018 if (!m_compCtx.
ids_all.empty()) {
4019 detail::CompiledOp op{};
4020 op.
opcode = pick_all_opcode(isSimple, isAs);
4021 (void)add_gate_op(GAIA_MOV(op));
4025 if (!m_compCtx.
ids_or.empty()) {
4026 detail::CompiledOp op{};
4027 op.
opcode = pick_or_opcode(!m_compCtx.
ids_all.empty(), isSimple, isAs);
4028 (void)add_op(GAIA_MOV(op));
4032 if (!m_compCtx.
ids_not.empty()) {
4033 detail::CompiledOp op{};
4034 op.
opcode = pick_not_opcode(isSimple, isAs);
4035 (void)add_op(GAIA_MOV(op));
4039 if (m_compCtx.has_variable_terms()) {
4040 detail::CompiledOp op{};
4041 op.
opcode = detail::EOpcode::Var_Filter;
4042 (void)add_gate_op(GAIA_MOV(op));
4045 m_compCtx.mainOpsCount = (uint16_t)m_compCtx.ops.size();
4047 if (!preservedVarOps.empty()) {
4048 const auto newProgramBase = (uint16_t)m_compCtx.ops.size();
4049 for (
const auto& op: preservedVarOps)
4050 m_compCtx.ops.push_back(op);
4052 GAIA_ASSERT(preservedProgramOffsets.size() == m_compCtx.
var_programs.size());
4053 const auto programCnt = (uint32_t)m_compCtx.
var_programs.size();
4054 GAIA_FOR(programCnt)
4055 m_compCtx.
var_programs[i].program.begin = (uint16_t)(newProgramBase + preservedProgramOffsets[i]);
4059 queryCtx.data.
flags &= ~QueryCtx::QueryFlags::Recompile;
4062 GAIA_NODISCARD
bool is_compiled()
const {
4063 return !m_compCtx.ops.empty();
4066 GAIA_NODISCARD uint32_t op_count()
const {
4067 return (uint32_t)m_compCtx.ops.size();
4070 GAIA_NODISCARD uint64_t op_signature()
const {
4071 uint64_t hash = 1469598103934665603ull;
4072 for (
const auto& op: m_compCtx.ops) {
4073 const uint64_t packed =
4074 (uint64_t)(uint8_t)op.
opcode |
4075 ((uint64_t)op.
pc_ok << 8u) |
4076 ((uint64_t)op.
pc_fail << 24u) |
4077 ((uint64_t)op.
arg << 40u) |
4078 ((uint64_t)op.
cost << 48u);
4080 hash *= 1099511628211ull;
4087 GAIA_PROF_SCOPE(vm::exec);
4089 if (m_compCtx.mainOpsCount == 0)
4096 auto& stackItem = m_compCtx.ops[ctx.
pc];
4097 GAIA_ASSERT((uint32_t)stackItem.
opcode < (uint32_t)detail::EOpcode::Src_Never);
4098 const bool ret = exec_opcode(stackItem, ctx);
4100 }
while (ctx.
pc < m_compCtx.mainOpsCount);
Array of elements of type.
Definition darray_ext_impl.h:28
Array with variable size of elements of type.
Definition darray_impl.h:25
Array of elements of type.
Definition sarray_ext_impl.h:27
Array of elements of type.
Definition sarray_impl.h:26
Definition span_impl.h:99
Definition archetype.h:83
Wrapper for two Entities forming a relationship pair.
Definition id.h:500
Wrapper for two types forming a relationship pair. Depending on what types are used to form a pair it...
Definition id.h:218
void exec(MatchingCtx &ctx)
Executes compiled opcodes.
Definition vm.h:4086
void compile(const EntityToArchetypeMap &entityToArchetypeMap, std::span< const Archetype * > allArchetypes, QueryCtx &queryCtx)
Transforms inputs into virtual machine opcodes.
Definition vm.h:3608
Definition robin_hood.h:720
Checks if endianess was detected correctly at compile-time.
Definition bitset.h:9
Definition query_match_stamps.h:13
Hashmap lookup structure used for Entity.
Definition id.h:439
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:639
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:642
Dependencies deps
Explicit dependency metadata derived from query shape.
Definition query_common.h:663
uint8_t flags
Query flags.
Definition query_common.h:655
Definition query_common.h:471
Internal representation of QueryInput.
Definition query_common.h:363
Entity id
Queried id.
Definition query_common.h:365
uint8_t travDepth
Maximum number of traversal steps.
Definition query_common.h:373
Entity entTrav
Optional traversal relation for source lookups.
Definition query_common.h:369
Entity src
Source of where the queried id is looked up at.
Definition query_common.h:367
QueryArchetypeCacheIndexMap * pLastMatchedArchetypeIdx_Not
Idx of the last matched archetype against the NOT opcode.
Definition vm.h:78
EntitySpan idsToMatch
List of entity ids in a query to consider.
Definition vm.h:102
Entity ent
Entity to match.
Definition vm.h:100
cnt::sarray< Entity, MaxVarCnt > varBindings
Runtime variable bindings (Var0..Var7) provided by the query.
Definition vm.h:90
ArchetypeMatchStamps * pMatchesStampByArchetypeId
Per-archetype stamp table for O(1) dedup in hot loops.
Definition vm.h:70
std::span< const Archetype * > allArchetypes
Array of all archetypes in the world.
Definition vm.h:66
uint32_t matchesVersion
Current dedup version used with pMatchesStampByArchetypeId.
Definition vm.h:72
uint8_t flags
Flags copied over from QueryCtx::Data.
Definition vm.h:88
QueryArchetypeCacheIndexMap * pLastMatchedArchetypeIdx_All
Idx of the last matched archetype against the ALL opcode.
Definition vm.h:74
bool skipOr
OR group was already satisfied by source terms.
Definition vm.h:94
uint32_t pc
Current stack position (program counter)
Definition vm.h:104
EntitySpan targetEntities
Entities for which we run the VM. If empty, we run against all of them.
Definition vm.h:62
QueryMask queryMask
Mask for speeding up simple query matching.
Definition vm.h:80
cnt::darr< const Archetype * > * pMatchesArr
Array of already matches archetypes. Reset before each exec().
Definition vm.h:68
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:83
const World * pWorld
World.
Definition vm.h:60
QueryArchetypeCacheIndexMap * pLastMatchedArchetypeIdx_Or
Idx of the last matched archetype against the OR opcode.
Definition vm.h:76
ArchetypeLookupView archetypeLookup
entity -> archetypes lookup used to seed structural candidate archetypes
Definition vm.h:64
uint8_t varBindingMask
Bitmask of bindings set in varBindings.
Definition vm.h:92
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:86
VmLabel pc_ok
Stack position to go to if the opcode returns true.
Definition vm.h:225
VmLabel pc_fail
Stack position to go to if the opcode returns false.
Definition vm.h:227
EOpcode opcode
Opcode to execute.
Definition vm.h:223
uint8_t cost
Optional planner-side cost used for sorting compiled micro-op plans.
Definition vm.h:231
uint8_t arg
Optional opcode argument (e.g. index to a source term array).
Definition vm.h:229
cnt::sarray_ext< SourceTermOp, MAX_ITEMS_IN_QUERY > terms_not_src
Source lookup terms for NOT.
Definition vm.h:303
cnt::sarray_ext< Entity, MAX_ITEMS_IN_QUERY > ids_or
Array of ops that can be evaluated with a OR opcode.
Definition vm.h:295
cnt::sarray_ext< VarTermOp, MAX_ITEMS_IN_QUERY > terms_any_var
Variable terms for ANY.
Definition vm.h:311
uint8_t varMaskAll
Variable masks (Var0..Var7) used by variable terms.
Definition vm.h:315
cnt::sarray_ext< VarTermOp, MAX_ITEMS_IN_QUERY > terms_not_var
Variable terms for NOT.
Definition vm.h:309
cnt::sarray_ext< VarProgramStep, MaxVarCnt > var_programs
Variable programs.
Definition vm.h:313
cnt::sarray_ext< SourceTermOp, MAX_ITEMS_IN_QUERY > terms_all_src
Source lookup terms for ALL.
Definition vm.h:299
cnt::sarray_ext< Entity, MAX_ITEMS_IN_QUERY > ids_all
Array of ops that can be evaluated with a ALL opcode.
Definition vm.h:293
cnt::sarray_ext< VarTermOp, MAX_ITEMS_IN_QUERY > terms_or_var
Variable terms for OR.
Definition vm.h:307
cnt::sarray_ext< Entity, MAX_ITEMS_IN_QUERY > ids_not
Array of ops that can be evaluated with a NOT opcode.
Definition vm.h:297
cnt::sarray_ext< SourceTermOp, MAX_ITEMS_IN_QUERY > terms_or_src
Source lookup terms for OR.
Definition vm.h:301
cnt::sarray_ext< VarTermOp, MAX_ITEMS_IN_QUERY > terms_all_var
Variable terms for ALL.
Definition vm.h:305
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