Brak opisu
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

BitField.cs 26KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818
  1. using System;
  2. using System.Diagnostics;
  3. using Unity.Mathematics;
  4. namespace Unity.Collections
  5. {
  6. [BurstCompatible]
  7. internal unsafe struct Bitwise
  8. {
  9. internal static int AlignDown(int value, int alignPow2)
  10. {
  11. return value & ~(alignPow2 - 1);
  12. }
  13. internal static int AlignUp(int value, int alignPow2)
  14. {
  15. return AlignDown(value + alignPow2 - 1, alignPow2);
  16. }
  17. internal static int FromBool(bool value)
  18. {
  19. return value ? 1 : 0;
  20. }
  21. // 32-bit uint
  22. internal static uint ExtractBits(uint input, int pos, uint mask)
  23. {
  24. var tmp0 = input >> pos;
  25. return tmp0 & mask;
  26. }
  27. internal static uint ReplaceBits(uint input, int pos, uint mask, uint value)
  28. {
  29. var tmp0 = (value & mask) << pos;
  30. var tmp1 = input & ~(mask << pos);
  31. return tmp0 | tmp1;
  32. }
  33. internal static uint SetBits(uint input, int pos, uint mask, bool value)
  34. {
  35. return ReplaceBits(input, pos, mask, (uint)-FromBool(value));
  36. }
  37. // 64-bit ulong
  38. internal static ulong ExtractBits(ulong input, int pos, ulong mask)
  39. {
  40. var tmp0 = input >> pos;
  41. return tmp0 & mask;
  42. }
  43. internal static ulong ReplaceBits(ulong input, int pos, ulong mask, ulong value)
  44. {
  45. var tmp0 = (value & mask) << pos;
  46. var tmp1 = input & ~(mask << pos);
  47. return tmp0 | tmp1;
  48. }
  49. internal static ulong SetBits(ulong input, int pos, ulong mask, bool value)
  50. {
  51. return ReplaceBits(input, pos, mask, (ulong)-(long)FromBool(value));
  52. }
  53. internal static int lzcnt(byte value)
  54. {
  55. return math.lzcnt((uint)value) - 24;
  56. }
  57. internal static int tzcnt(byte value)
  58. {
  59. return math.min(8, math.tzcnt((uint)value));
  60. }
  61. internal static int lzcnt(ushort value)
  62. {
  63. return math.lzcnt((uint)value) - 16;
  64. }
  65. internal static int tzcnt(ushort value)
  66. {
  67. return math.min(16, math.tzcnt((uint)value));
  68. }
  69. static int FindUlong(ulong* ptr, int beginBit, int endBit, int numBits)
  70. {
  71. var bits = ptr;
  72. var numSteps = (numBits + 63) >> 6;
  73. var numBitsPerStep = 64;
  74. var maxBits = numSteps * numBitsPerStep;
  75. for (int i = beginBit / numBitsPerStep, end = AlignUp(endBit, numBitsPerStep) / numBitsPerStep; i < end; ++i)
  76. {
  77. if (bits[i] != 0)
  78. {
  79. continue;
  80. }
  81. var idx = i * numBitsPerStep;
  82. var num = math.min(idx + numBitsPerStep, endBit) - idx;
  83. if (idx != beginBit)
  84. {
  85. var test = bits[idx / numBitsPerStep - 1];
  86. var newIdx = math.max(idx - math.lzcnt(test), beginBit);
  87. num += idx - newIdx;
  88. idx = newIdx;
  89. }
  90. for (++i; i < end; ++i)
  91. {
  92. if (num >= numBits)
  93. {
  94. return idx;
  95. }
  96. var test = bits[i];
  97. var pos = i * numBitsPerStep;
  98. num += math.min(pos + math.tzcnt(test), endBit) - pos;
  99. if (test != 0)
  100. {
  101. break;
  102. }
  103. }
  104. if (num >= numBits)
  105. {
  106. return idx;
  107. }
  108. }
  109. return endBit;
  110. }
  111. static int FindUint(ulong* ptr, int beginBit, int endBit, int numBits)
  112. {
  113. var bits = (uint*)ptr;
  114. var numSteps = (numBits + 31) >> 5;
  115. var numBitsPerStep = 32;
  116. var maxBits = numSteps * numBitsPerStep;
  117. for (int i = beginBit / numBitsPerStep, end = AlignUp(endBit, numBitsPerStep) / numBitsPerStep; i < end; ++i)
  118. {
  119. if (bits[i] != 0)
  120. {
  121. continue;
  122. }
  123. var idx = i * numBitsPerStep;
  124. var num = math.min(idx + numBitsPerStep, endBit) - idx;
  125. if (idx != beginBit)
  126. {
  127. var test = bits[idx / numBitsPerStep - 1];
  128. var newIdx = math.max(idx - math.lzcnt(test), beginBit);
  129. num += idx - newIdx;
  130. idx = newIdx;
  131. }
  132. for (++i; i < end; ++i)
  133. {
  134. if (num >= numBits)
  135. {
  136. return idx;
  137. }
  138. var test = bits[i];
  139. var pos = i * numBitsPerStep;
  140. num += math.min(pos + math.tzcnt(test), endBit) - pos;
  141. if (test != 0)
  142. {
  143. break;
  144. }
  145. }
  146. if (num >= numBits)
  147. {
  148. return idx;
  149. }
  150. }
  151. return endBit;
  152. }
  153. static int FindUshort(ulong* ptr, int beginBit, int endBit, int numBits)
  154. {
  155. var bits = (ushort*)ptr;
  156. var numSteps = (numBits + 15) >> 4;
  157. var numBitsPerStep = 16;
  158. var maxBits = numSteps * numBitsPerStep;
  159. for (int i = beginBit / numBitsPerStep, end = AlignUp(endBit, numBitsPerStep) / numBitsPerStep; i < end; ++i)
  160. {
  161. if (bits[i] != 0)
  162. {
  163. continue;
  164. }
  165. var idx = i * numBitsPerStep;
  166. var num = math.min(idx + numBitsPerStep, endBit) - idx;
  167. if (idx != beginBit)
  168. {
  169. var test = bits[idx / numBitsPerStep - 1];
  170. var newIdx = math.max(idx - lzcnt(test), beginBit);
  171. num += idx - newIdx;
  172. idx = newIdx;
  173. }
  174. for (++i; i < end; ++i)
  175. {
  176. if (num >= numBits)
  177. {
  178. return idx;
  179. }
  180. var test = bits[i];
  181. var pos = i * numBitsPerStep;
  182. num += math.min(pos + tzcnt(test), endBit) - pos;
  183. if (test != 0)
  184. {
  185. break;
  186. }
  187. }
  188. if (num >= numBits)
  189. {
  190. return idx;
  191. }
  192. }
  193. return endBit;
  194. }
  195. static int FindByte(ulong* ptr, int beginBit, int endBit, int numBits)
  196. {
  197. var bits = (byte*)ptr;
  198. var numSteps = (numBits + 7) >> 3;
  199. var numBitsPerStep = 8;
  200. var maxBits = numSteps * numBitsPerStep;
  201. for (int i = beginBit / numBitsPerStep, end = AlignUp(endBit, numBitsPerStep) / numBitsPerStep; i < end; ++i)
  202. {
  203. if (bits[i] != 0)
  204. {
  205. continue;
  206. }
  207. var idx = i * numBitsPerStep;
  208. var num = math.min(idx + numBitsPerStep, endBit) - idx;
  209. if (idx != beginBit)
  210. {
  211. var test = bits[idx / numBitsPerStep - 1];
  212. var newIdx = math.max(idx - lzcnt(test), beginBit);
  213. num += idx - newIdx;
  214. idx = newIdx;
  215. }
  216. for (++i; i < end; ++i)
  217. {
  218. if (num >= numBits)
  219. {
  220. return idx;
  221. }
  222. var test = bits[i];
  223. var pos = i * numBitsPerStep;
  224. num += math.min(pos + tzcnt(test), endBit) - pos;
  225. if (test != 0)
  226. {
  227. break;
  228. }
  229. }
  230. if (num >= numBits)
  231. {
  232. return idx;
  233. }
  234. }
  235. return endBit;
  236. }
  237. static int FindUpto14bits(ulong* ptr, int beginBit, int endBit, int numBits)
  238. {
  239. var bits = (byte*)ptr;
  240. var bit = (byte)(beginBit & 7);
  241. byte beginMask = (byte)~(0xff << bit);
  242. var lz = 0;
  243. for (int begin = beginBit / 8, end = AlignUp(endBit, 8) / 8, i = begin; i < end; ++i)
  244. {
  245. var test = bits[i];
  246. test |= i == begin ? beginMask : (byte)0;
  247. if (test == 0xff)
  248. {
  249. continue;
  250. }
  251. var pos = i * 8;
  252. var tz = math.min(pos + tzcnt(test), endBit) - pos;
  253. if (lz + tz >= numBits)
  254. {
  255. return pos - lz;
  256. }
  257. lz = lzcnt(test);
  258. var idx = pos + 8;
  259. var newIdx = math.max(idx - lz, beginBit);
  260. lz = math.min(idx, endBit) - newIdx;
  261. if (lz >= numBits)
  262. {
  263. return newIdx;
  264. }
  265. }
  266. return endBit;
  267. }
  268. static int FindUpto6bits(ulong* ptr, int beginBit, int endBit, int numBits)
  269. {
  270. var bits = (byte*)ptr;
  271. byte beginMask = (byte)~(0xff << (beginBit & 7));
  272. byte endMask = (byte)~(0xff >> ((8-(endBit & 7) & 7)));
  273. var mask = 1 << numBits - 1;
  274. for (int begin = beginBit / 8, end = AlignUp(endBit, 8) / 8, i = begin; i < end; ++i)
  275. {
  276. var test = bits[i];
  277. test |= i == begin ? beginMask : (byte)0;
  278. test |= i == end - 1 ? endMask : (byte)0;
  279. if (test == 0xff)
  280. {
  281. continue;
  282. }
  283. for (int pos = i * 8, posEnd = pos + 7; pos < posEnd; ++pos)
  284. {
  285. var tz = tzcnt((byte)(test^0xff));
  286. test >>= tz;
  287. pos += tz;
  288. if ((test & mask) == 0)
  289. {
  290. return pos;
  291. }
  292. test >>= 1;
  293. }
  294. }
  295. return endBit;
  296. }
  297. internal static int FindWithBeginEnd(ulong* ptr, int beginBit, int endBit, int numBits)
  298. {
  299. int idx;
  300. if (numBits >= 127)
  301. {
  302. idx = FindUlong(ptr, beginBit, endBit, numBits);
  303. if (idx != endBit)
  304. {
  305. return idx;
  306. }
  307. }
  308. if (numBits >= 63)
  309. {
  310. idx = FindUint(ptr, beginBit, endBit, numBits);
  311. if (idx != endBit)
  312. {
  313. return idx;
  314. }
  315. }
  316. if (numBits >= 128)
  317. {
  318. // early out - no smaller step will find this gap
  319. return int.MaxValue;
  320. }
  321. if (numBits >= 31)
  322. {
  323. idx = FindUshort(ptr, beginBit, endBit, numBits);
  324. if (idx != endBit)
  325. {
  326. return idx;
  327. }
  328. }
  329. if (numBits >= 64)
  330. {
  331. // early out - no smaller step will find this gap
  332. return int.MaxValue;
  333. }
  334. idx = FindByte(ptr, beginBit, endBit, numBits);
  335. if (idx != endBit)
  336. {
  337. return idx;
  338. }
  339. if (numBits < 15)
  340. {
  341. idx = FindUpto14bits(ptr, beginBit, endBit, numBits);
  342. if (idx != endBit)
  343. {
  344. return idx;
  345. }
  346. if (numBits < 7)
  347. {
  348. // The worst case scenario when every byte boundary bit is set (pattern 0x81),
  349. // and we're looking for 6 or less bits. It will rescan byte-by-byte to find
  350. // any inner byte gap.
  351. idx = FindUpto6bits(ptr, beginBit, endBit, numBits);
  352. if (idx != endBit)
  353. {
  354. return idx;
  355. }
  356. }
  357. }
  358. return int.MaxValue;
  359. }
  360. internal static int Find(ulong* ptr, int pos, int count, int numBits) => FindWithBeginEnd(ptr, pos, pos + count, numBits);
  361. }
  362. /// <summary>
  363. /// A 32-bit array of bits.
  364. /// </summary>
  365. /// <remarks>
  366. /// Stack allocated, so it does not require thread safety checks or disposal.
  367. /// </remarks>
  368. [DebuggerTypeProxy(typeof(BitField32DebugView))]
  369. [BurstCompatible]
  370. public struct BitField32
  371. {
  372. /// <summary>
  373. /// The 32 bits, stored as a uint.
  374. /// </summary>
  375. /// <value>The 32 bits, stored as a uint.</value>
  376. public uint Value;
  377. /// <summary>
  378. /// Initializes and returns an instance of BitField32.
  379. /// </summary>
  380. /// <param name="initialValue">Initial value of the bit field. Default is 0.</param>
  381. public BitField32(uint initialValue = 0u)
  382. {
  383. Value = initialValue;
  384. }
  385. /// <summary>
  386. /// Clears all the bits to 0.
  387. /// </summary>
  388. public void Clear()
  389. {
  390. Value = 0u;
  391. }
  392. /// <summary>
  393. /// Sets a single bit to 1 or 0.
  394. /// </summary>
  395. /// <param name="pos">Position in this bit field to set (must be 0-31).</param>
  396. /// <param name="value">If true, sets the bit to 1. If false, sets the bit to 0.</param>
  397. /// <exception cref="ArgumentException">Thrown if `pos`is out of range.</exception>
  398. public void SetBits(int pos, bool value)
  399. {
  400. CheckArgs(pos, 1);
  401. Value = Bitwise.SetBits(Value, pos, 1, value);
  402. }
  403. /// <summary>
  404. /// Sets one or more contiguous bits to 1 or 0.
  405. /// </summary>
  406. /// <param name="pos">Position in the bit field of the first bit to set (must be 0-31).</param>
  407. /// <param name="value">If true, sets the bits to 1. If false, sets the bits to 0.</param>
  408. /// <param name="numBits">Number of bits to set (must be 1-32).</param>
  409. /// <exception cref="ArgumentException">Thrown if `pos` or `numBits` are out of bounds or if `pos + numBits` exceeds 32.</exception>
  410. public void SetBits(int pos, bool value, int numBits)
  411. {
  412. CheckArgs(pos, numBits);
  413. var mask = 0xffffffffu >> (32 - numBits);
  414. Value = Bitwise.SetBits(Value, pos, mask, value);
  415. }
  416. /// <summary>
  417. /// Returns one or more contiguous bits from the bit field as the lower bits of a uint.
  418. /// </summary>
  419. /// <param name="pos">Position in the bit field of the first bit to get (must be 0-31).</param>
  420. /// <param name="numBits">Number of bits to get (must be 1-32).</param>
  421. /// <exception cref="ArgumentException">Thrown if `pos` or `numBits` are out of bounds or if `pos + numBits` exceeds 32.</exception>
  422. /// <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>
  423. public uint GetBits(int pos, int numBits = 1)
  424. {
  425. CheckArgs(pos, numBits);
  426. var mask = 0xffffffffu >> (32 - numBits);
  427. return Bitwise.ExtractBits(Value, pos, mask);
  428. }
  429. /// <summary>
  430. /// Returns true if the bit at a position is 1.
  431. /// </summary>
  432. /// <param name="pos">Position in the bit field (must be 0-31).</param>
  433. /// <returns>True if the bit at the position is 1.</returns>
  434. public bool IsSet(int pos)
  435. {
  436. return 0 != GetBits(pos);
  437. }
  438. /// <summary>
  439. /// Returns true if none of the bits in a contiguous range are 1.
  440. /// </summary>
  441. /// <param name="pos">Position in the bit field (must be 0-31).</param>
  442. /// <param name="numBits">Number of bits to test (must be 1-32).</param>
  443. /// <exception cref="ArgumentException">Thrown if `pos` or `numBits` are out of bounds or if `pos + numBits` exceeds 32.</exception>
  444. /// <returns>True if none of the bits in the contiguous range are 1.</returns>
  445. public bool TestNone(int pos, int numBits = 1)
  446. {
  447. return 0u == GetBits(pos, numBits);
  448. }
  449. /// <summary>
  450. /// Returns true if any of the bits in a contiguous range are 1.
  451. /// </summary>
  452. /// <param name="pos">Position in the bit field (must be 0-31).</param>
  453. /// <param name="numBits">Number of bits to test (must be 1-32).</param>
  454. /// <exception cref="ArgumentException">Thrown if `pos` or `numBits` are out of bounds or if `pos + numBits` exceeds 32.</exception>
  455. /// <returns>True if at least one bit in the contiguous range is 1.</returns>
  456. public bool TestAny(int pos, int numBits = 1)
  457. {
  458. return 0u != GetBits(pos, numBits);
  459. }
  460. /// <summary>
  461. /// Returns true if all of the bits in a contiguous range are 1.
  462. /// </summary>
  463. /// <param name="pos">Position in the bit field (must be 0-31).</param>
  464. /// <param name="numBits">Number of bits to test (must be 1-32).</param>
  465. /// <exception cref="ArgumentException">Thrown if `pos` or `numBits` are out of bounds or if `pos + numBits` exceeds 32.</exception>
  466. /// <returns>True if all bits in the contiguous range are 1.</returns>
  467. public bool TestAll(int pos, int numBits = 1)
  468. {
  469. CheckArgs(pos, numBits);
  470. var mask = 0xffffffffu >> (32 - numBits);
  471. return mask == Bitwise.ExtractBits(Value, pos, mask);
  472. }
  473. /// <summary>
  474. /// Returns the number of bits that are 1.
  475. /// </summary>
  476. /// <returns>The number of bits that are 1.</returns>
  477. public int CountBits()
  478. {
  479. return math.countbits(Value);
  480. }
  481. /// <summary>
  482. /// Returns the number of leading zeroes.
  483. /// </summary>
  484. /// <returns>The number of leading zeros.</returns>
  485. public int CountLeadingZeros()
  486. {
  487. return math.lzcnt(Value);
  488. }
  489. /// <summary>
  490. /// Returns the number of trailing zeros.
  491. /// </summary>
  492. /// <returns>The number of trailing zeros.</returns>
  493. public int CountTrailingZeros()
  494. {
  495. return math.tzcnt(Value);
  496. }
  497. [Conditional("ENABLE_UNITY_COLLECTIONS_CHECKS")]
  498. static void CheckArgs(int pos, int numBits)
  499. {
  500. if (pos > 31
  501. || numBits == 0
  502. || numBits > 32
  503. || pos + numBits > 32)
  504. {
  505. throw new ArgumentException($"BitField32 invalid arguments: pos {pos} (must be 0-31), numBits {numBits} (must be 1-32).");
  506. }
  507. }
  508. }
  509. sealed class BitField32DebugView
  510. {
  511. BitField32 BitField;
  512. public BitField32DebugView(BitField32 bitfield)
  513. {
  514. BitField = bitfield;
  515. }
  516. public bool[] Bits
  517. {
  518. get
  519. {
  520. var array = new bool[32];
  521. for (int i = 0; i < 32; ++i)
  522. {
  523. array[i] = BitField.IsSet(i);
  524. }
  525. return array;
  526. }
  527. }
  528. }
  529. /// <summary>
  530. /// A 64-bit array of bits.
  531. /// </summary>
  532. /// <remarks>
  533. /// Stack allocated, so it does not require thread safety checks or disposal.
  534. /// </remarks>
  535. [DebuggerTypeProxy(typeof(BitField64DebugView))]
  536. [BurstCompatible]
  537. public struct BitField64
  538. {
  539. /// <summary>
  540. /// The 64 bits, stored as a ulong.
  541. /// </summary>
  542. /// <value>The 64 bits, stored as a uint.</value>
  543. public ulong Value;
  544. /// <summary>
  545. /// Initializes and returns an instance of BitField64.
  546. /// </summary>
  547. /// <param name="initialValue">Initial value of the bit field. Default is 0.</param>
  548. public BitField64(ulong initialValue = 0ul)
  549. {
  550. Value = initialValue;
  551. }
  552. /// <summary>
  553. /// Clears all bits to 0.
  554. /// </summary>
  555. public void Clear()
  556. {
  557. Value = 0ul;
  558. }
  559. /// <summary>
  560. /// Sets a single bit to 1 or 0.
  561. /// </summary>
  562. /// <param name="pos">Position in this bit field to set (must be 0-63).</param>
  563. /// <param name="value">If true, sets the bit to 1. If false, sets the bit to 0.</param>
  564. /// <exception cref="ArgumentException">Thrown if `pos`is out of range.</exception>
  565. public void SetBits(int pos, bool value)
  566. {
  567. CheckArgs(pos, 1);
  568. Value = Bitwise.SetBits(Value, pos, 1, value);
  569. }
  570. /// <summary>
  571. /// Sets one or more contiguous bits to 1 or 0.
  572. /// </summary>
  573. /// <param name="pos">Position in the bit field of the first bit to set (must be 0-63).</param>
  574. /// <param name="value">If true, sets the bits to 1. If false, sets the bits to 0.</param>
  575. /// <param name="numBits">Number of bits to set (must be 1-64).</param>
  576. /// <exception cref="ArgumentException">Thrown if `pos` or `numBits` are out of bounds or if `pos + numBits` exceeds 64.</exception>
  577. public void SetBits(int pos, bool value, int numBits = 1)
  578. {
  579. CheckArgs(pos, numBits);
  580. var mask = 0xfffffffffffffffful >> (64 - numBits);
  581. Value = Bitwise.SetBits(Value, pos, mask, value);
  582. }
  583. /// <summary>
  584. /// Returns one or more contiguous bits from the bit field as the lower bits of a ulong.
  585. /// </summary>
  586. /// <param name="pos">Position in the bit field of the first bit to get (must be 0-63).</param>
  587. /// <param name="numBits">Number of bits to get (must be 1-64).</param>
  588. /// <exception cref="ArgumentException">Thrown if `pos` or `numBits` are out of bounds or if `pos + numBits` exceeds 64.</exception>
  589. /// <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>
  590. public ulong GetBits(int pos, int numBits = 1)
  591. {
  592. CheckArgs(pos, numBits);
  593. var mask = 0xfffffffffffffffful >> (64 - numBits);
  594. return Bitwise.ExtractBits(Value, pos, mask);
  595. }
  596. /// <summary>
  597. /// Returns true if the bit at a position is 1.
  598. /// </summary>
  599. /// <param name="pos">Position in the bit field (must be 0-63).</param>
  600. /// <returns>True if the bit at the position is 1.</returns>
  601. public bool IsSet(int pos)
  602. {
  603. return 0ul != GetBits(pos);
  604. }
  605. /// <summary>
  606. /// Returns true if none of the bits in a contiguous range are 1.
  607. /// </summary>
  608. /// <param name="pos">Position in the bit field (must be 0-63).</param>
  609. /// <param name="numBits">Number of bits to test (must be 1-64).</param>
  610. /// <exception cref="ArgumentException">Thrown if `pos` or `numBits` are out of bounds or if `pos + numBits` exceeds 64.</exception>
  611. /// <returns>True if none of the bits in the contiguous range are 1.</returns>
  612. public bool TestNone(int pos, int numBits = 1)
  613. {
  614. return 0ul == GetBits(pos, numBits);
  615. }
  616. /// <summary>
  617. /// Returns true if any of the bits in a contiguous range are 1.
  618. /// </summary>
  619. /// <param name="pos">Position in the bit field (must be 0-63).</param>
  620. /// <param name="numBits">Number of bits to test (must be 1-64).</param>
  621. /// <exception cref="ArgumentException">Thrown if `pos` or `numBits` are out of bounds or if `pos + numBits` exceeds 64.</exception>
  622. /// <returns>True if at least one bit in the contiguous range is 1.</returns>
  623. public bool TestAny(int pos, int numBits = 1)
  624. {
  625. return 0ul != GetBits(pos, numBits);
  626. }
  627. /// <summary>
  628. /// Returns true if all of the bits in a contiguous range are 1.
  629. /// </summary>
  630. /// <param name="pos">Position in the bit field (must be 0-63).</param>
  631. /// <param name="numBits">Number of bits to test (must be 1-64).</param>
  632. /// <exception cref="ArgumentException">Thrown if `pos` or `numBits` are out of bounds or if `pos + numBits` exceeds 64.</exception>
  633. /// <returns>True if all bits in the contiguous range are 1.</returns>
  634. public bool TestAll(int pos, int numBits = 1)
  635. {
  636. CheckArgs(pos, numBits);
  637. var mask = 0xfffffffffffffffful >> (64 - numBits);
  638. return mask == Bitwise.ExtractBits(Value, pos, mask);
  639. }
  640. /// <summary>
  641. /// Returns the number of bits that are 1.
  642. /// </summary>
  643. /// <returns>The number of bits that are 1.</returns>
  644. public int CountBits()
  645. {
  646. return math.countbits(Value);
  647. }
  648. /// <summary>
  649. /// Returns the number of leading zeroes.
  650. /// </summary>
  651. /// <returns>The number of leading zeros.</returns>
  652. public int CountLeadingZeros()
  653. {
  654. return math.lzcnt(Value);
  655. }
  656. /// <summary>
  657. /// Returns the number of trailing zeros.
  658. /// </summary>
  659. /// <returns>The number of trailing zeros.</returns>
  660. public int CountTrailingZeros()
  661. {
  662. return math.tzcnt(Value);
  663. }
  664. [Conditional("ENABLE_UNITY_COLLECTIONS_CHECKS")]
  665. static void CheckArgs(int pos, int numBits)
  666. {
  667. if (pos > 63
  668. || numBits == 0
  669. || numBits > 64
  670. || pos + numBits > 64)
  671. {
  672. throw new ArgumentException($"BitField32 invalid arguments: pos {pos} (must be 0-63), numBits {numBits} (must be 1-64).");
  673. }
  674. }
  675. }
  676. sealed class BitField64DebugView
  677. {
  678. BitField64 Data;
  679. public BitField64DebugView(BitField64 data)
  680. {
  681. Data = data;
  682. }
  683. public bool[] Bits
  684. {
  685. get
  686. {
  687. var array = new bool[64];
  688. for (int i = 0; i < 64; ++i)
  689. {
  690. array[i] = Data.IsSet(i);
  691. }
  692. return array;
  693. }
  694. }
  695. }
  696. }