// 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.Diagnostics; using System.Runtime.CompilerServices; using EditorBrowsableState = System.ComponentModel.EditorBrowsableState; using EditorBrowsableAttribute = System.ComponentModel.EditorBrowsableAttribute; #pragma warning disable 0809 //warning CS0809: Obsolete member 'Span.Equals(object)' overrides non-obsolete member 'object.Equals(object)' 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. /// public struct ReadOnlySpan { /// A byref or a native ptr. private readonly ByReference _pointer; /// The number of elements this ReadOnlySpan contains. 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 JitHelpers.GetArrayData(array)); _length = array.Length; } /// /// Creates a new read-only span over the portion of the target array beginning /// at 'start' index and covering the remainder of the array. /// /// The target array. /// The index at which to begin the read-only span. /// Thrown when is a null /// reference (Nothing in Visual Basic). /// /// Thrown when the specified is not in the range (<0 or >=Length). /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public ReadOnlySpan(T[] array, int start) { if (array == null) ThrowHelper.ThrowArgumentNullException(ExceptionArgument.array); if ((uint)start > (uint)array.Length) ThrowHelper.ThrowArgumentOutOfRangeException(); _pointer = new ByReference(ref Unsafe.Add(ref JitHelpers.GetArrayData(array), start)); _length = array.Length - start; } /// /// 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 JitHelpers.GetArrayData(array), 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 (JitHelpers.ContainsReferences()) ThrowHelper.ThrowInvalidTypeWithPointersNotSupported(typeof(T)); if (length < 0) ThrowHelper.ThrowArgumentOutOfRangeException(); _pointer = new ByReference(ref Unsafe.As(ref *(byte*)pointer)); _length = length; } /// /// Create a new read-only span over a portion of a regular managed object. This can be useful /// if part of a managed object represents a "fixed array." This is dangerous because /// "length" is not checked, nor is the fact that "rawPointer" actually lies within the object. /// /// The managed object that contains the data to span over. /// A reference to data within that object. /// The number of elements the memory contains. /// /// Thrown when the specified object is null. /// /// /// Thrown when the specified is negative. /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public static ReadOnlySpan DangerousCreate(object obj, ref T objectData, int length) { if (obj == null) ThrowHelper.ThrowArgumentNullException(ExceptionArgument.obj); if (length < 0) ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.length); return new ReadOnlySpan(ref objectData, 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 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. /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public ref T DangerousGetPinnableReference() { return ref _pointer.Value; } /// /// 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. /// /// /// /// /// Thrown when index less than 0 or index greater than or equal to Length /// public T this[int index] { [MethodImpl(MethodImplOptions.AggressiveInlining)] get { if ((uint)index >= (uint)_length) ThrowHelper.ThrowIndexOutOfRangeException(); return Unsafe.Add(ref _pointer.Value, index); } } /// /// 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) { if (!TryCopyTo(destination)) 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) { if ((uint)_length > (uint)destination.Length) return false; SpanHelper.CopyTo(ref destination.DangerousGetPinnableReference(), ref _pointer.Value, _length); return true; } /// /// 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. /// 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 Span will always throw an exception. Use == instead.")] [EditorBrowsable(EditorBrowsableState.Never)] public override bool Equals(object obj) { ThrowHelper.ThrowNotSupportedException_CannotCallEqualsOnSpan(); // Prevent compiler error CS0161: 'Span.Equals(object)': not all code paths return a value return default(bool); } /// /// This method is not supported as spans cannot be boxed. /// /// Always thrown by this method. /// /// [Obsolete("GetHashCode() on Span will always throw an exception.")] [EditorBrowsable(EditorBrowsableState.Never)] public override int GetHashCode() { ThrowHelper.ThrowNotSupportedException_CannotCallGetHashCodeOnSpan(); // Prevent compiler error CS0161: 'Span.GetHashCode()': not all code paths return a value return default(int); } /// /// Defines an implicit conversion of an array to a /// public static implicit operator ReadOnlySpan(T[] array) => new ReadOnlySpan(array); /// /// Defines an implicit conversion of a to a /// public static implicit operator ReadOnlySpan(ArraySegment arraySegment) => new ReadOnlySpan(arraySegment.Array, arraySegment.Offset, arraySegment.Count); /// /// 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]; SpanHelper.CopyTo(ref JitHelpers.GetArrayData(destination), ref _pointer.Value, _length); return destination; } /// /// Returns a 0-length read-only span whose base is the null pointer. /// public static ReadOnlySpan Empty => default(ReadOnlySpan); } }