Skip to content

Commit cbc0f55

Browse files
committed
[Last] add TryLast extension.
1 parent be6edc6 commit cbc0f55

File tree

4 files changed

+153
-123
lines changed

4 files changed

+153
-123
lines changed
Lines changed: 34 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
1-
using System;
2-
using System.Linq;
1+
using System.Linq;
32
using FluentAssertions;
43
using Xunit;
54

@@ -10,58 +9,78 @@ public class RefLastTests
109
[Fact]
1110
public void ShouldReturnLastElement()
1211
{
13-
var array = Enumerable.Range(0, 10)
12+
int last = default;
13+
Enumerable.Range(0, 10)
1414
.ToArray()
1515
.ToRefStructEnumerable()
16-
.Last()
16+
.TryLast(ref last)
1717
.Should()
18-
.Be(9);
18+
.BeTrue();
19+
last.Should().Be(9);
1920
}
2021

2122
[Fact]
2223
public void ShouldReturnLastElementZeroAlloc()
2324
{
25+
int last = default;
2426
var array = Enumerable.Range(0, 10)
2527
.ToArray()
2628
.ToRefStructEnumerable()
27-
.Last(x => x)
29+
.TryLast(ref last, x=>x)
2830
.Should()
29-
.Be(9);
31+
.BeTrue();
32+
last.Should().Be(9);
3033
}
3134

3235
[Fact]
3336
public void ShouldThrowException()
3437
{
35-
Assert.Throws<Exception>(() => StructEnumerable.Empty<int>().ToArray().ToRefStructEnumerable().Last());
38+
int last = default;
39+
StructEnumerable.Empty<int>()
40+
.ToArray()
41+
.ToRefStructEnumerable()
42+
.TryLast(ref last)
43+
.Should()
44+
.BeFalse();
3645
}
3746

3847
[Fact]
3948
public void ShouldThrowExceptionZeroAlloc()
4049
{
41-
Assert.Throws<Exception>(() => StructEnumerable.Empty<int>().ToArray().ToRefStructEnumerable().Last(x => x));
50+
int last = default;
51+
StructEnumerable.Empty<int>()
52+
.ToArray()
53+
.ToRefStructEnumerable()
54+
.TryLast(ref last, x=>x)
55+
.Should()
56+
.BeFalse();
4257
}
4358

4459

4560
[Fact]
4661
public void ShouldReturnLastElementWithFunc()
4762
{
48-
var array = Enumerable.Range(0, 10)
63+
int last = default;
64+
Enumerable.Range(0, 10)
4965
.ToArray()
5066
.ToRefStructEnumerable()
51-
.Last(x => x > 5)
67+
.TryLast(x=> x > 5, ref last)
5268
.Should()
53-
.Be(9);
69+
.BeTrue();
70+
last.Should().Be(9);
5471
}
5572

5673
[Fact]
5774
public void ShouldReturnLastElementWithFuncZeroAlloc()
5875
{
59-
var array = Enumerable.Range(0, 10)
76+
int last = default;
77+
Enumerable.Range(0, 10)
6078
.ToArray()
6179
.ToRefStructEnumerable()
62-
.Last(x => x > 5, x => x)
80+
.TryLast(x => x > 5, ref last, x=> x)
6381
.Should()
64-
.Be(9);
82+
.BeTrue();
83+
last.Should().Be(9);
6584
}
6685
}
6786
}
Lines changed: 24 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
// ReSharper disable once CheckNamespace
22

33
using System;
4-
using System.Buffers;
54
using System.Runtime.CompilerServices;
65

76
// ReSharper disable once CheckNamespace
@@ -10,108 +9,102 @@ namespace StructLinq
109
public static partial class StructEnumerable
1110
{
1211
[MethodImpl(MethodImplOptions.AggressiveInlining)]
13-
private static ref T RefInnerLast<T, TEnumerator>(TEnumerator enumerator)
12+
private static bool RefTryInnerLast<T, TEnumerator>(ref TEnumerator enumerator, ref T last)
1413
where TEnumerator : struct, IRefStructEnumerator<T>
1514
{
1615
if (enumerator.MoveNext())
1716
{
18-
var rent = ArrayPool<T>.Shared.Rent(1);
1917
do
2018
{
21-
ref var elt = ref rent[0];
2219
ref var current = ref enumerator.Current;
23-
elt = current;
20+
last = current;
2421
}
2522
while (enumerator.MoveNext());
2623
enumerator.Dispose();
27-
ref var result = ref rent[0];
28-
ArrayPool<T>.Shared.Return(rent);
29-
return ref result;
24+
return true;
3025
}
31-
enumerator.Dispose();
32-
throw new Exception("No Elements");
26+
return false;
3327
}
3428

3529
[MethodImpl(MethodImplOptions.AggressiveInlining)]
36-
private static ref T RefInnerLast<T, TEnumerator>(TEnumerator enumerator, Func<T, bool> predicate)
30+
private static bool TryRefInnerLast<T, TEnumerator>(ref TEnumerator enumerator, Func<T, bool> predicate, ref T last)
3731
where TEnumerator : struct, IRefStructEnumerator<T>
3832
{
39-
var rent = ArrayPool<T>.Shared.Rent(1);
33+
bool found = false;
4034
while (enumerator.MoveNext())
4135
{
4236
ref var current = ref enumerator.Current;
4337
if (predicate(current))
4438
{
45-
ref var elt = ref rent[0];
46-
elt = current;
39+
last = current;
40+
found = true;
4741
}
4842
}
4943
enumerator.Dispose();
50-
ref var result = ref rent[0];
51-
ArrayPool<T>.Shared.Return(rent);
52-
return ref result;
44+
return found;
5345
}
5446

5547
[MethodImpl(MethodImplOptions.AggressiveInlining)]
56-
private static ref T RefInnerLast<T, TEnumerator, TFunc>(TEnumerator enumerator, ref TFunc predicate)
48+
private static bool TryRefInnerLast<T, TEnumerator, TFunc>(ref TEnumerator enumerator, ref TFunc predicate, ref T last)
5749
where TEnumerator : struct, IRefStructEnumerator<T>
5850
where TFunc : struct, IInFunction<T, bool>
5951
{
52+
bool found = false;
6053
while (enumerator.MoveNext())
6154
{
6255
ref var current = ref enumerator.Current;
6356
if (predicate.Eval(in current))
6457
{
65-
enumerator.Dispose();
66-
return ref current;
58+
last = current;
59+
found = true;
6760
}
6861
}
6962
enumerator.Dispose();
70-
throw new Exception("No Match");
63+
return found;
7164
}
7265

7366
[MethodImpl(MethodImplOptions.AggressiveInlining)]
74-
public static ref T Last<T, TEnumerable, TEnumerator>(this TEnumerable enumerable, Func<TEnumerable, IRefStructEnumerable<T, TEnumerator>> _)
67+
public static bool TryLast<T, TEnumerable, TEnumerator>(this TEnumerable enumerable, ref T last, Func<TEnumerable, IRefStructEnumerable<T, TEnumerator>> _)
7568
where TEnumerator : struct, IRefStructEnumerator<T>
7669
where TEnumerable : IRefStructEnumerable<T, TEnumerator>
7770
{
7871
var enumerator = enumerable.GetEnumerator();
79-
return ref RefInnerLast<T, TEnumerator>(enumerator);
72+
return RefTryInnerLast<T, TEnumerator>(ref enumerator, ref last);
8073
}
8174

8275
[MethodImpl(MethodImplOptions.AggressiveInlining)]
83-
public static ref T Last<T, TEnumerator>(this IRefStructEnumerable<T, TEnumerator> enumerable)
76+
public static bool TryLast<T, TEnumerator>(this IRefStructEnumerable<T, TEnumerator> enumerable, ref T last)
8477
where TEnumerator : struct, IRefStructEnumerator<T>
8578
{
8679
var enumerator = enumerable.GetEnumerator();
87-
return ref RefInnerLast<T, TEnumerator>(enumerator);
80+
return RefTryInnerLast<T, TEnumerator>(ref enumerator, ref last);
8881
}
8982

9083
[MethodImpl(MethodImplOptions.AggressiveInlining)]
91-
public static ref T Last<T, TEnumerable, TEnumerator>(this TEnumerable enumerable, Func<T, bool> predicate, Func<TEnumerable, IRefStructEnumerable<T, TEnumerator>> _)
84+
public static bool TryLast<T, TEnumerable, TEnumerator>(this TEnumerable enumerable, Func<T, bool> predicate, ref T last, Func<TEnumerable, IRefStructEnumerable<T, TEnumerator>> _)
9285
where TEnumerator : struct, IRefStructEnumerator<T>
9386
where TEnumerable : IRefStructEnumerable<T, TEnumerator>
9487
{
9588
var enumerator = enumerable.GetEnumerator();
96-
return ref RefInnerLast(enumerator, predicate);
89+
return TryRefInnerLast(ref enumerator, predicate, ref last);
9790
}
9891

9992
[MethodImpl(MethodImplOptions.AggressiveInlining)]
100-
public static ref T Last<T, TEnumerator>(this IRefStructEnumerable<T, TEnumerator> enumerable, Func<T, bool> predicate)
93+
public static bool TryLast<T, TEnumerator>(this IRefStructEnumerable<T, TEnumerator> enumerable, Func<T, bool> predicate, ref T last)
10194
where TEnumerator : struct, IRefStructEnumerator<T>
10295
{
10396
var enumerator = enumerable.GetEnumerator();
104-
return ref RefInnerLast<T, TEnumerator>(enumerator, predicate);
97+
return TryRefInnerLast(ref enumerator, predicate, ref last);
10598
}
10699

107100
[MethodImpl(MethodImplOptions.AggressiveInlining)]
108-
public static ref T Last<T, TEnumerable, TEnumerator, TFunc>(this TEnumerable enumerable, ref TFunc predicate, Func<TEnumerable, IRefStructEnumerable<T, TEnumerator>> _)
101+
public static bool TryLast<T, TEnumerable, TEnumerator, TFunc>(this TEnumerable enumerable, ref TFunc predicate, ref T last, Func<TEnumerable, IRefStructEnumerable<T, TEnumerator>> _)
109102
where TEnumerator : struct, IRefStructEnumerator<T>
110103
where TEnumerable : IRefStructEnumerable<T, TEnumerator>
111104
where TFunc : struct, IInFunction<T, bool>
112105
{
113106
var enumerator = enumerable.GetEnumerator();
114-
return ref RefInnerLast<T, TEnumerator, TFunc>(enumerator, ref predicate);
107+
return TryRefInnerLast(ref enumerator, ref predicate, ref last);
115108
}
116109
}
117110
}

src/StructLinq/Last/StructEnumerable .LastOrDefault.cs

Lines changed: 15 additions & 58 deletions
Original file line numberDiff line numberDiff line change
@@ -8,74 +8,25 @@ namespace StructLinq
88
{
99
public static partial class StructEnumerable
1010
{
11-
[MethodImpl(MethodImplOptions.AggressiveInlining)]
12-
private static T InnerLastOrDefault<T, TEnumerator>(ref TEnumerator enumerator)
13-
where TEnumerator : struct, IStructEnumerator<T>
14-
{
15-
if (enumerator.MoveNext())
16-
{
17-
T result;
18-
do
19-
{
20-
result = enumerator.Current;
21-
22-
}
23-
while (enumerator.MoveNext());
24-
enumerator.Dispose();
25-
return result;
26-
}
27-
enumerator.Dispose();
28-
return default;
29-
}
30-
31-
[MethodImpl(MethodImplOptions.AggressiveInlining)]
32-
private static T InnerLastOrDefault<T, TEnumerator>(ref TEnumerator enumerator, Func<T, bool> predicate)
33-
where TEnumerator : struct, IStructEnumerator<T>
34-
{
35-
T result = default;
36-
while (enumerator.MoveNext())
37-
{
38-
var current = enumerator.Current;
39-
if (predicate(current))
40-
result = current;
41-
}
42-
enumerator.Dispose();
43-
return result;
44-
}
45-
46-
[MethodImpl(MethodImplOptions.AggressiveInlining)]
47-
private static T InnerLastOrDefault<T, TEnumerator, TFunc>(ref TEnumerator enumerator, ref TFunc predicate)
48-
where TEnumerator : struct, IStructEnumerator<T>
49-
where TFunc : struct, IFunction<T, bool>
50-
{
51-
while (enumerator.MoveNext())
52-
{
53-
var current = enumerator.Current;
54-
if (predicate.Eval(current))
55-
{
56-
enumerator.Dispose();
57-
return current;
58-
}
59-
}
60-
enumerator.Dispose();
61-
return default;
62-
}
63-
6411
[MethodImpl(MethodImplOptions.AggressiveInlining)]
6512
public static T LastOrDefault<T, TEnumerable, TEnumerator>(this TEnumerable enumerable, Func<TEnumerable, IStructEnumerable<T, TEnumerator>> _)
6613
where TEnumerator : struct, IStructEnumerator<T>
6714
where TEnumerable : IStructEnumerable<T, TEnumerator>
6815
{
6916
var enumerator = enumerable.GetEnumerator();
70-
return InnerLastOrDefault<T, TEnumerator>(ref enumerator);
17+
T last = default;
18+
TryInnerLast(ref enumerator, ref last);
19+
return last;
7120
}
7221

7322
[MethodImpl(MethodImplOptions.AggressiveInlining)]
7423
public static T LastOrDefault<T, TEnumerator>(this IStructEnumerable<T, TEnumerator> enumerable)
7524
where TEnumerator : struct, IStructEnumerator<T>
7625
{
7726
var enumerator = enumerable.GetEnumerator();
78-
return InnerLastOrDefault<T, TEnumerator>(ref enumerator);
27+
T last = default;
28+
TryInnerLast(ref enumerator, ref last);
29+
return last;
7930
}
8031

8132
[MethodImpl(MethodImplOptions.AggressiveInlining)]
@@ -84,15 +35,19 @@ public static T LastOrDefault<T, TEnumerable, TEnumerator>(this TEnumerable enum
8435
where TEnumerable : IStructEnumerable<T, TEnumerator>
8536
{
8637
var enumerator = enumerable.GetEnumerator();
87-
return InnerLastOrDefault(ref enumerator, predicate);
38+
T last = default;
39+
TryInnerLast(ref enumerator, predicate, ref last);
40+
return last;
8841
}
8942

9043
[MethodImpl(MethodImplOptions.AggressiveInlining)]
9144
public static T LastOrDefault<T, TEnumerator>(this IStructEnumerable<T, TEnumerator> enumerable, Func<T, bool> predicate)
9245
where TEnumerator : struct, IStructEnumerator<T>
9346
{
9447
var enumerator = enumerable.GetEnumerator();
95-
return InnerLastOrDefault(ref enumerator, predicate);
48+
T last = default;
49+
TryInnerLast(ref enumerator, predicate, ref last);
50+
return last;
9651
}
9752

9853
[MethodImpl(MethodImplOptions.AggressiveInlining)]
@@ -102,7 +57,9 @@ public static T LastOrDefault<T, TEnumerable, TEnumerator, TFunc>(this TEnumerab
10257
where TFunc : struct, IFunction<T, bool>
10358
{
10459
var enumerator = enumerable.GetEnumerator();
105-
return InnerLastOrDefault<T, TEnumerator, TFunc>(ref enumerator, ref predicate);
60+
T last = default;
61+
TryInnerLast(ref enumerator, ref predicate, ref last);
62+
return last;
10663
}
10764
}
10865
}

0 commit comments

Comments
 (0)