Skip to content
This repository has been archived by the owner on Jan 23, 2023. It is now read-only.
/ corefx Public archive

Index and Ranges Changes #35003

Closed
wants to merge 4 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
8 changes: 8 additions & 0 deletions src/System.Memory/ref/System.Memory.cs
Original file line number Diff line number Diff line change
Expand Up @@ -12,21 +12,29 @@ public static partial class MemoryExtensions
public static System.ReadOnlyMemory<char> AsMemory(this string text) { throw null; }
public static System.ReadOnlyMemory<char> AsMemory(this string text, int start) { throw null; }
public static System.ReadOnlyMemory<char> AsMemory(this string text, int start, int length) { throw null; }
public static System.ReadOnlyMemory<char> AsMemory(this string text, Index startIndex) { throw null; }
public static System.ReadOnlyMemory<char> AsMemory(this string text, Range range) { throw null; }
public static System.Memory<T> AsMemory<T>(this System.ArraySegment<T> segment) { throw null; }
public static System.Memory<T> AsMemory<T>(this System.ArraySegment<T> segment, int start) { throw null; }
public static System.Memory<T> AsMemory<T>(this System.ArraySegment<T> segment, int start, int length) { throw null; }
public static System.Memory<T> AsMemory<T>(this T[] array) { throw null; }
public static System.Memory<T> AsMemory<T>(this T[] array, int start) { throw null; }
public static System.Memory<T> AsMemory<T>(this T[] array, int start, int length) { throw null; }
public static System.Memory<T> AsMemory<T>(this T[] array, Index startIndex) { throw null; }
public static System.Memory<T> AsMemory<T>(this T[] array, Range range) { throw null; }
public static System.ReadOnlySpan<char> AsSpan(this string text) { throw null; }
public static System.ReadOnlySpan<char> AsSpan(this string text, int start) { throw null; }
public static System.ReadOnlySpan<char> AsSpan(this string text, int start, int length) { throw null; }
public static System.Span<T> AsSpan<T>(this System.ArraySegment<T> segment) { throw null; }
public static System.Span<T> AsSpan<T>(this System.ArraySegment<T> segment, int start) { throw null; }
public static System.Span<T> AsSpan<T>(this System.ArraySegment<T> segment, int start, int length) { throw null; }
public static Span<T> AsSpan<T>(this System.ArraySegment<T> segment, Index startIndex) { throw null; }
public static Span<T> AsSpan<T>(this System.ArraySegment<T> segment, Range range) { throw null; }
public static System.Span<T> AsSpan<T>(this T[] array) { throw null; }
public static System.Span<T> AsSpan<T>(this T[] array, int start) { throw null; }
public static System.Span<T> AsSpan<T>(this T[] array, int start, int length) { throw null; }
public static Span<T> AsSpan<T>(this T[] array, Index startIndex) { throw null; }
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If we are adding Index/Range overloads to AsSpan for array/segment, why not add it for string as well?

public static System.ReadOnlySpan<char> AsSpan(this string text, int start, int length) { throw null; }

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a good question, I don't think this was discussed and I don't see a strong reason why we shouldn't have it. but in general it is easy to achieve the same with the current APIs String.AsSpan().Slice(Index/Range)

@terrajobst what you think about this one?

public static Span<T> AsSpan<T>(this T[] array, Range range) { throw null; }
public static int BinarySearch<T>(this System.ReadOnlySpan<T> span, System.IComparable<T> comparable) { throw null; }
public static int BinarySearch<T>(this System.Span<T> span, System.IComparable<T> comparable) { throw null; }
public static int BinarySearch<T, TComparer>(this System.ReadOnlySpan<T> span, T value, TComparer comparer) where TComparer : System.Collections.Generic.IComparer<T> { throw null; }
Expand Down
100 changes: 100 additions & 0 deletions src/System.Memory/tests/Memory/IndexAndRange.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.

using Xunit;

namespace System.MemoryTests
{
public static partial class MemoryTests
{
[Fact]
public static void SlicingUsingIndexAndRangeTest()
{
Range range;
int[] a = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
Memory<int> memory = a;
ReadOnlyMemory<int> roMemory = a;

for (int i = 0; i < a.Length; i++)
{
range = new Range(Index.FromStart(i), Index.FromEnd(0));
Assert.Equal(memory.Slice(i, a.Length - i), memory[range]);
Assert.Equal(roMemory.Slice(i, a.Length - i), roMemory[range]);

Assert.Equal(memory.Slice(i), memory.Slice(Index.FromStart(i)));
Assert.Equal(roMemory.Slice(i), roMemory.Slice(Index.FromStart(i)));

Assert.Equal(memory.Slice(i, a.Length - i), memory.Slice(range));
Assert.Equal(roMemory.Slice(i, a.Length - i), roMemory.Slice(range));
}

range = new Range(Index.FromStart(0), Index.FromStart(a.Length + 1));
Assert.Throws<ArgumentOutOfRangeException>(() => { Memory<int> m = memory[range]; });
Assert.Throws<ArgumentOutOfRangeException>(() => { ReadOnlyMemory<int> m = roMemory[range]; });
}

[Fact]
public static void MemoryExtensionsTest()
{
Range range;
int[] a = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
Memory<int> memory = a;
ReadOnlyMemory<int> roMemory = a;

for (int i = 0; i < a.Length; i++)
{
Assert.Equal(memory.Slice(i), a.AsMemory(Index.FromStart(i)));

range = new Range(Index.FromStart(i), Index.FromEnd(0));
Assert.Equal(memory.Slice(i, a.Length - i), a.AsMemory(range));
}

range = new Range(Index.FromStart(0), Index.FromStart(a.Length + 1));
Assert.Throws<ArgumentOutOfRangeException>(() => { Memory<int> m = a.AsMemory(Index.FromStart(a.Length + 1)); });
Assert.Throws<ArgumentOutOfRangeException>(() => { Memory<int> m = a.AsMemory(range); });

string s = "0123456789ABCDEF";
ReadOnlyMemory<char> roStringMemory = s.AsMemory();

for (int i = 0; i < s.Length; i++)
{
Assert.Equal(roStringMemory.Slice(i), s.AsMemory(Index.FromStart(i)));

range = new Range(Index.FromStart(i), Index.FromEnd(0));
Assert.Equal(roStringMemory.Slice(i, s.Length - i), s.AsMemory(range));
}

range = new Range(Index.FromStart(0), Index.FromStart(s.Length + 1));
Assert.Throws<ArgumentOutOfRangeException>(() => { ReadOnlyMemory<char> m = s.AsMemory(Index.FromStart(s.Length + 1)); });
Assert.Throws<ArgumentOutOfRangeException>(() => { ReadOnlyMemory<char> m = s.AsMemory(range); });

Span<int> span = a.AsSpan();

for (int i = 0; i < a.Length; i++)
{
Assert.True(span.Slice(i) == a.AsSpan(Index.FromStart(i)));

range = new Range(Index.FromStart(i), Index.FromEnd(0));
Assert.True(span.Slice(i, span.Length - i) == a.AsSpan(range));
}

range = new Range(Index.FromStart(0), Index.FromStart(a.Length + 1));
Assert.Throws<ArgumentOutOfRangeException>(() => { Span<int> sp = a.AsSpan(Index.FromStart(a.Length + 1)); });
Assert.Throws<ArgumentOutOfRangeException>(() => { Span<int> sp = a.AsSpan(range); });

ArraySegment<int> segment = new ArraySegment<int>(a);
for (int i = 0; i < a.Length; i++)
{
Assert.True(span.Slice(i) == segment.AsSpan(Index.FromStart(i)));

range = new Range(Index.FromStart(i), Index.FromEnd(0));
Assert.True(span.Slice(i, span.Length - i) == segment.AsSpan(range));
}

range = new Range(Index.FromStart(0), Index.FromStart(a.Length + 1));
Assert.Throws<ArgumentOutOfRangeException>(() => { Span<int> sp = segment.AsSpan(Index.FromStart(a.Length + 1)); });
Assert.Throws<ArgumentOutOfRangeException>(() => { Span<int> sp = segment.AsSpan(range); });
}
}
}
34 changes: 30 additions & 4 deletions src/System.Memory/tests/Span/Indexer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -32,18 +32,44 @@ public static void IndexerWithIndexTest()
public static void IndexerWithRangeTest()
{
ReadOnlySpan<char> span = "Hello".AsSpan();
ReadOnlySpan<char> sliced = span[Range.Create(new Index(1, fromEnd: false), new Index(1, fromEnd: true))];
ReadOnlySpan<char> sliced = span[new Range(new Index(1, fromEnd: false), new Index(1, fromEnd: true))];
Assert.True(span.Slice(1, 3) == sliced);

Assert.Throws<ArgumentOutOfRangeException>(() =>
{ ReadOnlySpan<char> s = "Hello".AsSpan()[Range.Create(new Index(1, fromEnd: true), new Index(1, fromEnd: false))]; });
{ ReadOnlySpan<char> s = "Hello".AsSpan()[new Range(new Index(1, fromEnd: true), new Index(1, fromEnd: false))]; });

Span<char> span1 = new Span<char>(new char [] { 'H', 'e', 'l', 'l', 'o'});
Span<char> sliced1 = span1[Range.Create(new Index(2, fromEnd: false), new Index(1, fromEnd: true))];
Span<char> sliced1 = span1[new Range(new Index(2, fromEnd: false), new Index(1, fromEnd: true))];
Assert.True(span1.Slice(2, 2) == sliced1);

Assert.Throws<ArgumentOutOfRangeException>(() =>
{ Span<char> s = new Span<char>(new char [] { 'H', 'i' })[Range.Create(new Index(0, fromEnd: true), new Index(1, fromEnd: false))]; });
{ Span<char> s = new Span<char>(new char [] { 'H', 'i' })[new Range(new Index(0, fromEnd: true), new Index(1, fromEnd: false))]; });
}

[Fact]
public static void SlicingUsingIndexAndRangeTest()
{
Range range;
string s = "0123456789ABCDEF";
ReadOnlySpan<char> roSpan = s.AsSpan();
Span<char> span = new Span<char>(s.ToCharArray());

for (int i = 0; i < span.Length; i++)
{
Assert.True(span.Slice(i) == span.Slice(Index.FromStart(i)));
Assert.True(span.Slice(span.Length - i - 1) == span.Slice(Index.FromEnd(i + 1)));

Assert.True(roSpan.Slice(i) == roSpan.Slice(Index.FromStart(i)));
Assert.True(roSpan.Slice(roSpan.Length - i - 1) == roSpan.Slice(Index.FromEnd(i + 1)));

range = new Range(Index.FromStart(i), Index.FromEnd(0));
Assert.True(span.Slice(i, span.Length - i) == span.Slice(range));
Assert.True(roSpan.Slice(i, roSpan.Length - i) == roSpan.Slice(range));
}

range = new Range(Index.FromStart(0), Index.FromStart(span.Length + 1));
Assert.Throws<ArgumentOutOfRangeException>(() => new Span<char>(s.ToCharArray()).Slice(range));
Assert.Throws<ArgumentOutOfRangeException>(() => s.AsSpan().Slice(range));
}
}
}
1 change: 1 addition & 0 deletions src/System.Memory/tests/System.Memory.Tests.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -173,6 +173,7 @@
<Compile Include="Memory\Equality.cs" />
<Compile Include="Memory\GetHashCode.cs" />
<Compile Include="Memory\ImplicitConversion.cs" />
<Compile Include="Memory\IndexAndRange.cs" />
<Compile Include="Memory\MemoryManager.cs" />
<Compile Include="Memory\Pin.cs" />
<Compile Include="Memory\Slice.cs" />
Expand Down
44 changes: 38 additions & 6 deletions src/System.Runtime/ref/System.Runtime.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1458,9 +1458,14 @@ public partial interface IFormattable
public readonly partial struct Index : System.IEquatable<System.Index>
{
private readonly int _dummyPrimitive;
public Index(int value, bool fromEnd) { throw null; }
public bool FromEnd { get { throw null; } }
public Index(int value, bool fromEnd = false) { throw null; }
public bool IsFromEnd { get { throw null; } }
public int Value { get { throw null; } }
public static Index Start { get { throw null; } }
public static Index End { get { throw null; } }
public static Index FromStart(int value) { throw null; }
public static Index FromEnd(int value) { throw null; }
public int GetOffset(int length) { throw null; }
public bool Equals(System.Index other) { throw null; }
public override bool Equals(object value) { throw null; }
public override int GetHashCode() { throw null; }
Expand Down Expand Up @@ -1747,6 +1752,9 @@ public partial class MemberAccessException : System.SystemException
public System.Buffers.MemoryHandle Pin() { throw null; }
public System.Memory<T> Slice(int start) { throw null; }
public System.Memory<T> Slice(int start, int length) { throw null; }
public System.Memory<T> Slice(System.Index startIndex) { throw null; }
public System.Memory<T> Slice(System.Range range) { throw null; }
public System.Memory<T> this[System.Range range] { get { throw null; } }
public T[] ToArray() { throw null; }
public override string ToString() { throw null; }
public bool TryCopyTo(System.Memory<T> destination) { throw null; }
Expand Down Expand Up @@ -1973,14 +1981,24 @@ public partial class PlatformNotSupportedException : System.NotSupportedExceptio
private readonly int _dummyPrimitive;
public System.Index End { get { throw null; } }
public System.Index Start { get { throw null; } }
public static System.Range All() { throw null; }
public static System.Range Create(System.Index start, System.Index end) { throw null; }
public Range(System.Index start, System.Index end) { throw null; }
public OffsetAndLength GetOffsetAndLength(int length) { throw null; }
public override bool Equals(object value) { throw null; }
public bool Equals(System.Range other) { throw null; }
public static System.Range FromStart(System.Index start) { throw null; }
public override int GetHashCode() { throw null; }
public static System.Range ToEnd(System.Index end) { throw null; }
public override string ToString() { throw null; }
public static System.Range StartAt(System.Index start) { throw null; }
public static System.Range EndAt(System.Index end) { throw null; }
public static System.Range All { get { throw null; } }

public readonly struct OffsetAndLength
{
private readonly int _dummyPrimitive;
public int Offset { get { throw null; } }
public int Length { get { throw null; } }
public OffsetAndLength(int offset, int length) { throw null; }
public void Deconstruct(out int offset, out int length) { throw null; }
}
}
public partial class RankException : System.SystemException
{
Expand Down Expand Up @@ -2010,6 +2028,9 @@ public partial class RankException : System.SystemException
public System.Buffers.MemoryHandle Pin() { throw null; }
public System.ReadOnlyMemory<T> Slice(int start) { throw null; }
public System.ReadOnlyMemory<T> Slice(int start, int length) { throw null; }
public System.ReadOnlyMemory<T> Slice(System.Index startIndex) { throw null; }
public System.ReadOnlyMemory<T> Slice(System.Range range) { throw null; }
public System.ReadOnlyMemory<T> this[System.Range range] { get { throw null; } }
public T[] ToArray() { throw null; }
public override string ToString() { throw null; }
public bool TryCopyTo(System.Memory<T> destination) { throw null; }
Expand Down Expand Up @@ -2044,6 +2065,8 @@ public partial class RankException : System.SystemException
public static bool operator !=(System.ReadOnlySpan<T> left, System.ReadOnlySpan<T> right) { throw null; }
public System.ReadOnlySpan<T> Slice(int start) { throw null; }
public System.ReadOnlySpan<T> Slice(int start, int length) { throw null; }
public System.ReadOnlySpan<T> Slice(System.Index startIndex) { throw null; }
public System.ReadOnlySpan<T> Slice(System.Range range) { throw null; }
public T[] ToArray() { throw null; }
public override string ToString() { throw null; }
public bool TryCopyTo(System.Span<T> destination) { throw null; }
Expand Down Expand Up @@ -2251,6 +2274,8 @@ public sealed partial class SerializableAttribute : System.Attribute
public static bool operator !=(System.Span<T> left, System.Span<T> right) { throw null; }
public System.Span<T> Slice(int start) { throw null; }
public System.Span<T> Slice(int start, int length) { throw null; }
public System.Span<T> Slice(System.Index startIndex) { throw null; }
public System.Span<T> Slice(System.Range range) { throw null; }
public T[] ToArray() { throw null; }
public override string ToString() { throw null; }
public bool TryCopyTo(System.Span<T> destination) { throw null; }
Expand Down Expand Up @@ -2292,6 +2317,10 @@ public sealed partial class String : System.Collections.Generic.IEnumerable<char
public unsafe String(sbyte* value, int startIndex, int length, System.Text.Encoding enc) { }
[System.Runtime.CompilerServices.IndexerName("Chars")]
public char this[int index] { get { throw null; } }
[System.Runtime.CompilerServices.IndexerName("Chars")]
public char this[System.Index index] { get { throw null; } }
[System.Runtime.CompilerServices.IndexerName("Chars")]
public System.String this[System.Range range] { get { throw null; } }
public int Length { get { throw null; } }
public object Clone() { throw null; }
public static int Compare(System.String strA, int indexA, System.String strB, int indexB, int length) { throw null; }
Expand Down Expand Up @@ -2424,6 +2453,8 @@ public sealed partial class String : System.Collections.Generic.IEnumerable<char
public bool StartsWith(System.String value, System.StringComparison comparisonType) { throw null; }
public System.String Substring(int startIndex) { throw null; }
public System.String Substring(int startIndex, int length) { throw null; }
public System.String Substring(System.Index startIndex) { throw null; }
public System.String Substring(System.Range range) { throw null; }
System.Collections.Generic.IEnumerator<char> System.Collections.Generic.IEnumerable<System.Char>.GetEnumerator() { throw null; }
System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() { throw null; }
bool System.IConvertible.ToBoolean(System.IFormatProvider provider) { throw null; }
Expand Down Expand Up @@ -6778,6 +6809,7 @@ public static partial class RuntimeHelpers
public static int GetHashCode(object o) { throw null; }
public static object GetObjectValue(object obj) { throw null; }
public static object GetUninitializedObject(System.Type type) { throw null; }
public static T[] GetSubArray<T>(T[] array, System.Range range) { throw null; }
public static void InitializeArray(System.Array array, System.RuntimeFieldHandle fldHandle) { }
public static bool IsReferenceOrContainsReferences<T>() { throw null; }
public static void PrepareConstrainedRegions() { }
Expand Down