├── .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 | --------------------------------------------------------------------------------