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}