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.
5 using System.ComponentModel;
6 using System.Diagnostics;
7 using System.Runtime.CompilerServices;
8 using System.Runtime.Versioning;
9 using Internal.Runtime.CompilerServices;
11 #pragma warning disable 0809 //warning CS0809: Obsolete member 'Span<T>.Equals(object)' overrides non-obsolete member 'object.Equals(object)'
14 using nuint = System.UInt64;
16 using nuint = System.UInt32;
22 /// ReadOnlySpan represents a contiguous region of arbitrary memory. Unlike arrays, it can point to either managed
23 /// or native memory, or to memory allocated on the stack. It is type- and memory-safe.
25 [DebuggerTypeProxy(typeof(SpanDebugView<>))]
26 [DebuggerDisplay("{DebuggerDisplay,nq}")]
28 public readonly ref struct ReadOnlySpan<T>
30 /// <summary>A byref or a native ptr.</summary>
31 internal readonly ByReference<T> _pointer;
32 /// <summary>The number of elements this ReadOnlySpan contains.</summary>
36 private readonly int _length;
39 /// Creates a new read-only span over the entirety of the target array.
41 /// <param name="array">The target array.</param>
42 /// <exception cref="System.ArgumentNullException">Thrown when <paramref name="array"/> is a null
43 /// reference (Nothing in Visual Basic).</exception>
44 [MethodImpl(MethodImplOptions.AggressiveInlining)]
45 public ReadOnlySpan(T[] array)
48 ThrowHelper.ThrowArgumentNullException(ExceptionArgument.array);
50 _pointer = new ByReference<T>(ref Unsafe.As<byte, T>(ref array.GetRawSzArrayData()));
51 _length = array.Length;
55 /// Creates a new read-only span over the portion of the target array beginning
56 /// at 'start' index and ending at 'end' index (exclusive).
58 /// <param name="array">The target array.</param>
59 /// <param name="start">The index at which to begin the read-only span.</param>
60 /// <param name="length">The number of items in the read-only span.</param>
61 /// <exception cref="System.ArgumentNullException">Thrown when <paramref name="array"/> is a null
62 /// reference (Nothing in Visual Basic).</exception>
63 /// <exception cref="System.ArgumentOutOfRangeException">
64 /// Thrown when the specified <paramref name="start"/> or end index is not in the range (<0 or >=Length).
66 [MethodImpl(MethodImplOptions.AggressiveInlining)]
67 public ReadOnlySpan(T[] array, int start, int length)
70 ThrowHelper.ThrowArgumentNullException(ExceptionArgument.array);
71 if ((uint)start > (uint)array.Length || (uint)length > (uint)(array.Length - start))
72 ThrowHelper.ThrowArgumentOutOfRangeException();
74 _pointer = new ByReference<T>(ref Unsafe.Add(ref Unsafe.As<byte, T>(ref array.GetRawSzArrayData()), start));
79 /// Creates a new read-only span over the target unmanaged buffer. Clearly this
80 /// is quite dangerous, because we are creating arbitrarily typed T's
81 /// out of a void*-typed block of memory. And the length is not checked.
82 /// But if this creation is correct, then all subsequent uses are correct.
84 /// <param name="pointer">An unmanaged pointer to memory.</param>
85 /// <param name="length">The number of <typeparamref name="T"/> elements the memory contains.</param>
86 /// <exception cref="System.ArgumentException">
87 /// Thrown when <typeparamref name="T"/> is reference type or contains pointers and hence cannot be stored in unmanaged memory.
89 /// <exception cref="System.ArgumentOutOfRangeException">
90 /// Thrown when the specified <paramref name="length"/> is negative.
93 [MethodImpl(MethodImplOptions.AggressiveInlining)]
94 public unsafe ReadOnlySpan(void* pointer, int length)
96 if (RuntimeHelpers.IsReferenceOrContainsReferences<T>())
97 ThrowHelper.ThrowInvalidTypeWithPointersNotSupported(typeof(T));
99 ThrowHelper.ThrowArgumentOutOfRangeException();
101 _pointer = new ByReference<T>(ref Unsafe.As<byte, T>(ref *(byte*)pointer));
106 /// Create a new read-only span over a portion of a regular managed object. This can be useful
107 /// if part of a managed object represents a "fixed array." This is dangerous because neither the
108 /// <paramref name="length"/> is checked, nor <paramref name="obj"/> being null, nor the fact that
109 /// "rawPointer" actually lies within <paramref name="obj"/>.
111 /// <param name="obj">The managed object that contains the data to span over.</param>
112 /// <param name="objectData">A reference to data within that object.</param>
113 /// <param name="length">The number of <typeparamref name="T"/> elements the memory contains.</param>
114 [MethodImpl(MethodImplOptions.AggressiveInlining)]
115 [EditorBrowsable(EditorBrowsableState.Never)]
116 public static ReadOnlySpan<T> DangerousCreate(object obj, ref T objectData, int length) => new ReadOnlySpan<T>(ref objectData, length);
118 // Constructor for internal use only.
119 [MethodImpl(MethodImplOptions.AggressiveInlining)]
120 internal ReadOnlySpan(ref T ptr, int length)
122 Debug.Assert(length >= 0);
124 _pointer = new ByReference<T>(ref ptr);
128 //Debugger Display = {T[length]}
129 private string DebuggerDisplay => string.Format("{{{0}[{1}]}}", typeof(T).Name, _length);
132 /// 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
133 /// would have been stored. Such a reference can be used for pinning but must never be dereferenced.
135 [MethodImpl(MethodImplOptions.AggressiveInlining)]
136 [EditorBrowsable(EditorBrowsableState.Never)]
137 internal ref readonly T DangerousGetPinnableReference()
139 return ref _pointer.Value;
143 /// The number of items in the read-only span.
155 /// Returns true if Length is 0.
167 /// Returns the specified element of the read-only span.
169 /// <param name="index"></param>
170 /// <returns></returns>
171 /// <exception cref="System.IndexOutOfRangeException">
172 /// Thrown when index less than 0 or index greater than or equal to Length
174 public ref readonly T this[int index]
180 return ref Unsafe.Add(ref _pointer.Value, index);
184 [MethodImpl(MethodImplOptions.AggressiveInlining)]
188 if ((uint)index >= (uint)_length)
189 ThrowHelper.ThrowIndexOutOfRangeException();
190 return ref Unsafe.Add(ref _pointer.Value, index);
196 /// Copies the contents of this read-only span into destination span. If the source
197 /// and destinations overlap, this method behaves as if the original values in
198 /// a temporary location before the destination is overwritten.
200 /// <param name="destination">The span to copy items into.</param>
201 /// <exception cref="System.ArgumentException">
202 /// Thrown when the destination Span is shorter than the source Span.
205 public void CopyTo(Span<T> destination)
207 // Using "if (!TryCopyTo(...))" results in two branches: one for the length
208 // check, and one for the result of TryCopyTo. Since these checks are equivalent,
209 // we can optimize by performing the check once ourselves then calling Memmove directly.
211 if ((uint)_length <= (uint)destination.Length)
213 Buffer.Memmove(ref destination.DangerousGetPinnableReference(), ref _pointer.Value, (nuint)_length);
217 ThrowHelper.ThrowArgumentException_DestinationTooShort();
221 /// Copies the contents of this read-only span into destination span. If the source
222 /// and destinations overlap, this method behaves as if the original values in
223 /// a temporary location before the destination is overwritten.
225 /// <returns>If the destination span is shorter than the source span, this method
226 /// return false and no data is written to the destination.</returns>
227 /// <param name="destination">The span to copy items into.</param>
228 public bool TryCopyTo(Span<T> destination)
231 if ((uint)_length <= (uint)destination.Length)
233 Buffer.Memmove(ref destination.DangerousGetPinnableReference(), ref _pointer.Value, (nuint)_length);
240 /// Returns true if left and right point at the same memory and have the same length. Note that
241 /// this does *not* check to see if the *contents* are equal.
243 public static bool operator ==(ReadOnlySpan<T> left, ReadOnlySpan<T> right)
245 return left._length == right._length && Unsafe.AreSame<T>(ref left._pointer.Value, ref right._pointer.Value);
249 /// Returns false if left and right point at the same memory and have the same length. Note that
250 /// this does *not* check to see if the *contents* are equal.
252 public static bool operator !=(ReadOnlySpan<T> left, ReadOnlySpan<T> right) => !(left == right);
255 /// This method is not supported as spans cannot be boxed. To compare two spans, use operator==.
256 /// <exception cref="System.NotSupportedException">
257 /// Always thrown by this method.
260 [Obsolete("Equals() on ReadOnlySpan will always throw an exception. Use == instead.")]
261 [EditorBrowsable(EditorBrowsableState.Never)]
262 public override bool Equals(object obj)
264 throw new NotSupportedException(SR.NotSupported_CannotCallEqualsOnSpan);
268 /// This method is not supported as spans cannot be boxed.
269 /// <exception cref="System.NotSupportedException">
270 /// Always thrown by this method.
273 [Obsolete("GetHashCode() on ReadOnlySpan will always throw an exception.")]
274 [EditorBrowsable(EditorBrowsableState.Never)]
275 public override int GetHashCode()
277 throw new NotSupportedException(SR.NotSupported_CannotCallGetHashCodeOnSpan);
281 /// Returns a <see cref="String"/> with the name of the type and the number of elements
283 public override string ToString() => "System.Span[" + Length.ToString() + "]";
286 /// Defines an implicit conversion of an array to a <see cref="ReadOnlySpan{T}"/>
288 public static implicit operator ReadOnlySpan<T>(T[] array) => array != null ? new ReadOnlySpan<T>(array) : default;
291 /// Defines an implicit conversion of a <see cref="ArraySegment{T}"/> to a <see cref="ReadOnlySpan{T}"/>
293 public static implicit operator ReadOnlySpan<T>(ArraySegment<T> arraySegment)
294 => arraySegment.Array != null ? new ReadOnlySpan<T>(arraySegment.Array, arraySegment.Offset, arraySegment.Count) : default;
297 /// Forms a slice out of the given read-only span, beginning at 'start'.
299 /// <param name="start">The index at which to begin this slice.</param>
300 /// <exception cref="System.ArgumentOutOfRangeException">
301 /// Thrown when the specified <paramref name="start"/> index is not in range (<0 or >=Length).
303 [MethodImpl(MethodImplOptions.AggressiveInlining)]
304 public ReadOnlySpan<T> Slice(int start)
306 if ((uint)start > (uint)_length)
307 ThrowHelper.ThrowArgumentOutOfRangeException();
309 return new ReadOnlySpan<T>(ref Unsafe.Add(ref _pointer.Value, start), _length - start);
313 /// Forms a slice out of the given read-only span, beginning at 'start', of given length
315 /// <param name="start">The index at which to begin this slice.</param>
316 /// <param name="length">The desired length for the slice (exclusive).</param>
317 /// <exception cref="System.ArgumentOutOfRangeException">
318 /// Thrown when the specified <paramref name="start"/> or end index is not in range (<0 or >=Length).
320 [MethodImpl(MethodImplOptions.AggressiveInlining)]
321 public ReadOnlySpan<T> Slice(int start, int length)
323 if ((uint)start > (uint)_length || (uint)length > (uint)(_length - start))
324 ThrowHelper.ThrowArgumentOutOfRangeException();
326 return new ReadOnlySpan<T>(ref Unsafe.Add(ref _pointer.Value, start), length);
330 /// Copies the contents of this read-only span into a new array. This heap
331 /// allocates, so should generally be avoided, however it is sometimes
332 /// necessary to bridge the gap with APIs written in terms of arrays.
337 return Array.Empty<T>();
339 var destination = new T[_length];
340 Buffer.Memmove(ref Unsafe.As<byte, T>(ref destination.GetRawSzArrayData()), ref _pointer.Value, (nuint)_length);
345 /// Returns a 0-length read-only span whose base is the null pointer.
347 public static ReadOnlySpan<T> Empty => default(ReadOnlySpan<T>);
349 /// <summary>Gets an enumerator for this span.</summary>
350 public Enumerator GetEnumerator() => new Enumerator(this);
352 /// <summary>Enumerates the elements of a <see cref="ReadOnlySpan{T}"/>.</summary>
353 public ref struct Enumerator
355 /// <summary>The span being enumerated.</summary>
356 private readonly ReadOnlySpan<T> _span;
357 /// <summary>The next index to yield.</summary>
360 /// <summary>Initialize the enumerator.</summary>
361 /// <param name="span">The span to enumerate.</param>
362 [MethodImpl(MethodImplOptions.AggressiveInlining)]
363 internal Enumerator(ReadOnlySpan<T> span)
369 /// <summary>Advances the enumerator to the next element of the span.</summary>
370 [MethodImpl(MethodImplOptions.AggressiveInlining)]
371 public bool MoveNext()
373 int index = _index + 1;
374 if (index < _span.Length)
383 /// <summary>Gets the element at the current position of the enumerator.</summary>
384 public ref readonly T Current
386 [MethodImpl(MethodImplOptions.AggressiveInlining)]
387 get => ref _span[_index];