diff --git a/src/Common/src/CoreLib/System/ReadOnlySpan.Fast.cs b/src/Common/src/CoreLib/System/ReadOnlySpan.Fast.cs new file mode 100644 index 000000000000..5c627cc166be --- /dev/null +++ b/src/Common/src/CoreLib/System/ReadOnlySpan.Fast.cs @@ -0,0 +1,262 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.ComponentModel; +using System.Diagnostics; +using System.Runtime.CompilerServices; +using System.Runtime.Versioning; +using Internal.Runtime.CompilerServices; + +#pragma warning disable 0809 //warning CS0809: Obsolete member 'Span.Equals(object)' overrides non-obsolete member 'object.Equals(object)' + +#if BIT64 +using nuint = System.UInt64; +#else +using nuint = System.UInt32; +#endif + +namespace System +{ + /// + /// ReadOnlySpan represents a contiguous region of arbitrary memory. Unlike arrays, it can point to either managed + /// or native memory, or to memory allocated on the stack. It is type- and memory-safe. + /// + [DebuggerTypeProxy(typeof(SpanDebugView<>))] + [DebuggerDisplay("{DebuggerDisplay,nq}")] + [NonVersionable] + public readonly ref partial struct ReadOnlySpan + { + /// A byref or a native ptr. + internal readonly ByReference _pointer; + /// The number of elements this ReadOnlySpan contains. +#if PROJECTN + [Bound] +#endif + private readonly int _length; + + /// + /// Creates a new read-only span over the entirety of the target array. + /// + /// The target array. + /// Thrown when is a null + /// reference (Nothing in Visual Basic). + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public ReadOnlySpan(T[] array) + { + if (array == null) + ThrowHelper.ThrowArgumentNullException(ExceptionArgument.array); + + _pointer = new ByReference(ref Unsafe.As(ref array.GetRawSzArrayData())); + _length = array.Length; + } + + /// + /// Creates a new read-only span over the portion of the target array beginning + /// at 'start' index and ending at 'end' index (exclusive). + /// + /// The target array. + /// The index at which to begin the read-only span. + /// The number of items in the read-only span. + /// Thrown when is a null + /// reference (Nothing in Visual Basic). + /// + /// Thrown when the specified or end index is not in the range (<0 or >=Length). + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public ReadOnlySpan(T[] array, int start, int length) + { + if (array == null) + ThrowHelper.ThrowArgumentNullException(ExceptionArgument.array); + if ((uint)start > (uint)array.Length || (uint)length > (uint)(array.Length - start)) + ThrowHelper.ThrowArgumentOutOfRangeException(); + + _pointer = new ByReference(ref Unsafe.Add(ref Unsafe.As(ref array.GetRawSzArrayData()), start)); + _length = length; + } + + /// + /// Creates a new read-only span over the target unmanaged buffer. Clearly this + /// is quite dangerous, because we are creating arbitrarily typed T's + /// 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. + /// + /// An unmanaged pointer to memory. + /// The number of elements the memory contains. + /// + /// Thrown when is reference type or contains pointers and hence cannot be stored in unmanaged memory. + /// + /// + /// Thrown when the specified is negative. + /// + [CLSCompliant(false)] + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public unsafe ReadOnlySpan(void* pointer, int length) + { + if (RuntimeHelpers.IsReferenceOrContainsReferences()) + ThrowHelper.ThrowInvalidTypeWithPointersNotSupported(typeof(T)); + if (length < 0) + ThrowHelper.ThrowArgumentOutOfRangeException(); + + _pointer = new ByReference(ref Unsafe.As(ref *(byte*)pointer)); + _length = length; + } + + // Constructor for internal use only. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + internal ReadOnlySpan(ref T ptr, int length) + { + Debug.Assert(length >= 0); + + _pointer = new ByReference(ref ptr); + _length = length; + } + + /// + /// Returns the specified element of the read-only span. + /// + /// + /// + /// + /// Thrown when index less than 0 or index greater than or equal to Length + /// + public ref readonly T this[int index] + { +#if PROJECTN + [BoundsChecking] + get + { + return ref Unsafe.Add(ref _pointer.Value, index); + } +#else + [Intrinsic] + [MethodImpl(MethodImplOptions.AggressiveInlining)] + [NonVersionable] + get + { + if ((uint)index >= (uint)_length) + ThrowHelper.ThrowIndexOutOfRangeException(); + return ref Unsafe.Add(ref _pointer.Value, index); + } +#endif + } + + /// + /// Copies the contents of this read-only 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. + /// + /// The span to copy items into. + /// + /// Thrown when the destination Span is shorter than the source Span. + /// + /// + public void CopyTo(Span destination) + { + // Using "if (!TryCopyTo(...))" results in two branches: one for the length + // check, and one for the result of TryCopyTo. Since these checks are equivalent, + // we can optimize by performing the check once ourselves then calling Memmove directly. + + if ((uint)_length <= (uint)destination.Length) + { + Buffer.Memmove(ref destination._pointer.Value, ref _pointer.Value, (nuint)_length); + } + else + { + ThrowHelper.ThrowArgumentException_DestinationTooShort(); + } + } + + /// Copies the contents of this read-only 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. + /// + /// If the destination span is shorter than the source span, this method + /// return false and no data is written to the destination. + /// The span to copy items into. + public bool TryCopyTo(Span destination) + { + bool retVal = false; + if ((uint)_length <= (uint)destination.Length) + { + Buffer.Memmove(ref destination._pointer.Value, ref _pointer.Value, (nuint)_length); + retVal = true; + } + return retVal; + } + + /// + /// 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. + /// + public static bool operator ==(ReadOnlySpan left, ReadOnlySpan right) + { + return left._length == right._length && Unsafe.AreSame(ref left._pointer.Value, ref right._pointer.Value); + } + + /// + /// For , returns a new instance of string that represents the characters pointed to by the span. + /// Otherwise, returns a with the name of the type and the number of elements. + /// + public override string ToString() + { + if (typeof(T) == typeof(char)) + { + unsafe + { + fixed (char* src = &Unsafe.As(ref _pointer.Value)) + return new string(src, 0, _length); + } + } + return string.Format("System.ReadOnlySpan<{0}>[{1}]", typeof(T).Name, _length); + } + + /// + /// Forms a slice out of the given read-only span, beginning at 'start'. + /// + /// The index at which to begin this slice. + /// + /// Thrown when the specified index is not in range (<0 or >=Length). + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public ReadOnlySpan Slice(int start) + { + if ((uint)start > (uint)_length) + ThrowHelper.ThrowArgumentOutOfRangeException(); + + return new ReadOnlySpan(ref Unsafe.Add(ref _pointer.Value, start), _length - start); + } + + /// + /// Forms a slice out of the given read-only span, beginning at 'start', of given length + /// + /// The index at which to begin this slice. + /// The desired length for the slice (exclusive). + /// + /// Thrown when the specified or end index is not in range (<0 or >=Length). + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public ReadOnlySpan Slice(int start, int length) + { + if ((uint)start > (uint)_length || (uint)length > (uint)(_length - start)) + ThrowHelper.ThrowArgumentOutOfRangeException(); + + return new ReadOnlySpan(ref Unsafe.Add(ref _pointer.Value, start), length); + } + + /// + /// Copies the contents of this read-only 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. + /// + public T[] ToArray() + { + if (_length == 0) + return Array.Empty(); + + var destination = new T[_length]; + Buffer.Memmove(ref Unsafe.As(ref destination.GetRawSzArrayData()), ref _pointer.Value, (nuint)_length); + return destination; + } + } +} diff --git a/src/Common/src/CoreLib/System/ReadOnlySpan.cs b/src/Common/src/CoreLib/System/ReadOnlySpan.cs index a7d173673e07..f0f022605e90 100644 --- a/src/Common/src/CoreLib/System/ReadOnlySpan.cs +++ b/src/Common/src/CoreLib/System/ReadOnlySpan.cs @@ -5,17 +5,12 @@ using System.ComponentModel; using System.Diagnostics; using System.Runtime.CompilerServices; +#if !FEATURE_PORTABLE_SPAN using System.Runtime.Versioning; -using Internal.Runtime.CompilerServices; +#endif // !FEATURE_PORTABLE_SPAN #pragma warning disable 0809 //warning CS0809: Obsolete member 'Span.Equals(object)' overrides non-obsolete member 'object.Equals(object)' -#if BIT64 -using nuint = System.UInt64; -#else -using nuint = System.UInt32; -#endif - namespace System { /// @@ -24,200 +19,35 @@ namespace System /// [DebuggerTypeProxy(typeof(SpanDebugView<>))] [DebuggerDisplay("{DebuggerDisplay,nq}")] - [NonVersionable] - public readonly ref struct ReadOnlySpan + public readonly ref partial struct ReadOnlySpan { - /// A byref or a native ptr. - internal readonly ByReference _pointer; - /// The number of elements this ReadOnlySpan contains. -#if PROJECTN - [Bound] -#endif - private readonly int _length; - - /// - /// Creates a new read-only span over the entirety of the target array. - /// - /// The target array. - /// Thrown when is a null - /// reference (Nothing in Visual Basic). - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public ReadOnlySpan(T[] array) - { - if (array == null) - ThrowHelper.ThrowArgumentNullException(ExceptionArgument.array); - - _pointer = new ByReference(ref Unsafe.As(ref array.GetRawSzArrayData())); - _length = array.Length; - } - - /// - /// Creates a new read-only span over the portion of the target array beginning - /// at 'start' index and ending at 'end' index (exclusive). - /// - /// The target array. - /// The index at which to begin the read-only span. - /// The number of items in the read-only span. - /// Thrown when is a null - /// reference (Nothing in Visual Basic). - /// - /// Thrown when the specified or end index is not in the range (<0 or >=Length). - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public ReadOnlySpan(T[] array, int start, int length) - { - if (array == null) - ThrowHelper.ThrowArgumentNullException(ExceptionArgument.array); - if ((uint)start > (uint)array.Length || (uint)length > (uint)(array.Length - start)) - ThrowHelper.ThrowArgumentOutOfRangeException(); - - _pointer = new ByReference(ref Unsafe.Add(ref Unsafe.As(ref array.GetRawSzArrayData()), start)); - _length = length; - } - - /// - /// Creates a new read-only span over the target unmanaged buffer. Clearly this - /// is quite dangerous, because we are creating arbitrarily typed T's - /// 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. - /// - /// An unmanaged pointer to memory. - /// The number of elements the memory contains. - /// - /// Thrown when is reference type or contains pointers and hence cannot be stored in unmanaged memory. - /// - /// - /// Thrown when the specified is negative. - /// - [CLSCompliant(false)] - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public unsafe ReadOnlySpan(void* pointer, int length) - { - if (RuntimeHelpers.IsReferenceOrContainsReferences()) - ThrowHelper.ThrowInvalidTypeWithPointersNotSupported(typeof(T)); - if (length < 0) - ThrowHelper.ThrowArgumentOutOfRangeException(); - - _pointer = new ByReference(ref Unsafe.As(ref *(byte*)pointer)); - _length = length; - } - - // Constructor for internal use only. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - internal ReadOnlySpan(ref T ptr, int length) - { - Debug.Assert(length >= 0); - - _pointer = new ByReference(ref ptr); - _length = length; - } - /// /// The number of items in the read-only span. /// public int Length { +#if !FEATURE_PORTABLE_SPAN [NonVersionable] - get - { - return _length; - } - } +#endif // !FEATURE_PORTABLE_SPAN + get + { + return _length; + } + } /// /// Returns true if Length is 0. /// public bool IsEmpty { +#if !FEATURE_PORTABLE_SPAN [NonVersionable] +#endif // !FEATURE_PORTABLE_SPAN get { return _length == 0; } } - - /// - /// Returns the specified element of the read-only span. - /// - /// - /// - /// - /// Thrown when index less than 0 or index greater than or equal to Length - /// - public ref readonly T this[int index] - { -#if PROJECTN - [BoundsChecking] - get - { - return ref Unsafe.Add(ref _pointer.Value, index); - } -#else - [Intrinsic] - [MethodImpl(MethodImplOptions.AggressiveInlining)] - [NonVersionable] - get - { - if ((uint)index >= (uint)_length) - ThrowHelper.ThrowIndexOutOfRangeException(); - return ref Unsafe.Add(ref _pointer.Value, index); - } -#endif - } - - /// - /// Copies the contents of this read-only 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. - /// - /// The span to copy items into. - /// - /// Thrown when the destination Span is shorter than the source Span. - /// - /// - public void CopyTo(Span destination) - { - // Using "if (!TryCopyTo(...))" results in two branches: one for the length - // check, and one for the result of TryCopyTo. Since these checks are equivalent, - // we can optimize by performing the check once ourselves then calling Memmove directly. - - if ((uint)_length <= (uint)destination.Length) - { - Buffer.Memmove(ref destination._pointer.Value, ref _pointer.Value, (nuint)_length); - } - else - { - ThrowHelper.ThrowArgumentException_DestinationTooShort(); - } - } - - /// Copies the contents of this read-only 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. - /// - /// If the destination span is shorter than the source span, this method - /// return false and no data is written to the destination. - /// The span to copy items into. - public bool TryCopyTo(Span destination) - { - bool retVal = false; - if ((uint)_length <= (uint)destination.Length) - { - Buffer.Memmove(ref destination._pointer.Value, ref _pointer.Value, (nuint)_length); - retVal = true; - } - return retVal; - } - - /// - /// 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. - /// - public static bool operator ==(ReadOnlySpan left, ReadOnlySpan right) - { - return left._length == right._length && Unsafe.AreSame(ref left._pointer.Value, ref right._pointer.Value); - } - /// /// 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. @@ -250,23 +80,6 @@ public override int GetHashCode() throw new NotSupportedException(SR.NotSupported_CannotCallGetHashCodeOnSpan); } - /// - /// For , returns a new instance of string that represents the characters pointed to by the span. - /// Otherwise, returns a with the name of the type and the number of elements. - /// - public override string ToString() - { - if (typeof(T) == typeof(char)) - { - unsafe - { - fixed (char* src = &Unsafe.As(ref _pointer.Value)) - return new string(src, 0, _length); - } - } - return string.Format("System.ReadOnlySpan<{0}>[{1}]", typeof(T).Name, _length); - } - /// /// Defines an implicit conversion of an array to a /// @@ -278,54 +91,6 @@ public override string ToString() public static implicit operator ReadOnlySpan(ArraySegment arraySegment) => arraySegment.Array != null ? new ReadOnlySpan(arraySegment.Array, arraySegment.Offset, arraySegment.Count) : default; - /// - /// Forms a slice out of the given read-only span, beginning at 'start'. - /// - /// The index at which to begin this slice. - /// - /// Thrown when the specified index is not in range (<0 or >=Length). - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public ReadOnlySpan Slice(int start) - { - if ((uint)start > (uint)_length) - ThrowHelper.ThrowArgumentOutOfRangeException(); - - return new ReadOnlySpan(ref Unsafe.Add(ref _pointer.Value, start), _length - start); - } - - /// - /// Forms a slice out of the given read-only span, beginning at 'start', of given length - /// - /// The index at which to begin this slice. - /// The desired length for the slice (exclusive). - /// - /// Thrown when the specified or end index is not in range (<0 or >=Length). - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public ReadOnlySpan Slice(int start, int length) - { - if ((uint)start > (uint)_length || (uint)length > (uint)(_length - start)) - ThrowHelper.ThrowArgumentOutOfRangeException(); - - return new ReadOnlySpan(ref Unsafe.Add(ref _pointer.Value, start), length); - } - - /// - /// Copies the contents of this read-only 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. - /// - public T[] ToArray() - { - if (_length == 0) - return Array.Empty(); - - var destination = new T[_length]; - Buffer.Memmove(ref Unsafe.As(ref destination.GetRawSzArrayData()), ref _pointer.Value, (nuint)_length); - return destination; - } - /// /// Returns a 0-length read-only span whose base is the null pointer. /// diff --git a/src/Common/src/CoreLib/System/Span.Fast.cs b/src/Common/src/CoreLib/System/Span.Fast.cs new file mode 100644 index 000000000000..1382e3d214a5 --- /dev/null +++ b/src/Common/src/CoreLib/System/Span.Fast.cs @@ -0,0 +1,342 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.ComponentModel; +using System.Diagnostics; +using System.Runtime.CompilerServices; +using System.Runtime.Versioning; +using Internal.Runtime.CompilerServices; + +#pragma warning disable 0809 //warning CS0809: Obsolete member 'Span.Equals(object)' overrides non-obsolete member 'object.Equals(object)' + +#if BIT64 +using nuint = System.UInt64; +#else +using nuint = System.UInt32; +#endif + +namespace System +{ + /// + /// Span represents a contiguous region of arbitrary memory. Unlike arrays, it can point to either managed + /// or native memory, or to memory allocated on the stack. It is type- and memory-safe. + /// + [DebuggerTypeProxy(typeof(SpanDebugView<>))] + [DebuggerDisplay("{DebuggerDisplay,nq}")] + [NonVersionable] + public readonly ref partial struct Span + { + /// A byref or a native ptr. + internal readonly ByReference _pointer; + /// The number of elements this Span contains. +#if PROJECTN + [Bound] +#endif + private readonly int _length; + + /// + /// Creates a new span over the entirety of the target array. + /// + /// The target array. + /// Thrown when is a null + /// reference (Nothing in Visual Basic). + /// Thrown when is covariant and array's type is not exactly T[]. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public Span(T[] array) + { + if (array == null) + ThrowHelper.ThrowArgumentNullException(ExceptionArgument.array); + if (default(T) == null && array.GetType() != typeof(T[])) + ThrowHelper.ThrowArrayTypeMismatchException(); + + _pointer = new ByReference(ref Unsafe.As(ref array.GetRawSzArrayData())); + _length = array.Length; + } + + /// + /// Creates a new span over the portion of the target array beginning + /// at 'start' index and ending at 'end' index (exclusive). + /// + /// The target array. + /// The index at which to begin the span. + /// The number of items in the span. + /// Thrown when is a null + /// reference (Nothing in Visual Basic). + /// Thrown when is covariant and array's type is not exactly T[]. + /// + /// Thrown when the specified or end index is not in the range (<0 or >=Length). + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public Span(T[] array, int start, int length) + { + if (array == null) + ThrowHelper.ThrowArgumentNullException(ExceptionArgument.array); + if (default(T) == null && array.GetType() != typeof(T[])) + ThrowHelper.ThrowArrayTypeMismatchException(); + if ((uint)start > (uint)array.Length || (uint)length > (uint)(array.Length - start)) + ThrowHelper.ThrowArgumentOutOfRangeException(); + + _pointer = new ByReference(ref Unsafe.Add(ref Unsafe.As(ref array.GetRawSzArrayData()), start)); + _length = length; + } + + /// + /// Creates a new span over the target unmanaged buffer. Clearly this + /// is quite dangerous, because we are creating arbitrarily typed T's + /// 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. + /// + /// An unmanaged pointer to memory. + /// The number of elements the memory contains. + /// + /// Thrown when is reference type or contains pointers and hence cannot be stored in unmanaged memory. + /// + /// + /// Thrown when the specified is negative. + /// + [CLSCompliant(false)] + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public unsafe Span(void* pointer, int length) + { + if (RuntimeHelpers.IsReferenceOrContainsReferences()) + ThrowHelper.ThrowInvalidTypeWithPointersNotSupported(typeof(T)); + if (length < 0) + ThrowHelper.ThrowArgumentOutOfRangeException(); + + _pointer = new ByReference(ref Unsafe.As(ref *(byte*)pointer)); + _length = length; + } + + // Constructor for internal use only. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + internal Span(ref T ptr, int length) + { + Debug.Assert(length >= 0); + + _pointer = new ByReference(ref ptr); + _length = length; + } + + /// Returns a reference to specified element of the Span. + /// + /// + /// + /// + /// Thrown when index less than 0 or index greater than or equal to Length + /// + public ref T this[int index] + { +#if PROJECTN + [BoundsChecking] + get + { + return ref Unsafe.Add(ref _pointer.Value, index); + } +#else + [Intrinsic] + [MethodImpl(MethodImplOptions.AggressiveInlining)] + [NonVersionable] + get + { + if ((uint)index >= (uint)_length) + ThrowHelper.ThrowIndexOutOfRangeException(); + return ref Unsafe.Add(ref _pointer.Value, index); + } +#endif + } + + /// + /// Clears the contents of this span. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void Clear() + { + if (RuntimeHelpers.IsReferenceOrContainsReferences()) + { + Span.ClearWithReferences(ref Unsafe.As(ref _pointer.Value), (nuint)_length * (nuint)(Unsafe.SizeOf() / sizeof(nuint))); + } + else + { + Span.ClearWithoutReferences(ref Unsafe.As(ref _pointer.Value), (nuint)_length * (nuint)Unsafe.SizeOf()); + } + } + + /// + /// Fills the contents of this span with the given value. + /// + public void Fill(T value) + { + if (Unsafe.SizeOf() == 1) + { + uint length = (uint)_length; + if (length == 0) + return; + + T tmp = value; // Avoid taking address of the "value" argument. It would regress performance of the loop below. + Unsafe.InitBlockUnaligned(ref Unsafe.As(ref _pointer.Value), Unsafe.As(ref tmp), length); + } + else + { + // Do all math as nuint to avoid unnecessary 64->32->64 bit integer truncations + nuint length = (uint)_length; + if (length == 0) + return; + + ref T r = ref _pointer.Value; + + // TODO: Create block fill for value types of power of two sizes e.g. 2,4,8,16 + + nuint elementSize = (uint)Unsafe.SizeOf(); + nuint i = 0; + for (; i < (length & ~(nuint)7); i += 8) + { + Unsafe.AddByteOffset(ref r, (i + 0) * elementSize) = value; + Unsafe.AddByteOffset(ref r, (i + 1) * elementSize) = value; + Unsafe.AddByteOffset(ref r, (i + 2) * elementSize) = value; + Unsafe.AddByteOffset(ref r, (i + 3) * elementSize) = value; + Unsafe.AddByteOffset(ref r, (i + 4) * elementSize) = value; + Unsafe.AddByteOffset(ref r, (i + 5) * elementSize) = value; + Unsafe.AddByteOffset(ref r, (i + 6) * elementSize) = value; + Unsafe.AddByteOffset(ref r, (i + 7) * elementSize) = value; + } + if (i < (length & ~(nuint)3)) + { + Unsafe.AddByteOffset(ref r, (i + 0) * elementSize) = value; + Unsafe.AddByteOffset(ref r, (i + 1) * elementSize) = value; + Unsafe.AddByteOffset(ref r, (i + 2) * elementSize) = value; + Unsafe.AddByteOffset(ref r, (i + 3) * elementSize) = value; + i += 4; + } + for (; i < length; i++) + { + Unsafe.AddByteOffset(ref r, i * elementSize) = value; + } + } + } + + /// + /// 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. + /// + /// The span to copy items into. + /// + /// Thrown when the destination Span is shorter than the source Span. + /// + public void CopyTo(Span destination) + { + // Using "if (!TryCopyTo(...))" results in two branches: one for the length + // check, and one for the result of TryCopyTo. Since these checks are equivalent, + // we can optimize by performing the check once ourselves then calling Memmove directly. + + if ((uint)_length <= (uint)destination.Length) + { + Buffer.Memmove(ref destination._pointer.Value, ref _pointer.Value, (nuint)_length); + } + else + { + ThrowHelper.ThrowArgumentException_DestinationTooShort(); + } + } + + /// + /// 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. + /// + /// The span to copy items into. + /// If the destination span is shorter than the source span, this method + /// return false and no data is written to the destination. + public bool TryCopyTo(Span destination) + { + bool retVal = false; + if ((uint)_length <= (uint)destination.Length) + { + Buffer.Memmove(ref destination._pointer.Value, ref _pointer.Value, (nuint)_length); + retVal = true; + } + return retVal; + } + + /// + /// 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. + /// + public static bool operator ==(Span left, Span right) + { + return left._length == right._length && Unsafe.AreSame(ref left._pointer.Value, ref right._pointer.Value); + } + + /// + /// Defines an implicit conversion of a to a + /// + public static implicit operator ReadOnlySpan(Span span) => new ReadOnlySpan(ref span._pointer.Value, span._length); + + /// + /// For , returns a new instance of string that represents the characters pointed to by the span. + /// Otherwise, returns a with the name of the type and the number of elements. + /// + public override string ToString() + { + if (typeof(T) == typeof(char)) + { + unsafe + { + fixed (char* src = &Unsafe.As(ref _pointer.Value)) + return new string(src, 0, _length); + } + } + return string.Format("System.Span<{0}>[{1}]", typeof(T).Name, _length); + } + + /// + /// Forms a slice out of the given span, beginning at 'start'. + /// + /// The index at which to begin this slice. + /// + /// Thrown when the specified index is not in range (<0 or >=Length). + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public Span Slice(int start) + { + if ((uint)start > (uint)_length) + ThrowHelper.ThrowArgumentOutOfRangeException(); + + return new Span(ref Unsafe.Add(ref _pointer.Value, start), _length - start); + } + + /// + /// Forms a slice out of the given span, beginning at 'start', of given length + /// + /// The index at which to begin this slice. + /// The desired length for the slice (exclusive). + /// + /// Thrown when the specified or end index is not in range (<0 or >=Length). + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public Span Slice(int start, int length) + { + if ((uint)start > (uint)_length || (uint)length > (uint)(_length - start)) + ThrowHelper.ThrowArgumentOutOfRangeException(); + + return new Span(ref Unsafe.Add(ref _pointer.Value, start), length); + } + + /// + /// 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. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public T[] ToArray() + { + if (_length == 0) + return Array.Empty(); + + var destination = new T[_length]; + Buffer.Memmove(ref Unsafe.As(ref destination.GetRawSzArrayData()), ref _pointer.Value, (nuint)_length); + return destination; + } + } +} diff --git a/src/Common/src/CoreLib/System/Span.cs b/src/Common/src/CoreLib/System/Span.cs index fd5ad269172c..f5918110dd46 100644 --- a/src/Common/src/CoreLib/System/Span.cs +++ b/src/Common/src/CoreLib/System/Span.cs @@ -5,17 +5,12 @@ using System.ComponentModel; using System.Diagnostics; using System.Runtime.CompilerServices; +#if !FEATURE_PORTABLE_SPAN using System.Runtime.Versioning; -using Internal.Runtime.CompilerServices; +#endif // !FEATURE_PORTABLE_SPAN #pragma warning disable 0809 //warning CS0809: Obsolete member 'Span.Equals(object)' overrides non-obsolete member 'object.Equals(object)' -#if BIT64 -using nuint = System.UInt64; -#else -using nuint = System.UInt32; -#endif - namespace System { /// @@ -24,275 +19,36 @@ namespace System /// [DebuggerTypeProxy(typeof(SpanDebugView<>))] [DebuggerDisplay("{DebuggerDisplay,nq}")] - [NonVersionable] - public readonly ref struct Span + public readonly ref partial struct Span { - /// A byref or a native ptr. - internal readonly ByReference _pointer; - /// The number of elements this Span contains. -#if PROJECTN - [Bound] -#endif - private readonly int _length; - - /// - /// Creates a new span over the entirety of the target array. - /// - /// The target array. - /// Thrown when is a null - /// reference (Nothing in Visual Basic). - /// Thrown when is covariant and array's type is not exactly T[]. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public Span(T[] array) - { - if (array == null) - ThrowHelper.ThrowArgumentNullException(ExceptionArgument.array); - if (default(T) == null && array.GetType() != typeof(T[])) - ThrowHelper.ThrowArrayTypeMismatchException(); - - _pointer = new ByReference(ref Unsafe.As(ref array.GetRawSzArrayData())); - _length = array.Length; - } - - /// - /// Creates a new span over the portion of the target array beginning - /// at 'start' index and ending at 'end' index (exclusive). - /// - /// The target array. - /// The index at which to begin the span. - /// The number of items in the span. - /// Thrown when is a null - /// reference (Nothing in Visual Basic). - /// Thrown when is covariant and array's type is not exactly T[]. - /// - /// Thrown when the specified or end index is not in the range (<0 or >=Length). - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public Span(T[] array, int start, int length) - { - if (array == null) - ThrowHelper.ThrowArgumentNullException(ExceptionArgument.array); - if (default(T) == null && array.GetType() != typeof(T[])) - ThrowHelper.ThrowArrayTypeMismatchException(); - if ((uint)start > (uint)array.Length || (uint)length > (uint)(array.Length - start)) - ThrowHelper.ThrowArgumentOutOfRangeException(); - - _pointer = new ByReference(ref Unsafe.Add(ref Unsafe.As(ref array.GetRawSzArrayData()), start)); - _length = length; - } - - /// - /// Creates a new span over the target unmanaged buffer. Clearly this - /// is quite dangerous, because we are creating arbitrarily typed T's - /// 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. - /// - /// An unmanaged pointer to memory. - /// The number of elements the memory contains. - /// - /// Thrown when is reference type or contains pointers and hence cannot be stored in unmanaged memory. - /// - /// - /// Thrown when the specified is negative. - /// - [CLSCompliant(false)] - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public unsafe Span(void* pointer, int length) - { - if (RuntimeHelpers.IsReferenceOrContainsReferences()) - ThrowHelper.ThrowInvalidTypeWithPointersNotSupported(typeof(T)); - if (length < 0) - ThrowHelper.ThrowArgumentOutOfRangeException(); - - _pointer = new ByReference(ref Unsafe.As(ref *(byte*)pointer)); - _length = length; - } - - // Constructor for internal use only. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - internal Span(ref T ptr, int length) - { - Debug.Assert(length >= 0); - - _pointer = new ByReference(ref ptr); - _length = length; - } - /// /// The number of items in the span. /// public int Length { +#if !FEATURE_PORTABLE_SPAN [NonVersionable] - get - { - return _length; - } - } +#endif // !FEATURE_PORTABLE_SPAN + get + { + return _length; + } + } /// /// Returns true if Length is 0. /// public bool IsEmpty { +#if !FEATURE_PORTABLE_SPAN [NonVersionable] +#endif // !FEATURE_PORTABLE_SPAN get { return _length == 0; } } - /// - /// Returns a reference to specified element of the Span. - /// - /// - /// - /// - /// Thrown when index less than 0 or index greater than or equal to Length - /// - public ref T this[int index] - { -#if PROJECTN - [BoundsChecking] - get - { - return ref Unsafe.Add(ref _pointer.Value, index); - } -#else - [Intrinsic] - [MethodImpl(MethodImplOptions.AggressiveInlining)] - [NonVersionable] - get - { - if ((uint)index >= (uint)_length) - ThrowHelper.ThrowIndexOutOfRangeException(); - return ref Unsafe.Add(ref _pointer.Value, index); - } -#endif - } - - /// - /// Clears the contents of this span. - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void Clear() - { - if (RuntimeHelpers.IsReferenceOrContainsReferences()) - { - Span.ClearWithReferences(ref Unsafe.As(ref _pointer.Value), (nuint)_length * (nuint)(Unsafe.SizeOf() / sizeof(nuint))); - } - else - { - Span.ClearWithoutReferences(ref Unsafe.As(ref _pointer.Value), (nuint)_length * (nuint)Unsafe.SizeOf()); - } - } - - /// - /// Fills the contents of this span with the given value. - /// - public void Fill(T value) - { - if (Unsafe.SizeOf() == 1) - { - uint length = (uint)_length; - if (length == 0) - return; - - T tmp = value; // Avoid taking address of the "value" argument. It would regress performance of the loop below. - Unsafe.InitBlockUnaligned(ref Unsafe.As(ref _pointer.Value), Unsafe.As(ref tmp), length); - } - else - { - // Do all math as nuint to avoid unnecessary 64->32->64 bit integer truncations - nuint length = (uint)_length; - if (length == 0) - return; - - ref T r = ref _pointer.Value; - - // TODO: Create block fill for value types of power of two sizes e.g. 2,4,8,16 - - nuint elementSize = (uint)Unsafe.SizeOf(); - nuint i = 0; - for (; i < (length & ~(nuint)7); i += 8) - { - Unsafe.AddByteOffset(ref r, (i + 0) * elementSize) = value; - Unsafe.AddByteOffset(ref r, (i + 1) * elementSize) = value; - Unsafe.AddByteOffset(ref r, (i + 2) * elementSize) = value; - Unsafe.AddByteOffset(ref r, (i + 3) * elementSize) = value; - Unsafe.AddByteOffset(ref r, (i + 4) * elementSize) = value; - Unsafe.AddByteOffset(ref r, (i + 5) * elementSize) = value; - Unsafe.AddByteOffset(ref r, (i + 6) * elementSize) = value; - Unsafe.AddByteOffset(ref r, (i + 7) * elementSize) = value; - } - if (i < (length & ~(nuint)3)) - { - Unsafe.AddByteOffset(ref r, (i + 0) * elementSize) = value; - Unsafe.AddByteOffset(ref r, (i + 1) * elementSize) = value; - Unsafe.AddByteOffset(ref r, (i + 2) * elementSize) = value; - Unsafe.AddByteOffset(ref r, (i + 3) * elementSize) = value; - i += 4; - } - for (; i < length; i++) - { - Unsafe.AddByteOffset(ref r, i * elementSize) = value; - } - } - } - - /// - /// 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. - /// - /// The span to copy items into. - /// - /// Thrown when the destination Span is shorter than the source Span. - /// - public void CopyTo(Span destination) - { - // Using "if (!TryCopyTo(...))" results in two branches: one for the length - // check, and one for the result of TryCopyTo. Since these checks are equivalent, - // we can optimize by performing the check once ourselves then calling Memmove directly. - - if ((uint)_length <= (uint)destination.Length) - { - Buffer.Memmove(ref destination._pointer.Value, ref _pointer.Value, (nuint)_length); - } - else - { - ThrowHelper.ThrowArgumentException_DestinationTooShort(); - } - } - - /// - /// 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. - /// - /// The span to copy items into. - /// If the destination span is shorter than the source span, this method - /// return false and no data is written to the destination. - public bool TryCopyTo(Span destination) - { - bool retVal = false; - if ((uint)_length <= (uint)destination.Length) - { - Buffer.Memmove(ref destination._pointer.Value, ref _pointer.Value, (nuint)_length); - retVal = true; - } - return retVal; - } - - /// - /// 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. - /// - public static bool operator ==(Span left, Span right) - { - return left._length == right._length && Unsafe.AreSame(ref left._pointer.Value, ref right._pointer.Value); - } - /// /// 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. @@ -325,23 +81,6 @@ public override int GetHashCode() throw new NotSupportedException(SR.NotSupported_CannotCallGetHashCodeOnSpan); } - /// - /// For , returns a new instance of string that represents the characters pointed to by the span. - /// Otherwise, returns a with the name of the type and the number of elements. - /// - public override string ToString() - { - if (typeof(T) == typeof(char)) - { - unsafe - { - fixed (char* src = &Unsafe.As(ref _pointer.Value)) - return new string(src, 0, _length); - } - } - return string.Format("System.Span<{0}>[{1}]", typeof(T).Name, _length); - } - /// /// Defines an implicit conversion of an array to a /// @@ -354,60 +93,6 @@ public static implicit operator Span(ArraySegment arraySegment) => arraySegment.Array != null ? new Span(arraySegment.Array, arraySegment.Offset, arraySegment.Count) : default; /// - /// Defines an implicit conversion of a to a - /// - public static implicit operator ReadOnlySpan(Span span) => new ReadOnlySpan(ref span._pointer.Value, span._length); - - /// - /// Forms a slice out of the given span, beginning at 'start'. - /// - /// The index at which to begin this slice. - /// - /// Thrown when the specified index is not in range (<0 or >=Length). - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public Span Slice(int start) - { - if ((uint)start > (uint)_length) - ThrowHelper.ThrowArgumentOutOfRangeException(); - - return new Span(ref Unsafe.Add(ref _pointer.Value, start), _length - start); - } - - /// - /// Forms a slice out of the given span, beginning at 'start', of given length - /// - /// The index at which to begin this slice. - /// The desired length for the slice (exclusive). - /// - /// Thrown when the specified or end index is not in range (<0 or >=Length). - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public Span Slice(int start, int length) - { - if ((uint)start > (uint)_length || (uint)length > (uint)(_length - start)) - ThrowHelper.ThrowArgumentOutOfRangeException(); - - return new Span(ref Unsafe.Add(ref _pointer.Value, start), length); - } - - /// - /// 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. - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public T[] ToArray() - { - if (_length == 0) - return Array.Empty(); - - var destination = new T[_length]; - Buffer.Memmove(ref Unsafe.As(ref destination.GetRawSzArrayData()), ref _pointer.Value, (nuint)_length); - return destination; - } - - // /// Returns an empty /// public static Span Empty => default(Span); diff --git a/src/System.Memory/src/Resources/Strings.resx b/src/System.Memory/src/Resources/Strings.resx index 219d3c94c541..802f75523e82 100644 --- a/src/System.Memory/src/Resources/Strings.resx +++ b/src/System.Memory/src/Resources/Strings.resx @@ -117,10 +117,10 @@ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - + Equals() on Span and ReadOnlySpan is not supported. Use operator== instead. - + GetHashCode() on Span and ReadOnlySpan is not supported. diff --git a/src/System.Memory/src/System.Memory.csproj b/src/System.Memory/src/System.Memory.csproj index 7150e7fe6d50..26dd461feb92 100644 --- a/src/System.Memory/src/System.Memory.csproj +++ b/src/System.Memory/src/System.Memory.csproj @@ -106,13 +106,15 @@ + + - - + + diff --git a/src/System.Memory/src/System/ReadOnlySpan.cs b/src/System.Memory/src/System/ReadOnlySpan.Portable.cs similarity index 73% rename from src/System.Memory/src/System/ReadOnlySpan.cs rename to src/System.Memory/src/System/ReadOnlySpan.Portable.cs index 9bb869ef44bf..a1025f35828f 100644 --- a/src/System.Memory/src/System/ReadOnlySpan.cs +++ b/src/System.Memory/src/System/ReadOnlySpan.Portable.cs @@ -17,7 +17,7 @@ namespace System /// [DebuggerTypeProxy(typeof(SpanDebugView<>))] [DebuggerDisplay("{DebuggerDisplay,nq}")] - public readonly ref struct ReadOnlySpan + public readonly ref partial struct ReadOnlySpan { /// /// Creates a new read-only span over the entirety of the target array. @@ -105,16 +105,6 @@ internal ReadOnlySpan(Pinnable pinnable, IntPtr byteOffset, int length) //Debugger Display = System.ReadOnlySpan[length] private string DebuggerDisplay => string.Format("System.ReadOnlySpan<{0}>[{1}]", typeof(T).Name, Length); - /// - /// The number of items in the read-only span. - /// - public int Length => _length; - - /// - /// Returns true if Length is 0. - /// - public bool IsEmpty => _length == 0; - /// /// Returns the specified element of the read-only span. /// @@ -189,55 +179,12 @@ public bool TryCopyTo(Span destination) return left._length == right._length && Unsafe.AreSame(ref left.DangerousGetPinnableReference(), ref right.DangerousGetPinnableReference()); } - /// - /// 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. - /// - public static bool operator !=(ReadOnlySpan left, ReadOnlySpan right) => !(left == right); - - /// - /// This method is not supported as spans cannot be boxed. To compare two spans, use operator==. - /// - /// Always thrown by this method. - /// - /// - [Obsolete("Equals() on ReadOnlySpan will always throw an exception. Use == instead.")] - [EditorBrowsable(EditorBrowsableState.Never)] - public override bool Equals(object obj) - { - throw new NotSupportedException(SR.CannotCallEqualsOnSpan); - } - - /// - /// This method is not supported as spans cannot be boxed. - /// - /// Always thrown by this method. - /// - /// - [Obsolete("GetHashCode() on ReadOnlySpan will always throw an exception.")] - [EditorBrowsable(EditorBrowsableState.Never)] - public override int GetHashCode() - { - throw new NotSupportedException(SR.CannotCallGetHashCodeOnSpan); - } - /// /// Returns a with the name of the type and the number of elements /// /// A with the name of the type and the number of elements public override string ToString() => string.Format("System.ReadOnlySpan<{0}>[{1}]", typeof(T).Name, _length); - /// - /// Defines an implicit conversion of an array to a - /// - public static implicit operator ReadOnlySpan(T[] array) => array != null ? new ReadOnlySpan(array) : default; - - /// - /// Defines an implicit conversion of a to a - /// - public static implicit operator ReadOnlySpan(ArraySegment arraySegment) - => arraySegment.Array != null ? new ReadOnlySpan(arraySegment.Array, arraySegment.Offset, arraySegment.Count) : default; - /// /// Forms a slice out of the given read-only span, beginning at 'start'. /// @@ -289,11 +236,6 @@ public T[] ToArray() return result; } - /// - /// Returns a 0-length read-only span whose base is the null pointer. - /// - public static ReadOnlySpan Empty => default(ReadOnlySpan); - /// /// This method is obsolete, use System.Runtime.InteropServices.MemoryMarshal.GetReference instead. /// 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 @@ -309,61 +251,6 @@ internal ref T DangerousGetPinnableReference() return ref Unsafe.AddByteOffset(ref _pinnable.Data, _byteOffset); } - /// Gets an enumerator for this span. - public Enumerator GetEnumerator() => new Enumerator(this); - - /// Enumerates the elements of a . - public ref struct Enumerator - { - /// The span being enumerated. - private readonly ReadOnlySpan _span; - /// The next index to yield. - private int _index; - - /// Initialize the enumerator. - /// The span to enumerate. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - internal Enumerator(ReadOnlySpan span) - { - _span = span; - _index = -1; - } - - /// Advances the enumerator to the next element of the span. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public bool MoveNext() - { - int index = _index + 1; - if (index < _span.Length) - { - _index = index; - return true; - } - - return false; - } - - /// Gets the element at the current position of the enumerator. - public ref readonly T Current - { - [MethodImpl(MethodImplOptions.AggressiveInlining)] - get - { - // TODO https://github.com/dotnet/corefx/issues/24105: - // Change this to simply be: - // get => ref _span[_index]; - // once ReadOnlySpan's indexer returns ref readonly. - - if ((uint)_index >= (uint)_span.Length) - { - ThrowHelper.ThrowIndexOutOfRangeException(); - } - - return ref Unsafe.Add(ref _span.DangerousGetPinnableReference(), _index); - } - } - } - // These expose the internal representation for Span-related apis use only. internal Pinnable Pinnable => _pinnable; internal IntPtr ByteOffset => _byteOffset; diff --git a/src/System.Memory/src/System/Span.cs b/src/System.Memory/src/System/Span.Portable.cs similarity index 81% rename from src/System.Memory/src/System/Span.cs rename to src/System.Memory/src/System/Span.Portable.cs index 471c530716e5..39162b07a11d 100644 --- a/src/System.Memory/src/System/Span.cs +++ b/src/System.Memory/src/System/Span.Portable.cs @@ -17,7 +17,7 @@ namespace System /// [DebuggerTypeProxy(typeof(SpanDebugView<>))] [DebuggerDisplay("{DebuggerDisplay,nq}")] - public readonly ref struct Span + public readonly ref partial struct Span { /// /// Creates a new span over the entirety of the target array. @@ -109,16 +109,6 @@ internal Span(Pinnable pinnable, IntPtr byteOffset, int length) //Debugger Display = System.Span[length] private string DebuggerDisplay => string.Format("System.Span<{0}>[{1}]", typeof(T).Name, _length); - /// - /// The number of items in the span. - /// - public int Length => _length; - - /// - /// Returns true if Length is 0. - /// - public bool IsEmpty => _length == 0; - /// /// Returns a reference to specified element of the Span. /// @@ -297,36 +287,9 @@ public bool TryCopyTo(Span destination) } /// - /// 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. - /// - public static bool operator !=(Span left, Span right) => !(left == right); - - /// - /// This method is not supported as spans cannot be boxed. To compare two spans, use operator==. - /// - /// Always thrown by this method. - /// - /// - [Obsolete("Equals() on Span will always throw an exception. Use == instead.")] - [EditorBrowsable(EditorBrowsableState.Never)] - public override bool Equals(object obj) - { - throw new NotSupportedException(SR.CannotCallEqualsOnSpan); - } - - /// - /// This method is not supported as spans cannot be boxed. - /// - /// Always thrown by this method. - /// + /// Defines an implicit conversion of a to a /// - [Obsolete("GetHashCode() on Span will always throw an exception.")] - [EditorBrowsable(EditorBrowsableState.Never)] - public override int GetHashCode() - { - throw new NotSupportedException(SR.CannotCallGetHashCodeOnSpan); - } + public static implicit operator ReadOnlySpan(Span span) => new ReadOnlySpan(span._pinnable, span._byteOffset, span._length); /// /// Returns a with the name of the type and the number of elements @@ -334,22 +297,6 @@ public override int GetHashCode() /// A with the name of the type and the number of elements public override string ToString() => string.Format("System.Span<{0}>[{1}]", typeof(T).Name, Length); - /// - /// Defines an implicit conversion of an array to a - /// - public static implicit operator Span(T[] array) => array != null ? new Span(array) : default; - - /// - /// Defines an implicit conversion of a to a - /// - public static implicit operator Span(ArraySegment arraySegment) - => arraySegment.Array != null ? new Span(arraySegment.Array, arraySegment.Offset, arraySegment.Count) : default; - - /// - /// Defines an implicit conversion of a to a - /// - public static implicit operator ReadOnlySpan(Span span) => new ReadOnlySpan(span._pinnable, span._byteOffset, span._length); - /// /// Forms a slice out of the given span, beginning at 'start'. /// @@ -401,11 +348,6 @@ public T[] ToArray() return result; } - /// - /// Returns a 0-length span whose base is the null pointer. - /// - public static Span Empty => default(Span); - /// /// This method is obsolete, use System.Runtime.InteropServices.MemoryMarshal.GetReference instead. /// 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 @@ -421,48 +363,6 @@ internal ref T DangerousGetPinnableReference() return ref Unsafe.AddByteOffset(ref _pinnable.Data, _byteOffset); } - /// Gets an enumerator for this span. - public Enumerator GetEnumerator() => new Enumerator(this); - - /// Enumerates the elements of a . - public ref struct Enumerator - { - /// The span being enumerated. - private readonly Span _span; - /// The next index to yield. - private int _index; - - /// Initialize the enumerator. - /// The span to enumerate. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - internal Enumerator(Span span) - { - _span = span; - _index = -1; - } - - /// Advances the enumerator to the next element of the span. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public bool MoveNext() - { - int index = _index + 1; - if (index < _span.Length) - { - _index = index; - return true; - } - - return false; - } - - /// Gets the element at the current position of the enumerator. - public ref T Current - { - [MethodImpl(MethodImplOptions.AggressiveInlining)] - get => ref _span[_index]; - } - } - // These expose the internal representation for Span-related apis use only. internal Pinnable Pinnable => _pinnable; internal IntPtr ByteOffset => _byteOffset;