public static class QueryManager {
// I used a SortedSet here so you can get O(log n) lookups
// and insertion, since your bit sets can be ordered
// lexicographically for a binary search via Query.CompareTo()
static SortedSet<Query> _queryCache = new();
static List<Archetype> _archetypes = new();
static Query _tempQuery;
// When a new query is registered, cache all matching
// archetypes known at that time and return the cached query.
// Cached queries are shared/de-duplicated between multiple requests.
public static Query RegisterQuery(ComponentSignature components) {
_tempQuery.signature = components;
if (!_queryCache.TryGetValue(_tempQuery, out Query cached)) {
_tempQuery.matches = new List<Archetype>();
foreach(var archetype in _archetypes) {
if (_tempQuery.Matches(archetype)) {
_tempQuery.matches.Add(archetype);
}
}
cached = _tempQuery;
_queryCache.Add(cached);
// Set aside a fresh temporary for the next attempt,
// since our old one got promoted to a permanent cached version.
_tempQuery = new Query();
}
return cached;
}
// When a new archetype is registered, add it to all
// cached queries that match it.
public static void RegisterArchetype(Archetype archetype) {
_archetypes.Add(archetype);
foreach(var query in _queryCache) {
if (query.Matches(archetype)) {
query.matches.Add(archetype);
}
}
}
}
(And the archetypes could be similar to the ones I sketch in this answer)