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

Commit

Permalink
Span<T> api update (#8583)
Browse files Browse the repository at this point in the history
* Changing method/property order to match CoreFX impl

To make diffing the files easier

* Added other missing methods to match CoreFX impl

Added:
- public void CopyTo(Span<T> destination)
- public static bool operator ==(Span<T> left, Span<T> right)
- public static bool operator !=(Span<T> left, Span<T> right)
- public override bool Equals(object obj)
- public override int GetHashCode()

Also removed 'public void Set(ReadOnlySpan<T> values)' and it's no
longer part of the Span<T> public API, see

https://github.com/dotnet/apireviews/tree/master/2016/11-04-SpanOfT#spantset
  • Loading branch information
mattwarren authored and jkotas committed Dec 12, 2016
1 parent 5dbaa3c commit 98798b7
Show file tree
Hide file tree
Showing 4 changed files with 130 additions and 75 deletions.
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
182 changes: 108 additions & 74 deletions src/mscorlib/src/System/Span.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@
using System.Diagnostics;
using System.Runtime.CompilerServices;

#pragma warning disable 0809 //warning CS0809: Obsolete member 'Span<T>.Equals(object)' overrides non-obsolete member 'object.Equals(object)'

namespace System
{
/// <summary>
Expand All @@ -26,7 +28,7 @@ public struct Span<T>
/// <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 +50,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 +78,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 +103,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 @@ -139,45 +141,15 @@ 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>
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>); }
}
public int Length => _length;

/// <summary>
/// Returns whether the <see cref="Span{T}"/> is empty.
/// </summary>
public bool IsEmpty
{
get { return _length == 0; }
}
public bool IsEmpty => _length == 0;

/// <summary>
/// Fetches the element at the specified index.
Expand All @@ -204,20 +176,97 @@ 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) => 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) => !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) => 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) => new Span<T>(arraySegment.Array, arraySegment.Offset, arraySegment.Count);

/// <summary>
/// Forms a slice out of the given span, beginning at 'start'.
/// </summary>
Expand All @@ -238,7 +287,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 +301,24 @@ 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.
/// </summary>
public bool Equals(Span<T> other)
{
return (_length == other.Length) &&
(_length == 0 || Unsafe.AreSame(ref DangerousGetPinnableReference(), ref other.DangerousGetPinnableReference()));
}

/// <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.
/// 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>
/// <param name="destination">The span to copy items into.</param>
public bool TryCopyTo(Span<T> destination)
public T[] ToArray()
{
if ((uint)_length > (uint)destination.Length)
return false;
if (_length == 0)
return Array.Empty<T>();

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

/// <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)
{
if ((uint)values.Length > (uint)_length)
ThrowHelper.ThrowArgumentOutOfRangeException();

SpanHelper.CopyTo<T>(ref DangerousGetPinnableReference(), ref values.DangerousGetPinnableReference(), values.Length);
}
// <summary>
/// Returns an empty <see cref="Span{T}"/>
/// </summary>
public static Span<T> Empty => default(Span<T>);
}

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

0 comments on commit 98798b7

Please sign in to comment.