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

Span<T> api update #8583

Merged
merged 4 commits into from
Dec 12, 2016
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 5 additions & 1 deletion src/mscorlib/model.xml
Original file line number Diff line number Diff line change
Expand Up @@ -12422,6 +12422,8 @@
<Member Name="DangerousGetPinnableReference" />
<Member Name="op_Implicit(T[])" ReturnType="System.Span&lt;T&gt;" />
<Member Name="op_Implicit(System.ArraySegment&lt;T&gt;)" ReturnType="System.Span&lt;T&gt;" />
<Member Name="op_Equality(System.Span&lt;T&gt;,System.Span&lt;T&gt;)" />
<Member Name="op_Inequality(System.Span&lt;T&gt;,System.Span&lt;T&gt;)" />
<Member Name="get_Length" />
<Member Name="get_Empty" />
<Member Name="get_IsEmpty" />
Expand All @@ -12431,8 +12433,10 @@
<Member Name="Slice(System.Int32)" />
<Member Name="Slice(System.Int32,System.Int32)" />
<Member Name="Equals(System.Span&lt;T&gt;)" />
<Member Name="Equals(System.Object)" />
<Member Name="GetHashCode" />
<Member Name="CopyTo(System.Span&lt;T&gt;)" />
<Member Name="TryCopyTo(System.Span&lt;T&gt;)" />
<Member Name="Set(System.ReadOnlySpan&lt;T&gt;)" />
</Type>
<Type Name="System.ReadOnlySpan&lt;T&gt;" Condition="FEATURE_SPAN_OF_T">
<Member Name="#ctor(T[])" />
Expand Down
5 changes: 5 additions & 0 deletions src/mscorlib/src/System.Private.CoreLib.txt
Original file line number Diff line number Diff line change
Expand Up @@ -592,6 +592,7 @@ Argument_NativeOverlappedAlreadyFree = 'overlapped' has already been freed.
Argument_AlreadyBoundOrSyncHandle = 'handle' has already been bound to the thread pool, or was not opened for asynchronous I/O.
#if FEATURE_SPAN_OF_T
Argument_InvalidTypeWithPointersNotSupported = Cannot use type '{0}'. Only value types without pointers or references are supported.
Argument_DestinationTooShort = Destination is too short.
#endif // FEATURE_SPAN_OF_T

;
Expand Down Expand Up @@ -1431,6 +1432,10 @@ NotSupported_WindowsPhone = {0} is not supported.
NotSupported_AssemblyLoadCodeBase = Assembly.Load with a Codebase is not supported.
NotSupported_AssemblyLoadFromHash = Assembly.LoadFrom with hashValue is not supported.
#endif
#if FEATURE_SPAN_OF_T
NotSupported_CannotCallEqualsOnSpan = Equals() on Span and ReadOnlySpan is not supported. Use operator== instead.
NotSupported_CannotCallGetHashCodeOnSpan = GetHashCode() on Span and ReadOnlySpan is not supported.
#endif // FEATURE_SPAN_OF_T

; TypeLoadException
TypeLoad_ResolveType = Could not resolve type '{0}'.
Expand Down
203 changes: 128 additions & 75 deletions src/mscorlib/src/System/Span.cs
Original file line number Diff line number Diff line change
Expand Up @@ -15,18 +15,13 @@ namespace System
/// </summary>
public struct Span<T>
{
/// <summary>A byref or a native ptr.</summary>
private readonly ByReference<T> _pointer;
/// <summary>The number of elements this Span contains.</summary>
private readonly int _length;

/// <summary>
Copy link
Member

Choose a reason for hiding this comment

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

The convention is to keep fields at the top. (It was recently added to https://github.com/dotnet/corefx/blob/master/Documentation/coding-guidelines/coding-style.md to make it explicit.)

Copy link
Author

Choose a reason for hiding this comment

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

Sorry, I got a bit carried away in making the ordering match the CoreFX version, I'll move these back

/// Creates a new span over the entirety of the target array.
/// </summary>
/// <param name="array">The target array.</param>
/// <exception cref="System.ArgumentNullException">Thrown when <paramref name="array"/> is a null
/// reference (Nothing in Visual Basic).</exception>
/// <exception cref="System.ArrayTypeMismatchException">Thrown when <paramref name="array"/> is covariant.</exception>
/// <exception cref="System.ArrayTypeMismatchException">Thrown when <paramref name="array"/> is covariant and array's type is not exactly T[].</exception>
public Span(T[] array)
{
if (array == null)
Expand All @@ -48,7 +43,7 @@ public Span(T[] array)
/// <param name="start">The index at which to begin the span.</param>
/// <exception cref="System.ArgumentNullException">Thrown when <paramref name="array"/> is a null
/// reference (Nothing in Visual Basic).</exception>
/// <exception cref="System.ArrayTypeMismatchException">Thrown when <paramref name="array"/> is covariant.</exception>
/// <exception cref="System.ArrayTypeMismatchException">Thrown when <paramref name="array"/> is covariant and array's type is not exactly T[].</exception>
/// <exception cref="System.ArgumentOutOfRangeException">
/// Thrown when the specified <paramref name="start"/> is not in the range (&lt;0 or &gt;=Length).
/// </exception>
Expand Down Expand Up @@ -76,7 +71,7 @@ public Span(T[] array, int start)
/// <param name="length">The number of items in the span.</param>
/// <exception cref="System.ArgumentNullException">Thrown when <paramref name="array"/> is a null
/// reference (Nothing in Visual Basic).</exception>
/// <exception cref="System.ArrayTypeMismatchException">Thrown when <paramref name="array"/> is covariant.</exception>
/// <exception cref="System.ArrayTypeMismatchException">Thrown when <paramref name="array"/> is covariant and array's type is not exactly T[].</exception>
/// <exception cref="System.ArgumentOutOfRangeException">
/// Thrown when the specified <paramref name="start"/> or end index is not in the range (&lt;0 or &gt;=Length).
/// </exception>
Expand All @@ -101,7 +96,7 @@ public Span(T[] array, int start, int length)
/// out of a void*-typed block of memory. And the length is not checked.
/// But if this creation is correct, then all subsequent uses are correct.
/// </summary>
/// <param name="ptr">An unmanaged pointer to memory.</param>
/// <param name="pointer">An unmanaged pointer to memory.</param>
/// <param name="length">The number of <typeparamref name="T"/> elements the memory contains.</param>
/// <exception cref="System.ArgumentException">
/// Thrown when <typeparamref name="T"/> is reference type or contains pointers and hence cannot be stored in unmanaged memory.
Expand Down Expand Up @@ -130,31 +125,6 @@ internal Span(ref T ptr, int length)
_length = length;
}

/// <summary>
/// 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
/// would have been stored. Such a reference can be used for pinning but must never be dereferenced.
/// </summary>
public ref T DangerousGetPinnableReference()
{
return ref _pointer.Value;
}

/// <summary>
/// Defines an implicit conversion of an array to a <see cref="Span{T}"/>
/// </summary>
public static implicit operator Span<T>(T[] array)
{
return new Span<T>(array);
}

/// <summary>
/// Defines an implicit conversion of a <see cref="ArraySegment{T}"/> to a <see cref="Span{T}"/>
/// </summary>
public static implicit operator Span<T>(ArraySegment<T> arraySegment)
{
return new Span<T>(arraySegment.Array, arraySegment.Offset, arraySegment.Count);
}

/// <summary>
/// Gets the number of elements contained in the <see cref="Span{T}"/>
/// </summary>
Expand All @@ -163,14 +133,6 @@ public int Length
get { return _length; }
}

/// <summary>
/// Returns an empty <see cref="Span{T}"/>
/// </summary>
public static Span<T> Empty
{
get { return default(Span<T>); }
}

/// <summary>
/// Returns whether the <see cref="Span{T}"/> is empty.
/// </summary>
Expand Down Expand Up @@ -204,18 +166,107 @@ public T this[int index]
}

/// <summary>
/// Copies the contents of this span into a new array. This heap
/// allocates, so should generally be avoided, however is sometimes
/// necessary to bridge the gap with APIs written in terms of arrays.
/// Copies the contents of this span into destination span. If the source
/// and destinations overlap, this method behaves as if the original values in
/// a temporary location before the destination is overwritten.
/// </summary>
public T[] ToArray()
/// <param name="destination">The span to copy items into.</param>
/// <exception cref="System.ArgumentException">
/// Thrown when the destination Span is shorter than the source Span.
/// </exception>
public void CopyTo(Span<T> destination)
{
if (_length == 0)
return Array.Empty<T>();
if (!TryCopyTo(destination))
ThrowHelper.ThrowArgumentException_DestinationTooShort();
}

var destination = new T[_length];
SpanHelper.CopyTo<T>(ref JitHelpers.GetArrayData(destination), ref DangerousGetPinnableReference(), _length);
return destination;
/// <summary>
/// Copies the contents of this span into destination span. If the source
/// and destinations overlap, this method behaves as if the original values in
/// a temporary location before the destination is overwritten.
/// </summary>
/// <param name="destination">The span to copy items into.</param>
/// <returns>If the destination span is shorter than the source span, this method
/// return false and no data is written to the destination.</returns>
public bool TryCopyTo(Span<T> destination)
{
if ((uint)_length > (uint)destination.Length)
return false;

SpanHelper.CopyTo<T>(ref destination.DangerousGetPinnableReference(), ref DangerousGetPinnableReference(), _length);
return true;
}

/// <summary>
/// Returns true if left and right point at the same memory and have the same length. Note that
/// this does *not* check to see if the *contents* are equal.
/// </summary>
public static bool operator ==(Span<T> left, Span<T> right)
{
return left.Equals(right);
}

/// <summary>
/// Returns false if left and right point at the same memory and have the same length. Note that
/// this does *not* check to see if the *contents* are equal.
/// </summary>
public static bool operator !=(Span<T> left, Span<T> right)
{
return !left.Equals(right);
}

/// <summary>
/// Checks to see if two spans point at the same memory. Note that
/// this does *not* check to see if the *contents* are equal.
/// </summary>
public bool Equals(Span<T> other)
{
return (_length == other.Length) &&
(_length == 0 || Unsafe.AreSame(ref DangerousGetPinnableReference(), ref other.DangerousGetPinnableReference()));
}

/// <summary>
/// This method is not supported as spans cannot be boxed. To compare two spans, use operator==.
/// <exception cref="System.NotSupportedException">
/// Always thrown by this method.
/// </exception>
/// </summary>
[Obsolete("Equals() on Span will always throw an exception. Use == instead.")]
public override bool Equals(object obj)
{
ThrowHelper.ThrowNotSupportedException_CannotCallEqualsOnSpan();
// Prevent compiler error CS0161: 'Span<T>.Equals(object)': not all code paths return a value
return default(bool);
}

/// <summary>
/// This method is not supported as spans cannot be boxed.
/// <exception cref="System.NotSupportedException">
/// Always thrown by this method.
/// </exception>
/// </summary>
[Obsolete("GetHashCode() on Span will always throw an exception.")]
public override int GetHashCode()
{
ThrowHelper.ThrowNotSupportedException_CannotCallGetHashCodeOnSpan();
// Prevent compiler error CS0161: 'Span<T>.GetHashCode()': not all code paths return a value
return default(int);
}

/// <summary>
/// Defines an implicit conversion of an array to a <see cref="Span{T}"/>
/// </summary>
public static implicit operator Span<T>(T[] array)
{
return new Span<T>(array);
}

/// <summary>
/// Defines an implicit conversion of a <see cref="ArraySegment{T}"/> to a <see cref="Span{T}"/>
/// </summary>
public static implicit operator Span<T>(ArraySegment<T> arraySegment)
{
return new Span<T>(arraySegment.Array, arraySegment.Offset, arraySegment.Count);
}

/// <summary>
Expand All @@ -238,7 +289,7 @@ public Span<T> Slice(int start)
/// Forms a slice out of the given span, beginning at 'start', of given length
/// </summary>
/// <param name="start">The index at which to begin this slice.</param>
/// <param name="end">The index at which to end this slice (exclusive).</param>
/// <param name="length">The desired length for the slice (exclusive).</param>
/// <exception cref="System.ArgumentOutOfRangeException">
/// Thrown when the specified <paramref name="start"/> or end index is not in range (&lt;0 or &gt;&eq;Length).
/// </exception>
Expand All @@ -252,39 +303,41 @@ public Span<T> Slice(int start, int length)
}

/// <summary>
/// Checks to see if two spans point at the same memory. Note that
/// this does *not* check to see if the *contents* are equal.
/// Copies the contents of this span into a new array. This heap
/// allocates, so should generally be avoided, however it is sometimes
/// necessary to bridge the gap with APIs written in terms of arrays.
/// </summary>
public bool Equals(Span<T> other)
public T[] ToArray()
{
return (_length == other.Length) &&
(_length == 0 || Unsafe.AreSame(ref DangerousGetPinnableReference(), ref other.DangerousGetPinnableReference()));
if (_length == 0)
return Array.Empty<T>();

var destination = new T[_length];
SpanHelper.CopyTo<T>(ref JitHelpers.GetArrayData(destination), ref DangerousGetPinnableReference(), _length);
return destination;
}

/// <summary>
/// Copies the contents of this span into destination span. The destination
/// must be at least as big as the source, and may be bigger.
// <summary>
/// Returns an empty <see cref="Span{T}"/>
/// </summary>
/// <param name="destination">The span to copy items into.</param>
public bool TryCopyTo(Span<T> destination)
public static Span<T> Empty
{
if ((uint)_length > (uint)destination.Length)
return false;

SpanHelper.CopyTo<T>(ref destination.DangerousGetPinnableReference(), ref DangerousGetPinnableReference(), _length);
return true;
get { return default(Span<T>); }
}

/// <exception cref="System.ArgumentOutOfRangeException">
/// Thrown when the specified <paramref name="values"/>'s Length is longer than source span's Length.
/// </exception>
public void Set(ReadOnlySpan<T> values)
/// <summary>
/// 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
/// would have been stored. Such a reference can be used for pinning but must never be dereferenced.
/// </summary>
public ref T DangerousGetPinnableReference()
Copy link
Member

@jkotas jkotas Dec 10, 2016

Choose a reason for hiding this comment

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

Nit: I think it would look better to keep this right after constructors where it was originally.

Copy link
Author

Choose a reason for hiding this comment

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

np, I'll move it back (same as last comment, I got a bit carried away in making the ordering match the CoreFX version!)

{
if ((uint)values.Length > (uint)_length)
ThrowHelper.ThrowArgumentOutOfRangeException();

SpanHelper.CopyTo<T>(ref DangerousGetPinnableReference(), ref values.DangerousGetPinnableReference(), values.Length);
return ref _pointer.Value;
}

/// <summary>A byref or a native ptr.</summary>
private readonly ByReference<T> _pointer;
/// <summary>The number of elements this Span contains.</summary>
private readonly int _length;
}

public static class SpanExtensions
Expand Down
12 changes: 12 additions & 0 deletions src/mscorlib/src/System/ThrowHelper.cs
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,18 @@ internal static void ThrowIndexOutOfRangeException() {
internal static void ThrowArgumentOutOfRangeException() {
throw new ArgumentOutOfRangeException();
}

internal static void ThrowArgumentException_DestinationTooShort() {
throw new ArgumentException(Environment.GetResourceString("Argument_DestinationTooShort"));
}

internal static void ThrowNotSupportedException_CannotCallEqualsOnSpan() {
throw new NotSupportedException(Environment.GetResourceString("NotSupported_CannotCallEqualsOnSpan"));
}

internal static void ThrowNotSupportedException_CannotCallGetHashCodeOnSpan() {
throw new NotSupportedException(Environment.GetResourceString("NotSupported_CannotCallGetHashCodeOnSpan"));
}
#endif

internal static void ThrowArgumentOutOfRange_IndexException() {
Expand Down