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 /// Span 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 Span<T>
30 /// <summary>A byref or a native ptr.</summary>
31 internal readonly ByReference<T> _pointer;
32 /// <summary>The number of elements this Span contains.</summary>
36 private readonly int _length;
39 /// Creates a new 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 /// <exception cref="System.ArrayTypeMismatchException">Thrown when <paramref name="array"/> is covariant and array's type is not exactly T[].</exception>
45 [MethodImpl(MethodImplOptions.AggressiveInlining)]
46 public Span(T[] array)
49 ThrowHelper.ThrowArgumentNullException(ExceptionArgument.array);
50 if (default(T) == null && array.GetType() != typeof(T[]))
51 ThrowHelper.ThrowArrayTypeMismatchException();
53 _pointer = new ByReference<T>(ref Unsafe.As<byte, T>(ref array.GetRawSzArrayData()));
54 _length = array.Length;
58 /// Creates a new span over the portion of the target array beginning
59 /// at 'start' index and ending at 'end' index (exclusive).
61 /// <param name="array">The target array.</param>
62 /// <param name="start">The index at which to begin the span.</param>
63 /// <param name="length">The number of items in the span.</param>
64 /// <exception cref="System.ArgumentNullException">Thrown when <paramref name="array"/> is a null
65 /// reference (Nothing in Visual Basic).</exception>
66 /// <exception cref="System.ArrayTypeMismatchException">Thrown when <paramref name="array"/> is covariant and array's type is not exactly T[].</exception>
67 /// <exception cref="System.ArgumentOutOfRangeException">
68 /// Thrown when the specified <paramref name="start"/> or end index is not in the range (<0 or >=Length).
70 [MethodImpl(MethodImplOptions.AggressiveInlining)]
71 public Span(T[] array, int start, int length)
74 ThrowHelper.ThrowArgumentNullException(ExceptionArgument.array);
75 if (default(T) == null && array.GetType() != typeof(T[]))
76 ThrowHelper.ThrowArrayTypeMismatchException();
77 if ((uint)start > (uint)array.Length || (uint)length > (uint)(array.Length - start))
78 ThrowHelper.ThrowArgumentOutOfRangeException();
80 _pointer = new ByReference<T>(ref Unsafe.Add(ref Unsafe.As<byte, T>(ref array.GetRawSzArrayData()), start));
85 /// Creates a new span over the target unmanaged buffer. Clearly this
86 /// is quite dangerous, because we are creating arbitrarily typed T's
87 /// out of a void*-typed block of memory. And the length is not checked.
88 /// But if this creation is correct, then all subsequent uses are correct.
90 /// <param name="pointer">An unmanaged pointer to memory.</param>
91 /// <param name="length">The number of <typeparamref name="T"/> elements the memory contains.</param>
92 /// <exception cref="System.ArgumentException">
93 /// Thrown when <typeparamref name="T"/> is reference type or contains pointers and hence cannot be stored in unmanaged memory.
95 /// <exception cref="System.ArgumentOutOfRangeException">
96 /// Thrown when the specified <paramref name="length"/> is negative.
99 [MethodImpl(MethodImplOptions.AggressiveInlining)]
100 public unsafe Span(void* pointer, int length)
102 if (RuntimeHelpers.IsReferenceOrContainsReferences<T>())
103 ThrowHelper.ThrowInvalidTypeWithPointersNotSupported(typeof(T));
105 ThrowHelper.ThrowArgumentOutOfRangeException();
107 _pointer = new ByReference<T>(ref Unsafe.As<byte, T>(ref *(byte*)pointer));
112 /// Create a new span over a portion of a regular managed object. This can be useful
113 /// if part of a managed object represents a "fixed array." This is dangerous because neither the
114 /// <paramref name="length"/> is checked, nor <paramref name="obj"/> being null, nor the fact that
115 /// "rawPointer" actually lies within <paramref name="obj"/>.
117 /// <param name="obj">The managed object that contains the data to span over.</param>
118 /// <param name="objectData">A reference to data within that object.</param>
119 /// <param name="length">The number of <typeparamref name="T"/> elements the memory contains.</param>
120 [MethodImpl(MethodImplOptions.AggressiveInlining)]
121 [EditorBrowsable(EditorBrowsableState.Never)]
122 public static Span<T> DangerousCreate(object obj, ref T objectData, int length) => new Span<T>(ref objectData, length);
124 // Constructor for internal use only.
125 [MethodImpl(MethodImplOptions.AggressiveInlining)]
126 internal Span(ref T ptr, int length)
128 Debug.Assert(length >= 0);
130 _pointer = new ByReference<T>(ref ptr);
134 //Debugger Display = {T[length]}
135 private string DebuggerDisplay => string.Format("{{{0}[{1}]}}", typeof(T).Name, _length);
138 /// 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
139 /// would have been stored. Such a reference can be used for pinning but must never be dereferenced.
141 [MethodImpl(MethodImplOptions.AggressiveInlining)]
142 [EditorBrowsable(EditorBrowsableState.Never)]
143 internal ref T DangerousGetPinnableReference()
145 return ref _pointer.Value;
149 /// The number of items in the span.
161 /// Returns true if Length is 0.
173 /// Returns a reference to specified element of the Span.
175 /// <param name="index"></param>
176 /// <returns></returns>
177 /// <exception cref="System.IndexOutOfRangeException">
178 /// Thrown when index less than 0 or index greater than or equal to Length
180 public ref T this[int index]
186 return ref Unsafe.Add(ref _pointer.Value, index);
190 [MethodImpl(MethodImplOptions.AggressiveInlining)]
194 if ((uint)index >= (uint)_length)
195 ThrowHelper.ThrowIndexOutOfRangeException();
196 return ref Unsafe.Add(ref _pointer.Value, index);
202 /// Clears the contents of this span.
204 [MethodImpl(MethodImplOptions.AggressiveInlining)]
207 if (RuntimeHelpers.IsReferenceOrContainsReferences<T>())
209 Span.ClearWithReferences(ref Unsafe.As<T, IntPtr>(ref _pointer.Value), (nuint)_length * (nuint)(Unsafe.SizeOf<T>() / sizeof(nuint)));
213 Span.ClearWithoutReferences(ref Unsafe.As<T, byte>(ref _pointer.Value), (nuint)_length * (nuint)Unsafe.SizeOf<T>());
218 /// Fills the contents of this span with the given value.
220 public void Fill(T value)
222 if (Unsafe.SizeOf<T>() == 1)
224 uint length = (uint)_length;
228 T tmp = value; // Avoid taking address of the "value" argument. It would regress performance of the loop below.
229 Unsafe.InitBlockUnaligned(ref Unsafe.As<T, byte>(ref _pointer.Value), Unsafe.As<T, byte>(ref tmp), length);
233 // Do all math as nuint to avoid unnecessary 64->32->64 bit integer truncations
234 nuint length = (uint)_length;
238 ref T r = ref DangerousGetPinnableReference();
240 // TODO: Create block fill for value types of power of two sizes e.g. 2,4,8,16
242 nuint elementSize = (uint)Unsafe.SizeOf<T>();
244 for (; i < (length & ~(nuint)7); i += 8)
246 Unsafe.AddByteOffset<T>(ref r, (i + 0) * elementSize) = value;
247 Unsafe.AddByteOffset<T>(ref r, (i + 1) * elementSize) = value;
248 Unsafe.AddByteOffset<T>(ref r, (i + 2) * elementSize) = value;
249 Unsafe.AddByteOffset<T>(ref r, (i + 3) * elementSize) = value;
250 Unsafe.AddByteOffset<T>(ref r, (i + 4) * elementSize) = value;
251 Unsafe.AddByteOffset<T>(ref r, (i + 5) * elementSize) = value;
252 Unsafe.AddByteOffset<T>(ref r, (i + 6) * elementSize) = value;
253 Unsafe.AddByteOffset<T>(ref r, (i + 7) * elementSize) = value;
255 if (i < (length & ~(nuint)3))
257 Unsafe.AddByteOffset<T>(ref r, (i + 0) * elementSize) = value;
258 Unsafe.AddByteOffset<T>(ref r, (i + 1) * elementSize) = value;
259 Unsafe.AddByteOffset<T>(ref r, (i + 2) * elementSize) = value;
260 Unsafe.AddByteOffset<T>(ref r, (i + 3) * elementSize) = value;
263 for (; i < length; i++)
265 Unsafe.AddByteOffset<T>(ref r, i * elementSize) = value;
271 /// Copies the contents of this span into destination span. If the source
272 /// and destinations overlap, this method behaves as if the original values in
273 /// a temporary location before the destination is overwritten.
275 /// <param name="destination">The span to copy items into.</param>
276 /// <exception cref="System.ArgumentException">
277 /// Thrown when the destination Span is shorter than the source Span.
279 public void CopyTo(Span<T> destination)
281 // Using "if (!TryCopyTo(...))" results in two branches: one for the length
282 // check, and one for the result of TryCopyTo. Since these checks are equivalent,
283 // we can optimize by performing the check once ourselves then calling Memmove directly.
285 if ((uint)_length <= (uint)destination.Length)
287 Buffer.Memmove(ref destination.DangerousGetPinnableReference(), ref _pointer.Value, (nuint)_length);
291 ThrowHelper.ThrowArgumentException_DestinationTooShort();
296 /// Copies the contents of this span into destination span. If the source
297 /// and destinations overlap, this method behaves as if the original values in
298 /// a temporary location before the destination is overwritten.
300 /// <param name="destination">The span to copy items into.</param>
301 /// <returns>If the destination span is shorter than the source span, this method
302 /// return false and no data is written to the destination.</returns>
303 public bool TryCopyTo(Span<T> destination)
306 if ((uint)_length <= (uint)destination.Length)
308 Buffer.Memmove(ref destination.DangerousGetPinnableReference(), ref _pointer.Value, (nuint)_length);
315 /// Returns true if left and right point at the same memory and have the same length. Note that
316 /// this does *not* check to see if the *contents* are equal.
318 public static bool operator ==(Span<T> left, Span<T> right)
320 return left._length == right._length && Unsafe.AreSame<T>(ref left._pointer.Value, ref right._pointer.Value);
324 /// Returns false if left and right point at the same memory and have the same length. Note that
325 /// this does *not* check to see if the *contents* are equal.
327 public static bool operator !=(Span<T> left, Span<T> right) => !(left == right);
330 /// This method is not supported as spans cannot be boxed. To compare two spans, use operator==.
331 /// <exception cref="System.NotSupportedException">
332 /// Always thrown by this method.
335 [Obsolete("Equals() on Span will always throw an exception. Use == instead.")]
336 [EditorBrowsable(EditorBrowsableState.Never)]
337 public override bool Equals(object obj)
339 throw new NotSupportedException(SR.NotSupported_CannotCallEqualsOnSpan);
343 /// This method is not supported as spans cannot be boxed.
344 /// <exception cref="System.NotSupportedException">
345 /// Always thrown by this method.
348 [Obsolete("GetHashCode() on Span will always throw an exception.")]
349 [EditorBrowsable(EditorBrowsableState.Never)]
350 public override int GetHashCode()
352 throw new NotSupportedException(SR.NotSupported_CannotCallGetHashCodeOnSpan);
356 /// Returns a <see cref="String"/> with the name of the type and the number of elements
358 public override string ToString() => "System.Span[" + Length.ToString() + "]";
361 /// Defines an implicit conversion of an array to a <see cref="Span{T}"/>
363 public static implicit operator Span<T>(T[] array) => array != null ? new Span<T>(array) : default;
366 /// Defines an implicit conversion of a <see cref="ArraySegment{T}"/> to a <see cref="Span{T}"/>
368 public static implicit operator Span<T>(ArraySegment<T> arraySegment)
369 => arraySegment.Array != null ? new Span<T>(arraySegment.Array, arraySegment.Offset, arraySegment.Count) : default;
372 /// Defines an implicit conversion of a <see cref="Span{T}"/> to a <see cref="ReadOnlySpan{T}"/>
374 public static implicit operator ReadOnlySpan<T>(Span<T> span) => new ReadOnlySpan<T>(ref span._pointer.Value, span._length);
377 /// Forms a slice out of the given span, beginning at 'start'.
379 /// <param name="start">The index at which to begin this slice.</param>
380 /// <exception cref="System.ArgumentOutOfRangeException">
381 /// Thrown when the specified <paramref name="start"/> index is not in range (<0 or >=Length).
383 [MethodImpl(MethodImplOptions.AggressiveInlining)]
384 public Span<T> Slice(int start)
386 if ((uint)start > (uint)_length)
387 ThrowHelper.ThrowArgumentOutOfRangeException();
389 return new Span<T>(ref Unsafe.Add(ref _pointer.Value, start), _length - start);
393 /// Forms a slice out of the given span, beginning at 'start', of given length
395 /// <param name="start">The index at which to begin this slice.</param>
396 /// <param name="length">The desired length for the slice (exclusive).</param>
397 /// <exception cref="System.ArgumentOutOfRangeException">
398 /// Thrown when the specified <paramref name="start"/> or end index is not in range (<0 or >=Length).
400 [MethodImpl(MethodImplOptions.AggressiveInlining)]
401 public Span<T> Slice(int start, int length)
403 if ((uint)start > (uint)_length || (uint)length > (uint)(_length - start))
404 ThrowHelper.ThrowArgumentOutOfRangeException();
406 return new Span<T>(ref Unsafe.Add(ref _pointer.Value, start), length);
410 /// Copies the contents of this span into a new array. This heap
411 /// allocates, so should generally be avoided, however it is sometimes
412 /// necessary to bridge the gap with APIs written in terms of arrays.
414 [MethodImpl(MethodImplOptions.AggressiveInlining)]
418 return Array.Empty<T>();
420 var destination = new T[_length];
421 Buffer.Memmove(ref Unsafe.As<byte, T>(ref destination.GetRawSzArrayData()), ref _pointer.Value, (nuint)_length);
426 /// Returns an empty <see cref="Span{T}"/>
428 public static Span<T> Empty => default(Span<T>);
430 /// <summary>Gets an enumerator for this span.</summary>
431 public Enumerator GetEnumerator() => new Enumerator(this);
433 /// <summary>Enumerates the elements of a <see cref="Span{T}"/>.</summary>
434 public ref struct Enumerator
436 /// <summary>The span being enumerated.</summary>
437 private readonly Span<T> _span;
438 /// <summary>The next index to yield.</summary>
441 /// <summary>Initialize the enumerator.</summary>
442 /// <param name="span">The span to enumerate.</param>
443 [MethodImpl(MethodImplOptions.AggressiveInlining)]
444 internal Enumerator(Span<T> span)
450 /// <summary>Advances the enumerator to the next element of the span.</summary>
451 [MethodImpl(MethodImplOptions.AggressiveInlining)]
452 public bool MoveNext()
454 int index = _index + 1;
455 if (index < _span.Length)
464 /// <summary>Gets the element at the current position of the enumerator.</summary>
467 [MethodImpl(MethodImplOptions.AggressiveInlining)]
468 get => ref _span[_index];