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