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

Commit 4cf57a7

Browse files
jkotassafern
authored andcommitted
Move Span/ReadOnlySpan to shared CoreLib partition (dotnet/coreclr#10988)
Fix a few method names to better names used in CoreRT Contributes to dotnet/corert#2966 Signed-off-by: dotnet-bot-corefx-mirror <dotnet-bot@microsoft.com>
1 parent a0f10a5 commit 4cf57a7

File tree

4 files changed

+1241
-0
lines changed

4 files changed

+1241
-0
lines changed

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

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -169,6 +169,7 @@
169169
<Compile Include="$(MSBuildThisFileDirectory)System\Progress.cs"/>
170170
<Compile Include="$(MSBuildThisFileDirectory)System\Random.cs"/>
171171
<Compile Include="$(MSBuildThisFileDirectory)System\RankException.cs"/>
172+
<Compile Include="$(MSBuildThisFileDirectory)System\ReadOnlySpan.cs"/>
172173
<Compile Include="$(MSBuildThisFileDirectory)System\Reflection\AmbiguousMatchException.cs" />
173174
<Compile Include="$(MSBuildThisFileDirectory)System\Reflection\Assembly.cs" />
174175
<Compile Include="$(MSBuildThisFileDirectory)System\Reflection\AssemblyAlgorithmIdAttribute.cs" />
@@ -338,6 +339,8 @@
338339
<Compile Include="$(MSBuildThisFileDirectory)System\Security\SuppressUnmanagedCodeSecurityAttribute.cs"/>
339340
<Compile Include="$(MSBuildThisFileDirectory)System\Security\UnverifiableCodeAttribute.cs"/>
340341
<Compile Include="$(MSBuildThisFileDirectory)System\Security\VerificationException.cs"/>
342+
<Compile Include="$(MSBuildThisFileDirectory)System\Span.cs"/>
343+
<Compile Include="$(MSBuildThisFileDirectory)System\Span.NonGeneric.cs"/>
341344
<Compile Include="$(MSBuildThisFileDirectory)System\StackOverflowException.cs"/>
342345
<Compile Include="$(MSBuildThisFileDirectory)System\StringComparer.cs"/>
343346
<Compile Include="$(MSBuildThisFileDirectory)System\StringComparison.cs"/>
Lines changed: 313 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,313 @@
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+
using System.Runtime.CompilerServices;
7+
using EditorBrowsableState = System.ComponentModel.EditorBrowsableState;
8+
using EditorBrowsableAttribute = System.ComponentModel.EditorBrowsableAttribute;
9+
10+
#pragma warning disable 0809 //warning CS0809: Obsolete member 'Span<T>.Equals(object)' overrides non-obsolete member 'object.Equals(object)'
11+
12+
namespace System
13+
{
14+
/// <summary>
15+
/// ReadOnlySpan represents a contiguous region of arbitrary memory. Unlike arrays, it can point to either managed
16+
/// or native memory, or to memory allocated on the stack. It is type- and memory-safe.
17+
/// </summary>
18+
public struct ReadOnlySpan<T>
19+
{
20+
/// <summary>A byref or a native ptr.</summary>
21+
private readonly ByReference<T> _pointer;
22+
/// <summary>The number of elements this ReadOnlySpan contains.</summary>
23+
private readonly int _length;
24+
25+
/// <summary>
26+
/// Creates a new read-only span over the entirety of the target array.
27+
/// </summary>
28+
/// <param name="array">The target array.</param>
29+
/// <exception cref="System.ArgumentNullException">Thrown when <paramref name="array"/> is a null
30+
/// reference (Nothing in Visual Basic).</exception>
31+
[MethodImpl(MethodImplOptions.AggressiveInlining)]
32+
public ReadOnlySpan(T[] array)
33+
{
34+
if (array == null)
35+
ThrowHelper.ThrowArgumentNullException(ExceptionArgument.array);
36+
37+
_pointer = new ByReference<T>(ref Unsafe.As<byte, T>(ref array.GetRawSzArrayData()));
38+
_length = array.Length;
39+
}
40+
41+
/// <summary>
42+
/// Creates a new read-only span over the portion of the target array beginning
43+
/// at 'start' index and covering the remainder of the array.
44+
/// </summary>
45+
/// <param name="array">The target array.</param>
46+
/// <param name="start">The index at which to begin the read-only span.</param>
47+
/// <exception cref="System.ArgumentNullException">Thrown when <paramref name="array"/> is a null
48+
/// reference (Nothing in Visual Basic).</exception>
49+
/// <exception cref="System.ArgumentOutOfRangeException">
50+
/// Thrown when the specified <paramref name="start"/> is not in the range (&lt;0 or &gt;=Length).
51+
/// </exception>
52+
[MethodImpl(MethodImplOptions.AggressiveInlining)]
53+
public ReadOnlySpan(T[] array, int start)
54+
{
55+
if (array == null)
56+
ThrowHelper.ThrowArgumentNullException(ExceptionArgument.array);
57+
if ((uint)start > (uint)array.Length)
58+
ThrowHelper.ThrowArgumentOutOfRangeException();
59+
60+
_pointer = new ByReference<T>(ref Unsafe.Add(ref Unsafe.As<byte, T>(ref array.GetRawSzArrayData()), start));
61+
_length = array.Length - start;
62+
}
63+
64+
/// <summary>
65+
/// Creates a new read-only span over the portion of the target array beginning
66+
/// at 'start' index and ending at 'end' index (exclusive).
67+
/// </summary>
68+
/// <param name="array">The target array.</param>
69+
/// <param name="start">The index at which to begin the read-only span.</param>
70+
/// <param name="length">The number of items in the read-only span.</param>
71+
/// <exception cref="System.ArgumentNullException">Thrown when <paramref name="array"/> is a null
72+
/// reference (Nothing in Visual Basic).</exception>
73+
/// <exception cref="System.ArgumentOutOfRangeException">
74+
/// Thrown when the specified <paramref name="start"/> or end index is not in the range (&lt;0 or &gt;=Length).
75+
/// </exception>
76+
[MethodImpl(MethodImplOptions.AggressiveInlining)]
77+
public ReadOnlySpan(T[] array, int start, int length)
78+
{
79+
if (array == null)
80+
ThrowHelper.ThrowArgumentNullException(ExceptionArgument.array);
81+
if ((uint)start > (uint)array.Length || (uint)length > (uint)(array.Length - start))
82+
ThrowHelper.ThrowArgumentOutOfRangeException();
83+
84+
_pointer = new ByReference<T>(ref Unsafe.Add(ref Unsafe.As<byte, T>(ref array.GetRawSzArrayData()), start));
85+
_length = length;
86+
}
87+
88+
/// <summary>
89+
/// Creates a new read-only span over the target unmanaged buffer. Clearly this
90+
/// is quite dangerous, because we are creating arbitrarily typed T's
91+
/// out of a void*-typed block of memory. And the length is not checked.
92+
/// But if this creation is correct, then all subsequent uses are correct.
93+
/// </summary>
94+
/// <param name="pointer">An unmanaged pointer to memory.</param>
95+
/// <param name="length">The number of <typeparamref name="T"/> elements the memory contains.</param>
96+
/// <exception cref="System.ArgumentException">
97+
/// Thrown when <typeparamref name="T"/> is reference type or contains pointers and hence cannot be stored in unmanaged memory.
98+
/// </exception>
99+
/// <exception cref="System.ArgumentOutOfRangeException">
100+
/// Thrown when the specified <paramref name="length"/> is negative.
101+
/// </exception>
102+
[CLSCompliant(false)]
103+
[MethodImpl(MethodImplOptions.AggressiveInlining)]
104+
public unsafe ReadOnlySpan(void* pointer, int length)
105+
{
106+
if (RuntimeHelpers.IsReferenceOrContainsReferences<T>())
107+
ThrowHelper.ThrowInvalidTypeWithPointersNotSupported(typeof(T));
108+
if (length < 0)
109+
ThrowHelper.ThrowArgumentOutOfRangeException();
110+
111+
_pointer = new ByReference<T>(ref Unsafe.As<byte, T>(ref *(byte*)pointer));
112+
_length = length;
113+
}
114+
115+
/// <summary>
116+
/// Create a new read-only span over a portion of a regular managed object. This can be useful
117+
/// if part of a managed object represents a "fixed array." This is dangerous because neither the
118+
/// <paramref name="length"/> is checked, nor <paramref name="obj"/> being null, nor the fact that
119+
/// "rawPointer" actually lies within <paramref name="obj"/>.
120+
/// </summary>
121+
/// <param name="obj">The managed object that contains the data to span over.</param>
122+
/// <param name="objectData">A reference to data within that object.</param>
123+
/// <param name="length">The number of <typeparamref name="T"/> elements the memory contains.</param>
124+
[MethodImpl(MethodImplOptions.AggressiveInlining)]
125+
public static ReadOnlySpan<T> DangerousCreate(object obj, ref T objectData, int length) => new ReadOnlySpan<T>(ref objectData, length);
126+
127+
// Constructor for internal use only.
128+
[MethodImpl(MethodImplOptions.AggressiveInlining)]
129+
internal ReadOnlySpan(ref T ptr, int length)
130+
{
131+
Debug.Assert(length >= 0);
132+
133+
_pointer = new ByReference<T>(ref ptr);
134+
_length = length;
135+
}
136+
137+
/// <summary>
138+
/// Returns a reference to the 0th element of the Span. If the Span is empty, returns a reference to the location where the 0th element
139+
/// would have been stored. Such a reference can be used for pinning but must never be dereferenced.
140+
/// </summary>
141+
[MethodImpl(MethodImplOptions.AggressiveInlining)]
142+
public ref T DangerousGetPinnableReference()
143+
{
144+
return ref _pointer.Value;
145+
}
146+
147+
/// <summary>
148+
/// The number of items in the read-only span.
149+
/// </summary>
150+
public int Length => _length;
151+
152+
/// <summary>
153+
/// Returns true if Length is 0.
154+
/// </summary>
155+
public bool IsEmpty => _length == 0;
156+
157+
/// <summary>
158+
/// Returns the specified element of the read-only span.
159+
/// </summary>
160+
/// <param name="index"></param>
161+
/// <returns></returns>
162+
/// <exception cref="System.IndexOutOfRangeException">
163+
/// Thrown when index less than 0 or index greater than or equal to Length
164+
/// </exception>
165+
public T this[int index]
166+
{
167+
[MethodImpl(MethodImplOptions.AggressiveInlining)]
168+
get
169+
{
170+
if ((uint)index >= (uint)_length)
171+
ThrowHelper.ThrowIndexOutOfRangeException();
172+
173+
return Unsafe.Add(ref _pointer.Value, index);
174+
}
175+
}
176+
177+
/// <summary>
178+
/// Copies the contents of this read-only span into destination span. If the source
179+
/// and destinations overlap, this method behaves as if the original values in
180+
/// a temporary location before the destination is overwritten.
181+
///
182+
/// <param name="destination">The span to copy items into.</param>
183+
/// <exception cref="System.ArgumentException">
184+
/// Thrown when the destination Span is shorter than the source Span.
185+
/// </exception>
186+
/// </summary>
187+
public void CopyTo(Span<T> destination)
188+
{
189+
if (!TryCopyTo(destination))
190+
ThrowHelper.ThrowArgumentException_DestinationTooShort();
191+
}
192+
193+
/// Copies the contents of this read-only span into destination span. If the source
194+
/// and destinations overlap, this method behaves as if the original values in
195+
/// a temporary location before the destination is overwritten.
196+
/// </summary>
197+
/// <returns>If the destination span is shorter than the source span, this method
198+
/// return false and no data is written to the destination.</returns>
199+
/// <param name="destination">The span to copy items into.</param>
200+
public bool TryCopyTo(Span<T> destination)
201+
{
202+
if ((uint)_length > (uint)destination.Length)
203+
return false;
204+
205+
Span.CopyTo<T>(ref destination.DangerousGetPinnableReference(), ref _pointer.Value, _length);
206+
return true;
207+
}
208+
209+
/// <summary>
210+
/// Returns true if left and right point at the same memory and have the same length. Note that
211+
/// this does *not* check to see if the *contents* are equal.
212+
/// </summary>
213+
public static bool operator ==(ReadOnlySpan<T> left, ReadOnlySpan<T> right)
214+
{
215+
return left._length == right._length && Unsafe.AreSame<T>(ref left._pointer.Value, ref right._pointer.Value);
216+
}
217+
218+
/// <summary>
219+
/// Returns false if left and right point at the same memory and have the same length. Note that
220+
/// this does *not* check to see if the *contents* are equal.
221+
/// </summary>
222+
public static bool operator !=(ReadOnlySpan<T> left, ReadOnlySpan<T> right) => !(left == right);
223+
224+
/// <summary>
225+
/// This method is not supported as spans cannot be boxed. To compare two spans, use operator==.
226+
/// <exception cref="System.NotSupportedException">
227+
/// Always thrown by this method.
228+
/// </exception>
229+
/// </summary>
230+
[Obsolete("Equals() on Span will always throw an exception. Use == instead.")]
231+
[EditorBrowsable(EditorBrowsableState.Never)]
232+
public override bool Equals(object obj)
233+
{
234+
throw new NotSupportedException(SR.NotSupported_CannotCallEqualsOnSpan);
235+
}
236+
237+
/// <summary>
238+
/// This method is not supported as spans cannot be boxed.
239+
/// <exception cref="System.NotSupportedException">
240+
/// Always thrown by this method.
241+
/// </exception>
242+
/// </summary>
243+
[Obsolete("GetHashCode() on Span will always throw an exception.")]
244+
[EditorBrowsable(EditorBrowsableState.Never)]
245+
public override int GetHashCode()
246+
{
247+
throw new NotSupportedException(SR.NotSupported_CannotCallGetHashCodeOnSpan);
248+
}
249+
250+
/// <summary>
251+
/// Defines an implicit conversion of an array to a <see cref="ReadOnlySpan{T}"/>
252+
/// </summary>
253+
public static implicit operator ReadOnlySpan<T>(T[] array) => new ReadOnlySpan<T>(array);
254+
255+
/// <summary>
256+
/// Defines an implicit conversion of a <see cref="ArraySegment{T}"/> to a <see cref="ReadOnlySpan{T}"/>
257+
/// </summary>
258+
public static implicit operator ReadOnlySpan<T>(ArraySegment<T> arraySegment) => new ReadOnlySpan<T>(arraySegment.Array, arraySegment.Offset, arraySegment.Count);
259+
260+
/// <summary>
261+
/// Forms a slice out of the given read-only span, beginning at 'start'.
262+
/// </summary>
263+
/// <param name="start">The index at which to begin this slice.</param>
264+
/// <exception cref="System.ArgumentOutOfRangeException">
265+
/// Thrown when the specified <paramref name="start"/> index is not in range (&lt;0 or &gt;=Length).
266+
/// </exception>
267+
[MethodImpl(MethodImplOptions.AggressiveInlining)]
268+
public ReadOnlySpan<T> Slice(int start)
269+
{
270+
if ((uint)start > (uint)_length)
271+
ThrowHelper.ThrowArgumentOutOfRangeException();
272+
273+
return new ReadOnlySpan<T>(ref Unsafe.Add(ref _pointer.Value, start), _length - start);
274+
}
275+
276+
/// <summary>
277+
/// Forms a slice out of the given read-only span, beginning at 'start', of given length
278+
/// </summary>
279+
/// <param name="start">The index at which to begin this slice.</param>
280+
/// <param name="length">The desired length for the slice (exclusive).</param>
281+
/// <exception cref="System.ArgumentOutOfRangeException">
282+
/// Thrown when the specified <paramref name="start"/> or end index is not in range (&lt;0 or &gt;=Length).
283+
/// </exception>
284+
[MethodImpl(MethodImplOptions.AggressiveInlining)]
285+
public ReadOnlySpan<T> Slice(int start, int length)
286+
{
287+
if ((uint)start > (uint)_length || (uint)length > (uint)(_length - start))
288+
ThrowHelper.ThrowArgumentOutOfRangeException();
289+
290+
return new ReadOnlySpan<T>(ref Unsafe.Add(ref _pointer.Value, start), length);
291+
}
292+
293+
/// <summary>
294+
/// Copies the contents of this read-only span into a new array. This heap
295+
/// allocates, so should generally be avoided, however it is sometimes
296+
/// necessary to bridge the gap with APIs written in terms of arrays.
297+
/// </summary>
298+
public T[] ToArray()
299+
{
300+
if (_length == 0)
301+
return Array.Empty<T>();
302+
303+
var destination = new T[_length];
304+
Span.CopyTo<T>(ref Unsafe.As<byte, T>(ref destination.GetRawSzArrayData()), ref _pointer.Value, _length);
305+
return destination;
306+
}
307+
308+
/// <summary>
309+
/// Returns a 0-length read-only span whose base is the null pointer.
310+
/// </summary>
311+
public static ReadOnlySpan<T> Empty => default(ReadOnlySpan<T>);
312+
}
313+
}

0 commit comments

Comments
 (0)