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.
@@ -0,0 +1,548 @@
1
+ import type { InputBuffer } from "./streams.js";
2
+ import {
3
+ CRC32_TABLE,
4
+ DICTIONARY_SIZE_THRESHOLD,
5
+ } from "./utils.js";
6
+
7
+ /**
8
+ * BinTreeMatchFinder — Binary tree match finder for LZMA encoding.
9
+ *
10
+ * Combines input window management, hash-based match finding, and
11
+ * binary tree search into a single cohesive class.
12
+ */
13
+ export class BinTreeMatchFinder {
14
+ // Input window fields
15
+ _posLimit: number = 0;
16
+ _bufferBase: Uint8Array = new Uint8Array(0);
17
+ _pos: number = 0;
18
+ _streamPos: number = 0;
19
+ _streamEndWasReached: number = 0;
20
+ _bufferOffset: number = 0;
21
+ _blockSize: number = 0;
22
+ _keepSizeBefore: number = 0;
23
+ _keepSizeAfter: number = 0;
24
+ _pointerToLastSafePosition: number = 0;
25
+ _stream: InputBuffer | null = null;
26
+
27
+ // Hash and tree fields
28
+ HASH_ARRAY: boolean = true;
29
+ kNumHashDirectBytes: number = 0;
30
+ kMinMatchCheck: number = 4;
31
+ kFixHashSize: number = 66560;
32
+ _hashMask: number = 0;
33
+ _hashSizeSum: number = 0;
34
+ _hash: Int32Array = new Int32Array(0);
35
+ _cyclicBufferSize: number = 0;
36
+ _cyclicBufferPos: number = 0;
37
+ _son: Int32Array = new Int32Array(0);
38
+ _matchMaxLen: number = 0;
39
+ _cutValue: number = 0xff;
40
+
41
+ // --- LzInWindow methods ---
42
+
43
+ getIndexByte(index: number): number {
44
+ return this._bufferBase[this._bufferOffset + this._pos + index];
45
+ }
46
+
47
+ getMatchLen(index: number, distance: number, limit: number): number {
48
+ if (this._streamEndWasReached) {
49
+ if (this._pos + index + limit > this._streamPos) {
50
+ limit = this._streamPos - (this._pos + index);
51
+ }
52
+ }
53
+
54
+ ++distance;
55
+ let i;
56
+ const pby = this._bufferOffset + this._pos + index;
57
+
58
+ for (
59
+ i = 0;
60
+ i < limit
61
+ && this._bufferBase[pby + i]
62
+ == this._bufferBase[pby + i - distance];
63
+ ++i
64
+ );
65
+
66
+ return i;
67
+ }
68
+
69
+ getNumAvailableBytes(): number {
70
+ return this._streamPos - this._pos;
71
+ }
72
+
73
+ moveBlock(): void {
74
+ let offset = this._bufferOffset + this._pos - this._keepSizeBefore;
75
+
76
+ if (offset > 0) {
77
+ --offset;
78
+ }
79
+
80
+ const numBytes = this._bufferOffset + this._streamPos - offset;
81
+ this._bufferBase.copyWithin(0, offset, offset + numBytes);
82
+
83
+ this._bufferOffset -= offset;
84
+ }
85
+
86
+ movePosInWindow(): void {
87
+ this._pos += 1;
88
+
89
+ if (this._pos > this._posLimit) {
90
+ const pointerToPosition = this._bufferOffset + this._pos;
91
+
92
+ if (pointerToPosition > this._pointerToLastSafePosition) {
93
+ this.moveBlock();
94
+ }
95
+
96
+ this.readBlock();
97
+ }
98
+ }
99
+
100
+ readBlock(): void {
101
+ if (this._streamEndWasReached) {
102
+ return;
103
+ }
104
+
105
+ while (true) {
106
+ const size = -this._bufferOffset + this._blockSize - this._streamPos;
107
+ if (!size) {
108
+ return;
109
+ }
110
+
111
+ const bytesRead = this.readFromStream(
112
+ this._bufferOffset + this._streamPos,
113
+ size,
114
+ );
115
+
116
+ if (bytesRead == -1) {
117
+ this._posLimit = this._streamPos;
118
+ const pointerToPosition = this._bufferOffset + this._posLimit;
119
+
120
+ if (pointerToPosition > this._pointerToLastSafePosition) {
121
+ this._posLimit = this._pointerToLastSafePosition - this._bufferOffset;
122
+ }
123
+
124
+ this._streamEndWasReached = 1;
125
+ return;
126
+ }
127
+
128
+ this._streamPos += bytesRead;
129
+ if (this._streamPos >= this._pos + this._keepSizeAfter) {
130
+ this._posLimit = this._streamPos - this._keepSizeAfter;
131
+ }
132
+ }
133
+ }
134
+
135
+ reduceOffsets(subValue: number): void {
136
+ this._bufferOffset += subValue;
137
+ this._posLimit -= subValue;
138
+ this._pos -= subValue;
139
+ this._streamPos -= subValue;
140
+ }
141
+
142
+ private readFromStream(off: number, len: number): number {
143
+ const stream = this._stream!;
144
+ const buffer = this._bufferBase;
145
+
146
+ return stream.readBytes(buffer, off, len);
147
+ }
148
+
149
+ // --- Match finder configuration methods ---
150
+
151
+ createBuffers(
152
+ keepSizeBefore: number,
153
+ keepSizeAfter: number,
154
+ keepSizeReserv: number,
155
+ ): void {
156
+ this._keepSizeBefore = keepSizeBefore;
157
+ this._keepSizeAfter = keepSizeAfter;
158
+ const blockSize = keepSizeBefore + keepSizeAfter + keepSizeReserv;
159
+
160
+ if (this._blockSize != blockSize) {
161
+ this._bufferBase = new Uint8Array(blockSize);
162
+ this._blockSize = blockSize;
163
+ }
164
+
165
+ this._pointerToLastSafePosition = this._blockSize - keepSizeAfter;
166
+ }
167
+
168
+ create(
169
+ dictionarySize: number,
170
+ numFastBytes: number,
171
+ keepAddBufferBefore: number,
172
+ keepAddBufferAfter: number,
173
+ ): void {
174
+ if (dictionarySize < DICTIONARY_SIZE_THRESHOLD) {
175
+ this._cutValue = 0x10 + (numFastBytes >> 1);
176
+
177
+ const windowReservSize = ~~((dictionarySize + keepAddBufferBefore + numFastBytes + keepAddBufferAfter) / 2) + 0x100;
178
+
179
+ this.createBuffers(
180
+ dictionarySize + keepAddBufferBefore,
181
+ numFastBytes + keepAddBufferAfter,
182
+ windowReservSize,
183
+ );
184
+
185
+ this._matchMaxLen = numFastBytes;
186
+
187
+ // Ensure cyclic buffer
188
+ const cyclicBufferSize = dictionarySize + 1;
189
+ if (this._cyclicBufferSize !== cyclicBufferSize) {
190
+ this._cyclicBufferSize = cyclicBufferSize;
191
+ this._son = new Int32Array(cyclicBufferSize * 2);
192
+ }
193
+
194
+ // Compute hash size
195
+ let hs = 0x10000;
196
+
197
+ if (this.HASH_ARRAY) {
198
+ hs = dictionarySize - 1;
199
+ hs |= hs >> 1;
200
+ hs |= hs >> 2;
201
+ hs |= hs >> 4;
202
+ hs |= hs >> 0x08;
203
+ hs >>= 1;
204
+ hs |= 0xFFFF;
205
+
206
+ if (hs > 0x1000000) {
207
+ hs >>= 1;
208
+ }
209
+
210
+ this._hashMask = hs;
211
+ hs += 1;
212
+
213
+ const finalHashSizeSum = hs + this.kFixHashSize;
214
+
215
+ if (finalHashSizeSum !== this._hashSizeSum) {
216
+ this._hashSizeSum = finalHashSizeSum;
217
+ this._hash = new Int32Array(finalHashSizeSum);
218
+ }
219
+ } else {
220
+ if (hs !== this._hashSizeSum) {
221
+ this._hashSizeSum = hs;
222
+ this._hash = new Int32Array(hs);
223
+ }
224
+ }
225
+ }
226
+ }
227
+
228
+ // --- Match finding methods ---
229
+
230
+ getMatches(distances: number[]): number {
231
+ let count,
232
+ cur,
233
+ curMatch,
234
+ curMatch2,
235
+ curMatch3,
236
+ cyclicPos,
237
+ delta,
238
+ hash2Value,
239
+ hash3Value,
240
+ hashValue,
241
+ len,
242
+ len0,
243
+ len1,
244
+ lenLimit,
245
+ matchMinPos,
246
+ maxLen,
247
+ offset,
248
+ pby1,
249
+ ptr0,
250
+ ptr1,
251
+ temp;
252
+
253
+ if (this._pos + this._matchMaxLen <= this._streamPos) {
254
+ lenLimit = this._matchMaxLen;
255
+ } else {
256
+ lenLimit = this._streamPos - this._pos;
257
+ if (lenLimit < this.kMinMatchCheck) {
258
+ this.movePos();
259
+ return 0;
260
+ }
261
+ }
262
+
263
+ offset = 0;
264
+ matchMinPos = this._pos > this._cyclicBufferSize
265
+ ? this._pos - this._cyclicBufferSize
266
+ : 0;
267
+
268
+ cur = this._bufferOffset + this._pos;
269
+ maxLen = 1;
270
+ hash2Value = 0;
271
+ hash3Value = 0;
272
+
273
+ if (this.HASH_ARRAY) {
274
+ temp = CRC32_TABLE[this._bufferBase[cur]] ^ this._bufferBase[cur + 1];
275
+ hash2Value = temp & 0x3FF;
276
+ temp ^= this._bufferBase[cur + 2] << 0x08;
277
+ hash3Value = temp & 0xFFFF;
278
+ hashValue = (temp ^ (CRC32_TABLE[this._bufferBase[cur + 3]] << 5)) & this._hashMask;
279
+ } else {
280
+ hashValue = this._bufferBase[cur] ^ (this._bufferBase[cur + 1] << 0x08);
281
+ }
282
+
283
+ curMatch = this._hash[this.kFixHashSize + hashValue] || 0;
284
+ if (this.HASH_ARRAY) {
285
+ curMatch2 = this._hash[hash2Value] || 0;
286
+ curMatch3 = this._hash[0x400 + hash3Value] || 0;
287
+ this._hash[hash2Value] = this._pos;
288
+ this._hash[0x400 + hash3Value] = this._pos;
289
+
290
+ if (curMatch2 > matchMinPos) {
291
+ if (this._bufferBase[this._bufferOffset + curMatch2] == this._bufferBase[cur]) {
292
+ distances[offset++] = maxLen = 2;
293
+ distances[offset++] = this._pos - curMatch2 - 1;
294
+ }
295
+ }
296
+
297
+ if (curMatch3 > matchMinPos) {
298
+ if (this._bufferBase[this._bufferOffset + curMatch3] == this._bufferBase[cur]) {
299
+ if (curMatch3 == curMatch2) {
300
+ offset -= 2;
301
+ }
302
+ distances[offset++] = maxLen = 3;
303
+ distances[offset++] = this._pos - curMatch3 - 1;
304
+ curMatch2 = curMatch3;
305
+ }
306
+ }
307
+
308
+ if (offset != 0 && curMatch2 == curMatch) {
309
+ offset -= 2;
310
+ maxLen = 1;
311
+ }
312
+ }
313
+
314
+ this._hash[this.kFixHashSize + hashValue] = this._pos;
315
+ ptr0 = (this._cyclicBufferPos << 1) + 1;
316
+ ptr1 = this._cyclicBufferPos << 1;
317
+ len0 = len1 = this.kNumHashDirectBytes;
318
+
319
+ if (this.kNumHashDirectBytes != 0) {
320
+ if (curMatch > matchMinPos) {
321
+ if (
322
+ this._bufferBase[
323
+ this._bufferOffset + curMatch + this.kNumHashDirectBytes
324
+ ] != this._bufferBase[cur + this.kNumHashDirectBytes]
325
+ ) {
326
+ distances[offset++] = maxLen = this.kNumHashDirectBytes;
327
+ distances[offset++] = this._pos - curMatch - 1;
328
+ }
329
+ }
330
+ }
331
+ count = this._cutValue;
332
+
333
+ while (1) {
334
+ if (curMatch <= matchMinPos || count == 0) {
335
+ count -= 1;
336
+ this._son[ptr0] = this._son[ptr1] = 0;
337
+ break;
338
+ }
339
+ delta = this._pos - curMatch;
340
+
341
+ cyclicPos = (delta <= this._cyclicBufferPos
342
+ ? this._cyclicBufferPos - delta
343
+ : this._cyclicBufferPos - delta + this._cyclicBufferSize) << 1;
344
+
345
+ pby1 = this._bufferOffset + curMatch;
346
+ len = len0 < len1 ? len0 : len1;
347
+
348
+ if (
349
+ this._bufferBase[pby1 + len] == this._bufferBase[cur + len]
350
+ ) {
351
+ while ((len += 1) != lenLimit) {
352
+ if (
353
+ this._bufferBase[pby1 + len] != this._bufferBase[cur + len]
354
+ ) {
355
+ break;
356
+ }
357
+ }
358
+
359
+ if (maxLen < len) {
360
+ distances[offset++] = maxLen = len;
361
+ distances[offset++] = delta - 1;
362
+ if (len == lenLimit) {
363
+ this._son[ptr1] = this._son[cyclicPos];
364
+ this._son[ptr0] = this._son[cyclicPos + 1];
365
+ break;
366
+ }
367
+ }
368
+ }
369
+
370
+ if (
371
+ this._bufferBase[pby1 + len] < this._bufferBase[cur + len]
372
+ ) {
373
+ this._son[ptr1] = curMatch;
374
+ ptr1 = cyclicPos + 1;
375
+ curMatch = this._son[ptr1];
376
+ len1 = len;
377
+ } else {
378
+ this._son[ptr0] = curMatch;
379
+ ptr0 = cyclicPos;
380
+ curMatch = this._son[ptr0];
381
+ len0 = len;
382
+ }
383
+ }
384
+
385
+ this.movePos();
386
+ return offset;
387
+ }
388
+
389
+ skip(num: number): void {
390
+ let count,
391
+ cur,
392
+ curMatch,
393
+ cyclicPos,
394
+ delta,
395
+ hash2Value,
396
+ hash3Value,
397
+ hashValue,
398
+ len,
399
+ len0,
400
+ len1,
401
+ lenLimit,
402
+ matchMinPos,
403
+ pby1,
404
+ ptr0,
405
+ ptr1,
406
+ temp;
407
+
408
+ do {
409
+ if (this._pos + this._matchMaxLen <= this._streamPos) {
410
+ lenLimit = this._matchMaxLen;
411
+ } else {
412
+ lenLimit = this._streamPos - this._pos;
413
+ if (lenLimit < this.kMinMatchCheck) {
414
+ this.movePos();
415
+ continue;
416
+ }
417
+ }
418
+
419
+ matchMinPos = this._pos > this._cyclicBufferSize
420
+ ? this._pos - this._cyclicBufferSize
421
+ : 0;
422
+
423
+ cur = this._bufferOffset + this._pos;
424
+
425
+ if (this.HASH_ARRAY) {
426
+ temp = CRC32_TABLE[this._bufferBase[cur]] ^ this._bufferBase[cur + 1];
427
+ hash2Value = temp & 0x3FF;
428
+ this._hash[hash2Value] = this._pos;
429
+ temp ^= this._bufferBase[cur + 2] << 0x08;
430
+ hash3Value = temp & 0xFFFF;
431
+ this._hash[0x400 + hash3Value] = this._pos;
432
+ hashValue = (temp ^ (CRC32_TABLE[this._bufferBase[cur + 3]] << 5)) & this._hashMask;
433
+ } else {
434
+ hashValue = this._bufferBase[cur] ^ (this._bufferBase[cur + 1] << 0x08);
435
+ }
436
+
437
+ curMatch = this._hash[this.kFixHashSize + hashValue];
438
+ this._hash[this.kFixHashSize + hashValue] = this._pos;
439
+ ptr0 = (this._cyclicBufferPos << 1) + 1;
440
+ ptr1 = this._cyclicBufferPos << 1;
441
+ len0 = len1 = this.kNumHashDirectBytes;
442
+ count = this._cutValue;
443
+
444
+ while (1) {
445
+ if (curMatch <= matchMinPos || count == 0) {
446
+ count -= 1;
447
+ this._son[ptr0] = this._son[ptr1] = 0;
448
+ break;
449
+ }
450
+ delta = this._pos - curMatch;
451
+
452
+ cyclicPos = (delta <= this._cyclicBufferPos
453
+ ? this._cyclicBufferPos - delta
454
+ : this._cyclicBufferPos - delta + this._cyclicBufferSize) << 1;
455
+
456
+ pby1 = this._bufferOffset + curMatch;
457
+
458
+ len = len0 < len1 ? len0 : len1;
459
+
460
+ if (this._bufferBase[pby1 + len] == this._bufferBase[cur + len]) {
461
+ while ((len += 1) != lenLimit) {
462
+ if (
463
+ this._bufferBase[pby1 + len] != this._bufferBase[cur + len]
464
+ ) {
465
+ break;
466
+ }
467
+ }
468
+
469
+ if (len == lenLimit) {
470
+ this._son[ptr1] = this._son[cyclicPos];
471
+ this._son[ptr0] = this._son[cyclicPos + 1];
472
+ break;
473
+ }
474
+ }
475
+
476
+ if (this._bufferBase[pby1 + len] < this._bufferBase[cur + len]) {
477
+ this._son[ptr1] = curMatch;
478
+ ptr1 = cyclicPos + 1;
479
+ curMatch = this._son[ptr1];
480
+ len1 = len;
481
+ } else {
482
+ this._son[ptr0] = curMatch;
483
+ ptr0 = cyclicPos;
484
+ curMatch = this._son[ptr0];
485
+ len0 = len;
486
+ }
487
+ }
488
+ this.movePos();
489
+ } while ((num -= 1) != 0);
490
+ }
491
+
492
+ movePos(): void {
493
+ if ((this._cyclicBufferPos += 1) >= this._cyclicBufferSize) {
494
+ this._cyclicBufferPos = 0;
495
+ }
496
+
497
+ this.movePosInWindow();
498
+
499
+ if (this._pos == DICTIONARY_SIZE_THRESHOLD) {
500
+ const subValue = this._pos - this._cyclicBufferSize;
501
+
502
+ this.normalizeLinks(this._son, this._cyclicBufferSize * 2, subValue);
503
+ this.normalizeLinks(this._hash, this._hashSizeSum, subValue);
504
+
505
+ this.reduceOffsets(subValue);
506
+ }
507
+ }
508
+
509
+ init(): void {
510
+ this._bufferOffset = 0;
511
+ this._pos = 0;
512
+ this._streamPos = 0;
513
+ this._streamEndWasReached = 0;
514
+ this.readBlock();
515
+
516
+ this._cyclicBufferPos = 0;
517
+ this.reduceOffsets(-1);
518
+ }
519
+
520
+ /**
521
+ * This is only called after reading one whole gigabyte.
522
+ */
523
+ normalizeLinks(items: Int32Array, numItems: number, subValue: number): void {
524
+ for (let i = 0; i < numItems; ++i) {
525
+ let value = items[i];
526
+ if (value <= subValue) {
527
+ value = 0;
528
+ } else {
529
+ value -= subValue;
530
+ }
531
+ items[i] = value;
532
+ }
533
+ }
534
+
535
+ setType(numHashBytes: number): void {
536
+ this.HASH_ARRAY = numHashBytes > 2;
537
+
538
+ if (this.HASH_ARRAY) {
539
+ this.kNumHashDirectBytes = 0;
540
+ this.kMinMatchCheck = 4;
541
+ this.kFixHashSize = 66560;
542
+ } else {
543
+ this.kNumHashDirectBytes = 2;
544
+ this.kMinMatchCheck = 3;
545
+ this.kFixHashSize = 0;
546
+ }
547
+ }
548
+ }
@@ -0,0 +1,109 @@
1
+ import {
2
+ type BasicRangeDecoder,
3
+ type BasicRangeEncoder,
4
+ getBitPrice,
5
+ initArray,
6
+ } from "./utils.js";
7
+
8
+ export class RangeBitTreeCoder {
9
+ private models: number[];
10
+ private numBitLevels: number;
11
+
12
+ constructor(numBitLevels: number) {
13
+ this.numBitLevels = numBitLevels;
14
+ this.models = initArray(1 << numBitLevels, 1024); // Initialize with default probability
15
+ }
16
+
17
+ /**
18
+ * Decode symbol using range decoder
19
+ */
20
+ decode(rd: BasicRangeDecoder): number {
21
+ let res = 1;
22
+ for (let bitIndex = this.numBitLevels; bitIndex !== 0; bitIndex--) {
23
+ const bit = rd.decodeBit(this.models, res);
24
+ res = (res << 1) + bit;
25
+ }
26
+ res -= 1 << this.numBitLevels;
27
+ return res;
28
+ }
29
+
30
+ /**
31
+ * Reverse decode symbol using range decoder
32
+ */
33
+ reverseDecode(rd: BasicRangeDecoder): number {
34
+ let index = 1;
35
+ let res = 0;
36
+ for (let bitIndex = 0; bitIndex < this.numBitLevels; bitIndex++) {
37
+ const bit = rd.decodeBit(this.models, index);
38
+ index <<= 1;
39
+ index += bit;
40
+ res |= bit << bitIndex;
41
+ }
42
+ return res;
43
+ }
44
+
45
+ /**
46
+ * Encode symbol using range encoder
47
+ */
48
+ encode(re: BasicRangeEncoder, symbol: number): void {
49
+ let m = 1;
50
+ for (let bitIndex = this.numBitLevels; bitIndex !== 0;) {
51
+ bitIndex--;
52
+ const bit = (symbol >> bitIndex) & 1;
53
+ re.encodeBit(this.models, m, bit);
54
+ m = (m << 1) | bit;
55
+ }
56
+ }
57
+
58
+ /**
59
+ * Reverse encode symbol using range encoder
60
+ */
61
+ reverseEncode(re: BasicRangeEncoder, symbol: number): void {
62
+ let m = 1;
63
+ for (let i = 0; i < this.numBitLevels; i++) {
64
+ const bit = symbol & 1;
65
+ re.encodeBit(this.models, m, bit);
66
+ m = (m << 1) | bit;
67
+ symbol >>= 1;
68
+ }
69
+ }
70
+
71
+ /**
72
+ * Get price for encoding symbol
73
+ */
74
+ getPrice(symbol: number): number {
75
+ let res = 0;
76
+ let m = 1;
77
+ for (let bitIndex = this.numBitLevels; bitIndex !== 0;) {
78
+ bitIndex--;
79
+ const bit = (symbol >> bitIndex) & 1;
80
+ res += getBitPrice(this.models[m], bit);
81
+ m = (m << 1) + bit;
82
+ }
83
+ return res;
84
+ }
85
+
86
+ /**
87
+ * Get price for reverse encoding symbol
88
+ */
89
+ reverseGetPrice(symbol: number): number {
90
+ let res = 0;
91
+ let m = 1;
92
+ for (let i = this.numBitLevels; i !== 0; i--) {
93
+ const bit = symbol & 1;
94
+ symbol >>= 1;
95
+ res += getBitPrice(this.models[m], bit);
96
+ m = (m << 1) | bit;
97
+ }
98
+ return res;
99
+ }
100
+
101
+ /**
102
+ * Reset models to initial state
103
+ */
104
+ reset(): void {
105
+ for (let i = 0; i < this.models.length; i++) {
106
+ this.models[i] = 1024; // Default probability
107
+ }
108
+ }
109
+ }