using System.Runtime.CompilerServices; namespace OECS; /// /// Provides zero-allocation ref struct iterators and entity-finding /// extension methods for querying a . /// public static class WorldQueryExtensions { // ── Select (ref struct enumerators) ────────────────────────────── public static Select1 Select(this World world, Query query = default) where T1 : struct { return new Select1(world, query); } public static Select2 Select(this World world, Query query = default) where T1 : struct where T2 : struct { return new Select2(world, query); } public static Select3 Select(this World world, Query query = default) where T1 : struct where T2 : struct where T3 : struct { return new Select3(world, query); } public static Select4 Select(this World world, Query query = default) where T1 : struct where T2 : struct where T3 : struct where T4 : struct { return new Select4(world, query); } public static Select5 Select(this World world, Query query = default) where T1 : struct where T2 : struct where T3 : struct where T4 : struct where T5 : struct { return new Select5(world, query); } public static Select6 Select(this World world, Query query = default) where T1 : struct where T2 : struct where T3 : struct where T4 : struct where T5 : struct where T6 : struct { return new Select6(world, query); } // ── FindEntity ────────────────────────────────────────────────── public static Entity FindEntity(this World world, Query query = default) where T1 : struct { using var iter = new Select1(world, query); return iter.MoveNext() ? iter.Entity : Entity.Null; } public static Entity FindEntity(this World world, Query query = default) where T1 : struct where T2 : struct { using var iter = new Select2(world, query); return iter.MoveNext() ? iter.Entity : Entity.Null; } public static Entity FindEntity(this World world, Query query = default) where T1 : struct where T2 : struct where T3 : struct { using var iter = new Select3(world, query); return iter.MoveNext() ? iter.Entity : Entity.Null; } } // ── Ref struct enumerators ─────────────────────────────────────────── /// Ref struct iterator for queries with one component type. public ref struct Select1 where T1 : struct { private readonly World _world; private readonly SparseSet? _set; private readonly ComponentStore _store; private readonly HashSet? _without; private int _index; private readonly int _count; internal Select1(World world, Query query) { _world = world; _store = world.Components; _without = query.WithoutTypes; _set = _store.GetSet(typeof(T1)) as SparseSet; _count = _set?.Count ?? 0; _index = -1; _world.BeginBatching(); } /// The current entity handle. public Entity Entity { get; private set; } /// The current enumerator (required for foreach duck-typing). public Select1 Current => this; /// /// Mutable reference to the current entity's component of type . /// Access is tracked for auto-dirty-marking during a batching scope. /// public ref T1 Ref1 { [MethodImpl(MethodImplOptions.AggressiveInlining)] get { _world.TrackAccess(Entity, typeof(T1)); return ref _set!.Get(Entity); } } /// /// Read-only reference to the current entity's component of type . /// Access is NOT tracked for auto-dirty-marking. /// public ref readonly T1 Val1 => ref _set!.Get(Entity); [MethodImpl(MethodImplOptions.AggressiveInlining)] public bool MoveNext() { if (_set == null) return false; while (++_index < _count) { Entity = _set.DenseEntities[_index]; if (!PassesWithout()) continue; if (_world.IsSingletonEntity(Entity)) continue; return true; } return false; } public Select1 GetEnumerator() => this; public void Dispose() { _world.EndBatching(); } private bool PassesWithout() { if (_without == null) return true; foreach (var type in _without) { var set = _store.GetSet(type); if (set != null && set.Contains(Entity)) return false; } return true; } } /// Ref struct iterator for queries with two component types. Drives from the smaller set. public ref struct Select2 where T1 : struct where T2 : struct { private readonly World _world; private readonly SparseSet? _set1; private readonly SparseSet? _set2; private readonly ComponentStore _store; private readonly HashSet? _without; private int _index; private readonly int _count; private readonly bool _swapped; internal Select2(World world, Query query) { _world = world; _store = world.Components; _without = query.WithoutTypes; _set1 = _store.GetSet(typeof(T1)) as SparseSet; _set2 = _store.GetSet(typeof(T2)) as SparseSet; int c1 = _set1?.Count ?? 0; int c2 = _set2?.Count ?? 0; _swapped = c2 < c1; _count = _swapped ? c2 : c1; _index = -1; _world.BeginBatching(); } public Entity Entity { get; private set; } public Select2 Current => this; public ref T1 Ref1 { [MethodImpl(MethodImplOptions.AggressiveInlining)] get { _world.TrackAccess(Entity, typeof(T1)); return ref _set1!.Get(Entity); } } public ref T2 Ref2 { [MethodImpl(MethodImplOptions.AggressiveInlining)] get { _world.TrackAccess(Entity, typeof(T2)); return ref _set2!.Get(Entity); } } public ref readonly T1 Val1 => ref _set1!.Get(Entity); public ref readonly T2 Val2 => ref _set2!.Get(Entity); [MethodImpl(MethodImplOptions.AggressiveInlining)] public bool MoveNext() { if (_set1 == null || _set2 == null) return false; while (++_index < _count) { if (_swapped) { Entity = _set2!.DenseEntities[_index]; if (!_set1.Contains(Entity)) continue; } else { Entity = _set1.DenseEntities[_index]; if (!_set2.Contains(Entity)) continue; } if (!PassesWithout()) continue; if (_world.IsSingletonEntity(Entity)) continue; return true; } return false; } public Select2 GetEnumerator() => this; public void Dispose() => _world.EndBatching(); private bool PassesWithout() { if (_without == null) return true; foreach (var type in _without) { var set = _store.GetSet(type); if (set != null && set.Contains(Entity)) return false; } return true; } } /// Ref struct iterator for queries with three component types. Drives from the smallest set. public ref struct Select3 where T1 : struct where T2 : struct where T3 : struct { private readonly World _world; private readonly SparseSet? _set1; private readonly SparseSet? _set2; private readonly SparseSet? _set3; private readonly ComponentStore _store; private readonly HashSet? _without; private int _index; private readonly int _count; private readonly int _driver; // 0 = set1, 1 = set2, 2 = set3 internal Select3(World world, Query query) { _world = world; _store = world.Components; _without = query.WithoutTypes; _set1 = _store.GetSet(typeof(T1)) as SparseSet; _set2 = _store.GetSet(typeof(T2)) as SparseSet; _set3 = _store.GetSet(typeof(T3)) as SparseSet; int c1 = _set1?.Count ?? 0; int c2 = _set2?.Count ?? 0; int c3 = _set3?.Count ?? 0; if (c2 <= c1 && c2 <= c3) { _driver = 1; _count = c2; } else if (c3 <= c1 && c3 <= c2) { _driver = 2; _count = c3; } else { _driver = 0; _count = c1; } _index = -1; _world.BeginBatching(); } public Entity Entity { get; private set; } public Select3 Current => this; public ref T1 Ref1 { [MethodImpl(MethodImplOptions.AggressiveInlining)] get { _world.TrackAccess(Entity, typeof(T1)); return ref _set1!.Get(Entity); } } public ref T2 Ref2 { [MethodImpl(MethodImplOptions.AggressiveInlining)] get { _world.TrackAccess(Entity, typeof(T2)); return ref _set2!.Get(Entity); } } public ref T3 Ref3 { [MethodImpl(MethodImplOptions.AggressiveInlining)] get { _world.TrackAccess(Entity, typeof(T3)); return ref _set3!.Get(Entity); } } public ref readonly T1 Val1 => ref _set1!.Get(Entity); public ref readonly T2 Val2 => ref _set2!.Get(Entity); public ref readonly T3 Val3 => ref _set3!.Get(Entity); [MethodImpl(MethodImplOptions.AggressiveInlining)] public bool MoveNext() { if (_set1 == null || _set2 == null || _set3 == null) return false; while (++_index < _count) { switch (_driver) { case 0: Entity = _set1.DenseEntities[_index]; if (!_set2.Contains(Entity)) continue; if (!_set3.Contains(Entity)) continue; break; case 1: Entity = _set2!.DenseEntities[_index]; if (!_set1.Contains(Entity)) continue; if (!_set3.Contains(Entity)) continue; break; case 2: Entity = _set3!.DenseEntities[_index]; if (!_set1.Contains(Entity)) continue; if (!_set2.Contains(Entity)) continue; break; } if (!PassesWithout()) continue; if (_world.IsSingletonEntity(Entity)) continue; return true; } return false; } public Select3 GetEnumerator() => this; public void Dispose() => _world.EndBatching(); private bool PassesWithout() { if (_without == null) return true; foreach (var type in _without) { var set = _store.GetSet(type); if (set != null && set.Contains(Entity)) return false; } return true; } } /// Ref struct iterator for queries with four component types. public ref struct Select4 where T1 : struct where T2 : struct where T3 : struct where T4 : struct { private readonly World _world; private readonly SparseSet? _set1; private readonly SparseSet? _set2; private readonly SparseSet? _set3; private readonly SparseSet? _set4; private readonly ComponentStore _store; private readonly HashSet? _without; private int _index; private readonly int _count; private readonly int _driver; internal Select4(World world, Query query) { _world = world; _store = world.Components; _without = query.WithoutTypes; _set1 = _store.GetSet(typeof(T1)) as SparseSet; _set2 = _store.GetSet(typeof(T2)) as SparseSet; _set3 = _store.GetSet(typeof(T3)) as SparseSet; _set4 = _store.GetSet(typeof(T4)) as SparseSet; int c1 = _set1?.Count ?? 0, c2 = _set2?.Count ?? 0; int c3 = _set3?.Count ?? 0, c4 = _set4?.Count ?? 0; int min = c1; _driver = 0; if (c2 < min) { min = c2; _driver = 1; } if (c3 < min) { min = c3; _driver = 2; } if (c4 < min) { min = c4; _driver = 3; } _count = min; _index = -1; _world.BeginBatching(); } public Entity Entity { get; private set; } public Select4 Current => this; public ref T1 Ref1 { [MethodImpl(MethodImplOptions.AggressiveInlining)] get { _world.TrackAccess(Entity, typeof(T1)); return ref _set1!.Get(Entity); } } public ref T2 Ref2 { [MethodImpl(MethodImplOptions.AggressiveInlining)] get { _world.TrackAccess(Entity, typeof(T2)); return ref _set2!.Get(Entity); } } public ref T3 Ref3 { [MethodImpl(MethodImplOptions.AggressiveInlining)] get { _world.TrackAccess(Entity, typeof(T3)); return ref _set3!.Get(Entity); } } public ref T4 Ref4 { [MethodImpl(MethodImplOptions.AggressiveInlining)] get { _world.TrackAccess(Entity, typeof(T4)); return ref _set4!.Get(Entity); } } public ref readonly T1 Val1 => ref _set1!.Get(Entity); public ref readonly T2 Val2 => ref _set2!.Get(Entity); public ref readonly T3 Val3 => ref _set3!.Get(Entity); public ref readonly T4 Val4 => ref _set4!.Get(Entity); [MethodImpl(MethodImplOptions.AggressiveInlining)] public bool MoveNext() { if (_set1 == null || _set2 == null || _set3 == null || _set4 == null) return false; while (++_index < _count) { switch (_driver) { case 0: Entity = _set1.DenseEntities[_index]; if (!_set2.Contains(Entity)) continue; if (!_set3.Contains(Entity)) continue; if (!_set4.Contains(Entity)) continue; break; case 1: Entity = _set2!.DenseEntities[_index]; if (!_set1.Contains(Entity)) continue; if (!_set3.Contains(Entity)) continue; if (!_set4.Contains(Entity)) continue; break; case 2: Entity = _set3!.DenseEntities[_index]; if (!_set1.Contains(Entity)) continue; if (!_set2.Contains(Entity)) continue; if (!_set4.Contains(Entity)) continue; break; case 3: Entity = _set4!.DenseEntities[_index]; if (!_set1.Contains(Entity)) continue; if (!_set2.Contains(Entity)) continue; if (!_set3.Contains(Entity)) continue; break; } if (!PassesWithout()) continue; if (_world.IsSingletonEntity(Entity)) continue; return true; } return false; } public Select4 GetEnumerator() => this; public void Dispose() => _world.EndBatching(); private bool PassesWithout() { if (_without == null) return true; foreach (var type in _without) { var set = _store.GetSet(type); if (set != null && set.Contains(Entity)) return false; } return true; } } /// Ref struct iterator for queries with five component types. public ref struct Select5 where T1 : struct where T2 : struct where T3 : struct where T4 : struct where T5 : struct { private readonly World _world; private readonly SparseSet? _set1; private readonly SparseSet? _set2; private readonly SparseSet? _set3; private readonly SparseSet? _set4; private readonly SparseSet? _set5; private readonly ComponentStore _store; private readonly HashSet? _without; private int _index; private readonly int _count; private readonly int _driver; internal Select5(World world, Query query) { _world = world; _store = world.Components; _without = query.WithoutTypes; _set1 = _store.GetSet(typeof(T1)) as SparseSet; _set2 = _store.GetSet(typeof(T2)) as SparseSet; _set3 = _store.GetSet(typeof(T3)) as SparseSet; _set4 = _store.GetSet(typeof(T4)) as SparseSet; _set5 = _store.GetSet(typeof(T5)) as SparseSet; int c1 = _set1?.Count ?? 0, c2 = _set2?.Count ?? 0; int c3 = _set3?.Count ?? 0, c4 = _set4?.Count ?? 0; int c5 = _set5?.Count ?? 0; int min = c1; _driver = 0; if (c2 < min) { min = c2; _driver = 1; } if (c3 < min) { min = c3; _driver = 2; } if (c4 < min) { min = c4; _driver = 3; } if (c5 < min) { min = c5; _driver = 4; } _count = min; _index = -1; _world.BeginBatching(); } public Entity Entity { get; private set; } public Select5 Current => this; public ref T1 Ref1 { [MethodImpl(MethodImplOptions.AggressiveInlining)] get { _world.TrackAccess(Entity, typeof(T1)); return ref _set1!.Get(Entity); } } public ref T2 Ref2 { [MethodImpl(MethodImplOptions.AggressiveInlining)] get { _world.TrackAccess(Entity, typeof(T2)); return ref _set2!.Get(Entity); } } public ref T3 Ref3 { [MethodImpl(MethodImplOptions.AggressiveInlining)] get { _world.TrackAccess(Entity, typeof(T3)); return ref _set3!.Get(Entity); } } public ref T4 Ref4 { [MethodImpl(MethodImplOptions.AggressiveInlining)] get { _world.TrackAccess(Entity, typeof(T4)); return ref _set4!.Get(Entity); } } public ref T5 Ref5 { [MethodImpl(MethodImplOptions.AggressiveInlining)] get { _world.TrackAccess(Entity, typeof(T5)); return ref _set5!.Get(Entity); } } public ref readonly T1 Val1 => ref _set1!.Get(Entity); public ref readonly T2 Val2 => ref _set2!.Get(Entity); public ref readonly T3 Val3 => ref _set3!.Get(Entity); public ref readonly T4 Val4 => ref _set4!.Get(Entity); public ref readonly T5 Val5 => ref _set5!.Get(Entity); [MethodImpl(MethodImplOptions.AggressiveInlining)] public bool MoveNext() { if (_set1 == null || _set2 == null || _set3 == null || _set4 == null || _set5 == null) return false; while (++_index < _count) { switch (_driver) { case 0: Entity = _set1.DenseEntities[_index]; if (!_set2.Contains(Entity)) continue; if (!_set3.Contains(Entity)) continue; if (!_set4.Contains(Entity)) continue; if (!_set5.Contains(Entity)) continue; break; case 1: Entity = _set2!.DenseEntities[_index]; if (!_set1.Contains(Entity)) continue; if (!_set3.Contains(Entity)) continue; if (!_set4.Contains(Entity)) continue; if (!_set5.Contains(Entity)) continue; break; case 2: Entity = _set3!.DenseEntities[_index]; if (!_set1.Contains(Entity)) continue; if (!_set2.Contains(Entity)) continue; if (!_set4.Contains(Entity)) continue; if (!_set5.Contains(Entity)) continue; break; case 3: Entity = _set4!.DenseEntities[_index]; if (!_set1.Contains(Entity)) continue; if (!_set2.Contains(Entity)) continue; if (!_set3.Contains(Entity)) continue; if (!_set5.Contains(Entity)) continue; break; case 4: Entity = _set5!.DenseEntities[_index]; if (!_set1.Contains(Entity)) continue; if (!_set2.Contains(Entity)) continue; if (!_set3.Contains(Entity)) continue; if (!_set4.Contains(Entity)) continue; break; } if (!PassesWithout()) continue; if (_world.IsSingletonEntity(Entity)) continue; return true; } return false; } public Select5 GetEnumerator() => this; public void Dispose() => _world.EndBatching(); private bool PassesWithout() { if (_without == null) return true; foreach (var type in _without) { var set = _store.GetSet(type); if (set != null && set.Contains(Entity)) return false; } return true; } } /// Ref struct iterator for queries with six component types. public ref struct Select6 where T1 : struct where T2 : struct where T3 : struct where T4 : struct where T5 : struct where T6 : struct { private readonly World _world; private readonly SparseSet? _set1; private readonly SparseSet? _set2; private readonly SparseSet? _set3; private readonly SparseSet? _set4; private readonly SparseSet? _set5; private readonly SparseSet? _set6; private readonly ComponentStore _store; private readonly HashSet? _without; private int _index; private readonly int _count; private readonly int _driver; internal Select6(World world, Query query) { _world = world; _store = world.Components; _without = query.WithoutTypes; _set1 = _store.GetSet(typeof(T1)) as SparseSet; _set2 = _store.GetSet(typeof(T2)) as SparseSet; _set3 = _store.GetSet(typeof(T3)) as SparseSet; _set4 = _store.GetSet(typeof(T4)) as SparseSet; _set5 = _store.GetSet(typeof(T5)) as SparseSet; _set6 = _store.GetSet(typeof(T6)) as SparseSet; int c1 = _set1?.Count ?? 0, c2 = _set2?.Count ?? 0; int c3 = _set3?.Count ?? 0, c4 = _set4?.Count ?? 0; int c5 = _set5?.Count ?? 0, c6 = _set6?.Count ?? 0; int min = c1; _driver = 0; if (c2 < min) { min = c2; _driver = 1; } if (c3 < min) { min = c3; _driver = 2; } if (c4 < min) { min = c4; _driver = 3; } if (c5 < min) { min = c5; _driver = 4; } if (c6 < min) { min = c6; _driver = 5; } _count = min; _index = -1; _world.BeginBatching(); } public Entity Entity { get; private set; } public Select6 Current => this; public ref T1 Ref1 { [MethodImpl(MethodImplOptions.AggressiveInlining)] get { _world.TrackAccess(Entity, typeof(T1)); return ref _set1!.Get(Entity); } } public ref T2 Ref2 { [MethodImpl(MethodImplOptions.AggressiveInlining)] get { _world.TrackAccess(Entity, typeof(T2)); return ref _set2!.Get(Entity); } } public ref T3 Ref3 { [MethodImpl(MethodImplOptions.AggressiveInlining)] get { _world.TrackAccess(Entity, typeof(T3)); return ref _set3!.Get(Entity); } } public ref T4 Ref4 { [MethodImpl(MethodImplOptions.AggressiveInlining)] get { _world.TrackAccess(Entity, typeof(T4)); return ref _set4!.Get(Entity); } } public ref T5 Ref5 { [MethodImpl(MethodImplOptions.AggressiveInlining)] get { _world.TrackAccess(Entity, typeof(T5)); return ref _set5!.Get(Entity); } } public ref T6 Ref6 { [MethodImpl(MethodImplOptions.AggressiveInlining)] get { _world.TrackAccess(Entity, typeof(T6)); return ref _set6!.Get(Entity); } } public ref readonly T1 Val1 => ref _set1!.Get(Entity); public ref readonly T2 Val2 => ref _set2!.Get(Entity); public ref readonly T3 Val3 => ref _set3!.Get(Entity); public ref readonly T4 Val4 => ref _set4!.Get(Entity); public ref readonly T5 Val5 => ref _set5!.Get(Entity); public ref readonly T6 Val6 => ref _set6!.Get(Entity); [MethodImpl(MethodImplOptions.AggressiveInlining)] public bool MoveNext() { if (_set1 == null || _set2 == null || _set3 == null || _set4 == null || _set5 == null || _set6 == null) return false; while (++_index < _count) { switch (_driver) { case 0: Entity = _set1.DenseEntities[_index]; if (!_set2.Contains(Entity)) continue; if (!_set3.Contains(Entity)) continue; if (!_set4.Contains(Entity)) continue; if (!_set5.Contains(Entity)) continue; if (!_set6.Contains(Entity)) continue; break; case 1: Entity = _set2!.DenseEntities[_index]; if (!_set1.Contains(Entity)) continue; if (!_set3.Contains(Entity)) continue; if (!_set4.Contains(Entity)) continue; if (!_set5.Contains(Entity)) continue; if (!_set6.Contains(Entity)) continue; break; case 2: Entity = _set3!.DenseEntities[_index]; if (!_set1.Contains(Entity)) continue; if (!_set2.Contains(Entity)) continue; if (!_set4.Contains(Entity)) continue; if (!_set5.Contains(Entity)) continue; if (!_set6.Contains(Entity)) continue; break; case 3: Entity = _set4!.DenseEntities[_index]; if (!_set1.Contains(Entity)) continue; if (!_set2.Contains(Entity)) continue; if (!_set3.Contains(Entity)) continue; if (!_set5.Contains(Entity)) continue; if (!_set6.Contains(Entity)) continue; break; case 4: Entity = _set5!.DenseEntities[_index]; if (!_set1.Contains(Entity)) continue; if (!_set2.Contains(Entity)) continue; if (!_set3.Contains(Entity)) continue; if (!_set4.Contains(Entity)) continue; if (!_set6.Contains(Entity)) continue; break; case 5: Entity = _set6!.DenseEntities[_index]; if (!_set1.Contains(Entity)) continue; if (!_set2.Contains(Entity)) continue; if (!_set3.Contains(Entity)) continue; if (!_set4.Contains(Entity)) continue; if (!_set5.Contains(Entity)) continue; break; } if (!PassesWithout()) continue; if (_world.IsSingletonEntity(Entity)) continue; return true; } return false; } public Select6 GetEnumerator() => this; public void Dispose() => _world.EndBatching(); private bool PassesWithout() { if (_without == null) return true; foreach (var type in _without) { var set = _store.GetSet(type); if (set != null && set.Contains(Entity)) return false; } return true; } }