1 // Licensed to the .NET Foundation under one or more agreements. 2 // The .NET Foundation licenses this file to you under the MIT license. 3 // See the LICENSE file in the project root for more information. 4 5 using System.ComponentModel; 6 using System.Diagnostics; 7 using System.Runtime.CompilerServices; 8 using System.Runtime.Versioning; 9 using Internal.Runtime.CompilerServices; 10 11 #pragma warning disable 0809 //warning CS0809: Obsolete member 'Span<T>.Equals(object)' overrides non-obsolete member 'object.Equals(object)' 12 13 namespace System 14 { 15 /// <summary> 16 /// ReadOnlySpan represents a contiguous region of arbitrary memory. Unlike arrays, it can point to either managed 17 /// or native memory, or to memory allocated on the stack. It is type- and memory-safe. 18 /// </summary> 19 [DebuggerTypeProxy(typeof(SpanDebugView<>))] 20 [DebuggerDisplay("{DebuggerDisplay,nq}")] 21 [NonVersionable] 22 public readonly ref struct ReadOnlySpan<T> 23 { 24 /// <summary>A byref or a native ptr.</summary> 25 internal readonly ByReference<T> _pointer; 26 /// <summary>The number of elements this ReadOnlySpan contains.</summary> 27 #if PROJECTN 28 [Bound] 29 #endif 30 private readonly int _length; 31 32 /// <summary> 33 /// Creates a new read-only span over the entirety of the target array. 34 /// </summary> 35 /// <param name="array">The target array.</param> 36 /// <exception cref="System.ArgumentNullException">Thrown when <paramref name="array"/> is a null 37 /// reference (Nothing in Visual Basic).</exception> 38 [MethodImpl(MethodImplOptions.AggressiveInlining)] ReadOnlySpanSystem.ReadOnlySpan39 public ReadOnlySpan(T[] array) 40 { 41 if (array == null) 42 ThrowHelper.ThrowArgumentNullException(ExceptionArgument.array); 43 44 _pointer = new ByReference<T>(ref Unsafe.As<byte, T>(ref array.GetRawSzArrayData())); 45 _length = array.Length; 46 } 47 48 /// <summary> 49 /// Creates a new read-only span over the portion of the target array beginning 50 /// at 'start' index and ending at 'end' index (exclusive). 51 /// </summary> 52 /// <param name="array">The target array.</param> 53 /// <param name="start">The index at which to begin the read-only span.</param> 54 /// <param name="length">The number of items in the read-only span.</param> 55 /// <exception cref="System.ArgumentNullException">Thrown when <paramref name="array"/> is a null 56 /// reference (Nothing in Visual Basic).</exception> 57 /// <exception cref="System.ArgumentOutOfRangeException"> 58 /// Thrown when the specified <paramref name="start"/> or end index is not in the range (<0 or >=Length). 59 /// </exception> 60 [MethodImpl(MethodImplOptions.AggressiveInlining)] ReadOnlySpanSystem.ReadOnlySpan61 public ReadOnlySpan(T[] array, int start, int length) 62 { 63 if (array == null) 64 ThrowHelper.ThrowArgumentNullException(ExceptionArgument.array); 65 if ((uint)start > (uint)array.Length || (uint)length > (uint)(array.Length - start)) 66 ThrowHelper.ThrowArgumentOutOfRangeException(); 67 68 _pointer = new ByReference<T>(ref Unsafe.Add(ref Unsafe.As<byte, T>(ref array.GetRawSzArrayData()), start)); 69 _length = length; 70 } 71 72 /// <summary> 73 /// Creates a new read-only span over the target unmanaged buffer. Clearly this 74 /// is quite dangerous, because we are creating arbitrarily typed T's 75 /// out of a void*-typed block of memory. And the length is not checked. 76 /// But if this creation is correct, then all subsequent uses are correct. 77 /// </summary> 78 /// <param name="pointer">An unmanaged pointer to memory.</param> 79 /// <param name="length">The number of <typeparamref name="T"/> elements the memory contains.</param> 80 /// <exception cref="System.ArgumentException"> 81 /// Thrown when <typeparamref name="T"/> is reference type or contains pointers and hence cannot be stored in unmanaged memory. 82 /// </exception> 83 /// <exception cref="System.ArgumentOutOfRangeException"> 84 /// Thrown when the specified <paramref name="length"/> is negative. 85 /// </exception> 86 [CLSCompliant(false)] 87 [MethodImpl(MethodImplOptions.AggressiveInlining)] ReadOnlySpanSystem.ReadOnlySpan88 public unsafe ReadOnlySpan(void* pointer, int length) 89 { 90 if (RuntimeHelpers.IsReferenceOrContainsReferences<T>()) 91 ThrowHelper.ThrowInvalidTypeWithPointersNotSupported(typeof(T)); 92 if (length < 0) 93 ThrowHelper.ThrowArgumentOutOfRangeException(); 94 95 _pointer = new ByReference<T>(ref Unsafe.As<byte, T>(ref *(byte*)pointer)); 96 _length = length; 97 } 98 99 /// <summary> 100 /// Create a new read-only span over a portion of a regular managed object. This can be useful 101 /// if part of a managed object represents a "fixed array." This is dangerous because neither the 102 /// <paramref name="length"/> is checked, nor <paramref name="obj"/> being null, nor the fact that 103 /// "rawPointer" actually lies within <paramref name="obj"/>. 104 /// </summary> 105 /// <param name="obj">The managed object that contains the data to span over.</param> 106 /// <param name="objectData">A reference to data within that object.</param> 107 /// <param name="length">The number of <typeparamref name="T"/> elements the memory contains.</param> 108 [MethodImpl(MethodImplOptions.AggressiveInlining)] 109 [EditorBrowsable(EditorBrowsableState.Never)] DangerousCreateSystem.ReadOnlySpan110 public static ReadOnlySpan<T> DangerousCreate(object obj, ref T objectData, int length) => new ReadOnlySpan<T>(ref objectData, length); 111 112 // Constructor for internal use only. 113 [MethodImpl(MethodImplOptions.AggressiveInlining)] ReadOnlySpanSystem.ReadOnlySpan114 internal ReadOnlySpan(ref T ptr, int length) 115 { 116 Debug.Assert(length >= 0); 117 118 _pointer = new ByReference<T>(ref ptr); 119 _length = length; 120 } 121 122 //Debugger Display = {T[length]} 123 private string DebuggerDisplay => string.Format("{{{0}[{1}]}}", typeof(T).Name, _length); 124 125 /// <summary> 126 /// 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 127 /// would have been stored. Such a reference can be used for pinning but must never be dereferenced. 128 /// </summary> 129 [MethodImpl(MethodImplOptions.AggressiveInlining)] 130 [EditorBrowsable(EditorBrowsableState.Never)] DangerousGetPinnableReferenceSystem.ReadOnlySpan131 internal ref readonly T DangerousGetPinnableReference() 132 { 133 return ref _pointer.Value; 134 } 135 136 /// <summary> 137 /// The number of items in the read-only span. 138 /// </summary> 139 public int Length 140 { 141 [NonVersionable] 142 get 143 { 144 return _length; 145 } 146 } 147 148 /// <summary> 149 /// Returns true if Length is 0. 150 /// </summary> 151 public bool IsEmpty 152 { 153 [NonVersionable] 154 get 155 { 156 return _length == 0; 157 } 158 } 159 160 /// <summary> 161 /// Returns the specified element of the read-only span. 162 /// </summary> 163 /// <param name="index"></param> 164 /// <returns></returns> 165 /// <exception cref="System.IndexOutOfRangeException"> 166 /// Thrown when index less than 0 or index greater than or equal to Length 167 /// </exception> 168 public ref readonly T this[int index] 169 { 170 #if PROJECTN 171 [BoundsChecking] 172 get 173 { 174 return ref Unsafe.Add(ref _pointer.Value, index); 175 } 176 #else 177 [Intrinsic] 178 [MethodImpl(MethodImplOptions.AggressiveInlining)] 179 [NonVersionable] 180 get 181 { 182 if ((uint)index >= (uint)_length) 183 ThrowHelper.ThrowIndexOutOfRangeException(); 184 return ref Unsafe.Add(ref _pointer.Value, index); 185 } 186 #endif 187 } 188 189 /// <summary> 190 /// Copies the contents of this read-only span into destination span. If the source 191 /// and destinations overlap, this method behaves as if the original values in 192 /// a temporary location before the destination is overwritten. 193 /// 194 /// <param name="destination">The span to copy items into.</param> 195 /// <exception cref="System.ArgumentException"> 196 /// Thrown when the destination Span is shorter than the source Span. 197 /// </exception> 198 /// </summary> CopyToSystem.ReadOnlySpan199 public void CopyTo(Span<T> destination) 200 { 201 if (!TryCopyTo(destination)) 202 ThrowHelper.ThrowArgumentException_DestinationTooShort(); 203 } 204 205 /// Copies the contents of this read-only span into destination span. If the source 206 /// and destinations overlap, this method behaves as if the original values in 207 /// a temporary location before the destination is overwritten. 208 /// </summary> 209 /// <returns>If the destination span is shorter than the source span, this method 210 /// return false and no data is written to the destination.</returns> 211 /// <param name="destination">The span to copy items into.</param> TryCopyToSystem.ReadOnlySpan212 public bool TryCopyTo(Span<T> destination) 213 { 214 if ((uint)_length > (uint)destination.Length) 215 return false; 216 217 Span.CopyTo<T>(ref destination.DangerousGetPinnableReference(), ref _pointer.Value, _length); 218 return true; 219 } 220 221 /// <summary> 222 /// Returns true if left and right point at the same memory and have the same length. Note that 223 /// this does *not* check to see if the *contents* are equal. 224 /// </summary> operator ==System.ReadOnlySpan225 public static bool operator ==(ReadOnlySpan<T> left, ReadOnlySpan<T> right) 226 { 227 return left._length == right._length && Unsafe.AreSame<T>(ref left._pointer.Value, ref right._pointer.Value); 228 } 229 230 /// <summary> 231 /// Returns false if left and right point at the same memory and have the same length. Note that 232 /// this does *not* check to see if the *contents* are equal. 233 /// </summary> operator !=System.ReadOnlySpan234 public static bool operator !=(ReadOnlySpan<T> left, ReadOnlySpan<T> right) => !(left == right); 235 236 /// <summary> 237 /// This method is not supported as spans cannot be boxed. To compare two spans, use operator==. 238 /// <exception cref="System.NotSupportedException"> 239 /// Always thrown by this method. 240 /// </exception> 241 /// </summary> 242 [Obsolete("Equals() on Span will always throw an exception. Use == instead.")] 243 [EditorBrowsable(EditorBrowsableState.Never)] EqualsSystem.ReadOnlySpan244 public override bool Equals(object obj) 245 { 246 throw new NotSupportedException(SR.NotSupported_CannotCallEqualsOnSpan); 247 } 248 249 /// <summary> 250 /// This method is not supported as spans cannot be boxed. 251 /// <exception cref="System.NotSupportedException"> 252 /// Always thrown by this method. 253 /// </exception> 254 /// </summary> 255 [Obsolete("GetHashCode() on Span will always throw an exception.")] 256 [EditorBrowsable(EditorBrowsableState.Never)] GetHashCodeSystem.ReadOnlySpan257 public override int GetHashCode() 258 { 259 throw new NotSupportedException(SR.NotSupported_CannotCallGetHashCodeOnSpan); 260 } 261 262 /// <summary> 263 /// Defines an implicit conversion of an array to a <see cref="ReadOnlySpan{T}"/> 264 /// </summary> 265 public static implicit operator ReadOnlySpan<T>(T[] array) => array != null ? new ReadOnlySpan<T>(array) : default; 266 267 /// <summary> 268 /// Defines an implicit conversion of a <see cref="ArraySegment{T}"/> to a <see cref="ReadOnlySpan{T}"/> 269 /// </summary> 270 public static implicit operator ReadOnlySpan<T>(ArraySegment<T> arraySegment) 271 => arraySegment.Array != null ? new ReadOnlySpan<T>(arraySegment.Array, arraySegment.Offset, arraySegment.Count) : default; 272 273 /// <summary> 274 /// Forms a slice out of the given read-only span, beginning at 'start'. 275 /// </summary> 276 /// <param name="start">The index at which to begin this slice.</param> 277 /// <exception cref="System.ArgumentOutOfRangeException"> 278 /// Thrown when the specified <paramref name="start"/> index is not in range (<0 or >=Length). 279 /// </exception> 280 [MethodImpl(MethodImplOptions.AggressiveInlining)] SliceSystem.ReadOnlySpan281 public ReadOnlySpan<T> Slice(int start) 282 { 283 if ((uint)start > (uint)_length) 284 ThrowHelper.ThrowArgumentOutOfRangeException(); 285 286 return new ReadOnlySpan<T>(ref Unsafe.Add(ref _pointer.Value, start), _length - start); 287 } 288 289 /// <summary> 290 /// Forms a slice out of the given read-only span, beginning at 'start', of given length 291 /// </summary> 292 /// <param name="start">The index at which to begin this slice.</param> 293 /// <param name="length">The desired length for the slice (exclusive).</param> 294 /// <exception cref="System.ArgumentOutOfRangeException"> 295 /// Thrown when the specified <paramref name="start"/> or end index is not in range (<0 or >=Length). 296 /// </exception> 297 [MethodImpl(MethodImplOptions.AggressiveInlining)] SliceSystem.ReadOnlySpan298 public ReadOnlySpan<T> Slice(int start, int length) 299 { 300 if ((uint)start > (uint)_length || (uint)length > (uint)(_length - start)) 301 ThrowHelper.ThrowArgumentOutOfRangeException(); 302 303 return new ReadOnlySpan<T>(ref Unsafe.Add(ref _pointer.Value, start), length); 304 } 305 306 /// <summary> 307 /// Copies the contents of this read-only span into a new array. This heap 308 /// allocates, so should generally be avoided, however it is sometimes 309 /// necessary to bridge the gap with APIs written in terms of arrays. 310 /// </summary> ToArraySystem.ReadOnlySpan311 public T[] ToArray() 312 { 313 if (_length == 0) 314 return Array.Empty<T>(); 315 316 var destination = new T[_length]; 317 Span.CopyTo<T>(ref Unsafe.As<byte, T>(ref destination.GetRawSzArrayData()), ref _pointer.Value, _length); 318 return destination; 319 } 320 321 /// <summary> 322 /// Returns a 0-length read-only span whose base is the null pointer. 323 /// </summary> 324 public static ReadOnlySpan<T> Empty => default(ReadOnlySpan<T>); 325 326 /// <summary>Gets an enumerator for this span.</summary> GetEnumeratorSystem.ReadOnlySpan327 public Enumerator GetEnumerator() => new Enumerator(this); 328 329 /// <summary>Enumerates the elements of a <see cref="ReadOnlySpan{T}"/>.</summary> 330 public ref struct Enumerator 331 { 332 /// <summary>The span being enumerated.</summary> 333 private readonly ReadOnlySpan<T> _span; 334 /// <summary>The next index to yield.</summary> 335 private int _index; 336 337 /// <summary>Initialize the enumerator.</summary> 338 /// <param name="span">The span to enumerate.</param> 339 [MethodImpl(MethodImplOptions.AggressiveInlining)] EnumeratorSystem.ReadOnlySpan.Enumerator340 internal Enumerator(ReadOnlySpan<T> span) 341 { 342 _span = span; 343 _index = -1; 344 } 345 346 /// <summary>Advances the enumerator to the next element of the span.</summary> 347 [MethodImpl(MethodImplOptions.AggressiveInlining)] MoveNextSystem.ReadOnlySpan.Enumerator348 public bool MoveNext() 349 { 350 int index = _index + 1; 351 if (index < _span.Length) 352 { 353 _index = index; 354 return true; 355 } 356 357 return false; 358 } 359 360 /// <summary>Gets the element at the current position of the enumerator.</summary> 361 public ref readonly T Current 362 { 363 [MethodImpl(MethodImplOptions.AggressiveInlining)] 364 get => ref _span[_index]; 365 } 366 } 367 } 368 } 369