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.

155 lines
3.6 KiB

4 years ago
  1. // Copyright 2012 The Obvious Corporation.
  2. /*
  3. * bits: Bitwise buffer utilities. The utilities here treat a buffer
  4. * as a little-endian bigint, so the lowest-order bit is bit #0 of
  5. * `buffer[0]`, and the highest-order bit is bit #7 of
  6. * `buffer[buffer.length - 1]`.
  7. */
  8. /*
  9. * Modules used
  10. */
  11. "use strict";
  12. /*
  13. * Exported bindings
  14. */
  15. /**
  16. * Extracts the given number of bits from the buffer at the indicated
  17. * index, returning a simple number as the result. If bits are requested
  18. * that aren't covered by the buffer, the `defaultBit` is used as their
  19. * value.
  20. *
  21. * The `bitLength` must be no more than 32. The `defaultBit` if not
  22. * specified is taken to be `0`.
  23. */
  24. Object.defineProperty(exports, "__esModule", {
  25. value: true
  26. });
  27. exports.extract = extract;
  28. exports.inject = inject;
  29. exports.getSign = getSign;
  30. exports.highOrder = highOrder;
  31. function extract(buffer, bitIndex, bitLength, defaultBit) {
  32. if (bitLength < 0 || bitLength > 32) {
  33. throw new Error("Bad value for bitLength.");
  34. }
  35. if (defaultBit === undefined) {
  36. defaultBit = 0;
  37. } else if (defaultBit !== 0 && defaultBit !== 1) {
  38. throw new Error("Bad value for defaultBit.");
  39. }
  40. var defaultByte = defaultBit * 0xff;
  41. var result = 0; // All starts are inclusive. The {endByte, endBit} pair is exclusive, but
  42. // if endBit !== 0, then endByte is inclusive.
  43. var lastBit = bitIndex + bitLength;
  44. var startByte = Math.floor(bitIndex / 8);
  45. var startBit = bitIndex % 8;
  46. var endByte = Math.floor(lastBit / 8);
  47. var endBit = lastBit % 8;
  48. if (endBit !== 0) {
  49. // `(1 << endBit) - 1` is the mask of all bits up to but not including
  50. // the endBit.
  51. result = get(endByte) & (1 << endBit) - 1;
  52. }
  53. while (endByte > startByte) {
  54. endByte--;
  55. result = result << 8 | get(endByte);
  56. }
  57. result >>>= startBit;
  58. return result;
  59. function get(index) {
  60. var result = buffer[index];
  61. return result === undefined ? defaultByte : result;
  62. }
  63. }
  64. /**
  65. * Injects the given bits into the given buffer at the given index. Any
  66. * bits in the value beyond the length to set are ignored.
  67. */
  68. function inject(buffer, bitIndex, bitLength, value) {
  69. if (bitLength < 0 || bitLength > 32) {
  70. throw new Error("Bad value for bitLength.");
  71. }
  72. var lastByte = Math.floor((bitIndex + bitLength - 1) / 8);
  73. if (bitIndex < 0 || lastByte >= buffer.length) {
  74. throw new Error("Index out of range.");
  75. } // Just keeping it simple, until / unless profiling shows that this
  76. // is a problem.
  77. var atByte = Math.floor(bitIndex / 8);
  78. var atBit = bitIndex % 8;
  79. while (bitLength > 0) {
  80. if (value & 1) {
  81. buffer[atByte] |= 1 << atBit;
  82. } else {
  83. buffer[atByte] &= ~(1 << atBit);
  84. }
  85. value >>= 1;
  86. bitLength--;
  87. atBit = (atBit + 1) % 8;
  88. if (atBit === 0) {
  89. atByte++;
  90. }
  91. }
  92. }
  93. /**
  94. * Gets the sign bit of the given buffer.
  95. */
  96. function getSign(buffer) {
  97. return buffer[buffer.length - 1] >>> 7;
  98. }
  99. /**
  100. * Gets the zero-based bit number of the highest-order bit with the
  101. * given value in the given buffer.
  102. *
  103. * If the buffer consists entirely of the other bit value, then this returns
  104. * `-1`.
  105. */
  106. function highOrder(bit, buffer) {
  107. var length = buffer.length;
  108. var fullyWrongByte = (bit ^ 1) * 0xff; // the other-bit extended to a full byte
  109. while (length > 0 && buffer[length - 1] === fullyWrongByte) {
  110. length--;
  111. }
  112. if (length === 0) {
  113. // Degenerate case. The buffer consists entirely of ~bit.
  114. return -1;
  115. }
  116. var byteToCheck = buffer[length - 1];
  117. var result = length * 8 - 1;
  118. for (var i = 7; i > 0; i--) {
  119. if ((byteToCheck >> i & 1) === bit) {
  120. break;
  121. }
  122. result--;
  123. }
  124. return result;
  125. }