-
Notifications
You must be signed in to change notification settings - Fork 0
/
transcoder.ts
154 lines (137 loc) · 5.19 KB
/
transcoder.ts
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
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
const SEQ_LEN = 2;
const SES_LEN = 2;
/**
* Decodes ClientId.
* Expects message in format: `<1B length><Id of given length><Other data>`
* @param buf {Buffer} Input message
* @returns {[Buffer, Buffer]} First is the Id, then the other data.
*/
export function decodeClientId(buf: Buffer): [Buffer, Buffer] {
if (!Buffer.isBuffer(buf)) {
throw new TypeError("Input should be Buffer");
}
if (buf.length < 2) {
throw new Error("buffer too short");
}
const l = buf.readUInt8(0);
if (buf.length < l + 1) {
throw new Error("buffer too short");
}
return [buf.slice(1, 1 + l), buf.slice(1 + l)];
}
/**
* Encodes ClientId.
* Creates the following format: `<1B length><Id of given length><Other data>`
* @param clientId {Buffer} Client Id in the form of a Buffer. Cannot be longer than 255B.
* @param rest {Buffer} Remaining data to encode
* @returns {Buffer} The encoded Buffer
*/
export function encodeClientId(clientId: Buffer, rest = Buffer.from("")): Buffer {
if (!Buffer.isBuffer(clientId) || !Buffer.isBuffer(rest)) {
throw new TypeError("Input should be Buffer");
}
if (clientId.length > 255) {
throw new Error(`clientId too large${clientId}`);
}
return Buffer.concat([Buffer.from([clientId.length]), clientId, rest]);
}
/**
* Decode sequence number and acknowledgement numbers from the provided Buffer. Expects numbers to be encoded in Little Endian.
* @param buf {Buffer} Buffer to decode.
* @returns {[number, Array<number>, Buffer]} The sequence number, acknowledgement numbers and remaining message.
*/
export function decodeSeqAck(buf: Buffer): [number, Array<number>, Buffer] {
if (!Buffer.isBuffer(buf)) {
throw new TypeError("Input should be Buffer");
}
let requiredLengthMin = SEQ_LEN + 1;
if (buf.length < requiredLengthMin) {
throw new Error(`Buffer too short. is:${buf.length} min:${requiredLengthMin}`);
}
let offset = 0;
const seq = buf.readUInt16LE(offset);
offset += SEQ_LEN;
const ackLen = buf.readUInt8(offset);
offset += 1;
requiredLengthMin += ackLen * SEQ_LEN;
if (buf.length < requiredLengthMin) {
throw new Error(`Buffer too short. is:${buf.length} min:${requiredLengthMin}`);
}
const acks = [];
for (let i = 0; i < ackLen; i++) {
const ack = buf.readUInt16LE(offset);
offset += SEQ_LEN;
if (ack !== 0) {
acks.push(ack);
continue;
}
requiredLengthMin += 2 * SEQ_LEN;
if (buf.length < requiredLengthMin) {
throw new Error(`Buffer too short. is:${buf.length} min:${requiredLengthMin}`);
}
let ack1 = buf.readUInt16LE(offset);
offset += SEQ_LEN;
const ack2 = buf.readUInt16LE(offset);
offset += SEQ_LEN;
for (; ack1 <= ack2; ack1++) {
acks.push(ack1);
}
}
return [seq, acks, buf.slice(offset)];
}
/**
* Encode sequence number and acknowledgement numbers together with the remaining Buffer. Numbers are encoded with Little Endian.
* @param seq {number} Sequence number. Must be in [0, 65535].
* @param acks {Array<number>} Array of acknowledgements. Each acknowledgement must be in [0, 65535].
* @param rest {Buffer} Remaining message
* @returns {Buffer} The encoded message
*/
export function encodeSeqAck(seq: number, acks: Array<number> = [], rest: Buffer = Buffer.alloc(0)): Buffer {
if (!Buffer.isBuffer(rest)) {
throw new TypeError("Input should be Buffer");
}
if (acks.length > 255) {
throw new Error("seq too high");
}
acks.map((v) => {
if (!Number.isInteger(v) || v > 65535 || v < 0) {
throw new Error(`All acks must be int in [0,65535]. This is:${v} of type:${typeof v}`);
}
});
const output = Buffer.alloc(SEQ_LEN + 1);
output.writeUInt16LE(seq, 0);
output.writeUInt8(acks.length, SEQ_LEN);
const acksBuf = Buffer.from(new Uint16Array(acks).buffer);
return Buffer.concat([output, acksBuf, rest]);
}
/**
* Decode SessionId from Buffer. Reads the first 2B as UInt16 using Little Endian.
* @param buf {Buffer} Input Buffer
* @returns {[number, Buffer]} The decoded SessionId and the remaining message.
*/
export function decodeSessionId(buf: Buffer): [number, Buffer] {
if (!Buffer.isBuffer(buf)) {
throw new TypeError("Input should be Buffer");
}
if (buf.length < SES_LEN) {
throw new Error("Buffer too short");
}
return [buf.readUInt16LE(0), buf.slice(SES_LEN)];
}
/**
* Encodes SessionId together with the remaining Buffer. Writes UInt16 using Little Endian.
* @param sessionId {number} The SessionId to write. Must be in [0, 65535].
* @param rest {Buffer} Remaining message to encode
* @returns {Buffer} Encoded message.
*/
export function encodeSessionId(sessionId: number, rest = Buffer.allocUnsafe(0)): Buffer {
if (!Buffer.isBuffer(rest)) {
throw new TypeError("Input should be Buffer");
}
if (!Number.isInteger(sessionId) || sessionId > 65535 || sessionId < 0) {
throw new Error("SessionId must be in [1, 65535]");
}
const output = Buffer.alloc(SES_LEN);
output.writeUInt16LE(sessionId, 0);
return Buffer.concat([output, rest]);
}