@flighthq/compression 0.3.0-next.1494.7333e82
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/contract.d.ts +3 -0
- package/dist/contract.d.ts.map +1 -0
- package/dist/contract.js +3 -0
- package/dist/contract.js.map +1 -0
- package/dist/decompressor.d.ts +6 -0
- package/dist/decompressor.d.ts.map +1 -0
- package/dist/decompressor.js +22 -0
- package/dist/decompressor.js.map +1 -0
- package/dist/deflate.d.ts +4 -0
- package/dist/deflate.d.ts.map +1 -0
- package/dist/deflate.js +258 -0
- package/dist/deflate.js.map +1 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +3 -0
- package/dist/index.js.map +1 -0
- package/package.json +46 -0
- package/src/decompressor.test.ts +58 -0
- package/src/deflate.test.ts +120 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"contract.d.ts","sourceRoot":"","sources":["../src/contract.ts"],"names":[],"mappings":"AAAA,cAAc,gBAAgB,CAAC;AAC/B,cAAc,WAAW,CAAC"}
|
package/dist/contract.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"contract.js","sourceRoot":"","sources":["../src/contract.ts"],"names":[],"mappings":"AAAA,cAAc,gBAAgB,CAAC;AAC/B,cAAc,WAAW,CAAC"}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import type { Compression, Decompressor } from '@flighthq/types/contract';
|
|
2
|
+
export declare function getDecompressor(compression: Compression): Decompressor | null;
|
|
3
|
+
export declare function hasDecompressor(compression: Compression): boolean;
|
|
4
|
+
export declare function registerDecompressor(compression: Compression, decompress: Decompressor): void;
|
|
5
|
+
export declare function unregisterDecompressor(compression: Compression): void;
|
|
6
|
+
//# sourceMappingURL=decompressor.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"decompressor.d.ts","sourceRoot":"","sources":["../src/decompressor.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,YAAY,EAAE,MAAM,0BAA0B,CAAC;AAU1E,wBAAgB,eAAe,CAAC,WAAW,EAAE,WAAW,GAAG,YAAY,GAAG,IAAI,CAE7E;AAED,wBAAgB,eAAe,CAAC,WAAW,EAAE,WAAW,GAAG,OAAO,CAEjE;AAED,wBAAgB,oBAAoB,CAAC,WAAW,EAAE,WAAW,EAAE,UAAU,EAAE,YAAY,GAAG,IAAI,CAE7F;AAED,wBAAgB,sBAAsB,CAAC,WAAW,EAAE,WAAW,GAAG,IAAI,CAErE"}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
// The one registry every container format resolves through. A caller registers an algorithm once and
|
|
2
|
+
// every consumer that carries a compressed body — SWF's `CWS`/`ZWS`, AWD2's compressed blocks, anything
|
|
3
|
+
// added later — can read it, rather than each format owning a private registry keyed by its own
|
|
4
|
+
// vocabulary and asking the caller to register the same function twice.
|
|
5
|
+
//
|
|
6
|
+
// It starts empty and is only ever filled by an explicit `register*` call, so importing a codec module
|
|
7
|
+
// registers nothing and a build that never decompresses pays for no codec. Registration is
|
|
8
|
+
// last-write-wins, which is what lets a host replace a portable decoder with a native or wasm one.
|
|
9
|
+
export function getDecompressor(compression) {
|
|
10
|
+
return _decompressors.get(compression) ?? null;
|
|
11
|
+
}
|
|
12
|
+
export function hasDecompressor(compression) {
|
|
13
|
+
return _decompressors.has(compression);
|
|
14
|
+
}
|
|
15
|
+
export function registerDecompressor(compression, decompress) {
|
|
16
|
+
_decompressors.set(compression, decompress);
|
|
17
|
+
}
|
|
18
|
+
export function unregisterDecompressor(compression) {
|
|
19
|
+
_decompressors.delete(compression);
|
|
20
|
+
}
|
|
21
|
+
const _decompressors = new Map();
|
|
22
|
+
//# sourceMappingURL=decompressor.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"decompressor.js","sourceRoot":"","sources":["../src/decompressor.ts"],"names":[],"mappings":"AAEA,qGAAqG;AACrG,wGAAwG;AACxG,gGAAgG;AAChG,wEAAwE;AACxE,EAAE;AACF,uGAAuG;AACvG,2FAA2F;AAC3F,mGAAmG;AACnG,MAAM,UAAU,eAAe,CAAC,WAAwB;IACtD,OAAO,cAAc,CAAC,GAAG,CAAC,WAAW,CAAC,IAAI,IAAI,CAAC;AACjD,CAAC;AAED,MAAM,UAAU,eAAe,CAAC,WAAwB;IACtD,OAAO,cAAc,CAAC,GAAG,CAAC,WAAW,CAAC,CAAC;AACzC,CAAC;AAED,MAAM,UAAU,oBAAoB,CAAC,WAAwB,EAAE,UAAwB;IACrF,cAAc,CAAC,GAAG,CAAC,WAAW,EAAE,UAAU,CAAC,CAAC;AAC9C,CAAC;AAED,MAAM,UAAU,sBAAsB,CAAC,WAAwB;IAC7D,cAAc,CAAC,MAAM,CAAC,WAAW,CAAC,CAAC;AACrC,CAAC;AAED,MAAM,cAAc,GAAG,IAAI,GAAG,EAA6B,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"deflate.d.ts","sourceRoot":"","sources":["../src/deflate.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,0BAA0B,CAAC;AAe7D,eAAO,MAAM,cAAc,EAAE,YAc5B,CAAC;AAKF,wBAAgB,2BAA2B,IAAI,IAAI,CAElD"}
|
package/dist/deflate.js
ADDED
|
@@ -0,0 +1,258 @@
|
|
|
1
|
+
import { Compression } from '@flighthq/types/contract';
|
|
2
|
+
import { registerDecompressor } from './decompressor';
|
|
3
|
+
// Dependency-free, synchronous RFC 1951 (DEFLATE) and RFC 1950 (zlib) decoding. Kept in its own module so
|
|
4
|
+
// the registry stays tree-shakable: a bundle that never calls `registerDeflateDecompressor` pays nothing
|
|
5
|
+
// for this Huffman decoder. Sync because inflate is a straight-line state machine, which keeps every
|
|
6
|
+
// synchronous parser that resolves through the registry synchronous too; a host with a faster native or
|
|
7
|
+
// wasm codec registers that instead.
|
|
8
|
+
//
|
|
9
|
+
// Real containers mix the two framings — SWF's `CWS` body and Away3D's `ByteArray.compress()` are zlib
|
|
10
|
+
// (a 2-byte header, the DEFLATE stream, an Adler-32), while other producers emit the bare stream — so a
|
|
11
|
+
// zlib header is detected and skipped when present and the input is otherwise read as raw DEFLATE.
|
|
12
|
+
// `uncompressedLength` is unused: DEFLATE encodes its own end and the output buffer grows to fit.
|
|
13
|
+
export const inflateDeflate = (compressed) => {
|
|
14
|
+
const input = compressed;
|
|
15
|
+
// A zlib header: the low nibble of CMF is the compression method (8 = deflate) and the big-endian
|
|
16
|
+
// (CMF,FLG) pair is a multiple of 31. A preset dictionary (FDICT) is not supported.
|
|
17
|
+
let start = 0;
|
|
18
|
+
if (input.length >= 2 && (input[0] & 0x0f) === 8 && (((input[0] << 8) | input[1]) & 0xffff) % 31 === 0) {
|
|
19
|
+
if ((input[1] & 0x20) !== 0)
|
|
20
|
+
return null;
|
|
21
|
+
start = 2;
|
|
22
|
+
}
|
|
23
|
+
try {
|
|
24
|
+
return rawInflate(input, start);
|
|
25
|
+
}
|
|
26
|
+
catch {
|
|
27
|
+
return null;
|
|
28
|
+
}
|
|
29
|
+
};
|
|
30
|
+
// Registers `inflateDeflate` for `Compression.Deflate`, which is what lets every consumer read a
|
|
31
|
+
// zlib-or-raw compressed body. Opt-in, so the codec is only bundled when a caller asks for it.
|
|
32
|
+
// Idempotent; last registration wins.
|
|
33
|
+
export function registerDeflateDecompressor() {
|
|
34
|
+
registerDecompressor(Compression.Deflate, inflateDeflate);
|
|
35
|
+
}
|
|
36
|
+
// RFC 1951 length codes 257-285: the base copy length and the number of extra bits that follow.
|
|
37
|
+
const LENGTH_BASE = [
|
|
38
|
+
3, 4, 5, 6, 7, 8, 9, 10, 11, 13, 15, 17, 19, 23, 27, 31, 35, 43, 51, 59, 67, 83, 99, 115, 131, 163, 195, 227, 258,
|
|
39
|
+
];
|
|
40
|
+
const LENGTH_EXTRA = [0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 5, 0];
|
|
41
|
+
// RFC 1951 distance codes 0-29: the base back-distance and the number of extra bits that follow.
|
|
42
|
+
const DISTANCE_BASE = [
|
|
43
|
+
1, 2, 3, 4, 5, 7, 9, 13, 17, 25, 33, 49, 65, 97, 129, 193, 257, 385, 513, 769, 1025, 1537, 2049, 3073, 4097, 6145,
|
|
44
|
+
8193, 12289, 16385, 24577,
|
|
45
|
+
];
|
|
46
|
+
const DISTANCE_EXTRA = [
|
|
47
|
+
0, 0, 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, 8, 9, 9, 10, 10, 11, 11, 12, 12, 13, 13,
|
|
48
|
+
];
|
|
49
|
+
// The order in which the 19 code-length-code lengths are written in a dynamic-Huffman block header.
|
|
50
|
+
const CODE_LENGTH_ORDER = [16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15];
|
|
51
|
+
// An LSB-first bit reader over the DEFLATE stream, plus the growable output buffer. Throwing on overrun
|
|
52
|
+
// keeps the decode a straight line; inflateAwdDeflate turns the throw into a null return.
|
|
53
|
+
class InflateState {
|
|
54
|
+
input;
|
|
55
|
+
position;
|
|
56
|
+
bitBuffer = 0;
|
|
57
|
+
bitCount = 0;
|
|
58
|
+
output = new Uint8Array(1024);
|
|
59
|
+
outputLength = 0;
|
|
60
|
+
constructor(input, position) {
|
|
61
|
+
this.input = input;
|
|
62
|
+
this.position = position;
|
|
63
|
+
}
|
|
64
|
+
readBit() {
|
|
65
|
+
if (this.bitCount === 0) {
|
|
66
|
+
if (this.position >= this.input.length)
|
|
67
|
+
throw new Error('deflate: read past end of stream');
|
|
68
|
+
this.bitBuffer = this.input[this.position++];
|
|
69
|
+
this.bitCount = 8;
|
|
70
|
+
}
|
|
71
|
+
const bit = this.bitBuffer & 1;
|
|
72
|
+
this.bitBuffer >>= 1;
|
|
73
|
+
this.bitCount--;
|
|
74
|
+
return bit;
|
|
75
|
+
}
|
|
76
|
+
readBits(count, base) {
|
|
77
|
+
let value = 0;
|
|
78
|
+
for (let i = 0; i < count; i++)
|
|
79
|
+
value |= this.readBit() << i;
|
|
80
|
+
return value + base;
|
|
81
|
+
}
|
|
82
|
+
writeByte(byte) {
|
|
83
|
+
if (this.outputLength >= this.output.length) {
|
|
84
|
+
// Every other bound in this package is a field checked against the buffer. This one cannot be:
|
|
85
|
+
// the quantity that sizes the allocation is the COMPRESSION RATIO, which is not in the file, is
|
|
86
|
+
// not bounded by its length, and no per-field check can reach it. A kilobyte of nested maximum-
|
|
87
|
+
// length back-references expands to gigabytes and takes the process with it — unrecoverable, and
|
|
88
|
+
// reachable from any untrusted .awd. Where the file cannot bound the allocation, an explicit cap
|
|
89
|
+
// has to.
|
|
90
|
+
if (this.output.length * 2 > MAX_INFLATE_BYTES)
|
|
91
|
+
throw new Error('deflate: output exceeds the inflate limit');
|
|
92
|
+
const grown = new Uint8Array(this.output.length * 2);
|
|
93
|
+
grown.set(this.output);
|
|
94
|
+
this.output = grown;
|
|
95
|
+
}
|
|
96
|
+
this.output[this.outputLength++] = byte;
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
// Inflates the raw DEFLATE block stream at `start`, returning exactly the decompressed bytes.
|
|
100
|
+
function rawInflate(input, start) {
|
|
101
|
+
const state = new InflateState(input, start);
|
|
102
|
+
let final = 0;
|
|
103
|
+
do {
|
|
104
|
+
final = state.readBit();
|
|
105
|
+
const type = state.readBits(2, 0);
|
|
106
|
+
if (type === 0)
|
|
107
|
+
inflateStoredBlock(state);
|
|
108
|
+
else if (type === 1)
|
|
109
|
+
inflateHuffmanBlock(state, FIXED_LITERAL_TREE, FIXED_DISTANCE_TREE);
|
|
110
|
+
else if (type === 2)
|
|
111
|
+
inflateDynamicBlock(state);
|
|
112
|
+
else
|
|
113
|
+
throw new Error('deflate: invalid block type');
|
|
114
|
+
} while (final === 0);
|
|
115
|
+
return state.output.slice(0, state.outputLength);
|
|
116
|
+
}
|
|
117
|
+
// A stored (uncompressed) block: align to the next byte, read the 16-bit LEN and its one's-complement
|
|
118
|
+
// NLEN, then copy LEN literal bytes.
|
|
119
|
+
function inflateStoredBlock(state) {
|
|
120
|
+
state.bitBuffer = 0;
|
|
121
|
+
state.bitCount = 0;
|
|
122
|
+
if (state.position + 4 > state.input.length)
|
|
123
|
+
throw new Error('deflate: truncated stored block header');
|
|
124
|
+
const len = state.input[state.position] | (state.input[state.position + 1] << 8);
|
|
125
|
+
const nlen = state.input[state.position + 2] | (state.input[state.position + 3] << 8);
|
|
126
|
+
state.position += 4;
|
|
127
|
+
if ((len ^ 0xffff) !== nlen)
|
|
128
|
+
throw new Error('deflate: stored block length mismatch');
|
|
129
|
+
if (state.position + len > state.input.length)
|
|
130
|
+
throw new Error('deflate: truncated stored block data');
|
|
131
|
+
for (let i = 0; i < len; i++)
|
|
132
|
+
state.writeByte(state.input[state.position++]);
|
|
133
|
+
}
|
|
134
|
+
// Decodes one Huffman-coded block against the given literal/length and distance trees, emitting literals
|
|
135
|
+
// and resolving <length, distance> back-references against the bytes already written.
|
|
136
|
+
function inflateHuffmanBlock(state, literalTree, distanceTree) {
|
|
137
|
+
for (;;) {
|
|
138
|
+
const symbol = decodeSymbol(state, literalTree);
|
|
139
|
+
if (symbol === 256)
|
|
140
|
+
return; // end of block
|
|
141
|
+
if (symbol < 256) {
|
|
142
|
+
state.writeByte(symbol);
|
|
143
|
+
continue;
|
|
144
|
+
}
|
|
145
|
+
const lengthIndex = symbol - 257;
|
|
146
|
+
if (lengthIndex >= LENGTH_BASE.length)
|
|
147
|
+
throw new Error('deflate: invalid length symbol');
|
|
148
|
+
const length = state.readBits(LENGTH_EXTRA[lengthIndex], LENGTH_BASE[lengthIndex]);
|
|
149
|
+
const distanceSymbol = decodeSymbol(state, distanceTree);
|
|
150
|
+
if (distanceSymbol >= DISTANCE_BASE.length)
|
|
151
|
+
throw new Error('deflate: invalid distance symbol');
|
|
152
|
+
const distance = state.readBits(DISTANCE_EXTRA[distanceSymbol], DISTANCE_BASE[distanceSymbol]);
|
|
153
|
+
let source = state.outputLength - distance;
|
|
154
|
+
if (source < 0)
|
|
155
|
+
throw new Error('deflate: back-reference before start of output');
|
|
156
|
+
for (let i = 0; i < length; i++)
|
|
157
|
+
state.writeByte(state.output[source++]);
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
// Builds the dynamic literal/length and distance Huffman trees from the block header (their code lengths
|
|
161
|
+
// are themselves Huffman-coded by a code-length code), then decodes the block.
|
|
162
|
+
function inflateDynamicBlock(state) {
|
|
163
|
+
const literalCount = state.readBits(5, 257);
|
|
164
|
+
const distanceCount = state.readBits(5, 1);
|
|
165
|
+
const codeLengthCount = state.readBits(4, 4);
|
|
166
|
+
const codeLengthLengths = new Array(19).fill(0);
|
|
167
|
+
for (let i = 0; i < codeLengthCount; i++)
|
|
168
|
+
codeLengthLengths[CODE_LENGTH_ORDER[i]] = state.readBits(3, 0);
|
|
169
|
+
const codeLengthTree = buildHuffmanTree(codeLengthLengths, 19);
|
|
170
|
+
// Decode the literal+distance code lengths as one run, honoring the repeat codes 16 (copy previous
|
|
171
|
+
// 3-6×), 17 (zero 3-10×), and 18 (zero 11-138×).
|
|
172
|
+
const lengths = new Array(literalCount + distanceCount).fill(0);
|
|
173
|
+
let i = 0;
|
|
174
|
+
while (i < lengths.length) {
|
|
175
|
+
const symbol = decodeSymbol(state, codeLengthTree);
|
|
176
|
+
if (symbol < 16) {
|
|
177
|
+
lengths[i++] = symbol;
|
|
178
|
+
}
|
|
179
|
+
else if (symbol === 16) {
|
|
180
|
+
if (i === 0)
|
|
181
|
+
throw new Error('deflate: repeat with no previous length');
|
|
182
|
+
const repeat = state.readBits(2, 3);
|
|
183
|
+
const previous = lengths[i - 1];
|
|
184
|
+
for (let r = 0; r < repeat && i < lengths.length; r++)
|
|
185
|
+
lengths[i++] = previous;
|
|
186
|
+
}
|
|
187
|
+
else if (symbol === 17) {
|
|
188
|
+
const repeat = state.readBits(3, 3);
|
|
189
|
+
for (let r = 0; r < repeat && i < lengths.length; r++)
|
|
190
|
+
lengths[i++] = 0;
|
|
191
|
+
}
|
|
192
|
+
else if (symbol === 18) {
|
|
193
|
+
const repeat = state.readBits(7, 11);
|
|
194
|
+
for (let r = 0; r < repeat && i < lengths.length; r++)
|
|
195
|
+
lengths[i++] = 0;
|
|
196
|
+
}
|
|
197
|
+
else {
|
|
198
|
+
throw new Error('deflate: invalid code-length symbol');
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
const literalTree = buildHuffmanTree(lengths.slice(0, literalCount), literalCount);
|
|
202
|
+
const distanceTree = buildHuffmanTree(lengths.slice(literalCount), distanceCount);
|
|
203
|
+
inflateHuffmanBlock(state, literalTree, distanceTree);
|
|
204
|
+
}
|
|
205
|
+
// Builds a canonical Huffman decode tree from per-symbol code lengths (0 = unused). Symbols are bucketed
|
|
206
|
+
// by length so decodeSymbol can walk the code space one bit at a time.
|
|
207
|
+
function buildHuffmanTree(lengths, count) {
|
|
208
|
+
const counts = new Array(16).fill(0);
|
|
209
|
+
for (let i = 0; i < count; i++)
|
|
210
|
+
counts[lengths[i]]++;
|
|
211
|
+
counts[0] = 0;
|
|
212
|
+
const offsets = new Array(16).fill(0);
|
|
213
|
+
for (let len = 1; len < 16; len++)
|
|
214
|
+
offsets[len] = offsets[len - 1] + counts[len - 1];
|
|
215
|
+
const symbols = new Array(count).fill(0);
|
|
216
|
+
for (let i = 0; i < count; i++) {
|
|
217
|
+
if (lengths[i] !== 0)
|
|
218
|
+
symbols[offsets[lengths[i]]++] = i;
|
|
219
|
+
}
|
|
220
|
+
return { counts, symbols };
|
|
221
|
+
}
|
|
222
|
+
// Reads bits until they identify one canonical Huffman symbol (the tinf decode: extend the code one bit
|
|
223
|
+
// at a time, subtracting each length's code count until the code falls within a bucket).
|
|
224
|
+
function decodeSymbol(state, tree) {
|
|
225
|
+
let code = 0;
|
|
226
|
+
let first = 0;
|
|
227
|
+
let index = 0;
|
|
228
|
+
for (let len = 1; len < 16; len++) {
|
|
229
|
+
code |= state.readBit();
|
|
230
|
+
const count = tree.counts[len];
|
|
231
|
+
if (code - first < count)
|
|
232
|
+
return tree.symbols[index + (code - first)];
|
|
233
|
+
index += count;
|
|
234
|
+
first += count;
|
|
235
|
+
first <<= 1;
|
|
236
|
+
code <<= 1;
|
|
237
|
+
}
|
|
238
|
+
throw new Error('deflate: invalid Huffman code');
|
|
239
|
+
}
|
|
240
|
+
// The RFC 1951 fixed Huffman trees: literals/lengths 0-287 (lengths 8/9/7/8 by range) and 5-bit distances.
|
|
241
|
+
const FIXED_LITERAL_TREE = buildFixedLiteralTree();
|
|
242
|
+
const FIXED_DISTANCE_TREE = buildHuffmanTree(new Array(30).fill(5), 30);
|
|
243
|
+
function buildFixedLiteralTree() {
|
|
244
|
+
const lengths = new Array(288);
|
|
245
|
+
for (let i = 0; i < 144; i++)
|
|
246
|
+
lengths[i] = 8;
|
|
247
|
+
for (let i = 144; i < 256; i++)
|
|
248
|
+
lengths[i] = 9;
|
|
249
|
+
for (let i = 256; i < 280; i++)
|
|
250
|
+
lengths[i] = 7;
|
|
251
|
+
for (let i = 280; i < 288; i++)
|
|
252
|
+
lengths[i] = 8;
|
|
253
|
+
return buildHuffmanTree(lengths, 288);
|
|
254
|
+
}
|
|
255
|
+
// The ceiling on a single inflate. Generous for any real AWD body — a 256 MB scene is far past what
|
|
256
|
+
// this importer is used for — and small enough that hitting it fails a parse instead of the process.
|
|
257
|
+
const MAX_INFLATE_BYTES = 256 * 1024 * 1024;
|
|
258
|
+
//# sourceMappingURL=deflate.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"deflate.js","sourceRoot":"","sources":["../src/deflate.ts"],"names":[],"mappings":"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"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,2BAA2B,EAAE,MAAM,WAAW,CAAC;AACxE,OAAO,EAAE,eAAe,EAAE,eAAe,EAAE,oBAAoB,EAAE,sBAAsB,EAAE,MAAM,gBAAgB,CAAC"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,2BAA2B,EAAE,MAAM,WAAW,CAAC;AACxE,OAAO,EAAE,eAAe,EAAE,eAAe,EAAE,oBAAoB,EAAE,sBAAsB,EAAE,MAAM,gBAAgB,CAAC"}
|
package/package.json
ADDED
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@flighthq/compression",
|
|
3
|
+
"version": "0.3.0-next.1494.7333e82",
|
|
4
|
+
"repository": {
|
|
5
|
+
"type": "git",
|
|
6
|
+
"url": "https://github.com/flighthq/flight.git",
|
|
7
|
+
"directory": "packages/compression"
|
|
8
|
+
},
|
|
9
|
+
"type": "module",
|
|
10
|
+
"main": "dist/index.js",
|
|
11
|
+
"types": "dist/index.d.ts",
|
|
12
|
+
"exports": {
|
|
13
|
+
".": {
|
|
14
|
+
"types": "./dist/index.d.ts",
|
|
15
|
+
"default": "./dist/index.js"
|
|
16
|
+
},
|
|
17
|
+
"./contract": {
|
|
18
|
+
"types": "./dist/contract.d.ts",
|
|
19
|
+
"default": "./dist/contract.js"
|
|
20
|
+
}
|
|
21
|
+
},
|
|
22
|
+
"files": [
|
|
23
|
+
"dist",
|
|
24
|
+
"src/**/*.test.ts",
|
|
25
|
+
"!dist/**/*.test.js",
|
|
26
|
+
"!dist/**/*.test.d.ts",
|
|
27
|
+
"!dist/**/*.test.js.map",
|
|
28
|
+
"!dist/**/*.test.d.ts.map"
|
|
29
|
+
],
|
|
30
|
+
"scripts": {
|
|
31
|
+
"build": "tsc -b",
|
|
32
|
+
"clean": "tsc -b --clean",
|
|
33
|
+
"test": "vitest run --config vitest.config.ts",
|
|
34
|
+
"test:watch": "vitest --watch --config vitest.config.ts",
|
|
35
|
+
"prepack": "npm run clean && npm run clean:dist && npm run build",
|
|
36
|
+
"clean:dist": "tsx ../../scripts/clean-package-dist.ts"
|
|
37
|
+
},
|
|
38
|
+
"dependencies": {
|
|
39
|
+
"@flighthq/types": "0.3.0-next.1494.7333e82"
|
|
40
|
+
},
|
|
41
|
+
"devDependencies": {
|
|
42
|
+
"typescript": "^5.3.0"
|
|
43
|
+
},
|
|
44
|
+
"description": "Compressed-byte decoders and the shared decompressor registry consumers resolve through",
|
|
45
|
+
"sideEffects": false
|
|
46
|
+
}
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import { Compression } from '@flighthq/types/contract';
|
|
2
|
+
|
|
3
|
+
import { getDecompressor, hasDecompressor, registerDecompressor, unregisterDecompressor } from './decompressor';
|
|
4
|
+
|
|
5
|
+
describe('getDecompressor', () => {
|
|
6
|
+
it('reports nothing registered rather than throwing', () => {
|
|
7
|
+
expect(getDecompressor(Compression.Deflate)).toBeNull();
|
|
8
|
+
expect(getDecompressor(Compression.Lzma)).toBeNull();
|
|
9
|
+
});
|
|
10
|
+
});
|
|
11
|
+
|
|
12
|
+
describe('hasDecompressor', () => {
|
|
13
|
+
afterEach(() => unregisterDecompressor(Compression.Lzma));
|
|
14
|
+
|
|
15
|
+
it('answers without handing back the implementation', () => {
|
|
16
|
+
expect(hasDecompressor(Compression.Lzma)).toBe(false);
|
|
17
|
+
registerDecompressor(Compression.Lzma, () => null);
|
|
18
|
+
expect(hasDecompressor(Compression.Lzma)).toBe(true);
|
|
19
|
+
});
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
describe('registerDecompressor', () => {
|
|
23
|
+
afterEach(() => {
|
|
24
|
+
unregisterDecompressor(Compression.Deflate);
|
|
25
|
+
unregisterDecompressor(Compression.Lzma);
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
it('registers per algorithm, last write winning', () => {
|
|
29
|
+
const first = (): Uint8Array => new Uint8Array([1]);
|
|
30
|
+
const second = (): Uint8Array => new Uint8Array([2]);
|
|
31
|
+
|
|
32
|
+
registerDecompressor(Compression.Deflate, first);
|
|
33
|
+
expect(getDecompressor(Compression.Deflate)).toBe(first);
|
|
34
|
+
// A host replacing a portable decoder with a native or wasm one relies on last-write-wins.
|
|
35
|
+
registerDecompressor(Compression.Deflate, second);
|
|
36
|
+
expect(getDecompressor(Compression.Deflate)).toBe(second);
|
|
37
|
+
// Registering one algorithm leaves the others alone.
|
|
38
|
+
expect(getDecompressor(Compression.Lzma)).toBeNull();
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
it('serves every consumer from the one registration', () => {
|
|
42
|
+
const decompress = (): Uint8Array => new Uint8Array([7]);
|
|
43
|
+
registerDecompressor(Compression.Deflate, decompress);
|
|
44
|
+
|
|
45
|
+
// Whatever container asks — a SWF body, an AWD2 block — resolves the same implementation.
|
|
46
|
+
expect(getDecompressor(Compression.Deflate)).toBe(decompress);
|
|
47
|
+
expect(getDecompressor(Compression.Deflate)).toBe(getDecompressor(Compression.Deflate));
|
|
48
|
+
});
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
describe('unregisterDecompressor', () => {
|
|
52
|
+
it('removes a registration and is safe when none exists', () => {
|
|
53
|
+
registerDecompressor(Compression.Deflate, () => new Uint8Array());
|
|
54
|
+
unregisterDecompressor(Compression.Deflate);
|
|
55
|
+
expect(getDecompressor(Compression.Deflate)).toBeNull();
|
|
56
|
+
expect(() => unregisterDecompressor(Compression.Deflate)).not.toThrow();
|
|
57
|
+
});
|
|
58
|
+
});
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
import { Compression } from '@flighthq/types/contract';
|
|
2
|
+
|
|
3
|
+
import { getDecompressor, unregisterDecompressor } from './decompressor';
|
|
4
|
+
import { inflateDeflate, registerDeflateDecompressor } from './deflate';
|
|
5
|
+
|
|
6
|
+
// The compressed fixtures below are precomputed with Node's zlib and embedded as base64 rather than
|
|
7
|
+
// generated at test time: `scene-formats` is a browser-clean package whose build carries no `@types/node`,
|
|
8
|
+
// so a `node:zlib` import fails the `tsc -b` build. Provenance — generated once with node v22.22.1:
|
|
9
|
+
// const b64 = (u8) => Buffer.from(u8).toString('base64');
|
|
10
|
+
// const enc = (s) => new TextEncoder().encode(s);
|
|
11
|
+
// b64(deflateSync(new Uint8Array(0))) // EMPTY
|
|
12
|
+
// b64(deflateSync(enc('flighthq scene-formats'))) // LITERAL (zlib)
|
|
13
|
+
// b64(deflateRawSync(enc('flighthq scene-formats'))) // RAW_LITERAL (headerless)
|
|
14
|
+
// b64(deflateSync(enc('abcABC123'.repeat(600)))) // REPETITIVE
|
|
15
|
+
// b64(deflateSync(enc('Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore. '.repeat(12)))) // LOREM (genuine dynamic-Huffman block)
|
|
16
|
+
// b64(deflateSync(enc('the quick brown fox jumps over the lazy dog'), { level: 0 })) // STORED_L0
|
|
17
|
+
// The round-trip assertions still verify the vendored inflater reproduces the original bytes EXACTLY.
|
|
18
|
+
const LOREM = 'Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore. ';
|
|
19
|
+
const FIXTURES = {
|
|
20
|
+
EMPTY: 'eJwDAAAAAAE=',
|
|
21
|
+
// 64 KB of a repeating byte run, zlib-deflated: 124 bytes expanding 528x — a high but honest ratio.
|
|
22
|
+
HIGH_RATIO:
|
|
23
|
+
'eJztxbEBADAEADC0/H+yQyRLIuv9npAkSZIkSZIkSZIkSZIkSZIkSZIkSZIkSZIkSZIkSZIkSZIkSZIkSZIkSZIkSZIkSZIkSZIkSZIkSZIkSZIkSZIkSZIkSZIkSZIkSZIkSZIkSZIkSZIkSZIkSZIkSZIk3W4BATsAKQ==',
|
|
24
|
+
LITERAL: 'eJxLy8lMzyjJKFQoTk7NS9VNyy/KTSwpBgBjVQiv',
|
|
25
|
+
RAW_LITERAL: 'S8vJTM8oyShUKE5OzUvVTcsvyk0sKQYA',
|
|
26
|
+
REPETITIVE: 'eJztxjEBACAIALBMYgIxCdC/g0HcrlXPybtil4iIiIiIiIiIiMgfeU6Y4Pw=',
|
|
27
|
+
LOREM:
|
|
28
|
+
'eJztzdENQyEMQ9FVPEDVSd4SlESVJUIQSfYvQ/STb+v6PL7VwBVlEB++EUw003yh+wztqVkbTbgYnfMLHTxjqJwAygpzQaqtE3N2CqVmohKjfc79G89FLnKRi/wX+QG1gr9k',
|
|
29
|
+
STORED_L0: 'eAEBKwDU/3RoZSBxdWljayBicm93biBmb3gganVtcHMgb3ZlciB0aGUgbGF6eSBkb2dhPA/6',
|
|
30
|
+
} as const;
|
|
31
|
+
|
|
32
|
+
// Decodes a base64 string to bytes with no external dependency (no `atob`/`Buffer`) so the test stays
|
|
33
|
+
// browser-clean. The embedded fixtures are canonical base64 (no whitespace, standard alphabet).
|
|
34
|
+
function decodeBase64(input: string): Uint8Array {
|
|
35
|
+
const alphabet = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/';
|
|
36
|
+
const lookup = new Uint8Array(128);
|
|
37
|
+
for (let i = 0; i < alphabet.length; i++) lookup[alphabet.charCodeAt(i)] = i;
|
|
38
|
+
const clean = input.replace(/=+$/, '');
|
|
39
|
+
const out = new Uint8Array((clean.length * 3) >> 2);
|
|
40
|
+
let accumulator = 0;
|
|
41
|
+
let bits = 0;
|
|
42
|
+
let o = 0;
|
|
43
|
+
for (let i = 0; i < clean.length; i++) {
|
|
44
|
+
accumulator = (accumulator << 6) | lookup[clean.charCodeAt(i)];
|
|
45
|
+
bits += 6;
|
|
46
|
+
if (bits >= 8) {
|
|
47
|
+
bits -= 8;
|
|
48
|
+
out[o++] = (accumulator >> bits) & 0xff;
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
return out;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
const encode = (s: string): Uint8Array => new TextEncoder().encode(s);
|
|
55
|
+
|
|
56
|
+
describe('inflateDeflate', () => {
|
|
57
|
+
it('round-trips empty input', () => {
|
|
58
|
+
expect(inflateDeflate(decodeBase64(FIXTURES.EMPTY), 0)).toEqual(new Uint8Array(0));
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
it('keeps inflating a stream whose expansion ratio is large but bounded', () => {
|
|
62
|
+
// The inflate cap must not fire on real content. AWD bodies are float arrays with long repeating
|
|
63
|
+
// runs, so a high ratio is ORDINARY here — the cap is for a ratio that is unbounded, not merely
|
|
64
|
+
// large, and one that rejected honest compression would be worse than the bomb it prevents. This
|
|
65
|
+
// fixture is 124 bytes expanding 528x to 64 KB, and must still round-trip byte for byte.
|
|
66
|
+
const source = new Uint8Array(64 * 1024);
|
|
67
|
+
for (let i = 0; i < source.length; i++) source[i] = i % 7;
|
|
68
|
+
expect(inflateDeflate(decodeBase64(FIXTURES.HIGH_RATIO), 0)).toEqual(source);
|
|
69
|
+
});
|
|
70
|
+
|
|
71
|
+
it('round-trips a short literal run (zlib-wrapped)', () => {
|
|
72
|
+
expect(inflateDeflate(decodeBase64(FIXTURES.LITERAL), 0)).toEqual(encode('flighthq scene-formats'));
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
it('round-trips a headerless raw DEFLATE stream via the fallback path', () => {
|
|
76
|
+
expect(inflateDeflate(decodeBase64(FIXTURES.RAW_LITERAL), 0)).toEqual(encode('flighthq scene-formats'));
|
|
77
|
+
});
|
|
78
|
+
|
|
79
|
+
it('round-trips highly repetitive data through back-references and grows the output buffer', () => {
|
|
80
|
+
// 5400 bytes out — past the 1024-byte initial buffer, exercising the grow path.
|
|
81
|
+
expect(inflateDeflate(decodeBase64(FIXTURES.REPETITIVE), 0)).toEqual(encode('abcABC123'.repeat(600)));
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
it('round-trips prose through a genuine dynamic-Huffman block', () => {
|
|
85
|
+
// Verified offline that zlib emits BTYPE=2 (dynamic Huffman) for this input, exercising the
|
|
86
|
+
// dynamic literal/length + distance code-length decode path.
|
|
87
|
+
expect(inflateDeflate(decodeBase64(FIXTURES.LOREM), 0)).toEqual(encode(LOREM.repeat(12)));
|
|
88
|
+
});
|
|
89
|
+
|
|
90
|
+
it('round-trips a stored (level 0) block', () => {
|
|
91
|
+
expect(inflateDeflate(decodeBase64(FIXTURES.STORED_L0), 0)).toEqual(
|
|
92
|
+
encode('the quick brown fox jumps over the lazy dog'),
|
|
93
|
+
);
|
|
94
|
+
});
|
|
95
|
+
|
|
96
|
+
it('returns null on a truncated stream rather than throwing', () => {
|
|
97
|
+
expect(inflateDeflate(decodeBase64(FIXTURES.REPETITIVE).subarray(0, 6), 0)).toBeNull();
|
|
98
|
+
});
|
|
99
|
+
|
|
100
|
+
it('returns null on a corrupt (invalid block type) stream', () => {
|
|
101
|
+
// 0x78 0x9c is a valid zlib header; 0xff 0xff after it decodes an invalid block type.
|
|
102
|
+
expect(inflateDeflate(new Uint8Array([0x78, 0x9c, 0xff, 0xff]), 0)).toBeNull();
|
|
103
|
+
});
|
|
104
|
+
});
|
|
105
|
+
|
|
106
|
+
describe('registerDeflateDecompressor', () => {
|
|
107
|
+
afterEach(() => unregisterDecompressor(Compression.Deflate));
|
|
108
|
+
|
|
109
|
+
it('puts the inflater in the shared registry every consumer resolves through', () => {
|
|
110
|
+
expect(getDecompressor(Compression.Deflate)).toBeNull();
|
|
111
|
+
|
|
112
|
+
registerDeflateDecompressor();
|
|
113
|
+
|
|
114
|
+
// One registration is what every container format reads through — none of them owns its own registry.
|
|
115
|
+
expect(getDecompressor(Compression.Deflate)).toBe(inflateDeflate);
|
|
116
|
+
expect(getDecompressor(Compression.Deflate)?.(decodeBase64(FIXTURES.LITERAL), 0)).toEqual(
|
|
117
|
+
encode('flighthq scene-formats'),
|
|
118
|
+
);
|
|
119
|
+
});
|
|
120
|
+
});
|