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

Commit a2cb15c

Browse files
authored
Add the array/arraysegment->Span/Memory overloads to MemoryExtensions (#27584)
* Add the array/arraysegment->Span/Memory overloads to MemoryExtensions Part of https://github.com/dotnet/corefx/issues/26894 * Remove comment, fix tests
1 parent 8034664 commit a2cb15c

File tree

6 files changed

+273
-2
lines changed

6 files changed

+273
-2
lines changed

src/System.Memory/ref/System.Memory.cs

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,12 @@ public static partial class MemoryExtensions
1111
{
1212
public static System.ReadOnlySpan<byte> AsBytes<T>(this System.ReadOnlySpan<T> source) where T : struct { throw null; }
1313
public static System.Span<byte> AsBytes<T>(this System.Span<T> source) where T : struct { throw null; }
14+
public static System.Memory<T> AsMemory<T>(this System.ArraySegment<T> segment) { throw null; }
15+
public static System.Memory<T> AsMemory<T>(this System.ArraySegment<T> segment, int start) { throw null; }
16+
public static System.Memory<T> AsMemory<T>(this System.ArraySegment<T> segment, int start, int length) { throw null; }
17+
public static System.Memory<T> AsMemory<T>(this T[] array) { throw null; }
18+
public static System.Memory<T> AsMemory<T>(this T[] array, int start) { throw null; }
19+
public static System.Memory<T> AsMemory<T>(this T[] array, int start, int length) { throw null; }
1420
public static System.ReadOnlyMemory<char> AsMemory(this string text) { throw null; }
1521
public static System.ReadOnlyMemory<char> AsMemory(this string text, int start) { throw null; }
1622
public static System.ReadOnlyMemory<char> AsMemory(this string text, int start, int length) { throw null; }
@@ -21,8 +27,12 @@ public static partial class MemoryExtensions
2127
public static System.ReadOnlySpan<T> AsReadOnlySpan<T>(this System.ArraySegment<T> arraySegment) { throw null; }
2228
public static System.ReadOnlySpan<T> AsReadOnlySpan<T>(this System.Span<T> span) { throw null; }
2329
public static System.ReadOnlySpan<T> AsReadOnlySpan<T>(this T[] array) { throw null; }
24-
public static System.Span<T> AsSpan<T>(this System.ArraySegment<T> arraySegment) { throw null; }
30+
public static System.Span<T> AsSpan<T>(this System.ArraySegment<T> segment) { throw null; }
31+
public static System.Span<T> AsSpan<T>(this System.ArraySegment<T> segment, int start) { throw null; }
32+
public static System.Span<T> AsSpan<T>(this System.ArraySegment<T> segment, int start, int length) { throw null; }
2533
public static System.Span<T> AsSpan<T>(this T[] array) { throw null; }
34+
public static System.Span<T> AsSpan<T>(this T[] array, int start) { throw null; }
35+
public static System.Span<T> AsSpan<T>(this T[] array, int start, int length) { throw null; }
2636
public static int BinarySearch<T>(this System.ReadOnlySpan<T> span, System.IComparable<T> comparable) { throw null; }
2737
public static int BinarySearch<T>(this System.Span<T> span, System.IComparable<T> comparable) { throw null; }
2838
public static int BinarySearch<T, TComparer>(this System.ReadOnlySpan<T> span, T value, TComparer comparer) where TComparer : System.Collections.Generic.IComparer<T> { throw null; }

src/System.Memory/src/System/MemoryExtensions.Portable.cs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,11 @@ namespace System
1313
/// </summary>
1414
public static partial class MemoryExtensions
1515
{
16+
/// <summary>
17+
/// Creates a new span over the portion of the target array.
18+
/// </summary>
19+
public static Span<T> AsSpan<T>(this T[] array, int start) => Span<T>.Create(array, start);
20+
1621
/// <summary>
1722
/// Returns a value indicating whether the specified <paramref name="value"/> occurs within the <paramref name="span"/>.
1823
/// <param name="span">The source span.</param>

src/System.Memory/src/System/Span.Portable.cs

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,28 @@ public Span(T[] array)
4141
_byteOffset = SpanHelpers.PerTypeValues<T>.ArrayAdjustment;
4242
}
4343

44+
// This is a constructor that takes an array and start but not length. The reason we expose it as a static method as a constructor
45+
// is to mirror the actual api shape. This overload of the constructor was removed from the api surface area due to possible
46+
// confusion with other overloads that take an int parameter that don't represent a start index.
47+
[MethodImpl(MethodImplOptions.AggressiveInlining)]
48+
internal static Span<T> Create(T[] array, int start)
49+
{
50+
if (array == null)
51+
{
52+
if (start != 0)
53+
ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.start);
54+
return default;
55+
}
56+
if (default(T) == null && array.GetType() != typeof(T[]))
57+
ThrowHelper.ThrowArrayTypeMismatchException();
58+
if ((uint)start > (uint)array.Length)
59+
ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.start);
60+
61+
IntPtr byteOffset = SpanHelpers.PerTypeValues<T>.ArrayAdjustment.Add<T>(start);
62+
int length = array.Length - start;
63+
return new Span<T>(pinnable: Unsafe.As<Pinnable<T>>(array), byteOffset: byteOffset, length: length);
64+
}
65+
4466
/// <summary>
4567
/// Creates a new span over the portion of the target array beginning
4668
/// at 'start' index and ending at 'end' index (exclusive).
Lines changed: 122 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,122 @@
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 Xunit;
6+
7+
namespace System.MemoryTests
8+
{
9+
public static class AsMemory
10+
{
11+
[Theory]
12+
[InlineData(0, 0)]
13+
[InlineData(3, 0)]
14+
[InlineData(3, 1)]
15+
[InlineData(3, 2)]
16+
[InlineData(3, 3)]
17+
[InlineData(10, 0)]
18+
[InlineData(10, 3)]
19+
[InlineData(10, 10)]
20+
public static void ArrayAsMemoryWithStart(int length, int start)
21+
{
22+
int[] a = new int[length];
23+
Memory<int> m = a.AsMemory(start);
24+
Assert.Equal(length - start, m.Length);
25+
if (start != length)
26+
{
27+
m.Span[0] = 42;
28+
Assert.Equal(42, a[start]);
29+
}
30+
}
31+
32+
[Theory]
33+
[InlineData(0, 0)]
34+
[InlineData(3, 0)]
35+
[InlineData(3, 1)]
36+
[InlineData(3, 2)]
37+
[InlineData(3, 3)]
38+
[InlineData(10, 0)]
39+
[InlineData(10, 3)]
40+
[InlineData(10, 10)]
41+
public static void ArraySegmentAsMemoryWithStart(int length, int start)
42+
{
43+
const int segmentOffset = 5;
44+
45+
int[] a = new int[length + segmentOffset];
46+
ArraySegment<int> segment = new ArraySegment<int>(a, 5, length);
47+
Memory<int> m = segment.AsMemory(start);
48+
Assert.Equal(length - start, m.Length);
49+
if (m.Length != 0)
50+
{
51+
m.Span[0] = 42;
52+
Assert.Equal(42, a[segmentOffset + start]);
53+
}
54+
}
55+
56+
[Theory]
57+
[InlineData(0, 0, 0)]
58+
[InlineData(3, 0, 3)]
59+
[InlineData(3, 1, 2)]
60+
[InlineData(3, 2, 1)]
61+
[InlineData(3, 3, 0)]
62+
[InlineData(10, 0, 5)]
63+
[InlineData(10, 3, 2)]
64+
public static void ArrayAsMemoryWithStartAndLength(int length, int start, int subLength)
65+
{
66+
int[] a = new int[length];
67+
Memory<int> m = a.AsMemory(start, subLength);
68+
Assert.Equal(subLength, m.Length);
69+
if (subLength != 0)
70+
{
71+
m.Span[0] = 42;
72+
Assert.Equal(42, a[start]);
73+
}
74+
}
75+
76+
[Theory]
77+
[InlineData(0, 0, 0)]
78+
[InlineData(3, 0, 3)]
79+
[InlineData(3, 1, 2)]
80+
[InlineData(3, 2, 1)]
81+
[InlineData(3, 3, 0)]
82+
[InlineData(10, 0, 5)]
83+
[InlineData(10, 3, 2)]
84+
public static void ArraySegmentAsMemoryWithStartAndLength(int length, int start, int subLength)
85+
{
86+
const int segmentOffset = 5;
87+
88+
int[] a = new int[length + segmentOffset];
89+
ArraySegment<int> segment = new ArraySegment<int>(a, segmentOffset, length);
90+
Memory<int> m = segment.AsMemory(start, subLength);
91+
Assert.Equal(subLength, m.Length);
92+
if (subLength != 0)
93+
{
94+
m.Span[0] = 42;
95+
Assert.Equal(42, a[segmentOffset + start]);
96+
}
97+
}
98+
99+
[Theory]
100+
[InlineData(0, -1)]
101+
[InlineData(0, 1)]
102+
[InlineData(5, 6)]
103+
public static void ArrayAsMemoryWithStartNegative(int length, int start)
104+
{
105+
int[] a = new int[length];
106+
Assert.Throws<ArgumentOutOfRangeException>(() => a.AsMemory(start));
107+
}
108+
109+
[Theory]
110+
[InlineData(0, -1, 0)]
111+
[InlineData(0, 1, 0)]
112+
[InlineData(0, 0, -1)]
113+
[InlineData(0, 0, 1)]
114+
[InlineData(5, 6, 0)]
115+
[InlineData(5, 3, 3)]
116+
public static void ArrayAsMemoryWithStartAndLengthNegative(int length, int start, int subLength)
117+
{
118+
int[] a = new int[length];
119+
Assert.Throws<ArgumentOutOfRangeException>(() => a.AsMemory(start, subLength));
120+
}
121+
}
122+
}

src/System.Memory/tests/Span/AsSpan.cs

Lines changed: 111 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -95,5 +95,116 @@ public static void ZeroLengthArraySegmentAsSpan()
9595
Span<int> spanInt = segmentInt.AsSpan();
9696
spanInt.ValidateNonNullEmpty();
9797
}
98+
99+
[Theory]
100+
[InlineData(0, 0)]
101+
[InlineData(3, 0)]
102+
[InlineData(3, 1)]
103+
[InlineData(3, 2)]
104+
[InlineData(3, 3)]
105+
[InlineData(10, 0)]
106+
[InlineData(10, 3)]
107+
[InlineData(10, 10)]
108+
public static void ArrayAsSpanWithStart(int length, int start)
109+
{
110+
int[] a = new int[length];
111+
Span<int> s = a.AsSpan(start);
112+
Assert.Equal(length - start, s.Length);
113+
if (start != length)
114+
{
115+
s[0] = 42;
116+
Assert.Equal(42, a[start]);
117+
}
118+
}
119+
120+
[Theory]
121+
[InlineData(0, 0)]
122+
[InlineData(3, 0)]
123+
[InlineData(3, 1)]
124+
[InlineData(3, 2)]
125+
[InlineData(3, 3)]
126+
[InlineData(10, 0)]
127+
[InlineData(10, 3)]
128+
[InlineData(10, 10)]
129+
public static void ArraySegmentAsSpanWithStart(int length, int start)
130+
{
131+
const int segmentOffset = 5;
132+
133+
int[] a = new int[length + segmentOffset];
134+
ArraySegment<int> segment = new ArraySegment<int>(a, 5, length);
135+
Span<int> s = segment.AsSpan(start);
136+
Assert.Equal(length - start, s.Length);
137+
if (s.Length != 0)
138+
{
139+
s[0] = 42;
140+
Assert.Equal(42, a[segmentOffset + start]);
141+
}
142+
}
143+
144+
[Theory]
145+
[InlineData(0, 0, 0)]
146+
[InlineData(3, 0, 3)]
147+
[InlineData(3, 1, 2)]
148+
[InlineData(3, 2, 1)]
149+
[InlineData(3, 3, 0)]
150+
[InlineData(10, 0, 5)]
151+
[InlineData(10, 3, 2)]
152+
public static void ArrayAsSpanWithStartAndLength(int length, int start, int subLength)
153+
{
154+
int[] a = new int[length];
155+
Span<int> s = a.AsSpan(start, subLength);
156+
Assert.Equal(subLength, s.Length);
157+
if (subLength != 0)
158+
{
159+
s[0] = 42;
160+
Assert.Equal(42, a[start]);
161+
}
162+
}
163+
164+
[Theory]
165+
[InlineData(0, 0, 0)]
166+
[InlineData(3, 0, 3)]
167+
[InlineData(3, 1, 2)]
168+
[InlineData(3, 2, 1)]
169+
[InlineData(3, 3, 0)]
170+
[InlineData(10, 0, 5)]
171+
[InlineData(10, 3, 2)]
172+
public static void ArraySegmentAsSpanWithStartAndLength(int length, int start, int subLength)
173+
{
174+
const int segmentOffset = 5;
175+
176+
int[] a = new int[length + segmentOffset];
177+
ArraySegment<int> segment = new ArraySegment<int>(a, segmentOffset, length);
178+
Span<int> s = segment.AsSpan(start, subLength);
179+
Assert.Equal(subLength, s.Length);
180+
if (subLength != 0)
181+
{
182+
s[0] = 42;
183+
Assert.Equal(42, a[segmentOffset + start]);
184+
}
185+
}
186+
187+
[Theory]
188+
[InlineData(0, -1)]
189+
[InlineData(0, 1)]
190+
[InlineData(5, 6)]
191+
public static void ArrayAsSpanWithStartNegative(int length, int start)
192+
{
193+
int[] a = new int[length];
194+
Assert.Throws<ArgumentOutOfRangeException>(() => a.AsSpan(start));
195+
}
196+
197+
[Theory]
198+
[InlineData(0, -1, 0)]
199+
[InlineData(0, 1, 0)]
200+
[InlineData(0, 0, -1)]
201+
[InlineData(0, 0, 1)]
202+
[InlineData(5, 6, 0)]
203+
[InlineData(5, 3, 3)]
204+
public static void ArrayAsSpanWithStartAndLengthNegative(int length, int start, int subLength)
205+
{
206+
int[] a = new int[length];
207+
Assert.Throws<ArgumentOutOfRangeException>(() => a.AsSpan(start, subLength));
208+
}
98209
}
99210
}

src/System.Memory/tests/System.Memory.Tests.csproj

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -133,6 +133,7 @@
133133
<Compile Include="ReadOnlySpan\TrimWhiteSpace.cs" />
134134
</ItemGroup>
135135
<ItemGroup>
136+
<Compile Include="Memory\AsMemory.cs" />
136137
<Compile Include="Memory\AsReadOnlyMemory.cs" />
137138
<Compile Include="Memory\CopyTo.cs" />
138139
<Compile Include="Memory\CtorArray.cs" />
@@ -224,4 +225,4 @@
224225
<Service Include="{82A7F48D-3B50-4B1E-B82E-3ADA8210C358}" />
225226
</ItemGroup>
226227
<Import Project="$([MSBuild]::GetDirectoryNameOfFileAbove($(MSBuildThisFileDirectory), dir.targets))\dir.targets" />
227-
</Project>
228+
</Project>

0 commit comments

Comments
 (0)