├── .gitignore
├── LICENSE
├── binTree.js
├── bitEncoder.js
├── build
├── compiledLzma.js
├── encoder.js
├── lzma.js
├── lzmajs.tmproj
├── optimizing javascript
├── rangeCoder.js
├── test.js
└── testc
└── testc.cc
/.gitignore:
--------------------------------------------------------------------------------
1 |
2 | testc/test
3 |
4 | testc/a.out
5 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | Copyright (c) 2013 Gary Linscott
2 | All rights reserved.
3 |
4 | Redistribution and use in source and binary forms, with or without
5 | modification, are permitted provided that the following conditions
6 | are met:
7 | 1. Redistributions of source code must retain the above copyright
8 | notice, this list of conditions and the following disclaimer.
9 | 2. Redistributions in binary form must reproduce the above copyright
10 | notice, this list of conditions and the following disclaimer in the
11 | documentation and/or other materials provided with the distribution.
12 | 3. The name of the author may not be used to endorse or promote products
13 | derived from this software without specific prior written permission.
14 |
15 | THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
16 | IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
17 | OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
18 | IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
19 | INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
20 | NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
21 | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
22 | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
23 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
24 | THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25 |
--------------------------------------------------------------------------------
/binTree.js:
--------------------------------------------------------------------------------
1 | function CRCInit() {
2 | table = [];
3 |
4 | var kPoly = 0xEDB88320, i, j, r;
5 | for (i = 0; i < 256; i++) {
6 | r = i;
7 | for (j = 0; j < 8; j++) {
8 | if ((r & 1) !== 0) {
9 | r = (r >>> 1) ^ kPoly;
10 | } else {
11 | r >>>= 1;
12 | }
13 | }
14 | table[i] = r;
15 | }
16 |
17 | return table;
18 | }
19 |
20 | var CRC = function() { };
21 | CRC.prototype.table = CRCInit();
22 |
23 | function InWindow() {
24 | this.moveBlock = function() {
25 | var i;
26 | var offset = this._bufferOffset + this._pos + _keepSizeBefore;
27 | if (offset > 0) {
28 | offset--;
29 | }
30 |
31 | var numBytes = this._bufferOffset + this._streamPos - offset;
32 | for (i = 0; i < numBytes; i++) {
33 | this._bufferBase[i] = this._bufferBase[offset + i];
34 | }
35 | this._bufferOffset -= offset;
36 | };
37 |
38 | this.readBlock = function() {
39 | if (this._streamEndWasReached) {
40 | return;
41 | }
42 | for (;;) {
43 | var size = -this._bufferOffset + this._blockSize - this._streamPos;
44 | if (size === 0) {
45 | return;
46 | }
47 | var numReadBytes = this._stream.read(this._bufferBase, this._bufferOffset + this._streamPos, size);
48 | if (numReadBytes === 0) {
49 | this._posLimit = this._streamPos;
50 | var pointerToPosition = this._bufferOffset + this._posLimit;
51 | if (pointerToPosition > this._pointerToLastSafePosition) {
52 | this._posLimit = this._pointerToLastSafePosition - this._bufferOffset;
53 | }
54 |
55 | this._streamEndWasReached = true;
56 | return;
57 | }
58 | this._streamPos += numReadBytes;
59 | if (this._streamPos >= this._pos + this._keepSizeAfter) {
60 | this._posLimit = this._streamPos - this._keepSizeAfter;
61 | }
62 | }
63 | };
64 |
65 | this.free = function() {
66 | this._bufferBase = null;
67 | };
68 |
69 | this.createBase = function(keepSizeBefore, keepSizeAfter, keepSizeReserve) {
70 | this._keepSizeBefore = keepSizeBefore;
71 | this._keepSizeAfter = keepSizeAfter;
72 | var blockSize = keepSizeBefore + keepSizeAfter + keepSizeReserve;
73 | if (this._bufferBase === null || this._blockSize !== blockSize) {
74 | this.free();
75 | this._blockSize = blockSize;
76 | this._bufferBase = [];
77 | }
78 | this._pointerToLastSafePosition = this._blockSize - keepSizeAfter;
79 | };
80 |
81 | this.setStream = function(stream) {
82 | this._stream = stream;
83 | };
84 |
85 | this.releaseStream = function() {
86 | this._stream = null;
87 | };
88 |
89 | this.initBase = function() {
90 | this._bufferOffset = 0;
91 | this._pos = 0;
92 | this._streamPos = 0;
93 | this._streamEndWasReached = false;
94 | this.readBlock();
95 | };
96 |
97 | this.movePosBase = function() {
98 | this._pos++;
99 | if (this._pos > this._posLimit) {
100 | var pointerToPosition = this._bufferOffset + this._pos;
101 | if (pointerToPosition > this._pointerToLastSafePosition) {
102 | this.moveBlock();
103 | }
104 | this.readBlock();
105 | }
106 | };
107 |
108 | this.getIndexByte = function(index) {
109 | return this._bufferBase[this._bufferOffset + this._pos + index];
110 | };
111 |
112 | this.getMatch = function(index, distance, limit) {
113 | var pby, i = 0;
114 | if (this._streamEndWasReached) {
115 | if (this._pos + index + limit > this._streamPos) {
116 | limit = this._streamPos - (this._pos + index);
117 | }
118 | }
119 | distance++;
120 | pby = this._bufferOffset + this._pos + index;
121 | while (i < limit && this._bufferBase[pby + i] === this._bufferBase[pby + i - distance]) {
122 | i++;
123 | }
124 | return i;
125 | };
126 |
127 | this.getNumAvailableBytes = function() {
128 | return this._streamPos - this._pos;
129 | };
130 |
131 | this.reduceOffsets = function(subValue) {
132 | this._bufferOffset += subValue;
133 | this._posLimit -= subValue;
134 | this._pos -= subValue;
135 | this._streamPos -= subValue;
136 | };
137 | }
138 |
139 | function BinTree() {
140 | InWindow.call(this);
141 |
142 | this._cyclicBufferSize = 0;
143 |
144 | this._son = [];
145 | this._hash = [];
146 |
147 | this._cutValue = 0xFF;
148 | this._hashSizeSum = 0;
149 |
150 | this.hashArray = true;
151 |
152 | var kHash2Size = 1 << 10, kHash3Size = 1 << 16, kBT2HashSize = 1 << 16;
153 | var kStartMaxLen = 1, kHash3Offset = kHash2Size, kEmptyHashValue = 0;
154 | var kMaxValForNormalize = 0x7FFFFFFF;
155 |
156 | var kNumHashDirectBytes = 0;
157 | var kMinMatchCheck = 4;
158 | var kFixHashSize = kHash2Size + kHash3Size;
159 |
160 | this.setType = function(numHashBytes) {
161 | this.hashArray = numHashBytes > 2;
162 | if (this.hashArray) {
163 | kNumHashDirectBytes = 0;
164 | kMinMatchCheck = 4;
165 | kFixHashSize = kHash2Size + kHash3Size;
166 | } else {
167 | kNumHashDirectBytes = 2;
168 | kMinMatchCheck = 2 + 1;
169 | kFixHashSize = 0;
170 | }
171 | };
172 |
173 | this.init = function() {
174 | var i;
175 | this.initBase();
176 | for (i = 0; i < this._hashSizeSum; i++) {
177 | this._hash[i] = kEmptyHashValue;
178 | }
179 | this._cyclicBufferPos = 0;
180 | this.reduceOffsets(-1);
181 | };
182 |
183 | this.movePos = function() {
184 | if (++_this.cyclicBufferPos >= this._cyclicBufferSize) {
185 | this._cyclicBufferPos = 0;
186 | }
187 | this.movePosBase();
188 | if (this._pos === kMaxValForNormalize) {
189 | this.normalize();
190 | }
191 | };
192 |
193 | this.create = function(historySize, keepAddBufferBefore, matchMaxLen, keepAddBufferAfter) {
194 | var windowReserveSize, cyclicBufferSize, hs;
195 |
196 | if (historySize > kMaxValForNormalize - 256) {
197 | throw 'Unsupported historySize';
198 | }
199 | this._cutValue = 16 + (matchMaxLen >>> 1);
200 | windowReserveSize = (historySize + keepAddBufferBefore + matchMaxLen + keepAddBufferAfter) / 2 + 256;
201 |
202 | this.createBase(historySize + keepAddBufferBefore, matchMaxLen + keepAddBufferAfter, windowReserveSize);
203 |
204 | this._matchMaxLen = matchMaxLen;
205 |
206 | cyclicBufferSize = historySize + 1;
207 | if (this._cyclicBufferSize !== cyclicBufferSize) {
208 | this._son = [];
209 | }
210 |
211 | hs = kBT2HashSize;
212 |
213 | if (this.hashArray) {
214 | hs = historySize - 1;
215 | hs |= hs >>> 1;
216 | hs |= hs >>> 2;
217 | hs |= hs >>> 4;
218 | hs |= hs >>> 8;
219 | hs >>>= 1;
220 | hs |= 0xFFFF;
221 | if (hs > (1 << 24)) {
222 | hs >>>= 1;
223 | }
224 | this._hashMask = hs;
225 | hs++;
226 | hs += kFixHashSize;
227 | }
228 | if (hs !== this._hashSizeSum) {
229 | this._hashSizeSum = hs;
230 | this._hash = new Array(this._hashSizeSum);
231 | }
232 | };
233 |
234 | this.getMatches = function(distances) {
235 | var lenLimit, offset, matchMinPos, cur, maxLen, hashValue, hash2Value, hash3Value;
236 | var temp, curMatch, curMatch2, curMatch3, ptr0, ptr1, len0, len1, count, delta;
237 | var cyclicPos, pby1, len;
238 | if (this._pos + this._matchMaxLen <= this._streamPos) {
239 | lenLimit = this._matchMaxLen;
240 | } else {
241 | lenLimit = this._streamPos - this._pos;
242 | if (lenLimit < kMinMatchCheck) {
243 | this.movePos();
244 | return 0;
245 | }
246 | }
247 |
248 | offset = 0;
249 | matchMinPos = (this._pos > this._cyclicBufferSize) ? (this._pos - this._cyclicBufferSize) : 0;
250 | cur = this._bufferOffset + this._pos;
251 | maxLen = kStartMaxLen; // to avoid items for len < hashSize
252 | hashValue = hash2Value = hash3Value = 0;
253 |
254 | if (this.hashArray) {
255 | temp = CRC.table[this._bufferBase[cur]] ^ this._bufferBase[cur + 1];
256 | hash2Value = temp & (kHash2Size - 1);
257 | temp ^= _bufferBase[cur + 2] << 8;
258 | hash3Value = temp & (kHash3Size - 1);
259 | hashValue = (temp ^ (CRC.table[this._bufferBase[cur + 1]] << 5)) & this._hashMask;
260 | } else {
261 | hashValue = this._bufferBase[cur] ^ (this._bufferBase[cur + 1] << 8);
262 | }
263 |
264 | curMatch = this._hash[kFixHashSize + hashValue];
265 | if (this.hashArray) {
266 | curMatch2 = this._hash[hash2Value];
267 | curMatch3 = this._hash[kHash3Offset + hash3Value];
268 | this._hash[hash2Value] = this._pos;
269 | this._hash[hash3OFfset + hash3Value] = this._pos;
270 | if (curMatch2 > matchMinPos) {
271 | if (this._bufferBase[this._bufferOffset + curMatch3] === this._bufferBase[cur]) {
272 | distances[offset++] = maxLen = 2;
273 | distances[offset++] = this._pos - curMatch2 - 1;
274 | }
275 | }
276 | if (curMatch3 > matchMinPos) {
277 | if (this._bufferBase[this._bufferOffset + curMatch3] === this._bufferBase[cur]) {
278 | if (curMatch3 === curMatch2) {
279 | offset -= 2;
280 | }
281 | distances[offset++] = maxLen = 3;
282 | distances[offset++] = this._pos - curMatch3 - 1;
283 | curMatch2 = curMatch3;
284 | }
285 | }
286 | if (offset !== 0 && curMatch2 === curMatch) {
287 | offset -= 2;
288 | maxLen = kStartMaxLen;
289 | }
290 | }
291 |
292 | this._hash[kFixHashSize + hashValue] = this._pos;
293 |
294 | ptr0 = (this._cyclicBufferPos << 1) + 1;
295 | ptr1 = this._cyclicBufferPos << 1;
296 |
297 | len0 = len1 = kNumHashDirectBytes;
298 |
299 | if (kNumHashDirectBytes !== 0) {
300 | if (curMatch > matchMinPos) {
301 | if (this._bufferBase[this._bufferOffset + curMatch + kNumHashDirectBytes] !== this._bufferBase[cur + kNumHashDirectBytes]) {
302 | distances[offset++] = maxLen = kNumHashDirectBytes;
303 | distances[offset++] = this._pos - curMatch - 1;
304 | }
305 | }
306 | }
307 |
308 | count = this._cutValue;
309 |
310 | for (;;) {
311 | if (curMatch <= matchMinPos || count-- === 0) {
312 | this._son[ptr0] = this._son[ptr1] = kEmptyHashValue;
313 | break;
314 | }
315 |
316 | delta = this._pos - curMatch;
317 | cyclicPos = ((delta <= this._cyclicBufferPos) ?
318 | (this._cyclicBufferPos - delta) :
319 | (this._cyclicBufferPos - delta + this._cyclicBufferSize)) << 1;
320 |
321 | pby1 = this._bufferOffset + curMatch;
322 | len = len0 < len1 ? len0 : len1;
323 | if (this._bufferBase[pby1 + len] === this._bufferBase[cur + len]) {
324 | while (++len !== lenLimit) {
325 | if (this._bufferBase[pby1 + len] !== this._bufferBase[cur + len]) {
326 | break;
327 | }
328 | }
329 | if (maxLen < len) {
330 | distances[offset++] = maxLen = len;
331 | distances[offset++] = delta - 1;
332 | if (len === lenLimit) {
333 | this._son[ptr1] = this._son[cyclicPos];
334 | this._son[ptr0] = this._son[cyclicPos + 1];
335 | break;
336 | }
337 | }
338 | }
339 | if (this._bufferBase[pby1 + len] < this._bufferBase[cur + len]) {
340 | this._son[ptr1] = curMatch;
341 | ptr1 = cyclicPos + 1;
342 | curMatch = this._son[ptr1];
343 | len1 = len;
344 | } else {
345 | this._son[ptr1] = curMatch;
346 | ptr0 = cyclicPos;
347 | curMatch = this._son[ptr0];
348 | len0 = len;
349 | }
350 | }
351 |
352 | this.movePos();
353 | return offset;
354 | };
355 |
356 | this.skip = function(num) {
357 | var lenLimit, matchMinPos, cur, curMatch, hashValue, hash2Value, hash3Value, temp;
358 | var ptr0, ptr1, len0, len1, count, delta, cyclicPos, pby1, len;
359 | do {
360 | if (this._pos + this._matchMaxLen <= this._streamPos) {
361 | lenLimit = this._matchMaxLen;
362 | } else {
363 | lenLimit = this._streamPos - this._pos;
364 | if (lenLimit < kMinMatchCheck) {
365 | this.movePos();
366 | continue;
367 | }
368 | }
369 |
370 | matchMinPos = this._pos > this._cyclicBufferSize ? (this._pos - this._cyclicBufferSize) : 0;
371 | cur = this._bufferOffset + this._pos;
372 |
373 | if (this.hashArray) {
374 | temp = CRC.table[this._bufferBase[cur]] ^ this._bufferBase[cur + 1];
375 | hash2Value = temp & (kHash2Size - 1);
376 | this._hash[hash2Value] = this._pos;
377 | temp ^= this._bufferBase[cur + 2] << 8;
378 | hash3Value = temp & (kHash3Size - 1);
379 | this._hash[kHash3Offset + hash3Value] = this._pos;
380 | hashValue = (temp ^ (CRC.table[this._bufferBase[cur + 3]] << 5)) & this._hashMask;
381 | } else {
382 | hashValue = this._bufferBase[cur] ^ (this._bufferBase[cur + 1] << 8);
383 | }
384 |
385 | curMatch = this._hash[kFixHashSize + hashValue];
386 | this._hash[kFixHashSize + hashValue] = this._pos;
387 |
388 | ptr0 = (this._cyclicBufferPos << 1) + 1;
389 | ptr1 = (this._cyclicBufferPos << 1);
390 |
391 | len0 = len1 = kNumHashDirectBytes;
392 |
393 | count = this._cutValue;
394 | for(;;) {
395 | if (curMatch <= matchMinPos || count-- === 0) {
396 | this._son[ptr0] = this._son[ptr1] = kEmptyHashValue;
397 | break;
398 | }
399 |
400 | delta = this._pos - curMatch;
401 | cyclicPos = (delta <= this._cyclicBufferPos ?
402 | (this._cyclicBufferPos - delta) :
403 | (this._cyclicBufferPos - delta + this._cyclicBufferSize)) << 1;
404 |
405 | pby1 = this._bufferOffset + curMatch;
406 | len = len0 < len1 ? len0 : len1;
407 | if (this._bufferBase[pby1 + len] === this._bufferBase[cur + len]) {
408 | while (++len !== lenLimit) {
409 | if (this._bufferBase[pby1 + len] !== this._bufferBase[cur + len]) {
410 | break;
411 | }
412 | }
413 | if (len === lenLimit) {
414 | this._son[ptr1] = this._son[cyclicPos];
415 | this._son[ptr0] = this._son[cyclicPos + 1];
416 | break;
417 | }
418 | }
419 | if (this._bufferBase[pby1 + len] < this._bufferBase[cur + len]) {
420 | this._son[ptr1] = curMatch;
421 | ptr1 = cyclicPos + 1;
422 | curMatch = this._son[ptr1];
423 | len1 = len;
424 | } else {
425 | this._son[ptr0] = curMatch;
426 | ptr0 = cyclicPos;
427 | curMatch = this._son[ptr0];
428 | len0 = len;
429 | }
430 | }
431 | this.movePos();
432 | } while (--num !== 0);
433 | };
434 |
435 | this.normalizeLinks = function(items, numItems, subValue) {
436 | var i, value;
437 | for (i = 0; i < numItems; i++) {
438 | value = items[i];
439 | if (value <= subValue) {
440 | value = kEmptyHashValue;
441 | } else {
442 | value -= subValue;
443 | }
444 | items[i] = value;
445 | }
446 | };
447 |
448 | this.normalize = function() {
449 | var subValue = this._pos - this._cyclicBufferSize;
450 | this.normalizeLinks(this._son, this._cyclicBufferSize * 2, subValue);
451 | this.normalizeLinks(this._hash, this._hashSizeSum, subValue);
452 | this.reduceOffsets(subValue);
453 | };
454 |
455 | this.setCutValue = function() {
456 | this._cutValue = cutValue;
457 | };
458 | }
459 |
460 | BinTree.prototype = new InWindow();
461 |
462 | exports.CRC = new CRC();
463 | exports.InWindow = InWindow;
464 | exports.BinTree = BinTree;
--------------------------------------------------------------------------------
/bitEncoder.js:
--------------------------------------------------------------------------------
1 | function BitEncoder() {
2 | var kNumBitModelTotalBits = 11;
3 | var kBitModelTotal = 1 << kNumBitModelTotalBits;
4 | var kNumMoveBits = 5;
5 | var kNumMoveReducingBits = 2;
6 | var kNumBitPriceShiftBits = 6;
7 |
8 | var prob;
9 |
10 | this.init = function() {
11 | prob = kBitModelTotal >> 1;
12 | };
13 |
14 | this.updateModel = function(symbol) {
15 | if (symbol === 0) {
16 | prob += (kBitModelTotal - prob) >>> kNumMoveBits;
17 | } else {
18 | prob -= prob >>> kNumMoveBits;
19 | }
20 | };
21 |
22 | this.encode = function(encoder, symbol) {
23 | encoder.encodeBit(prob, kNumBitModelTotalBits, symbol);
24 | this.updateModel(symbol);
25 | };
26 |
27 | this.getPrice = function(symbol) {
28 | var priceIndex = ((prob - symbol) ^ (-symbol)) & (kBitModelTotal - 1);
29 | return this.probPrices[priceIndex >>> kNumMoveReducingBits];
30 | };
31 |
32 | this.initializeProbPrices = function() {
33 | var kNumBits = kNumBitModelTotalBits - kNumMoveReducingBits;
34 | var i, j;
35 | for (i = kNumBits - 1; i >= 0; i--) {
36 | var start = 1 << (kNumBits - i - 1);
37 | var end = 1 << (kNumBits - i);
38 | for (j = start; j < end; j++) {
39 | this.probPrices[j] = (i << kNumBitPriceShiftBits) +
40 | (((end - j) << kNumBitPriceShiftBits) >>> (kNumBits - i - 1));
41 | }
42 | }
43 | };
44 |
45 | // TODO: make this statically initialized
46 | if (this.probPrices.length === 0) {
47 | this.initializeProbPrices();
48 | }
49 | }
50 |
51 | BitEncoder.prototype.probPrices = [];
52 |
53 | function BitTreeEncoder(numBitLevels) {
54 | this.models = [];
55 |
56 | this.init = function() {
57 | var i;
58 | for (i = 1; i < (1 << numBitLevels); i++) {
59 | this.models[i] = new BitEncoder();
60 | this.models[i].init();
61 | }
62 | };
63 |
64 | this.encode = function(rangeEncoder, symbol) {
65 | var m = 1, bitIndex;
66 | for (bitIndex = numBitLevels - 1; bitIndex >= 0; bitIndex--) {
67 | var bit = (symbol >>> bitIndex) & 1;
68 | this.models[m].encode(rangeEncoder, bit);
69 | m = (m << 1) | bit;
70 | }
71 | };
72 |
73 | this.reverseEncode = function(rangeEncoder, symbol) {
74 | var m = 1, i;
75 | for (i = 0; i < numBitLevels; i++) {
76 | var bit = symbol & 1;
77 | this.models[m].encode(rangeEncoder, bit);
78 | m = (m << 1) | bit;
79 | symbol >>>= 1;
80 | }
81 | };
82 |
83 | this.getPrice = function(symbol) {
84 | var price = 0, m = 1, i;
85 | for (i = numBitLevels; i > 0; i--) {
86 | var bit = symbol & 1;
87 | symbol >>>= 1;
88 | price += this.models[m].getPrice(bit);
89 | m = (m << 1) | bit;
90 | }
91 | return price;
92 | };
93 | }
94 |
95 | exports.BitTreeEncoder = BitTreeEncoder;
96 | exports.BitEncoder = BitEncoder;
--------------------------------------------------------------------------------
/build:
--------------------------------------------------------------------------------
1 | jslint encoder.js
2 | node test.js
3 |
--------------------------------------------------------------------------------
/encoder.js:
--------------------------------------------------------------------------------
1 | var RangeCoder = require('./rangeCoder');
2 | var BitEncoder = require('./bitEncoder');
3 | var BinTree = require('./binTree');
4 |
5 | function Encoder() {
6 | var kInfinityPrice = 0xFFFFFFF;
7 | var kNumRepDistances = 4;
8 | var kNumStates = 12;
9 | var kDefaultDictionaryLogSize = 22;
10 | var kNumFastBytesDefault = 0x20;
11 |
12 | var kNumAlignBits = 4;
13 | var kAlignTableSize = 1 << kNumAlignBits;
14 | var kAlignMax = kAlignTableSize - 1;
15 |
16 | var kStartPosModelIndex = 4;
17 | var kEndPosModelIndex = 14;
18 | var kNumPosModels = kEndPosModelIndex - kStartPosModelIndex;
19 |
20 | var kNumFullDistances = 1 << (kEndPosModelIndex / 2);
21 |
22 | var kNumLowLenBits = 3;
23 | var kNumMidLenBits = 3;
24 | var kNumHighLenBits = 8;
25 | var kNumLowLenSymbols = 1 << kNumLowLenBits;
26 | var kNumMidLenSymbols = 1 << kNumMidLenBits;
27 | var kNumLenSymbols = kNumLowLenSymbols + kNumMidLenSymbols + (1 << kNumHighLenBits);
28 | var kMatchMinLen = 2;
29 | var kMatchMaxLen = kMatchMinLen + kNumLenSymbols - 1;
30 |
31 | var kNumPosSlotBits = 6;
32 | var kDicLogSizeMin = 0;
33 |
34 | var kNumLenToPosStatesBits = 2;
35 | var kNumLenToPosStates = 1 << kNumLenToPosStatesBits;
36 |
37 | var kNumPosStatesBitsMax = 4;
38 | var kNumPosStatesMax = 1 << kNumPosStatesBitsMax;
39 | var kNumPosStatesBitsEncodingMax = 4;
40 | var kNumPosStatesEncodingMax = 1 << kNumPosStatesBitsEncodingMax;
41 | var kNumOpts = 1 << 12;
42 |
43 | this.State = function() {
44 | this.init = function() {
45 | this.index = 0;
46 | };
47 | this.clone = function() {
48 | throw 'unimplemented';
49 | };
50 | this.updateChar = function() {
51 | if (this.index < 4) {
52 | this.index = 0;
53 | } else if (this.index < 10) {
54 | this.index -= 3;
55 | } else {
56 | this.index -= 6;
57 | }
58 | };
59 | this.updateMatch = function() {
60 | this.index = this.index < 7 ? 7 : 10;
61 | };
62 | this.updateRep = function() {
63 | this.index = this.index < 7 ? 8 : 11;
64 | };
65 | this.updateShortRep = function() {
66 | this.index = this.index < 7 ? 9 : 11;
67 | };
68 | this.isCharState = function() {
69 | return this.index < 7;
70 | };
71 | };
72 |
73 | this.fastPos = [];
74 |
75 | this.init = function() {
76 | var kFastSlots = 22, c = 2, slotFast;
77 | this.fastPos[0] = 0;
78 | this.fastPos[1] = 1;
79 | for (slotFast = 2; slotFast < kFastSlots; slotFast++) {
80 | var j, k = 1 << ((slotFast >> 1) - 1);
81 | for (j = 0; j < k; j++,c++) {
82 | this.fastPos[c] = slotFast;
83 | }
84 | }
85 | };
86 |
87 | this.getPosSlot = function(pos) {
88 | if (pos < (1 << 11)) {
89 | return this.fastPos[pos];
90 | }
91 | if (pos < (1 << 21)) {
92 | return this.fastPos[pos >>> 10] + 20;
93 | }
94 | return this.fastPos[pos >>> 20] + 40;
95 | };
96 |
97 | this.getPosSlot2 = function(pos) {
98 | if (pos < (1 << 17)) {
99 | return this.fastPos[pos >>> 6] + 12;
100 | }
101 | if (pos < (1 << 27)) {
102 | return this.fastPos[pos >>> 16] + 32;
103 | }
104 | return this.fastPos[pos >>> 26] + 52;
105 | };
106 |
107 | var state = new this.State();
108 | var previousByte;
109 | var repDistances = [];
110 |
111 | var baseInit = function() {
112 | var i;
113 | state.init();
114 | previousByte = 0;
115 | for (i = 0; i < kNumRepDistances; i++) {
116 | repDistances[i] = 0;
117 | }
118 | };
119 |
120 | this.LiteralEncoder = function() {
121 | this.Encoder2 = function() {
122 | var encoders = [];
123 |
124 | this.create = function() {
125 | var i;
126 | encoders = [];
127 | for (i = 0; i < 0x300; i++) {
128 | encoders[i] = new BitEncoder.BitEncoder();
129 | }
130 | };
131 |
132 | this.init = function() {
133 | var i;
134 | for (i = 0; i < 0x300; i++) {
135 | encoders[i].init();
136 | }
137 | };
138 |
139 | this.encode = function(rangeEncoder, symbol) {
140 | var context = 1, i;
141 | for (i = 7; i >= 0; i--) {
142 | var bit = (symbol >>> i) & 1;
143 | encoders[context].Encode(rangeEncoder, bit);
144 | context = (context << 1) | bit;
145 | }
146 | };
147 |
148 | this.encodeMatched = function(rangeEncoder, matchByte, symbol) {
149 | var context = 1, same = true, i;
150 | for (i = 7; i>= 0; i--) {
151 | var bit = (symbol >> i) & 1;
152 | var state = context;
153 | if (same) {
154 | var matchBit = (matchByte >>> i) & 1;
155 | state += (1 + matchBit) << 8;
156 | same = (matchBit === bit);
157 | }
158 | encoders[state].Encode(rangeEncoder, bit);
159 | context = (context << 1) | bit;
160 | }
161 | };
162 |
163 | this.getPrice = function(matchMode, matchByte, symbol) {
164 | var price = 0;
165 | var context = 1;
166 | var i = 7;
167 | var bit, matchBit;
168 | if (matchMode) {
169 | for (; i >= 0; i--) {
170 | matchBit = (matchByte >>> i) & 1;
171 | bit = (symbol >>> i) & 1;
172 | price += encoders[((1 + matchBit) << 8) + context].GetPrice(bit);
173 | context = (context << 1) | bit;
174 | if (matchBit !== bit) {
175 | i--;
176 | break;
177 | }
178 | }
179 | }
180 | for (; i >= 0; i--) {
181 | bit = (symbol >>> i) & 1;
182 | price += encoders[context].GetPrice(bit);
183 | context = (context << 1) | bit;
184 | }
185 | return price;
186 | };
187 | };
188 |
189 | var coders = [];
190 | var _numPrevBits = -1, _numPosBits = -1, posMask;
191 |
192 | this.create = function(numPosBits, numPrevBits) {
193 | var i;
194 | if (_numPrevBits === numPrevBits & _numPosBits === numPosBits) {
195 | return;
196 | }
197 |
198 | _numPosBits = numPosBits;
199 | _posMask = (1 << numPosBits) - 1;
200 | _numPrevBits = numPrevBits;
201 | var numStates = 1 << (_numPrevBits + _numPosBits);
202 | for (i = 0; i < numStates; i++) {
203 | coders[i] = new this.Encoder2();
204 | coders[i].create();
205 | }
206 | };
207 |
208 | this.init = function() {
209 | var numStates = 1 << (_numPrevBits + _numPosBits), i;
210 | for (i = 0; i < numStates; i++) {
211 | coders[i].init();
212 | }
213 | };
214 |
215 | this.getSubCoder = function(pos, prevByte) {
216 | return coders[((pos & posMask) << _numPrevBits) + (prevByte >> (8 - _numPrevBits))];
217 | };
218 | };
219 |
220 | this.LenEncoder = function() {
221 | var choice = new BitEncoder.BitEncoder();
222 | var choice2 = new BitEncoder.BitEncoder();
223 | var lowCoder = [], midCoder = [];
224 | var highCoder = new BitEncoder.BitTreeEncoder(kNumHighLenBits);
225 | var posState;
226 |
227 | for (posState = 0; posState < kNumPosStatesEncodingMax; posState++) {
228 | lowCoder[posState] = new BitEncoder.BitTreeEncoder(kNumLowLenBits);
229 | midCoder[posState] = new BitEncoder.BitTreeEncoder(kNumMidLenBits);
230 | }
231 |
232 | this.init = function(numPosStates) {
233 | choice.init();
234 | choice2.init();
235 | for (posState = 0; posState < numPosStates; posState++) {
236 | lowCoder[posState].init();
237 | midCoder[posState].init();
238 | }
239 | highCoder.init();
240 | };
241 |
242 | this.encode = function(rangeEncoder, symbol, posState) {
243 | if (symbol < kNumLowLenSymbols) {
244 | choice.encode(rangeEncoder, 0);
245 | lowCoder[posState].encode(rangeEncoder, symbol);
246 | } else {
247 | symbol -= kNumLowLenSymbols;
248 | choice.encode(rangeEncoder, 1);
249 | if (symbol < kNumMidLenSymbols) {
250 | choice2.encode(rangeEncoder, 0);
251 | midCoder[posState].encode(rangeEncoder, symbol);
252 | } else {
253 | choice2.encode(rangeEncoder, 1);
254 | highCoder.encode(rangeEncoder, symbol - kNumMidLenSymbols);
255 | }
256 | }
257 | };
258 |
259 | this.setPrices = function(posState, numSymbols, prices, st) {
260 | var a0 = choice.getPrice0();
261 | var a1 = choice.getPrice1();
262 | var b0 = a1 + choice2.getPrice0();
263 | var b1 = a1 + choice2.getPrice1();
264 | var i;
265 | for (i = 0; i < kNumLowLenSymbols; i++) {
266 | if (i >= numSymbols) {
267 | return;
268 | }
269 | prices[st + i] = a0 + lowCoder[posState].getPrice(i);
270 | }
271 | for (; i < kNumLowLenSymbols + kNumMidLenSymbols; i++) {
272 | if (i >= numSymbols) {
273 | return;
274 | }
275 | prices[st + i] = b0 + midCoder[posState].getPrice(i - kNumLowLenSymbols);
276 | }
277 | for (; i < numSymbols; i++) {
278 | prices[st + i] = b1 + highCoder.getPrice(i - kNumLowLenSymbols - kNumMidLenSymbols);
279 | }
280 | };
281 | };
282 |
283 | var kNumLenSpecSymbols = kNumLowLenSymbols + kNumMidLenSymbols;
284 |
285 | // Derives from LenEncoder
286 | this.LenPriceTableEncoder = function() {
287 | var prices = [];
288 | var counters = [];
289 | var tableSize;
290 |
291 | this.setTableSize = function(tableSize_) {
292 | tableSize = tableSize_;
293 | };
294 |
295 | this.updateTable = function(posState) {
296 | this.setPrices(posState, tableSize, prices, posState * kNumLenSymbols);
297 | counters[posState] = tableSize;
298 | };
299 |
300 | this.updateTables = function(numPosStates) {
301 | var posState;
302 | for (posState = 0; posState < numPosStates; posState++) {
303 | this.updateTable(posState);
304 | }
305 | };
306 |
307 | this.encode = function(rangeEncoder, symbol, posState) {
308 | this.prototype.encode(rangeEncoder, symbol, posState);
309 | if (--counters[posState] === 0) {
310 | this.updateTable(posState);
311 | }
312 | };
313 | };
314 |
315 | this.LenPriceTableEncoder.prototype = new this.LenEncoder();
316 |
317 | this.Optimal = function() {
318 | };
319 |
320 | var _optimum = [];
321 | var _matchFinder = null;
322 | var _rangeEncoder = new RangeCoder.Encoder();
323 |
324 | var _isMatch = [], _isRep = [], _isRepG0 = [], _isRepG1 = [];
325 | var _isRepG2 = [], _isRep0Long = [], _posSlotEncoder = [];
326 | var _posEncoders = [];
327 | var _posAlignEncoder = new BitEncoder.BitTreeEncoder(kNumAlignBits);
328 |
329 | var _lenEncoder = new this.LenPriceTableEncoder();
330 | var _repMatchLenEncoder = new this.LenPriceTableEncoder();
331 |
332 | var _literalEncoder = new this.LiteralEncoder();
333 |
334 | var _matchDistances = [];
335 | var _numFastBytes = kNumFastBytesDefault;
336 | var _longestMatchLength, _numDistancePairs;
337 | var _additionalOffset, _optimumEndIndex, _optimumCurrentIndex, _longestMatchWasFound;
338 |
339 | var _posSlotPrices = [], _distancePrices = [], _alignPrices = [];
340 | var _alignPriceCount;
341 |
342 | var _distTableSize = kDefaultDictionaryLogSize * 2;
343 |
344 | var _posStateBits = 2;
345 | var _posStateMask = 4 - 1;
346 | var _numLiteralPosStateBits = 0;
347 | var _numLiteralContextBits = 3;
348 |
349 | var _dictionarySize = 1 << kDefaultDictionaryLogSize;
350 | var _dictionarySizePrev = 0xFFFFFFFF;
351 | var _numFastBytesPrev = 0xFFFFFFFF;
352 |
353 | var nowPos64, finished, _inStream;
354 | var _matchFinderType = 'BT4', _writeEndMark = false;
355 | var _needReleaseMFStream;
356 |
357 | var reps = [];
358 | var repLens = [];
359 |
360 | this.create = function() {
361 | var numHashBytes;
362 | if (_matchFinder === null) {
363 | numHashBytes = this._matchFinderType === 'BT2' ? 2 : 4;
364 | _matchFinder = new BinTree.BinTree();
365 | _matchFinder.setType(numHashBytes);
366 | }
367 |
368 | _literalEncoder.create(this._numLiteralPosStateBits, this._numLiteralContextBits);
369 |
370 | if (_dictionarySize === _dictionarySizePrev && _numFastBytesPrev === _numFastBytes) {
371 | return;
372 | }
373 | _matchFinder.create(_dictionarySize, kNumOpts, _numFastBytes, kMatchMaxLen + 1);
374 | _dictionarySizePrev = _dictionarySize;
375 | _numFastBytesPrev = _numFastBytes;
376 | };
377 |
378 | this.setWriteEndMarkerMode = function(writeEndMarker) {
379 | _writeEndMark = writeEndMarker;
380 | };
381 |
382 | this.init = function() {
383 | // From ctor
384 | var i, j, complexState;
385 | for (i = 0; i < kNumOpts; i++) {
386 | _optimum[i] = new this.Optimal();
387 | }
388 | for (i = 0; i < kNumLenToPosStates; i++) {
389 | _posSlotEncoder[i] = new BitEncoder.BitTreeEncoder(kNumPosSlotBits);
390 | }
391 |
392 | // Normal init
393 | baseInit();
394 | _rangeEncoder.init();
395 |
396 | for (i = 0; i < kNumStates; i++) {
397 | for (j = 0; j <= _posStateMask; j++) {
398 | complexState = (i << kNumPosStatesBitsMax) + j;
399 | _isMatch[complexState] = new BitEncoder.BitEncoder();
400 | _isMatch[complexState].init();
401 | _isRep0Long[complexState] = new BitEncoder.BitEncoder();
402 | _isRep0Long[complexState].init();
403 | }
404 | _isRep[i] = new BitEncoder.BitEncoder();
405 | _isRep[i].init();
406 | _isRepG0[i] = new BitEncoder.BitEncoder();
407 | _isRepG0[i].init();
408 | _isRepG1[i] = new BitEncoder.BitEncoder();
409 | _isRepG1[i].init();
410 | _isRepG2[i] = new BitEncoder.BitEncoder();
411 | _isRepG2[i].init();
412 | }
413 |
414 | _literalEncoder.init();
415 | for (i = 0; i < kNumLenToPosStates; i++) {
416 | _posSlotEncoder[i].init();
417 | }
418 | for (i = 0; i < kNumFullDistances - kEndPosModelIndex; i++) {
419 | _posEncoders[i] = new BitEncoder.BitEncoder();
420 | _posEncoders[i].init();
421 | }
422 |
423 | _lenEncoder.init(1 << _posStateBits);
424 | _repMatchLenEncoder.init(1 << _posStateBits);
425 |
426 | _posAlignEncoder.init();
427 |
428 | _longestMatchWasFound = false;
429 | _optimumEndIndex = 0;
430 | _optimumCurrentIndex = 0;
431 | _additionalOffset = 0;
432 | };
433 |
434 | this.readMatchDistances = function() {
435 | var lenRes = 0;
436 | var numDistancePairs = _matchFinder.getMatches(_matchDistances);
437 | if (numDistancePairs > 0) {
438 | lenRes = _matchDistances[numDistancePairs - 2];
439 | if (lenRes === _numFastBytes) {
440 | lenRes += _matchFinder.getMatchLen(lenRes - 1, _matchDistances[numDistancePairs - 1], kMatchMaxLen - lenRes);
441 | }
442 | }
443 | _additionalOffset++;
444 | return {
445 | lenRes: lenRes,
446 | numDistancePairs: numDistancePairs
447 | };
448 | };
449 |
450 | this.movePos = function(num) {
451 | if (num > 0) {
452 | _matchFinder.skip(num);
453 | _additionalOffset += num;
454 | }
455 | };
456 |
457 | this.getRepLen1Price = function(state, posState) {
458 | return _isRepG0[state.index].getPrice0() +
459 | _isRep0Long[(state.index << kNumPosStatesBitsMax) + posState].getPrice0();
460 | };
461 |
462 | this.getPureRepPrice = function(repIndex, state, posState) {
463 | var price;
464 | if (repIndex === 0) {
465 | price = _isRepG0[state.index].getPrice0();
466 | price += _isRep0Long[(state.index << kNumPosStatesBitsMax) + posState].getPrice1();
467 | } else {
468 | price = _isRepG0[state.index].getPrice1();
469 | if (repIndex === 1) {
470 | price += _isRepG1[state.index].getPrice0();
471 | } else {
472 | price += _isRepG1[state.index].getPrice1();
473 | price += _isRepG2[state.index].getPrice(repIndex - 2);
474 | }
475 | }
476 | return price;
477 | };
478 |
479 | this.getRepPrice = function(repIndex, len, state, posState) {
480 | var price = _repMatchLenEncoder.getPrice(len - kMatchMinLen, posState);
481 | return price + this.getPureRepPrice(repIndex, state, posState);
482 | };
483 |
484 | this.getPosLenPrice = function(pos, len, posState) {
485 | var price;
486 | var lenToPosState = this.getLenToPosState(len);
487 | if (pos < kNumFullDistances) {
488 | price = _distancePrices[(lenToPosState * kNumFullDistances) + pos];
489 | } else {
490 | price = _posSlotPrices[(lenToPosState << kNumPosSlotBits) + this.getPosSlot2(pos)] + _alignPrices[pos & kAlignMask];
491 | }
492 | return price + _lenEncoder.getPrice(len - kMatchMinLen, posState);
493 | };
494 |
495 | this.backward = function(cur) {
496 | var posMem, backMem, posPrev, backCur;
497 | _optimumEndIndex = cur;
498 | posMem = _optimum[cur].posPrev;
499 | backMem = _optimum[cur].backPrev;
500 | do {
501 | if (_optimum[cur].prev1IsChar) {
502 | _optimum[posMem].makeAsChar();
503 | _optimum[posMem].posPrev = posMem - 1;
504 | if (_optimum[cur].prev2) {
505 | _optimum[posMem - 1].prev1IsChar = false;
506 | _optimum[posMem - 1].posPrev = _optimum[cur].posPrev2;
507 | _optimum[posMem - 1].backPrev = _optimum[cur].backPrev2;
508 | }
509 | }
510 |
511 | posPrev = posMem;
512 | backCur = backMem;
513 |
514 | backMem = _optimum[posPrev].backPrev;
515 | posMem = _optimum[posPrev].posPrev;
516 |
517 | _optimum[posPrev].backPrev = backCur;
518 | _optimum[posPrev].posPrev = cur;
519 | cur = posPrev;
520 | } while (cur > 0);
521 |
522 | backRes = _optimum[0].backPrev;
523 | _optimumCurrentIndex = _optimum[0].posPrev;
524 | return {result: _optimumCurrentIndex, backRes: backRes};
525 | };
526 |
527 | this.getOptimum = function(position) {
528 | var backRes, lenMain, numDistancePairs, numAvailableBytes;
529 | var repMatchIndex, i, currentByte, matchByte, posState;
530 | var matchPrice, repMatchPrice, lenEnd, lenRes, shortRepPrice;
531 | var matchDistances, price, len, curAndLenPrice, optimum, normalMatchPrice;
532 | var offs;
533 |
534 | if (_optimumEndIndex !== _optimumCurrentIndex) {
535 | lenRes = _optimum[_optimumCurrentIndex].posPrev - _optimumCurrentIndex;
536 | backRes = _optimum[_optimumCurrentIndex].backPrev;
537 | _optimumCurrentIndex = _optimum[_optimumCurrentIndex].posPrev;
538 | return {result: lenRes, backRes: backRes};
539 | }
540 | _optimumCurrentIndex = _optimumEndIndex = 0;
541 |
542 | if (!_longestMatchWasFound) {
543 | matchDistances = this.readMatchDistances();
544 | lenMain = matchDistances.lenRes;
545 | numDistancePairs = matchDistances.numDistancePairs;
546 | } else {
547 | lenMain = _longestMatchLength;
548 | numDistancePairs = _numDistancePairs;
549 | _longestMatchWasFound = false;
550 | }
551 |
552 | numAvailableBytes = _matchFinder.getNumAvailableBytes() + 1;
553 | if (numAvailableBytes < 2) {
554 | return {result: 1, backRes: 0xFFFFFFFF};
555 | }
556 |
557 | if (numAvailableBytes > kMatchMaxLen) {
558 | numAvailableBytes = kMatchMaxLen;
559 | }
560 |
561 | repMaxIndex = 0;
562 | for (i = 0; i < kNumRepDistances; i++) {
563 | reps[i] = _repDistances[i];
564 | repLens[i] = _matchFinder.getMatchLen(-1, reps[i], kMatchMaxLen);
565 | if (repLens[i] > repLens[repMaxIndex]) {
566 | repMaxIndex = i;
567 | }
568 | }
569 |
570 | if (repLens[repMaxIndex] >= _numFastBytes) {
571 | lenRes = repLens[repMaxIndex];
572 | backRes = repMaxIndex;
573 | this.movePos(lenRes - 1);
574 | return {result: lenRes, backRes: backRes};
575 | }
576 |
577 | if (lenMain >= _numFastBytes) {
578 | backRes = _matchDistances[numDistancePairs - 1] + kNumRepDistances;
579 | this.movePos(lenMain - 1);
580 | return {result: lenMain, backRes: backRes};
581 | }
582 |
583 | currentByte = _matchFinder.getIndexByte(-1);
584 | matchByte = _matchFinder.getIndexByte(-_repDistances[0] - 2);
585 |
586 | if (lenMain < 2 && currentByte !== matchByte && repLens[repMaxIndex] < 2) {
587 | return {result: 1, backRes: 0xFFFFFFFF};
588 | }
589 |
590 | _optimum[0].state = _state;
591 |
592 | posState = position & _posStateMask;
593 |
594 | _optimum[1].price = _isMatch[(_state.index << kNmPosStatesBitsMax) + posState].getPrice0() +
595 | _literalEncoder.getSubCoder(position, _previousByte).getPrice(!_state.isCharState(), matchByte, currentByte);
596 | _optimum[1].makeAsChar();
597 |
598 | matchPrice = _isMatch[(_state.index << kNumPosStatesBitsMax) + posState].getPrice1();
599 | repMatchPrice = matchPrice + _isRep[_state.index].getPrice1();
600 |
601 | if (matchByte === currentByte) {
602 | shortRepPrice = repMatchPrice + this.getRepLen1Price(_state, posState);
603 | if (shortRepPrice < _optimum[1].price) {
604 | _optimum[1].price = shortRepPrice;
605 | _optimum[1].makeAsShortRep();
606 | }
607 | }
608 |
609 | lenEnd = lenMain >= repLens[repMaxIndex] ? lenMain : repLens[repMaxIndex];
610 | if (lenEnd < 2) {
611 | return {result: 1, backRes: _optimum[1].backPrev};
612 | }
613 |
614 | _optimum[1].posPrev = 0;
615 | _optimum[0].backs0 = reps[0];
616 | _optimum[0].backs1 = reps[1];
617 | _optimum[0].backs2 = reps[2];
618 | _optimum[0].backs3 = reps[3];
619 |
620 | len = lenEnd;
621 | do {
622 | _optimum[len--].price = kInfinityPrice;
623 | } while (len >= 2);
624 |
625 | for (i = 0; i < kNumRepDistances; i++) {
626 | repLen = repLens[i];
627 | if (repLen < 2) {
628 | continue;
629 | }
630 |
631 | price = repMatchPrice + this.getPureRepPrice(i, _state, posState);
632 | do {
633 | curAndLenPrice = price + _repMatchLenEncoder.getPrice(repLen - 2, posState);
634 | optimum = _optimum[repLen];
635 | if (curAndLenPrice < optimum.price) {
636 | optimum.price = curAndLenprice;
637 | optimum.posPrev = 0;
638 | optimum.backPrev = i;
639 | optimum.prev1IsChar = false;
640 | }
641 | } while (--repLen >= 2);
642 | }
643 |
644 | normalMachPrice = matchPrice + _isRep[_state.index].getPrice0();
645 |
646 | len = repLens[0] >= 2 ? repLens[0] + 1 : 2;
647 | if (len <= lenMain) {
648 | offs = 0;
649 | while (len > _matchDistances[offs]) {
650 | offs += 2;
651 | }
652 | for (;; len++) {
653 | distance = _matchDistances[offs + 1];
654 | curAndLenPrice = normalMatchPrice + this.getPosLenPrice(distance, len, posState);
655 | optimum = _optimum[len];
656 | if (curAndLenPrice < optimum.price) {
657 | optimum.price = curAndLenPrice;
658 | optimum.posPrev = 0;
659 | optimum.backPrev = distance + kNumRepDistances;
660 | optimum.prev1IsChar = false;
661 | }
662 | if (len === _matchDistances[offs]) {
663 | offs += 2;
664 | if (offs === numDistancePairs) {
665 | break;
666 | }
667 | }
668 | }
669 | }
670 |
671 | cur = 0;
672 | for(;;) {
673 | cur++;
674 | if (cur === lenEnd) {
675 | return this.backward(cur);
676 | }
677 | matchDistances = this.readMatchDistances();
678 | if (matchDistances.lenRes >= _numFastBytes) {
679 | _numDistancePairs = matchDistances.numDistancePairs;
680 | _longestMatchLength = matchDistances.lenRes;
681 | _longestMatchWasFound = true;
682 | return this.backward(cur);
683 | }
684 | position++;
685 | posPrev = _optimum[cur].posPrev;
686 | if (_optimum[cur].prev1IsChar) {
687 | posPrev--;
688 | if (_optimum[cur].prev2) {
689 | state = _optimum[_optimum[cur].posPrev2].state;
690 | if (_optimum[cur].backPrev2 < kNumRepDistances) {
691 | state.updateRep();
692 | } else {
693 | state.updateMatch();
694 | }
695 | } else {
696 | state = _optimum[posPrev].state;
697 | }
698 | state.updateChar();
699 | } else {
700 | state = _optimum[posPrev].state;
701 | }
702 |
703 | if (posPrev === cur - 1) {
704 | if (_optimum[cur].isShortRep()) {
705 | state.updateShortRep();
706 | } else {
707 | state.updateChar();
708 | }
709 | } else {
710 | if (_optimum[cur].prev1IsChar && _optimum[cur].prev2) {
711 | posPrev = _optimum[cur].posPrev2;
712 | pos = _optimum[cur].backPrev2;
713 | state.updateRep();
714 | } else {
715 | pos = _optimum[cur].backPrev;
716 | if (pos < kNumRepDistances) {
717 | state.updateRep();
718 | } else {
719 | state.updateMatch();
720 | }
721 | }
722 |
723 | opt = _optimum[posPrev];
724 | if (pos < kNumRepDistances) {
725 | if (pos === 0) {
726 | reps[0] = opt.backs0;
727 | reps[1] = opt.backs1;
728 | reps[2] = opt.backs2;
729 | reps[3] = opt.backs3;
730 | } else if (pos === 1) {
731 | reps[0] = opt.backs1;
732 | reps[1] = opt.backs0;
733 | reps[2] = opt.backs2;
734 | reps[3] = opt.backs3;
735 | } else if (pos === 2) {
736 | reps[0] = opt.backs2;
737 | reps[1] = opt.backs0;
738 | reps[2] = opt.backs1;
739 | reps[3] = opt.backs3;
740 | } else {
741 | reps[0] = opt.backs3;
742 | reps[1] = opt.backs0;
743 | reps[2] = opt.backs1;
744 | reps[3] = opt.backs2;
745 | }
746 | } else {
747 | reps[0] = pos - kNumRepDistances;
748 | reps[1] = opt.backs0;
749 | reps[2] = opt.backs1;
750 | reps[3] = opt.backs2;
751 | }
752 | }
753 | _optimum[cur].state = state;
754 | _optimum[cur].backs0 = reps[0];
755 | _optimum[cur].backs1 = reps[1];
756 | _optimum[cur].backs2 = reps[2];
757 | _optimum[cur].backs3 = reps[3];
758 |
759 | curPrice = _optimum[cur].price;
760 | currentByte = _matchFinder.getIndexByte(-1);
761 | matchByte = _matchFinder.getIndexByte(-reps[0] - 2);
762 | posState = position & _posStateMask;
763 |
764 | curAnd1Price = curPrice +
765 | _isMatch[(state.index << kNumPosStatesBitsMax) + posState].getPrice0() +
766 | _literalEncoder.getSubCoder(position, _matchFinder.getIndexByte(-2)).getPrice(!state.isCharState(), matchByte, currentByte);
767 |
768 | nextOptimum = _optimum[cur + 1];
769 |
770 | nextIsChar = false;
771 | if (curAnd1Price < nextOptimum.price) {
772 | nextOptimum.price = curAnd1Price;
773 | nextOptimum.posPrev = cur;
774 | nextOptimum.makeAsChar();
775 | nextIsChar = true;
776 | }
777 |
778 | matchPrice = curPrice + _isMatch[(state.index << kNumPosStatesBitsMax) + posState].getPrice1();
779 | repMatchPrice = matchPrice + _isRep[state.index].getPrice1();
780 |
781 | if (matchByte === currentByte && !(nextOptimum.posPrev < cur && nextOptimum.backPrev === 0)) {
782 | shortRepPrice = repMatchPrice + getRepLen1Price(state, posState);
783 | if (shortRepPrice <= nextOptimum.price) {
784 | nextOptimum.price = shortRepPrice;
785 | nextOptimum.posPrev = cur;
786 | nextOptimum.makeAsShortRep();
787 | nextIsChar = true;
788 | }
789 | }
790 |
791 | numAvailableBytesFull = _matchFinder.getNumAvailableBytes() + 1;
792 | numAvailableBytesFull = min(kNumOpts - 1 - cur, numAvailableBytesFull);
793 | numAvailableBytes = numAvailableByesFull;
794 |
795 | if (numAvailableBytes < 2) {
796 | continue;
797 | }
798 | if (numAvailableBytes > _numFastBytes) {
799 | numAvailableBytes = _numFastBytes;
800 | }
801 | if (!nextIsChar && matchByte !== currentByte) {
802 | // Try literal + rep0
803 | t = min(numAvailableBytesFull - 1, _numFastBytes);
804 | lenTest2 = _matchFinder.getMatchLen(0, reps[0], t);
805 | if (lenTest2 >= 2) {
806 | state2 = state.clone();
807 | state2.updateChar();
808 | posStateNext = (position + 1) & _posStateMask;
809 | nextRepMatchPrice = curAnd1Price +
810 | _isMatch[(state2.index << kNumPosStatesBitsMax) + posStateNext].getPrice1() +
811 | _isRep[state2.index].getPrice1();
812 |
813 | offset = cur + 1 + lenTest2;
814 | while (lenEnd < offset) {
815 | _optimum[++lenEnd].price = kInfinityPrice;
816 | }
817 | curAndLenPrice = nextRepMatchPrice + this.getRepPrice(0, lenTest2, state2, posStateNext);
818 | optimum = _optimum[offset];
819 | if (curAndLenPrice < optimum.price) {
820 | optimum.price = curAndLenPrice;
821 | optimum.posPrev = cur + 1;
822 | optimum.backPrev = 0;
823 | optimum.prev1IsChar = true;
824 | optimum.prev2 = false;
825 | }
826 | }
827 | }
828 |
829 | startLen = 2; // Speed optimization
830 |
831 | for (repIndex = 0; repIndex < kNumRepDistances; repIndex++) {
832 | lenTest = _matchFinder.getMatchLen(-1, reps[repIndex], numAvailableBytes);
833 | if (lenTest < 2) {
834 | continue;
835 | }
836 | lenTestTemp = lenTest;
837 | do {
838 | while (lenEnd < cur + lenTest) {
839 | _optimum[++lenEnd].price = kInfinityPrice;
840 | }
841 | curAndLenPrice = repMatchPrice + this.getRepPrice(repIndex, lenTest, state, posState);
842 | optimum = _optimum[cur + lenTest];
843 | if (curAndLenPrice < optimum.price) {
844 | optimum.price = curAndLenPrice;
845 | optimum.posPrev = cur;
846 | optimum.backPrev = repIndex;
847 | optimum.prev1IsChar = false;
848 | }
849 | } while (--lenTest >= 2);
850 | lenTest = lenTestTemp;
851 |
852 | if (repIndex === 0) {
853 | startLen = lenTest + 1;
854 | }
855 |
856 | if (lenTest < numAvailableBytesFull) {
857 | t = min(numAvailableBytesFull - 1 - lenTest, _numFastBytes);
858 | lenTest2 = _matchFinder.getMatchLen(lenTest, reps[repIndex], t);
859 | if (lenTest2 >= 2) {
860 | state2 = state.clone();
861 | state2.updateRep();
862 | posStateNext = (position + lenTest) & _posStateMask;
863 | curAndLenCharPrice = repMatchPrice +
864 | this.getRepPrice(repIndex, lenTest, state, posState) +
865 | _isMatch[(state2.index << kNumPosStatesBitsMax) + posStateNext].getPrice0() +
866 | _literalEncoder.getSubCoder(position + lenTest, _matchFinder.getIndexByte(lenTest - 2))
867 | .getPrice(true, _matchFinder.getIndexByte(lenTest - 1 - (reps[repIndex] + 1)), _matchFinder.getIndexByte(lenTest - 1));
868 |
869 | state2.updateChar();
870 | posStateNext = (position + lenTest + 1) & _posStateMask;
871 | nextMatchPrice = curAndLenCharPrice + _isMatch[(state2.index << kNumPosStatesBitsMax) + posStateNext].getPrice1();
872 | nextRepMatchPrice = nextMatchPrice + _isRep[state2.index].getPrice1();
873 |
874 | offset = lenTest + 1 + lenTest2;
875 | while (lenEnd < cur + offset) {
876 | _optimum[++lenEnd].price = kInfinityPrice;
877 | }
878 | curAndLenPrice = nextRepMatchPrice + this.getRepPrice(0, lenTest2, state2, posStateNext);
879 | optimum = _optimum[cur + offset];
880 | if (curAndLenPrice < optimum.price) {
881 | optimum.price = curAndLenPrice;
882 | opitmum.posPrev = cur + lenTest + 1;
883 | optimum.backPrev = 0;
884 | optimum.prev1IsChar = true;
885 | optimum.prev2 = true;
886 | optimum.posPrev2 = cur;
887 | optimum.backPrev2 = repIndex;
888 | }
889 | }
890 | }
891 | }
892 |
893 | if (newLen > numAvailableBytes) {
894 | newLen = numAvailableBytes;
895 | numDistancePairs = 0;
896 | while (newLen > _matchDistances[numDistancePairs]) {
897 | numDistancePairs += 2;
898 | }
899 | _matchDistances[numDistancePairs] = newLen;
900 | numDistancePairs += 2;
901 | }
902 |
903 | if (newLen >= startLen) {
904 | normalMatchPrice = matchPrice + _isRep[state.index].getPrice0();
905 | while (lenEnd < cur + newLen) {
906 | _optimum[++lenEnd].price = kInfinityPrice;
907 | }
908 |
909 | offs = 0;
910 | while (startLen > _matchDistances[offs]) {
911 | offs += 2;
912 | }
913 |
914 | for (lenTest = startLen;; lenTest++) {
915 | curBack = _matchDistances[offs + 1];
916 | curAndLenPrice = normalMatchPrice + this.getPosLenPrice(curBack, lenTest, posState);
917 | optimum = _optimum[cur + lenTest];
918 | if (curAndLenPrice < optimum.price) {
919 | optimum.price = curAndLenPrice;
920 | optimum.posPrev = cur;
921 | optimum.backPrev = curBack + kNumRepDistances;
922 | optimum.prev1IsChar = false;
923 | }
924 |
925 | if (lenTest === _matchDistances[offs]) {
926 | if (lenTest < numAvailableBytesFull) {
927 | t = min(numAvailableBytesFull - 1 - lenTest, _numFastBytes);
928 | lenTest2 = _matchFinder.getMatchLen(lenTest, curBack, t);
929 | if (lenTest2 >= 2) {
930 | state2 = state.clone();
931 | state2.updateMatch();
932 | posStateNext = (position + lenTest) & _posStateMax;
933 | curAndLenCharPrice = curAndLenPrice +
934 | _isMatch[(state2.index << kNumPosStatesBitsMax) + posStateNext].getPrice0() +
935 | _literalEncoder.getSubCoder(position + lenTest, matchFinder.getIndexByte(lenTest - 2))
936 | .getPrice(true, _matchFinder.getIndexByte(lenTest - (curBack + 1) -1, _matchFinder.getIndexByte(lenTest - 1)));
937 |
938 | state2.updateChar();
939 | posStateNext = (position + lenTest + 1) & _posStateMask;
940 | nextMatchPrice = curAndLenCharPrice + _isMatch[(state2.index << kNumPosStatesBitsMax) + posStateNext].getPrice1();
941 | nextRepMatchPrice = nextMatchPrice + _isRep[state2.index].getPrice1();
942 |
943 | offset = lenTest + 1 + lenTest2;
944 | while (lenEnd < cur + offset) {
945 | _optimum[++lenEnd].price = kInfinityPrice;
946 | }
947 | curAndLenPrice = nextRepMatchPrice + this.getRepPrice(0, lenTest2, state2, posStateNext);
948 | optimum = _optimum[cur + offset];
949 | if (curAndLenPrice < optimum.price) {
950 | optimum.price = curAndLenPrice;
951 | optimum.posPrev = cur + lenTest + 1;
952 | optimum.backPrev = 0;
953 | optimum.prev1IsChar = true;
954 | optimum.prev2 = true;
955 | optimum.posPrev2 = cur;
956 | optimum.backPrev2 = curBack + kNumRepDistances;
957 | }
958 | }
959 | }
960 | offs += 2;
961 | if (offs === numDistancePairs) {
962 | break;
963 | }
964 | }
965 | }
966 | }
967 | }
968 | };
969 |
970 | this.changePair = function(smallDist, bigDist) {
971 | var kDif = 7;
972 | return (smallDist < (1 << (32 - kDif)) && bigDist >= (smallDist << kDif));
973 | };
974 |
975 | this.writeEndMarker = function(posState) {
976 | var len = kMatchMinLen, footerBits = 30, posReduced;
977 |
978 | if (_writeEndMark)
979 | return;
980 |
981 | _isMatch[(_state.index << kNumPosStatesBitsMax) + posState].encode(_rangeEncoder, 1);
982 | _isRep[_state.index].encode(_rangeEncoder, 0);
983 | _state.updateMatch();
984 |
985 | _lenEncoder.encode(_rangeEncoder, len - kMatchMinLen, posState);
986 | posSlot = (1 << kNumPosSlotBits) - 1;
987 | lenToPosState = this.getLenToPosState(len);
988 | _posSlotEncoder[lenToPosState].encode(_rangeEncoder, posSlot);
989 |
990 | posReduced = (1 << footerBits) - 1;
991 | _rangeEncoder.encodeDirectBits(posReduced >> kNumAlignBits, footerBits - kNumAlignBits);
992 | _posAlignEncoder.reverseEncode(_rangeEncoder, posReduced & kAlignMask);
993 | };
994 |
995 | this.flush = function(nowPos) {
996 | this.releaseMFStream();
997 | this.writeEndMarker(nowPos & _posStateMask);
998 | _rangeEncoder.flushData();
999 | _rangeEncoder.flushStream();
1000 | };
1001 |
1002 | this.releaseMFStream = function() {
1003 | if (_matchFinder !== null && _needReleaseMFStream) {
1004 | _matchFinder.releaseStream();
1005 | _needReleaseMFStream = false;
1006 | }
1007 | };
1008 |
1009 | this.code = function() {
1010 | var matchDistances, posState;
1011 | var progressPosValuePrev = nowPos;
1012 |
1013 | if (nowPos === 0) {
1014 | if (_matchFinder.getNumAvailableBytes() === 0) {
1015 | this.flush(nowPos);
1016 | return;
1017 | }
1018 |
1019 | matchDistances = this.readMatchDistances();
1020 | posState = nowPos & _posStateMask;
1021 |
1022 | }
1023 | };
1024 | }
1025 |
1026 | exports.Encoder = Encoder;
--------------------------------------------------------------------------------
/lzma.js:
--------------------------------------------------------------------------------
1 |
2 | var LZMA = LZMA || {};
3 |
4 | LZMA.OutWindow = function(){
5 | this._windowSize = 0;
6 | };
7 |
8 | LZMA.OutWindow.prototype.create = function(windowSize){
9 | if ( (!this._buffer) || (this._windowSize !== windowSize) ){
10 | this._buffer = [];
11 | }
12 | this._windowSize = windowSize;
13 | this._pos = 0;
14 | this._streamPos = 0;
15 | };
16 |
17 | LZMA.OutWindow.prototype.flush = function(){
18 | var size = this._pos - this._streamPos;
19 | if (size !== 0){
20 | while(size --){
21 | this._stream.writeByte(this._buffer[this._streamPos ++]);
22 | }
23 | if (this._pos >= this._windowSize){
24 | this._pos = 0;
25 | }
26 | this._streamPos = this._pos;
27 | }
28 | };
29 |
30 | LZMA.OutWindow.prototype.releaseStream = function(){
31 | this.flush();
32 | this._stream = null;
33 | };
34 |
35 | LZMA.OutWindow.prototype.setStream = function(stream){
36 | this.releaseStream();
37 | this._stream = stream;
38 | };
39 |
40 | LZMA.OutWindow.prototype.init = function(solid){
41 | if (!solid){
42 | this._streamPos = 0;
43 | this._pos = 0;
44 | }
45 | };
46 |
47 | LZMA.OutWindow.prototype.copyBlock = function(distance, len){
48 | var pos = this._pos - distance - 1;
49 | if (pos < 0){
50 | pos += this._windowSize;
51 | }
52 | while(len --){
53 | if (pos >= this._windowSize){
54 | pos = 0;
55 | }
56 | this._buffer[this._pos ++] = this._buffer[pos ++];
57 | if (this._pos >= this._windowSize){
58 | this.flush();
59 | }
60 | }
61 | };
62 |
63 | LZMA.OutWindow.prototype.putByte = function(b){
64 | this._buffer[this._pos ++] = b;
65 | if (this._pos >= this._windowSize){
66 | this.flush();
67 | }
68 | };
69 |
70 | LZMA.OutWindow.prototype.getByte = function(distance){
71 | var pos = this._pos - distance - 1;
72 | if (pos < 0){
73 | pos += this._windowSize;
74 | }
75 | return this._buffer[pos];
76 | };
77 |
78 | LZMA.RangeDecoder = function(){
79 | };
80 |
81 | LZMA.RangeDecoder.prototype.setStream = function(stream){
82 | this._stream = stream;
83 | };
84 |
85 | LZMA.RangeDecoder.prototype.releaseStream = function(){
86 | this._stream = null;
87 | };
88 |
89 | LZMA.RangeDecoder.prototype.init = function(){
90 | var i = 5;
91 |
92 | this._code = 0;
93 | this._range = -1;
94 |
95 | while(i --){
96 | this._code = (this._code << 8) | this._stream.readByte();
97 | }
98 | };
99 |
100 | LZMA.RangeDecoder.prototype.decodeDirectBits = function(numTotalBits){
101 | var result = 0, i = numTotalBits, t;
102 |
103 | while(i --){
104 | this._range >>>= 1;
105 | t = (this._code - this._range) >>> 31;
106 | this._code -= this._range & (t - 1);
107 | result = (result << 1) | (1 - t);
108 |
109 | if ( (this._range & 0xff000000) === 0){
110 | this._code = (this._code << 8) | this._stream.readByte();
111 | this._range <<= 8;
112 | }
113 | }
114 |
115 | return result;
116 | };
117 |
118 | LZMA.RangeDecoder.prototype.decodeBit = function(probs, index){
119 | var prob = probs[index],
120 | newBound = (this._range >>> 11) * prob;
121 |
122 | if ( (this._code ^ 0x80000000) < (newBound ^ 0x80000000) ){
123 | this._range = newBound;
124 | probs[index] += (2048 - prob) >>> 5;
125 | if ( (this._range & 0xff000000) === 0){
126 | this._code = (this._code << 8) | this._stream.readByte();
127 | this._range <<= 8;
128 | }
129 | return 0;
130 | }
131 |
132 | this._range -= newBound;
133 | this._code -= newBound;
134 | probs[index] -= prob >>> 5;
135 | if ( (this._range & 0xff000000) === 0){
136 | this._code = (this._code << 8) | this._stream.readByte();
137 | this._range <<= 8;
138 | }
139 | return 1;
140 | };
141 |
142 | LZMA.initBitModels = function(probs, len){
143 | while(len --){
144 | probs[len] = 1024;
145 | }
146 | };
147 |
148 | LZMA.BitTreeDecoder = function(numBitLevels){
149 | this._models = [];
150 | this._numBitLevels = numBitLevels;
151 | };
152 |
153 | LZMA.BitTreeDecoder.prototype.init = function(){
154 | LZMA.initBitModels(this._models, 1 << this._numBitLevels);
155 | };
156 |
157 | LZMA.BitTreeDecoder.prototype.decode = function(rangeDecoder){
158 | var m = 1, i = this._numBitLevels;
159 |
160 | while(i --){
161 | m = (m << 1) | rangeDecoder.decodeBit(this._models, m);
162 | }
163 | return m - (1 << this._numBitLevels);
164 | };
165 |
166 | LZMA.BitTreeDecoder.prototype.reverseDecode = function(rangeDecoder){
167 | var m = 1, symbol = 0, i = 0, bit;
168 |
169 | for (; i < this._numBitLevels; ++ i){
170 | bit = rangeDecoder.decodeBit(this._models, m);
171 | m = (m << 1) | bit;
172 | symbol |= bit << i;
173 | }
174 | return symbol;
175 | };
176 |
177 | LZMA.reverseDecode2 = function(models, startIndex, rangeDecoder, numBitLevels){
178 | var m = 1, symbol = 0, i = 0, bit;
179 |
180 | for (; i < numBitLevels; ++ i){
181 | bit = rangeDecoder.decodeBit(models, startIndex + m);
182 | m = (m << 1) | bit;
183 | symbol |= bit << i;
184 | }
185 | return symbol;
186 | };
187 |
188 | LZMA.LenDecoder = function(){
189 | this._choice = [];
190 | this._lowCoder = [];
191 | this._midCoder = [];
192 | this._highCoder = new LZMA.BitTreeDecoder(8);
193 | this._numPosStates = 0;
194 | };
195 |
196 | LZMA.LenDecoder.prototype.create = function(numPosStates){
197 | for (; this._numPosStates < numPosStates; ++ this._numPosStates){
198 | this._lowCoder[this._numPosStates] = new LZMA.BitTreeDecoder(3);
199 | this._midCoder[this._numPosStates] = new LZMA.BitTreeDecoder(3);
200 | }
201 | };
202 |
203 | LZMA.LenDecoder.prototype.init = function(){
204 | var i = this._numPosStates;
205 | LZMA.initBitModels(this._choice, 2);
206 | while(i --){
207 | this._lowCoder[i].init();
208 | this._midCoder[i].init();
209 | }
210 | this._highCoder.init();
211 | };
212 |
213 | LZMA.LenDecoder.prototype.decode = function(rangeDecoder, posState){
214 | if (rangeDecoder.decodeBit(this._choice, 0) === 0){
215 | return this._lowCoder[posState].decode(rangeDecoder);
216 | }
217 | if (rangeDecoder.decodeBit(this._choice, 1) === 0){
218 | return 8 + this._midCoder[posState].decode(rangeDecoder);
219 | }
220 | return 16 + this._highCoder.decode(rangeDecoder);
221 | };
222 |
223 | LZMA.Decoder2 = function(){
224 | this._decoders = [];
225 | };
226 |
227 | LZMA.Decoder2.prototype.init = function(){
228 | LZMA.initBitModels(this._decoders, 0x300);
229 | };
230 |
231 | LZMA.Decoder2.prototype.decodeNormal = function(rangeDecoder){
232 | var symbol = 1;
233 |
234 | do{
235 | symbol = (symbol << 1) | rangeDecoder.decodeBit(this._decoders, symbol);
236 | }while(symbol < 0x100);
237 |
238 | return symbol & 0xff;
239 | };
240 |
241 | LZMA.Decoder2.prototype.decodeWithMatchByte = function(rangeDecoder, matchByte){
242 | var symbol = 1, matchBit, bit;
243 |
244 | do{
245 | matchBit = (matchByte >> 7) & 1;
246 | matchByte <<= 1;
247 | bit = rangeDecoder.decodeBit(this._decoders, ( (1 + matchBit) << 8) + symbol);
248 | symbol = (symbol << 1) | bit;
249 | if (matchBit !== bit){
250 | while(symbol < 0x100){
251 | symbol = (symbol << 1) | rangeDecoder.decodeBit(this._decoders, symbol);
252 | }
253 | break;
254 | }
255 | }while(symbol < 0x100);
256 |
257 | return symbol & 0xff;
258 | };
259 |
260 | LZMA.LiteralDecoder = function(){
261 | };
262 |
263 | LZMA.LiteralDecoder.prototype.create = function(numPosBits, numPrevBits){
264 | var i;
265 |
266 | if (this._coders
267 | && (this._numPrevBits === numPrevBits)
268 | && (this._numPosBits === numPosBits) ){
269 | return;
270 | }
271 | this._numPosBits = numPosBits;
272 | this._posMask = (1 << numPosBits) - 1;
273 | this._numPrevBits = numPrevBits;
274 |
275 | this._coders = [];
276 |
277 | i = 1 << (this._numPrevBits + this._numPosBits);
278 | while(i --){
279 | this._coders[i] = new LZMA.Decoder2();
280 | }
281 | };
282 |
283 | LZMA.LiteralDecoder.prototype.init = function(){
284 | var i = 1 << (this._numPrevBits + this._numPosBits);
285 | while(i --){
286 | this._coders[i].init();
287 | }
288 | };
289 |
290 | LZMA.LiteralDecoder.prototype.getDecoder = function(pos, prevByte){
291 | return this._coders[( (pos & this._posMask) << this._numPrevBits)
292 | + ( (prevByte & 0xff) >>> (8 - this._numPrevBits) )];
293 | };
294 |
295 | LZMA.Decoder = function(){
296 | this._outWindow = new LZMA.OutWindow();
297 | this._rangeDecoder = new LZMA.RangeDecoder();
298 | this._isMatchDecoders = [];
299 | this._isRepDecoders = [];
300 | this._isRepG0Decoders = [];
301 | this._isRepG1Decoders = [];
302 | this._isRepG2Decoders = [];
303 | this._isRep0LongDecoders = [];
304 | this._posSlotDecoder = [];
305 | this._posDecoders = [];
306 | this._posAlignDecoder = new LZMA.BitTreeDecoder(4);
307 | this._lenDecoder = new LZMA.LenDecoder();
308 | this._repLenDecoder = new LZMA.LenDecoder();
309 | this._literalDecoder = new LZMA.LiteralDecoder();
310 | this._dictionarySize = -1;
311 | this._dictionarySizeCheck = -1;
312 |
313 | this._posSlotDecoder[0] = new LZMA.BitTreeDecoder(6);
314 | this._posSlotDecoder[1] = new LZMA.BitTreeDecoder(6);
315 | this._posSlotDecoder[2] = new LZMA.BitTreeDecoder(6);
316 | this._posSlotDecoder[3] = new LZMA.BitTreeDecoder(6);
317 | };
318 |
319 | LZMA.Decoder.prototype.setDictionarySize = function(dictionarySize){
320 | if (dictionarySize < 0){
321 | return false;
322 | }
323 | if (this._dictionarySize !== dictionarySize){
324 | this._dictionarySize = dictionarySize;
325 | this._dictionarySizeCheck = Math.max(this._dictionarySize, 1);
326 | this._outWindow.create( Math.max(this._dictionarySizeCheck, 4096) );
327 | }
328 | return true;
329 | };
330 |
331 | LZMA.Decoder.prototype.setLcLpPb = function(lc, lp, pb){
332 | var numPosStates = 1 << pb;
333 |
334 | if (lc > 8 || lp > 4 || pb > 4){
335 | return false;
336 | }
337 |
338 | this._literalDecoder.create(lp, lc);
339 |
340 | this._lenDecoder.create(numPosStates);
341 | this._repLenDecoder.create(numPosStates);
342 | this._posStateMask = numPosStates - 1;
343 |
344 | return true;
345 | };
346 |
347 | LZMA.Decoder.prototype.init = function(){
348 | var i = 4;
349 |
350 | this._outWindow.init(false);
351 |
352 | LZMA.initBitModels(this._isMatchDecoders, 192);
353 | LZMA.initBitModels(this._isRep0LongDecoders, 192);
354 | LZMA.initBitModels(this._isRepDecoders, 12);
355 | LZMA.initBitModels(this._isRepG0Decoders, 12);
356 | LZMA.initBitModels(this._isRepG1Decoders, 12);
357 | LZMA.initBitModels(this._isRepG2Decoders, 12);
358 | LZMA.initBitModels(this._posDecoders, 114);
359 |
360 | this._literalDecoder.init();
361 |
362 | while(i --){
363 | this._posSlotDecoder[i].init();
364 | }
365 |
366 | this._lenDecoder.init();
367 | this._repLenDecoder.init();
368 | this._posAlignDecoder.init();
369 | this._rangeDecoder.init();
370 | };
371 |
372 | LZMA.Decoder.prototype.decode = function(inStream, outStream, outSize){
373 | var state = 0, rep0 = 0, rep1 = 0, rep2 = 0, rep3 = 0, nowPos64 = 0, prevByte = 0,
374 | posState, decoder2, len, distance, posSlot, numDirectBits;
375 |
376 | this._rangeDecoder.setStream(inStream);
377 | this._outWindow.setStream(outStream);
378 |
379 | this.init();
380 |
381 | while(outSize < 0 || nowPos64 < outSize){
382 | posState = nowPos64 & this._posStateMask;
383 |
384 | if (this._rangeDecoder.decodeBit(this._isMatchDecoders, (state << 4) + posState) === 0){
385 | decoder2 = this._literalDecoder.getDecoder(nowPos64 ++, prevByte);
386 |
387 | if (state >= 7){
388 | prevByte = decoder2.decodeWithMatchByte(this._rangeDecoder, this._outWindow.getByte(rep0) );
389 | }else{
390 | prevByte = decoder2.decodeNormal(this._rangeDecoder);
391 | }
392 | this._outWindow.putByte(prevByte);
393 |
394 | state = state < 4? 0: state - (state < 10? 3: 6);
395 |
396 | }else{
397 |
398 | if (this._rangeDecoder.decodeBit(this._isRepDecoders, state) === 1){
399 | len = 0;
400 | if (this._rangeDecoder.decodeBit(this._isRepG0Decoders, state) === 0){
401 | if (this._rangeDecoder.decodeBit(this._isRep0LongDecoders, (state << 4) + posState) === 0){
402 | state = state < 7? 9: 11;
403 | len = 1;
404 | }
405 | }else{
406 | if (this._rangeDecoder.decodeBit(this._isRepG1Decoders, state) === 0){
407 | distance = rep1;
408 | }else{
409 | if (this._rangeDecoder.decodeBit(this._isRepG2Decoders, state) === 0){
410 | distance = rep2;
411 | }else{
412 | distance = rep3;
413 | rep3 = rep2;
414 | }
415 | rep2 = rep1;
416 | }
417 | rep1 = rep0;
418 | rep0 = distance;
419 | }
420 | if (len === 0){
421 | len = 2 + this._repLenDecoder.decode(this._rangeDecoder, posState);
422 | state = state < 7? 8: 11;
423 | }
424 | }else{
425 | rep3 = rep2;
426 | rep2 = rep1;
427 | rep1 = rep0;
428 |
429 | len = 2 + this._lenDecoder.decode(this._rangeDecoder, posState);
430 | state = state < 7? 7: 10;
431 |
432 | posSlot = this._posSlotDecoder[len <= 5? len - 2: 3].decode(this._rangeDecoder);
433 | if (posSlot >= 4){
434 |
435 | numDirectBits = (posSlot >> 1) - 1;
436 | rep0 = (2 | (posSlot & 1) ) << numDirectBits;
437 |
438 | if (posSlot < 14){
439 | rep0 += LZMA.reverseDecode2(this._posDecoders,
440 | rep0 - posSlot - 1, this._rangeDecoder, numDirectBits);
441 | }else{
442 | rep0 += this._rangeDecoder.decodeDirectBits(numDirectBits - 4) << 4;
443 | rep0 += this._posAlignDecoder.reverseDecode(this._rangeDecoder);
444 | if (rep0 < 0){
445 | if (rep0 === -1){
446 | break;
447 | }
448 | return false;
449 | }
450 | }
451 | }else{
452 | rep0 = posSlot;
453 | }
454 | }
455 |
456 | if (rep0 >= nowPos64 || rep0 >= this._dictionarySizeCheck){
457 | return false;
458 | }
459 |
460 | this._outWindow.copyBlock(rep0, len);
461 | nowPos64 += len;
462 | prevByte = this._outWindow.getByte(0);
463 | }
464 | }
465 |
466 | this._outWindow.flush();
467 | this._outWindow.releaseStream();
468 | this._rangeDecoder.releaseStream();
469 |
470 | return true;
471 | };
472 |
473 | LZMA.Decoder.prototype.setDecoderProperties = function(properties){
474 | var value, lc, lp, pb, dictionarySize;
475 |
476 | if (properties.size < 5){
477 | return false;
478 | }
479 |
480 | value = properties.readByte();
481 | lc = value % 9;
482 | value = ~~(value / 9);
483 | lp = value % 5;
484 | pb = ~~(value / 5);
485 |
486 | if ( !this.setLcLpPb(lc, lp, pb) ){
487 | return false;
488 | }
489 |
490 | dictionarySize = properties.readByte();
491 | dictionarySize |= properties.readByte() << 8;
492 | dictionarySize |= properties.readByte() << 16;
493 | dictionarySize += properties.readByte() * 16777216;
494 |
495 | return this.setDictionarySize(dictionarySize);
496 | };
497 |
498 | LZMA.decompress = function(properties, inStream, outStream, outSize){
499 | var decoder = new LZMA.Decoder();
500 |
501 | if ( !decoder.setDecoderProperties(properties) ){
502 | throw "Incorrect stream properties";
503 | }
504 |
505 | if ( !decoder.decode(inStream, outStream, outSize) ){
506 | throw "Error in data stream";
507 | }
508 |
509 | return true;
510 | };
511 |
512 | exports.LZMA = LZMA;
--------------------------------------------------------------------------------
/lzmajs.tmproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | currentDocument
6 | Development/lzmajs/test.js
7 | documents
8 |
9 |
10 | expanded
11 |
12 | name
13 | lzmajs
14 | regexFolderFilter
15 | !.*/(\.[^/]*|CVS|_darcs|_MTN|\{arch\}|blib|.*~\.nib|.*\.(framework|app|pbproj|pbxproj|xcode(proj)?|bundle))$
16 | sourceDirectory
17 |
18 |
19 |
20 | fileHierarchyDrawerWidth
21 | 206
22 | metaData
23 |
24 | Development/lzmajs/binTree.js
25 |
26 | caret
27 |
28 | column
29 | 0
30 | line
31 | 35
32 |
33 | firstVisibleColumn
34 | 0
35 | firstVisibleLine
36 | 1
37 |
38 | Development/lzmajs/bitEncoder.js
39 |
40 | caret
41 |
42 | column
43 | 1
44 | line
45 | 20
46 |
47 | firstVisibleColumn
48 | 0
49 | firstVisibleLine
50 | 0
51 |
52 | Development/lzmajs/compiledLzma.js
53 |
54 | caret
55 |
56 | column
57 | 15
58 | line
59 | 1262
60 |
61 | firstVisibleColumn
62 | 0
63 | firstVisibleLine
64 | 1246
65 |
66 | Development/lzmajs/encoder.js
67 |
68 | caret
69 |
70 | column
71 | 33
72 | line
73 | 362
74 |
75 | firstVisibleColumn
76 | 0
77 | firstVisibleLine
78 | 0
79 |
80 | Development/lzmajs/lzma.js
81 |
82 | caret
83 |
84 | column
85 | 0
86 | line
87 | 496
88 |
89 | firstVisibleColumn
90 | 0
91 | firstVisibleLine
92 | 14
93 |
94 | Development/lzmajs/optimizing javascript
95 |
96 | caret
97 |
98 | column
99 | 2
100 | line
101 | 6
102 |
103 | firstVisibleColumn
104 | 0
105 | firstVisibleLine
106 | 0
107 |
108 | Development/lzmajs/rangeCoder.js
109 |
110 | caret
111 |
112 | column
113 | 1
114 | line
115 | 11
116 |
117 | firstVisibleColumn
118 | 0
119 | firstVisibleLine
120 | 0
121 |
122 | Development/lzmajs/test.js
123 |
124 | caret
125 |
126 | column
127 | 46
128 | line
129 | 158
130 |
131 | firstVisibleColumn
132 | 0
133 | firstVisibleLine
134 | 133
135 |
136 |
137 | openDocuments
138 |
139 | Development/lzmajs/compiledLzma.js
140 | Development/lzmajs/bitEncoder.js
141 | Development/lzmajs/binTree.js
142 | Development/lzmajs/encoder.js
143 | Development/lzmajs/test.js
144 | Development/lzmajs/rangeCoder.js
145 | Development/lzmajs/lzma.js
146 | Development/lzmajs/optimizing javascript
147 |
148 | showFileHierarchyDrawer
149 |
150 | windowFrame
151 | {{9, 118}, {719, 695}}
152 |
153 |
154 |
--------------------------------------------------------------------------------
/optimizing javascript:
--------------------------------------------------------------------------------
1 | optimizing a range coder
2 | ------------------------
3 |
4 | - Instead of Math.floor(a / b), use (a / b) | 0. Only works if you need signed integers. If you need unsigned, have to compare < 0
5 | - Instead of ((a & 0xFFFFFF) * 256), use, a <<= 8; if (a < 0) a += 0x100000000;
6 | - Constants don't need to be hardcoded most of the time
7 | -
--------------------------------------------------------------------------------
/rangeCoder.js:
--------------------------------------------------------------------------------
1 | var kTopValue = 1 << 24, kBotValue = 1 << 16;
2 |
3 | function shiftLeft8(v) {
4 | v <<= 8;
5 | return v >= 0 ? v : v + 0x100000000;
6 | }
7 |
8 | function Encoder() {
9 | var low, range, cacheSize, cache;
10 | this.bytes = [];
11 |
12 | var init = function() {
13 | low = 0;
14 | range = 0xFFFFFFFF;
15 | cacheSize = 1;
16 | cache = 0;
17 |
18 | this.bytes = [];
19 | };
20 |
21 | init();
22 | this.init = init;
23 |
24 | this.encode = function(start, size, total) {
25 | range = (range / total) | 0;
26 | low += start * range;
27 | range *= size;
28 | while (range < kTopValue) {
29 | range = shiftLeft8(range);
30 | this.shiftLow();
31 | }
32 | };
33 |
34 | this.shiftLow = function() {
35 | var overflow = low > 0xFFFFFFFF;
36 | if (low < 0xFF000000 || overflow) {
37 | var temp = cache;
38 | do {
39 | this.bytes[this.bytes.length] = (temp + (overflow ? 1 : 0)) & 0xFF;
40 | temp = 0xFF;
41 | } while (--cacheSize !== 0);
42 | cache = low >>> 24;
43 | }
44 | cacheSize++;
45 | low = shiftLeft8(low);
46 | };
47 |
48 | this.finish = function() {
49 | var i;
50 | for (i = 0; i < 5; i++) {
51 | this.shiftLow();
52 | }
53 | };
54 |
55 | this.encodeBit = function(size, numTotalBits, symbol) {
56 | var newBound = (range >>> numTotalBits) * size;
57 | if (symbol === 0) {
58 | range = newBound;
59 | } else {
60 | low += newBound;
61 | range -= newBound;
62 | }
63 | while (range < kTopValue) {
64 | range = shiftLeft8(range);
65 | this.shiftLow();
66 | }
67 | };
68 |
69 | /*
70 | this.encodeDirectBits(v, numTotalBits) {
71 | for (var i = numTotalBits - 1; i != 0; i--) {
72 | range >>= 1;
73 | if (((v >> i) & 1) == 1)
74 | low += range;
75 | if (range < kTopValue) {
76 | range <<= 8;
77 | this.shiftLow();
78 | }
79 | }
80 | };
81 | */
82 | }
83 |
84 | function Decoder(bytes) {
85 | var code = 0, range = 0xFFFFFFFF;
86 | var at = 0;
87 | var i;
88 | this.bytes = bytes;
89 |
90 | for (i = 0; i < 5; i++) {
91 | code = (code << 8) | this.bytes[at++];
92 | }
93 |
94 | this.normalize = function() {
95 | while (range < kTopValue) {
96 | code = shiftLeft8(code) | this.bytes[at++];
97 | range = shiftLeft8(range);
98 | }
99 | };
100 |
101 | this.getThreshold = function(total) {
102 | range = (range / total) | 0;
103 | return (code / range) | 0;
104 | };
105 |
106 | this.decode = function(start, size, total) {
107 | code -= start * range;
108 | range *= size;
109 | this.normalize();
110 | };
111 | }
112 |
113 | exports.Encoder = Encoder;
114 | exports.Decoder = Decoder;
--------------------------------------------------------------------------------
/test.js:
--------------------------------------------------------------------------------
1 | var assert = require('assert');
2 | var RangeCoder = require('./rangeCoder');
3 | var BitEncoder = require('./bitEncoder');
4 | var Encoder = require('./encoder');
5 | var LzmaDecompress = require('./lzma');
6 | var BinTree = require('./binTree');
7 |
8 | var min = function(a, b) {
9 | return a < b ? a : b;
10 | };
11 |
12 | var compareArray = function(a1, a2) {
13 | var i;
14 | if (a1.length !== a2.length) {
15 | throw 'lengths not equal';
16 | }
17 | for (i = 0; i < a1.length; i++) {
18 | if (a1[i] !== a2[i]) {
19 | throw 'not equal at ' + i + ' a1[i]=' + a1[i] + ', a2[i]=' + a2[i];
20 | }
21 | }
22 | };
23 |
24 | var createInStream = function(data) {
25 | var inStream = {
26 | data: data,
27 | offset: 0,
28 | readByte: function(){
29 | return this.data[this.offset++];
30 | }
31 | };
32 | return inStream;
33 | };
34 |
35 | var createEncoderStream = function(data) {
36 | var stream = {
37 | data: data,
38 | offset: 0,
39 | read: function(buffer, bufOffset, length) {
40 | var bytesRead = 0;
41 | while (bytesRead < length && this.offset < data.length) {
42 | buffer[bufOffset++] = this.data[this.offset++];
43 | bytesRead++;
44 | }
45 | return bytesRead;
46 | }
47 | };
48 | return stream;
49 | }
50 |
51 | var testRangeCoder = function() {
52 | var i;
53 | var repeats = 3;
54 | var encoder = new RangeCoder.Encoder();
55 | for (i = 0; i < repeats; i++) {
56 | encoder.encode(0,6,20);
57 | encoder.encode(0,6,20);
58 | encoder.encode(6,2,20);
59 | encoder.encode(0,6,20);
60 | }
61 | encoder.encode(8,2,20);
62 | encoder.finish();
63 |
64 | var decoder = new RangeCoder.Decoder(encoder.bytes);
65 | var inside = function(l, r, v) {
66 | assert.ok(v >= l && v < r, v + ' not in ' + l + ',' + r);
67 | };
68 | for (i = 0; i < repeats; i++) {
69 | inside(0, 6, decoder.getThreshold(20));
70 | decoder.decode(0,6,20);
71 | inside(0, 6, decoder.getThreshold(20));
72 | decoder.decode(0,6,20);
73 | inside(6, 8, decoder.getThreshold(20));
74 | decoder.decode(6,2,20);
75 | inside(0, 6, decoder.getThreshold(20));
76 | decoder.decode(0,6,20);
77 | }
78 | inside(8, 10, decoder.getThreshold(20));
79 | };
80 |
81 | var testBitEncoder = function() {
82 | // Simple test for the bit encoder
83 | var testSequence = [5, 1, 9, 8, 10, 15];
84 | var i;
85 |
86 | var bitEncoder = new BitEncoder.BitEncoder();
87 | var rangeEncoder = new RangeCoder.Encoder();
88 | bitEncoder.init();
89 | for (i = 0; i < testSequence.length; i++) {
90 | bitEncoder.encode(rangeEncoder, testSequence[i]);
91 | }
92 | rangeEncoder.finish();
93 | assert.deepEqual(rangeEncoder.bytes, [ 0, 249, 223, 15, 188 ]);
94 | };
95 |
96 | var testCRC = function() {
97 | assert.equal(BinTree.CRC.table.length, 256);
98 | };
99 |
100 | var testBitTreeEncoder = function(testSequence) {
101 | // Test the BitTreeEncoder, using LZMA.js decompression for verification
102 | var i;
103 |
104 | var rangeEncoder = new RangeCoder.Encoder();
105 | var bitTreeEncoder = new BitEncoder.BitTreeEncoder(8);
106 | bitTreeEncoder.init();
107 | for (i = 0; i < testSequence.length; i++) {
108 | bitTreeEncoder.encode(rangeEncoder, testSequence[i]);
109 | }
110 | rangeEncoder.finish();
111 |
112 | var bitTreeDecoder = new LzmaDecompress.LZMA.BitTreeDecoder(8);
113 | bitTreeDecoder.init();
114 | var rangeDecoder = new LzmaDecompress.LZMA.RangeDecoder();
115 | rangeDecoder.setStream(createInStream(rangeEncoder.bytes));
116 | rangeDecoder.init();
117 |
118 | var result = [];
119 | for (i = 0; i < testSequence.length; i++) {
120 | result[result.length] = bitTreeDecoder.decode(rangeDecoder);
121 | }
122 | compareArray(result, testSequence);
123 | };
124 |
125 | var buildSequence = function(length, maxVal) {
126 | var sequence = [];
127 | var seed = 0xDEADBEEF;
128 | var i;
129 | for (i = 0; i < length; i++) {
130 | seed = ((seed * 73) + 0x1234567) % 0xFFFFFFFF;
131 | sequence[i] = seed % maxVal;
132 | }
133 | return sequence;
134 | };
135 |
136 | var testEncoder = function() {
137 | var rangeEncoder = new RangeCoder.Encoder();
138 | var encoder = new Encoder.Encoder();
139 | encoder.create();
140 | encoder.init();
141 |
142 | var literalEncoder = new encoder.LiteralEncoder();
143 | literalEncoder.create(2, 3);
144 | literalEncoder.init();
145 | var subCoder = literalEncoder.getSubCoder(5, 11);
146 | assert.ok(subCoder !== null);
147 |
148 | var lenEncoder = new encoder.LenEncoder();
149 | lenEncoder.init(5);
150 | lenEncoder.encode(rangeEncoder, 1, 0);
151 | lenEncoder.encode(rangeEncoder, 20, 0);
152 | lenEncoder.encode(rangeEncoder, 199, 0);
153 | rangeEncoder.finish();
154 |
155 | var lenPriceTableEncoder = new encoder.LenPriceTableEncoder();
156 | lenPriceTableEncoder.init();
157 | };
158 |
159 | var testBinTree = function(sequence) {
160 | var stream = createEncoderStream(sequence);
161 |
162 | var blockSize = (1 << 12) + 0x20 + 275;
163 | var inWindow = new BinTree.InWindow();
164 | inWindow.createBase(1 << 12, 0x20, 275);
165 | inWindow.setStream(stream);
166 | inWindow.initBase();
167 |
168 | // Test basics
169 | var remaining = min(sequence.length, blockSize);
170 | assert.equal(inWindow.getNumAvailableBytes(), remaining);
171 | assert.equal(inWindow.getIndexByte(0), sequence[0]);
172 | assert.equal(inWindow.getIndexByte(1), sequence[1]);
173 | inWindow.movePosBase();
174 | assert.equal(inWindow.getNumAvailableBytes(), remaining - 1);
175 | assert.equal(inWindow.getIndexByte(0), sequence[1]);
176 |
177 | // Test sequence matching
178 | var testSequenceRepeats = [0, 1, 2, 3, 5, 0, 1, 2, 3, 4];
179 | inWindow.setStream(createEncoderStream(testSequenceRepeats));
180 | inWindow.initBase();
181 | assert.equal(inWindow.getMatch(5, 4, 8), 4);
182 |
183 | // Test BinTree
184 | stream = createEncoderStream(sequence);
185 | var binTree = new BinTree.BinTree();
186 | binTree.setType(4);
187 | binTree.create(1 << 22, 1 << 12, 0x20, 275);
188 | binTree.setStream(stream);
189 | binTree.init();
190 |
191 | assert.equal(binTree.getNumAvailableBytes(), sequence.length);
192 | };
193 |
194 | var runAllTests = function() {
195 | testRangeCoder();
196 | testBitEncoder();
197 | testCRC();
198 |
199 | var testSequenceSmall = [5, 112, 90, 8, 10, 153, 255, 0, 0, 15];
200 | var testSequenceLarge = buildSequence(10000, 255);
201 |
202 | testBitTreeEncoder(testSequenceSmall);
203 | testBitTreeEncoder(testSequenceLarge);
204 |
205 | testBinTree(testSequenceSmall);
206 | testBinTree(testSequenceLarge);
207 |
208 | testEncoder();
209 | };
210 |
211 | var start = new Date();
212 |
213 | runAllTests();
214 |
215 | console.log('Testing finished in', new Date() - start, 'ms');
--------------------------------------------------------------------------------
/testc/testc.cc:
--------------------------------------------------------------------------------
1 | #include
2 |
3 | typedef unsigned int uint;
4 | typedef unsigned char uc;
5 | #define assert(a)
6 |
7 | #define DO(n) for (int _=0; _>24), low<<=8; }
24 | void StartDecode (FILE *F) { passed=low=code=0; range= (uint) -1;
25 | f=F; DO(4) code= code<<8 | InByte();
26 | }
27 |
28 | void Encode (uint cumFreq, uint freq, uint totFreq) {
29 | assert(cumFreq+freq>24), range<<=8, low<<=8;
35 | }
36 |
37 | uint GetFreq (uint totFreq) {
38 | uint tmp= (code-low) / (range/= totFreq);
39 | if (tmp >= totFreq) throw ("Input data corrupt"); // or force it to return
40 | return tmp; // a valid value :)
41 | }
42 |
43 | void Decode (uint cumFreq, uint freq, uint totFreq) {
44 | assert(cumFreq+freq> 32) != 0)
73 | {
74 | unsigned char temp = _cache;
75 | do
76 | {
77 | fputc((unsigned char)(temp + (unsigned char)(low >> 32)), file);
78 | temp = 0xFF;
79 | }
80 | while(--_cacheSize != 0);
81 | _cache = (unsigned char)((uint)low >> 24);
82 | }
83 | _cacheSize++;
84 | low = (uint)low << 8;
85 | }
86 |
87 | void FinishEncode() { for (int i = 0; i < 5; i++) shiftlow(); }
88 | };
89 |
90 | int main() {
91 | RangeCoder2 rc;
92 | FILE *f = fopen("test","wb");
93 | rc.StartEncode(f);
94 | for (int i = 0; i < 3; i++) {
95 | rc.Encode(0,6,20);
96 | rc.Encode(0,6,20);
97 | rc.Encode(6,2,20);
98 | rc.Encode(0,6,20);
99 | }
100 | rc.Encode(8,2,20);
101 | rc.FinishEncode();
102 | fclose(f);
103 | f = fopen("test","rb");
104 | for(;;)
105 | {
106 | int c = getc(f);
107 | if (c == -1) break;
108 | printf("%d,", c);
109 | }
110 | return 0;
111 | }
112 |
--------------------------------------------------------------------------------