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