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.

169 lines
3.8 KiB

4 years ago
  1. var hpack = require('../hpack');
  2. var utils = hpack.utils;
  3. var huffman = hpack.huffman.decode;
  4. var assert = utils.assert;
  5. var OffsetBuffer = require('obuf');
  6. function Decoder() {
  7. this.buffer = new OffsetBuffer();
  8. this.bitOffset = 0;
  9. // Used internally in decodeStr
  10. this._huffmanNode = null;
  11. }
  12. module.exports = Decoder;
  13. Decoder.create = function create() {
  14. return new Decoder();
  15. };
  16. Decoder.prototype.isEmpty = function isEmpty() {
  17. return this.buffer.isEmpty();
  18. };
  19. Decoder.prototype.push = function push(chunk) {
  20. this.buffer.push(chunk);
  21. };
  22. Decoder.prototype.decodeBit = function decodeBit() {
  23. // Need at least one octet
  24. assert(this.buffer.has(1), 'Buffer too small for an int');
  25. var octet;
  26. var offset = this.bitOffset;
  27. if (++this.bitOffset === 8) {
  28. octet = this.buffer.readUInt8();
  29. this.bitOffset = 0;
  30. } else {
  31. octet = this.buffer.peekUInt8();
  32. }
  33. return (octet >>> (7 - offset)) & 1;
  34. };
  35. // Just for testing
  36. Decoder.prototype.skipBits = function skipBits(n) {
  37. this.bitOffset += n;
  38. this.buffer.skip(this.bitOffset >> 3);
  39. this.bitOffset &= 0x7;
  40. };
  41. Decoder.prototype.decodeInt = function decodeInt() {
  42. // Need at least one octet
  43. assert(this.buffer.has(1), 'Buffer too small for an int');
  44. var prefix = 8 - this.bitOffset;
  45. // We are going to end up octet-aligned
  46. this.bitOffset = 0;
  47. var max = (1 << prefix) - 1;
  48. var octet = this.buffer.readUInt8() & max;
  49. // Fast case - int fits into the prefix
  50. if (octet !== max)
  51. return octet;
  52. // TODO(indutny): what about > 32bit numbers?
  53. var res = 0;
  54. var isLast = false;
  55. var len = 0;
  56. do {
  57. octet = this.buffer.readUInt8();
  58. isLast = (octet & 0x80) === 0;
  59. res <<= 7;
  60. res |= octet & 0x7f;
  61. len++;
  62. } while (!isLast);
  63. assert(isLast, 'Incomplete data for multi-octet integer');
  64. assert(len <= 4, 'Integer does not fit into 32 bits');
  65. // Reverse bits
  66. res = (res >>> 21) |
  67. (((res >> 14) & 0x7f) << 7) |
  68. (((res >> 7) & 0x7f) << 14) |
  69. ((res & 0x7f) << 21);
  70. res >>= (4 - len) * 7;
  71. // Append prefix max
  72. res += max;
  73. return res;
  74. };
  75. Decoder.prototype.decodeHuffmanWord = function decodeHuffmanWord(input,
  76. inputBits,
  77. out) {
  78. var root = huffman;
  79. var node = this._huffmanNode;
  80. var word = input;
  81. var bits = inputBits;
  82. for (; bits > 0; word &= (1 << bits) - 1) {
  83. // Nudge the word bit length to match it
  84. for (var i = Math.max(0, bits - 8); i < bits; i++) {
  85. var subnode = node[word >>> i];
  86. if (typeof subnode !== 'number') {
  87. node = subnode;
  88. bits = i;
  89. break;
  90. }
  91. if (subnode === 0)
  92. continue;
  93. // Word bit length should match
  94. if ((subnode >>> 9) !== bits - i) {
  95. subnode = 0;
  96. continue;
  97. }
  98. var octet = subnode & 0x1ff;
  99. assert(octet !== 256, 'EOS in encoding');
  100. out.push(octet);
  101. node = root;
  102. bits = i;
  103. break;
  104. }
  105. if (subnode === 0)
  106. break;
  107. }
  108. this._huffmanNode = node;
  109. return bits;
  110. };
  111. Decoder.prototype.decodeStr = function decodeStr() {
  112. var isHuffman = this.decodeBit();
  113. var len = this.decodeInt();
  114. assert(this.buffer.has(len), 'Not enough octets for string');
  115. if (!isHuffman)
  116. return this.buffer.take(len);
  117. this._huffmanNode = huffman;
  118. var out = [];
  119. var word = 0;
  120. var bits = 0;
  121. var lastKey = 0;
  122. for (var i = 0; i < len; i++) {
  123. word <<= 8;
  124. word |= this.buffer.readUInt8();
  125. bits += 8;
  126. bits = this.decodeHuffmanWord(word, bits, out);
  127. lastKey = word >> bits;
  128. word &= (1 << bits) - 1;
  129. }
  130. assert(this._huffmanNode === huffman, '8-bit EOS');
  131. assert(word + 1 === (1 << bits), 'Final sequence is not EOS');
  132. this._huffmanNode = null;
  133. return out;
  134. };