A game about forced loneliness, made by TACStudios
1using System; 2using System.Diagnostics; 3using Unity.Mathematics; 4 5namespace Unity.Collections 6{ 7 [GenerateTestsForBurstCompatibility] 8 internal unsafe struct Bitwise 9 { 10 internal static int AlignDown(int value, int alignPow2) 11 { 12 return value & ~(alignPow2 - 1); 13 } 14 15 internal static int AlignUp(int value, int alignPow2) 16 { 17 return AlignDown(value + alignPow2 - 1, alignPow2); 18 } 19 20 internal static int FromBool(bool value) 21 { 22 return value ? 1 : 0; 23 } 24 25 // 32-bit uint 26 27 internal static uint ExtractBits(uint input, int pos, uint mask) 28 { 29 var tmp0 = input >> pos; 30 return tmp0 & mask; 31 } 32 33 internal static uint ReplaceBits(uint input, int pos, uint mask, uint value) 34 { 35 var tmp0 = (value & mask) << pos; 36 var tmp1 = input & ~(mask << pos); 37 return tmp0 | tmp1; 38 } 39 40 internal static uint SetBits(uint input, int pos, uint mask, bool value) 41 { 42 return ReplaceBits(input, pos, mask, (uint)-FromBool(value)); 43 } 44 45 // 64-bit ulong 46 47 internal static ulong ExtractBits(ulong input, int pos, ulong mask) 48 { 49 var tmp0 = input >> pos; 50 return tmp0 & mask; 51 } 52 53 internal static ulong ReplaceBits(ulong input, int pos, ulong mask, ulong value) 54 { 55 var tmp0 = (value & mask) << pos; 56 var tmp1 = input & ~(mask << pos); 57 return tmp0 | tmp1; 58 } 59 60 internal static ulong SetBits(ulong input, int pos, ulong mask, bool value) 61 { 62 return ReplaceBits(input, pos, mask, (ulong)-(long)FromBool(value)); 63 } 64 65 internal static int lzcnt(byte value) 66 { 67 return math.lzcnt((uint)value) - 24; 68 } 69 70 internal static int tzcnt(byte value) 71 { 72 return math.min(8, math.tzcnt((uint)value)); 73 } 74 75 internal static int lzcnt(ushort value) 76 { 77 return math.lzcnt((uint)value) - 16; 78 } 79 80 internal static int tzcnt(ushort value) 81 { 82 return math.min(16, math.tzcnt((uint)value)); 83 } 84 85 static int FindUlong(ulong* ptr, int beginBit, int endBit, int numBits) 86 { 87 var bits = ptr; 88 var numSteps = (numBits + 63) >> 6; 89 var numBitsPerStep = 64; 90 var maxBits = numSteps * numBitsPerStep; 91 92 for (int i = beginBit / numBitsPerStep, end = AlignUp(endBit, numBitsPerStep) / numBitsPerStep; i < end; ++i) 93 { 94 if (bits[i] != 0) 95 { 96 continue; 97 } 98 99 var idx = i * numBitsPerStep; 100 var num = math.min(idx + numBitsPerStep, endBit) - idx; 101 102 if (idx != beginBit) 103 { 104 var test = bits[idx / numBitsPerStep - 1]; 105 var newIdx = math.max(idx - math.lzcnt(test), beginBit); 106 107 num += idx - newIdx; 108 idx = newIdx; 109 } 110 111 for (++i; i < end; ++i) 112 { 113 if (num >= numBits) 114 { 115 return idx; 116 } 117 118 var test = bits[i]; 119 var pos = i * numBitsPerStep; 120 num += math.min(pos + math.tzcnt(test), endBit) - pos; 121 122 if (test != 0) 123 { 124 break; 125 } 126 } 127 128 if (num >= numBits) 129 { 130 return idx; 131 } 132 } 133 134 return endBit; 135 } 136 137 static int FindUint(ulong* ptr, int beginBit, int endBit, int numBits) 138 { 139 var bits = (uint*)ptr; 140 var numSteps = (numBits + 31) >> 5; 141 var numBitsPerStep = 32; 142 var maxBits = numSteps * numBitsPerStep; 143 144 for (int i = beginBit / numBitsPerStep, end = AlignUp(endBit, numBitsPerStep) / numBitsPerStep; i < end; ++i) 145 { 146 if (bits[i] != 0) 147 { 148 continue; 149 } 150 151 var idx = i * numBitsPerStep; 152 var num = math.min(idx + numBitsPerStep, endBit) - idx; 153 154 if (idx != beginBit) 155 { 156 var test = bits[idx / numBitsPerStep - 1]; 157 var newIdx = math.max(idx - math.lzcnt(test), beginBit); 158 159 num += idx - newIdx; 160 idx = newIdx; 161 } 162 163 for (++i; i < end; ++i) 164 { 165 if (num >= numBits) 166 { 167 return idx; 168 } 169 170 var test = bits[i]; 171 var pos = i * numBitsPerStep; 172 num += math.min(pos + math.tzcnt(test), endBit) - pos; 173 174 if (test != 0) 175 { 176 break; 177 } 178 } 179 180 if (num >= numBits) 181 { 182 return idx; 183 } 184 } 185 186 return endBit; 187 } 188 189 static int FindUshort(ulong* ptr, int beginBit, int endBit, int numBits) 190 { 191 var bits = (ushort*)ptr; 192 var numSteps = (numBits + 15) >> 4; 193 var numBitsPerStep = 16; 194 var maxBits = numSteps * numBitsPerStep; 195 196 for (int i = beginBit / numBitsPerStep, end = AlignUp(endBit, numBitsPerStep) / numBitsPerStep; i < end; ++i) 197 { 198 if (bits[i] != 0) 199 { 200 continue; 201 } 202 203 var idx = i * numBitsPerStep; 204 var num = math.min(idx + numBitsPerStep, endBit) - idx; 205 206 if (idx != beginBit) 207 { 208 var test = bits[idx / numBitsPerStep - 1]; 209 var newIdx = math.max(idx - lzcnt(test), beginBit); 210 211 num += idx - newIdx; 212 idx = newIdx; 213 } 214 215 for (++i; i < end; ++i) 216 { 217 if (num >= numBits) 218 { 219 return idx; 220 } 221 222 var test = bits[i]; 223 var pos = i * numBitsPerStep; 224 num += math.min(pos + tzcnt(test), endBit) - pos; 225 226 if (test != 0) 227 { 228 break; 229 } 230 } 231 232 if (num >= numBits) 233 { 234 return idx; 235 } 236 } 237 238 return endBit; 239 } 240 241 static int FindByte(ulong* ptr, int beginBit, int endBit, int numBits) 242 { 243 var bits = (byte*)ptr; 244 var numSteps = (numBits + 7) >> 3; 245 var numBitsPerStep = 8; 246 var maxBits = numSteps * numBitsPerStep; 247 248 for (int i = beginBit / numBitsPerStep, end = AlignUp(endBit, numBitsPerStep) / numBitsPerStep; i < end; ++i) 249 { 250 if (bits[i] != 0) 251 { 252 continue; 253 } 254 255 var idx = i * numBitsPerStep; 256 var num = math.min(idx + numBitsPerStep, endBit) - idx; 257 258 if (idx != beginBit) 259 { 260 var test = bits[idx / numBitsPerStep - 1]; 261 var newIdx = math.max(idx - lzcnt(test), beginBit); 262 263 num += idx - newIdx; 264 idx = newIdx; 265 } 266 267 for (++i; i < end; ++i) 268 { 269 if (num >= numBits) 270 { 271 return idx; 272 } 273 274 var test = bits[i]; 275 var pos = i * numBitsPerStep; 276 num += math.min(pos + tzcnt(test), endBit) - pos; 277 278 if (test != 0) 279 { 280 break; 281 } 282 } 283 284 if (num >= numBits) 285 { 286 return idx; 287 } 288 } 289 290 return endBit; 291 } 292 293 static int FindUpto14bits(ulong* ptr, int beginBit, int endBit, int numBits) 294 { 295 var bits = (byte*)ptr; 296 297 var bit = (byte)(beginBit & 7); 298 byte beginMask = (byte)~(0xff << bit); 299 300 var lz = 0; 301 for (int begin = beginBit / 8, end = AlignUp(endBit, 8) / 8, i = begin; i < end; ++i) 302 { 303 var test = bits[i]; 304 test |= i == begin ? beginMask : (byte)0; 305 306 if (test == 0xff) 307 { 308 continue; 309 } 310 311 var pos = i * 8; 312 var tz = math.min(pos + tzcnt(test), endBit) - pos; 313 314 if (lz + tz >= numBits) 315 { 316 return pos - lz; 317 } 318 319 lz = lzcnt(test); 320 321 var idx = pos + 8; 322 var newIdx = math.max(idx - lz, beginBit); 323 lz = math.min(idx, endBit) - newIdx; 324 325 if (lz >= numBits) 326 { 327 return newIdx; 328 } 329 } 330 331 return endBit; 332 } 333 334 static int FindUpto6bits(ulong* ptr, int beginBit, int endBit, int numBits) 335 { 336 var bits = (byte*)ptr; 337 338 byte beginMask = (byte)~(0xff << (beginBit & 7)); 339 byte endMask = (byte)~(0xff >> ((8 - (endBit & 7) & 7))); 340 341 var mask = 1 << numBits - 1; 342 343 for (int begin = beginBit / 8, end = AlignUp(endBit, 8) / 8, i = begin; i < end; ++i) 344 { 345 var test = bits[i]; 346 test |= i == begin ? beginMask : (byte)0; 347 test |= i == end - 1 ? endMask : (byte)0; 348 349 if (test == 0xff) 350 { 351 continue; 352 } 353 354 for (int pos = i * 8, posEnd = pos + 7; pos < posEnd; ++pos) 355 { 356 var tz = tzcnt((byte)(test ^ 0xff)); 357 test >>= tz; 358 359 pos += tz; 360 361 if ((test & mask) == 0) 362 { 363 return pos; 364 } 365 366 test >>= 1; 367 } 368 } 369 370 return endBit; 371 } 372 373 internal static int FindWithBeginEnd(ulong* ptr, int beginBit, int endBit, int numBits) 374 { 375 int idx; 376 377 if (numBits >= 127) 378 { 379 idx = FindUlong(ptr, beginBit, endBit, numBits); 380 if (idx != endBit) 381 { 382 return idx; 383 } 384 } 385 386 if (numBits >= 63) 387 { 388 idx = FindUint(ptr, beginBit, endBit, numBits); 389 if (idx != endBit) 390 { 391 return idx; 392 } 393 } 394 395 if (numBits >= 128) 396 { 397 // early out - no smaller step will find this gap 398 return int.MaxValue; 399 } 400 401 if (numBits >= 31) 402 { 403 idx = FindUshort(ptr, beginBit, endBit, numBits); 404 if (idx != endBit) 405 { 406 return idx; 407 } 408 } 409 410 if (numBits >= 64) 411 { 412 // early out - no smaller step will find this gap 413 return int.MaxValue; 414 } 415 416 idx = FindByte(ptr, beginBit, endBit, numBits); 417 if (idx != endBit) 418 { 419 return idx; 420 } 421 422 if (numBits < 15) 423 { 424 idx = FindUpto14bits(ptr, beginBit, endBit, numBits); 425 426 if (idx != endBit) 427 { 428 return idx; 429 } 430 431 if (numBits < 7) 432 { 433 // The worst case scenario when every byte boundary bit is set (pattern 0x81), 434 // and we're looking for 6 or less bits. It will rescan byte-by-byte to find 435 // any inner byte gap. 436 idx = FindUpto6bits(ptr, beginBit, endBit, numBits); 437 438 if (idx != endBit) 439 { 440 return idx; 441 } 442 } 443 } 444 445 return int.MaxValue; 446 } 447 448 internal static int Find(ulong* ptr, int pos, int count, int numBits) => FindWithBeginEnd(ptr, pos, pos + count, numBits); 449 450 internal static bool TestNone(ulong* ptr, int length, int pos, int numBits = 1) 451 { 452 var end = math.min(pos + numBits, length); 453 var idxB = pos >> 6; 454 var shiftB = pos & 0x3f; 455 var idxE = (end - 1) >> 6; 456 var shiftE = end & 0x3f; 457 var maskB = 0xfffffffffffffffful << shiftB; 458 var maskE = 0xfffffffffffffffful >> (64 - shiftE); 459 460 if (idxB == idxE) 461 { 462 var mask = maskB & maskE; 463 return 0ul == (ptr[idxB] & mask); 464 } 465 466 if (0ul != (ptr[idxB] & maskB)) 467 { 468 return false; 469 } 470 471 for (var idx = idxB + 1; idx < idxE; ++idx) 472 { 473 if (0ul != ptr[idx]) 474 { 475 return false; 476 } 477 } 478 479 return 0ul == (ptr[idxE] & maskE); 480 } 481 482 internal static bool TestAny(ulong* ptr, int length, int pos, int numBits = 1) 483 { 484 var end = math.min(pos + numBits, length); 485 var idxB = pos >> 6; 486 var shiftB = pos & 0x3f; 487 var idxE = (end - 1) >> 6; 488 var shiftE = end & 0x3f; 489 var maskB = 0xfffffffffffffffful << shiftB; 490 var maskE = 0xfffffffffffffffful >> (64 - shiftE); 491 492 if (idxB == idxE) 493 { 494 var mask = maskB & maskE; 495 return 0ul != (ptr[idxB] & mask); 496 } 497 498 if (0ul != (ptr[idxB] & maskB)) 499 { 500 return true; 501 } 502 503 for (var idx = idxB + 1; idx < idxE; ++idx) 504 { 505 if (0ul != ptr[idx]) 506 { 507 return true; 508 } 509 } 510 511 return 0ul != (ptr[idxE] & maskE); 512 } 513 514 internal static bool TestAll(ulong* ptr, int length, int pos, int numBits = 1) 515 { 516 var end = math.min(pos + numBits, length); 517 var idxB = pos >> 6; 518 var shiftB = pos & 0x3f; 519 var idxE = (end - 1) >> 6; 520 var shiftE = end & 0x3f; 521 var maskB = 0xfffffffffffffffful << shiftB; 522 var maskE = 0xfffffffffffffffful >> (64 - shiftE); 523 524 if (idxB == idxE) 525 { 526 var mask = maskB & maskE; 527 return mask == (ptr[idxB] & mask); 528 } 529 530 if (maskB != (ptr[idxB] & maskB)) 531 { 532 return false; 533 } 534 535 for (var idx = idxB + 1; idx < idxE; ++idx) 536 { 537 if (0xfffffffffffffffful != ptr[idx]) 538 { 539 return false; 540 } 541 } 542 543 return maskE == (ptr[idxE] & maskE); 544 } 545 546 internal static int CountBits(ulong* ptr, int length, int pos, int numBits = 1) 547 { 548 var end = math.min(pos + numBits, length); 549 var idxB = pos >> 6; 550 var shiftB = pos & 0x3f; 551 var idxE = (end - 1) >> 6; 552 var shiftE = end & 0x3f; 553 var maskB = 0xfffffffffffffffful << shiftB; 554 var maskE = 0xfffffffffffffffful >> (64 - shiftE); 555 556 if (idxB == idxE) 557 { 558 var mask = maskB & maskE; 559 return math.countbits(ptr[idxB] & mask); 560 } 561 562 var count = math.countbits(ptr[idxB] & maskB); 563 564 for (var idx = idxB + 1; idx < idxE; ++idx) 565 { 566 count += math.countbits(ptr[idx]); 567 } 568 569 count += math.countbits(ptr[idxE] & maskE); 570 571 return count; 572 } 573 574 internal static bool IsSet(ulong* ptr, int pos) 575 { 576 var idx = pos >> 6; 577 var shift = pos & 0x3f; 578 var mask = 1ul << shift; 579 return 0ul != (ptr[idx] & mask); 580 } 581 582 internal static ulong GetBits(ulong* ptr, int length, int pos, int numBits = 1) 583 { 584 var idxB = pos >> 6; 585 var shiftB = pos & 0x3f; 586 587 if (shiftB + numBits <= 64) 588 { 589 var mask = 0xfffffffffffffffful >> (64 - numBits); 590 return Bitwise.ExtractBits(ptr[idxB], shiftB, mask); 591 } 592 593 var end = math.min(pos + numBits, length); 594 var idxE = (end - 1) >> 6; 595 var shiftE = end & 0x3f; 596 597 var maskB = 0xfffffffffffffffful >> shiftB; 598 ulong valueB = Bitwise.ExtractBits(ptr[idxB], shiftB, maskB); 599 600 var maskE = 0xfffffffffffffffful >> (64 - shiftE); 601 ulong valueE = Bitwise.ExtractBits(ptr[idxE], 0, maskE); 602 603 return (valueE << (64 - shiftB)) | valueB; 604 } 605 606 } 607 608 /// <summary> 609 /// A 32-bit array of bits. 610 /// </summary> 611 /// <remarks> 612 /// Stack allocated, so it does not require thread safety checks or disposal. 613 /// </remarks> 614 [DebuggerTypeProxy(typeof(BitField32DebugView))] 615 [GenerateTestsForBurstCompatibility] 616 public struct BitField32 617 { 618 /// <summary> 619 /// The 32 bits, stored as a uint. 620 /// </summary> 621 /// <value>The 32 bits, stored as a uint.</value> 622 public uint Value; 623 624 /// <summary> 625 /// Initializes and returns an instance of BitField32. 626 /// </summary> 627 /// <param name="initialValue">Initial value of the bit field. Default is 0.</param> 628 public BitField32(uint initialValue = 0u) 629 { 630 Value = initialValue; 631 } 632 633 /// <summary> 634 /// Clears all the bits to 0. 635 /// </summary> 636 public void Clear() 637 { 638 Value = 0u; 639 } 640 641 /// <summary> 642 /// Sets a single bit to 1 or 0. 643 /// </summary> 644 /// <param name="pos">Position in this bit field to set (must be 0-31).</param> 645 /// <param name="value">If true, sets the bit to 1. If false, sets the bit to 0.</param> 646 /// <exception cref="ArgumentException">Thrown if `pos`is out of range.</exception> 647 public void SetBits(int pos, bool value) 648 { 649 CheckArgs(pos, 1); 650 Value = Bitwise.SetBits(Value, pos, 1, value); 651 } 652 653 /// <summary> 654 /// Sets one or more contiguous bits to 1 or 0. 655 /// </summary> 656 /// <param name="pos">Position in the bit field of the first bit to set (must be 0-31).</param> 657 /// <param name="value">If true, sets the bits to 1. If false, sets the bits to 0.</param> 658 /// <param name="numBits">Number of bits to set (must be 1-32).</param> 659 /// <exception cref="ArgumentException">Thrown if `pos` or `numBits` are out of bounds or if `pos + numBits` exceeds 32.</exception> 660 public void SetBits(int pos, bool value, int numBits) 661 { 662 CheckArgs(pos, numBits); 663 var mask = 0xffffffffu >> (32 - numBits); 664 Value = Bitwise.SetBits(Value, pos, mask, value); 665 } 666 667 /// <summary> 668 /// Returns one or more contiguous bits from the bit field as the lower bits of a uint. 669 /// </summary> 670 /// <param name="pos">Position in the bit field of the first bit to get (must be 0-31).</param> 671 /// <param name="numBits">Number of bits to get (must be 1-32).</param> 672 /// <exception cref="ArgumentException">Thrown if `pos` or `numBits` are out of bounds or if `pos + numBits` exceeds 32.</exception> 673 /// <returns>The requested range of bits from the bit field stored in the least-significant bits of a uint. All other bits of the uint will be 0.</returns> 674 public uint GetBits(int pos, int numBits = 1) 675 { 676 CheckArgs(pos, numBits); 677 var mask = 0xffffffffu >> (32 - numBits); 678 return Bitwise.ExtractBits(Value, pos, mask); 679 } 680 681 /// <summary> 682 /// Returns true if the bit at a position is 1. 683 /// </summary> 684 /// <param name="pos">Position in the bit field (must be 0-31).</param> 685 /// <returns>True if the bit at the position is 1.</returns> 686 public bool IsSet(int pos) 687 { 688 return 0 != GetBits(pos); 689 } 690 691 /// <summary> 692 /// Returns true if none of the bits in a contiguous range are 1. 693 /// </summary> 694 /// <param name="pos">Position in the bit field (must be 0-31).</param> 695 /// <param name="numBits">Number of bits to test (must be 1-32).</param> 696 /// <exception cref="ArgumentException">Thrown if `pos` or `numBits` are out of bounds or if `pos + numBits` exceeds 32.</exception> 697 /// <returns>True if none of the bits in the contiguous range are 1.</returns> 698 public bool TestNone(int pos, int numBits = 1) 699 { 700 return 0u == GetBits(pos, numBits); 701 } 702 703 /// <summary> 704 /// Returns true if any of the bits in a contiguous range are 1. 705 /// </summary> 706 /// <param name="pos">Position in the bit field (must be 0-31).</param> 707 /// <param name="numBits">Number of bits to test (must be 1-32).</param> 708 /// <exception cref="ArgumentException">Thrown if `pos` or `numBits` are out of bounds or if `pos + numBits` exceeds 32.</exception> 709 /// <returns>True if at least one bit in the contiguous range is 1.</returns> 710 public bool TestAny(int pos, int numBits = 1) 711 { 712 return 0u != GetBits(pos, numBits); 713 } 714 715 /// <summary> 716 /// Returns true if all of the bits in a contiguous range are 1. 717 /// </summary> 718 /// <param name="pos">Position in the bit field (must be 0-31).</param> 719 /// <param name="numBits">Number of bits to test (must be 1-32).</param> 720 /// <exception cref="ArgumentException">Thrown if `pos` or `numBits` are out of bounds or if `pos + numBits` exceeds 32.</exception> 721 /// <returns>True if all bits in the contiguous range are 1.</returns> 722 public bool TestAll(int pos, int numBits = 1) 723 { 724 CheckArgs(pos, numBits); 725 var mask = 0xffffffffu >> (32 - numBits); 726 return mask == Bitwise.ExtractBits(Value, pos, mask); 727 } 728 729 /// <summary> 730 /// Returns the number of bits that are 1. 731 /// </summary> 732 /// <returns>The number of bits that are 1.</returns> 733 public int CountBits() 734 { 735 return math.countbits(Value); 736 } 737 738 /// <summary> 739 /// Returns the number of leading zeroes. 740 /// </summary> 741 /// <returns>The number of leading zeros.</returns> 742 public int CountLeadingZeros() 743 { 744 return math.lzcnt(Value); 745 } 746 747 /// <summary> 748 /// Returns the number of trailing zeros. 749 /// </summary> 750 /// <returns>The number of trailing zeros.</returns> 751 public int CountTrailingZeros() 752 { 753 return math.tzcnt(Value); 754 } 755 756 [Conditional("ENABLE_UNITY_COLLECTIONS_CHECKS"), Conditional("UNITY_DOTS_DEBUG")] 757 static void CheckArgs(int pos, int numBits) 758 { 759 if (pos > 31 760 || numBits == 0 761 || numBits > 32 762 || pos + numBits > 32) 763 { 764 throw new ArgumentException($"BitField32 invalid arguments: pos {pos} (must be 0-31), numBits {numBits} (must be 1-32)."); 765 } 766 } 767 } 768 769 sealed class BitField32DebugView 770 { 771 BitField32 BitField; 772 773 public BitField32DebugView(BitField32 bitfield) 774 { 775 BitField = bitfield; 776 } 777 778 public bool[] Bits 779 { 780 get 781 { 782 var array = new bool[32]; 783 for (int i = 0; i < 32; ++i) 784 { 785 array[i] = BitField.IsSet(i); 786 } 787 return array; 788 } 789 } 790 } 791 792 /// <summary> 793 /// A 64-bit array of bits. 794 /// </summary> 795 /// <remarks> 796 /// Stack allocated, so it does not require thread safety checks or disposal. 797 /// </remarks> 798 [DebuggerTypeProxy(typeof(BitField64DebugView))] 799 [GenerateTestsForBurstCompatibility] 800 public struct BitField64 801 { 802 /// <summary> 803 /// The 64 bits, stored as a ulong. 804 /// </summary> 805 /// <value>The 64 bits, stored as a uint.</value> 806 public ulong Value; 807 808 /// <summary> 809 /// Initializes and returns an instance of BitField64. 810 /// </summary> 811 /// <param name="initialValue">Initial value of the bit field. Default is 0.</param> 812 public BitField64(ulong initialValue = 0ul) 813 { 814 Value = initialValue; 815 } 816 817 /// <summary> 818 /// Clears all bits to 0. 819 /// </summary> 820 public void Clear() 821 { 822 Value = 0ul; 823 } 824 /// <summary> 825 /// Sets a single bit to 1 or 0. 826 /// </summary> 827 /// <param name="pos">Position in this bit field to set (must be 0-63).</param> 828 /// <param name="value">If true, sets the bit to 1. If false, sets the bit to 0.</param> 829 /// <exception cref="ArgumentException">Thrown if `pos`is out of range.</exception> 830 public void SetBits(int pos, bool value) 831 { 832 CheckArgs(pos, 1); 833 Value = Bitwise.SetBits(Value, pos, 1, value); 834 } 835 836 837 /// <summary> 838 /// Sets one or more contiguous bits to 1 or 0. 839 /// </summary> 840 /// <param name="pos">Position in the bit field of the first bit to set (must be 0-63).</param> 841 /// <param name="value">If true, sets the bits to 1. If false, sets the bits to 0.</param> 842 /// <param name="numBits">Number of bits to set (must be 1-64).</param> 843 /// <exception cref="ArgumentException">Thrown if `pos` or `numBits` are out of bounds or if `pos + numBits` exceeds 64.</exception> 844 public void SetBits(int pos, bool value, int numBits = 1) 845 { 846 CheckArgs(pos, numBits); 847 var mask = 0xfffffffffffffffful >> (64 - numBits); 848 Value = Bitwise.SetBits(Value, pos, mask, value); 849 } 850 851 /// <summary> 852 /// Returns one or more contiguous bits from the bit field as the lower bits of a ulong. 853 /// </summary> 854 /// <param name="pos">Position in the bit field of the first bit to get (must be 0-63).</param> 855 /// <param name="numBits">Number of bits to get (must be 1-64).</param> 856 /// <exception cref="ArgumentException">Thrown if `pos` or `numBits` are out of bounds or if `pos + numBits` exceeds 64.</exception> 857 /// <returns>The requested range of bits from the bit field stored in the least-significant bits of a ulong. All other bits of the ulong will be 0.</returns> 858 public ulong GetBits(int pos, int numBits = 1) 859 { 860 CheckArgs(pos, numBits); 861 var mask = 0xfffffffffffffffful >> (64 - numBits); 862 return Bitwise.ExtractBits(Value, pos, mask); 863 } 864 865 /// <summary> 866 /// Returns true if the bit at a position is 1. 867 /// </summary> 868 /// <param name="pos">Position in the bit field (must be 0-63).</param> 869 /// <returns>True if the bit at the position is 1.</returns> 870 public bool IsSet(int pos) 871 { 872 return 0ul != GetBits(pos); 873 } 874 875 /// <summary> 876 /// Returns true if none of the bits in a contiguous range are 1. 877 /// </summary> 878 /// <param name="pos">Position in the bit field (must be 0-63).</param> 879 /// <param name="numBits">Number of bits to test (must be 1-64).</param> 880 /// <exception cref="ArgumentException">Thrown if `pos` or `numBits` are out of bounds or if `pos + numBits` exceeds 64.</exception> 881 /// <returns>True if none of the bits in the contiguous range are 1.</returns> 882 public bool TestNone(int pos, int numBits = 1) 883 { 884 return 0ul == GetBits(pos, numBits); 885 } 886 887 /// <summary> 888 /// Returns true if any of the bits in a contiguous range are 1. 889 /// </summary> 890 /// <param name="pos">Position in the bit field (must be 0-63).</param> 891 /// <param name="numBits">Number of bits to test (must be 1-64).</param> 892 /// <exception cref="ArgumentException">Thrown if `pos` or `numBits` are out of bounds or if `pos + numBits` exceeds 64.</exception> 893 /// <returns>True if at least one bit in the contiguous range is 1.</returns> 894 public bool TestAny(int pos, int numBits = 1) 895 { 896 return 0ul != GetBits(pos, numBits); 897 } 898 899 /// <summary> 900 /// Returns true if all of the bits in a contiguous range are 1. 901 /// </summary> 902 /// <param name="pos">Position in the bit field (must be 0-63).</param> 903 /// <param name="numBits">Number of bits to test (must be 1-64).</param> 904 /// <exception cref="ArgumentException">Thrown if `pos` or `numBits` are out of bounds or if `pos + numBits` exceeds 64.</exception> 905 /// <returns>True if all bits in the contiguous range are 1.</returns> 906 public bool TestAll(int pos, int numBits = 1) 907 { 908 CheckArgs(pos, numBits); 909 var mask = 0xfffffffffffffffful >> (64 - numBits); 910 return mask == Bitwise.ExtractBits(Value, pos, mask); 911 } 912 913 /// <summary> 914 /// Returns the number of bits that are 1. 915 /// </summary> 916 /// <returns>The number of bits that are 1.</returns> 917 public int CountBits() 918 { 919 return math.countbits(Value); 920 } 921 922 /// <summary> 923 /// Returns the number of leading zeroes. 924 /// </summary> 925 /// <returns>The number of leading zeros.</returns> 926 public int CountLeadingZeros() 927 { 928 return math.lzcnt(Value); 929 } 930 931 /// <summary> 932 /// Returns the number of trailing zeros. 933 /// </summary> 934 /// <returns>The number of trailing zeros.</returns> 935 public int CountTrailingZeros() 936 { 937 return math.tzcnt(Value); 938 } 939 940 [Conditional("ENABLE_UNITY_COLLECTIONS_CHECKS"), Conditional("UNITY_DOTS_DEBUG")] 941 static void CheckArgs(int pos, int numBits) 942 { 943 if (pos > 63 944 || numBits == 0 945 || numBits > 64 946 || pos + numBits > 64) 947 { 948 throw new ArgumentException($"BitField32 invalid arguments: pos {pos} (must be 0-63), numBits {numBits} (must be 1-64)."); 949 } 950 } 951 } 952 953 sealed class BitField64DebugView 954 { 955 BitField64 Data; 956 957 public BitField64DebugView(BitField64 data) 958 { 959 Data = data; 960 } 961 962 public bool[] Bits 963 { 964 get 965 { 966 var array = new bool[64]; 967 for (int i = 0; i < 64; ++i) 968 { 969 array[i] = Data.IsSet(i); 970 } 971 return array; 972 } 973 } 974 } 975}