This repository has been archived by the owner on Jan 23, 2023. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 2.7k
Span<T> api update #8583
Merged
Merged
Span<T> api update #8583
Changes from 2 commits
Commits
Show all changes
4 commits
Select commit
Hold shift + click to select a range
a678cc3
Changing method/property order to match CoreFX impl
mattwarren 3652e65
Added other missing methods to match CoreFX impl
mattwarren cb8fe6a
Merge remote-tracking branch 'dotnet/master' into SpanApiUpdate
mattwarren 4640176
Addressing code review comments
mattwarren File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -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> | ||
/// 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) | ||
|
@@ -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 (<0 or >=Length). | ||
/// </exception> | ||
|
@@ -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 (<0 or >=Length). | ||
/// </exception> | ||
|
@@ -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. | ||
|
@@ -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> | ||
|
@@ -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> | ||
|
@@ -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> | ||
|
@@ -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 (<0 or >&eq;Length). | ||
/// </exception> | ||
|
@@ -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() | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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. There was a problem hiding this comment. Choose a reason for hiding this commentThe 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 | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
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.)
There was a problem hiding this comment.
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