Improve Dictionary TryGetValue size/perfomance (dotnet/coreclr#27195)
[mono-project.git] / netcore / System.Private.CoreLib / shared / System / ReadOnlySpan.Fast.cs
blob238b90f90fd07f0716860798e423de07e201a186
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.Diagnostics;
6 using System.Runtime.CompilerServices;
7 using System.Runtime.Versioning;
8 using System.Text;
9 using EditorBrowsableAttribute = System.ComponentModel.EditorBrowsableAttribute;
10 using EditorBrowsableState = System.ComponentModel.EditorBrowsableState;
11 using Internal.Runtime.CompilerServices;
13 #pragma warning disable 0809 //warning CS0809: Obsolete member 'Span<T>.Equals(object)' overrides non-obsolete member 'object.Equals(object)'
15 #pragma warning disable SA1121 // explicitly using type aliases instead of built-in types
16 #if BIT64
17 using nuint = System.UInt64;
18 #else
19 using nuint = System.UInt32;
20 #endif
22 namespace System
24 /// <summary>
25 /// ReadOnlySpan represents a contiguous region of arbitrary memory. Unlike arrays, it can point to either managed
26 /// or native memory, or to memory allocated on the stack. It is type- and memory-safe.
27 /// </summary>
28 [NonVersionable]
29 public readonly ref partial struct ReadOnlySpan<T>
31 /// <summary>A byref or a native ptr.</summary>
32 internal readonly ByReference<T> _pointer;
33 /// <summary>The number of elements this ReadOnlySpan contains.</summary>
34 private readonly int _length;
36 /// <summary>
37 /// Creates a new read-only span over the entirety of the target array.
38 /// </summary>
39 /// <param name="array">The target array.</param>
40 /// <remarks>Returns default when <paramref name="array"/> is null.</remarks>
41 [MethodImpl(MethodImplOptions.AggressiveInlining)]
42 public ReadOnlySpan(T[]? array)
44 if (array == null)
46 this = default;
47 return; // returns default
50 _pointer = new ByReference<T>(ref Unsafe.As<byte, T>(ref array.GetRawSzArrayData()));
51 _length = array.Length;
54 /// <summary>
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).
57 /// </summary>
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 /// <remarks>Returns default when <paramref name="array"/> is null.</remarks>
62 /// <exception cref="System.ArgumentOutOfRangeException">
63 /// Thrown when the specified <paramref name="start"/> or end index is not in the range (&lt;0 or &gt;Length).
64 /// </exception>
65 [MethodImpl(MethodImplOptions.AggressiveInlining)]
66 public ReadOnlySpan(T[]? array, int start, int length)
68 if (array == null)
70 if (start != 0 || length != 0)
71 ThrowHelper.ThrowArgumentOutOfRangeException();
72 this = default;
73 return; // returns default
75 #if BIT64
76 // See comment in Span<T>.Slice for how this works.
77 if ((ulong)(uint)start + (ulong)(uint)length > (ulong)(uint)array.Length)
78 ThrowHelper.ThrowArgumentOutOfRangeException();
79 #else
80 if ((uint)start > (uint)array.Length || (uint)length > (uint)(array.Length - start))
81 ThrowHelper.ThrowArgumentOutOfRangeException();
82 #endif
84 _pointer = new ByReference<T>(ref Unsafe.Add(ref Unsafe.As<byte, T>(ref array.GetRawSzArrayData()), start));
85 _length = length;
88 /// <summary>
89 /// Creates a new read-only span over the target unmanaged buffer. Clearly this
90 /// is quite dangerous, because we are creating arbitrarily typed T's
91 /// out of a void*-typed block of memory. And the length is not checked.
92 /// But if this creation is correct, then all subsequent uses are correct.
93 /// </summary>
94 /// <param name="pointer">An unmanaged pointer to memory.</param>
95 /// <param name="length">The number of <typeparamref name="T"/> elements the memory contains.</param>
96 /// <exception cref="System.ArgumentException">
97 /// Thrown when <typeparamref name="T"/> is reference type or contains pointers and hence cannot be stored in unmanaged memory.
98 /// </exception>
99 /// <exception cref="System.ArgumentOutOfRangeException">
100 /// Thrown when the specified <paramref name="length"/> is negative.
101 /// </exception>
102 [CLSCompliant(false)]
103 [MethodImpl(MethodImplOptions.AggressiveInlining)]
104 public unsafe ReadOnlySpan(void* pointer, int length)
106 if (RuntimeHelpers.IsReferenceOrContainsReferences<T>())
107 ThrowHelper.ThrowInvalidTypeWithPointersNotSupported(typeof(T));
108 if (length < 0)
109 ThrowHelper.ThrowArgumentOutOfRangeException();
111 _pointer = new ByReference<T>(ref Unsafe.As<byte, T>(ref *(byte*)pointer));
112 _length = length;
115 // Constructor for internal use only.
116 [MethodImpl(MethodImplOptions.AggressiveInlining)]
117 internal ReadOnlySpan(ref T ptr, int length)
119 Debug.Assert(length >= 0);
121 _pointer = new ByReference<T>(ref ptr);
122 _length = length;
125 /// <summary>
126 /// Returns the specified element of the read-only span.
127 /// </summary>
128 /// <param name="index"></param>
129 /// <returns></returns>
130 /// <exception cref="System.IndexOutOfRangeException">
131 /// Thrown when index less than 0 or index greater than or equal to Length
132 /// </exception>
133 public ref readonly T this[int index]
135 [Intrinsic]
136 [MethodImpl(MethodImplOptions.AggressiveInlining)]
137 [NonVersionable]
140 if ((uint)index >= (uint)_length)
141 ThrowHelper.ThrowIndexOutOfRangeException();
142 return ref Unsafe.Add(ref _pointer.Value, index);
146 /// <summary>
147 /// Returns a reference to the 0th element of the Span. If the Span is empty, returns null reference.
148 /// It can be used for pinning and is required to support the use of span within a fixed statement.
149 /// </summary>
150 [EditorBrowsable(EditorBrowsableState.Never)]
151 public ref readonly T GetPinnableReference()
153 // Ensure that the native code has just one forward branch that is predicted-not-taken.
154 ref T ret = ref Unsafe.NullRef<T>();
155 if (_length != 0) ret = ref _pointer.Value;
156 return ref ret;
159 /// <summary>
160 /// Copies the contents of this read-only span into destination span. If the source
161 /// and destinations overlap, this method behaves as if the original values in
162 /// a temporary location before the destination is overwritten.
164 /// <param name="destination">The span to copy items into.</param>
165 /// <exception cref="System.ArgumentException">
166 /// Thrown when the destination Span is shorter than the source Span.
167 /// </exception>
168 /// </summary>
169 [MethodImpl(MethodImplOptions.AggressiveInlining)]
170 public void CopyTo(Span<T> destination)
172 // Using "if (!TryCopyTo(...))" results in two branches: one for the length
173 // check, and one for the result of TryCopyTo. Since these checks are equivalent,
174 // we can optimize by performing the check once ourselves then calling Memmove directly.
176 if ((uint)_length <= (uint)destination.Length)
178 Buffer.Memmove(ref destination._pointer.Value, ref _pointer.Value, (nuint)_length);
180 else
182 ThrowHelper.ThrowArgumentException_DestinationTooShort();
186 /// <summary>
187 /// Copies the contents of this read-only span into destination span. If the source
188 /// and destinations overlap, this method behaves as if the original values in
189 /// a temporary location before the destination is overwritten.
190 /// </summary>
191 /// <returns>If the destination span is shorter than the source span, this method
192 /// return false and no data is written to the destination.</returns>
193 /// <param name="destination">The span to copy items into.</param>
194 public bool TryCopyTo(Span<T> destination)
196 bool retVal = false;
197 if ((uint)_length <= (uint)destination.Length)
199 Buffer.Memmove(ref destination._pointer.Value, ref _pointer.Value, (nuint)_length);
200 retVal = true;
202 return retVal;
205 /// <summary>
206 /// Returns true if left and right point at the same memory and have the same length. Note that
207 /// this does *not* check to see if the *contents* are equal.
208 /// </summary>
209 public static bool operator ==(ReadOnlySpan<T> left, ReadOnlySpan<T> right) =>
210 left._length == right._length &&
211 Unsafe.AreSame<T>(ref left._pointer.Value, ref right._pointer.Value);
213 /// <summary>
214 /// For <see cref="ReadOnlySpan{Char}"/>, returns a new instance of string that represents the characters pointed to by the span.
215 /// Otherwise, returns a <see cref="string"/> with the name of the type and the number of elements.
216 /// </summary>
217 public override string ToString()
219 if (typeof(T) == typeof(char))
221 return new string(new ReadOnlySpan<char>(ref Unsafe.As<T, char>(ref _pointer.Value), _length));
223 #if FEATURE_UTF8STRING
224 else if (typeof(T) == typeof(Char8))
226 // TODO_UTF8STRING: Call into optimized transcoding routine when it's available.
227 return Encoding.UTF8.GetString(new ReadOnlySpan<byte>(ref Unsafe.As<T, byte>(ref _pointer.Value), _length));
229 #endif // FEATURE_UTF8STRING
230 return string.Format("System.ReadOnlySpan<{0}>[{1}]", typeof(T).Name, _length);
233 /// <summary>
234 /// Forms a slice out of the given read-only span, beginning at 'start'.
235 /// </summary>
236 /// <param name="start">The index at which to begin this slice.</param>
237 /// <exception cref="System.ArgumentOutOfRangeException">
238 /// Thrown when the specified <paramref name="start"/> index is not in range (&lt;0 or &gt;Length).
239 /// </exception>
240 [MethodImpl(MethodImplOptions.AggressiveInlining)]
241 public ReadOnlySpan<T> Slice(int start)
243 if ((uint)start > (uint)_length)
244 ThrowHelper.ThrowArgumentOutOfRangeException();
246 return new ReadOnlySpan<T>(ref Unsafe.Add(ref _pointer.Value, start), _length - start);
249 /// <summary>
250 /// Forms a slice out of the given read-only span, beginning at 'start', of given length
251 /// </summary>
252 /// <param name="start">The index at which to begin this slice.</param>
253 /// <param name="length">The desired length for the slice (exclusive).</param>
254 /// <exception cref="System.ArgumentOutOfRangeException">
255 /// Thrown when the specified <paramref name="start"/> or end index is not in range (&lt;0 or &gt;Length).
256 /// </exception>
257 [MethodImpl(MethodImplOptions.AggressiveInlining)]
258 public ReadOnlySpan<T> Slice(int start, int length)
260 #if BIT64
261 // See comment in Span<T>.Slice for how this works.
262 if ((ulong)(uint)start + (ulong)(uint)length > (ulong)(uint)_length)
263 ThrowHelper.ThrowArgumentOutOfRangeException();
264 #else
265 if ((uint)start > (uint)_length || (uint)length > (uint)(_length - start))
266 ThrowHelper.ThrowArgumentOutOfRangeException();
267 #endif
269 return new ReadOnlySpan<T>(ref Unsafe.Add(ref _pointer.Value, start), length);
272 /// <summary>
273 /// Copies the contents of this read-only span into a new array. This heap
274 /// allocates, so should generally be avoided, however it is sometimes
275 /// necessary to bridge the gap with APIs written in terms of arrays.
276 /// </summary>
277 public T[] ToArray()
279 if (_length == 0)
280 return Array.Empty<T>();
282 var destination = new T[_length];
283 Buffer.Memmove(ref Unsafe.As<byte, T>(ref destination.GetRawSzArrayData()), ref _pointer.Value, (nuint)_length);
284 return destination;