lzma1 0.1.2 → 0.2.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 +9 -3
- package/lib/chunker.d.ts +46 -0
- package/lib/chunker.js +68 -0
- package/lib/decoder.d.ts +80 -0
- package/lib/decoder.js +469 -0
- package/lib/encoder.d.ts +266 -0
- package/lib/encoder.js +822 -0
- package/lib/index.d.ts +4 -3
- package/lib/index.js +16 -9
- package/lib/len-coder.d.ts +70 -0
- package/lib/len-coder.js +171 -0
- package/lib/lit-coder.d.ts +63 -0
- package/lib/lit-coder.js +162 -0
- package/lib/lz-in-window.d.ts +43 -0
- package/lib/lz-in-window.js +132 -0
- package/lib/lz-window.d.ts +35 -0
- package/lib/lz-window.js +89 -0
- package/lib/lzma.d.ts +97 -56
- package/lib/lzma.js +244 -1485
- package/lib/match-finder-config.d.ts +34 -0
- package/lib/match-finder-config.js +63 -0
- package/lib/range-bit-tree-coder.d.ts +34 -0
- package/lib/range-bit-tree-coder.js +95 -0
- package/lib/range-decoder.d.ts +34 -0
- package/lib/range-decoder.js +98 -0
- package/lib/range-encoder.d.ts +46 -0
- package/lib/range-encoder.js +129 -0
- package/lib/streams.d.ts +32 -0
- package/lib/streams.js +1 -0
- package/lib/utils.d.ts +127 -0
- package/lib/utils.js +300 -0
- package/package.json +3 -3
package/lib/encoder.js
ADDED
|
@@ -0,0 +1,822 @@
|
|
|
1
|
+
import { LenEncoder, } from "./len-coder.js";
|
|
2
|
+
import { LitCoder } from "./lit-coder.js";
|
|
3
|
+
import { add64, compare64, create64, createBitTree, fromInt64, G_FAST_POS, getBitPrice, getLenToPosState, initArray, initBitModels, lowBits64, PROB_PRICES, } from "./utils.js";
|
|
4
|
+
const bitTreePriceCache = new Map();
|
|
5
|
+
/**
|
|
6
|
+
* Calculate price for direct bit encoding
|
|
7
|
+
*/
|
|
8
|
+
function getDirectBitsPrice(numBits) {
|
|
9
|
+
return numBits << 6;
|
|
10
|
+
}
|
|
11
|
+
/**
|
|
12
|
+
* Get price for bit tree encoding with caching
|
|
13
|
+
*/
|
|
14
|
+
function getBitTreePrice(bitTree, symbol) {
|
|
15
|
+
const cacheKey = `${bitTree.numBitLevels}-${symbol}`;
|
|
16
|
+
if (bitTreePriceCache.has(cacheKey)) {
|
|
17
|
+
return bitTreePriceCache.get(cacheKey);
|
|
18
|
+
}
|
|
19
|
+
let price = 0;
|
|
20
|
+
let modelIndex = 1;
|
|
21
|
+
for (let bitIndex = bitTree.numBitLevels; bitIndex > 0; bitIndex--) {
|
|
22
|
+
const bit = (symbol >>> (bitIndex - 1)) & 1;
|
|
23
|
+
price += getBitPrice(bitTree.models[modelIndex], bit);
|
|
24
|
+
modelIndex = (modelIndex << 1) + bit;
|
|
25
|
+
}
|
|
26
|
+
if (bitTreePriceCache.size < 10000) {
|
|
27
|
+
bitTreePriceCache.set(cacheKey, price);
|
|
28
|
+
}
|
|
29
|
+
return price;
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* LZMA Encoder State - Encapsulates all encoder state management
|
|
33
|
+
*/
|
|
34
|
+
class EncoderState {
|
|
35
|
+
// Core state
|
|
36
|
+
state = 0;
|
|
37
|
+
previousByte = 0;
|
|
38
|
+
position = [0, 0];
|
|
39
|
+
// Repetition distances (LZ77 back-references)
|
|
40
|
+
repDistances = [0, 0, 0, 0];
|
|
41
|
+
// Match finding state
|
|
42
|
+
longestMatchLength = 0;
|
|
43
|
+
longestMatchWasFound = false;
|
|
44
|
+
additionalOffset = 0;
|
|
45
|
+
// Probability models for different encoding decisions
|
|
46
|
+
isMatch = initArray(0xC0);
|
|
47
|
+
isRep = initArray(0x0C);
|
|
48
|
+
isRepG0 = initArray(0x0C);
|
|
49
|
+
isRepG1 = initArray(0x0C);
|
|
50
|
+
isRepG2 = initArray(0x0C);
|
|
51
|
+
isRep0Long = initArray(0xC0);
|
|
52
|
+
/**
|
|
53
|
+
* Initialize all probability models to default values
|
|
54
|
+
*/
|
|
55
|
+
initModels() {
|
|
56
|
+
initBitModels(this.isMatch);
|
|
57
|
+
initBitModels(this.isRep);
|
|
58
|
+
initBitModels(this.isRepG0);
|
|
59
|
+
initBitModels(this.isRepG1);
|
|
60
|
+
initBitModels(this.isRepG2);
|
|
61
|
+
initBitModels(this.isRep0Long);
|
|
62
|
+
}
|
|
63
|
+
/**
|
|
64
|
+
* Update repetition distances when a new match is found
|
|
65
|
+
*/
|
|
66
|
+
updateRepDistances(newDistance, repIndex) {
|
|
67
|
+
if (repIndex === 0) {
|
|
68
|
+
// New match becomes rep0, shift others
|
|
69
|
+
this.repDistances[3] = this.repDistances[2];
|
|
70
|
+
this.repDistances[2] = this.repDistances[1];
|
|
71
|
+
this.repDistances[1] = this.repDistances[0];
|
|
72
|
+
this.repDistances[0] = newDistance;
|
|
73
|
+
}
|
|
74
|
+
else {
|
|
75
|
+
// Move specific rep to front, shift others
|
|
76
|
+
const temp = this.repDistances[repIndex];
|
|
77
|
+
for (let i = repIndex; i > 0; i--) {
|
|
78
|
+
this.repDistances[i] = this.repDistances[i - 1];
|
|
79
|
+
}
|
|
80
|
+
this.repDistances[0] = temp;
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
/**
|
|
85
|
+
* Position Encoder - Handles position slot and alignment encoding
|
|
86
|
+
*/
|
|
87
|
+
class PositionEncoder {
|
|
88
|
+
posSlotEncoder = [];
|
|
89
|
+
posEncoders = initArray(0x72);
|
|
90
|
+
posAlignEncoder;
|
|
91
|
+
constructor() {
|
|
92
|
+
// Initialize position slot encoders for different length states
|
|
93
|
+
for (let lenState = 0; lenState < 4; lenState++) {
|
|
94
|
+
this.posSlotEncoder[lenState] = createBitTree(6);
|
|
95
|
+
}
|
|
96
|
+
this.posAlignEncoder = createBitTree(4);
|
|
97
|
+
}
|
|
98
|
+
/**
|
|
99
|
+
* Initialize all position models
|
|
100
|
+
*/
|
|
101
|
+
init() {
|
|
102
|
+
for (const encoder of this.posSlotEncoder) {
|
|
103
|
+
initBitModels(encoder.models);
|
|
104
|
+
}
|
|
105
|
+
initBitModels(this.posEncoders);
|
|
106
|
+
initBitModels(this.posAlignEncoder.models);
|
|
107
|
+
}
|
|
108
|
+
/**
|
|
109
|
+
* Encode position using optimal method
|
|
110
|
+
*/
|
|
111
|
+
encodePosition(distance, lenState, rangeEncoder) {
|
|
112
|
+
const posSlot = this.getPosSlot(distance);
|
|
113
|
+
rangeEncoder.encodeBitTree(this.posSlotEncoder[lenState], posSlot);
|
|
114
|
+
if (posSlot >= 4) {
|
|
115
|
+
const footerBits = (posSlot >> 1) - 1;
|
|
116
|
+
const baseVal = (2 | (posSlot & 1)) << footerBits;
|
|
117
|
+
const posReduced = distance - baseVal;
|
|
118
|
+
if (posSlot < 14) {
|
|
119
|
+
// Use position encoders for middle range
|
|
120
|
+
this.encodeReverseBits(posReduced, footerBits, rangeEncoder);
|
|
121
|
+
}
|
|
122
|
+
else {
|
|
123
|
+
// Use direct bits for high range + alignment
|
|
124
|
+
rangeEncoder.encodeDirectBits(posReduced >> 4, footerBits - 4);
|
|
125
|
+
rangeEncoder.encodeBitTree(this.posAlignEncoder, posReduced & 0x0F);
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
/**
|
|
130
|
+
* Calculate price for encoding a position
|
|
131
|
+
*/
|
|
132
|
+
getPositionPrice(distance, lenState) {
|
|
133
|
+
const posSlot = this.getPosSlot(distance);
|
|
134
|
+
let price = getBitTreePrice(this.posSlotEncoder[lenState], posSlot);
|
|
135
|
+
if (posSlot >= 4) {
|
|
136
|
+
const footerBits = (posSlot >> 1) - 1;
|
|
137
|
+
const baseVal = (2 | (posSlot & 1)) << footerBits;
|
|
138
|
+
const posReduced = distance - baseVal;
|
|
139
|
+
if (posSlot < 14) {
|
|
140
|
+
price += this.getReverseBitsPrice(posReduced, footerBits);
|
|
141
|
+
}
|
|
142
|
+
else {
|
|
143
|
+
price += getDirectBitsPrice(footerBits - 4);
|
|
144
|
+
price += getBitTreePrice(this.posAlignEncoder, posReduced & 0x0F);
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
return price;
|
|
148
|
+
}
|
|
149
|
+
getPosSlot(distance) {
|
|
150
|
+
if (distance < 4)
|
|
151
|
+
return distance;
|
|
152
|
+
if (distance < (1 << (31 - 11))) {
|
|
153
|
+
return G_FAST_POS[distance >> 6] + 12;
|
|
154
|
+
}
|
|
155
|
+
return G_FAST_POS[distance >> 26] + 52;
|
|
156
|
+
}
|
|
157
|
+
encodeReverseBits(value, numBits, rangeEncoder) {
|
|
158
|
+
let modelIndex = 1;
|
|
159
|
+
for (let i = 0; i < numBits; i++) {
|
|
160
|
+
const bit = value & 1;
|
|
161
|
+
rangeEncoder.encodeBit(this.posEncoders, modelIndex, bit);
|
|
162
|
+
modelIndex = (modelIndex << 1) | bit;
|
|
163
|
+
value >>>= 1;
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
getReverseBitsPrice(value, numBits) {
|
|
167
|
+
let price = 0;
|
|
168
|
+
let modelIndex = 1;
|
|
169
|
+
for (let i = 0; i < numBits; i++) {
|
|
170
|
+
const bit = value & 1;
|
|
171
|
+
price += getBitPrice(this.posEncoders[modelIndex], bit);
|
|
172
|
+
modelIndex = (modelIndex << 1) | bit;
|
|
173
|
+
value >>>= 1;
|
|
174
|
+
}
|
|
175
|
+
return price;
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
/**
|
|
179
|
+
* LZMA Encoder class that handles compression operations
|
|
180
|
+
*/
|
|
181
|
+
export class Encoder {
|
|
182
|
+
encoderState = new EncoderState();
|
|
183
|
+
positionEncoder = new PositionEncoder();
|
|
184
|
+
// Core state properties
|
|
185
|
+
_state = 0;
|
|
186
|
+
_previousByte = 0;
|
|
187
|
+
_distTableSize = 0;
|
|
188
|
+
_longestMatchWasFound = 0;
|
|
189
|
+
_optimumEndIndex = 0;
|
|
190
|
+
_optimumCurrentIndex = 0;
|
|
191
|
+
_additionalOffset = 0;
|
|
192
|
+
// Dictionary and match finding
|
|
193
|
+
_dictionarySize = 0;
|
|
194
|
+
_matchFinder = null;
|
|
195
|
+
_dictionarySizePrev = 0;
|
|
196
|
+
_numFastBytes = 0;
|
|
197
|
+
// Literal encoding configuration
|
|
198
|
+
_numLiteralContextBits = 0;
|
|
199
|
+
_numLiteralPosStateBits = 0;
|
|
200
|
+
_posStateBits = 0;
|
|
201
|
+
_posStateMask = 0;
|
|
202
|
+
// Stream and processing state
|
|
203
|
+
_needReleaseMFStream = 0;
|
|
204
|
+
_inStream = null;
|
|
205
|
+
_finished = 0;
|
|
206
|
+
nowPos64 = [0, 0];
|
|
207
|
+
// Distance and repetition arrays
|
|
208
|
+
_repDistances = initArray(4);
|
|
209
|
+
_optimum = [];
|
|
210
|
+
// Range encoder
|
|
211
|
+
_rangeEncoder = {
|
|
212
|
+
stream: {
|
|
213
|
+
buf: [],
|
|
214
|
+
count: 0,
|
|
215
|
+
},
|
|
216
|
+
rrange: 0,
|
|
217
|
+
cache: 0,
|
|
218
|
+
low: [0, 0],
|
|
219
|
+
cacheSize: 0,
|
|
220
|
+
position: [0, 0],
|
|
221
|
+
encodeBit: () => { },
|
|
222
|
+
encodeBitTree: () => { },
|
|
223
|
+
encodeDirectBits: () => { },
|
|
224
|
+
};
|
|
225
|
+
// Bit model arrays for different types of encoding decisions
|
|
226
|
+
_isMatch = initArray(0xC0);
|
|
227
|
+
_isRep = initArray(0x0C);
|
|
228
|
+
_isRepG0 = initArray(0x0C);
|
|
229
|
+
_isRepG1 = initArray(0x0C);
|
|
230
|
+
_isRepG2 = initArray(0x0C);
|
|
231
|
+
_isRep0Long = initArray(0xC0);
|
|
232
|
+
// Position and alignment encoders
|
|
233
|
+
_posSlotEncoder = [];
|
|
234
|
+
_posEncoders = initArray(0x72);
|
|
235
|
+
_posAlignEncoder = null;
|
|
236
|
+
// Length encoders
|
|
237
|
+
_lenEncoder = null;
|
|
238
|
+
_repMatchLenEncoder = null;
|
|
239
|
+
// Literal encoder
|
|
240
|
+
_literalEncoder = null;
|
|
241
|
+
// Distance and price arrays
|
|
242
|
+
_matchDistances = [];
|
|
243
|
+
_posSlotPrices = [];
|
|
244
|
+
_distancesPrices = [];
|
|
245
|
+
_alignPrices = initArray(0x10);
|
|
246
|
+
_matchPriceCount = 0;
|
|
247
|
+
_alignPriceCount = 0;
|
|
248
|
+
// Optimization arrays
|
|
249
|
+
reps = initArray(4);
|
|
250
|
+
repLens = initArray(4);
|
|
251
|
+
// Processing counters
|
|
252
|
+
processedInSize = [[0, 0]];
|
|
253
|
+
processedOutSize = [[0, 0]];
|
|
254
|
+
finished = [0];
|
|
255
|
+
properties = initArray(5);
|
|
256
|
+
tempPrices = initArray(0x80); // 128
|
|
257
|
+
// Match finding properties
|
|
258
|
+
_longestMatchLength = 0;
|
|
259
|
+
_matchFinderType = 1;
|
|
260
|
+
_numDistancePairs = 0;
|
|
261
|
+
_numFastBytesPrev = -1;
|
|
262
|
+
backRes = 0;
|
|
263
|
+
constructor() {
|
|
264
|
+
// Encoder is initialized with default values above
|
|
265
|
+
// Additional initialization will be done through specific init methods
|
|
266
|
+
}
|
|
267
|
+
/**
|
|
268
|
+
* Initialize basic encoder state
|
|
269
|
+
*/
|
|
270
|
+
baseInit() {
|
|
271
|
+
this._state = 0;
|
|
272
|
+
this._previousByte = 0;
|
|
273
|
+
for (let i = 0; i < 4; ++i) {
|
|
274
|
+
this._repDistances[i] = 0;
|
|
275
|
+
}
|
|
276
|
+
}
|
|
277
|
+
/**
|
|
278
|
+
* Get optimum array
|
|
279
|
+
*/
|
|
280
|
+
getOptimum() {
|
|
281
|
+
return this._optimum;
|
|
282
|
+
}
|
|
283
|
+
/**
|
|
284
|
+
* Get back result
|
|
285
|
+
*/
|
|
286
|
+
getBackRes() {
|
|
287
|
+
return this.backRes;
|
|
288
|
+
}
|
|
289
|
+
setBackRes(backRes) {
|
|
290
|
+
this.backRes = backRes;
|
|
291
|
+
}
|
|
292
|
+
init() {
|
|
293
|
+
this.baseInit();
|
|
294
|
+
this.encoderState.initModels();
|
|
295
|
+
this.positionEncoder.init();
|
|
296
|
+
// Initialize optimum array properly
|
|
297
|
+
this._optimum = [];
|
|
298
|
+
for (let i = 0; i < 0x1000; i++) {
|
|
299
|
+
this._optimum[i] = {};
|
|
300
|
+
}
|
|
301
|
+
this.initEncoderState();
|
|
302
|
+
initBitModels(this._isMatch);
|
|
303
|
+
initBitModels(this._isRep0Long);
|
|
304
|
+
initBitModels(this._isRep);
|
|
305
|
+
initBitModels(this._isRepG0);
|
|
306
|
+
initBitModels(this._isRepG1);
|
|
307
|
+
initBitModels(this._isRepG2);
|
|
308
|
+
initBitModels(this._posEncoders);
|
|
309
|
+
this.initLiteralEncoder();
|
|
310
|
+
for (let i = 0; i < 4; ++i) {
|
|
311
|
+
initBitModels(this._posSlotEncoder[i].models);
|
|
312
|
+
}
|
|
313
|
+
if (this._lenEncoder) {
|
|
314
|
+
this._lenEncoder.init(1 << this._posStateBits);
|
|
315
|
+
}
|
|
316
|
+
if (this._repMatchLenEncoder) {
|
|
317
|
+
this._repMatchLenEncoder.init(1 << this._posStateBits);
|
|
318
|
+
}
|
|
319
|
+
if (this._posAlignEncoder) {
|
|
320
|
+
initBitModels(this._posAlignEncoder.models);
|
|
321
|
+
}
|
|
322
|
+
this._longestMatchWasFound = 0;
|
|
323
|
+
this._optimumEndIndex = 0;
|
|
324
|
+
this._optimumCurrentIndex = 0;
|
|
325
|
+
this._additionalOffset = 0;
|
|
326
|
+
}
|
|
327
|
+
/**
|
|
328
|
+
* Initialize encoder range coder
|
|
329
|
+
*/
|
|
330
|
+
initEncoderState() {
|
|
331
|
+
this._rangeEncoder.low = [0, 0];
|
|
332
|
+
this._rangeEncoder.rrange = 0xFFFFFFFF;
|
|
333
|
+
this._rangeEncoder.cacheSize = 1;
|
|
334
|
+
this._rangeEncoder.cache = 0;
|
|
335
|
+
this._rangeEncoder.position = [0, 0];
|
|
336
|
+
}
|
|
337
|
+
/**
|
|
338
|
+
* Initialize literal encoder
|
|
339
|
+
*/
|
|
340
|
+
initLiteralEncoder() {
|
|
341
|
+
const totalStates = 1 << (this._literalEncoder.numPrevBits + this._literalEncoder.numPosBits);
|
|
342
|
+
for (let i = 0; i < totalStates; ++i) {
|
|
343
|
+
initBitModels(this._literalEncoder.coders[i].decoders);
|
|
344
|
+
}
|
|
345
|
+
}
|
|
346
|
+
/**
|
|
347
|
+
* Create optimum structures
|
|
348
|
+
*/
|
|
349
|
+
createOptimumStructures() {
|
|
350
|
+
for (let i = 0; i < 0x1000; ++i) {
|
|
351
|
+
this._optimum[i] = {};
|
|
352
|
+
}
|
|
353
|
+
for (let i = 0; i < 4; ++i) {
|
|
354
|
+
this._posSlotEncoder[i] = createBitTree(6);
|
|
355
|
+
}
|
|
356
|
+
}
|
|
357
|
+
/**
|
|
358
|
+
* Create length price table encoder
|
|
359
|
+
*/
|
|
360
|
+
createLenPriceTableEncoder() {
|
|
361
|
+
const encoder = new LenEncoder();
|
|
362
|
+
encoder.initPriceTable();
|
|
363
|
+
return encoder;
|
|
364
|
+
}
|
|
365
|
+
/**
|
|
366
|
+
* Create literal encoder encoder2
|
|
367
|
+
*/
|
|
368
|
+
createLiteralEncoderEncoder2() {
|
|
369
|
+
const encoder = {
|
|
370
|
+
decoders: initArray(0x300),
|
|
371
|
+
};
|
|
372
|
+
return encoder;
|
|
373
|
+
}
|
|
374
|
+
/**
|
|
375
|
+
* Create literal encoder
|
|
376
|
+
*/
|
|
377
|
+
createLiteralEncoder() {
|
|
378
|
+
// Check if we need to recreate the encoder
|
|
379
|
+
if (this._literalEncoder != null
|
|
380
|
+
&& this._literalEncoder.numPrevBits == this._numLiteralContextBits
|
|
381
|
+
&& this._literalEncoder.numPosBits == this._numLiteralPosStateBits) {
|
|
382
|
+
return;
|
|
383
|
+
}
|
|
384
|
+
// Replace #LZMA_Encoder_LiteralEncoder_Create with LitCoder instantiation
|
|
385
|
+
this._literalEncoder = new LitCoder(this._numLiteralPosStateBits, this._numLiteralContextBits);
|
|
386
|
+
}
|
|
387
|
+
/**
|
|
388
|
+
* Initialize completely with proper encoder state
|
|
389
|
+
*/
|
|
390
|
+
initialize() {
|
|
391
|
+
// Initialize encoder structures first
|
|
392
|
+
this._lenEncoder = this.createLenPriceTableEncoder();
|
|
393
|
+
this._repMatchLenEncoder = this.createLenPriceTableEncoder();
|
|
394
|
+
this._posAlignEncoder = createBitTree(0x04);
|
|
395
|
+
// Initialize optimum array
|
|
396
|
+
this._optimum = [];
|
|
397
|
+
this.createOptimumStructures();
|
|
398
|
+
// Create literal encoder
|
|
399
|
+
this.createLiteralEncoder();
|
|
400
|
+
// Now call init to set up the state
|
|
401
|
+
this.init();
|
|
402
|
+
}
|
|
403
|
+
/**
|
|
404
|
+
* Configure encoder settings
|
|
405
|
+
*/
|
|
406
|
+
configure(mode) {
|
|
407
|
+
this.setDictionarySize(0x1 << mode.searchDepth);
|
|
408
|
+
this._numFastBytes = mode.filterStrength;
|
|
409
|
+
this._matchFinderType = mode.modeIndex;
|
|
410
|
+
// lc is always 3, lp is always 0, pb is always 2
|
|
411
|
+
this._numLiteralContextBits = 0x3;
|
|
412
|
+
this._numLiteralPosStateBits = 0x0;
|
|
413
|
+
this._posStateBits = 0x2;
|
|
414
|
+
this._posStateMask = 0x3;
|
|
415
|
+
}
|
|
416
|
+
/**
|
|
417
|
+
* Set dictionary size
|
|
418
|
+
*/
|
|
419
|
+
setDictionarySize(dictionarySize) {
|
|
420
|
+
this._dictionarySize = dictionarySize;
|
|
421
|
+
let dicLogSize = 0;
|
|
422
|
+
for (; dictionarySize > (1 << dicLogSize); ++dicLogSize)
|
|
423
|
+
;
|
|
424
|
+
this._distTableSize = dicLogSize * 2;
|
|
425
|
+
}
|
|
426
|
+
/**
|
|
427
|
+
* Encode a bit using range coder
|
|
428
|
+
*/
|
|
429
|
+
encodeBit(probs, index, symbol) {
|
|
430
|
+
const rangeEncoder = this._rangeEncoder;
|
|
431
|
+
let newBound, prob = probs[index];
|
|
432
|
+
newBound = (rangeEncoder.rrange >>> 11) * prob;
|
|
433
|
+
if (!symbol) {
|
|
434
|
+
rangeEncoder.rrange = newBound;
|
|
435
|
+
probs[index] = prob + (2048 - prob >>> 5) << 16 >> 16;
|
|
436
|
+
}
|
|
437
|
+
else {
|
|
438
|
+
// Need helper methods for 64-bit arithmetic
|
|
439
|
+
rangeEncoder.low = add64(rangeEncoder.low, this.and64(fromInt64(newBound), [0xFFFFFFFF, 0]));
|
|
440
|
+
rangeEncoder.rrange -= newBound;
|
|
441
|
+
probs[index] = prob - (prob >>> 5) << 16 >> 16;
|
|
442
|
+
}
|
|
443
|
+
if (!(rangeEncoder.rrange & -0x1000000)) {
|
|
444
|
+
rangeEncoder.rrange <<= 8;
|
|
445
|
+
this.shiftLow();
|
|
446
|
+
}
|
|
447
|
+
}
|
|
448
|
+
/**
|
|
449
|
+
* Encode bit tree
|
|
450
|
+
*/
|
|
451
|
+
encodeBitTree(encoder, symbol) {
|
|
452
|
+
let bit, bitIndex, m = 1;
|
|
453
|
+
for (bitIndex = encoder.numBitLevels; bitIndex != 0;) {
|
|
454
|
+
bitIndex -= 1;
|
|
455
|
+
bit = symbol >>> bitIndex & 1;
|
|
456
|
+
this.encodeBit(encoder.models, m, bit);
|
|
457
|
+
m = m << 1 | bit;
|
|
458
|
+
}
|
|
459
|
+
}
|
|
460
|
+
/**
|
|
461
|
+
* Encode literal
|
|
462
|
+
*/
|
|
463
|
+
encodeLiteral(encoder, symbol) {
|
|
464
|
+
let bit, context = 1;
|
|
465
|
+
for (let i = 7; i >= 0; --i) {
|
|
466
|
+
bit = (symbol >> i) & 1;
|
|
467
|
+
this.encodeBit(encoder.decoders, context, bit);
|
|
468
|
+
context = context << 1 | bit;
|
|
469
|
+
}
|
|
470
|
+
}
|
|
471
|
+
/**
|
|
472
|
+
* Encode matched literal
|
|
473
|
+
*/
|
|
474
|
+
encodeMatched(encoder, matchByte, symbol) {
|
|
475
|
+
let bit, matchBit, state, same = true, context = 1;
|
|
476
|
+
for (let i = 7; i >= 0; --i) {
|
|
477
|
+
bit = (symbol >> i) & 1;
|
|
478
|
+
state = context;
|
|
479
|
+
if (same) {
|
|
480
|
+
matchBit = (matchByte >> i) & 1;
|
|
481
|
+
state += (1 + matchBit) << 8;
|
|
482
|
+
same = matchBit === bit;
|
|
483
|
+
}
|
|
484
|
+
this.encodeBit(encoder.decoders, state, bit);
|
|
485
|
+
context = context << 1 | bit;
|
|
486
|
+
}
|
|
487
|
+
}
|
|
488
|
+
/**
|
|
489
|
+
* Encode length using direct method calls
|
|
490
|
+
*/
|
|
491
|
+
encodeLength(encoder, symbol, posState) {
|
|
492
|
+
encoder.encode(symbol, posState, this);
|
|
493
|
+
}
|
|
494
|
+
/**
|
|
495
|
+
* Encode direct bits
|
|
496
|
+
*/
|
|
497
|
+
encodeDirectBits(valueToEncode, numTotalBits) {
|
|
498
|
+
const rangeEncoder = this._rangeEncoder;
|
|
499
|
+
for (let i = numTotalBits - 1; i >= 0; i -= 1) {
|
|
500
|
+
rangeEncoder.rrange >>>= 1;
|
|
501
|
+
if ((valueToEncode >>> i & 1) == 1) {
|
|
502
|
+
rangeEncoder.low = add64(rangeEncoder.low, fromInt64(rangeEncoder.rrange));
|
|
503
|
+
}
|
|
504
|
+
if (!(rangeEncoder.rrange & -0x1000000)) {
|
|
505
|
+
rangeEncoder.rrange <<= 8;
|
|
506
|
+
this.shiftLow();
|
|
507
|
+
}
|
|
508
|
+
}
|
|
509
|
+
}
|
|
510
|
+
/**
|
|
511
|
+
* Reverse encode
|
|
512
|
+
*/
|
|
513
|
+
reverseEncode(symbol) {
|
|
514
|
+
const posAlignEncoder = this._posAlignEncoder;
|
|
515
|
+
if (!posAlignEncoder)
|
|
516
|
+
return;
|
|
517
|
+
let bit, m = 1;
|
|
518
|
+
for (let i = 0; i < posAlignEncoder.numBitLevels; ++i) {
|
|
519
|
+
bit = symbol & 1;
|
|
520
|
+
this.encodeBit(posAlignEncoder.models, m, bit);
|
|
521
|
+
m = m << 1 | bit;
|
|
522
|
+
symbol >>= 1;
|
|
523
|
+
}
|
|
524
|
+
}
|
|
525
|
+
/**
|
|
526
|
+
* Reverse encode range
|
|
527
|
+
*/
|
|
528
|
+
reverseEncodeRange(startIndex, numBitLevels, symbol) {
|
|
529
|
+
let bit, m = 1;
|
|
530
|
+
for (let i = 0; i < numBitLevels; ++i) {
|
|
531
|
+
bit = symbol & 1;
|
|
532
|
+
this.encodeBit(this._posEncoders, startIndex + m, bit);
|
|
533
|
+
m = m << 1 | bit;
|
|
534
|
+
symbol >>= 1;
|
|
535
|
+
}
|
|
536
|
+
}
|
|
537
|
+
/**
|
|
538
|
+
* Write end marker
|
|
539
|
+
*/
|
|
540
|
+
writeEndMarker(positionState) {
|
|
541
|
+
this.encodeBit(this._isMatch, (this._state << 4) + positionState, 1);
|
|
542
|
+
this.encodeBit(this._isRep, this._state, 0);
|
|
543
|
+
this._state = this._state < 7 ? 7 : 10;
|
|
544
|
+
this.encodeLength(this._lenEncoder, 0, positionState);
|
|
545
|
+
const posSlot = 63;
|
|
546
|
+
const lenToPosState = getLenToPosState(2); // Length to position state for minimum length
|
|
547
|
+
this.encodeBitTree(this._posSlotEncoder[lenToPosState], posSlot);
|
|
548
|
+
this.encodeDirectBits(67108863, 26);
|
|
549
|
+
this.reverseEncode(15);
|
|
550
|
+
}
|
|
551
|
+
/**
|
|
552
|
+
* Encode length with price table update
|
|
553
|
+
*/
|
|
554
|
+
encodeLengthWithPriceUpdate(encoder, symbol, posState) {
|
|
555
|
+
encoder.encodeWithUpdate(symbol, posState, this);
|
|
556
|
+
}
|
|
557
|
+
and64(a, b) {
|
|
558
|
+
const highBits = ~~Math.max(Math.min(a[1] / 0x100000000, 0x7FFFFFFF), -0x80000000) & ~~Math.max(Math.min(b[1] / 0x100000000, 0x7FFFFFFF), -0x80000000);
|
|
559
|
+
const lowBits = lowBits64(a) & lowBits64(b);
|
|
560
|
+
let high = highBits * 0x100000000;
|
|
561
|
+
let low = lowBits;
|
|
562
|
+
if (lowBits < 0) {
|
|
563
|
+
low += 0x100000000;
|
|
564
|
+
}
|
|
565
|
+
return [low, high];
|
|
566
|
+
}
|
|
567
|
+
shru64(a, n) {
|
|
568
|
+
n &= 0x3F;
|
|
569
|
+
let shiftFact = this.pwrAsDouble(n);
|
|
570
|
+
let sr = create64(Math.floor(a[0] / shiftFact), a[1] / shiftFact);
|
|
571
|
+
if (a[1] < 0) {
|
|
572
|
+
sr = add64(sr, this.shl64([2, 0], 0x3F - n));
|
|
573
|
+
}
|
|
574
|
+
return sr;
|
|
575
|
+
}
|
|
576
|
+
shl64(a, n) {
|
|
577
|
+
let diff, newHigh, newLow, twoToN;
|
|
578
|
+
n &= 0x3F;
|
|
579
|
+
if (a[0] == 0 && a[1] == -9223372036854775808) {
|
|
580
|
+
if (!n) {
|
|
581
|
+
return a;
|
|
582
|
+
}
|
|
583
|
+
return [0, 0];
|
|
584
|
+
}
|
|
585
|
+
if (a[1] < 0) {
|
|
586
|
+
throw new Error("Neg");
|
|
587
|
+
}
|
|
588
|
+
twoToN = this.pwrAsDouble(n);
|
|
589
|
+
newHigh = a[1] * twoToN % 1.8446744073709552E19;
|
|
590
|
+
newLow = a[0] * twoToN;
|
|
591
|
+
diff = newLow - newLow % 0x100000000;
|
|
592
|
+
newHigh += diff;
|
|
593
|
+
newLow -= diff;
|
|
594
|
+
if (newHigh >= 9223372036854775807) {
|
|
595
|
+
newHigh -= 1.8446744073709552E19;
|
|
596
|
+
}
|
|
597
|
+
return [newLow, newHigh];
|
|
598
|
+
}
|
|
599
|
+
pwrAsDouble(n) {
|
|
600
|
+
if (n <= 0x1E) {
|
|
601
|
+
return 1 << n;
|
|
602
|
+
}
|
|
603
|
+
return this.pwrAsDouble(0x1E) * this.pwrAsDouble(n - 0x1E);
|
|
604
|
+
}
|
|
605
|
+
/**
|
|
606
|
+
* Shift low helper (proper implementation) - public method for external access
|
|
607
|
+
*/
|
|
608
|
+
shiftLow() {
|
|
609
|
+
const rangeEncoder = this._rangeEncoder;
|
|
610
|
+
const LowHi = lowBits64(this.shru64(rangeEncoder.low, 32));
|
|
611
|
+
if (LowHi != 0 || compare64(rangeEncoder.low, [4278190080, 0]) < 0) {
|
|
612
|
+
rangeEncoder.position = add64(rangeEncoder.position, fromInt64(rangeEncoder.cacheSize));
|
|
613
|
+
let temp = rangeEncoder.cache;
|
|
614
|
+
do {
|
|
615
|
+
this.writeToStream(rangeEncoder.stream, temp + LowHi);
|
|
616
|
+
temp = 255;
|
|
617
|
+
} while ((rangeEncoder.cacheSize -= 1) != 0);
|
|
618
|
+
rangeEncoder.cache = lowBits64(rangeEncoder.low) >>> 24;
|
|
619
|
+
}
|
|
620
|
+
rangeEncoder.cacheSize += 1;
|
|
621
|
+
rangeEncoder.low = this.shl64(this.and64(rangeEncoder.low, [16777215, 0]), 8);
|
|
622
|
+
}
|
|
623
|
+
/**
|
|
624
|
+
* Write byte to stream
|
|
625
|
+
*/
|
|
626
|
+
writeToStream(stream, b) {
|
|
627
|
+
if (!stream)
|
|
628
|
+
return;
|
|
629
|
+
// Ensure buffer has enough capacity
|
|
630
|
+
if (stream.count >= stream.buf.length) {
|
|
631
|
+
const newSize = Math.max(stream.buf.length * 2, stream.count + 1);
|
|
632
|
+
const newBuf = new Array(newSize);
|
|
633
|
+
for (let i = 0; i < stream.count; i++) {
|
|
634
|
+
newBuf[i] = stream.buf[i];
|
|
635
|
+
}
|
|
636
|
+
stream.buf = newBuf;
|
|
637
|
+
}
|
|
638
|
+
stream.buf[stream.count++] = b << 24 >> 24;
|
|
639
|
+
}
|
|
640
|
+
initRangeEncoder() {
|
|
641
|
+
this._rangeEncoder.position = [0, 0];
|
|
642
|
+
this._rangeEncoder.low = [0, 0];
|
|
643
|
+
this._rangeEncoder.rrange = -1;
|
|
644
|
+
this._rangeEncoder.cacheSize = 1;
|
|
645
|
+
this._rangeEncoder.cache = 0;
|
|
646
|
+
}
|
|
647
|
+
/**
|
|
648
|
+
* Fill alignment prices for position alignment encoder
|
|
649
|
+
*/
|
|
650
|
+
fillAlignPrices() {
|
|
651
|
+
for (let i = 0; i < 16; ++i) {
|
|
652
|
+
this._alignPrices[i] = this.reverseGetPrice(this._posAlignEncoder, i);
|
|
653
|
+
}
|
|
654
|
+
this._alignPriceCount = 0;
|
|
655
|
+
}
|
|
656
|
+
/**
|
|
657
|
+
* Fill distance prices for position encoders
|
|
658
|
+
*/
|
|
659
|
+
fillDistancesPrices() {
|
|
660
|
+
let baseVal, bitTreeEncoder, footerBits, posSlot, st, st2;
|
|
661
|
+
for (let i = 4; i < 0x80; ++i) {
|
|
662
|
+
posSlot = this.getPosSlot(i);
|
|
663
|
+
footerBits = (posSlot >> 1) - 1;
|
|
664
|
+
baseVal = (2 | (posSlot & 1)) << footerBits;
|
|
665
|
+
this.tempPrices[i] = this.reverseGetPriceArray(this._posEncoders, baseVal - posSlot - 1, footerBits, i - baseVal);
|
|
666
|
+
}
|
|
667
|
+
for (let lenToPosState = 0; lenToPosState < 4; ++lenToPosState) {
|
|
668
|
+
bitTreeEncoder = this._posSlotEncoder[lenToPosState];
|
|
669
|
+
st = lenToPosState << 6;
|
|
670
|
+
for (posSlot = 0; posSlot < this._distTableSize; posSlot += 1) {
|
|
671
|
+
this._posSlotPrices[st + posSlot] = this.rangeCoder_Encoder_GetPrice_1(bitTreeEncoder, posSlot);
|
|
672
|
+
}
|
|
673
|
+
for (posSlot = 14; posSlot < this._distTableSize; posSlot += 1) {
|
|
674
|
+
this._posSlotPrices[st + posSlot] += (posSlot >> 1) - 1 - 4 << 6;
|
|
675
|
+
}
|
|
676
|
+
st2 = lenToPosState * 0x80;
|
|
677
|
+
for (let i = 0; i < 4; ++i) {
|
|
678
|
+
this._distancesPrices[st2 + i] = this._posSlotPrices[st + i];
|
|
679
|
+
}
|
|
680
|
+
for (let i = 4; i < 0x80; ++i) {
|
|
681
|
+
this._distancesPrices[st2 + i] = this._posSlotPrices[st + this.getPosSlot(i)] + this.tempPrices[i];
|
|
682
|
+
}
|
|
683
|
+
}
|
|
684
|
+
this._matchPriceCount = 0;
|
|
685
|
+
}
|
|
686
|
+
/**
|
|
687
|
+
* Get position slot for a distance value
|
|
688
|
+
*/
|
|
689
|
+
getPosSlot(pos) {
|
|
690
|
+
if (pos < 0x800) {
|
|
691
|
+
return G_FAST_POS[pos];
|
|
692
|
+
}
|
|
693
|
+
if (pos < 0x200000) {
|
|
694
|
+
return G_FAST_POS[pos >> 10] + 20;
|
|
695
|
+
}
|
|
696
|
+
return G_FAST_POS[pos >> 20] + 40;
|
|
697
|
+
}
|
|
698
|
+
/**
|
|
699
|
+
* Get reverse price for bit tree encoder
|
|
700
|
+
*/
|
|
701
|
+
reverseGetPrice(encoder, symbol) {
|
|
702
|
+
let bit, m = 1, price = 0;
|
|
703
|
+
for (let i = encoder.numBitLevels; i != 0; i -= 1) {
|
|
704
|
+
bit = symbol & 1;
|
|
705
|
+
symbol >>>= 1;
|
|
706
|
+
price += this.getPrice(encoder.models[m], bit);
|
|
707
|
+
m = m << 1 | bit;
|
|
708
|
+
}
|
|
709
|
+
return price;
|
|
710
|
+
}
|
|
711
|
+
/**
|
|
712
|
+
* Get reverse price for array of models
|
|
713
|
+
*/
|
|
714
|
+
reverseGetPriceArray(Models, startIndex, NumBitLevels, symbol) {
|
|
715
|
+
let bit, m = 1, price = 0;
|
|
716
|
+
for (let i = NumBitLevels; i != 0; i -= 1) {
|
|
717
|
+
bit = symbol & 1;
|
|
718
|
+
symbol >>>= 1;
|
|
719
|
+
price += PROB_PRICES[((Models[startIndex + m] - bit ^ -bit) & 2047) >>> 2];
|
|
720
|
+
m = m << 1 | bit;
|
|
721
|
+
}
|
|
722
|
+
return price;
|
|
723
|
+
}
|
|
724
|
+
/**
|
|
725
|
+
* Get price for probability model (optimized)
|
|
726
|
+
*/
|
|
727
|
+
getPrice(Prob, symbol) {
|
|
728
|
+
return getBitPrice(Prob, symbol);
|
|
729
|
+
}
|
|
730
|
+
/**
|
|
731
|
+
* Get price for bit tree encoder (optimized)
|
|
732
|
+
*/
|
|
733
|
+
rangeCoder_Encoder_GetPrice_1(encoder, symbol) {
|
|
734
|
+
return getBitTreePrice(encoder, symbol);
|
|
735
|
+
}
|
|
736
|
+
/**
|
|
737
|
+
* Create encoder optimization structures (optimized)
|
|
738
|
+
*/
|
|
739
|
+
createEncoderStructures() {
|
|
740
|
+
// Pre-allocate optimum array with proper size
|
|
741
|
+
// Initialize optimum array properly
|
|
742
|
+
this._optimum = [];
|
|
743
|
+
for (let i = 0; i < 0x1000; i++) {
|
|
744
|
+
this._optimum[i] = {};
|
|
745
|
+
}
|
|
746
|
+
for (let i = 0; i < 0x1000; ++i) {
|
|
747
|
+
this._optimum[i] = {};
|
|
748
|
+
}
|
|
749
|
+
// Initialize position slot encoders
|
|
750
|
+
this._posSlotEncoder = new Array(4);
|
|
751
|
+
for (let i = 0; i < 4; ++i) {
|
|
752
|
+
this._posSlotEncoder[i] = createBitTree(6);
|
|
753
|
+
}
|
|
754
|
+
}
|
|
755
|
+
/**
|
|
756
|
+
* Create match finder and encoder structures (replaces #Create_2)
|
|
757
|
+
*/
|
|
758
|
+
createMatchFinderAndStructures() {
|
|
759
|
+
// Create match finder if needed
|
|
760
|
+
if (!this._matchFinder) {
|
|
761
|
+
const binTree = {};
|
|
762
|
+
let numHashBytes = 4;
|
|
763
|
+
if (!this._matchFinderType) {
|
|
764
|
+
numHashBytes = 2;
|
|
765
|
+
}
|
|
766
|
+
// Set match finder type (replaces #SetType)
|
|
767
|
+
binTree.HASH_ARRAY = numHashBytes > 2;
|
|
768
|
+
if (binTree.HASH_ARRAY) {
|
|
769
|
+
binTree.kNumHashDirectBytes = 0;
|
|
770
|
+
binTree.kMinMatchCheck = 4;
|
|
771
|
+
binTree.kFixHashSize = 66560;
|
|
772
|
+
}
|
|
773
|
+
else {
|
|
774
|
+
binTree.kNumHashDirectBytes = 2;
|
|
775
|
+
binTree.kMinMatchCheck = 3;
|
|
776
|
+
binTree.kFixHashSize = 0;
|
|
777
|
+
}
|
|
778
|
+
// Initialize other match finder properties
|
|
779
|
+
binTree._cyclicBufferSize = 0;
|
|
780
|
+
binTree._cyclicBufferPos = 0;
|
|
781
|
+
binTree._streamPos = 0;
|
|
782
|
+
binTree._cutValue = 0xff;
|
|
783
|
+
binTree._matchMaxLen = 0;
|
|
784
|
+
binTree._streamEndWasReached = 0;
|
|
785
|
+
binTree._pos = 0;
|
|
786
|
+
binTree._posLimit = 0;
|
|
787
|
+
binTree._son = [];
|
|
788
|
+
binTree._hash = [];
|
|
789
|
+
binTree._bufferBase = [];
|
|
790
|
+
binTree._blockSize = 0;
|
|
791
|
+
binTree._keepSizeAfter = 0;
|
|
792
|
+
binTree._keepSizeBefore = 0;
|
|
793
|
+
binTree._pointerToLastSafePosition = 0;
|
|
794
|
+
this._matchFinder = binTree;
|
|
795
|
+
}
|
|
796
|
+
// Create literal encoder if needed
|
|
797
|
+
this.createLiteralEncoder();
|
|
798
|
+
// Check if we need to recreate structures
|
|
799
|
+
if (this._dictionarySize == this._dictionarySizePrev
|
|
800
|
+
&& this._numFastBytesPrev == this._numFastBytes) {
|
|
801
|
+
return;
|
|
802
|
+
}
|
|
803
|
+
// This would call equivalent of #Create_3(0x1000, 0x0112) logic
|
|
804
|
+
// For now, we'll handle the basic setup
|
|
805
|
+
this._dictionarySizePrev = this._dictionarySize;
|
|
806
|
+
this._numFastBytesPrev = this._numFastBytes;
|
|
807
|
+
}
|
|
808
|
+
/**
|
|
809
|
+
* Get literal encoder subcoder (utility method)
|
|
810
|
+
*/
|
|
811
|
+
getSubCoderUtility(pos, prevByte) {
|
|
812
|
+
// Calculate position mask bits
|
|
813
|
+
const posBits = pos & this._literalEncoder.posMask;
|
|
814
|
+
const posShifted = posBits << this._literalEncoder.numPrevBits;
|
|
815
|
+
// Calculate previous byte bits
|
|
816
|
+
const prevByteShift = 0x08 - this._literalEncoder.numPrevBits;
|
|
817
|
+
const prevByteBits = (prevByte & 0xFF) >>> prevByteShift;
|
|
818
|
+
// Combine position and prevByte bits to get final index
|
|
819
|
+
const coderIndex = posShifted + prevByteBits;
|
|
820
|
+
return this._literalEncoder.coders[coderIndex];
|
|
821
|
+
}
|
|
822
|
+
}
|