AsReadOnlySpan -> AsSpan rename to fix build breaks
[platform/upstream/coreclr.git] / src / mscorlib / shared / System / Span.NonGeneric.cs
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.Diagnostics;
6 using System.Globalization;
7 using System.Runtime;
8 using System.Runtime.CompilerServices;
9 using System.Runtime.InteropServices;
10
11 using Internal.Runtime.CompilerServices;
12
13 #if BIT64
14 using nuint = System.UInt64;
15 #else
16 using nuint = System.UInt32;
17 #endif
18
19 namespace System
20 {
21     /// <summary>
22     /// Extension methods and non-generic helpers for Span, ReadOnlySpan, Memory, and ReadOnlyMemory.
23     /// </summary>
24     public static class Span
25     {
26         public static bool Contains(this ReadOnlySpan<char> span, ReadOnlySpan<char> value, StringComparison comparisonType)
27         {
28             return (IndexOf(span, value, comparisonType) >= 0);
29         }
30
31         public static bool Equals(this ReadOnlySpan<char> span, ReadOnlySpan<char> value, StringComparison comparisonType)
32         {
33             StringSpanHelpers.CheckStringComparison(comparisonType);
34
35             switch (comparisonType)
36             {
37                 case StringComparison.CurrentCulture:
38                     return (CultureInfo.CurrentCulture.CompareInfo.Compare(span, value, CompareOptions.None) == 0);
39
40                 case StringComparison.CurrentCultureIgnoreCase:
41                     return (CultureInfo.CurrentCulture.CompareInfo.Compare(span, value, CompareOptions.IgnoreCase) == 0);
42
43                 case StringComparison.InvariantCulture:
44                     return (CompareInfo.Invariant.Compare(span, value, CompareOptions.None) == 0);
45
46                 case StringComparison.InvariantCultureIgnoreCase:
47                     return (CompareInfo.Invariant.Compare(span, value, CompareOptions.IgnoreCase) == 0);
48
49                 case StringComparison.Ordinal:
50                     if (span.Length != value.Length)
51                         return false;
52                     if (value.Length == 0)  // span.Length == value.Length == 0
53                         return true;
54                     return OrdinalHelper(span, value, value.Length);
55
56                 case StringComparison.OrdinalIgnoreCase:
57                     if (span.Length != value.Length)
58                         return false;
59                     if (value.Length == 0)  // span.Length == value.Length == 0
60                         return true;
61                     return (CompareInfo.CompareOrdinalIgnoreCase(span, value) == 0);
62             }
63
64             Debug.Fail("StringComparison outside range");
65             return false;
66         }
67
68         public static int CompareTo(this ReadOnlySpan<char> span, ReadOnlySpan<char> value, StringComparison comparisonType)
69         {
70             StringSpanHelpers.CheckStringComparison(comparisonType);
71
72             switch (comparisonType)
73             {
74                 case StringComparison.CurrentCulture:
75                     return CultureInfo.CurrentCulture.CompareInfo.Compare(span, value, CompareOptions.None);
76
77                 case StringComparison.CurrentCultureIgnoreCase:
78                     return CultureInfo.CurrentCulture.CompareInfo.Compare(span, value, CompareOptions.IgnoreCase);
79
80                 case StringComparison.InvariantCulture:
81                     return CompareInfo.Invariant.Compare(span, value, CompareOptions.None);
82
83                 case StringComparison.InvariantCultureIgnoreCase:
84                     return CompareInfo.Invariant.Compare(span, value, CompareOptions.IgnoreCase);
85
86                 case StringComparison.Ordinal:
87                     if (span.Length == 0 || value.Length == 0)
88                         return span.Length - value.Length;
89                     return string.CompareOrdinal(span, value);
90
91                 case StringComparison.OrdinalIgnoreCase:
92                     return CompareInfo.CompareOrdinalIgnoreCase(span, value);
93             }
94
95             Debug.Fail("StringComparison outside range");
96             return 0;
97         }
98
99         public static int IndexOf(this ReadOnlySpan<char> span, ReadOnlySpan<char> value, StringComparison comparisonType)
100         {
101             StringSpanHelpers.CheckStringComparison(comparisonType);
102
103             if (value.Length == 0)
104             {
105                 return 0;
106             }
107
108             if (span.Length == 0)
109             {
110                 return -1;
111             }
112
113             switch (comparisonType)
114             {
115                 case StringComparison.CurrentCulture:
116                     return IndexOfCultureHelper(span, value, CultureInfo.CurrentCulture.CompareInfo);
117
118                 case StringComparison.CurrentCultureIgnoreCase:
119                     return IndexOfCultureIgnoreCaseHelper(span, value, CultureInfo.CurrentCulture.CompareInfo);
120
121                 case StringComparison.InvariantCulture:
122                     return IndexOfCultureHelper(span, value, CompareInfo.Invariant);
123
124                 case StringComparison.InvariantCultureIgnoreCase:
125                     return IndexOfCultureIgnoreCaseHelper(span, value, CompareInfo.Invariant);
126
127                 case StringComparison.Ordinal:
128                     return IndexOfOrdinalHelper(span, value, ignoreCase: false);
129
130                 case StringComparison.OrdinalIgnoreCase:
131                     return IndexOfOrdinalHelper(span, value, ignoreCase: true);
132             }
133
134             Debug.Fail("StringComparison outside range");
135             return -1;
136         }
137
138         internal static int IndexOfCultureHelper(ReadOnlySpan<char> span, ReadOnlySpan<char> value, CompareInfo compareInfo)
139         {
140             Debug.Assert(span.Length != 0);
141             Debug.Assert(value.Length != 0);
142
143             if (GlobalizationMode.Invariant)
144             {
145                 return CompareInfo.InvariantIndexOf(span, value, ignoreCase: false);
146             }
147
148             return compareInfo.IndexOf(span, value, CompareOptions.None);
149         }
150
151         internal static int IndexOfCultureIgnoreCaseHelper(ReadOnlySpan<char> span, ReadOnlySpan<char> value, CompareInfo compareInfo)
152         {
153             Debug.Assert(span.Length != 0);
154             Debug.Assert(value.Length != 0);
155
156             if (GlobalizationMode.Invariant)
157             {
158                 return CompareInfo.InvariantIndexOf(span, value, ignoreCase: true);
159             }
160
161             return compareInfo.IndexOf(span, value, CompareOptions.IgnoreCase);
162         }
163
164         internal static int IndexOfOrdinalHelper(ReadOnlySpan<char> span, ReadOnlySpan<char> value, bool ignoreCase)
165         {
166             Debug.Assert(span.Length != 0);
167             Debug.Assert(value.Length != 0);
168
169             if (GlobalizationMode.Invariant)
170             {
171                 return CompareInfo.InvariantIndexOf(span, value, ignoreCase);
172             }
173
174             return CompareInfo.Invariant.IndexOfOrdinal(span, value, ignoreCase);
175         }
176
177         /// <summary>
178         /// Copies the characters from the source span into the destination, converting each character to lowercase.
179         /// </summary>
180         public static int ToLower(this ReadOnlySpan<char> source, Span<char> destination, CultureInfo culture)
181         {
182             if (culture == null)
183                 ThrowHelper.ThrowArgumentNullException(ExceptionArgument.culture);
184
185             // Assuming that changing case does not affect length
186             if (destination.Length < source.Length)
187                 return -1;
188
189             if (GlobalizationMode.Invariant)
190                 culture.TextInfo.ToLowerAsciiInvariant(source, destination);
191             else
192                 culture.TextInfo.ChangeCase(source, destination, toUpper: false);
193             return source.Length;
194         }
195
196         /// <summary>
197         /// Copies the characters from the source span into the destination, converting each character to lowercase
198         /// using the casing rules of the invariant culture.
199         /// </summary>
200         public static int ToLowerInvariant(this ReadOnlySpan<char> source, Span<char> destination)
201         {
202             // Assuming that changing case does not affect length
203             if (destination.Length < source.Length)
204                 return -1;
205
206             if (GlobalizationMode.Invariant)
207                 CultureInfo.InvariantCulture.TextInfo.ToLowerAsciiInvariant(source, destination);
208             else
209                 CultureInfo.InvariantCulture.TextInfo.ChangeCase(source, destination, toUpper: false);
210             return source.Length;
211         }
212
213         /// <summary>
214         /// Copies the characters from the source span into the destination, converting each character to uppercase.
215         /// </summary>
216         public static int ToUpper(this ReadOnlySpan<char> source, Span<char> destination, CultureInfo culture)
217         {
218             if (culture == null)
219                 ThrowHelper.ThrowArgumentNullException(ExceptionArgument.culture);
220
221             // Assuming that changing case does not affect length
222             if (destination.Length < source.Length)
223                 return -1;
224
225             if (GlobalizationMode.Invariant)
226                 culture.TextInfo.ToUpperAsciiInvariant(source, destination);
227             else
228                 culture.TextInfo.ChangeCase(source, destination, toUpper: true);
229             return source.Length;
230         }
231
232         /// <summary>
233         /// Copies the characters from the source span into the destination, converting each character to uppercase
234         /// using the casing rules of the invariant culture.
235         /// </summary>
236         public static int ToUpperInvariant(this ReadOnlySpan<char> source, Span<char> destination)
237         {
238             // Assuming that changing case does not affect length
239             if (destination.Length < source.Length)
240                 return -1;
241
242             if (GlobalizationMode.Invariant)
243                 CultureInfo.InvariantCulture.TextInfo.ToUpperAsciiInvariant(source, destination);
244             else
245                 CultureInfo.InvariantCulture.TextInfo.ChangeCase(source, destination, toUpper: true);
246             return source.Length;
247         }
248
249         /// <summary>
250         /// Determines whether the beginning of the span matches the specified value when compared using the specified comparison option.
251         /// </summary>
252         public static bool StartsWith(this ReadOnlySpan<char> span, ReadOnlySpan<char> value, StringComparison comparisonType)
253         {
254             if (value.Length == 0)
255             {
256                 StringSpanHelpers.CheckStringComparison(comparisonType);
257                 return true;
258             }
259
260             switch (comparisonType)
261             {
262                 case StringComparison.CurrentCulture:
263                     return StartsWithCultureHelper(span, value, CultureInfo.CurrentCulture.CompareInfo);
264
265                 case StringComparison.CurrentCultureIgnoreCase:
266                     return StartsWithCultureIgnoreCaseHelper(span, value, CultureInfo.CurrentCulture.CompareInfo);
267
268                 case StringComparison.InvariantCulture:
269                     return StartsWithCultureHelper(span, value, CompareInfo.Invariant);
270
271                 case StringComparison.InvariantCultureIgnoreCase:
272                     return StartsWithCultureIgnoreCaseHelper(span, value, CompareInfo.Invariant);
273
274                 case StringComparison.Ordinal:
275                     return StartsWithOrdinalHelper(span, value);
276
277                 case StringComparison.OrdinalIgnoreCase:
278                     return StartsWithOrdinalIgnoreCaseHelper(span, value);
279
280                 default:
281                     throw new ArgumentException(SR.NotSupported_StringComparison, nameof(comparisonType));
282             }
283         }
284
285         internal static bool StartsWithCultureHelper(ReadOnlySpan<char> span, ReadOnlySpan<char> value, CompareInfo compareInfo)
286         {
287             Debug.Assert(value.Length != 0);
288
289             if (GlobalizationMode.Invariant)
290             {
291                 return StartsWithOrdinalHelper(span, value);
292             }
293             if (span.Length == 0)
294             {
295                 return false;
296             }
297             return compareInfo.IsPrefix(span, value, CompareOptions.None);
298         }
299
300         internal static bool StartsWithCultureIgnoreCaseHelper(ReadOnlySpan<char> span, ReadOnlySpan<char> value, CompareInfo compareInfo)
301         {
302             Debug.Assert(value.Length != 0);
303
304             if (GlobalizationMode.Invariant)
305             {
306                 return StartsWithOrdinalIgnoreCaseHelper(span, value);
307             }
308             if (span.Length == 0)
309             {
310                 return false;
311             }
312             return compareInfo.IsPrefix(span, value, CompareOptions.IgnoreCase);
313         }
314
315         internal static bool StartsWithOrdinalIgnoreCaseHelper(ReadOnlySpan<char> span, ReadOnlySpan<char> value)
316         {
317             Debug.Assert(value.Length != 0);
318
319             if (span.Length < value.Length)
320             {
321                 return false;
322             }
323             return CompareInfo.CompareOrdinalIgnoreCase(span.Slice(0, value.Length), value) == 0;
324         }
325
326         internal static bool StartsWithOrdinalHelper(ReadOnlySpan<char> span, ReadOnlySpan<char> value)
327         {
328             Debug.Assert(value.Length != 0);
329
330             if (span.Length < value.Length)
331             {
332                 return false;
333             }
334             return OrdinalHelper(span, value, value.Length);
335         }
336
337         internal static unsafe bool OrdinalHelper(ReadOnlySpan<char> span, ReadOnlySpan<char> value, int length)
338         {
339             Debug.Assert(length != 0);
340             Debug.Assert(span.Length >= length);
341
342             fixed (char* ap = &MemoryMarshal.GetReference(span))
343             fixed (char* bp = &MemoryMarshal.GetReference(value))
344             {
345                 char* a = ap;
346                 char* b = bp;
347
348 #if BIT64
349                 // Single int read aligns pointers for the following long reads
350                 if (length >= 2)
351                 {
352                     if (*(int*)a != *(int*)b)
353                         return false;
354                     length -= 2;
355                     a += 2;
356                     b += 2;
357                 }
358
359                 while (length >= 12)
360                 {
361                     if (*(long*)a != *(long*)b)
362                         return false;
363                     if (*(long*)(a + 4) != *(long*)(b + 4))
364                         return false;
365                     if (*(long*)(a + 8) != *(long*)(b + 8))
366                         return false;
367                     length -= 12;
368                     a += 12;
369                     b += 12;
370                 }
371 #else
372                 while (length >= 10)
373                 {
374                     if (*(int*)a != *(int*)b) return false;
375                     if (*(int*)(a+2) != *(int*)(b+2)) return false;
376                     if (*(int*)(a+4) != *(int*)(b+4)) return false;
377                     if (*(int*)(a+6) != *(int*)(b+6)) return false;
378                     if (*(int*)(a+8) != *(int*)(b+8)) return false;
379                     length -= 10; a += 10; b += 10;
380                 }
381 #endif
382
383                 while (length >= 2)
384                 {
385                     if (*(int*)a != *(int*)b)
386                         return false;
387                     length -= 2;
388                     a += 2;
389                     b += 2;
390                 }
391
392                 // PERF: This depends on the fact that the String objects are always zero terminated 
393                 // and that the terminating zero is not included in the length. For even string sizes
394                 // this compare can include the zero terminator. Bitwise OR avoids a branch.
395                 return length == 0 | *a == *b;
396             }
397         }
398
399         /// <summary>
400         /// Determines whether the end of the span matches the specified value when compared using the specified comparison option.
401         /// </summary>
402         public static bool EndsWith(this ReadOnlySpan<char> span, ReadOnlySpan<char> value, StringComparison comparisonType)
403         {
404             if (value.Length == 0)
405             {
406                 StringSpanHelpers.CheckStringComparison(comparisonType);
407                 return true;
408             }
409
410             switch (comparisonType)
411             {
412                 case StringComparison.CurrentCulture:
413                     return EndsWithCultureHelper(span, value, CultureInfo.CurrentCulture.CompareInfo);
414
415                 case StringComparison.CurrentCultureIgnoreCase:
416                     return EndsWithCultureIgnoreCaseHelper(span, value, CultureInfo.CurrentCulture.CompareInfo);
417
418                 case StringComparison.InvariantCulture:
419                     return EndsWithCultureHelper(span, value, CompareInfo.Invariant);
420
421                 case StringComparison.InvariantCultureIgnoreCase:
422                     return EndsWithCultureIgnoreCaseHelper(span, value, CompareInfo.Invariant);
423
424                 case StringComparison.Ordinal:
425                     return EndsWithOrdinalHelper(span, value);
426
427                 case StringComparison.OrdinalIgnoreCase:
428                     return EndsWithOrdinalIgnoreCaseHelper(span, value);
429
430                 default:
431                     throw new ArgumentException(SR.NotSupported_StringComparison, nameof(comparisonType));
432             }
433         }
434
435         internal static bool EndsWithCultureHelper(ReadOnlySpan<char> span, ReadOnlySpan<char> value, CompareInfo compareInfo)
436         {
437             Debug.Assert(value.Length != 0);
438
439             if (GlobalizationMode.Invariant)
440             {
441                 return EndsWithOrdinalHelper(span, value);
442             }
443             if (span.Length == 0)
444             {
445                 return false;
446             }
447             return compareInfo.IsSuffix(span, value, CompareOptions.None);
448         }
449
450         internal static bool EndsWithCultureIgnoreCaseHelper(ReadOnlySpan<char> span, ReadOnlySpan<char> value, CompareInfo compareInfo)
451         {
452             Debug.Assert(value.Length != 0);
453
454             if (GlobalizationMode.Invariant)
455             {
456                 return EndsWithOrdinalIgnoreCaseHelper(span, value);
457             }
458             if (span.Length == 0)
459             {
460                 return false;
461             }
462             return compareInfo.IsSuffix(span, value, CompareOptions.IgnoreCase);
463         }
464
465         internal static bool EndsWithOrdinalIgnoreCaseHelper(ReadOnlySpan<char> span, ReadOnlySpan<char> value)
466         {
467             Debug.Assert(value.Length != 0);
468
469             if (span.Length < value.Length)
470             {
471                 return false;
472             }
473             return (CompareInfo.CompareOrdinalIgnoreCase(span.Slice(span.Length - value.Length), value) == 0);
474         }
475
476         internal static bool EndsWithOrdinalHelper(ReadOnlySpan<char> span, ReadOnlySpan<char> value)
477         {
478             Debug.Assert(value.Length != 0);
479
480             if (span.Length < value.Length)
481             {
482                 return false;
483             }
484             return OrdinalHelper(span.Slice(span.Length - value.Length), value, value.Length);
485         }
486
487         /// <summary>
488         /// Helper method for MemoryExtensions.AsSpan(T[] array, int start).
489         /// </summary>
490         public static Span<T> AsSpan<T>(T[] array, int start)
491         {
492             if (array == null)
493             {
494                 if (start != 0)
495                     ThrowHelper.ThrowArgumentOutOfRangeException();
496                 return default;
497             }
498             if (default(T) == null && array.GetType() != typeof(T[]))
499                 ThrowHelper.ThrowArrayTypeMismatchException();
500             if ((uint)start > (uint)array.Length)
501                 ThrowHelper.ThrowArgumentOutOfRangeException();
502
503             return new Span<T>(ref Unsafe.Add(ref Unsafe.As<byte, T>(ref array.GetRawSzArrayData()), start), array.Length - start);
504         }
505
506         /// <summary>
507         /// Helper method for MemoryExtensions.AsMemory(T[] array, int start).
508         /// </summary>
509         public static Memory<T> AsMemory<T>(T[] array, int start) => new Memory<T>(array, start);
510
511         /// <summary>Creates a new <see cref="ReadOnlyMemory{char}"/> over the portion of the target string.</summary>
512         /// <param name="text">The target string.</param>
513         /// <remarks>Returns default when <paramref name="text"/> is null.</remarks>
514         public static ReadOnlyMemory<char> AsReadOnlyMemory(this string text)
515         {
516             if (text == null)
517                 return default;
518
519             return new ReadOnlyMemory<char>(text, 0, text.Length);
520         }
521
522         /// <summary>Creates a new <see cref="ReadOnlyMemory{char}"/> over the portion of the target string.</summary>
523         /// <param name="text">The target string.</param>
524         /// <remarks>Returns default when <paramref name="text"/> is null.</remarks>
525         /// <exception cref="System.ArgumentOutOfRangeException">
526         /// Thrown when the specified <paramref name="start"/> index is not in range (&lt;0 or &gt;text.Length).
527         /// </exception>
528         public static ReadOnlyMemory<char> AsReadOnlyMemory(this string text, int start)
529         {
530             if (text == null)
531             {
532                 if (start != 0)
533                     ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.start);
534                 return default;
535             }
536
537             if ((uint)start > (uint)text.Length)
538                 ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.start);
539
540             return new ReadOnlyMemory<char>(text, start, text.Length - start);
541         }
542
543         /// <summary>Creates a new <see cref="ReadOnlyMemory{char}"/> over the portion of the target string.</summary>
544         /// <param name="text">The target string.</param>
545         /// <remarks>Returns default when <paramref name="text"/> is null.</remarks>
546         /// <exception cref="System.ArgumentOutOfRangeException">
547         /// Thrown when the specified <paramref name="start"/> index or <paramref name="length"/> is not in range.
548         /// </exception>
549         public static ReadOnlyMemory<char> AsReadOnlyMemory(this string text, int start, int length)
550         {
551             if (text == null)
552             {
553                 if (start != 0 || length != 0)
554                     ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.start);
555                 return default;
556             }
557
558             if ((uint)start > (uint)text.Length || (uint)length > (uint)(text.Length - start))
559                 ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.start);
560
561             return new ReadOnlyMemory<char>(text, start, length);
562         }
563
564         /// <summary>Attempts to get the underlying <see cref="string"/> from a <see cref="ReadOnlyMemory{T}"/>.</summary>
565         /// <param name="readOnlyMemory">The memory that may be wrapping a <see cref="string"/> object.</param>
566         /// <param name="text">The string.</param>
567         /// <param name="start">The starting location in <paramref name="text"/>.</param>
568         /// <param name="length">The number of items in <paramref name="text"/>.</param>
569         /// <returns></returns>
570         public static bool TryGetString(this ReadOnlyMemory<char> readOnlyMemory, out string text, out int start, out int length)
571         {
572             if (readOnlyMemory.GetObjectStartLength(out int offset, out int count) is string s)
573             {
574                 text = s;
575                 start = offset;
576                 length = count;
577                 return true;
578             }
579             else
580             {
581                 text = null;
582                 start = 0;
583                 length = 0;
584                 return false;
585             }
586         }
587
588         /// <summary>
589         /// Casts a Span of one primitive type <typeparamref name="T"/> to Span of bytes.
590         /// That type may not contain pointers or references. This is checked at runtime in order to preserve type safety.
591         /// </summary>
592         /// <param name="source">The source slice, of type <typeparamref name="T"/>.</param>
593         /// <exception cref="System.ArgumentException">
594         /// Thrown when <typeparamref name="T"/> contains pointers.
595         /// </exception>
596         [MethodImpl(MethodImplOptions.AggressiveInlining)]
597         public static Span<byte> AsBytes<T>(this Span<T> source)
598             where T : struct
599         {
600             if (RuntimeHelpers.IsReferenceOrContainsReferences<T>())
601                 ThrowHelper.ThrowInvalidTypeWithPointersNotSupported(typeof(T));
602
603             return new Span<byte>(
604                 ref Unsafe.As<T, byte>(ref MemoryMarshal.GetReference(source)),
605                 checked(source.Length * Unsafe.SizeOf<T>()));
606         }
607
608         /// <summary>
609         /// Casts a ReadOnlySpan of one primitive type <typeparamref name="T"/> to ReadOnlySpan of bytes.
610         /// That type may not contain pointers or references. This is checked at runtime in order to preserve type safety.
611         /// </summary>
612         /// <param name="source">The source slice, of type <typeparamref name="T"/>.</param>
613         /// <exception cref="System.ArgumentException">
614         /// Thrown when <typeparamref name="T"/> contains pointers.
615         /// </exception>
616         [MethodImpl(MethodImplOptions.AggressiveInlining)]
617         public static ReadOnlySpan<byte> AsBytes<T>(this ReadOnlySpan<T> source)
618             where T : struct
619         {
620             if (RuntimeHelpers.IsReferenceOrContainsReferences<T>())
621                 ThrowHelper.ThrowInvalidTypeWithPointersNotSupported(typeof(T));
622
623             return new ReadOnlySpan<byte>(
624                 ref Unsafe.As<T, byte>(ref MemoryMarshal.GetReference(source)),
625                 checked(source.Length * Unsafe.SizeOf<T>()));
626         }
627
628         /// <summary>
629         /// Creates a new readonly span over the portion of the target string.
630         /// </summary>
631         /// <param name="text">The target string.</param>
632         /// <remarks>Returns default when <paramref name="text"/> is null.</remarks>
633         /// reference (Nothing in Visual Basic).</exception>
634         [MethodImpl(MethodImplOptions.AggressiveInlining)]
635         public static ReadOnlySpan<char> AsSpan(this string text)
636         {
637             if (text == null)
638                 return default;
639
640             return new ReadOnlySpan<char>(ref text.GetRawStringData(), text.Length);
641         }
642
643         /// <summary>
644         /// Creates a new readonly span over the portion of the target string.
645         /// </summary>
646         /// <param name="text">The target string.</param>
647         /// <remarks>Returns default when <paramref name="text"/> is null.</remarks>
648         /// <exception cref="System.ArgumentOutOfRangeException">
649         /// Thrown when the specified <paramref name="start"/> index is not in range (&lt;0 or &gt;text.Length).
650         /// </exception>
651         [MethodImpl(MethodImplOptions.AggressiveInlining)]
652         public static ReadOnlySpan<char> AsSpan(this string text, int start)
653         {
654             if (text == null)
655             {
656                 if (start != 0)
657                     ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.start);
658                 return default;
659             }
660
661             if ((uint)start > (uint)text.Length)
662                 ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.start);
663
664             return new ReadOnlySpan<char>(ref Unsafe.Add(ref text.GetRawStringData(), start), text.Length - start);
665         }
666
667         /// <summary>
668         /// Creates a new readonly span over the portion of the target string.
669         /// </summary>
670         /// <param name="text">The target string.</param>
671         /// <remarks>Returns default when <paramref name="text"/> is null.</remarks>
672         /// <exception cref="System.ArgumentOutOfRangeException">
673         /// Thrown when the specified <paramref name="start"/> index or <paramref name="length"/> is not in range.
674         /// </exception>
675         [MethodImpl(MethodImplOptions.AggressiveInlining)]
676         public static ReadOnlySpan<char> AsSpan(this string text, int start, int length)
677         {
678             if (text == null)
679             {
680                 if (start != 0 || length != 0)
681                     ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.start);
682                 return default;
683             }
684
685             if ((uint)start > (uint)text.Length || (uint)length > (uint)(text.Length - start))
686                 ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.start);
687
688             return new ReadOnlySpan<char>(ref Unsafe.Add(ref text.GetRawStringData(), start), length);
689         }
690
691         // TODO: Delete once the AsReadOnlySpan -> AsSpan rename propages through the system
692         public static ReadOnlySpan<char> AsReadOnlySpan(this string text) => AsSpan(text);
693         public static ReadOnlySpan<char> AsReadOnlySpan(this string text, int start) => AsSpan(text, start);
694         public static ReadOnlySpan<char> AsReadOnlySpan(this string text, int start, int length) => AsSpan(text, start, length);
695
696         internal static unsafe void ClearWithoutReferences(ref byte b, nuint byteLength)
697         {
698             if (byteLength == 0)
699                 return;
700
701 #if CORECLR && (AMD64 || ARM64)
702             if (byteLength > 4096)
703                 goto PInvoke;
704             Unsafe.InitBlockUnaligned(ref b, 0, (uint)byteLength);
705             return;
706 #else
707             // TODO: Optimize other platforms to be on par with AMD64 CoreCLR
708             // Note: It's important that this switch handles lengths at least up to 22.
709             // See notes below near the main loop for why.
710
711             // The switch will be very fast since it can be implemented using a jump
712             // table in assembly. See http://stackoverflow.com/a/449297/4077294 for more info.
713
714             switch (byteLength)
715             {
716                 case 1:
717                     b = 0;
718                     return;
719                 case 2:
720                     Unsafe.As<byte, short>(ref b) = 0;
721                     return;
722                 case 3:
723                     Unsafe.As<byte, short>(ref b) = 0;
724                     Unsafe.Add<byte>(ref b, 2) = 0;
725                     return;
726                 case 4:
727                     Unsafe.As<byte, int>(ref b) = 0;
728                     return;
729                 case 5:
730                     Unsafe.As<byte, int>(ref b) = 0;
731                     Unsafe.Add<byte>(ref b, 4) = 0;
732                     return;
733                 case 6:
734                     Unsafe.As<byte, int>(ref b) = 0;
735                     Unsafe.As<byte, short>(ref Unsafe.Add<byte>(ref b, 4)) = 0;
736                     return;
737                 case 7:
738                     Unsafe.As<byte, int>(ref b) = 0;
739                     Unsafe.As<byte, short>(ref Unsafe.Add<byte>(ref b, 4)) = 0;
740                     Unsafe.Add<byte>(ref b, 6) = 0;
741                     return;
742                 case 8:
743 #if BIT64
744                     Unsafe.As<byte, long>(ref b) = 0;
745 #else
746                     Unsafe.As<byte, int>(ref b) = 0;
747                     Unsafe.As<byte, int>(ref Unsafe.Add<byte>(ref b, 4)) = 0;
748 #endif
749                     return;
750                 case 9:
751 #if BIT64
752                     Unsafe.As<byte, long>(ref b) = 0;
753 #else
754                     Unsafe.As<byte, int>(ref b) = 0;
755                     Unsafe.As<byte, int>(ref Unsafe.Add<byte>(ref b, 4)) = 0;
756 #endif
757                     Unsafe.Add<byte>(ref b, 8) = 0;
758                     return;
759                 case 10:
760 #if BIT64
761                     Unsafe.As<byte, long>(ref b) = 0;
762 #else
763                     Unsafe.As<byte, int>(ref b) = 0;
764                     Unsafe.As<byte, int>(ref Unsafe.Add<byte>(ref b, 4)) = 0;
765 #endif
766                     Unsafe.As<byte, short>(ref Unsafe.Add<byte>(ref b, 8)) = 0;
767                     return;
768                 case 11:
769 #if BIT64
770                     Unsafe.As<byte, long>(ref b) = 0;
771 #else
772                     Unsafe.As<byte, int>(ref b) = 0;
773                     Unsafe.As<byte, int>(ref Unsafe.Add<byte>(ref b, 4)) = 0;
774 #endif
775                     Unsafe.As<byte, short>(ref Unsafe.Add<byte>(ref b, 8)) = 0;
776                     Unsafe.Add<byte>(ref b, 10) = 0;
777                     return;
778                 case 12:
779 #if BIT64
780                     Unsafe.As<byte, long>(ref b) = 0;
781 #else
782                     Unsafe.As<byte, int>(ref b) = 0;
783                     Unsafe.As<byte, int>(ref Unsafe.Add<byte>(ref b, 4)) = 0;
784 #endif
785                     Unsafe.As<byte, int>(ref Unsafe.Add<byte>(ref b, 8)) = 0;
786                     return;
787                 case 13:
788 #if BIT64
789                     Unsafe.As<byte, long>(ref b) = 0;
790 #else
791                     Unsafe.As<byte, int>(ref b) = 0;
792                     Unsafe.As<byte, int>(ref Unsafe.Add<byte>(ref b, 4)) = 0;
793 #endif
794                     Unsafe.As<byte, int>(ref Unsafe.Add<byte>(ref b, 8)) = 0;
795                     Unsafe.Add<byte>(ref b, 12) = 0;
796                     return;
797                 case 14:
798 #if BIT64
799                     Unsafe.As<byte, long>(ref b) = 0;
800 #else
801                     Unsafe.As<byte, int>(ref b) = 0;
802                     Unsafe.As<byte, int>(ref Unsafe.Add<byte>(ref b, 4)) = 0;
803 #endif
804                     Unsafe.As<byte, int>(ref Unsafe.Add<byte>(ref b, 8)) = 0;
805                     Unsafe.As<byte, short>(ref Unsafe.Add<byte>(ref b, 12)) = 0;
806                     return;
807                 case 15:
808 #if BIT64
809                     Unsafe.As<byte, long>(ref b) = 0;
810 #else
811                     Unsafe.As<byte, int>(ref b) = 0;
812                     Unsafe.As<byte, int>(ref Unsafe.Add<byte>(ref b, 4)) = 0;
813 #endif
814                     Unsafe.As<byte, int>(ref Unsafe.Add<byte>(ref b, 8)) = 0;
815                     Unsafe.As<byte, short>(ref Unsafe.Add<byte>(ref b, 12)) = 0;
816                     Unsafe.Add<byte>(ref b, 14) = 0;
817                     return;
818                 case 16:
819 #if BIT64
820                     Unsafe.As<byte, long>(ref b) = 0;
821                     Unsafe.As<byte, long>(ref Unsafe.Add<byte>(ref b, 8)) = 0;
822 #else
823                     Unsafe.As<byte, int>(ref b) = 0;
824                     Unsafe.As<byte, int>(ref Unsafe.Add<byte>(ref b, 4)) = 0;
825                     Unsafe.As<byte, int>(ref Unsafe.Add<byte>(ref b, 8)) = 0;
826                     Unsafe.As<byte, int>(ref Unsafe.Add<byte>(ref b, 12)) = 0;
827 #endif
828                     return;
829                 case 17:
830 #if BIT64
831                     Unsafe.As<byte, long>(ref b) = 0;
832                     Unsafe.As<byte, long>(ref Unsafe.Add<byte>(ref b, 8)) = 0;
833 #else
834                     Unsafe.As<byte, int>(ref b) = 0;
835                     Unsafe.As<byte, int>(ref Unsafe.Add<byte>(ref b, 4)) = 0;
836                     Unsafe.As<byte, int>(ref Unsafe.Add<byte>(ref b, 8)) = 0;
837                     Unsafe.As<byte, int>(ref Unsafe.Add<byte>(ref b, 12)) = 0;
838 #endif
839                     Unsafe.Add<byte>(ref b, 16) = 0;
840                     return;
841                 case 18:
842 #if BIT64
843                     Unsafe.As<byte, long>(ref b) = 0;
844                     Unsafe.As<byte, long>(ref Unsafe.Add<byte>(ref b, 8)) = 0;
845 #else
846                     Unsafe.As<byte, int>(ref b) = 0;
847                     Unsafe.As<byte, int>(ref Unsafe.Add<byte>(ref b, 4)) = 0;
848                     Unsafe.As<byte, int>(ref Unsafe.Add<byte>(ref b, 8)) = 0;
849                     Unsafe.As<byte, int>(ref Unsafe.Add<byte>(ref b, 12)) = 0;
850 #endif
851                     Unsafe.As<byte, short>(ref Unsafe.Add<byte>(ref b, 16)) = 0;
852                     return;
853                 case 19:
854 #if BIT64
855                     Unsafe.As<byte, long>(ref b) = 0;
856                     Unsafe.As<byte, long>(ref Unsafe.Add<byte>(ref b, 8)) = 0;
857 #else
858                     Unsafe.As<byte, int>(ref b) = 0;
859                     Unsafe.As<byte, int>(ref Unsafe.Add<byte>(ref b, 4)) = 0;
860                     Unsafe.As<byte, int>(ref Unsafe.Add<byte>(ref b, 8)) = 0;
861                     Unsafe.As<byte, int>(ref Unsafe.Add<byte>(ref b, 12)) = 0;
862 #endif
863                     Unsafe.As<byte, short>(ref Unsafe.Add<byte>(ref b, 16)) = 0;
864                     Unsafe.Add<byte>(ref b, 18) = 0;
865                     return;
866                 case 20:
867 #if BIT64
868                     Unsafe.As<byte, long>(ref b) = 0;
869                     Unsafe.As<byte, long>(ref Unsafe.Add<byte>(ref b, 8)) = 0;
870 #else
871                     Unsafe.As<byte, int>(ref b) = 0;
872                     Unsafe.As<byte, int>(ref Unsafe.Add<byte>(ref b, 4)) = 0;
873                     Unsafe.As<byte, int>(ref Unsafe.Add<byte>(ref b, 8)) = 0;
874                     Unsafe.As<byte, int>(ref Unsafe.Add<byte>(ref b, 12)) = 0;
875 #endif
876                     Unsafe.As<byte, int>(ref Unsafe.Add<byte>(ref b, 16)) = 0;
877                     return;
878                 case 21:
879 #if BIT64
880                     Unsafe.As<byte, long>(ref b) = 0;
881                     Unsafe.As<byte, long>(ref Unsafe.Add<byte>(ref b, 8)) = 0;
882 #else
883                     Unsafe.As<byte, int>(ref b) = 0;
884                     Unsafe.As<byte, int>(ref Unsafe.Add<byte>(ref b, 4)) = 0;
885                     Unsafe.As<byte, int>(ref Unsafe.Add<byte>(ref b, 8)) = 0;
886                     Unsafe.As<byte, int>(ref Unsafe.Add<byte>(ref b, 12)) = 0;
887 #endif
888                     Unsafe.As<byte, int>(ref Unsafe.Add<byte>(ref b, 16)) = 0;
889                     Unsafe.Add<byte>(ref b, 20) = 0;
890                     return;
891                 case 22:
892 #if BIT64
893                     Unsafe.As<byte, long>(ref b) = 0;
894                     Unsafe.As<byte, long>(ref Unsafe.Add<byte>(ref b, 8)) = 0;
895 #else
896                     Unsafe.As<byte, int>(ref b) = 0;
897                     Unsafe.As<byte, int>(ref Unsafe.Add<byte>(ref b, 4)) = 0;
898                     Unsafe.As<byte, int>(ref Unsafe.Add<byte>(ref b, 8)) = 0;
899                     Unsafe.As<byte, int>(ref Unsafe.Add<byte>(ref b, 12)) = 0;
900 #endif
901                     Unsafe.As<byte, int>(ref Unsafe.Add<byte>(ref b, 16)) = 0;
902                     Unsafe.As<byte, short>(ref Unsafe.Add<byte>(ref b, 20)) = 0;
903                     return;
904             }
905
906             // P/Invoke into the native version for large lengths
907             if (byteLength >= 512) goto PInvoke;
908
909             nuint i = 0; // byte offset at which we're copying
910
911             if ((Unsafe.As<byte, int>(ref b) & 3) != 0)
912             {
913                 if ((Unsafe.As<byte, int>(ref b) & 1) != 0)
914                 {
915                     Unsafe.AddByteOffset<byte>(ref b, i) = 0;
916                     i += 1;
917                     if ((Unsafe.As<byte, int>(ref b) & 2) != 0)
918                         goto IntAligned;
919                 }
920                 Unsafe.As<byte, short>(ref Unsafe.AddByteOffset<byte>(ref b, i)) = 0;
921                 i += 2;
922             }
923
924             IntAligned:
925
926             // On 64-bit IntPtr.Size == 8, so we want to advance to the next 8-aligned address. If
927             // (int)b % 8 is 0, 5, 6, or 7, we will already have advanced by 0, 3, 2, or 1
928             // bytes to the next aligned address (respectively), so do nothing. On the other hand,
929             // if it is 1, 2, 3, or 4 we will want to copy-and-advance another 4 bytes until
930             // we're aligned.
931             // The thing 1, 2, 3, and 4 have in common that the others don't is that if you
932             // subtract one from them, their 3rd lsb will not be set. Hence, the below check.
933
934             if (((Unsafe.As<byte, int>(ref b) - 1) & 4) == 0)
935             {
936                 Unsafe.As<byte, int>(ref Unsafe.AddByteOffset<byte>(ref b, i)) = 0;
937                 i += 4;
938             }
939
940             nuint end = byteLength - 16;
941             byteLength -= i; // lower 4 bits of byteLength represent how many bytes are left *after* the unrolled loop
942
943             // We know due to the above switch-case that this loop will always run 1 iteration; max
944             // bytes we clear before checking is 23 (7 to align the pointers, 16 for 1 iteration) so
945             // the switch handles lengths 0-22.
946             Debug.Assert(end >= 7 && i <= end);
947
948             // This is separated out into a different variable, so the i + 16 addition can be
949             // performed at the start of the pipeline and the loop condition does not have
950             // a dependency on the writes.
951             nuint counter;
952
953             do
954             {
955                 counter = i + 16;
956
957                 // This loop looks very costly since there appear to be a bunch of temporary values
958                 // being created with the adds, but the jit (for x86 anyways) will convert each of
959                 // these to use memory addressing operands.
960
961                 // So the only cost is a bit of code size, which is made up for by the fact that
962                 // we save on writes to b.
963
964 #if BIT64
965                 Unsafe.As<byte, long>(ref Unsafe.AddByteOffset<byte>(ref b, i)) = 0;
966                 Unsafe.As<byte, long>(ref Unsafe.AddByteOffset<byte>(ref b, i + 8)) = 0;
967 #else
968                 Unsafe.As<byte, int>(ref Unsafe.AddByteOffset<byte>(ref b, i)) = 0;
969                 Unsafe.As<byte, int>(ref Unsafe.AddByteOffset<byte>(ref b, i + 4)) = 0;
970                 Unsafe.As<byte, int>(ref Unsafe.AddByteOffset<byte>(ref b, i + 8)) = 0;
971                 Unsafe.As<byte, int>(ref Unsafe.AddByteOffset<byte>(ref b, i + 12)) = 0;
972 #endif
973
974                 i = counter;
975
976                 // See notes above for why this wasn't used instead
977                 // i += 16;
978             }
979             while (counter <= end);
980
981             if ((byteLength & 8) != 0)
982             {
983 #if BIT64
984                 Unsafe.As<byte, long>(ref Unsafe.AddByteOffset<byte>(ref b, i)) = 0;
985 #else
986                 Unsafe.As<byte, int>(ref Unsafe.AddByteOffset<byte>(ref b, i)) = 0;
987                 Unsafe.As<byte, int>(ref Unsafe.AddByteOffset<byte>(ref b, i + 4)) = 0;
988 #endif
989                 i += 8;
990             }
991             if ((byteLength & 4) != 0)
992             {
993                 Unsafe.As<byte, int>(ref Unsafe.AddByteOffset<byte>(ref b, i)) = 0;
994                 i += 4;
995             }
996             if ((byteLength & 2) != 0)
997             {
998                 Unsafe.As<byte, short>(ref Unsafe.AddByteOffset<byte>(ref b, i)) = 0;
999                 i += 2;
1000             }
1001             if ((byteLength & 1) != 0)
1002             {
1003                 Unsafe.AddByteOffset<byte>(ref b, i) = 0;
1004                 // We're not using i after this, so not needed
1005                 // i += 1;
1006             }
1007
1008             return;
1009 #endif
1010
1011         PInvoke:
1012             RuntimeImports.RhZeroMemory(ref b, byteLength);
1013         }
1014
1015         internal static unsafe void ClearWithReferences(ref IntPtr ip, nuint pointerSizeLength)
1016         {
1017             if (pointerSizeLength == 0)
1018                 return;
1019
1020             // TODO: Perhaps do switch casing to improve small size perf
1021
1022             nuint i = 0;
1023             nuint n = 0;
1024             while ((n = i + 8) <= (pointerSizeLength))
1025             {
1026                 Unsafe.AddByteOffset<IntPtr>(ref ip, (i + 0) * (nuint)sizeof(IntPtr)) = default(IntPtr);
1027                 Unsafe.AddByteOffset<IntPtr>(ref ip, (i + 1) * (nuint)sizeof(IntPtr)) = default(IntPtr);
1028                 Unsafe.AddByteOffset<IntPtr>(ref ip, (i + 2) * (nuint)sizeof(IntPtr)) = default(IntPtr);
1029                 Unsafe.AddByteOffset<IntPtr>(ref ip, (i + 3) * (nuint)sizeof(IntPtr)) = default(IntPtr);
1030                 Unsafe.AddByteOffset<IntPtr>(ref ip, (i + 4) * (nuint)sizeof(IntPtr)) = default(IntPtr);
1031                 Unsafe.AddByteOffset<IntPtr>(ref ip, (i + 5) * (nuint)sizeof(IntPtr)) = default(IntPtr);
1032                 Unsafe.AddByteOffset<IntPtr>(ref ip, (i + 6) * (nuint)sizeof(IntPtr)) = default(IntPtr);
1033                 Unsafe.AddByteOffset<IntPtr>(ref ip, (i + 7) * (nuint)sizeof(IntPtr)) = default(IntPtr);
1034                 i = n;
1035             }
1036             if ((n = i + 4) <= (pointerSizeLength))
1037             {
1038                 Unsafe.AddByteOffset<IntPtr>(ref ip, (i + 0) * (nuint)sizeof(IntPtr)) = default(IntPtr);
1039                 Unsafe.AddByteOffset<IntPtr>(ref ip, (i + 1) * (nuint)sizeof(IntPtr)) = default(IntPtr);
1040                 Unsafe.AddByteOffset<IntPtr>(ref ip, (i + 2) * (nuint)sizeof(IntPtr)) = default(IntPtr);
1041                 Unsafe.AddByteOffset<IntPtr>(ref ip, (i + 3) * (nuint)sizeof(IntPtr)) = default(IntPtr);
1042                 i = n;
1043             }
1044             if ((n = i + 2) <= (pointerSizeLength))
1045             {
1046                 Unsafe.AddByteOffset<IntPtr>(ref ip, (i + 0) * (nuint)sizeof(IntPtr)) = default(IntPtr);
1047                 Unsafe.AddByteOffset<IntPtr>(ref ip, (i + 1) * (nuint)sizeof(IntPtr)) = default(IntPtr);
1048                 i = n;
1049             }
1050             if ((i + 1) <= (pointerSizeLength))
1051             {
1052                 Unsafe.AddByteOffset<IntPtr>(ref ip, (i + 0) * (nuint)sizeof(IntPtr)) = default(IntPtr);
1053             }
1054         }
1055     }
1056 }