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