Skip to content

Commit 3464b60

Browse files
authored
Expose Index and Range types (dotnet#20899)
* Expose Index and Range types * Address Review Comments * Address more feedback * Addressing more comments
1 parent 7c55615 commit 3464b60

File tree

6 files changed

+139
-9
lines changed

6 files changed

+139
-9
lines changed

src/System.Private.CoreLib/shared/System.Private.CoreLib.Shared.projitems

+3-1
Original file line numberDiff line numberDiff line change
@@ -222,6 +222,7 @@
222222
<Compile Include="$(MSBuildThisFileDirectory)System\IEquatable.cs" />
223223
<Compile Include="$(MSBuildThisFileDirectory)System\IFormatProvider.cs" />
224224
<Compile Include="$(MSBuildThisFileDirectory)System\IFormattable.cs" />
225+
<Compile Include="$(MSBuildThisFileDirectory)System\Index.cs" />
225226
<Compile Include="$(MSBuildThisFileDirectory)System\IndexOutOfRangeException.cs" />
226227
<Compile Include="$(MSBuildThisFileDirectory)System\InsufficientExecutionStackException.cs" />
227228
<Compile Include="$(MSBuildThisFileDirectory)System\InsufficientMemoryException.cs" />
@@ -308,6 +309,7 @@
308309
<Compile Include="$(MSBuildThisFileDirectory)System\PlatformNotSupportedException.cs" />
309310
<Compile Include="$(MSBuildThisFileDirectory)System\Progress.cs" />
310311
<Compile Include="$(MSBuildThisFileDirectory)System\Random.cs" />
312+
<Compile Include="$(MSBuildThisFileDirectory)System\Range.cs" />
311313
<Compile Include="$(MSBuildThisFileDirectory)System\RankException.cs" />
312314
<Compile Include="$(MSBuildThisFileDirectory)System\ReadOnlySpan.cs" />
313315
<Compile Include="$(MSBuildThisFileDirectory)System\ReadOnlySpan.Fast.cs" />
@@ -631,7 +633,7 @@
631633
<Compile Include="$(MSBuildThisFileDirectory)System\Threading\SemaphoreFullException.cs" />
632634
<Compile Include="$(MSBuildThisFileDirectory)System\Threading\SemaphoreSlim.cs" />
633635
<Compile Include="$(MSBuildThisFileDirectory)System\Threading\SendOrPostCallback.cs" />
634-
<Compile Include="$(MSBuildThisFileDirectory)System\Threading\SpinLock.cs" />
636+
<Compile Include="$(MSBuildThisFileDirectory)System\Threading\SpinLock.cs" />
635637
<Compile Include="$(MSBuildThisFileDirectory)System\Threading\SpinWait.cs" />
636638
<Compile Include="$(MSBuildThisFileDirectory)System\Threading\SynchronizationLockException.cs" />
637639
<Compile Include="$(MSBuildThisFileDirectory)System\Threading\ThreadLocal.cs" />
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
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+
namespace System
6+
{
7+
public readonly struct Index : IEquatable<Index>
8+
{
9+
private readonly int _value;
10+
11+
public Index(int value, bool fromEnd)
12+
{
13+
if (value < 0)
14+
{
15+
ThrowHelper.ThrowValueArgumentOutOfRange_NeedNonNegNumException();
16+
}
17+
18+
_value = fromEnd ? ~value : value;
19+
}
20+
21+
public int Value => _value < 0 ? ~_value : _value;
22+
public bool FromEnd => _value < 0;
23+
public override bool Equals(object value) => value is Index && _value == ((Index)value)._value;
24+
public bool Equals (Index other) => _value == other._value;
25+
26+
public override int GetHashCode()
27+
{
28+
return _value;
29+
}
30+
31+
public override string ToString()
32+
{
33+
string str = Value.ToString();
34+
return FromEnd ? "^" + str : str;
35+
}
36+
37+
public static implicit operator Index(int value)
38+
=> new Index(value, fromEnd: false);
39+
}
40+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
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+
namespace System
6+
{
7+
public readonly struct Range : IEquatable<Range>
8+
{
9+
public Index Start { get; }
10+
public Index End { get; }
11+
12+
private Range(Index start, Index end)
13+
{
14+
Start = start;
15+
End = end;
16+
}
17+
18+
public override bool Equals(object value)
19+
{
20+
if (value is Range)
21+
{
22+
Range r = (Range)value;
23+
return r.Start.Equals(Start) && r.End.Equals(End);
24+
}
25+
26+
return false;
27+
}
28+
29+
public bool Equals (Range other) => other.Start.Equals(Start) && other.End.Equals(End);
30+
31+
public override int GetHashCode()
32+
{
33+
return HashCode.Combine(Start.GetHashCode(), End.GetHashCode());
34+
}
35+
36+
public override string ToString()
37+
{
38+
return Start + ".." + End;
39+
}
40+
41+
public static Range Create(Index start, Index end) => new Range(start, end);
42+
public static Range FromStart(Index start) => new Range(start, new Index(0, fromEnd: true));
43+
public static Range ToEnd(Index end) => new Range(new Index(0, fromEnd: false), end);
44+
public static Range All() => new Range(new Index(0, fromEnd: false), new Index(0, fromEnd: true));
45+
}
46+
}

src/System.Private.CoreLib/shared/System/ReadOnlySpan.Fast.cs

+18
Original file line numberDiff line numberDiff line change
@@ -154,6 +154,24 @@ public ref readonly T this[int index]
154154
#endif
155155
}
156156

157+
public ref readonly T this[Index index]
158+
{
159+
get
160+
{
161+
return ref this [index.FromEnd ? _length - index.Value : index.Value];
162+
}
163+
}
164+
165+
public ReadOnlySpan<T> this[Range range]
166+
{
167+
get
168+
{
169+
int start = range.Start.FromEnd ? _length - range.Start.Value : range.Start.Value;
170+
int end = range.End.FromEnd ? _length - range.End.Value : range.End.Value;
171+
return Slice(start, end - start);
172+
}
173+
}
174+
157175
/// <summary>
158176
/// Returns a reference to the 0th element of the Span. If the Span is empty, returns null reference.
159177
/// It can be used for pinning and is required to support the use of span within a fixed statement.

src/System.Private.CoreLib/shared/System/Span.Fast.cs

+18
Original file line numberDiff line numberDiff line change
@@ -159,6 +159,24 @@ public ref T this[int index]
159159
#endif
160160
}
161161

162+
public ref T this[Index index]
163+
{
164+
get
165+
{
166+
return ref this [index.FromEnd ? _length - index.Value : index.Value];
167+
}
168+
}
169+
170+
public Span<T> this[Range range]
171+
{
172+
get
173+
{
174+
int start = range.Start.FromEnd ? _length - range.Start.Value : range.Start.Value;
175+
int end = range.End.FromEnd ? _length - range.End.Value : range.End.Value;
176+
return Slice(start, end - start);
177+
}
178+
}
179+
162180
/// <summary>
163181
/// Returns a reference to the 0th element of the Span. If the Span is empty, returns null reference.
164182
/// It can be used for pinning and is required to support the use of span within a fixed statement.

src/System.Private.CoreLib/src/System/ThrowHelper.cs

+14-8
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,8 @@
44

55

66
// This file defines an internal class used to throw exceptions in BCL code.
7-
// The main purpose is to reduce code size.
8-
//
7+
// The main purpose is to reduce code size.
8+
//
99
// The old way to throw an exception generates quite a lot IL code and assembly code.
1010
// Following is an example:
1111
// C# source
@@ -17,10 +17,10 @@
1717
// IL_0012: newobj instance void System.ArgumentNullException::.ctor(string,string)
1818
// IL_0017: throw
1919
// which is 21bytes in IL.
20-
//
20+
//
2121
// So we want to get rid of the ldstr and call to Environment.GetResource in IL.
2222
// In order to do that, I created two enums: ExceptionResource, ExceptionArgument to represent the
23-
// argument name and resource name in a small integer. The source code will be changed to
23+
// argument name and resource name in a small integer. The source code will be changed to
2424
// ThrowHelper.ThrowArgumentNullException(ExceptionArgument.key, ExceptionResource.ArgumentNull_Key);
2525
//
2626
// The IL code will be 7 bytes.
@@ -29,11 +29,11 @@
2929
// IL_000a: call void System.ThrowHelper::ThrowArgumentNullException(valuetype System.ExceptionArgument)
3030
// IL_000f: ldarg.0
3131
//
32-
// This will also reduce the Jitted code size a lot.
32+
// This will also reduce the Jitted code size a lot.
33+
//
34+
// It is very important we do this for generic classes because we can easily generate the same code
35+
// multiple times for different instantiation.
3336
//
34-
// It is very important we do this for generic classes because we can easily generate the same code
35-
// multiple times for different instantiation.
36-
//
3737

3838
using System.Collections.Generic;
3939
using System.Diagnostics;
@@ -87,6 +87,12 @@ internal static void ThrowIndexArgumentOutOfRange_NeedNonNegNumException()
8787
ExceptionResource.ArgumentOutOfRange_NeedNonNegNum);
8888
}
8989

90+
internal static void ThrowValueArgumentOutOfRange_NeedNonNegNumException()
91+
{
92+
throw GetArgumentOutOfRangeException(ExceptionArgument.value,
93+
ExceptionResource.ArgumentOutOfRange_NeedNonNegNum);
94+
}
95+
9096
internal static void ThrowLengthArgumentOutOfRange_ArgumentOutOfRange_NeedNonNegNum()
9197
{
9298
throw GetArgumentOutOfRangeException(ExceptionArgument.length,

0 commit comments

Comments
 (0)