1 | var hpack = require('../hpack');
|
---|
2 | var utils = hpack.utils;
|
---|
3 | var huffman = hpack.huffman.encode;
|
---|
4 | var assert = utils.assert;
|
---|
5 |
|
---|
6 | var WBuf = require('wbuf');
|
---|
7 |
|
---|
8 | function Encoder() {
|
---|
9 | this.buffer = new WBuf();
|
---|
10 | this.word = 0;
|
---|
11 | this.bitOffset = 0;
|
---|
12 | }
|
---|
13 | module.exports = Encoder;
|
---|
14 |
|
---|
15 | Encoder.create = function create() {
|
---|
16 | return new Encoder();
|
---|
17 | };
|
---|
18 |
|
---|
19 | Encoder.prototype.render = function render() {
|
---|
20 | return this.buffer.render();
|
---|
21 | };
|
---|
22 |
|
---|
23 | Encoder.prototype.encodeBit = function encodeBit(bit) {
|
---|
24 | var octet;
|
---|
25 |
|
---|
26 | this.word <<= 1;
|
---|
27 | this.word |= bit;
|
---|
28 | this.bitOffset++;
|
---|
29 |
|
---|
30 | if (this.bitOffset === 8) {
|
---|
31 | this.buffer.writeUInt8(this.word);
|
---|
32 | this.word = 0;
|
---|
33 | this.bitOffset = 0;
|
---|
34 | }
|
---|
35 | };
|
---|
36 |
|
---|
37 | Encoder.prototype.encodeBits = function encodeBits(bits, len) {
|
---|
38 | var left = bits;
|
---|
39 | var leftLen = len;
|
---|
40 |
|
---|
41 | while (leftLen > 0) {
|
---|
42 | var avail = Math.min(leftLen, 8 - this.bitOffset);
|
---|
43 | var toWrite = left >>> (leftLen - avail);
|
---|
44 |
|
---|
45 | if (avail === 8) {
|
---|
46 | this.buffer.writeUInt8(toWrite);
|
---|
47 | } else {
|
---|
48 | this.word <<= avail;
|
---|
49 | this.word |= toWrite;
|
---|
50 | this.bitOffset += avail;
|
---|
51 | if (this.bitOffset === 8) {
|
---|
52 | this.buffer.writeUInt8(this.word);
|
---|
53 | this.word = 0;
|
---|
54 | this.bitOffset = 0;
|
---|
55 | }
|
---|
56 | }
|
---|
57 |
|
---|
58 | leftLen -= avail;
|
---|
59 | left &= (1 << leftLen) - 1;
|
---|
60 | }
|
---|
61 | };
|
---|
62 |
|
---|
63 | // Just for testing
|
---|
64 | Encoder.prototype.skipBits = function skipBits(num) {
|
---|
65 | this.bitOffset += num;
|
---|
66 | this.buffer.skip(this.bitOffset >> 3);
|
---|
67 | this.bitOffset &= 0x7;
|
---|
68 | };
|
---|
69 |
|
---|
70 | Encoder.prototype.encodeInt = function encodeInt(num) {
|
---|
71 | var prefix = 8 - this.bitOffset;
|
---|
72 |
|
---|
73 | // We are going to end up octet-aligned
|
---|
74 | this.bitOffset = 0;
|
---|
75 |
|
---|
76 | var max = (1 << prefix) - 1;
|
---|
77 |
|
---|
78 | // Fast case - int fits into the prefix
|
---|
79 | if (num < max) {
|
---|
80 | this.buffer.writeUInt8((this.word << prefix) | num);
|
---|
81 | return octet;
|
---|
82 | }
|
---|
83 |
|
---|
84 | var left = num - max;
|
---|
85 | this.buffer.writeUInt8((this.word << prefix) | max);
|
---|
86 | do {
|
---|
87 | var octet = left & 0x7f;
|
---|
88 | left >>= 7;
|
---|
89 | if (left !== 0)
|
---|
90 | octet |= 0x80;
|
---|
91 |
|
---|
92 | this.buffer.writeUInt8(octet);
|
---|
93 | } while (left !== 0);
|
---|
94 | };
|
---|
95 |
|
---|
96 | Encoder.prototype.encodeStr = function encodeStr(value, isHuffman) {
|
---|
97 | this.encodeBit(isHuffman ? 1 : 0);
|
---|
98 |
|
---|
99 | if (!isHuffman) {
|
---|
100 | this.buffer.reserve(value.length + 1);
|
---|
101 | this.encodeInt(value.length);
|
---|
102 | for (var i = 0; i < value.length; i++)
|
---|
103 | this.buffer.writeUInt8(value[i]);
|
---|
104 | return;
|
---|
105 | }
|
---|
106 |
|
---|
107 | var codes = [];
|
---|
108 | var len = 0;
|
---|
109 | var pad = 0;
|
---|
110 |
|
---|
111 | for (var i = 0; i < value.length; i++) {
|
---|
112 | var code = huffman[value[i]];
|
---|
113 | codes.push(code);
|
---|
114 | len += code[0];
|
---|
115 | }
|
---|
116 | if (len % 8 !== 0)
|
---|
117 | pad = 8 - (len % 8);
|
---|
118 | len += pad;
|
---|
119 |
|
---|
120 | this.buffer.reserve((len / 8) + 1);
|
---|
121 | this.encodeInt(len / 8);
|
---|
122 | for (var i = 0; i < codes.length; i++) {
|
---|
123 | var code = codes[i];
|
---|
124 | this.encodeBits(code[1], code[0]);
|
---|
125 | }
|
---|
126 |
|
---|
127 | // Append padding
|
---|
128 | this.encodeBits(0xff >>> (8 - pad), pad);
|
---|
129 | };
|
---|