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/src/decoder.ts ADDED
@@ -0,0 +1,604 @@
1
+ import { LzOutWindow } from "./lz-window.js";
2
+ import { RangeDecoder } from "./range-decoder.js";
3
+ import type {
4
+ InputBuffer,
5
+ OutputBuffer,
6
+ } from "./streams.js";
7
+ import {
8
+ _MAX_UINT32,
9
+ type BitTree,
10
+ CHOICE_ARRAY_SIZE,
11
+ createBitTree,
12
+ DEFAULT_WINDOW_SIZE,
13
+ getLenToPosState,
14
+ initArray,
15
+ initBitModels,
16
+ LITERAL_DECODER_SIZE,
17
+ MATCH_DECODERS_SIZE,
18
+ POS_DECODERS_SIZE,
19
+ REP_DECODERS_SIZE,
20
+ stateUpdateChar,
21
+ } from "./utils.js";
22
+
23
+ // Decoder-specific interfaces (not shared with encoder-side LenEncoder/LitCoder
24
+ // which have private fields and different method signatures)
25
+ interface LenDecoder {
26
+ choice: number[];
27
+ lowCoder: BitTree[];
28
+ midCoder: BitTree[];
29
+ highCoder: BitTree;
30
+ numPosStates: number;
31
+ }
32
+
33
+ interface LiteralCoder {
34
+ coders: any[];
35
+ numPrevBits: number;
36
+ numPosBits: number;
37
+ posMask: number;
38
+ init?(): void;
39
+ }
40
+
41
+ export class Decoder {
42
+ rangeDecoder: RangeDecoder;
43
+ outWindow: LzOutWindow;
44
+
45
+ // Decoder state
46
+ state: number = 0;
47
+ rep0: number = 0;
48
+ rep1: number = 0;
49
+ rep2: number = 0;
50
+ rep3: number = 0;
51
+ prevByte: number = 0;
52
+ nowPos64: bigint = 0n;
53
+ outSize: bigint = 0n;
54
+
55
+ // Decoder configuration
56
+ posStateMask: number = 0;
57
+ dictSizeCheck: number = 0;
58
+
59
+ // Probability models for different symbols
60
+ matchDecoders: number[] = [];
61
+ rep0LongDecoders: number[] = [];
62
+ repDecoders: number[] = [];
63
+ repG0Decoders: number[] = [];
64
+ repG1Decoders: number[] = [];
65
+ repG2Decoders: number[] = [];
66
+ posDecoders: number[] = [];
67
+
68
+ // Complex decoders
69
+ literalDecoder: LiteralCoder;
70
+ posSlotDecoders: BitTree[] = [];
71
+ lenDecoder: LenDecoder;
72
+ repLenDecoder: LenDecoder;
73
+ posAlignDecoder: BitTree;
74
+
75
+ // Alias for compatibility with LZMA class
76
+ get literalCoder() {
77
+ return this.literalDecoder;
78
+ }
79
+
80
+ constructor() {
81
+ // Initialize range decoder
82
+ this.rangeDecoder = new RangeDecoder();
83
+
84
+ // Initialize output window using proper LzOutWindow
85
+ this.outWindow = new LzOutWindow(null, DEFAULT_WINDOW_SIZE);
86
+
87
+ // Initialize probability models
88
+ this.matchDecoders = initArray(MATCH_DECODERS_SIZE);
89
+ this.rep0LongDecoders = initArray(MATCH_DECODERS_SIZE);
90
+ this.repDecoders = initArray(REP_DECODERS_SIZE);
91
+ this.repG0Decoders = initArray(REP_DECODERS_SIZE);
92
+ this.repG1Decoders = initArray(REP_DECODERS_SIZE);
93
+ this.repG2Decoders = initArray(REP_DECODERS_SIZE);
94
+ this.posDecoders = initArray(POS_DECODERS_SIZE);
95
+
96
+ // Initialize literal decoder
97
+ this.literalDecoder = {
98
+ coders: [],
99
+ numPrevBits: 0,
100
+ numPosBits: 0,
101
+ posMask: 0,
102
+ init: () => this.initLiteralDecoder(),
103
+ };
104
+
105
+ // Initialize position slot decoders (4 different length-to-position states)
106
+ for (let i = 0; i < 4; i++) {
107
+ this.posSlotDecoders[i] = createBitTree(6);
108
+ }
109
+
110
+ // Initialize length decoders
111
+ this.lenDecoder = this.createLenDecoder();
112
+ this.repLenDecoder = this.createLenDecoder();
113
+
114
+ // Initialize position alignment decoder
115
+ this.posAlignDecoder = createBitTree(4);
116
+ }
117
+
118
+ /**
119
+ * Read LZMA header, configure decoder, and prepare for decompression.
120
+ */
121
+ initDecompression(input: InputBuffer, output: OutputBuffer): void {
122
+ // Read 5-byte header properties
123
+ const properties: number[] = [];
124
+ for (let i = 0; i < 5; ++i) {
125
+ const r = input.readByte();
126
+ if (r === -1) {
127
+ throw new Error("truncated input");
128
+ }
129
+ properties[i] = r << 24 >> 24;
130
+ }
131
+
132
+ if (!this.setDecoderProperties(properties)) {
133
+ throw new Error("corrupted input");
134
+ }
135
+
136
+ // Read 8-byte uncompressed length from header
137
+ let outSize: bigint;
138
+ {
139
+ let value = 0n;
140
+ for (let i = 0; i < 8; i++) {
141
+ const r = input.readByte();
142
+ if (r === -1) {
143
+ throw new Error("truncated input");
144
+ }
145
+ value += BigInt(r & 0xFF) << BigInt(i * 8);
146
+ }
147
+ // Check for unknown size marker (all 0xFF bytes)
148
+ if (value === 0xFFFFFFFFFFFFFFFFn) {
149
+ outSize = -1n;
150
+ } else if (value > BigInt(_MAX_UINT32)) {
151
+ outSize = -1n;
152
+ } else {
153
+ outSize = value;
154
+ }
155
+ }
156
+
157
+ // Set up range decoder and output window
158
+ this.rangeDecoder.setStream(input);
159
+ this.flush();
160
+ this.outWindow.stream = null;
161
+ this.outWindow.stream = output;
162
+
163
+ // Initialize decoder state
164
+ this.init();
165
+ this.state = 0;
166
+ this.rep0 = 0;
167
+ this.rep1 = 0;
168
+ this.rep2 = 0;
169
+ this.rep3 = 0;
170
+ this.outSize = outSize;
171
+ this.nowPos64 = 0n;
172
+ this.prevByte = 0;
173
+ }
174
+
175
+ /**
176
+ * Full decompression: read header, decode all chunks, flush and cleanup.
177
+ */
178
+ decompress(input: InputBuffer, output: OutputBuffer): void {
179
+ this.initDecompression(input, output);
180
+
181
+ while (true) {
182
+ const result = this.codeOneChunk();
183
+ if (result === -1) {
184
+ throw new Error("corrupted input");
185
+ }
186
+
187
+ const isOutputComplete = (this.outSize >= 0n)
188
+ && (this.nowPos64 >= this.outSize);
189
+
190
+ if (result || isOutputComplete) {
191
+ this.flush();
192
+ this.cleanup();
193
+ return;
194
+ }
195
+ }
196
+ }
197
+
198
+ createLenDecoder(): LenDecoder {
199
+ const decoder = {
200
+ choice: initArray(CHOICE_ARRAY_SIZE),
201
+ lowCoder: [] as BitTree[],
202
+ midCoder: [] as BitTree[],
203
+ highCoder: createBitTree(0x08),
204
+ numPosStates: 0,
205
+ };
206
+
207
+ return decoder;
208
+ }
209
+
210
+ setDecoderProperties(properties: number[]): boolean {
211
+ if (properties.length < 5) {
212
+ return false;
213
+ }
214
+
215
+ const lc = properties[0] % 9;
216
+ const remainder = Math.floor(properties[0] / 9);
217
+ const lp = remainder % 5;
218
+ const pb = Math.floor(remainder / 5);
219
+
220
+ if (pb > 4) {
221
+ return false;
222
+ }
223
+
224
+ // Set literal decoder properties
225
+ this.literalDecoder.numPrevBits = lc;
226
+ this.literalDecoder.numPosBits = lp;
227
+ this.literalDecoder.posMask = (1 << lp) - 1;
228
+
229
+ // Set position state mask
230
+ this.posStateMask = (1 << pb) - 1;
231
+
232
+ // Calculate dictionary size from properties[1-4]
233
+ let dictSize = 0;
234
+ for (let i = 0; i < 4; i++) {
235
+ // Treat bytes as unsigned (0-255) instead of signed (-128 to 127)
236
+ const unsignedByte = properties[1 + i] & 0xFF;
237
+ dictSize += unsignedByte << (i * 8);
238
+ }
239
+
240
+ // Set dictionary size and check value
241
+ this.dictSizeCheck = Math.max(dictSize, 1);
242
+
243
+ // Initialize output window
244
+ if (dictSize > 0) {
245
+ this.outWindow.windowSize = Math.max(dictSize, 4096);
246
+ this.outWindow.buffer = new Uint8Array(this.outWindow.windowSize);
247
+ }
248
+
249
+ // Initialize literal decoder coders
250
+ const numStates = 1 << (this.literalDecoder.numPrevBits + this.literalDecoder.numPosBits);
251
+ this.literalDecoder.coders = [];
252
+ for (let i = 0; i < numStates; i++) {
253
+ this.literalDecoder.coders[i] = {
254
+ decoders: initArray(LITERAL_DECODER_SIZE), // 0x300
255
+ };
256
+ }
257
+
258
+ // Initialize length decoders
259
+ this.lenDecoder.numPosStates = 1 << pb;
260
+ this.repLenDecoder.numPosStates = 1 << pb;
261
+
262
+ // Initialize low and mid coders for length decoders
263
+ this.lenDecoder.lowCoder = [];
264
+ this.lenDecoder.midCoder = [];
265
+ this.repLenDecoder.lowCoder = [];
266
+ this.repLenDecoder.midCoder = [];
267
+
268
+ for (let posState = 0; posState < (1 << pb); posState++) {
269
+ this.lenDecoder.lowCoder[posState] = createBitTree(3);
270
+ this.lenDecoder.midCoder[posState] = createBitTree(3);
271
+ this.repLenDecoder.lowCoder[posState] = createBitTree(3);
272
+ this.repLenDecoder.midCoder[posState] = createBitTree(3);
273
+ }
274
+
275
+ return true;
276
+ }
277
+
278
+ // Methods that modify decoder state
279
+ copyBlock(len: number): void {
280
+ const outputWindow = this.outWindow;
281
+ const distance = this.rep0;
282
+
283
+ let pos = outputWindow.pos - distance - 1;
284
+
285
+ if (pos < 0) {
286
+ pos += outputWindow.windowSize;
287
+ }
288
+
289
+ for (; len != 0; len -= 1) {
290
+ if (pos >= outputWindow.windowSize) {
291
+ pos = 0;
292
+ }
293
+ outputWindow.buffer![outputWindow.pos] = outputWindow.buffer![pos];
294
+ outputWindow.pos += 1;
295
+ pos += 1;
296
+
297
+ if (outputWindow.pos >= outputWindow.windowSize) {
298
+ this.flush();
299
+ }
300
+ }
301
+ }
302
+
303
+ putByte(b: number): void {
304
+ this.outWindow.buffer![this.outWindow.pos] = b;
305
+ this.outWindow.pos += 1;
306
+
307
+ if (this.outWindow.pos >= this.outWindow.windowSize) {
308
+ this.flush();
309
+ }
310
+ }
311
+
312
+ getByte(distance: number): number {
313
+ const outputWindow = this.outWindow;
314
+
315
+ let pos = outputWindow.pos - distance - 1;
316
+ if (pos < 0) {
317
+ pos += outputWindow.windowSize;
318
+ }
319
+
320
+ return outputWindow.buffer![pos];
321
+ }
322
+
323
+ getDecoder(pos: number, prevByte: number): any {
324
+ // Calculate index based on position and previous byte
325
+ const positionMask = pos & this.literalDecoder.posMask;
326
+ const prevBitsMask = (prevByte & 0xFF) >>> (8 - this.literalDecoder.numPrevBits);
327
+ const index = (positionMask << this.literalDecoder.numPrevBits) + prevBitsMask;
328
+
329
+ // Return decoder at calculated index
330
+ return this.literalDecoder.coders[index];
331
+ }
332
+
333
+ initLiteralDecoder(): void {
334
+ let numStates = 1 << (this.literalDecoder.numPrevBits + this.literalDecoder.numPosBits);
335
+
336
+ for (let i = 0; i < numStates; ++i) {
337
+ // Initialize bit models for each coder
338
+ for (let j = 0; j < this.literalDecoder.coders[i].decoders.length; j++) {
339
+ this.literalDecoder.coders[i].decoders[j] = 1024;
340
+ }
341
+ }
342
+ }
343
+
344
+ init(): void {
345
+ this.outWindow.streamPos = 0;
346
+ this.outWindow.pos = 0;
347
+
348
+ initBitModels(this.matchDecoders);
349
+ initBitModels(this.rep0LongDecoders);
350
+ initBitModels(this.repDecoders);
351
+ initBitModels(this.repG0Decoders);
352
+ initBitModels(this.repG1Decoders);
353
+ initBitModels(this.repG2Decoders);
354
+ initBitModels(this.posDecoders);
355
+
356
+ this.initLiteralDecoder();
357
+
358
+ for (let i = 0; i < 4; ++i) {
359
+ initBitModels(this.posSlotDecoders[i].models);
360
+ }
361
+
362
+ this.initLenDecoder(this.lenDecoder);
363
+ this.initLenDecoder(this.repLenDecoder);
364
+ initBitModels(this.posAlignDecoder.models);
365
+ this.initRangeDecoder();
366
+ }
367
+
368
+ initLenDecoder(decoder: LenDecoder): void {
369
+ initBitModels(decoder.choice);
370
+
371
+ for (let posState = 0; posState < decoder.numPosStates; ++posState) {
372
+ initBitModels(decoder.lowCoder[posState].models);
373
+ initBitModels(decoder.midCoder[posState].models);
374
+ }
375
+
376
+ initBitModels(decoder.highCoder.models);
377
+ }
378
+
379
+ outWindowReleaseStream(): void {
380
+ this.flush();
381
+ this.outWindow.stream = null;
382
+ }
383
+
384
+ decodeBit(probs: number[], index: number): 0 | 1 {
385
+ return this.rangeDecoder.decodeBit(probs, index);
386
+ }
387
+
388
+ decodeDirectBits(numTotalBits: number): number {
389
+ return this.rangeDecoder.decodeDirectBits(numTotalBits);
390
+ }
391
+
392
+ initRangeDecoder(): void {
393
+ this.rangeDecoder.init();
394
+ }
395
+
396
+ rangeBitTreeDecoder(bitTree: BitTree): number {
397
+ let bitIndex, m = 1;
398
+
399
+ for (bitIndex = bitTree.numBitLevels; bitIndex != 0; bitIndex -= 1) {
400
+ m = (m << 1) + this.decodeBit(bitTree.models, m);
401
+ }
402
+
403
+ return m - (1 << bitTree.numBitLevels);
404
+ }
405
+
406
+ reverseDecode(models: number[], startIndex: number, numBitLevels: number): number {
407
+ let symbol = 0;
408
+
409
+ for (let bitIndex = 0, m = 1, bit: number; bitIndex < numBitLevels; ++bitIndex) {
410
+ bit = this.decodeBit(models, startIndex + m);
411
+ m <<= 1;
412
+ m += bit;
413
+ symbol |= bit << bitIndex;
414
+ }
415
+
416
+ return symbol;
417
+ }
418
+
419
+ reverseDecodeAlignDecoder(): number {
420
+ let symbol = 0;
421
+
422
+ for (let m = 1, bitIndex = 0, bit: number; bitIndex < this.posAlignDecoder.numBitLevels; ++bitIndex) {
423
+ bit = this.decodeBit(this.posAlignDecoder.models, m);
424
+ m <<= 1;
425
+ m += bit;
426
+ symbol |= bit << bitIndex;
427
+ }
428
+
429
+ return symbol;
430
+ }
431
+
432
+ // Update the placeholder implementations with actual logic
433
+ decodeNormalWithRangeDecoder(decoder: any): number {
434
+ let symbol = 1;
435
+ do {
436
+ symbol = symbol << 1 | this.decodeBit(decoder.decoders, symbol);
437
+ } while (symbol < 0x100);
438
+
439
+ return symbol << 24 >> 24;
440
+ }
441
+
442
+ decodeWithMatchByteWithRangeDecoder(encoder: any, matchByte: number): number {
443
+ let bit, matchBit, symbol = 1;
444
+ do {
445
+ matchBit = (matchByte >> 7) & 1;
446
+ matchByte <<= 1;
447
+ bit = this.decodeBit(encoder.decoders, ((1 + matchBit) << 8) + symbol);
448
+ symbol = symbol << 1 | bit;
449
+
450
+ if (matchBit != bit) {
451
+ while (symbol < 0x100) {
452
+ symbol = symbol << 1 | this.decodeBit(encoder.decoders, symbol);
453
+ }
454
+ break;
455
+ }
456
+ } while (symbol < 0x100);
457
+
458
+ return symbol << 24 >> 24;
459
+ }
460
+
461
+ decodeLenWithRangeDecoder(decoder: LenDecoder, posState: number): number {
462
+ if (!this.decodeBit(decoder.choice, 0)) {
463
+ return this.rangeBitTreeDecoder(decoder.lowCoder[posState]);
464
+ }
465
+
466
+ let symbol = 0x08;
467
+
468
+ if (!this.decodeBit(decoder.choice, 1)) {
469
+ symbol += this.rangeBitTreeDecoder(decoder.midCoder[posState]);
470
+ } else {
471
+ symbol += 0x08 + this.rangeBitTreeDecoder(decoder.highCoder);
472
+ }
473
+
474
+ return symbol;
475
+ }
476
+
477
+ codeOneChunk(): 0 | 1 | -1 {
478
+ let decoder2: any, distance: number, len: number, numDirectBits: number, positionSlot: number;
479
+
480
+ let posState = Number(this.nowPos64 & 0xFFFFFFFFn) & this.posStateMask;
481
+
482
+ if (!this.decodeBit(this.matchDecoders, (this.state << 4) + posState)) {
483
+ decoder2 = this.getDecoder(Number(this.nowPos64 & 0xFFFFFFFFn), this.prevByte);
484
+
485
+ if (this.state < 7) {
486
+ this.prevByte = this.decodeNormalWithRangeDecoder(decoder2);
487
+ } else {
488
+ this.prevByte = this.decodeWithMatchByteWithRangeDecoder(decoder2, this.getByte(this.rep0));
489
+ }
490
+
491
+ this.putByte(this.prevByte);
492
+ this.state = stateUpdateChar(this.state);
493
+ this.nowPos64 += 1n;
494
+ } else {
495
+ if (this.decodeBit(this.repDecoders, this.state)) {
496
+ len = 0;
497
+ if (!this.decodeBit(this.repG0Decoders, this.state)) {
498
+ if (!this.decodeBit(this.rep0LongDecoders, (this.state << 4) + posState)) {
499
+ this.state = this.state < 7 ? 9 : 11;
500
+ len = 1;
501
+ }
502
+ } else {
503
+ if (!this.decodeBit(this.repG1Decoders, this.state)) {
504
+ distance = this.rep1;
505
+ } else {
506
+ if (!this.decodeBit(this.repG2Decoders, this.state)) {
507
+ distance = this.rep2;
508
+ } else {
509
+ distance = this.rep3;
510
+ this.rep3 = this.rep2;
511
+ }
512
+ this.rep2 = this.rep1;
513
+ }
514
+
515
+ this.rep1 = this.rep0;
516
+ this.rep0 = distance;
517
+ }
518
+
519
+ if (!len) {
520
+ len = this.decodeLenWithRangeDecoder(this.repLenDecoder, posState) + 2;
521
+ this.state = this.state < 7 ? 0x08 : 11;
522
+ }
523
+ } else {
524
+ this.rep3 = this.rep2;
525
+ this.rep2 = this.rep1;
526
+ this.rep1 = this.rep0;
527
+
528
+ len = 2 + this.decodeLenWithRangeDecoder(this.lenDecoder, posState);
529
+ this.state = this.state < 7 ? 7 : 10;
530
+
531
+ positionSlot = this.rangeBitTreeDecoder(this.posSlotDecoders[getLenToPosState(len)]);
532
+
533
+ if (positionSlot >= 4) {
534
+ numDirectBits = (positionSlot >> 1) - 1;
535
+ this.rep0 = (2 | (positionSlot & 1)) << numDirectBits;
536
+
537
+ if (positionSlot < 14) {
538
+ this.rep0 += this.reverseDecode(
539
+ this.posDecoders,
540
+ this.rep0 - positionSlot - 1,
541
+ numDirectBits,
542
+ );
543
+ } else {
544
+ this.rep0 += this.decodeDirectBits(numDirectBits - 4) << 4;
545
+ this.rep0 += this.reverseDecodeAlignDecoder();
546
+
547
+ if (this.rep0 < 0) {
548
+ if (this.rep0 == -1) {
549
+ return 1;
550
+ }
551
+ return -1;
552
+ }
553
+ }
554
+ } else {
555
+ this.rep0 = positionSlot;
556
+ }
557
+ }
558
+
559
+ if (BigInt(this.rep0) >= this.nowPos64 || this.rep0 >= this.dictSizeCheck) {
560
+ return -1;
561
+ }
562
+
563
+ this.copyBlock(len);
564
+ this.nowPos64 += BigInt(len);
565
+ this.prevByte = this.getByte(0);
566
+ }
567
+
568
+ return 0;
569
+ }
570
+
571
+ writeToOutput(buffer: OutputBuffer, data: Uint8Array, offset: number, length: number): void {
572
+ buffer.writeBytes(data, offset, length);
573
+ }
574
+
575
+ flush(): void {
576
+ const size = this.outWindow.pos - this.outWindow.streamPos;
577
+
578
+ if (!size) {
579
+ return;
580
+ }
581
+
582
+ if (this.outWindow.stream && this.outWindow.buffer) {
583
+ this.outWindow.stream.writeBytes(
584
+ this.outWindow.buffer,
585
+ this.outWindow.streamPos,
586
+ size,
587
+ );
588
+ }
589
+
590
+ if (this.outWindow.pos >= this.outWindow.windowSize) {
591
+ this.outWindow.pos = 0;
592
+ }
593
+
594
+ this.outWindow.streamPos = this.outWindow.pos;
595
+ }
596
+
597
+ /**
598
+ * Cleanup decoder resources
599
+ */
600
+ cleanup(): void {
601
+ this.outWindow.stream = null;
602
+ this.rangeDecoder.stream = null;
603
+ }
604
+ }