remap-bitfield.js 2.7 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889
  1. // @ts-check
  2. /**
  3. * We must remap all the old bits to new bits for each set variant
  4. * Only arbitrary variants are considered as those are the only
  5. * ones that need to be re-sorted at this time
  6. *
  7. * An iterated process that removes and sets individual bits simultaneously
  8. * will not work because we may have a new bit that is also a later old bit
  9. * This means that we would be removing a previously set bit which we don't
  10. * want to do
  11. *
  12. * For example (assume `bN` = `1<<N`)
  13. * Given the "total" mapping `[[b1, b3], [b2, b4], [b3, b1], [b4, b2]]`
  14. * The mapping is "total" because:
  15. * 1. Every input and output is accounted for
  16. * 2. All combinations are unique
  17. * 3. No one input maps to multiple outputs and vice versa
  18. * And, given an offset with all bits set:
  19. * V = b1 | b2 | b3 | b4
  20. *
  21. * Let's explore the issue with removing and setting bits simultaneously:
  22. * V & ~b1 | b3 = b2 | b3 | b4
  23. * V & ~b2 | b4 = b3 | b4
  24. * V & ~b3 | b1 = b1 | b4
  25. * V & ~b4 | b2 = b1 | b2
  26. *
  27. * As you can see, we end up with the wrong result.
  28. * This is because we're removing a bit that was previously set.
  29. * And, thus the final result is missing b3 and b4.
  30. *
  31. * Now, let's explore the issue with removing the bits first:
  32. * V & ~b1 = b2 | b3 | b4
  33. * V & ~b2 = b3 | b4
  34. * V & ~b3 = b4
  35. * V & ~b4 = 0
  36. *
  37. * And then setting the bits:
  38. * V | b3 = b3
  39. * V | b4 = b3 | b4
  40. * V | b1 = b1 | b3 | b4
  41. * V | b2 = b1 | b2 | b3 | b4
  42. *
  43. * We get the correct result because we're not removing any bits that were
  44. * previously set thus properly remapping the bits to the new order
  45. *
  46. * To collect this into a single operation that can be done simultaneously
  47. * we must first create a mask for the old bits that are set and a mask for
  48. * the new bits that are set. Then we can remove the old bits and set the new
  49. * bits simultaneously in a "single" operation like so:
  50. * OldMask = b1 | b2 | b3 | b4
  51. * NewMask = b3 | b4 | b1 | b2
  52. *
  53. * So this:
  54. * V & ~oldMask | newMask
  55. *
  56. * Expands to this:
  57. * V & ~b1 & ~b2 & ~b3 & ~b4 | b3 | b4 | b1 | b2
  58. *
  59. * Which becomes this:
  60. * b1 | b2 | b3 | b4
  61. *
  62. * Which is the correct result!
  63. *
  64. * @param {bigint} num
  65. * @param {[bigint, bigint][]} mapping
  66. */ "use strict";
  67. Object.defineProperty(exports, "__esModule", {
  68. value: true
  69. });
  70. Object.defineProperty(exports, "remapBitfield", {
  71. enumerable: true,
  72. get: function() {
  73. return remapBitfield;
  74. }
  75. });
  76. function remapBitfield(num, mapping) {
  77. // Create masks for the old and new bits that are set
  78. let oldMask = 0n;
  79. let newMask = 0n;
  80. for (let [oldBit, newBit] of mapping){
  81. if (num & oldBit) {
  82. oldMask = oldMask | oldBit;
  83. newMask = newMask | newBit;
  84. }
  85. }
  86. // Remove all old bits
  87. // Set all new bits
  88. return num & ~oldMask | newMask;
  89. }