f6cd939a73e79a59a80cd8c37710287fa944de3d
[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.Runtime;
7 using System.Runtime.CompilerServices;
8 using System.Runtime.InteropServices;
9
10 using Internal.Runtime.CompilerServices;
11
12 #if BIT64
13 using nuint = System.UInt64;
14 #else
15 using nuint = System.UInt32;
16 #endif
17
18 namespace System
19 {
20     /// <summary>
21     /// Extension methods and non-generic helpers for Span, ReadOnlySpan, Memory, and ReadOnlyMemory.
22     /// </summary>
23     public static class Span
24     {
25         /// <summary>Creates a new <see cref="ReadOnlyMemory{char}"/> over the portion of the target string.</summary>
26         /// <param name="text">The target string.</param>
27         /// <exception cref="System.ArgumentNullException">Thrown when <paramref name="text"/> is a null reference (Nothing in Visual Basic).</exception>
28         public static ReadOnlyMemory<char> AsReadOnlyMemory(this string text)
29         {
30             if (text == null)
31                 ThrowHelper.ThrowArgumentNullException(ExceptionArgument.text);
32
33             return new ReadOnlyMemory<char>(text, 0, text.Length);
34         }
35
36         /// <summary>Creates a new <see cref="ReadOnlyMemory{char}"/> over the portion of the target string.</summary>
37         /// <param name="text">The target string.</param>
38         /// <exception cref="System.ArgumentNullException">Thrown when <paramref name="text"/> is a null reference (Nothing in Visual Basic).</exception>
39         /// <exception cref="System.ArgumentOutOfRangeException">
40         /// Thrown when the specified <paramref name="start"/> index is not in range (&lt;0 or &gt;text.Length).
41         /// </exception>
42         public static ReadOnlyMemory<char> AsReadOnlyMemory(this string text, int start)
43         {
44             if (text == null)
45                 ThrowHelper.ThrowArgumentNullException(ExceptionArgument.text);
46
47             if ((uint)start > (uint)text.Length)
48                 ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.start);
49
50             return new ReadOnlyMemory<char>(text, start, text.Length - start);
51         }
52
53         /// <summary>Creates a new <see cref="ReadOnlyMemory{char}"/> over the portion of the target string.</summary>
54         /// <param name="text">The target string.</param>
55         /// <exception cref="System.ArgumentNullException">Thrown when <paramref name="text"/> is a null reference (Nothing in Visual Basic).</exception>
56         /// <exception cref="System.ArgumentOutOfRangeException">
57         /// Thrown when the specified <paramref name="start"/> index or <paramref name="length"/> is not in range.
58         /// </exception>
59         public static ReadOnlyMemory<char> AsReadOnlyMemory(this string text, int start, int length)
60         {
61             if (text == null)
62                 ThrowHelper.ThrowArgumentNullException(ExceptionArgument.text);
63
64             if ((uint)start > (uint)text.Length || (uint)length > (uint)(text.Length - start))
65                 ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.start);
66
67             return new ReadOnlyMemory<char>(text, start, length);
68         }
69
70         /// <summary>Attempts to get the underlying <see cref="string"/> from a <see cref="ReadOnlyMemory{T}"/>.</summary>
71         /// <param name="readOnlyMemory">The memory that may be wrapping a <see cref="string"/> object.</param>
72         /// <param name="text">The string.</param>
73         /// <param name="start">The starting location in <paramref name="text"/>.</param>
74         /// <param name="length">The number of items in <paramref name="text"/>.</param>
75         /// <returns></returns>
76         public static bool TryGetString(this ReadOnlyMemory<char> readOnlyMemory, out string text, out int start, out int length)
77         {
78             if (readOnlyMemory.GetObjectStartLength(out int offset, out int count) is string s)
79             {
80                 text = s;
81                 start = offset;
82                 length = count;
83                 return true;
84             }
85             else
86             {
87                 text = null;
88                 start = 0;
89                 length = 0;
90                 return false;
91             }
92         }
93
94         /// <summary>
95         /// Casts a Span of one primitive type <typeparamref name="T"/> to Span of bytes.
96         /// That type may not contain pointers or references. This is checked at runtime in order to preserve type safety.
97         /// </summary>
98         /// <param name="source">The source slice, of type <typeparamref name="T"/>.</param>
99         /// <exception cref="System.ArgumentException">
100         /// Thrown when <typeparamref name="T"/> contains pointers.
101         /// </exception>
102         [MethodImpl(MethodImplOptions.AggressiveInlining)]
103         public static Span<byte> AsBytes<T>(this Span<T> source)
104             where T : struct
105         {
106             if (RuntimeHelpers.IsReferenceOrContainsReferences<T>())
107                 ThrowHelper.ThrowInvalidTypeWithPointersNotSupported(typeof(T));
108
109             return new Span<byte>(
110                 ref Unsafe.As<T, byte>(ref MemoryMarshal.GetReference(source)),
111                 checked(source.Length * Unsafe.SizeOf<T>()));
112         }
113
114         /// <summary>
115         /// Casts a ReadOnlySpan of one primitive type <typeparamref name="T"/> to ReadOnlySpan of bytes.
116         /// That type may not contain pointers or references. This is checked at runtime in order to preserve type safety.
117         /// </summary>
118         /// <param name="source">The source slice, of type <typeparamref name="T"/>.</param>
119         /// <exception cref="System.ArgumentException">
120         /// Thrown when <typeparamref name="T"/> contains pointers.
121         /// </exception>
122         [MethodImpl(MethodImplOptions.AggressiveInlining)]
123         public static ReadOnlySpan<byte> AsBytes<T>(this ReadOnlySpan<T> source)
124             where T : struct
125         {
126             if (RuntimeHelpers.IsReferenceOrContainsReferences<T>())
127                 ThrowHelper.ThrowInvalidTypeWithPointersNotSupported(typeof(T));
128
129             return new ReadOnlySpan<byte>(
130                 ref Unsafe.As<T, byte>(ref MemoryMarshal.GetReference(source)),
131                 checked(source.Length * Unsafe.SizeOf<T>()));
132         }
133
134         /// <summary>
135         /// Casts a Span of one primitive type <typeparamref name="TFrom"/> to another primitive type <typeparamref name="TTo"/>.
136         /// These types may not contain pointers or references. This is checked at runtime in order to preserve type safety.
137         /// </summary>
138         /// <remarks>
139         /// Supported only for platforms that support misaligned memory access.
140         /// </remarks>
141         /// <param name="source">The source slice, of type <typeparamref name="TFrom"/>.</param>
142         /// <exception cref="System.ArgumentException">
143         /// Thrown when <typeparamref name="TFrom"/> or <typeparamref name="TTo"/> contains pointers.
144         /// </exception>
145         [MethodImpl(MethodImplOptions.AggressiveInlining)]
146         public static Span<TTo> NonPortableCast<TFrom, TTo>(this Span<TFrom> source)
147             where TFrom : struct
148             where TTo : struct
149         {
150             if (RuntimeHelpers.IsReferenceOrContainsReferences<TFrom>())
151                 ThrowHelper.ThrowInvalidTypeWithPointersNotSupported(typeof(TFrom));
152             if (RuntimeHelpers.IsReferenceOrContainsReferences<TTo>())
153                 ThrowHelper.ThrowInvalidTypeWithPointersNotSupported(typeof(TTo));
154
155             return new Span<TTo>(
156                 ref Unsafe.As<TFrom, TTo>(ref source.DangerousGetPinnableReference()),
157                 checked((int)((long)source.Length * Unsafe.SizeOf<TFrom>() / Unsafe.SizeOf<TTo>())));
158         }
159
160         /// <summary>
161         /// Casts a ReadOnlySpan of one primitive type <typeparamref name="TFrom"/> to another primitive type <typeparamref name="TTo"/>.
162         /// These types may not contain pointers or references. This is checked at runtime in order to preserve type safety.
163         /// </summary>
164         /// <remarks>
165         /// Supported only for platforms that support misaligned memory access.
166         /// </remarks>
167         /// <param name="source">The source slice, of type <typeparamref name="TFrom"/>.</param>
168         /// <exception cref="System.ArgumentException">
169         /// Thrown when <typeparamref name="TFrom"/> or <typeparamref name="TTo"/> contains pointers.
170         /// </exception>
171         [MethodImpl(MethodImplOptions.AggressiveInlining)]
172         public static ReadOnlySpan<TTo> NonPortableCast<TFrom, TTo>(this ReadOnlySpan<TFrom> source)
173             where TFrom : struct
174             where TTo : struct
175         {
176             if (RuntimeHelpers.IsReferenceOrContainsReferences<TFrom>())
177                 ThrowHelper.ThrowInvalidTypeWithPointersNotSupported(typeof(TFrom));
178             if (RuntimeHelpers.IsReferenceOrContainsReferences<TTo>())
179                 ThrowHelper.ThrowInvalidTypeWithPointersNotSupported(typeof(TTo));
180
181             return new ReadOnlySpan<TTo>(
182                 ref Unsafe.As<TFrom, TTo>(ref MemoryMarshal.GetReference(source)),
183                 checked((int)((long)source.Length * Unsafe.SizeOf<TFrom>() / Unsafe.SizeOf<TTo>())));
184         }
185
186         /// <summary>
187         /// Creates a new readonly span over the portion of the target string.
188         /// </summary>
189         /// <param name="text">The target string.</param>
190         /// <exception cref="System.ArgumentNullException">Thrown when <paramref name="text"/> is a null
191         /// reference (Nothing in Visual Basic).</exception>
192         [MethodImpl(MethodImplOptions.AggressiveInlining)]
193         public static ReadOnlySpan<char> AsReadOnlySpan(this string text)
194         {
195             if (text == null)
196                 ThrowHelper.ThrowArgumentNullException(ExceptionArgument.text);
197
198             return new ReadOnlySpan<char>(ref text.GetRawStringData(), text.Length);
199         }
200
201         /// <summary>
202         /// Creates a new readonly span over the portion of the target string.
203         /// </summary>
204         /// <param name="text">The target string.</param>
205         /// <exception cref="System.ArgumentNullException">Thrown when <paramref name="text"/> is a null
206         /// reference (Nothing in Visual Basic).
207         /// </exception>
208         /// <exception cref="System.ArgumentOutOfRangeException">
209         /// Thrown when the specified <paramref name="start"/> index is not in range (&lt;0 or &gt;text.Length).
210         /// </exception>
211         [MethodImpl(MethodImplOptions.AggressiveInlining)]
212         public static ReadOnlySpan<char> AsReadOnlySpan(this string text, int start)
213         {
214             if (text == null)
215                 ThrowHelper.ThrowArgumentNullException(ExceptionArgument.text);
216
217             if ((uint)start > (uint)text.Length)
218                 ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.start);
219
220             return new ReadOnlySpan<char>(ref Unsafe.Add(ref text.GetRawStringData(), start), text.Length - start);
221         }
222
223         /// <summary>
224         /// Creates a new readonly span over the portion of the target string.
225         /// </summary>
226         /// <param name="text">The target string.</param>
227         /// <exception cref="System.ArgumentNullException">Thrown when <paramref name="text"/> is a null
228         /// reference (Nothing in Visual Basic).
229         /// </exception>
230         /// <exception cref="System.ArgumentOutOfRangeException">
231         /// Thrown when the specified <paramref name="start"/> index or <paramref name="length"/> is not in range.
232         /// </exception>
233         [MethodImpl(MethodImplOptions.AggressiveInlining)]
234         public static ReadOnlySpan<char> AsReadOnlySpan(this string text, int start, int length)
235         {
236             if (text == null)
237                 ThrowHelper.ThrowArgumentNullException(ExceptionArgument.text);
238
239             if ((uint)start > (uint)text.Length || (uint)length > (uint)(text.Length - start))
240                 ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.start);
241
242             return new ReadOnlySpan<char>(ref Unsafe.Add(ref text.GetRawStringData(), start), length);
243         }
244
245         internal static unsafe void CopyTo<T>(ref T destination, ref T source, int elementsCount)
246         {
247             if (Unsafe.AreSame(ref destination, ref source))
248                 return;
249
250             if (elementsCount <= 1)
251             {
252                 if (elementsCount == 1)
253                 {
254                     destination = source;
255                 }
256                 return;
257             }
258
259             nuint byteCount = (nuint)elementsCount * (nuint)Unsafe.SizeOf<T>();
260             if (!RuntimeHelpers.IsReferenceOrContainsReferences<T>())
261             {
262                 fixed (byte* pDestination = &Unsafe.As<T, byte>(ref destination))
263                 {
264                     fixed (byte* pSource = &Unsafe.As<T, byte>(ref source))
265                     {
266                         Buffer.Memmove(pDestination, pSource, byteCount);
267                     }
268                 }
269             }
270             else
271             {
272                 RuntimeImports.RhBulkMoveWithWriteBarrier(
273                     ref Unsafe.As<T, byte>(ref destination),
274                     ref Unsafe.As<T, byte>(ref source),
275                     byteCount);
276             }
277         }
278
279         internal static unsafe void ClearWithoutReferences(ref byte b, nuint byteLength)
280         {
281             if (byteLength == 0)
282                 return;
283             
284 #if CORECLR && (AMD64 || ARM64)
285             if (byteLength > 4096) goto PInvoke;
286             Unsafe.InitBlockUnaligned(ref b, 0, (uint)byteLength);
287             return;
288 #else
289             // TODO: Optimize other platforms to be on par with AMD64 CoreCLR
290             // Note: It's important that this switch handles lengths at least up to 22.
291             // See notes below near the main loop for why.
292
293             // The switch will be very fast since it can be implemented using a jump
294             // table in assembly. See http://stackoverflow.com/a/449297/4077294 for more info.
295
296             switch (byteLength)
297             {
298                 case 1:
299                     b = 0;
300                     return;
301                 case 2:
302                     Unsafe.As<byte, short>(ref b) = 0;
303                     return;
304                 case 3:
305                     Unsafe.As<byte, short>(ref b) = 0;
306                     Unsafe.Add<byte>(ref b, 2) = 0;
307                     return;
308                 case 4:
309                     Unsafe.As<byte, int>(ref b) = 0;
310                     return;
311                 case 5:
312                     Unsafe.As<byte, int>(ref b) = 0;
313                     Unsafe.Add<byte>(ref b, 4) = 0;
314                     return;
315                 case 6:
316                     Unsafe.As<byte, int>(ref b) = 0;
317                     Unsafe.As<byte, short>(ref Unsafe.Add<byte>(ref b, 4)) = 0;
318                     return;
319                 case 7:
320                     Unsafe.As<byte, int>(ref b) = 0;
321                     Unsafe.As<byte, short>(ref Unsafe.Add<byte>(ref b, 4)) = 0;
322                     Unsafe.Add<byte>(ref b, 6) = 0;
323                     return;
324                 case 8:
325 #if BIT64
326                     Unsafe.As<byte, long>(ref b) = 0;
327 #else
328                     Unsafe.As<byte, int>(ref b) = 0;
329                     Unsafe.As<byte, int>(ref Unsafe.Add<byte>(ref b, 4)) = 0;
330 #endif
331                     return;
332                 case 9:
333 #if BIT64
334                     Unsafe.As<byte, long>(ref b) = 0;
335 #else
336                     Unsafe.As<byte, int>(ref b) = 0;
337                     Unsafe.As<byte, int>(ref Unsafe.Add<byte>(ref b, 4)) = 0;
338 #endif
339                     Unsafe.Add<byte>(ref b, 8) = 0;
340                     return;
341                 case 10:
342 #if BIT64
343                     Unsafe.As<byte, long>(ref b) = 0;
344 #else
345                     Unsafe.As<byte, int>(ref b) = 0;
346                     Unsafe.As<byte, int>(ref Unsafe.Add<byte>(ref b, 4)) = 0;
347 #endif
348                     Unsafe.As<byte, short>(ref Unsafe.Add<byte>(ref b, 8)) = 0;
349                     return;
350                 case 11:
351 #if BIT64
352                     Unsafe.As<byte, long>(ref b) = 0;
353 #else
354                     Unsafe.As<byte, int>(ref b) = 0;
355                     Unsafe.As<byte, int>(ref Unsafe.Add<byte>(ref b, 4)) = 0;
356 #endif
357                     Unsafe.As<byte, short>(ref Unsafe.Add<byte>(ref b, 8)) = 0;
358                     Unsafe.Add<byte>(ref b, 10) = 0;
359                     return;
360                 case 12:
361 #if BIT64
362                     Unsafe.As<byte, long>(ref b) = 0;
363 #else
364                     Unsafe.As<byte, int>(ref b) = 0;
365                     Unsafe.As<byte, int>(ref Unsafe.Add<byte>(ref b, 4)) = 0;
366 #endif
367                     Unsafe.As<byte, int>(ref Unsafe.Add<byte>(ref b, 8)) = 0;
368                     return;
369                 case 13:
370 #if BIT64
371                     Unsafe.As<byte, long>(ref b) = 0;
372 #else
373                     Unsafe.As<byte, int>(ref b) = 0;
374                     Unsafe.As<byte, int>(ref Unsafe.Add<byte>(ref b, 4)) = 0;
375 #endif
376                     Unsafe.As<byte, int>(ref Unsafe.Add<byte>(ref b, 8)) = 0;
377                     Unsafe.Add<byte>(ref b, 12) = 0;
378                     return;
379                 case 14:
380 #if BIT64
381                     Unsafe.As<byte, long>(ref b) = 0;
382 #else
383                     Unsafe.As<byte, int>(ref b) = 0;
384                     Unsafe.As<byte, int>(ref Unsafe.Add<byte>(ref b, 4)) = 0;
385 #endif
386                     Unsafe.As<byte, int>(ref Unsafe.Add<byte>(ref b, 8)) = 0;
387                     Unsafe.As<byte, short>(ref Unsafe.Add<byte>(ref b, 12)) = 0;
388                     return;
389                 case 15:
390 #if BIT64
391                     Unsafe.As<byte, long>(ref b) = 0;
392 #else
393                     Unsafe.As<byte, int>(ref b) = 0;
394                     Unsafe.As<byte, int>(ref Unsafe.Add<byte>(ref b, 4)) = 0;
395 #endif
396                     Unsafe.As<byte, int>(ref Unsafe.Add<byte>(ref b, 8)) = 0;
397                     Unsafe.As<byte, short>(ref Unsafe.Add<byte>(ref b, 12)) = 0;
398                     Unsafe.Add<byte>(ref b, 14) = 0;
399                     return;
400                 case 16:
401 #if BIT64
402                     Unsafe.As<byte, long>(ref b) = 0;
403                     Unsafe.As<byte, long>(ref Unsafe.Add<byte>(ref b, 8)) = 0;
404 #else
405                     Unsafe.As<byte, int>(ref b) = 0;
406                     Unsafe.As<byte, int>(ref Unsafe.Add<byte>(ref b, 4)) = 0;
407                     Unsafe.As<byte, int>(ref Unsafe.Add<byte>(ref b, 8)) = 0;
408                     Unsafe.As<byte, int>(ref Unsafe.Add<byte>(ref b, 12)) = 0;
409 #endif
410                     return;
411                 case 17:
412 #if BIT64
413                     Unsafe.As<byte, long>(ref b) = 0;
414                     Unsafe.As<byte, long>(ref Unsafe.Add<byte>(ref b, 8)) = 0;
415 #else
416                     Unsafe.As<byte, int>(ref b) = 0;
417                     Unsafe.As<byte, int>(ref Unsafe.Add<byte>(ref b, 4)) = 0;
418                     Unsafe.As<byte, int>(ref Unsafe.Add<byte>(ref b, 8)) = 0;
419                     Unsafe.As<byte, int>(ref Unsafe.Add<byte>(ref b, 12)) = 0;
420 #endif
421                     Unsafe.Add<byte>(ref b, 16) = 0;
422                     return;
423                 case 18:
424 #if BIT64
425                     Unsafe.As<byte, long>(ref b) = 0;
426                     Unsafe.As<byte, long>(ref Unsafe.Add<byte>(ref b, 8)) = 0;
427 #else
428                     Unsafe.As<byte, int>(ref b) = 0;
429                     Unsafe.As<byte, int>(ref Unsafe.Add<byte>(ref b, 4)) = 0;
430                     Unsafe.As<byte, int>(ref Unsafe.Add<byte>(ref b, 8)) = 0;
431                     Unsafe.As<byte, int>(ref Unsafe.Add<byte>(ref b, 12)) = 0;
432 #endif
433                     Unsafe.As<byte, short>(ref Unsafe.Add<byte>(ref b, 16)) = 0;
434                     return;
435                 case 19:
436 #if BIT64
437                     Unsafe.As<byte, long>(ref b) = 0;
438                     Unsafe.As<byte, long>(ref Unsafe.Add<byte>(ref b, 8)) = 0;
439 #else
440                     Unsafe.As<byte, int>(ref b) = 0;
441                     Unsafe.As<byte, int>(ref Unsafe.Add<byte>(ref b, 4)) = 0;
442                     Unsafe.As<byte, int>(ref Unsafe.Add<byte>(ref b, 8)) = 0;
443                     Unsafe.As<byte, int>(ref Unsafe.Add<byte>(ref b, 12)) = 0;
444 #endif
445                     Unsafe.As<byte, short>(ref Unsafe.Add<byte>(ref b, 16)) = 0;
446                     Unsafe.Add<byte>(ref b, 18) = 0;
447                     return;
448                 case 20:
449 #if BIT64
450                     Unsafe.As<byte, long>(ref b) = 0;
451                     Unsafe.As<byte, long>(ref Unsafe.Add<byte>(ref b, 8)) = 0;
452 #else
453                     Unsafe.As<byte, int>(ref b) = 0;
454                     Unsafe.As<byte, int>(ref Unsafe.Add<byte>(ref b, 4)) = 0;
455                     Unsafe.As<byte, int>(ref Unsafe.Add<byte>(ref b, 8)) = 0;
456                     Unsafe.As<byte, int>(ref Unsafe.Add<byte>(ref b, 12)) = 0;
457 #endif
458                     Unsafe.As<byte, int>(ref Unsafe.Add<byte>(ref b, 16)) = 0;
459                     return;
460                 case 21:
461 #if BIT64
462                     Unsafe.As<byte, long>(ref b) = 0;
463                     Unsafe.As<byte, long>(ref Unsafe.Add<byte>(ref b, 8)) = 0;
464 #else
465                     Unsafe.As<byte, int>(ref b) = 0;
466                     Unsafe.As<byte, int>(ref Unsafe.Add<byte>(ref b, 4)) = 0;
467                     Unsafe.As<byte, int>(ref Unsafe.Add<byte>(ref b, 8)) = 0;
468                     Unsafe.As<byte, int>(ref Unsafe.Add<byte>(ref b, 12)) = 0;
469 #endif
470                     Unsafe.As<byte, int>(ref Unsafe.Add<byte>(ref b, 16)) = 0;
471                     Unsafe.Add<byte>(ref b, 20) = 0;
472                     return;
473                 case 22:
474 #if BIT64
475                     Unsafe.As<byte, long>(ref b) = 0;
476                     Unsafe.As<byte, long>(ref Unsafe.Add<byte>(ref b, 8)) = 0;
477 #else
478                     Unsafe.As<byte, int>(ref b) = 0;
479                     Unsafe.As<byte, int>(ref Unsafe.Add<byte>(ref b, 4)) = 0;
480                     Unsafe.As<byte, int>(ref Unsafe.Add<byte>(ref b, 8)) = 0;
481                     Unsafe.As<byte, int>(ref Unsafe.Add<byte>(ref b, 12)) = 0;
482 #endif
483                     Unsafe.As<byte, int>(ref Unsafe.Add<byte>(ref b, 16)) = 0;
484                     Unsafe.As<byte, short>(ref Unsafe.Add<byte>(ref b, 20)) = 0;
485                     return;
486             }
487
488             // P/Invoke into the native version for large lengths
489             if (byteLength >= 512) goto PInvoke;
490
491             nuint i = 0; // byte offset at which we're copying
492
493             if ((Unsafe.As<byte, int>(ref b) & 3) != 0)
494             {
495                 if ((Unsafe.As<byte, int>(ref b) & 1) != 0)
496                 {
497                     Unsafe.AddByteOffset<byte>(ref b, i) = 0;
498                     i += 1;
499                     if ((Unsafe.As<byte, int>(ref b) & 2) != 0)
500                         goto IntAligned;
501                 }
502                 Unsafe.As<byte, short>(ref Unsafe.AddByteOffset<byte>(ref b, i)) = 0;
503                 i += 2;
504             }
505
506             IntAligned:
507
508             // On 64-bit IntPtr.Size == 8, so we want to advance to the next 8-aligned address. If
509             // (int)b % 8 is 0, 5, 6, or 7, we will already have advanced by 0, 3, 2, or 1
510             // bytes to the next aligned address (respectively), so do nothing. On the other hand,
511             // if it is 1, 2, 3, or 4 we will want to copy-and-advance another 4 bytes until
512             // we're aligned.
513             // The thing 1, 2, 3, and 4 have in common that the others don't is that if you
514             // subtract one from them, their 3rd lsb will not be set. Hence, the below check.
515
516             if (((Unsafe.As<byte, int>(ref b) - 1) & 4) == 0)
517             {
518                 Unsafe.As<byte, int>(ref Unsafe.AddByteOffset<byte>(ref b, i)) = 0;
519                 i += 4;
520             }
521
522             nuint end = byteLength - 16;
523             byteLength -= i; // lower 4 bits of byteLength represent how many bytes are left *after* the unrolled loop
524
525             // We know due to the above switch-case that this loop will always run 1 iteration; max
526             // bytes we clear before checking is 23 (7 to align the pointers, 16 for 1 iteration) so
527             // the switch handles lengths 0-22.
528             Debug.Assert(end >= 7 && i <= end);
529
530             // This is separated out into a different variable, so the i + 16 addition can be
531             // performed at the start of the pipeline and the loop condition does not have
532             // a dependency on the writes.
533             nuint counter;
534
535             do
536             {
537                 counter = i + 16;
538
539                 // This loop looks very costly since there appear to be a bunch of temporary values
540                 // being created with the adds, but the jit (for x86 anyways) will convert each of
541                 // these to use memory addressing operands.
542
543                 // So the only cost is a bit of code size, which is made up for by the fact that
544                 // we save on writes to b.
545
546 #if BIT64
547                 Unsafe.As<byte, long>(ref Unsafe.AddByteOffset<byte>(ref b, i)) = 0;
548                 Unsafe.As<byte, long>(ref Unsafe.AddByteOffset<byte>(ref b, i + 8)) = 0;
549 #else
550                 Unsafe.As<byte, int>(ref Unsafe.AddByteOffset<byte>(ref b, i)) = 0;
551                 Unsafe.As<byte, int>(ref Unsafe.AddByteOffset<byte>(ref b, i + 4)) = 0;
552                 Unsafe.As<byte, int>(ref Unsafe.AddByteOffset<byte>(ref b, i + 8)) = 0;
553                 Unsafe.As<byte, int>(ref Unsafe.AddByteOffset<byte>(ref b, i + 12)) = 0;
554 #endif
555
556                 i = counter;
557
558                 // See notes above for why this wasn't used instead
559                 // i += 16;
560             }
561             while (counter <= end);
562
563             if ((byteLength & 8) != 0)
564             {
565 #if BIT64
566                 Unsafe.As<byte, long>(ref Unsafe.AddByteOffset<byte>(ref b, i)) = 0;
567 #else
568                 Unsafe.As<byte, int>(ref Unsafe.AddByteOffset<byte>(ref b, i)) = 0;
569                 Unsafe.As<byte, int>(ref Unsafe.AddByteOffset<byte>(ref b, i + 4)) = 0;
570 #endif
571                 i += 8;
572             }
573             if ((byteLength & 4) != 0)
574             {
575                 Unsafe.As<byte, int>(ref Unsafe.AddByteOffset<byte>(ref b, i)) = 0;
576                 i += 4;
577             }
578             if ((byteLength & 2) != 0)
579             {
580                 Unsafe.As<byte, short>(ref Unsafe.AddByteOffset<byte>(ref b, i)) = 0;
581                 i += 2;
582             }
583             if ((byteLength & 1) != 0)
584             {
585                 Unsafe.AddByteOffset<byte>(ref b, i) = 0;
586                 // We're not using i after this, so not needed
587                 // i += 1;
588             }
589
590             return;
591 #endif
592             
593             PInvoke:
594             RuntimeImports.RhZeroMemory(ref b, byteLength);
595         }
596
597         internal static unsafe void ClearWithReferences(ref IntPtr ip, nuint pointerSizeLength)
598         {
599             if (pointerSizeLength == 0)
600                 return;
601
602             // TODO: Perhaps do switch casing to improve small size perf
603
604             nuint i = 0;
605             nuint n = 0;
606             while ((n = i + 8) <= (pointerSizeLength))
607             {
608                 Unsafe.AddByteOffset<IntPtr>(ref ip, (i + 0) * (nuint)sizeof(IntPtr)) = default(IntPtr);
609                 Unsafe.AddByteOffset<IntPtr>(ref ip, (i + 1) * (nuint)sizeof(IntPtr)) = default(IntPtr);
610                 Unsafe.AddByteOffset<IntPtr>(ref ip, (i + 2) * (nuint)sizeof(IntPtr)) = default(IntPtr);
611                 Unsafe.AddByteOffset<IntPtr>(ref ip, (i + 3) * (nuint)sizeof(IntPtr)) = default(IntPtr);
612                 Unsafe.AddByteOffset<IntPtr>(ref ip, (i + 4) * (nuint)sizeof(IntPtr)) = default(IntPtr);
613                 Unsafe.AddByteOffset<IntPtr>(ref ip, (i + 5) * (nuint)sizeof(IntPtr)) = default(IntPtr);
614                 Unsafe.AddByteOffset<IntPtr>(ref ip, (i + 6) * (nuint)sizeof(IntPtr)) = default(IntPtr);
615                 Unsafe.AddByteOffset<IntPtr>(ref ip, (i + 7) * (nuint)sizeof(IntPtr)) = default(IntPtr);
616                 i = n;
617             }
618             if ((n = i + 4) <= (pointerSizeLength))
619             {
620                 Unsafe.AddByteOffset<IntPtr>(ref ip, (i + 0) * (nuint)sizeof(IntPtr)) = default(IntPtr);
621                 Unsafe.AddByteOffset<IntPtr>(ref ip, (i + 1) * (nuint)sizeof(IntPtr)) = default(IntPtr);
622                 Unsafe.AddByteOffset<IntPtr>(ref ip, (i + 2) * (nuint)sizeof(IntPtr)) = default(IntPtr);
623                 Unsafe.AddByteOffset<IntPtr>(ref ip, (i + 3) * (nuint)sizeof(IntPtr)) = default(IntPtr);
624                 i = n;
625             }
626             if ((n = i + 2) <= (pointerSizeLength))
627             {
628                 Unsafe.AddByteOffset<IntPtr>(ref ip, (i + 0) * (nuint)sizeof(IntPtr)) = default(IntPtr);
629                 Unsafe.AddByteOffset<IntPtr>(ref ip, (i + 1) * (nuint)sizeof(IntPtr)) = default(IntPtr);
630                 i = n;
631             }
632             if ((i + 1) <= (pointerSizeLength))
633             {
634                 Unsafe.AddByteOffset<IntPtr>(ref ip, (i + 0) * (nuint)sizeof(IntPtr)) = default(IntPtr);
635             }
636         }
637     }
638 }