Nessuna descrizione
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 31KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975
  1. using System;
  2. using System.Diagnostics;
  3. using Unity.Mathematics;
  4. namespace Unity.Collections
  5. {
  6. [GenerateTestsForBurstCompatibility]
  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. internal static bool TestNone(ulong* ptr, int length, int pos, int numBits = 1)
  362. {
  363. var end = math.min(pos + numBits, length);
  364. var idxB = pos >> 6;
  365. var shiftB = pos & 0x3f;
  366. var idxE = (end - 1) >> 6;
  367. var shiftE = end & 0x3f;
  368. var maskB = 0xfffffffffffffffful << shiftB;
  369. var maskE = 0xfffffffffffffffful >> (64 - shiftE);
  370. if (idxB == idxE)
  371. {
  372. var mask = maskB & maskE;
  373. return 0ul == (ptr[idxB] & mask);
  374. }
  375. if (0ul != (ptr[idxB] & maskB))
  376. {
  377. return false;
  378. }
  379. for (var idx = idxB + 1; idx < idxE; ++idx)
  380. {
  381. if (0ul != ptr[idx])
  382. {
  383. return false;
  384. }
  385. }
  386. return 0ul == (ptr[idxE] & maskE);
  387. }
  388. internal static bool TestAny(ulong* ptr, int length, int pos, int numBits = 1)
  389. {
  390. var end = math.min(pos + numBits, length);
  391. var idxB = pos >> 6;
  392. var shiftB = pos & 0x3f;
  393. var idxE = (end - 1) >> 6;
  394. var shiftE = end & 0x3f;
  395. var maskB = 0xfffffffffffffffful << shiftB;
  396. var maskE = 0xfffffffffffffffful >> (64 - shiftE);
  397. if (idxB == idxE)
  398. {
  399. var mask = maskB & maskE;
  400. return 0ul != (ptr[idxB] & mask);
  401. }
  402. if (0ul != (ptr[idxB] & maskB))
  403. {
  404. return true;
  405. }
  406. for (var idx = idxB + 1; idx < idxE; ++idx)
  407. {
  408. if (0ul != ptr[idx])
  409. {
  410. return true;
  411. }
  412. }
  413. return 0ul != (ptr[idxE] & maskE);
  414. }
  415. internal static bool TestAll(ulong* ptr, int length, int pos, int numBits = 1)
  416. {
  417. var end = math.min(pos + numBits, length);
  418. var idxB = pos >> 6;
  419. var shiftB = pos & 0x3f;
  420. var idxE = (end - 1) >> 6;
  421. var shiftE = end & 0x3f;
  422. var maskB = 0xfffffffffffffffful << shiftB;
  423. var maskE = 0xfffffffffffffffful >> (64 - shiftE);
  424. if (idxB == idxE)
  425. {
  426. var mask = maskB & maskE;
  427. return mask == (ptr[idxB] & mask);
  428. }
  429. if (maskB != (ptr[idxB] & maskB))
  430. {
  431. return false;
  432. }
  433. for (var idx = idxB + 1; idx < idxE; ++idx)
  434. {
  435. if (0xfffffffffffffffful != ptr[idx])
  436. {
  437. return false;
  438. }
  439. }
  440. return maskE == (ptr[idxE] & maskE);
  441. }
  442. internal static int CountBits(ulong* ptr, int length, int pos, int numBits = 1)
  443. {
  444. var end = math.min(pos + numBits, length);
  445. var idxB = pos >> 6;
  446. var shiftB = pos & 0x3f;
  447. var idxE = (end - 1) >> 6;
  448. var shiftE = end & 0x3f;
  449. var maskB = 0xfffffffffffffffful << shiftB;
  450. var maskE = 0xfffffffffffffffful >> (64 - shiftE);
  451. if (idxB == idxE)
  452. {
  453. var mask = maskB & maskE;
  454. return math.countbits(ptr[idxB] & mask);
  455. }
  456. var count = math.countbits(ptr[idxB] & maskB);
  457. for (var idx = idxB + 1; idx < idxE; ++idx)
  458. {
  459. count += math.countbits(ptr[idx]);
  460. }
  461. count += math.countbits(ptr[idxE] & maskE);
  462. return count;
  463. }
  464. internal static bool IsSet(ulong* ptr, int pos)
  465. {
  466. var idx = pos >> 6;
  467. var shift = pos & 0x3f;
  468. var mask = 1ul << shift;
  469. return 0ul != (ptr[idx] & mask);
  470. }
  471. internal static ulong GetBits(ulong* ptr, int length, int pos, int numBits = 1)
  472. {
  473. var idxB = pos >> 6;
  474. var shiftB = pos & 0x3f;
  475. if (shiftB + numBits <= 64)
  476. {
  477. var mask = 0xfffffffffffffffful >> (64 - numBits);
  478. return Bitwise.ExtractBits(ptr[idxB], shiftB, mask);
  479. }
  480. var end = math.min(pos + numBits, length);
  481. var idxE = (end - 1) >> 6;
  482. var shiftE = end & 0x3f;
  483. var maskB = 0xfffffffffffffffful >> shiftB;
  484. ulong valueB = Bitwise.ExtractBits(ptr[idxB], shiftB, maskB);
  485. var maskE = 0xfffffffffffffffful >> (64 - shiftE);
  486. ulong valueE = Bitwise.ExtractBits(ptr[idxE], 0, maskE);
  487. return (valueE << (64 - shiftB)) | valueB;
  488. }
  489. }
  490. /// <summary>
  491. /// A 32-bit array of bits.
  492. /// </summary>
  493. /// <remarks>
  494. /// Stack allocated, so it does not require thread safety checks or disposal.
  495. /// </remarks>
  496. [DebuggerTypeProxy(typeof(BitField32DebugView))]
  497. [GenerateTestsForBurstCompatibility]
  498. public struct BitField32
  499. {
  500. /// <summary>
  501. /// The 32 bits, stored as a uint.
  502. /// </summary>
  503. /// <value>The 32 bits, stored as a uint.</value>
  504. public uint Value;
  505. /// <summary>
  506. /// Initializes and returns an instance of BitField32.
  507. /// </summary>
  508. /// <param name="initialValue">Initial value of the bit field. Default is 0.</param>
  509. public BitField32(uint initialValue = 0u)
  510. {
  511. Value = initialValue;
  512. }
  513. /// <summary>
  514. /// Clears all the bits to 0.
  515. /// </summary>
  516. public void Clear()
  517. {
  518. Value = 0u;
  519. }
  520. /// <summary>
  521. /// Sets a single bit to 1 or 0.
  522. /// </summary>
  523. /// <param name="pos">Position in this bit field to set (must be 0-31).</param>
  524. /// <param name="value">If true, sets the bit to 1. If false, sets the bit to 0.</param>
  525. /// <exception cref="ArgumentException">Thrown if `pos`is out of range.</exception>
  526. public void SetBits(int pos, bool value)
  527. {
  528. CheckArgs(pos, 1);
  529. Value = Bitwise.SetBits(Value, pos, 1, value);
  530. }
  531. /// <summary>
  532. /// Sets one or more contiguous bits to 1 or 0.
  533. /// </summary>
  534. /// <param name="pos">Position in the bit field of the first bit to set (must be 0-31).</param>
  535. /// <param name="value">If true, sets the bits to 1. If false, sets the bits to 0.</param>
  536. /// <param name="numBits">Number of bits to set (must be 1-32).</param>
  537. /// <exception cref="ArgumentException">Thrown if `pos` or `numBits` are out of bounds or if `pos + numBits` exceeds 32.</exception>
  538. public void SetBits(int pos, bool value, int numBits)
  539. {
  540. CheckArgs(pos, numBits);
  541. var mask = 0xffffffffu >> (32 - numBits);
  542. Value = Bitwise.SetBits(Value, pos, mask, value);
  543. }
  544. /// <summary>
  545. /// Returns one or more contiguous bits from the bit field as the lower bits of a uint.
  546. /// </summary>
  547. /// <param name="pos">Position in the bit field of the first bit to get (must be 0-31).</param>
  548. /// <param name="numBits">Number of bits to get (must be 1-32).</param>
  549. /// <exception cref="ArgumentException">Thrown if `pos` or `numBits` are out of bounds or if `pos + numBits` exceeds 32.</exception>
  550. /// <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>
  551. public uint GetBits(int pos, int numBits = 1)
  552. {
  553. CheckArgs(pos, numBits);
  554. var mask = 0xffffffffu >> (32 - numBits);
  555. return Bitwise.ExtractBits(Value, pos, mask);
  556. }
  557. /// <summary>
  558. /// Returns true if the bit at a position is 1.
  559. /// </summary>
  560. /// <param name="pos">Position in the bit field (must be 0-31).</param>
  561. /// <returns>True if the bit at the position is 1.</returns>
  562. public bool IsSet(int pos)
  563. {
  564. return 0 != GetBits(pos);
  565. }
  566. /// <summary>
  567. /// Returns true if none of the bits in a contiguous range are 1.
  568. /// </summary>
  569. /// <param name="pos">Position in the bit field (must be 0-31).</param>
  570. /// <param name="numBits">Number of bits to test (must be 1-32).</param>
  571. /// <exception cref="ArgumentException">Thrown if `pos` or `numBits` are out of bounds or if `pos + numBits` exceeds 32.</exception>
  572. /// <returns>True if none of the bits in the contiguous range are 1.</returns>
  573. public bool TestNone(int pos, int numBits = 1)
  574. {
  575. return 0u == GetBits(pos, numBits);
  576. }
  577. /// <summary>
  578. /// Returns true if any of the bits in a contiguous range are 1.
  579. /// </summary>
  580. /// <param name="pos">Position in the bit field (must be 0-31).</param>
  581. /// <param name="numBits">Number of bits to test (must be 1-32).</param>
  582. /// <exception cref="ArgumentException">Thrown if `pos` or `numBits` are out of bounds or if `pos + numBits` exceeds 32.</exception>
  583. /// <returns>True if at least one bit in the contiguous range is 1.</returns>
  584. public bool TestAny(int pos, int numBits = 1)
  585. {
  586. return 0u != GetBits(pos, numBits);
  587. }
  588. /// <summary>
  589. /// Returns true if all of the bits in a contiguous range are 1.
  590. /// </summary>
  591. /// <param name="pos">Position in the bit field (must be 0-31).</param>
  592. /// <param name="numBits">Number of bits to test (must be 1-32).</param>
  593. /// <exception cref="ArgumentException">Thrown if `pos` or `numBits` are out of bounds or if `pos + numBits` exceeds 32.</exception>
  594. /// <returns>True if all bits in the contiguous range are 1.</returns>
  595. public bool TestAll(int pos, int numBits = 1)
  596. {
  597. CheckArgs(pos, numBits);
  598. var mask = 0xffffffffu >> (32 - numBits);
  599. return mask == Bitwise.ExtractBits(Value, pos, mask);
  600. }
  601. /// <summary>
  602. /// Returns the number of bits that are 1.
  603. /// </summary>
  604. /// <returns>The number of bits that are 1.</returns>
  605. public int CountBits()
  606. {
  607. return math.countbits(Value);
  608. }
  609. /// <summary>
  610. /// Returns the number of leading zeroes.
  611. /// </summary>
  612. /// <returns>The number of leading zeros.</returns>
  613. public int CountLeadingZeros()
  614. {
  615. return math.lzcnt(Value);
  616. }
  617. /// <summary>
  618. /// Returns the number of trailing zeros.
  619. /// </summary>
  620. /// <returns>The number of trailing zeros.</returns>
  621. public int CountTrailingZeros()
  622. {
  623. return math.tzcnt(Value);
  624. }
  625. [Conditional("ENABLE_UNITY_COLLECTIONS_CHECKS"), Conditional("UNITY_DOTS_DEBUG")]
  626. static void CheckArgs(int pos, int numBits)
  627. {
  628. if (pos > 31
  629. || numBits == 0
  630. || numBits > 32
  631. || pos + numBits > 32)
  632. {
  633. throw new ArgumentException($"BitField32 invalid arguments: pos {pos} (must be 0-31), numBits {numBits} (must be 1-32).");
  634. }
  635. }
  636. }
  637. sealed class BitField32DebugView
  638. {
  639. BitField32 BitField;
  640. public BitField32DebugView(BitField32 bitfield)
  641. {
  642. BitField = bitfield;
  643. }
  644. public bool[] Bits
  645. {
  646. get
  647. {
  648. var array = new bool[32];
  649. for (int i = 0; i < 32; ++i)
  650. {
  651. array[i] = BitField.IsSet(i);
  652. }
  653. return array;
  654. }
  655. }
  656. }
  657. /// <summary>
  658. /// A 64-bit array of bits.
  659. /// </summary>
  660. /// <remarks>
  661. /// Stack allocated, so it does not require thread safety checks or disposal.
  662. /// </remarks>
  663. [DebuggerTypeProxy(typeof(BitField64DebugView))]
  664. [GenerateTestsForBurstCompatibility]
  665. public struct BitField64
  666. {
  667. /// <summary>
  668. /// The 64 bits, stored as a ulong.
  669. /// </summary>
  670. /// <value>The 64 bits, stored as a uint.</value>
  671. public ulong Value;
  672. /// <summary>
  673. /// Initializes and returns an instance of BitField64.
  674. /// </summary>
  675. /// <param name="initialValue">Initial value of the bit field. Default is 0.</param>
  676. public BitField64(ulong initialValue = 0ul)
  677. {
  678. Value = initialValue;
  679. }
  680. /// <summary>
  681. /// Clears all bits to 0.
  682. /// </summary>
  683. public void Clear()
  684. {
  685. Value = 0ul;
  686. }
  687. /// <summary>
  688. /// Sets a single bit to 1 or 0.
  689. /// </summary>
  690. /// <param name="pos">Position in this bit field to set (must be 0-63).</param>
  691. /// <param name="value">If true, sets the bit to 1. If false, sets the bit to 0.</param>
  692. /// <exception cref="ArgumentException">Thrown if `pos`is out of range.</exception>
  693. public void SetBits(int pos, bool value)
  694. {
  695. CheckArgs(pos, 1);
  696. Value = Bitwise.SetBits(Value, pos, 1, value);
  697. }
  698. /// <summary>
  699. /// Sets one or more contiguous bits to 1 or 0.
  700. /// </summary>
  701. /// <param name="pos">Position in the bit field of the first bit to set (must be 0-63).</param>
  702. /// <param name="value">If true, sets the bits to 1. If false, sets the bits to 0.</param>
  703. /// <param name="numBits">Number of bits to set (must be 1-64).</param>
  704. /// <exception cref="ArgumentException">Thrown if `pos` or `numBits` are out of bounds or if `pos + numBits` exceeds 64.</exception>
  705. public void SetBits(int pos, bool value, int numBits = 1)
  706. {
  707. CheckArgs(pos, numBits);
  708. var mask = 0xfffffffffffffffful >> (64 - numBits);
  709. Value = Bitwise.SetBits(Value, pos, mask, value);
  710. }
  711. /// <summary>
  712. /// Returns one or more contiguous bits from the bit field as the lower bits of a ulong.
  713. /// </summary>
  714. /// <param name="pos">Position in the bit field of the first bit to get (must be 0-63).</param>
  715. /// <param name="numBits">Number of bits to get (must be 1-64).</param>
  716. /// <exception cref="ArgumentException">Thrown if `pos` or `numBits` are out of bounds or if `pos + numBits` exceeds 64.</exception>
  717. /// <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>
  718. public ulong GetBits(int pos, int numBits = 1)
  719. {
  720. CheckArgs(pos, numBits);
  721. var mask = 0xfffffffffffffffful >> (64 - numBits);
  722. return Bitwise.ExtractBits(Value, pos, mask);
  723. }
  724. /// <summary>
  725. /// Returns true if the bit at a position is 1.
  726. /// </summary>
  727. /// <param name="pos">Position in the bit field (must be 0-63).</param>
  728. /// <returns>True if the bit at the position is 1.</returns>
  729. public bool IsSet(int pos)
  730. {
  731. return 0ul != GetBits(pos);
  732. }
  733. /// <summary>
  734. /// Returns true if none of the bits in a contiguous range are 1.
  735. /// </summary>
  736. /// <param name="pos">Position in the bit field (must be 0-63).</param>
  737. /// <param name="numBits">Number of bits to test (must be 1-64).</param>
  738. /// <exception cref="ArgumentException">Thrown if `pos` or `numBits` are out of bounds or if `pos + numBits` exceeds 64.</exception>
  739. /// <returns>True if none of the bits in the contiguous range are 1.</returns>
  740. public bool TestNone(int pos, int numBits = 1)
  741. {
  742. return 0ul == GetBits(pos, numBits);
  743. }
  744. /// <summary>
  745. /// Returns true if any of the bits in a contiguous range are 1.
  746. /// </summary>
  747. /// <param name="pos">Position in the bit field (must be 0-63).</param>
  748. /// <param name="numBits">Number of bits to test (must be 1-64).</param>
  749. /// <exception cref="ArgumentException">Thrown if `pos` or `numBits` are out of bounds or if `pos + numBits` exceeds 64.</exception>
  750. /// <returns>True if at least one bit in the contiguous range is 1.</returns>
  751. public bool TestAny(int pos, int numBits = 1)
  752. {
  753. return 0ul != GetBits(pos, numBits);
  754. }
  755. /// <summary>
  756. /// Returns true if all of the bits in a contiguous range are 1.
  757. /// </summary>
  758. /// <param name="pos">Position in the bit field (must be 0-63).</param>
  759. /// <param name="numBits">Number of bits to test (must be 1-64).</param>
  760. /// <exception cref="ArgumentException">Thrown if `pos` or `numBits` are out of bounds or if `pos + numBits` exceeds 64.</exception>
  761. /// <returns>True if all bits in the contiguous range are 1.</returns>
  762. public bool TestAll(int pos, int numBits = 1)
  763. {
  764. CheckArgs(pos, numBits);
  765. var mask = 0xfffffffffffffffful >> (64 - numBits);
  766. return mask == Bitwise.ExtractBits(Value, pos, mask);
  767. }
  768. /// <summary>
  769. /// Returns the number of bits that are 1.
  770. /// </summary>
  771. /// <returns>The number of bits that are 1.</returns>
  772. public int CountBits()
  773. {
  774. return math.countbits(Value);
  775. }
  776. /// <summary>
  777. /// Returns the number of leading zeroes.
  778. /// </summary>
  779. /// <returns>The number of leading zeros.</returns>
  780. public int CountLeadingZeros()
  781. {
  782. return math.lzcnt(Value);
  783. }
  784. /// <summary>
  785. /// Returns the number of trailing zeros.
  786. /// </summary>
  787. /// <returns>The number of trailing zeros.</returns>
  788. public int CountTrailingZeros()
  789. {
  790. return math.tzcnt(Value);
  791. }
  792. [Conditional("ENABLE_UNITY_COLLECTIONS_CHECKS"), Conditional("UNITY_DOTS_DEBUG")]
  793. static void CheckArgs(int pos, int numBits)
  794. {
  795. if (pos > 63
  796. || numBits == 0
  797. || numBits > 64
  798. || pos + numBits > 64)
  799. {
  800. throw new ArgumentException($"BitField32 invalid arguments: pos {pos} (must be 0-63), numBits {numBits} (must be 1-64).");
  801. }
  802. }
  803. }
  804. sealed class BitField64DebugView
  805. {
  806. BitField64 Data;
  807. public BitField64DebugView(BitField64 data)
  808. {
  809. Data = data;
  810. }
  811. public bool[] Bits
  812. {
  813. get
  814. {
  815. var array = new bool[64];
  816. for (int i = 0; i < 64; ++i)
  817. {
  818. array[i] = Data.IsSet(i);
  819. }
  820. return array;
  821. }
  822. }
  823. }
  824. }