lzma1 0.2.0 → 0.3.0
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/README.md +10 -3
- package/lib/decoder.js +84 -85
- package/lib/encoder.js +794 -143
- package/lib/index.js +0 -1
- package/lib/lz-window.js +3 -4
- package/lib/lzma.js +50 -1383
- package/lib/match-finder.js +402 -0
- package/lib/range-decoder.js +3 -16
- package/lib/range-encoder.js +14 -24
- package/lib/streams.js +64 -1
- package/lib/utils.js +4 -102
- package/package.json +24 -13
- package/src/decoder.ts +604 -0
- package/src/encoder.ts +2108 -0
- package/src/index.ts +71 -0
- package/src/len-coder.ts +217 -0
- package/src/lit-coder.ts +196 -0
- package/src/lz-window.ts +99 -0
- package/src/lzma.ts +142 -0
- package/src/match-finder.ts +548 -0
- package/src/range-bit-tree-coder.ts +109 -0
- package/src/range-decoder.ts +98 -0
- package/src/range-encoder.ts +136 -0
- package/src/streams.ts +73 -0
- package/src/utils.ts +263 -0
- package/lib/chunker.d.ts +0 -46
- package/lib/chunker.js +0 -68
- package/lib/decoder.d.ts +0 -80
- package/lib/encoder.d.ts +0 -266
- package/lib/index.d.ts +0 -38
- package/lib/len-coder.d.ts +0 -70
- package/lib/lit-coder.d.ts +0 -63
- package/lib/lz-in-window.d.ts +0 -43
- package/lib/lz-in-window.js +0 -132
- package/lib/lz-window.d.ts +0 -35
- package/lib/lzma.d.ts +0 -107
- package/lib/match-finder-config.d.ts +0 -34
- package/lib/match-finder-config.js +0 -63
- package/lib/range-bit-tree-coder.d.ts +0 -34
- package/lib/range-decoder.d.ts +0 -34
- package/lib/range-encoder.d.ts +0 -46
- package/lib/streams.d.ts +0 -32
- package/lib/utils.d.ts +0 -127
package/README.md
CHANGED
|
@@ -25,9 +25,9 @@ to read and maintain.
|
|
|
25
25
|
|
|
26
26
|
- Encode and decode LZMA streams
|
|
27
27
|
- Supports both string and Uint8Array data
|
|
28
|
-
- Supports both browser and Node.js environments
|
|
29
|
-
- Pure JavaScript implementation with TypeScript types
|
|
30
28
|
- No dependencies on runtime-specific APIs
|
|
29
|
+
- Pure JavaScript implementation with TypeScript types
|
|
30
|
+
- Isomorphic browser- and runtime-independent implementation
|
|
31
31
|
|
|
32
32
|
## Installation
|
|
33
33
|
|
|
@@ -155,7 +155,14 @@ commonly used in the 7z archive format.
|
|
|
155
155
|
The LZMA compressed data begins with a header that contains information needed
|
|
156
156
|
for decompression:
|
|
157
157
|
|
|
158
|
-
|
|
158
|
+
```mermaid
|
|
159
|
+
packet-beta
|
|
160
|
+
0-7: "lc (literal context)"
|
|
161
|
+
8-11: "lp (literal pos)"
|
|
162
|
+
12-15: "pb (position bits)"
|
|
163
|
+
16-47: "Dictionary Size (32-bit, LE)"
|
|
164
|
+
48-111: "Uncompressed Size (64-bit, LE)"
|
|
165
|
+
```
|
|
159
166
|
|
|
160
167
|
More [information][header_link] about the LZMA header structure.
|
|
161
168
|
|
package/lib/decoder.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { LzOutWindow } from "./lz-window.js";
|
|
2
2
|
import { RangeDecoder } from "./range-decoder.js";
|
|
3
|
-
import {
|
|
3
|
+
import { _MAX_UINT32, CHOICE_ARRAY_SIZE, createBitTree, DEFAULT_WINDOW_SIZE, getLenToPosState, initArray, initBitModels, LITERAL_DECODER_SIZE, MATCH_DECODERS_SIZE, POS_DECODERS_SIZE, REP_DECODERS_SIZE, stateUpdateChar, } from "./utils.js";
|
|
4
4
|
export class Decoder {
|
|
5
5
|
rangeDecoder;
|
|
6
6
|
outWindow;
|
|
@@ -11,8 +11,8 @@ export class Decoder {
|
|
|
11
11
|
rep2 = 0;
|
|
12
12
|
rep3 = 0;
|
|
13
13
|
prevByte = 0;
|
|
14
|
-
nowPos64 =
|
|
15
|
-
outSize =
|
|
14
|
+
nowPos64 = 0n;
|
|
15
|
+
outSize = 0n;
|
|
16
16
|
// Decoder configuration
|
|
17
17
|
posStateMask = 0;
|
|
18
18
|
dictSizeCheck = 0;
|
|
@@ -34,11 +34,6 @@ export class Decoder {
|
|
|
34
34
|
get literalCoder() {
|
|
35
35
|
return this.literalDecoder;
|
|
36
36
|
}
|
|
37
|
-
// Chunker properties for compatibility
|
|
38
|
-
decoder;
|
|
39
|
-
encoder = null;
|
|
40
|
-
alive = 0;
|
|
41
|
-
inBytesProcessed = [0, 0];
|
|
42
37
|
constructor() {
|
|
43
38
|
// Initialize range decoder
|
|
44
39
|
this.rangeDecoder = new RangeDecoder();
|
|
@@ -69,8 +64,79 @@ export class Decoder {
|
|
|
69
64
|
this.repLenDecoder = this.createLenDecoder();
|
|
70
65
|
// Initialize position alignment decoder
|
|
71
66
|
this.posAlignDecoder = createBitTree(4);
|
|
72
|
-
|
|
73
|
-
|
|
67
|
+
}
|
|
68
|
+
/**
|
|
69
|
+
* Read LZMA header, configure decoder, and prepare for decompression.
|
|
70
|
+
*/
|
|
71
|
+
initDecompression(input, output) {
|
|
72
|
+
// Read 5-byte header properties
|
|
73
|
+
const properties = [];
|
|
74
|
+
for (let i = 0; i < 5; ++i) {
|
|
75
|
+
const r = input.readByte();
|
|
76
|
+
if (r === -1) {
|
|
77
|
+
throw new Error("truncated input");
|
|
78
|
+
}
|
|
79
|
+
properties[i] = r << 24 >> 24;
|
|
80
|
+
}
|
|
81
|
+
if (!this.setDecoderProperties(properties)) {
|
|
82
|
+
throw new Error("corrupted input");
|
|
83
|
+
}
|
|
84
|
+
// Read 8-byte uncompressed length from header
|
|
85
|
+
let outSize;
|
|
86
|
+
{
|
|
87
|
+
let value = 0n;
|
|
88
|
+
for (let i = 0; i < 8; i++) {
|
|
89
|
+
const r = input.readByte();
|
|
90
|
+
if (r === -1) {
|
|
91
|
+
throw new Error("truncated input");
|
|
92
|
+
}
|
|
93
|
+
value += BigInt(r & 0xFF) << BigInt(i * 8);
|
|
94
|
+
}
|
|
95
|
+
// Check for unknown size marker (all 0xFF bytes)
|
|
96
|
+
if (value === 0xffffffffffffffffn) {
|
|
97
|
+
outSize = -1n;
|
|
98
|
+
}
|
|
99
|
+
else if (value > BigInt(_MAX_UINT32)) {
|
|
100
|
+
outSize = -1n;
|
|
101
|
+
}
|
|
102
|
+
else {
|
|
103
|
+
outSize = value;
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
// Set up range decoder and output window
|
|
107
|
+
this.rangeDecoder.setStream(input);
|
|
108
|
+
this.flush();
|
|
109
|
+
this.outWindow.stream = null;
|
|
110
|
+
this.outWindow.stream = output;
|
|
111
|
+
// Initialize decoder state
|
|
112
|
+
this.init();
|
|
113
|
+
this.state = 0;
|
|
114
|
+
this.rep0 = 0;
|
|
115
|
+
this.rep1 = 0;
|
|
116
|
+
this.rep2 = 0;
|
|
117
|
+
this.rep3 = 0;
|
|
118
|
+
this.outSize = outSize;
|
|
119
|
+
this.nowPos64 = 0n;
|
|
120
|
+
this.prevByte = 0;
|
|
121
|
+
}
|
|
122
|
+
/**
|
|
123
|
+
* Full decompression: read header, decode all chunks, flush and cleanup.
|
|
124
|
+
*/
|
|
125
|
+
decompress(input, output) {
|
|
126
|
+
this.initDecompression(input, output);
|
|
127
|
+
while (true) {
|
|
128
|
+
const result = this.codeOneChunk();
|
|
129
|
+
if (result === -1) {
|
|
130
|
+
throw new Error("corrupted input");
|
|
131
|
+
}
|
|
132
|
+
const isOutputComplete = (this.outSize >= 0n)
|
|
133
|
+
&& (this.nowPos64 >= this.outSize);
|
|
134
|
+
if (result || isOutputComplete) {
|
|
135
|
+
this.flush();
|
|
136
|
+
this.cleanup();
|
|
137
|
+
return;
|
|
138
|
+
}
|
|
139
|
+
}
|
|
74
140
|
}
|
|
75
141
|
createLenDecoder() {
|
|
76
142
|
const decoder = {
|
|
@@ -111,7 +177,7 @@ export class Decoder {
|
|
|
111
177
|
// Initialize output window
|
|
112
178
|
if (dictSize > 0) {
|
|
113
179
|
this.outWindow.windowSize = Math.max(dictSize, 4096);
|
|
114
|
-
this.outWindow.buffer =
|
|
180
|
+
this.outWindow.buffer = new Uint8Array(this.outWindow.windowSize);
|
|
115
181
|
}
|
|
116
182
|
// Initialize literal decoder coders
|
|
117
183
|
const numStates = 1 << (this.literalDecoder.numPrevBits + this.literalDecoder.numPosBits);
|
|
@@ -295,9 +361,9 @@ export class Decoder {
|
|
|
295
361
|
}
|
|
296
362
|
codeOneChunk() {
|
|
297
363
|
let decoder2, distance, len, numDirectBits, positionSlot;
|
|
298
|
-
let posState =
|
|
364
|
+
let posState = Number(this.nowPos64 & 0xffffffffn) & this.posStateMask;
|
|
299
365
|
if (!this.decodeBit(this.matchDecoders, (this.state << 4) + posState)) {
|
|
300
|
-
decoder2 = this.getDecoder(
|
|
366
|
+
decoder2 = this.getDecoder(Number(this.nowPos64 & 0xffffffffn), this.prevByte);
|
|
301
367
|
if (this.state < 7) {
|
|
302
368
|
this.prevByte = this.decodeNormalWithRangeDecoder(decoder2);
|
|
303
369
|
}
|
|
@@ -306,7 +372,7 @@ export class Decoder {
|
|
|
306
372
|
}
|
|
307
373
|
this.putByte(this.prevByte);
|
|
308
374
|
this.state = stateUpdateChar(this.state);
|
|
309
|
-
this.nowPos64
|
|
375
|
+
this.nowPos64 += 1n;
|
|
310
376
|
}
|
|
311
377
|
else {
|
|
312
378
|
if (this.decodeBit(this.repDecoders, this.state)) {
|
|
@@ -367,76 +433,17 @@ export class Decoder {
|
|
|
367
433
|
this.rep0 = positionSlot;
|
|
368
434
|
}
|
|
369
435
|
}
|
|
370
|
-
if (
|
|
436
|
+
if (BigInt(this.rep0) >= this.nowPos64 || this.rep0 >= this.dictSizeCheck) {
|
|
371
437
|
return -1;
|
|
372
438
|
}
|
|
373
439
|
this.copyBlock(len);
|
|
374
|
-
this.nowPos64
|
|
440
|
+
this.nowPos64 += BigInt(len);
|
|
375
441
|
this.prevByte = this.getByte(0);
|
|
376
442
|
}
|
|
377
443
|
return 0;
|
|
378
444
|
}
|
|
379
|
-
// Setup decoder for chunk processing
|
|
380
|
-
setupForDecoding(inStream, outSize, outputBuffer) {
|
|
381
|
-
this.rangeDecoder.setStream(inStream);
|
|
382
|
-
this.outSize = outSize;
|
|
383
|
-
this.outWindowReleaseStream();
|
|
384
|
-
this.outWindow.stream = outputBuffer;
|
|
385
|
-
this.init();
|
|
386
|
-
this.state = 0;
|
|
387
|
-
this.rep0 = 0;
|
|
388
|
-
this.rep1 = 0;
|
|
389
|
-
this.rep2 = 0;
|
|
390
|
-
this.rep3 = 0;
|
|
391
|
-
this.outSize = outSize;
|
|
392
|
-
this.nowPos64 = [0, 0];
|
|
393
|
-
this.prevByte = 0;
|
|
394
|
-
this.decoder = this;
|
|
395
|
-
this.encoder = null;
|
|
396
|
-
this.alive = 1;
|
|
397
|
-
}
|
|
398
|
-
// Process chunk and return alive status
|
|
399
|
-
processChunk() {
|
|
400
|
-
if (!this.alive) {
|
|
401
|
-
throw new Error("Bad state");
|
|
402
|
-
}
|
|
403
|
-
if (this.encoder) {
|
|
404
|
-
throw new Error("No encoding");
|
|
405
|
-
}
|
|
406
|
-
const result = this.codeOneChunk();
|
|
407
|
-
if (result === -1) {
|
|
408
|
-
throw new Error("Corrupted input");
|
|
409
|
-
}
|
|
410
|
-
const isOutputComplete = (compare64(this.outSize, [0, 0]) >= 0)
|
|
411
|
-
&& (compare64(this.nowPos64, this.outSize) >= 0);
|
|
412
|
-
if (result || isOutputComplete) {
|
|
413
|
-
this.flush();
|
|
414
|
-
this.outWindowReleaseStream();
|
|
415
|
-
this.rangeDecoder.setStream(null);
|
|
416
|
-
this.alive = 0;
|
|
417
|
-
}
|
|
418
|
-
return this.alive;
|
|
419
|
-
}
|
|
420
445
|
writeToOutput(buffer, data, offset, length) {
|
|
421
|
-
|
|
422
|
-
const requiredSize = buffer.count + length;
|
|
423
|
-
if (requiredSize > buffer.buf.length) {
|
|
424
|
-
const newSize = Math.max(buffer.buf.length * 2, requiredSize);
|
|
425
|
-
const newBuf = new Array(newSize);
|
|
426
|
-
for (let i = 0; i < buffer.count; i++) {
|
|
427
|
-
newBuf[i] = buffer.buf[i];
|
|
428
|
-
}
|
|
429
|
-
buffer.buf = newBuf;
|
|
430
|
-
}
|
|
431
|
-
// Copy data
|
|
432
|
-
for (let i = 0; i < length; i++) {
|
|
433
|
-
buffer.buf[buffer.count + i] = data[offset + i];
|
|
434
|
-
}
|
|
435
|
-
buffer.count += length;
|
|
436
|
-
}
|
|
437
|
-
isBufferWithCount(x) {
|
|
438
|
-
const s = x;
|
|
439
|
-
return !!s && Array.isArray(s.buf) && typeof s.count === "number" && typeof s.write === "function";
|
|
446
|
+
buffer.writeBytes(data, offset, length);
|
|
440
447
|
}
|
|
441
448
|
flush() {
|
|
442
449
|
const size = this.outWindow.pos - this.outWindow.streamPos;
|
|
@@ -444,15 +451,7 @@ export class Decoder {
|
|
|
444
451
|
return;
|
|
445
452
|
}
|
|
446
453
|
if (this.outWindow.stream && this.outWindow.buffer) {
|
|
447
|
-
|
|
448
|
-
if (this.isBufferWithCount(outputBuffer)) {
|
|
449
|
-
this.writeToOutput(outputBuffer, this.outWindow.buffer, this.outWindow.streamPos, size);
|
|
450
|
-
}
|
|
451
|
-
else if (typeof outputBuffer.write === "function") {
|
|
452
|
-
// Fallback: write directly if it's a plain Writer
|
|
453
|
-
const slice = this.outWindow.buffer.slice(this.outWindow.streamPos, this.outWindow.streamPos + size);
|
|
454
|
-
outputBuffer.write(slice);
|
|
455
|
-
}
|
|
454
|
+
this.outWindow.stream.writeBytes(this.outWindow.buffer, this.outWindow.streamPos, size);
|
|
456
455
|
}
|
|
457
456
|
if (this.outWindow.pos >= this.outWindow.windowSize) {
|
|
458
457
|
this.outWindow.pos = 0;
|