Skip to content
This repository was archived by the owner on Jan 23, 2023. It is now read-only.

Commit f89fe17

Browse files
committed
Implement and expose SkipLast/TakeLast
1 parent ee1bc8a commit f89fe17

23 files changed

+518
-16
lines changed

pkg/Microsoft.Private.PackageBaseline/packageIndex.json

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1634,7 +1634,8 @@
16341634
"4.0.0.0": "4.0.0",
16351635
"4.1.0.0": "4.1.0",
16361636
"4.1.1.0": "4.3.0",
1637-
"4.2.0.0": "4.4.0"
1637+
"4.2.0.0": "4.4.0",
1638+
"4.3.0.0": "4.4.0"
16381639
}
16391640
},
16401641
"System.Linq.Expressions": {
@@ -1678,7 +1679,8 @@
16781679
"4.0.0.0": "4.0.0",
16791680
"4.0.1.0": "4.0.1",
16801681
"4.0.2.0": "4.3.0",
1681-
"4.0.3.0": "4.4.0"
1682+
"4.0.3.0": "4.4.0",
1683+
"4.1.0.0": "4.4.0"
16821684
}
16831685
},
16841686
"System.Net.Http": {

src/Common/src/System/Collections/Generic/ArrayBuilder.cs

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -106,6 +106,32 @@ public T[] ToArray()
106106
return result;
107107
}
108108

109+
/// <summary>
110+
/// Creates a circular buffer from this builder.
111+
/// </summary>
112+
/// <remarks>
113+
/// <para>
114+
/// Before calling this method, the caller should ensure that <see cref="Count"/> is
115+
/// non-zero and is equal to <see cref="Capacity"/>.
116+
/// </para>
117+
/// <para>
118+
/// Do not use this builder again after calling this method.
119+
/// </para>
120+
/// </remarks>
121+
public WeakCircularBuffer<T> ToCircular()
122+
{
123+
T[] array = _array;
124+
125+
#if DEBUG
126+
Debug.Assert(_count > 0 && _count == _array.Length);
127+
128+
_array = null;
129+
_count = -1;
130+
#endif
131+
132+
return new WeakCircularBuffer<T>(array);
133+
}
134+
109135
/// <summary>
110136
/// Adds an item to the backing array, without checking if there is room.
111137
/// </summary>
Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
// Licensed to the .NET Foundation under one or more agreements.
2+
// The .NET Foundation licenses this file to you under the MIT license.
3+
// See the LICENSE file in the project root for more information.
4+
5+
using System.Diagnostics;
6+
7+
namespace System.Collections.Generic
8+
{
9+
/// <summary>
10+
/// A circular buffer in which the oldest items are automatically overwritten.
11+
/// </summary>
12+
/// <typeparam name="T">The element type.</typeparam>
13+
internal struct WeakCircularBuffer<T> : IDisposable
14+
{
15+
/// <summary>
16+
/// The underlying array for this buffer.
17+
/// </summary>
18+
private T[] _array;
19+
20+
/// <summary>
21+
/// The next index that will be overwritten.
22+
/// </summary>
23+
private int _index;
24+
25+
/// <summary>
26+
/// Constructs a circular buffer that is positioned at index 0.
27+
/// </summary>
28+
/// <param name="array">The underlying array for this buffer.</param>
29+
public WeakCircularBuffer(T[] array)
30+
{
31+
Debug.Assert(array?.Length > 0);
32+
33+
_array = array;
34+
_index = 0;
35+
}
36+
37+
/// <summary>
38+
/// Frees the resources associated with this buffer.
39+
/// </summary>
40+
/// <remarks>
41+
/// This method will allow the GC to reclaim the underlying array, even if the
42+
/// <see cref="WeakCircularBuffer{T}"/> is still GC-reachable.
43+
/// </remarks>
44+
public void Dispose()
45+
{
46+
_array = null;
47+
}
48+
49+
/// <summary>
50+
/// Exchanges an item with the oldest item in this buffer.
51+
/// </summary>
52+
/// <param name="item">The item, to place in this buffer.</param>
53+
/// <param name="oldest">The oldest item, to evict from this buffer.</param>
54+
public void Exchange(T item, out T oldest)
55+
{
56+
if (_index == _array.Length)
57+
{
58+
_index = 0;
59+
}
60+
61+
oldest = _array[_index];
62+
_array[_index++] = item;
63+
}
64+
}
65+
}

src/System.Linq.Queryable/dir.props

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,6 @@
22
<Project ToolsVersion="14.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
33
<Import Project="..\dir.props" />
44
<PropertyGroup>
5-
<AssemblyVersion>4.0.3.0</AssemblyVersion>
5+
<AssemblyVersion>4.1.0.0</AssemblyVersion>
66
</PropertyGroup>
77
</Project>

src/System.Linq.Queryable/ref/System.Linq.Queryable.cs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -126,6 +126,9 @@ public static partial class Queryable
126126
public static TSource SingleOrDefault<TSource>(this System.Linq.IQueryable<TSource> source) { throw null; }
127127
public static TSource SingleOrDefault<TSource>(this System.Linq.IQueryable<TSource> source, System.Linq.Expressions.Expression<System.Func<TSource, bool>> predicate) { throw null; }
128128
public static System.Linq.IQueryable<TSource> Skip<TSource>(this System.Linq.IQueryable<TSource> source, int count) { throw null; }
129+
#if netcoreapp11
130+
public static System.Linq.IQueryable<TSource> SkipLast<TSource>(this System.Linq.IQueryable<TSource> source, int count) { throw null; }
131+
#endif
129132
public static System.Linq.IQueryable<TSource> SkipWhile<TSource>(this System.Linq.IQueryable<TSource> source, System.Linq.Expressions.Expression<System.Func<TSource, bool>> predicate) { throw null; }
130133
public static System.Linq.IQueryable<TSource> SkipWhile<TSource>(this System.Linq.IQueryable<TSource> source, System.Linq.Expressions.Expression<System.Func<TSource, int, bool>> predicate) { throw null; }
131134
public static decimal Sum(this System.Linq.IQueryable<decimal> source) { throw null; }
@@ -149,6 +152,9 @@ public static partial class Queryable
149152
public static System.Nullable<float> Sum<TSource>(this System.Linq.IQueryable<TSource> source, System.Linq.Expressions.Expression<System.Func<TSource, System.Nullable<float>>> selector) { throw null; }
150153
public static float Sum<TSource>(this System.Linq.IQueryable<TSource> source, System.Linq.Expressions.Expression<System.Func<TSource, float>> selector) { throw null; }
151154
public static System.Linq.IQueryable<TSource> Take<TSource>(this System.Linq.IQueryable<TSource> source, int count) { throw null; }
155+
#if netcoreapp11
156+
public static System.Linq.IQueryable<TSource> TakeLast<TSource>(this System.Linq.IQueryable<TSource> source, int count) { throw null; }
157+
#endif
152158
public static System.Linq.IQueryable<TSource> TakeWhile<TSource>(this System.Linq.IQueryable<TSource> source, System.Linq.Expressions.Expression<System.Func<TSource, bool>> predicate) { throw null; }
153159
public static System.Linq.IQueryable<TSource> TakeWhile<TSource>(this System.Linq.IQueryable<TSource> source, System.Linq.Expressions.Expression<System.Func<TSource, int, bool>> predicate) { throw null; }
154160
public static System.Linq.IOrderedQueryable<TSource> ThenBy<TSource, TKey>(this System.Linq.IOrderedQueryable<TSource> source, System.Linq.Expressions.Expression<System.Func<TSource, TKey>> keySelector) { throw null; }

src/System.Linq.Queryable/ref/System.Linq.Queryable.csproj

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
<Project ToolsVersion="14.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
33
<Import Project="$([MSBuild]::GetDirectoryNameOfFileAbove($(MSBuildThisFileDirectory), dir.props))\dir.props" />
44
<PropertyGroup>
5-
<AssemblyVersion>4.0.0.0</AssemblyVersion>
5+
<AssemblyVersion>4.1.0.0</AssemblyVersion>
66
<OutputType>Library</OutputType>
77
<NuGetTargetMoniker>.NETStandard,Version=v1.7</NuGetTargetMoniker>
88
</PropertyGroup>

src/System.Linq.Queryable/src/System.Linq.Queryable.csproj

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
<PropertyGroup>
55
<ProjectGuid>{BE12B753-C130-4B68-86E3-877F1AEE52C0}</ProjectGuid>
66
<AssemblyName>System.Linq.Queryable</AssemblyName>
7+
<AssemblyVersion>4.1.0.0</AssemblyVersion>
78
<RootNamespace>System.Linq.Queryable</RootNamespace>
89
<IsPartialFacadeAssembly Condition="'$(TargetGroup)' == 'net46'">true</IsPartialFacadeAssembly>
910
<ResourcesSourceOutputDirectory Condition="'$(TargetGroup)' == 'net46'">None</ResourcesSourceOutputDirectory>

src/System.Linq.Queryable/src/System/Linq/CachedReflection.cs

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -837,5 +837,19 @@ public static MethodInfo Zip_TFirst_TSecond_TResult_3(Type TFirst, Type TSecond,
837837
(s_Zip_TFirst_TSecond_TResult_3 = new Func<IQueryable<object>, IEnumerable<object>, Expression<Func<object, object, object>>, IQueryable<object>>(Queryable.Zip).GetMethodInfo().GetGenericMethodDefinition()))
838838
.MakeGenericMethod(TFirst, TSecond, TResult);
839839

840+
841+
private static MethodInfo s_SkipLast_TSource_2;
842+
843+
public static MethodInfo SkipLast_TSource_2(Type TSource) =>
844+
(s_SkipLast_TSource_2 ??
845+
(s_SkipLast_TSource_2 = new Func<IQueryable<object>, int, IQueryable<object>>(Queryable.SkipLast).GetMethodInfo().GetGenericMethodDefinition()))
846+
.MakeGenericMethod(TSource);
847+
848+
private static MethodInfo s_TakeLast_TSource_2;
849+
850+
public static MethodInfo TakeLast_TSource_2(Type TSource) =>
851+
(s_TakeLast_TSource_2 ??
852+
(s_TakeLast_TSource_2 = new Func<IQueryable<object>, int, IQueryable<object>>(Queryable.TakeLast).GetMethodInfo().GetGenericMethodDefinition()))
853+
.MakeGenericMethod(TSource);
840854
}
841855
}

src/System.Linq.Queryable/src/System/Linq/Queryable.cs

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1609,5 +1609,35 @@ public static TResult Aggregate<TSource, TAccumulate, TResult>(this IQueryable<T
16091609
null,
16101610
CachedReflectionInfo.Aggregate_TSource_TAccumulate_TResult_4(typeof(TSource), typeof(TAccumulate), typeof(TResult)), source.Expression, Expression.Constant(seed), Expression.Quote(func), Expression.Quote(selector)));
16111611
}
1612+
1613+
public static IQueryable<TSource> SkipLast<TSource>(this IQueryable<TSource> source, int count)
1614+
{
1615+
if (source == null)
1616+
{
1617+
throw Error.ArgumentNull(nameof(source));
1618+
}
1619+
1620+
return source.Provider.CreateQuery<TSource>(
1621+
Expression.Call(
1622+
null,
1623+
CachedReflectionInfo.SkipLast_TSource_2(typeof(TSource)),
1624+
source.Expression, Expression.Constant(count)
1625+
));
1626+
}
1627+
1628+
public static IQueryable<TSource> TakeLast<TSource>(this IQueryable<TSource> source, int count)
1629+
{
1630+
if (source == null)
1631+
{
1632+
throw Error.ArgumentNull(nameof(source));
1633+
}
1634+
1635+
return source.Provider.CreateQuery<TSource>(
1636+
Expression.Call(
1637+
null,
1638+
CachedReflectionInfo.TakeLast_TSource_2(typeof(TSource)),
1639+
source.Expression, Expression.Constant(count)
1640+
));
1641+
}
16121642
}
16131643
}
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
// Licensed to the .NET Foundation under one or more agreements.
2+
// The .NET Foundation licenses this file to you under the MIT license.
3+
// See the LICENSE file in the project root for more information.
4+
5+
using System.Collections.Generic;
6+
using Xunit;
7+
8+
namespace System.Linq.Tests
9+
{
10+
public class SkipLastTests : EnumerableBasedTests
11+
{
12+
[Theory, MemberData(nameof(SkipLastData))]
13+
public void SkipLast(IQueryable<int> source, int count)
14+
{
15+
IQueryable<int> expected = source.Reverse().Skip(count).Reverse();
16+
IQueryable<int> actual = source.SkipLast(count);
17+
18+
Assert.Equal(expected, actual);
19+
}
20+
21+
public static IEnumerable<object[]> SkipLastData()
22+
{
23+
IEnumerable<int> counts = new[] { int.MinValue + 1, -1, 0, 1, 2, 3, 4, 5, int.MaxValue };
24+
for (int i = 0; i < 100; i += 11)
25+
{
26+
IEnumerable<int> enumerable = Enumerable.Range(i, i);
27+
foreach (int count in counts)
28+
{
29+
yield return new object[] { enumerable.AsQueryable(), count };
30+
}
31+
}
32+
}
33+
}
34+
}

src/System.Linq.Queryable/tests/System.Linq.Queryable.Tests.csproj

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
<?xml version="1.0" encoding="utf-8"?>
1+
<?xml version="1.0" encoding="utf-8"?>
22
<Project ToolsVersion="14.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
33
<Import Project="$([MSBuild]::GetDirectoryNameOfFileAbove($(MSBuildThisFileDirectory), dir.props))\dir.props" />
44
<PropertyGroup>
@@ -9,6 +9,7 @@
99
<AssemblyName>System.Linq.Queryable.Tests</AssemblyName>
1010
<RootNamespace>System.Linq.Queryable.Tests</RootNamespace>
1111
<NugetTargetMoniker>.NETStandard,Version=v1.7</NugetTargetMoniker>
12+
<DefineConstants Condition="'$(TargetGroup)' == ''">$(DefineConstants);netcoreapp11</DefineConstants>
1213
</PropertyGroup>
1314
<!-- Default configurations to help VS understand the configurations -->
1415
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
@@ -64,12 +65,21 @@
6465
<Compile Include="WhereTests.cs" />
6566
<Compile Include="ZipTests.cs" />
6667
</ItemGroup>
68+
<ItemGroup Condition="'$(TargetGroup)' == 'netcoreapp1.1'">
69+
<Compile Include="SkipLastTests.netcoreapp1.1.cs" />
70+
<Compile Include="TakeLastTests.netcoreapp1.1.cs" />
71+
</ItemGroup>
6772
<ItemGroup>
6873
<ProjectReference Include="..\pkg\System.Linq.Queryable.pkgproj">
6974
<Project>{be12b753-c130-4b68-86e3-877f1aee52c0}</Project>
7075
<Name>System.Linq.Queryable</Name>
7176
<Private>true</Private>
7277
</ProjectReference>
78+
<ProjectReference Include="..\..\System.Linq\src\System.Linq.csproj">
79+
<Project>{CA488507-3B6E-4494-B7BE-7B4EEEB2C4D1}</Project>
80+
<Name>System.Linq</Name>
81+
<Private>true</Private>
82+
</ProjectReference>
7383
</ItemGroup>
7484
<Import Project="$([MSBuild]::GetDirectoryNameOfFileAbove($(MSBuildThisFileDirectory), dir.targets))\dir.targets" />
7585
</Project>
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
// Licensed to the .NET Foundation under one or more agreements.
2+
// The .NET Foundation licenses this file to you under the MIT license.
3+
// See the LICENSE file in the project root for more information.
4+
5+
using System.Collections.Generic;
6+
using Xunit;
7+
8+
namespace System.Linq.Tests
9+
{
10+
public class TakeLastTests : EnumerableBasedTests
11+
{
12+
[Theory, MemberData(nameof(TakeLastData))]
13+
public void TakeLast(IQueryable<int> equivalent, int count)
14+
{
15+
IQueryable<int> expected = equivalent.Reverse().Take(count).Reverse();
16+
IQueryable<int> actual = equivalent.TakeLast(count);
17+
18+
Assert.Equal(expected, actual);
19+
}
20+
21+
public static IEnumerable<object[]> TakeLastData()
22+
{
23+
IEnumerable<int> counts = new[] { int.MinValue + 1, -1, 0, 1, 2, 3, 4, 5, int.MaxValue };
24+
for (int i = 0; i < 100; i += 11)
25+
{
26+
IEnumerable<int> enumerable = Enumerable.Range(i, i);
27+
foreach (int count in counts)
28+
{
29+
yield return new object[] { enumerable.AsQueryable(), count };
30+
}
31+
}
32+
}
33+
}
34+
}

src/System.Linq/dir.props

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,6 @@
22
<Project ToolsVersion="14.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
33
<Import Project="..\dir.props" />
44
<PropertyGroup>
5-
<AssemblyVersion>4.2.0.0</AssemblyVersion>
5+
<AssemblyVersion>4.3.0.0</AssemblyVersion>
66
</PropertyGroup>
77
</Project>

src/System.Linq/ref/System.Linq.cs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -143,6 +143,9 @@ public static partial class Enumerable
143143
public static TSource SingleOrDefault<TSource>(this System.Collections.Generic.IEnumerable<TSource> source) { throw null; }
144144
public static TSource SingleOrDefault<TSource>(this System.Collections.Generic.IEnumerable<TSource> source, System.Func<TSource, bool> predicate) { throw null; }
145145
public static System.Collections.Generic.IEnumerable<TSource> Skip<TSource>(this System.Collections.Generic.IEnumerable<TSource> source, int count) { throw null; }
146+
#if netcoreapp11
147+
public static System.Collections.Generic.IEnumerable<TSource> SkipLast<TSource>(this System.Collections.Generic.IEnumerable<TSource> source, int count) { throw null; }
148+
#endif
146149
public static System.Collections.Generic.IEnumerable<TSource> SkipWhile<TSource>(this System.Collections.Generic.IEnumerable<TSource> source, System.Func<TSource, bool> predicate) { throw null; }
147150
public static System.Collections.Generic.IEnumerable<TSource> SkipWhile<TSource>(this System.Collections.Generic.IEnumerable<TSource> source, System.Func<TSource, int, bool> predicate) { throw null; }
148151
public static decimal Sum(this System.Collections.Generic.IEnumerable<decimal> source) { throw null; }
@@ -166,6 +169,9 @@ public static partial class Enumerable
166169
public static System.Nullable<float> Sum<TSource>(this System.Collections.Generic.IEnumerable<TSource> source, System.Func<TSource, System.Nullable<float>> selector) { throw null; }
167170
public static float Sum<TSource>(this System.Collections.Generic.IEnumerable<TSource> source, System.Func<TSource, float> selector) { throw null; }
168171
public static System.Collections.Generic.IEnumerable<TSource> Take<TSource>(this System.Collections.Generic.IEnumerable<TSource> source, int count) { throw null; }
172+
#if netcoreapp11
173+
public static System.Collections.Generic.IEnumerable<TSource> TakeLast<TSource>(this System.Collections.Generic.IEnumerable<TSource> source, int count) { throw null; }
174+
#endif
169175
public static System.Collections.Generic.IEnumerable<TSource> TakeWhile<TSource>(this System.Collections.Generic.IEnumerable<TSource> source, System.Func<TSource, bool> predicate) { throw null; }
170176
public static System.Collections.Generic.IEnumerable<TSource> TakeWhile<TSource>(this System.Collections.Generic.IEnumerable<TSource> source, System.Func<TSource, int, bool> predicate) { throw null; }
171177
public static System.Linq.IOrderedEnumerable<TSource> ThenBy<TSource, TKey>(this System.Linq.IOrderedEnumerable<TSource> source, System.Func<TSource, TKey> keySelector) { throw null; }

src/System.Linq/ref/System.Linq.csproj

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
<Import Project="$([MSBuild]::GetDirectoryNameOfFileAbove($(MSBuildThisFileDirectory), dir.props))\dir.props" />
44
<PropertyGroup>
55
<OutputType>Library</OutputType>
6-
<AssemblyVersion>4.2.0.0</AssemblyVersion>
6+
<AssemblyVersion>4.3.0.0</AssemblyVersion>
77
<NuGetTargetMoniker>.NETStandard,Version=v1.6</NuGetTargetMoniker>
88
<DefineConstants Condition="'$(TargetGroup)' == 'netcoreapp1.1'">$(DefineConstants);netcoreapp11</DefineConstants>
99
</PropertyGroup>

src/System.Linq/src/System.Linq.csproj

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
<PropertyGroup>
55
<ProjectGuid>{CA488507-3B6E-4494-B7BE-7B4EEEB2C4D1}</ProjectGuid>
66
<AssemblyName>System.Linq</AssemblyName>
7-
<AssemblyVersion>4.2.0.0</AssemblyVersion>
7+
<AssemblyVersion>4.3.0.0</AssemblyVersion>
88
<RootNamespace>System.Linq</RootNamespace>
99
<IsPartialFacadeAssembly Condition="'$(TargetGroup)' == 'net461'">true</IsPartialFacadeAssembly>
1010
<!-- The following line needs to be removed once we have a targeting pack for 4.6.3 -->
@@ -32,6 +32,9 @@
3232
<Compile Include="$(CommonPath)\System\Collections\Generic\LargeArrayBuilder.cs">
3333
<Link>System\Collections\Generic\LargeArrayBuilder.cs</Link>
3434
</Compile>
35+
<Compile Include="$(CommonPath)\System\Collections\Generic\WeakCircularBuffer.cs">
36+
<Link>System\Collections\Generic\WeakCircularBuffer.cs</Link>
37+
</Compile>
3538
<Compile Include="$(CommonPath)\System\Diagnostics\CodeAnalysis\ExcludeFromCodeCoverageAttribute.cs">
3639
<Link>System\Diagnostics\CodeAnalysis\ExcludeFromCodeCoverageAttribute.cs</Link>
3740
</Compile>

0 commit comments

Comments
 (0)