Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Consolidate LINQ's internal IIListProvider/IPartition into base Iterator class #98969

Merged
merged 10 commits into from
Feb 28, 2024
6 changes: 3 additions & 3 deletions src/libraries/System.Linq/src/System.Linq.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
<PropertyGroup>
<TargetPlatformIdentifier>$([MSBuild]::GetTargetPlatformIdentifier('$(TargetFramework)'))</TargetPlatformIdentifier>
<OptimizeForSize Condition="'$(TargetPlatformIdentifier)' == 'browser' or '$(TargetPlatformIdentifier)' == 'android' or '$(TargetPlatformIdentifier)' == 'ios' or '$(TargetPlatformIdentifier)' == 'tvos'">true</OptimizeForSize>
<DefineConstants Condition="'$(OptimizeForSize)' == 'true'">$(DefineConstants);OPTIMIZE_FOR_SIZE</DefineConstants>
</PropertyGroup>

<ItemGroup Condition="'$(OptimizeForSize)' == true">
Expand All @@ -23,15 +24,16 @@
<Compile Include="System\Linq\DefaultIfEmpty.SpeedOpt.cs" />
<Compile Include="System\Linq\Distinct.SpeedOpt.cs" />
<Compile Include="System\Linq\Grouping.SpeedOpt.cs" />
<Compile Include="System\Linq\Iterator.SpeedOpt.cs" />
<Compile Include="System\Linq\Lookup.SpeedOpt.cs" />
<Compile Include="System\Linq\OrderedEnumerable.SpeedOpt.cs" />
<Compile Include="System\Linq\Partition.SpeedOpt.cs" />
<Compile Include="System\Linq\Range.SpeedOpt.cs" />
<Compile Include="System\Linq\Repeat.SpeedOpt.cs" />
<Compile Include="System\Linq\Reverse.SpeedOpt.cs" />
<Compile Include="System\Linq\Select.SpeedOpt.cs" />
<Compile Include="System\Linq\SelectMany.SpeedOpt.cs" />
<Compile Include="System\Linq\Skip.SpeedOpt.cs" />
<Compile Include="System\Linq\SkipTake.SpeedOpt.cs" />
<Compile Include="System\Linq\Take.SpeedOpt.cs" />
<Compile Include="System\Linq\Union.SpeedOpt.cs" />
<Compile Include="System\Linq\Where.SpeedOpt.cs" />
Expand Down Expand Up @@ -61,8 +63,6 @@
<Compile Include="System\Linq\Index.cs" />
<Compile Include="System\Linq\Intersect.cs" />
<Compile Include="System\Linq\Iterator.cs" />
<Compile Include="System\Linq\IIListProvider.cs" />
<Compile Include="System\Linq\IPartition.cs" />
<Compile Include="System\Linq\Join.cs" />
<Compile Include="System\Linq\Last.cs" />
<Compile Include="System\Linq\Lookup.cs" />
Expand Down
66 changes: 53 additions & 13 deletions src/libraries/System.Linq/src/System/Linq/AppendPrepend.SpeedOpt.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,15 +8,6 @@ namespace System.Linq
{
public static partial class Enumerable
{
private abstract partial class AppendPrependIterator<TSource> : IIListProvider<TSource>
{
public abstract TSource[] ToArray();

public abstract List<TSource> ToList();

public abstract int GetCount(bool onlyIfCheap);
}

private sealed partial class AppendPrepend1Iterator<TSource>
{
private TSource[] LazyToArray()
Expand Down Expand Up @@ -130,14 +121,63 @@ public override List<TSource> ToList()

public override int GetCount(bool onlyIfCheap)
{
if (_source is IIListProvider<TSource> listProv)
if (_source is Iterator<TSource> iterator)
{
int count = listProv.GetCount(onlyIfCheap);
int count = iterator.GetCount(onlyIfCheap);
return count == -1 ? -1 : count + 1;
}

return !onlyIfCheap || _source is ICollection<TSource> ? _source.Count() + 1 : -1;
}

public override TSource? TryGetFirst(out bool found)
{
if (_appending)
{
TSource? first = _source.TryGetFirst(out found);
if (found)
{
return first;
}
}

found = true;
return _item;
}

public override TSource? TryGetLast(out bool found)
{
if (!_appending)
{
TSource? last = _source.TryGetLast(out found);
if (found)
{
return last;
}
}

found = true;
return _item;
}

public override TSource? TryGetElementAt(int index, out bool found)
{
if (!_appending)
{
if (index == 0)
{
found = true;
return _item;
}

index--;
return
_source is Iterator<TSource> iterator ? iterator.TryGetElementAt(index, out found) :
TryGetElementAtNonIterator(_source, index, out found);
}

return base.TryGetElementAt(index, out found);
}
}

private sealed partial class AppendPrependN<TSource>
Expand Down Expand Up @@ -232,9 +272,9 @@ public override List<TSource> ToList()

public override int GetCount(bool onlyIfCheap)
{
if (_source is IIListProvider<TSource> listProv)
if (_source is Iterator<TSource> iterator)
{
int count = listProv.GetCount(onlyIfCheap);
int count = iterator.GetCount(onlyIfCheap);
return count == -1 ? -1 : count + _appendCount + _prependCount;
}

Expand Down
21 changes: 7 additions & 14 deletions src/libraries/System.Linq/src/System/Linq/Cast.SpeedOpt.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,11 @@ namespace System.Linq
{
public static partial class Enumerable
{
private sealed partial class CastICollectionIterator<TResult> : IPartition<TResult>
private sealed partial class CastICollectionIterator<TResult>
{
public int GetCount(bool onlyIfCheap) => _source.Count;
public override int GetCount(bool onlyIfCheap) => _source.Count;

public TResult[] ToArray()
public override TResult[] ToArray()
{
TResult[] array = new TResult[_source.Count];

Expand All @@ -25,7 +25,7 @@ public TResult[] ToArray()
return array;
}

public List<TResult> ToList()
public override List<TResult> ToList()
{
List<TResult> list = new(_source.Count);

Expand All @@ -37,7 +37,7 @@ public List<TResult> ToList()
return list;
}

public TResult? TryGetElementAt(int index, out bool found)
public override TResult? TryGetElementAt(int index, out bool found)
{
if (index >= 0)
{
Expand Down Expand Up @@ -65,7 +65,7 @@ public List<TResult> ToList()
return default;
}

public TResult? TryGetFirst(out bool found)
public override TResult? TryGetFirst(out bool found)
{
IEnumerator e = _source.GetEnumerator();
try
Expand All @@ -85,7 +85,7 @@ public List<TResult> ToList()
return default;
}

public TResult? TryGetLast(out bool found)
public override TResult? TryGetLast(out bool found)
{
IEnumerator e = _source.GetEnumerator();
try
Expand All @@ -110,13 +110,6 @@ public List<TResult> ToList()
(e as IDisposable)?.Dispose();
}
}

public override IEnumerable<TResult2> Select<TResult2>(Func<TResult, TResult2> selector) =>
new SelectIPartitionIterator<TResult, TResult2>(this, selector);

public IPartition<TResult>? Skip(int count) => new EnumerablePartition<TResult>(this, count, -1);

public IPartition<TResult>? Take(int count) => new EnumerablePartition<TResult>(this, 0, count - 1);
}
}
}
18 changes: 2 additions & 16 deletions src/libraries/System.Linq/src/System/Linq/Concat.SpeedOpt.cs
Original file line number Diff line number Diff line change
Expand Up @@ -342,13 +342,9 @@ private TSource[] PreallocatingToArray()
}
}

private abstract partial class ConcatIterator<TSource> : IPartition<TSource>
private abstract partial class ConcatIterator<TSource>
{
public abstract int GetCount(bool onlyIfCheap);

public abstract TSource[] ToArray();

public List<TSource> ToList()
public override List<TSource> ToList()
{
int count = GetCount(onlyIfCheap: true);
var list = count != -1 ? new List<TSource>(count) : new List<TSource>();
Expand All @@ -367,16 +363,6 @@ public List<TSource> ToList()
return list;
}

public abstract TSource? TryGetElementAt(int index, out bool found);

public abstract TSource? TryGetFirst(out bool found);

public abstract TSource? TryGetLast(out bool found);

public IPartition<TSource>? Skip(int count) => new EnumerablePartition<TSource>(this, count, -1);

public IPartition<TSource>? Take(int count) => new EnumerablePartition<TSource>(this, 0, count - 1);

}
}
}
12 changes: 8 additions & 4 deletions src/libraries/System.Linq/src/System/Linq/Count.cs
Original file line number Diff line number Diff line change
Expand Up @@ -20,10 +20,12 @@ public static int Count<TSource>(this IEnumerable<TSource> source)
return collectionoft.Count;
}

if (source is IIListProvider<TSource> listProv)
#if !OPTIMIZE_FOR_SIZE
if (source is Iterator<TSource> iterator)
stephentoub marked this conversation as resolved.
Show resolved Hide resolved
{
return listProv.GetCount(onlyIfCheap: false);
return iterator.GetCount(onlyIfCheap: false);
}
#endif

if (source is ICollection collection)
{
Expand Down Expand Up @@ -105,15 +107,17 @@ public static bool TryGetNonEnumeratedCount<TSource>(this IEnumerable<TSource> s
return true;
}

if (source is IIListProvider<TSource> listProv)
#if !OPTIMIZE_FOR_SIZE
if (source is Iterator<TSource> iterator)
{
int c = listProv.GetCount(onlyIfCheap: true);
int c = iterator.GetCount(onlyIfCheap: true);
if (c >= 0)
{
count = c;
return true;
}
}
#endif

if (source is ICollection collection)
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,15 +8,15 @@ namespace System.Linq
{
public static partial class Enumerable
{
private sealed partial class DefaultIfEmptyIterator<TSource> : IIListProvider<TSource>
private sealed partial class DefaultIfEmptyIterator<TSource>
{
public TSource[] ToArray()
public override TSource[] ToArray()
{
TSource[] array = _source.ToArray();
return array.Length == 0 ? [_default] : array;
}

public List<TSource> ToList()
public override List<TSource> ToList()
{
List<TSource> list = _source.ToList();
if (list.Count == 0)
Expand All @@ -27,7 +27,7 @@ public List<TSource> ToList()
return list;
}

public int GetCount(bool onlyIfCheap)
public override int GetCount(bool onlyIfCheap)
{
int count;
if (!onlyIfCheap || _source is ICollection<TSource> || _source is ICollection)
Expand All @@ -36,11 +36,51 @@ public int GetCount(bool onlyIfCheap)
}
else
{
count = _source is IIListProvider<TSource> listProv ? listProv.GetCount(onlyIfCheap: true) : -1;
count = _source is Iterator<TSource> iterator ? iterator.GetCount(onlyIfCheap: true) : -1;
}

return count == 0 ? 1 : count;
}

public override TSource? TryGetFirst(out bool found)
{
TSource? first = _source.TryGetFirst(out found);
if (found)
{
return first;
}

found = true;
return _default;
}

public override TSource? TryGetLast(out bool found)
{
TSource? last = _source.TryGetLast(out found);
if (found)
{
return last;
}

found = true;
return _default;
}

public override TSource? TryGetElementAt(int index, out bool found)
{
TSource? item = _source.TryGetElementAt(index, out found);
if (found)
{
return item;
}

if (index == 0)
{
found = true;
}

return _default;
}
}
}
}
10 changes: 6 additions & 4 deletions src/libraries/System.Linq/src/System/Linq/Distinct.SpeedOpt.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,15 @@ namespace System.Linq
{
public static partial class Enumerable
{
private sealed partial class DistinctIterator<TSource> : IIListProvider<TSource>
private sealed partial class DistinctIterator<TSource>
{
public TSource[] ToArray() => HashSetToArray(new HashSet<TSource>(_source, _comparer));
public override TSource[] ToArray() => HashSetToArray(new HashSet<TSource>(_source, _comparer));

public List<TSource> ToList() => new List<TSource>(new HashSet<TSource>(_source, _comparer));
public override List<TSource> ToList() => new List<TSource>(new HashSet<TSource>(_source, _comparer));

public int GetCount(bool onlyIfCheap) => onlyIfCheap ? -1 : new HashSet<TSource>(_source, _comparer).Count;
public override int GetCount(bool onlyIfCheap) => onlyIfCheap ? -1 : new HashSet<TSource>(_source, _comparer).Count;

public override TSource? TryGetFirst(out bool found) => _source.TryGetFirst(out found);
}
}
}
Loading
Loading