No Description
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.

MemoryHelpers.cs 24KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594
  1. using System;
  2. using Unity.Collections.LowLevel.Unsafe;
  3. namespace UnityEngine.InputSystem.Utilities
  4. {
  5. internal static unsafe class MemoryHelpers
  6. {
  7. public struct BitRegion
  8. {
  9. public uint bitOffset;
  10. public uint sizeInBits;
  11. public bool isEmpty => sizeInBits == 0;
  12. public BitRegion(uint bitOffset, uint sizeInBits)
  13. {
  14. this.bitOffset = bitOffset;
  15. this.sizeInBits = sizeInBits;
  16. }
  17. public BitRegion(uint byteOffset, uint bitOffset, uint sizeInBits)
  18. {
  19. this.bitOffset = byteOffset * 8 + bitOffset;
  20. this.sizeInBits = sizeInBits;
  21. }
  22. public BitRegion Overlap(BitRegion other)
  23. {
  24. ////REVIEW: too many branches; this can probably be done much smarter
  25. var thisEnd = bitOffset + sizeInBits;
  26. var otherEnd = other.bitOffset + other.sizeInBits;
  27. if (thisEnd <= other.bitOffset || otherEnd <= bitOffset)
  28. return default;
  29. var end = Math.Min(thisEnd, otherEnd);
  30. var start = Math.Max(bitOffset, other.bitOffset);
  31. return new BitRegion(start, end - start);
  32. }
  33. }
  34. public static bool Compare(void* ptr1, void* ptr2, BitRegion region)
  35. {
  36. if (region.sizeInBits == 1)
  37. return ReadSingleBit(ptr1, region.bitOffset) == ReadSingleBit(ptr2, region.bitOffset);
  38. return MemCmpBitRegion(ptr1, ptr2, region.bitOffset, region.sizeInBits);
  39. }
  40. public static uint ComputeFollowingByteOffset(uint byteOffset, uint sizeInBits)
  41. {
  42. return (uint)(byteOffset + sizeInBits / 8 + (sizeInBits % 8 > 0 ? 1 : 0));
  43. }
  44. public static void WriteSingleBit(void* ptr, uint bitOffset, bool value)
  45. {
  46. var byteOffset = bitOffset >> 3;
  47. bitOffset &= 7;
  48. if (value)
  49. *((byte*)ptr + byteOffset) |= (byte)(1U << (int)bitOffset);
  50. else
  51. *((byte*)ptr + byteOffset) &= (byte)~(1U << (int)bitOffset);
  52. }
  53. public static bool ReadSingleBit(void* ptr, uint bitOffset)
  54. {
  55. var byteOffset = bitOffset >> 3;
  56. bitOffset &= 7;
  57. return (*((byte*)ptr + byteOffset) & (1U << (int)bitOffset)) != 0;
  58. }
  59. public static void MemCpyBitRegion(void* destination, void* source, uint bitOffset, uint bitCount)
  60. {
  61. var destPtr = (byte*)destination;
  62. var sourcePtr = (byte*)source;
  63. // If we're offset by more than a byte, adjust our pointers.
  64. if (bitOffset >= 8)
  65. {
  66. var skipBytes = bitOffset / 8;
  67. destPtr += skipBytes;
  68. sourcePtr += skipBytes;
  69. bitOffset %= 8;
  70. }
  71. // Copy unaligned prefix, if any.
  72. if (bitOffset > 0)
  73. {
  74. var byteMask = 0xFF << (int)bitOffset;
  75. if (bitCount + bitOffset < 8)
  76. byteMask &= 0xFF >> (int)(8 - (bitCount + bitOffset));
  77. *destPtr = (byte)(((*destPtr & ~byteMask) | (*sourcePtr & byteMask)) & 0xFF);
  78. // If the total length of the memory region is equal or less than a byte,
  79. // we're done.
  80. if (bitCount + bitOffset <= 8)
  81. return;
  82. ++destPtr;
  83. ++sourcePtr;
  84. bitCount -= 8 - bitOffset;
  85. }
  86. // Copy contiguous bytes in-between, if any.
  87. var byteCount = bitCount / 8;
  88. if (byteCount >= 1)
  89. UnsafeUtility.MemCpy(destPtr, sourcePtr, byteCount);
  90. // Copy unaligned suffix, if any.
  91. var remainingBitCount = bitCount % 8;
  92. if (remainingBitCount > 0)
  93. {
  94. destPtr += byteCount;
  95. sourcePtr += byteCount;
  96. // We want the lowest remaining bits.
  97. var byteMask = 0xFF >> (int)(8 - remainingBitCount);
  98. *destPtr = (byte)(((*destPtr & ~byteMask) | (*sourcePtr & byteMask)) & 0xFF);
  99. }
  100. }
  101. /// <summary>
  102. /// Compare two memory regions that may be offset by a bit count and have a length expressed
  103. /// in bits.
  104. /// </summary>
  105. /// <param name="ptr1">Pointer to start of first memory region.</param>
  106. /// <param name="ptr2">Pointer to start of second memory region.</param>
  107. /// <param name="bitOffset">Offset in bits from each of the pointers to the start of the memory region to compare.</param>
  108. /// <param name="bitCount">Number of bits to compare in the memory region.</param>
  109. /// <param name="mask">If not null, only compare bits set in the mask. This allows comparing two memory regions while
  110. /// ignoring specific bits.</param>
  111. /// <returns>True if the two memory regions are identical, false otherwise.</returns>
  112. public static bool MemCmpBitRegion(void* ptr1, void* ptr2, uint bitOffset, uint bitCount, void* mask = null)
  113. {
  114. var bytePtr1 = (byte*)ptr1;
  115. var bytePtr2 = (byte*)ptr2;
  116. var maskPtr = (byte*)mask;
  117. // If we're offset by more than a byte, adjust our pointers.
  118. if (bitOffset >= 8)
  119. {
  120. var skipBytes = bitOffset / 8;
  121. bytePtr1 += skipBytes;
  122. bytePtr2 += skipBytes;
  123. if (maskPtr != null)
  124. maskPtr += skipBytes;
  125. bitOffset %= 8;
  126. }
  127. // Compare unaligned prefix, if any.
  128. if (bitOffset > 0)
  129. {
  130. // If the total length of the memory region is less than a byte, we need
  131. // to mask out parts of the bits we're reading.
  132. var byteMask = 0xFF << (int)bitOffset;
  133. if (bitCount + bitOffset < 8)
  134. byteMask &= 0xFF >> (int)(8 - (bitCount + bitOffset));
  135. if (maskPtr != null)
  136. {
  137. byteMask &= *maskPtr;
  138. ++maskPtr;
  139. }
  140. var byte1 = *bytePtr1 & byteMask;
  141. var byte2 = *bytePtr2 & byteMask;
  142. if (byte1 != byte2)
  143. return false;
  144. // If the total length of the memory region is equal or less than a byte,
  145. // we're done.
  146. if (bitCount + bitOffset <= 8)
  147. return true;
  148. ++bytePtr1;
  149. ++bytePtr2;
  150. bitCount -= 8 - bitOffset;
  151. }
  152. // Compare contiguous bytes in-between, if any.
  153. var byteCount = bitCount / 8;
  154. if (byteCount >= 1)
  155. {
  156. if (maskPtr != null)
  157. {
  158. ////REVIEW: could go int by int here for as long as we can
  159. // Have to go byte-by-byte in order to apply the masking.
  160. for (var i = 0; i < byteCount; ++i)
  161. {
  162. var byte1 = bytePtr1[i];
  163. var byte2 = bytePtr2[i];
  164. var byteMask = maskPtr[i];
  165. if ((byte1 & byteMask) != (byte2 & byteMask))
  166. return false;
  167. }
  168. }
  169. else
  170. {
  171. if (UnsafeUtility.MemCmp(bytePtr1, bytePtr2, byteCount) != 0)
  172. return false;
  173. }
  174. }
  175. // Compare unaligned suffix, if any.
  176. var remainingBitCount = bitCount % 8;
  177. if (remainingBitCount > 0)
  178. {
  179. bytePtr1 += byteCount;
  180. bytePtr2 += byteCount;
  181. // We want the lowest remaining bits.
  182. var byteMask = 0xFF >> (int)(8 - remainingBitCount);
  183. if (maskPtr != null)
  184. {
  185. maskPtr += byteCount;
  186. byteMask &= *maskPtr;
  187. }
  188. var byte1 = *bytePtr1 & byteMask;
  189. var byte2 = *bytePtr2 & byteMask;
  190. if (byte1 != byte2)
  191. return false;
  192. }
  193. return true;
  194. }
  195. public static void MemSet(void* destination, int numBytes, byte value)
  196. {
  197. var to = (byte*)destination;
  198. var pos = 0;
  199. unchecked
  200. {
  201. // 64bit blocks.
  202. #if UNITY_64
  203. while (numBytes >= 8)
  204. {
  205. *(ulong*)&to[pos] = ((ulong)value << 56) | ((ulong)value << 48) | ((ulong)value << 40) | ((ulong)value << 32)
  206. | ((ulong)value << 24) | ((ulong)value << 16) | ((ulong)value << 8) | value;
  207. numBytes -= 8;
  208. pos += 8;
  209. }
  210. #endif
  211. // 32bit blocks.
  212. while (numBytes >= 4)
  213. {
  214. *(uint*)&to[pos] = ((uint)value << 24) | ((uint)value << 16) | ((uint)value << 8) | value;
  215. numBytes -= 4;
  216. pos += 4;
  217. }
  218. // Remaining bytes.
  219. while (numBytes > 0)
  220. {
  221. to[pos] = value;
  222. numBytes -= 1;
  223. pos += 1;
  224. }
  225. }
  226. }
  227. /// <summary>
  228. /// Copy from <paramref name="source"/> to <paramref name="destination"/> all the bits that
  229. /// ARE set in <paramref name="mask"/>.
  230. /// </summary>
  231. /// <param name="destination">Memory to copy to.</param>
  232. /// <param name="source">Memory to copy from.</param>
  233. /// <param name="numBytes">Number of bytes to copy.</param>
  234. /// <param name="mask">Bitmask that determines which bits to copy. Bits that are set WILL be copied.</param>
  235. public static void MemCpyMasked(void* destination, void* source, int numBytes, void* mask)
  236. {
  237. var from = (byte*)source;
  238. var to = (byte*)destination;
  239. var bits = (byte*)mask;
  240. var pos = 0;
  241. unchecked
  242. {
  243. // Copy 64bit blocks.
  244. #if UNITY_64
  245. while (numBytes >= 8)
  246. {
  247. *(ulong*)(to + pos) &= ~*(ulong*)(bits + pos); // Preserve unmasked bits.
  248. *(ulong*)(to + pos) |= *(ulong*)(from + pos) & *(ulong*)(bits + pos); // Copy masked bits.
  249. numBytes -= 8;
  250. pos += 8;
  251. }
  252. #endif
  253. // Copy 32bit blocks.
  254. while (numBytes >= 4)
  255. {
  256. *(uint*)(to + pos) &= ~*(uint*)(bits + pos); // Preserve unmasked bits.
  257. *(uint*)(to + pos) |= *(uint*)(from + pos) & *(uint*)(bits + pos); // Copy masked bits.
  258. numBytes -= 4;
  259. pos += 4;
  260. }
  261. // Copy remaining bytes.
  262. while (numBytes > 0)
  263. {
  264. unchecked
  265. {
  266. to[pos] &= (byte)~bits[pos]; // Preserve unmasked bits.
  267. to[pos] |= (byte)(from[pos] & bits[pos]); // Copy masked bits.
  268. }
  269. numBytes -= 1;
  270. pos += 1;
  271. }
  272. }
  273. }
  274. /// <summary>
  275. /// Reads bits memory region as unsigned int, up to and including 32 bits, least-significant bit first (LSB).
  276. /// </summary>
  277. /// <param name="ptr">Pointer to memory region.</param>
  278. /// <param name="bitOffset">Offset in bits from the pointer to the start of the unsigned integer.</param>
  279. /// <param name="bitCount">Number of bits to read.</param>
  280. /// <returns>Read unsigned integer.</returns>
  281. public static uint ReadMultipleBitsAsUInt(void* ptr, uint bitOffset, uint bitCount)
  282. {
  283. if (ptr == null)
  284. throw new ArgumentNullException(nameof(ptr));
  285. if (bitCount > sizeof(int) * 8)
  286. throw new ArgumentException("Trying to read more than 32 bits as int", nameof(bitCount));
  287. // Shift the pointer up on larger bitmasks and retry.
  288. if (bitOffset > 32)
  289. {
  290. var newBitOffset = (int)bitOffset % 32;
  291. var intOffset = ((int)bitOffset - newBitOffset) / 32;
  292. ptr = (byte*)ptr + (intOffset * 4);
  293. bitOffset = (uint)newBitOffset;
  294. }
  295. // Bits out of byte.
  296. if (bitOffset + bitCount <= 8)
  297. {
  298. var value = *(byte*)ptr;
  299. value >>= (int)bitOffset;
  300. var mask = 0xFFu >> (8 - (int)bitCount);
  301. return value & mask;
  302. }
  303. // Bits out of short.
  304. if (bitOffset + bitCount <= 16)
  305. {
  306. var value = *(ushort*)ptr;
  307. value >>= (int)bitOffset;
  308. var mask = 0xFFFFu >> (16 - (int)bitCount);
  309. return value & mask;
  310. }
  311. // Bits out of int.
  312. if (bitOffset + bitCount <= 32)
  313. {
  314. var value = *(uint*)ptr;
  315. value >>= (int)bitOffset;
  316. var mask = 0xFFFFFFFFu >> (32 - (int)bitCount);
  317. return value & mask;
  318. }
  319. throw new NotImplementedException("Reading int straddling int boundary");
  320. }
  321. /// <summary>
  322. /// Writes unsigned int as bits to memory region, up to and including 32 bits, least-significant bit first (LSB).
  323. /// </summary>
  324. /// <param name="ptr">Pointer to memory region.</param>
  325. /// <param name="bitOffset">Offset in bits from the pointer to the start of the unsigned integer.</param>
  326. /// <param name="bitCount">Number of bits to read.</param>
  327. /// <param name="value">Value to write.</param>
  328. public static void WriteUIntAsMultipleBits(void* ptr, uint bitOffset, uint bitCount, uint value)
  329. {
  330. if (ptr == null)
  331. throw new ArgumentNullException(nameof(ptr));
  332. if (bitCount > sizeof(int) * 8)
  333. throw new ArgumentException("Trying to write more than 32 bits as int", nameof(bitCount));
  334. // Shift the pointer up on larger bitmasks and retry.
  335. if (bitOffset > 32)
  336. {
  337. var newBitOffset = (int)bitOffset % 32;
  338. var intOffset = ((int)bitOffset - newBitOffset) / 32;
  339. ptr = (byte*)ptr + (intOffset * 4);
  340. bitOffset = (uint)newBitOffset;
  341. }
  342. // Bits out of byte.
  343. if (bitOffset + bitCount <= 8)
  344. {
  345. var byteValue = (byte)value;
  346. byteValue <<= (int)bitOffset;
  347. var mask = ~((0xFFU >> (8 - (int)bitCount)) << (int)bitOffset);
  348. *(byte*)ptr = (byte)((*(byte*)ptr & mask) | byteValue);
  349. return;
  350. }
  351. // Bits out of short.
  352. if (bitOffset + bitCount <= 16)
  353. {
  354. var ushortValue = (ushort)value;
  355. ushortValue <<= (int)bitOffset;
  356. var mask = ~((0xFFFFU >> (16 - (int)bitCount)) << (int)bitOffset);
  357. *(ushort*)ptr = (ushort)((*(ushort*)ptr & mask) | ushortValue);
  358. return;
  359. }
  360. // Bits out of int.
  361. if (bitOffset + bitCount <= 32)
  362. {
  363. var uintValue = (uint)value;
  364. uintValue <<= (int)bitOffset;
  365. var mask = ~((0xFFFFFFFFU >> (32 - (int)bitCount)) << (int)bitOffset);
  366. *(uint*)ptr = (*(uint*)ptr & mask) | uintValue;
  367. return;
  368. }
  369. throw new NotImplementedException("Writing int straddling int boundary");
  370. }
  371. /// <summary>
  372. /// Reads bits memory region as two's complement integer, up to and including 32 bits, least-significant bit first (LSB).
  373. /// For example reading 0xff as 8 bits will result in -1.
  374. /// </summary>
  375. /// <param name="ptr">Pointer to memory region.</param>
  376. /// <param name="bitOffset">Offset in bits from the pointer to the start of the integer.</param>
  377. /// <param name="bitCount">Number of bits to read.</param>
  378. /// <returns>Read integer.</returns>
  379. public static int ReadTwosComplementMultipleBitsAsInt(void* ptr, uint bitOffset, uint bitCount)
  380. {
  381. // int is already represented as two's complement
  382. return (int)ReadMultipleBitsAsUInt(ptr, bitOffset, bitCount);
  383. }
  384. /// <summary>
  385. /// Writes bits memory region as two's complement integer, up to and including 32 bits, least-significant bit first (LSB).
  386. /// </summary>
  387. /// <param name="ptr">Pointer to memory region.</param>
  388. /// <param name="bitOffset">Offset in bits from the pointer to the start of the integer.</param>
  389. /// <param name="bitCount">Number of bits to read.</param>
  390. /// <param name="value">Value to write.</param>
  391. public static void WriteIntAsTwosComplementMultipleBits(void* ptr, uint bitOffset, uint bitCount, int value)
  392. {
  393. // int is already represented as two's complement, so write as-is
  394. WriteUIntAsMultipleBits(ptr, bitOffset, bitCount, (uint)value);
  395. }
  396. /// <summary>
  397. /// Reads bits memory region as excess-K integer where K is set to (2^bitCount)/2, up to and including 32 bits, least-significant bit first (LSB).
  398. /// For example reading 0 as 8 bits will result in -128. Reading 0xff as 8 bits will result in 127.
  399. /// </summary>
  400. /// <param name="ptr">Pointer to memory region.</param>
  401. /// <param name="bitOffset">Offset in bits from the pointer to the start of the integer.</param>
  402. /// <param name="bitCount">Number of bits to read.</param>
  403. /// <returns>Read integer.</returns>
  404. public static int ReadExcessKMultipleBitsAsInt(void* ptr, uint bitOffset, uint bitCount)
  405. {
  406. // https://en.wikipedia.org/wiki/Signed_number_representations#Offset_binary
  407. var value = (long)ReadMultipleBitsAsUInt(ptr, bitOffset, bitCount);
  408. var halfMax = (long)((1UL << (int)bitCount) / 2);
  409. return (int)(value - halfMax);
  410. }
  411. /// <summary>
  412. /// Writes bits memory region as excess-K integer where K is set to (2^bitCount)/2, up to and including 32 bits, least-significant bit first (LSB).
  413. /// </summary>
  414. /// <param name="ptr">Pointer to memory region.</param>
  415. /// <param name="bitOffset">Offset in bits from the pointer to the start of the integer.</param>
  416. /// <param name="bitCount">Number of bits to read.</param>
  417. /// <param name="value">Value to write.</param>
  418. public static void WriteIntAsExcessKMultipleBits(void* ptr, uint bitOffset, uint bitCount, int value)
  419. {
  420. // https://en.wikipedia.org/wiki/Signed_number_representations#Offset_binary
  421. var halfMax = (long)((1UL << (int)bitCount) / 2);
  422. var unsignedValue = halfMax + value;
  423. WriteUIntAsMultipleBits(ptr, bitOffset, bitCount, (uint)unsignedValue);
  424. }
  425. /// <summary>
  426. /// Reads bits memory region as normalized unsigned integer, up to and including 32 bits, least-significant bit first (LSB).
  427. /// For example reading 0 as 8 bits will result in 0.0f. Reading 0xff as 8 bits will result in 1.0f.
  428. /// </summary>
  429. /// <param name="ptr">Pointer to memory region.</param>
  430. /// <param name="bitOffset">Offset in bits from the pointer to the start of the unsigned integer.</param>
  431. /// <param name="bitCount">Number of bits to read.</param>
  432. /// <returns>Normalized unsigned integer.</returns>
  433. public static float ReadMultipleBitsAsNormalizedUInt(void* ptr, uint bitOffset, uint bitCount)
  434. {
  435. var uintValue = ReadMultipleBitsAsUInt(ptr, bitOffset, bitCount);
  436. var maxValue = (uint)((1UL << (int)bitCount) - 1);
  437. return NumberHelpers.UIntToNormalizedFloat(uintValue, 0, maxValue);
  438. }
  439. /// <summary>
  440. /// Writes bits memory region as normalized unsigned integer, up to and including 32 bits, least-significant bit first (LSB).
  441. /// </summary>
  442. /// <param name="ptr">Pointer to memory region.</param>
  443. /// <param name="bitOffset">Offset in bits from the pointer to the start of the unsigned integer.</param>
  444. /// <param name="bitCount">Number of bits to read.</param>
  445. /// <param name="value">Normalized value to write.</param>
  446. public static void WriteNormalizedUIntAsMultipleBits(void* ptr, uint bitOffset, uint bitCount, float value)
  447. {
  448. var maxValue = (uint)((1UL << (int)bitCount) - 1);
  449. var uintValue = NumberHelpers.NormalizedFloatToUInt(value, 0, maxValue);
  450. WriteUIntAsMultipleBits(ptr, bitOffset, bitCount, uintValue);
  451. }
  452. public static void SetBitsInBuffer(void* buffer, int byteOffset, int bitOffset, int sizeInBits, bool value)
  453. {
  454. if (buffer == null)
  455. throw new ArgumentException("A buffer must be provided to apply the bitmask on", nameof(buffer));
  456. if (sizeInBits < 0)
  457. throw new ArgumentException("Negative sizeInBits", nameof(sizeInBits));
  458. if (bitOffset < 0)
  459. throw new ArgumentException("Negative bitOffset", nameof(bitOffset));
  460. if (byteOffset < 0)
  461. throw new ArgumentException("Negative byteOffset", nameof(byteOffset));
  462. // If we're offset by more than a byte, adjust our pointers.
  463. if (bitOffset >= 8)
  464. {
  465. var skipBytes = bitOffset / 8;
  466. byteOffset += skipBytes;
  467. bitOffset %= 8;
  468. }
  469. var bytePos = (byte*)buffer + byteOffset;
  470. var sizeRemainingInBits = sizeInBits;
  471. // Handle first byte separately if unaligned to byte boundary.
  472. if (bitOffset != 0)
  473. {
  474. var mask = 0xFF << bitOffset;
  475. if (sizeRemainingInBits + bitOffset < 8)
  476. {
  477. mask &= 0xFF >> (8 - (sizeRemainingInBits + bitOffset));
  478. }
  479. if (value)
  480. *bytePos |= (byte)mask;
  481. else
  482. *bytePos &= (byte)~mask;
  483. ++bytePos;
  484. sizeRemainingInBits -= 8 - bitOffset;
  485. }
  486. // Handle full bytes in-between.
  487. while (sizeRemainingInBits >= 8)
  488. {
  489. *bytePos = value ? (byte)0xFF : (byte)0;
  490. ++bytePos;
  491. sizeRemainingInBits -= 8;
  492. }
  493. // Handle unaligned trailing byte, if present.
  494. if (sizeRemainingInBits > 0)
  495. {
  496. var mask = (byte)(0xFF >> 8 - sizeRemainingInBits);
  497. if (value)
  498. *bytePos |= mask;
  499. else
  500. *bytePos &= (byte)~mask;
  501. }
  502. Debug.Assert(bytePos <= (byte*)buffer +
  503. ComputeFollowingByteOffset((uint)byteOffset, (uint)bitOffset + (uint)sizeInBits));
  504. }
  505. public static void Swap<TValue>(ref TValue a, ref TValue b)
  506. {
  507. var temp = a;
  508. a = b;
  509. b = temp;
  510. }
  511. public static uint AlignNatural(uint offset, uint sizeInBytes)
  512. {
  513. var alignment = Math.Min(8, sizeInBytes);
  514. return offset.AlignToMultipleOf(alignment);
  515. }
  516. }
  517. }