-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathfastlz_decompression.ts
108 lines (94 loc) · 2.18 KB
/
fastlz_decompression.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
export function fastLZDecompressSync(input: Uint8Array): Uint8Array {
const output: Uint8Array = new Uint8Array(input.length * 2);
let inp = 0;
let out = 0;
let level: 1 | 2;
switch (input[inp] >>> 5) {
case 0:
level = 1;
break;
case 1:
level = 2;
break;
default:
throw new Error(
`Unsupported FastLZ block format ${input[inp] >>> 5}`,
);
}
while (inp < input.length) {
const { bytesRead, bytesWritten } = fastLZDecompressBlock(
level,
input,
inp,
output,
out,
inp === 0,
);
inp += bytesRead;
out += bytesWritten;
}
return output.subarray(0, out);
}
const FASTLZ_MAX_OFFSET_L1 = 8191; // 13 bits 1s
function fastLZDecompressBlock(
level: 1 | 2,
input: Uint8Array,
inp: number,
output: Uint8Array,
out: number,
isFirstBlock: boolean,
): {
bytesRead: number;
bytesWritten: number;
} {
let bytesRead = 0;
let bytesWritten = 0;
const instruction = isFirstBlock ? 0 : input[inp] >>> 5;
const ctrl = input[inp] & 0b11111;
inp++;
bytesRead++;
if (instruction === 0b000) {
// Literal run: 000LLLLL
const len = ctrl + 1;
output.set(input.subarray(inp, inp + len), out);
bytesRead += len;
bytesWritten += len;
} else {
let matchLen: number = instruction + 2;
let offset: number = ctrl << 8;
// Level 1:
// Long match: 111OOOOO LLLLLLLL OOOOOOOO
// Short match: LLLOOOOO OOOOOOOO
// Level 2:
// Long match: 111OOOOO LLLLLLLL [... LLLLLLLL] OOOOOOOO [QQQQQQQQ QQQQQQQQ]
// Short match: LLLOOOOO OOOOOOOO [QQQQQQQQ QQQQQQQQ]
if (instruction === 0b111) {
if (level === 1) {
matchLen += input[inp++];
bytesRead++;
} else {
let code: number;
do {
code = input[inp++];
bytesRead++;
matchLen += code;
} while (code === 0xff);
}
}
offset += input[inp++];
bytesRead++;
if (level === 2 && offset === FASTLZ_MAX_OFFSET_L1) {
// Offset extended by another 2 bytes
offset += input[inp++] << 8;
offset += input[inp++];
bytesRead += 2;
}
// Offset is a back reference from the last written byte
let src = out - offset - 1;
for (let i = 0; i < matchLen; i++) {
output[out++] = output[src++];
}
bytesWritten += matchLen;
}
return { bytesRead, bytesWritten };
}