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.Globalization;
8 using System.Runtime.CompilerServices;
9 using System.Runtime.InteropServices;
11 using Internal.Runtime.CompilerServices;
14 using nuint = System.UInt64;
16 using nuint = System.UInt32;
22 /// Extension methods and non-generic helpers for Span, ReadOnlySpan, Memory, and ReadOnlyMemory.
24 public static class Span
26 public static bool Contains(this ReadOnlySpan<char> span, ReadOnlySpan<char> value, StringComparison comparisonType)
28 return (IndexOf(span, value, comparisonType) >= 0);
31 public static bool Equals(this ReadOnlySpan<char> span, ReadOnlySpan<char> value, StringComparison comparisonType)
33 StringSpanHelpers.CheckStringComparison(comparisonType);
35 switch (comparisonType)
37 case StringComparison.CurrentCulture:
38 return (CultureInfo.CurrentCulture.CompareInfo.Compare(span, value, CompareOptions.None) == 0);
40 case StringComparison.CurrentCultureIgnoreCase:
41 return (CultureInfo.CurrentCulture.CompareInfo.Compare(span, value, CompareOptions.IgnoreCase) == 0);
43 case StringComparison.InvariantCulture:
44 return (CompareInfo.Invariant.Compare(span, value, CompareOptions.None) == 0);
46 case StringComparison.InvariantCultureIgnoreCase:
47 return (CompareInfo.Invariant.Compare(span, value, CompareOptions.IgnoreCase) == 0);
49 case StringComparison.Ordinal:
50 if (span.Length != value.Length)
52 if (value.Length == 0) // span.Length == value.Length == 0
54 return OrdinalHelper(span, value, value.Length);
56 case StringComparison.OrdinalIgnoreCase:
57 if (span.Length != value.Length)
59 if (value.Length == 0) // span.Length == value.Length == 0
61 return (CompareInfo.CompareOrdinalIgnoreCase(span, value) == 0);
64 Debug.Fail("StringComparison outside range");
68 public static int CompareTo(this ReadOnlySpan<char> span, ReadOnlySpan<char> value, StringComparison comparisonType)
70 StringSpanHelpers.CheckStringComparison(comparisonType);
72 switch (comparisonType)
74 case StringComparison.CurrentCulture:
75 return CultureInfo.CurrentCulture.CompareInfo.Compare(span, value, CompareOptions.None);
77 case StringComparison.CurrentCultureIgnoreCase:
78 return CultureInfo.CurrentCulture.CompareInfo.Compare(span, value, CompareOptions.IgnoreCase);
80 case StringComparison.InvariantCulture:
81 return CompareInfo.Invariant.Compare(span, value, CompareOptions.None);
83 case StringComparison.InvariantCultureIgnoreCase:
84 return CompareInfo.Invariant.Compare(span, value, CompareOptions.IgnoreCase);
86 case StringComparison.Ordinal:
87 if (span.Length == 0 || value.Length == 0)
88 return span.Length - value.Length;
89 return string.CompareOrdinal(span, value);
91 case StringComparison.OrdinalIgnoreCase:
92 return CompareInfo.CompareOrdinalIgnoreCase(span, value);
95 Debug.Fail("StringComparison outside range");
99 public static int IndexOf(this ReadOnlySpan<char> span, ReadOnlySpan<char> value, StringComparison comparisonType)
101 StringSpanHelpers.CheckStringComparison(comparisonType);
103 if (value.Length == 0)
108 if (span.Length == 0)
113 switch (comparisonType)
115 case StringComparison.CurrentCulture:
116 return IndexOfCultureHelper(span, value, CultureInfo.CurrentCulture.CompareInfo);
118 case StringComparison.CurrentCultureIgnoreCase:
119 return IndexOfCultureIgnoreCaseHelper(span, value, CultureInfo.CurrentCulture.CompareInfo);
121 case StringComparison.InvariantCulture:
122 return IndexOfCultureHelper(span, value, CompareInfo.Invariant);
124 case StringComparison.InvariantCultureIgnoreCase:
125 return IndexOfCultureIgnoreCaseHelper(span, value, CompareInfo.Invariant);
127 case StringComparison.Ordinal:
128 return IndexOfOrdinalHelper(span, value, ignoreCase: false);
130 case StringComparison.OrdinalIgnoreCase:
131 return IndexOfOrdinalHelper(span, value, ignoreCase: true);
134 Debug.Fail("StringComparison outside range");
138 internal static int IndexOfCultureHelper(ReadOnlySpan<char> span, ReadOnlySpan<char> value, CompareInfo compareInfo)
140 Debug.Assert(span.Length != 0);
141 Debug.Assert(value.Length != 0);
143 if (GlobalizationMode.Invariant)
145 return CompareInfo.InvariantIndexOf(span, value, ignoreCase: false);
148 return compareInfo.IndexOf(span, value, CompareOptions.None);
151 internal static int IndexOfCultureIgnoreCaseHelper(ReadOnlySpan<char> span, ReadOnlySpan<char> value, CompareInfo compareInfo)
153 Debug.Assert(span.Length != 0);
154 Debug.Assert(value.Length != 0);
156 if (GlobalizationMode.Invariant)
158 return CompareInfo.InvariantIndexOf(span, value, ignoreCase: true);
161 return compareInfo.IndexOf(span, value, CompareOptions.IgnoreCase);
164 internal static int IndexOfOrdinalHelper(ReadOnlySpan<char> span, ReadOnlySpan<char> value, bool ignoreCase)
166 Debug.Assert(span.Length != 0);
167 Debug.Assert(value.Length != 0);
169 if (GlobalizationMode.Invariant)
171 return CompareInfo.InvariantIndexOf(span, value, ignoreCase);
174 return CompareInfo.Invariant.IndexOfOrdinal(span, value, ignoreCase);
178 /// Copies the characters from the source span into the destination, converting each character to lowercase.
180 public static int ToLower(this ReadOnlySpan<char> source, Span<char> destination, CultureInfo culture)
183 ThrowHelper.ThrowArgumentNullException(ExceptionArgument.culture);
185 // Assuming that changing case does not affect length
186 if (destination.Length < source.Length)
189 if (GlobalizationMode.Invariant)
190 culture.TextInfo.ToLowerAsciiInvariant(source, destination);
192 culture.TextInfo.ChangeCase(source, destination, toUpper: false);
193 return source.Length;
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.
200 public static int ToLowerInvariant(this ReadOnlySpan<char> source, Span<char> destination)
202 // Assuming that changing case does not affect length
203 if (destination.Length < source.Length)
206 if (GlobalizationMode.Invariant)
207 CultureInfo.InvariantCulture.TextInfo.ToLowerAsciiInvariant(source, destination);
209 CultureInfo.InvariantCulture.TextInfo.ChangeCase(source, destination, toUpper: false);
210 return source.Length;
214 /// Copies the characters from the source span into the destination, converting each character to uppercase.
216 public static int ToUpper(this ReadOnlySpan<char> source, Span<char> destination, CultureInfo culture)
219 ThrowHelper.ThrowArgumentNullException(ExceptionArgument.culture);
221 // Assuming that changing case does not affect length
222 if (destination.Length < source.Length)
225 if (GlobalizationMode.Invariant)
226 culture.TextInfo.ToUpperAsciiInvariant(source, destination);
228 culture.TextInfo.ChangeCase(source, destination, toUpper: true);
229 return source.Length;
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.
236 public static int ToUpperInvariant(this ReadOnlySpan<char> source, Span<char> destination)
238 // Assuming that changing case does not affect length
239 if (destination.Length < source.Length)
242 if (GlobalizationMode.Invariant)
243 CultureInfo.InvariantCulture.TextInfo.ToUpperAsciiInvariant(source, destination);
245 CultureInfo.InvariantCulture.TextInfo.ChangeCase(source, destination, toUpper: true);
246 return source.Length;
250 /// Determines whether the beginning of the span matches the specified value when compared using the specified comparison option.
252 public static bool StartsWith(this ReadOnlySpan<char> span, ReadOnlySpan<char> value, StringComparison comparisonType)
254 if (value.Length == 0)
256 StringSpanHelpers.CheckStringComparison(comparisonType);
260 switch (comparisonType)
262 case StringComparison.CurrentCulture:
263 return StartsWithCultureHelper(span, value, CultureInfo.CurrentCulture.CompareInfo);
265 case StringComparison.CurrentCultureIgnoreCase:
266 return StartsWithCultureIgnoreCaseHelper(span, value, CultureInfo.CurrentCulture.CompareInfo);
268 case StringComparison.InvariantCulture:
269 return StartsWithCultureHelper(span, value, CompareInfo.Invariant);
271 case StringComparison.InvariantCultureIgnoreCase:
272 return StartsWithCultureIgnoreCaseHelper(span, value, CompareInfo.Invariant);
274 case StringComparison.Ordinal:
275 return StartsWithOrdinalHelper(span, value);
277 case StringComparison.OrdinalIgnoreCase:
278 return StartsWithOrdinalIgnoreCaseHelper(span, value);
281 throw new ArgumentException(SR.NotSupported_StringComparison, nameof(comparisonType));
285 internal static bool StartsWithCultureHelper(ReadOnlySpan<char> span, ReadOnlySpan<char> value, CompareInfo compareInfo)
287 Debug.Assert(value.Length != 0);
289 if (GlobalizationMode.Invariant)
291 return StartsWithOrdinalHelper(span, value);
293 if (span.Length == 0)
297 return compareInfo.IsPrefix(span, value, CompareOptions.None);
300 internal static bool StartsWithCultureIgnoreCaseHelper(ReadOnlySpan<char> span, ReadOnlySpan<char> value, CompareInfo compareInfo)
302 Debug.Assert(value.Length != 0);
304 if (GlobalizationMode.Invariant)
306 return StartsWithOrdinalIgnoreCaseHelper(span, value);
308 if (span.Length == 0)
312 return compareInfo.IsPrefix(span, value, CompareOptions.IgnoreCase);
315 internal static bool StartsWithOrdinalIgnoreCaseHelper(ReadOnlySpan<char> span, ReadOnlySpan<char> value)
317 Debug.Assert(value.Length != 0);
319 if (span.Length < value.Length)
323 return CompareInfo.CompareOrdinalIgnoreCase(span.Slice(0, value.Length), value) == 0;
326 internal static bool StartsWithOrdinalHelper(ReadOnlySpan<char> span, ReadOnlySpan<char> value)
328 Debug.Assert(value.Length != 0);
330 if (span.Length < value.Length)
334 return OrdinalHelper(span, value, value.Length);
337 internal static unsafe bool OrdinalHelper(ReadOnlySpan<char> span, ReadOnlySpan<char> value, int length)
339 Debug.Assert(length != 0);
340 Debug.Assert(span.Length >= length);
342 fixed (char* ap = &MemoryMarshal.GetReference(span))
343 fixed (char* bp = &MemoryMarshal.GetReference(value))
349 // Single int read aligns pointers for the following long reads
352 if (*(int*)a != *(int*)b)
361 if (*(long*)a != *(long*)b)
363 if (*(long*)(a + 4) != *(long*)(b + 4))
365 if (*(long*)(a + 8) != *(long*)(b + 8))
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;
385 if (*(int*)a != *(int*)b)
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;
400 /// Determines whether the end of the span matches the specified value when compared using the specified comparison option.
402 public static bool EndsWith(this ReadOnlySpan<char> span, ReadOnlySpan<char> value, StringComparison comparisonType)
404 if (value.Length == 0)
406 StringSpanHelpers.CheckStringComparison(comparisonType);
410 switch (comparisonType)
412 case StringComparison.CurrentCulture:
413 return EndsWithCultureHelper(span, value, CultureInfo.CurrentCulture.CompareInfo);
415 case StringComparison.CurrentCultureIgnoreCase:
416 return EndsWithCultureIgnoreCaseHelper(span, value, CultureInfo.CurrentCulture.CompareInfo);
418 case StringComparison.InvariantCulture:
419 return EndsWithCultureHelper(span, value, CompareInfo.Invariant);
421 case StringComparison.InvariantCultureIgnoreCase:
422 return EndsWithCultureIgnoreCaseHelper(span, value, CompareInfo.Invariant);
424 case StringComparison.Ordinal:
425 return EndsWithOrdinalHelper(span, value);
427 case StringComparison.OrdinalIgnoreCase:
428 return EndsWithOrdinalIgnoreCaseHelper(span, value);
431 throw new ArgumentException(SR.NotSupported_StringComparison, nameof(comparisonType));
435 internal static bool EndsWithCultureHelper(ReadOnlySpan<char> span, ReadOnlySpan<char> value, CompareInfo compareInfo)
437 Debug.Assert(value.Length != 0);
439 if (GlobalizationMode.Invariant)
441 return EndsWithOrdinalHelper(span, value);
443 if (span.Length == 0)
447 return compareInfo.IsSuffix(span, value, CompareOptions.None);
450 internal static bool EndsWithCultureIgnoreCaseHelper(ReadOnlySpan<char> span, ReadOnlySpan<char> value, CompareInfo compareInfo)
452 Debug.Assert(value.Length != 0);
454 if (GlobalizationMode.Invariant)
456 return EndsWithOrdinalIgnoreCaseHelper(span, value);
458 if (span.Length == 0)
462 return compareInfo.IsSuffix(span, value, CompareOptions.IgnoreCase);
465 internal static bool EndsWithOrdinalIgnoreCaseHelper(ReadOnlySpan<char> span, ReadOnlySpan<char> value)
467 Debug.Assert(value.Length != 0);
469 if (span.Length < value.Length)
473 return (CompareInfo.CompareOrdinalIgnoreCase(span.Slice(span.Length - value.Length), value) == 0);
476 internal static bool EndsWithOrdinalHelper(ReadOnlySpan<char> span, ReadOnlySpan<char> value)
478 Debug.Assert(value.Length != 0);
480 if (span.Length < value.Length)
484 return OrdinalHelper(span.Slice(span.Length - value.Length), value, value.Length);
488 /// Helper method for MemoryExtensions.AsSpan(T[] array, int start).
490 public static Span<T> AsSpan<T>(T[] array, int start)
495 ThrowHelper.ThrowArgumentOutOfRangeException();
498 if (default(T) == null && array.GetType() != typeof(T[]))
499 ThrowHelper.ThrowArrayTypeMismatchException();
500 if ((uint)start > (uint)array.Length)
501 ThrowHelper.ThrowArgumentOutOfRangeException();
503 return new Span<T>(ref Unsafe.Add(ref Unsafe.As<byte, T>(ref array.GetRawSzArrayData()), start), array.Length - start);
507 /// Helper method for MemoryExtensions.AsMemory(T[] array, int start).
509 public static Memory<T> AsMemory<T>(T[] array, int start) => new Memory<T>(array, start);
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)
519 return new ReadOnlyMemory<char>(text, 0, text.Length);
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 (<0 or >text.Length).
528 public static ReadOnlyMemory<char> AsReadOnlyMemory(this string text, int start)
533 ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.start);
537 if ((uint)start > (uint)text.Length)
538 ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.start);
540 return new ReadOnlyMemory<char>(text, start, text.Length - start);
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.
549 public static ReadOnlyMemory<char> AsReadOnlyMemory(this string text, int start, int length)
553 if (start != 0 || length != 0)
554 ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.start);
558 if ((uint)start > (uint)text.Length || (uint)length > (uint)(text.Length - start))
559 ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.start);
561 return new ReadOnlyMemory<char>(text, start, length);
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)
572 if (readOnlyMemory.GetObjectStartLength(out int offset, out int count) is string s)
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.
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.
596 [MethodImpl(MethodImplOptions.AggressiveInlining)]
597 public static Span<byte> AsBytes<T>(this Span<T> source)
600 if (RuntimeHelpers.IsReferenceOrContainsReferences<T>())
601 ThrowHelper.ThrowInvalidTypeWithPointersNotSupported(typeof(T));
603 return new Span<byte>(
604 ref Unsafe.As<T, byte>(ref MemoryMarshal.GetReference(source)),
605 checked(source.Length * Unsafe.SizeOf<T>()));
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.
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.
616 [MethodImpl(MethodImplOptions.AggressiveInlining)]
617 public static ReadOnlySpan<byte> AsBytes<T>(this ReadOnlySpan<T> source)
620 if (RuntimeHelpers.IsReferenceOrContainsReferences<T>())
621 ThrowHelper.ThrowInvalidTypeWithPointersNotSupported(typeof(T));
623 return new ReadOnlySpan<byte>(
624 ref Unsafe.As<T, byte>(ref MemoryMarshal.GetReference(source)),
625 checked(source.Length * Unsafe.SizeOf<T>()));
629 /// Creates a new readonly span over the portion of the target string.
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)
640 return new ReadOnlySpan<char>(ref text.GetRawStringData(), text.Length);
644 /// Creates a new readonly span over the portion of the target string.
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 (<0 or >text.Length).
651 [MethodImpl(MethodImplOptions.AggressiveInlining)]
652 public static ReadOnlySpan<char> AsSpan(this string text, int start)
657 ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.start);
661 if ((uint)start > (uint)text.Length)
662 ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.start);
664 return new ReadOnlySpan<char>(ref Unsafe.Add(ref text.GetRawStringData(), start), text.Length - start);
668 /// Creates a new readonly span over the portion of the target string.
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.
675 [MethodImpl(MethodImplOptions.AggressiveInlining)]
676 public static ReadOnlySpan<char> AsSpan(this string text, int start, int length)
680 if (start != 0 || length != 0)
681 ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.start);
685 if ((uint)start > (uint)text.Length || (uint)length > (uint)(text.Length - start))
686 ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.start);
688 return new ReadOnlySpan<char>(ref Unsafe.Add(ref text.GetRawStringData(), start), length);
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);
696 internal static unsafe void ClearWithoutReferences(ref byte b, nuint byteLength)
701 #if CORECLR && (AMD64 || ARM64)
702 if (byteLength > 4096)
704 Unsafe.InitBlockUnaligned(ref b, 0, (uint)byteLength);
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.
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.
720 Unsafe.As<byte, short>(ref b) = 0;
723 Unsafe.As<byte, short>(ref b) = 0;
724 Unsafe.Add<byte>(ref b, 2) = 0;
727 Unsafe.As<byte, int>(ref b) = 0;
730 Unsafe.As<byte, int>(ref b) = 0;
731 Unsafe.Add<byte>(ref b, 4) = 0;
734 Unsafe.As<byte, int>(ref b) = 0;
735 Unsafe.As<byte, short>(ref Unsafe.Add<byte>(ref b, 4)) = 0;
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;
744 Unsafe.As<byte, long>(ref b) = 0;
746 Unsafe.As<byte, int>(ref b) = 0;
747 Unsafe.As<byte, int>(ref Unsafe.Add<byte>(ref b, 4)) = 0;
752 Unsafe.As<byte, long>(ref b) = 0;
754 Unsafe.As<byte, int>(ref b) = 0;
755 Unsafe.As<byte, int>(ref Unsafe.Add<byte>(ref b, 4)) = 0;
757 Unsafe.Add<byte>(ref b, 8) = 0;
761 Unsafe.As<byte, long>(ref b) = 0;
763 Unsafe.As<byte, int>(ref b) = 0;
764 Unsafe.As<byte, int>(ref Unsafe.Add<byte>(ref b, 4)) = 0;
766 Unsafe.As<byte, short>(ref Unsafe.Add<byte>(ref b, 8)) = 0;
770 Unsafe.As<byte, long>(ref b) = 0;
772 Unsafe.As<byte, int>(ref b) = 0;
773 Unsafe.As<byte, int>(ref Unsafe.Add<byte>(ref b, 4)) = 0;
775 Unsafe.As<byte, short>(ref Unsafe.Add<byte>(ref b, 8)) = 0;
776 Unsafe.Add<byte>(ref b, 10) = 0;
780 Unsafe.As<byte, long>(ref b) = 0;
782 Unsafe.As<byte, int>(ref b) = 0;
783 Unsafe.As<byte, int>(ref Unsafe.Add<byte>(ref b, 4)) = 0;
785 Unsafe.As<byte, int>(ref Unsafe.Add<byte>(ref b, 8)) = 0;
789 Unsafe.As<byte, long>(ref b) = 0;
791 Unsafe.As<byte, int>(ref b) = 0;
792 Unsafe.As<byte, int>(ref Unsafe.Add<byte>(ref b, 4)) = 0;
794 Unsafe.As<byte, int>(ref Unsafe.Add<byte>(ref b, 8)) = 0;
795 Unsafe.Add<byte>(ref b, 12) = 0;
799 Unsafe.As<byte, long>(ref b) = 0;
801 Unsafe.As<byte, int>(ref b) = 0;
802 Unsafe.As<byte, int>(ref Unsafe.Add<byte>(ref b, 4)) = 0;
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;
809 Unsafe.As<byte, long>(ref b) = 0;
811 Unsafe.As<byte, int>(ref b) = 0;
812 Unsafe.As<byte, int>(ref Unsafe.Add<byte>(ref b, 4)) = 0;
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;
820 Unsafe.As<byte, long>(ref b) = 0;
821 Unsafe.As<byte, long>(ref Unsafe.Add<byte>(ref b, 8)) = 0;
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;
831 Unsafe.As<byte, long>(ref b) = 0;
832 Unsafe.As<byte, long>(ref Unsafe.Add<byte>(ref b, 8)) = 0;
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;
839 Unsafe.Add<byte>(ref b, 16) = 0;
843 Unsafe.As<byte, long>(ref b) = 0;
844 Unsafe.As<byte, long>(ref Unsafe.Add<byte>(ref b, 8)) = 0;
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;
851 Unsafe.As<byte, short>(ref Unsafe.Add<byte>(ref b, 16)) = 0;
855 Unsafe.As<byte, long>(ref b) = 0;
856 Unsafe.As<byte, long>(ref Unsafe.Add<byte>(ref b, 8)) = 0;
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;
863 Unsafe.As<byte, short>(ref Unsafe.Add<byte>(ref b, 16)) = 0;
864 Unsafe.Add<byte>(ref b, 18) = 0;
868 Unsafe.As<byte, long>(ref b) = 0;
869 Unsafe.As<byte, long>(ref Unsafe.Add<byte>(ref b, 8)) = 0;
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;
876 Unsafe.As<byte, int>(ref Unsafe.Add<byte>(ref b, 16)) = 0;
880 Unsafe.As<byte, long>(ref b) = 0;
881 Unsafe.As<byte, long>(ref Unsafe.Add<byte>(ref b, 8)) = 0;
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;
888 Unsafe.As<byte, int>(ref Unsafe.Add<byte>(ref b, 16)) = 0;
889 Unsafe.Add<byte>(ref b, 20) = 0;
893 Unsafe.As<byte, long>(ref b) = 0;
894 Unsafe.As<byte, long>(ref Unsafe.Add<byte>(ref b, 8)) = 0;
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;
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;
906 // P/Invoke into the native version for large lengths
907 if (byteLength >= 512) goto PInvoke;
909 nuint i = 0; // byte offset at which we're copying
911 if ((Unsafe.As<byte, int>(ref b) & 3) != 0)
913 if ((Unsafe.As<byte, int>(ref b) & 1) != 0)
915 Unsafe.AddByteOffset<byte>(ref b, i) = 0;
917 if ((Unsafe.As<byte, int>(ref b) & 2) != 0)
920 Unsafe.As<byte, short>(ref Unsafe.AddByteOffset<byte>(ref b, i)) = 0;
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
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.
934 if (((Unsafe.As<byte, int>(ref b) - 1) & 4) == 0)
936 Unsafe.As<byte, int>(ref Unsafe.AddByteOffset<byte>(ref b, i)) = 0;
940 nuint end = byteLength - 16;
941 byteLength -= i; // lower 4 bits of byteLength represent how many bytes are left *after* the unrolled loop
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);
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.
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.
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.
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;
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;
976 // See notes above for why this wasn't used instead
979 while (counter <= end);
981 if ((byteLength & 8) != 0)
984 Unsafe.As<byte, long>(ref Unsafe.AddByteOffset<byte>(ref b, i)) = 0;
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;
991 if ((byteLength & 4) != 0)
993 Unsafe.As<byte, int>(ref Unsafe.AddByteOffset<byte>(ref b, i)) = 0;
996 if ((byteLength & 2) != 0)
998 Unsafe.As<byte, short>(ref Unsafe.AddByteOffset<byte>(ref b, i)) = 0;
1001 if ((byteLength & 1) != 0)
1003 Unsafe.AddByteOffset<byte>(ref b, i) = 0;
1004 // We're not using i after this, so not needed
1012 RuntimeImports.RhZeroMemory(ref b, byteLength);
1015 internal static unsafe void ClearWithReferences(ref IntPtr ip, nuint pointerSizeLength)
1017 if (pointerSizeLength == 0)
1020 // TODO: Perhaps do switch casing to improve small size perf
1024 while ((n = i + 8) <= (pointerSizeLength))
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);
1036 if ((n = i + 4) <= (pointerSizeLength))
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);
1044 if ((n = i + 2) <= (pointerSizeLength))
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);
1050 if ((i + 1) <= (pointerSizeLength))
1052 Unsafe.AddByteOffset<IntPtr>(ref ip, (i + 0) * (nuint)sizeof(IntPtr)) = default(IntPtr);