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 (&lt;0 or &gt;=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 (&lt;0 or &gt;=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 (&lt;0 or &gt;=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