using System.Runtime.CompilerServices;
namespace OECS;
///
/// Provides zero-allocation ref struct iterators for querying entities.
/// Use with foreach or manual while (iter.MoveNext()).
/// Supports break, continue, and early returns.
///
public static class EntityIterator
{
///
/// Returns an iterator over entities matching the query, with ref access
/// to one component type.
///
public static Select1 Select(
this World world, QueryDescriptor query)
where T1 : struct
{
return new Select1(world, query);
}
///
/// Returns an iterator over entities matching the query, with ref access
/// to two component types.
///
public static Select2 Select(
this World world, QueryDescriptor query)
where T1 : struct where T2 : struct
{
return new Select2(world, query);
}
///
/// Returns an iterator over entities matching the query, with ref access
/// to three component types.
///
public static Select3 Select(
this World world, QueryDescriptor query)
where T1 : struct where T2 : struct where T3 : struct
{
return new Select3(world, query);
}
}
///
/// 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 IReadOnlySet _without;
private int _index;
private readonly int _count;
internal Select1(World world, QueryDescriptor query)
{
_world = world;
_store = world.Components;
_without = query.Without;
_set = _store.GetSet(typeof(T1)) as SparseSet;
_count = _set?.Count ?? 0;
_index = -1;
_world.BeginIteration();
}
public Entity CurrentEntity { get; private set; }
public ref T1 Current1 => ref _set!.Get(CurrentEntity);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool MoveNext()
{
if (_set == null) return false;
while (++_index < _count)
{
CurrentEntity = _set.DenseEntities[_index];
if (CurrentEntity.Id == 1) continue; // Skip singleton.
if (!PassesWithout()) continue;
return true;
}
return false;
}
public Select1 GetEnumerator() => this;
public void Dispose()
{
_world.EndIteration();
}
private bool PassesWithout()
{
foreach (var type in _without)
{
var set = _store.GetSet(type);
if (set != null && set.Contains(CurrentEntity))
return false;
}
return true;
}
}
///
/// Ref struct iterator for queries with two component types.
///
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 IReadOnlySet _without;
private int _index;
private readonly int _count;
internal Select2(World world, QueryDescriptor query)
{
_world = world;
_store = world.Components;
_without = query.Without;
_set1 = _store.GetSet(typeof(T1)) as SparseSet;
_set2 = _store.GetSet(typeof(T2)) as SparseSet;
_count = _set1?.Count ?? 0;
_index = -1;
_world.BeginIteration();
}
public Entity CurrentEntity { get; private set; }
public ref T1 Current1 => ref _set1!.Get(CurrentEntity);
public ref T2 Current2 => ref _set2!.Get(CurrentEntity);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool MoveNext()
{
if (_set1 == null || _set2 == null) return false;
while (++_index < _count)
{
CurrentEntity = _set1.DenseEntities[_index];
if (CurrentEntity.Id == 1) continue;
if (!_set2.Contains(CurrentEntity)) continue;
if (!PassesWithout()) continue;
return true;
}
return false;
}
public Select2 GetEnumerator() => this;
public void Dispose()
{
_world.EndIteration();
}
private bool PassesWithout()
{
foreach (var type in _without)
{
var set = _store.GetSet(type);
if (set != null && set.Contains(CurrentEntity))
return false;
}
return true;
}
}
///
/// Ref struct iterator for queries with three component types.
///
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 IReadOnlySet _without;
private int _index;
private readonly int _count;
internal Select3(World world, QueryDescriptor query)
{
_world = world;
_store = world.Components;
_without = query.Without;
_set1 = _store.GetSet(typeof(T1)) as SparseSet;
_set2 = _store.GetSet(typeof(T2)) as SparseSet;
_set3 = _store.GetSet(typeof(T3)) as SparseSet;
_count = _set1?.Count ?? 0;
_index = -1;
_world.BeginIteration();
}
public Entity CurrentEntity { get; private set; }
public ref T1 Current1 => ref _set1!.Get(CurrentEntity);
public ref T2 Current2 => ref _set2!.Get(CurrentEntity);
public ref T3 Current3 => ref _set3!.Get(CurrentEntity);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool MoveNext()
{
if (_set1 == null || _set2 == null || _set3 == null) return false;
while (++_index < _count)
{
CurrentEntity = _set1.DenseEntities[_index];
if (CurrentEntity.Id == 1) continue;
if (!_set2.Contains(CurrentEntity)) continue;
if (!_set3.Contains(CurrentEntity)) continue;
if (!PassesWithout()) continue;
return true;
}
return false;
}
public Select3 GetEnumerator() => this;
public void Dispose()
{
_world.EndIteration();
}
private bool PassesWithout()
{
foreach (var type in _without)
{
var set = _store.GetSet(type);
if (set != null && set.Contains(CurrentEntity))
return false;
}
return true;
}
}