diff --git a/webworker/README.md b/webworker/README.md new file mode 100644 index 0000000..21ab012 --- /dev/null +++ b/webworker/README.md @@ -0,0 +1,122 @@ +# WebWorker port of lzma-purejs + +This port has been built by assembling different libraries from lzma-purejs projectand others. + + +## Files + +__lzma.js__ + +Implements the LZMA wrapper class that initializes WebWorker and manage compression/decompression requests. + +__lzma.worker.js__ + +Includes LZMA implementation from lzma-purejs project and browser-buffer to enable browser support. + +__utf8.conv.js__ + +Includes stringToUtf8ByteArray() and utf8ByteArrayToString() from Google Closure library for string encoding to/from UTF8 byte array. + + +## lzma.worker.js structure + +* webworker related stuff +* lib/makeBuffer.js +* lib/LZ/InWindow.js +* lib/LZ/OutWindow.js +* lib/LZ/BinTree.js +* lib/RangeCoder/Encoder.js +* lib/RangeCoder/BitTreeDecoder.js +* lib/RangeCoder/BitTreeEncoder.js +* lib/RangeCoder/Decoder.js +* lib/LZMA/Base.js +* lib/LZMA/Decoder.js +* lib/LZMA/Encoder.js +* lib/Stream.js +* lib/Util.js +* lib/makeBuffer.js +* https://github.com/marcominetti/browser-buffer/blob/master/src/browser-buffer.js (forked from https://github.com/arextar/browser-buffer) + + +## How to install + +Add lzma.js and utf8.conv.js libraries into your app. + +```html + + +``` + +Copy (and minify) lzma.worker.js somewhere on your server. + +Modify lzma.js to fine-tune namespaces and string-bytearray encoding (if you need it). + + +## How to use + +```javascript +var LZMAWrapper = new LZMA('/path/to/lzma.worker.js'); +var LZMAData = null; +LZMAWrapper.Compress('string-to-compress...',3,function(CompressedByteArray){ LZMAData = CompressedByteArray; }); +LZMAWrapper.Compress(new Uint8Array(stringToUtf8ByteArray('string-to-compress...')),3,function(CompressedByteArray){ LZMAData = CompressedByteArray; }); + +[...] + +LZMAWrapper.Decompress(LZMAData,function(DecompressedByteArray){ console.log(utf8ByteArrayToString(DecompressedByteArray)) }); +``` + +### .Compress() synopsis + +LZMAWrapper.Compress(DataAsStringOrUint8Array,CompressLevelAsInt,CallBackFunction); + +CompressLevelAsInt can be a 1-9 integer for compression level. More info in lzma.worker.js at option_mapping var declaration and LZMA-SDK. + + +### .Decompress() synopsis + +LZMAWrapper.Decompress(DataAsStringOrUint8Array,CallBackFunction); + + +### Missing + +Error handling: an error callback function support could be implemented. + + +## References + +lzma-purejs - https://github.com/cscott/lzma-purejs + +Google Closure library (for UTF8Array[] <> string encoding) - https://github.com/google/closure-library + +browser-buffer - https://github.com/arextar/browser-buffer + + +## License (for the WebWorker ported version) + +> Copyright (c) 2011 Gary Linscott +> +> Copyright (c) 2011-2012 Juan Mellado +> +> Copyright (c) 2013 C. Scott Ananian +> +> Copyright (c) 2014 Marco Minetti +> +> All rights reserved. +> +> Permission is hereby granted, free of charge, to any person obtaining a copy +> of this software and associated documentation files (the "Software"), to deal +> in the Software without restriction, including without limitation the rights +> to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +> copies of the Software, and to permit persons to whom the Software is +> furnished to do so, subject to the following conditions: +> +> The above copyright notice and this permission notice shall be included in +> all copies or substantial portions of the Software. +> +> THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +> IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +> FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +> AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +> LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +> OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +> THE SOFTWARE. diff --git a/webworker/lzma.js b/webworker/lzma.js new file mode 100644 index 0000000..30f071c --- /dev/null +++ b/webworker/lzma.js @@ -0,0 +1,52 @@ +/* + LZMA-PUREJS WebWorker Wrapper 0.1 | github.com/cscott/lzma-purejs | (c) 2011-2014 Marco Minetti | MIT */ +this.LZMA = function (PathLZMA) { + LZMAWorker = new Worker(PathLZMA); + + LZMAWorker.CallCounter = 0; + LZMAWorker.Requests = []; + + LZMAWorker.onmessage = function (event) { + var Packet = event.data; + + if (LZMAWorker.Requests[Packet.PacketID] != null && typeof (LZMAWorker.Requests[Packet.PacketID].OnCompleteCallback) == "function") { + LZMAWorker.Requests[Packet.PacketID].OnCompleteCallback(Packet.Result); + } + delete LZMAWorker.Requests[Packet.PacketID]; + }; + + LZMAWorker.onerror = function(event) { + throw new Error(event.message + " (" + event.filename + ":" + event.lineno + ")"); + }; + + this.Compress = function(Data, Level, OnCompleteCallback) { + LZMAWorker.CallCounter++; + LZMAWorker.Requests[LZMAWorker.CallCounter] = {'OnCompleteCallback': OnCompleteCallback}; + + if (Data.constructor == String) { + Data = new Uint8Array(stringToUtf8ByteArray(Data)); + } + + LZMAWorker.postMessage({ + 'Type':'compress', + 'PacketID': LZMAWorker.CallCounter, + 'Data': Data, + 'Level': ((Level != null && typeof(Level)==='number' && Level > 0 && Level < 10) ? Level : 3) + }); + }; + + this.Decompress = function(Data, OnCompleteCallback) { + LZMAWorker.CallCounter++; + LZMAWorker.Requests[LZMAWorker.CallCounter] = {'OnCompleteCallback': OnCompleteCallback}; + + if (Data.constructor == String) { + Data = new Uint8Array(stringToUtf8ByteArray(Data)); + } + + LZMAWorker.postMessage({ + 'Type':'decompress', + 'PacketID': LZMAWorker.CallCounter, + 'Data': Data + }); + }; +} diff --git a/webworker/lzma.worker.js b/webworker/lzma.worker.js new file mode 100644 index 0000000..a32e872 --- /dev/null +++ b/webworker/lzma.worker.js @@ -0,0 +1,4211 @@ +/* + LZMA-JS 0.9.3 | github.com/cscott/lzma-purejs | (c) 2011-2013 Gary Linscott, Juan Mellado, Scott Ananian, Marco Minetti | MIT */ +self.onmessage = function(event) { + var Packet = event.data; + var Result = null; + var PacketID = Packet.PacketID; + try { + if (Packet.Data == null) { + throw new Error('No input data.'); + } + + if (Packet.Type == "compress") { + //C: checking if input is valid + if (Packet.Data.constructor != Uint8Array) { + throw new Error('Input data is not Uint8Array.'); + } + //C: encoding data + Result = Util.compressFile(Packet.Data,null,Packet.Level,null); + } else if (Packet.Type == "decompress") { + //C: checking if input is valid + if (Packet.Data.constructor != Uint8Array) { + throw new Error('Input data is not Uint8Array.'); + } + //C: decoding data + Result = Util.decompressFile(Packet.Data,null); + } + if(PacketID != null) + postMessage({"PacketID":Packet.PacketID, "Result":Result}); + else + postMessage({"Result":Result}); + } catch (CaughtException) { + if(PacketID != null) + postMessage({"PacketID":Packet.PacketID, "Error":CaughtException.message}); + else + postMessage({"Error":CaughtException.message}); + } +}; + + +/* lib/makeBuffer.js */ +// typed array / Buffer compatibility. +var makeBuffer = function (len) { + var b = [], + i; + for (i = 0; i < len; i++) { + b[i] = 0; + } + return b; +}; +if (typeof (Uint8Array) !== 'undefined') { + makeBuffer = function (len) { + return new Uint8Array(len); + }; +} else if (typeof (Buffer) !== 'undefined') { + makeBuffer = function (len) { + var b = new Buffer(len); + b.fill(0); // zero-fill, for consistency + return b; + }; +} + +var LZ = (function () { + + /* lib/LZ/InWindow.js */ + + var InWindow = function (keepSizeBefore, keepSizeAfter, keepSizeReserve, stream) { + if (arguments.length >= 4) { + // typical initialization sequence + var args = Array.prototype.slice.call(arguments, 0); + var _stream = args.pop(); + this.create.apply(this, args); + this.setStream(_stream); + this.init(); + } + }; + + InWindow.prototype.moveBlock = function () { + var i; + var offset = this._bufferOffset + this._pos + this._keepSizeBefore; + // we need one additional byte, since MovePos moves on 1 byte. + if (offset > 0) { + offset--; + } + + var numBytes = this._bufferOffset + this._streamPos - offset; + for (i = 0; i < numBytes; i++) { + this._bufferBase[i] = this._bufferBase[offset + i]; + } + this._bufferOffset -= offset; + }; + + InWindow.prototype.readBlock = function () { + if (this._streamEndWasReached) { + return; + } + while (true) { + var size = -this._bufferOffset + this._blockSize - this._streamPos; + if (size === 0) { + return; + } + var numReadBytes = + this._stream.read(this._bufferBase, + this._bufferOffset + this._streamPos, + size); + if (numReadBytes <= 0) { + this._posLimit = this._streamPos; + var pointerToPosition = this._bufferOffset + this._posLimit; + if (pointerToPosition > this._pointerToLastSafePosition) { + this._posLimit = this._pointerToLastSafePosition - this._bufferOffset; + } + + this._streamEndWasReached = true; + return; + } + this._streamPos += numReadBytes; + if (this._streamPos >= this._pos + this._keepSizeAfter) { + this._posLimit = this._streamPos - this._keepSizeAfter; + } + } + }; + + InWindow.prototype.free = function () { + this._bufferBase = null; + }; + + InWindow.prototype.create = function (keepSizeBefore, keepSizeAfter, keepSizeReserve) { + this._keepSizeBefore = keepSizeBefore; + this._keepSizeAfter = keepSizeAfter; + var blockSize = keepSizeBefore + keepSizeAfter + keepSizeReserve; + if ((!this._bufferBase) || this._blockSize !== blockSize) { + this.free(); + this._blockSize = blockSize; + this._bufferBase = makeBuffer(this._blockSize); + } + this._pointerToLastSafePosition = this._blockSize - keepSizeAfter; + }; + + InWindow.prototype.setStream = function (stream) { + this._stream = stream; + }; + + InWindow.prototype.releaseStream = function () { + this._stream = null; + }; + + InWindow.prototype.init = function () { + this._bufferOffset = 0; + this._pos = 0; + this._streamPos = 0; + this._streamEndWasReached = false; + this.readBlock(); + }; + + InWindow.prototype.movePos = function () { + this._pos++; + if (this._pos > this._posLimit) { + var pointerToPosition = this._bufferOffset + this._pos; + if (pointerToPosition > this._pointerToLastSafePosition) { + this.moveBlock(); + } + this.readBlock(); + } + }; + + InWindow.prototype.getIndexByte = function (index) { + return this._bufferBase[this._bufferOffset + this._pos + index]; + }; + + // index + limit have not to exceed _keepSizeAfter + InWindow.prototype.getMatchLen = function (index, distance, limit) { + var pby, i; + if (this._streamEndWasReached) { + if (this._pos + index + limit > this._streamPos) { + limit = this._streamPos - (this._pos + index); + } + } + distance++; + pby = this._bufferOffset + this._pos + index; + for (i = 0; i < limit && this._bufferBase[pby + i] === this._bufferBase[pby + i - distance];) { + i++; + } + return i; + }; + + InWindow.prototype.getNumAvailableBytes = function () { + return this._streamPos - this._pos; + }; + + InWindow.prototype.reduceOffsets = function (subValue) { + this._bufferOffset += subValue; + this._posLimit -= subValue; + this._pos -= subValue; + this._streamPos -= subValue; + }; + + + /* lib/LZ/OutWindow.js */ + var OutWindow = function () { + this._windowSize = 0; + }; + + OutWindow.prototype.create = function (windowSize) { + if ((!this._buffer) || (this._windowSize !== windowSize)) { + this._buffer = makeBuffer(windowSize); + } + this._windowSize = windowSize; + this._pos = 0; + this._streamPos = 0; + }; + + OutWindow.prototype.flush = function () { + var size = this._pos - this._streamPos; + if (size !== 0) { + while (size--) { + this._stream.writeByte(this._buffer[this._streamPos++]); + } + if (this._pos >= this._windowSize) { + this._pos = 0; + } + this._streamPos = this._pos; + } + }; + + OutWindow.prototype.releaseStream = function () { + this.flush(); + this._stream = null; + }; + + OutWindow.prototype.setStream = function (stream) { + this.releaseStream(); + this._stream = stream; + }; + + OutWindow.prototype.init = function (solid) { + if (!solid) { + this._streamPos = 0; + this._pos = 0; + } + }; + + OutWindow.prototype.copyBlock = function (distance, len) { + var pos = this._pos - distance - 1; + if (pos < 0) { + pos += this._windowSize; + } + while (len--) { + if (pos >= this._windowSize) { + pos = 0; + } + this._buffer[this._pos++] = this._buffer[pos++]; + if (this._pos >= this._windowSize) { + this.flush(); + } + } + }; + + OutWindow.prototype.putByte = function (b) { + this._buffer[this._pos++] = b; + if (this._pos >= this._windowSize) { + this.flush(); + } + }; + + OutWindow.prototype.getByte = function (distance) { + var pos = this._pos - distance - 1; + if (pos < 0) { + pos += this._windowSize; + } + return this._buffer[pos]; + }; + + /* lib/LZ/BinTree.js */ + var CrcTable = (function () { + var table = []; + if (typeof (Uint32Array) !== 'undefined') { + table = new Uint32Array(256); + } + + var kPoly = 0xEDB88320, + i, j, r; + for (i = 0; i < 256; i++) { + r = i; + for (j = 0; j < 8; j++) { + if ((r & 1) !== 0) { + r = (r >>> 1) ^ kPoly; + } else { + r >>>= 1; + } + } + table[i] = r; + } + + return table; + })(); + + // constants + var kHash2Size = 1 << 10, + kHash3Size = 1 << 16, + kBT2HashSize = 1 << 16; + var kStartMaxLen = 1, + kHash3Offset = kHash2Size, + kEmptyHashValue = 0; + var kMaxValForNormalize = (1 << 30) - 1; + + function BinTree() { + InWindow.call(this); + this._cyclicBufferSize = 0; + + this._son = []; + this._hash = []; + + this._cutValue = 0xFF; + this._hashSizeSum = 0; + + this.HASH_ARRAY = true; + + this.kNumHashDirectBytes = 0; + this.kMinMatchCheck = 4; + this.kFixHashSize = kHash2Size + kHash3Size; + + if (arguments.length >= 6) { + var args = Array.prototype.slice.call(arguments, 0); + this.setType(args.shift()); + var stream = args.pop(); + this.create.apply(this, args); + this.setStream(stream); + this.init(); + } + } + // a little bit of sugar for super-method invocations. + var _super_ = InWindow.prototype; + BinTree.prototype = Object.create(_super_); + + BinTree.prototype.setType = function (numHashBytes) { + this.HASH_ARRAY = numHashBytes > 2; + if (this.HASH_ARRAY) { + this.kNumHashDirectBytes = 0; + this.kMinMatchCheck = 4; + this.kFixHashSize = kHash2Size + kHash3Size; + } else { + this.kNumHashDirectBytes = 2; + this.kMinMatchCheck = 2 + 1; + this.kFixHashSize = 0; + } + }; + + BinTree.prototype.init = function () { + var i; + _super_.init.call(this); + for (i = 0; i < this._hashSizeSum; i++) { + this._hash[i] = kEmptyHashValue; + } + this._cyclicBufferPos = 0; + this.reduceOffsets(-1); + }; + + BinTree.prototype.movePos = function () { + if (++this._cyclicBufferPos >= this._cyclicBufferSize) { + this._cyclicBufferPos = 0; + } + _super_.movePos.call(this); + if (this._pos === kMaxValForNormalize) { + this.normalize(); + } + }; + + BinTree.prototype.create = function (historySize, keepAddBufferBefore, matchMaxLen, keepAddBufferAfter) { + var windowReservSize, cyclicBufferSize, hs; + + if (historySize > kMaxValForNormalize - 256) { + return false; + } + this._cutValue = 16 + (matchMaxLen >>> 1); + + windowReservSize = (historySize + keepAddBufferBefore + + matchMaxLen + keepAddBufferAfter) / 2 + 256; + + _super_.create.call(this, historySize + keepAddBufferBefore, + matchMaxLen + keepAddBufferAfter, + windowReservSize); + + this._matchMaxLen = matchMaxLen; + + cyclicBufferSize = historySize + 1; + if (this._cyclicBufferSize !== cyclicBufferSize) { + this._cyclicBufferSize = cyclicBufferSize; + this._son = []; + this._son.length = cyclicBufferSize * 2; + } + + hs = kBT2HashSize; + + if (this.HASH_ARRAY) { + hs = historySize - 1; + hs |= hs >>> 1; + hs |= hs >>> 2; + hs |= hs >>> 4; + hs |= hs >>> 8; + hs >>>= 1; + hs |= 0xFFFF; + if (hs > (1 << 24)) { + hs >>>= 1; + } + this._hashMask = hs; + hs++; + hs += this.kFixHashSize; + } + if (hs !== this._hashSizeSum) { + this._hashSizeSum = hs; + this._hash = []; + this._hash.length = this._hashSizeSum; + } + return true; + }; + + BinTree.prototype.getMatches = function (distances) { + var lenLimit; + if (this._pos + this._matchMaxLen <= this._streamPos) { + lenLimit = this._matchMaxLen; + } else { + lenLimit = this._streamPos - this._pos; + if (lenLimit < this.kMinMatchCheck) { + this.movePos(); + return 0; + } + } + + var offset = 0; + var matchMinPos = (this._pos > this._cyclicBufferSize) ? (this._pos - this._cyclicBufferSize) : 0; + var cur = this._bufferOffset + this._pos; + var maxLen = kStartMaxLen; // to avoid items for len < hashSize + var hashValue = 0, + hash2Value = 0, + hash3Value = 0; + + if (this.HASH_ARRAY) { + var temp = CrcTable[this._bufferBase[cur]] ^ this._bufferBase[cur + 1]; + hash2Value = temp & (kHash2Size - 1); + temp ^= this._bufferBase[cur + 2] << 8; + hash3Value = temp & (kHash3Size - 1); + hashValue = (temp ^ (CrcTable[this._bufferBase[cur + 3]] << 5)) & this._hashMask; + } else { + hashValue = this._bufferBase[cur] ^ (this._bufferBase[cur + 1] << 8); + } + + var curMatch = this._hash[this.kFixHashSize + hashValue]; + if (this.HASH_ARRAY) { + var curMatch2 = this._hash[hash2Value]; + var curMatch3 = this._hash[kHash3Offset + hash3Value]; + this._hash[hash2Value] = this._pos; + this._hash[kHash3Offset + hash3Value] = this._pos; + if (curMatch2 > matchMinPos) { + if (this._bufferBase[this._bufferOffset + curMatch2] === this._bufferBase[cur]) { + distances[offset++] = maxLen = 2; + distances[offset++] = this._pos - curMatch2 - 1; + } + } + if (curMatch3 > matchMinPos) { + if (this._bufferBase[this._bufferOffset + curMatch3] === this._bufferBase[cur]) { + if (curMatch3 === curMatch2) { + offset -= 2; + } + distances[offset++] = maxLen = 3; + distances[offset++] = this._pos - curMatch3 - 1; + curMatch2 = curMatch3; + } + } + if (offset !== 0 && curMatch2 === curMatch) { + offset -= 2; + maxLen = kStartMaxLen; + } + } + + this._hash[this.kFixHashSize + hashValue] = this._pos; + + var ptr0 = (this._cyclicBufferPos << 1) + 1; + var ptr1 = (this._cyclicBufferPos << 1); + + var len0, len1; + len0 = len1 = this.kNumHashDirectBytes; + + if (this.kNumHashDirectBytes !== 0) { + if (curMatch > matchMinPos) { + if (this._bufferBase[this._bufferOffset + curMatch + this.kNumHashDirectBytes] !== + this._bufferBase[cur + this.kNumHashDirectBytes]) { + distances[offset++] = maxLen = this.kNumHashDirectBytes; + distances[offset++] = this._pos - curMatch - 1; + } + } + } + + var count = this._cutValue; + + while (true) { + if (curMatch <= matchMinPos || count-- === 0) { + this._son[ptr0] = this._son[ptr1] = kEmptyHashValue; + break; + } + + var delta = this._pos - curMatch; + var cyclicPos = ((delta <= this._cyclicBufferPos) ? + (this._cyclicBufferPos - delta) : + (this._cyclicBufferPos - delta + this._cyclicBufferSize)) << 1; + + var pby1 = this._bufferOffset + curMatch; + var len = Math.min(len0, len1); + if (this._bufferBase[pby1 + len] === this._bufferBase[cur + len]) { + while (++len !== lenLimit) { + if (this._bufferBase[pby1 + len] !== this._bufferBase[cur + len]) { + break; + } + } + if (maxLen < len) { + distances[offset++] = maxLen = len; + distances[offset++] = delta - 1; + if (len === lenLimit) { + this._son[ptr1] = this._son[cyclicPos]; + this._son[ptr0] = this._son[cyclicPos + 1]; + break; + } + } + } + if (this._bufferBase[pby1 + len] < this._bufferBase[cur + len]) { + this._son[ptr1] = curMatch; + ptr1 = cyclicPos + 1; + curMatch = this._son[ptr1]; + len1 = len; + } else { + this._son[ptr0] = curMatch; + ptr0 = cyclicPos; + curMatch = this._son[ptr0]; + len0 = len; + } + } + + this.movePos(); + return offset; + }; + + BinTree.prototype.skip = function (num) { + var lenLimit, matchMinPos, cur, curMatch, hashValue, hash2Value, hash3Value, temp; + var ptr0, ptr1, len0, len1, count, delta, cyclicPos, pby1, len; + do { + if (this._pos + this._matchMaxLen <= this._streamPos) { + lenLimit = this._matchMaxLen; + } else { + lenLimit = this._streamPos - this._pos; + if (lenLimit < this.kMinMatchCheck) { + this.movePos(); + continue; + } + } + + matchMinPos = this._pos > this._cyclicBufferSize ? (this._pos - this._cyclicBufferSize) : 0; + cur = this._bufferOffset + this._pos; + + if (this.HASH_ARRAY) { + temp = CrcTable[this._bufferBase[cur]] ^ this._bufferBase[cur + 1]; + hash2Value = temp & (kHash2Size - 1); + this._hash[hash2Value] = this._pos; + temp ^= this._bufferBase[cur + 2] << 8; + hash3Value = temp & (kHash3Size - 1); + this._hash[kHash3Offset + hash3Value] = this._pos; + hashValue = (temp ^ (CrcTable[this._bufferBase[cur + 3]] << 5)) & this._hashMask; + } else { + hashValue = this._bufferBase[cur] ^ (this._bufferBase[cur + 1] << 8); + } + + curMatch = this._hash[this.kFixHashSize + hashValue]; + this._hash[this.kFixHashSize + hashValue] = this._pos; + + ptr0 = (this._cyclicBufferPos << 1) + 1; + ptr1 = (this._cyclicBufferPos << 1); + + len0 = len1 = this.kNumHashDirectBytes; + + count = this._cutValue; + while (true) { + if (curMatch <= matchMinPos || count-- === 0) { + this._son[ptr0] = this._son[ptr1] = kEmptyHashValue; + break; + } + + delta = this._pos - curMatch; + cyclicPos = (delta <= this._cyclicBufferPos ? + (this._cyclicBufferPos - delta) : + (this._cyclicBufferPos - delta + this._cyclicBufferSize)) << 1; + + pby1 = this._bufferOffset + curMatch; + len = (len0 < len1) ? len0 : len1; + if (this._bufferBase[pby1 + len] === this._bufferBase[cur + len]) { + while (++len !== lenLimit) { + if (this._bufferBase[pby1 + len] !== this._bufferBase[cur + len]) { + break; + } + } + if (len === lenLimit) { + this._son[ptr1] = this._son[cyclicPos]; + this._son[ptr0] = this._son[cyclicPos + 1]; + break; + } + } + if (this._bufferBase[pby1 + len] < this._bufferBase[cur + len]) { + this._son[ptr1] = curMatch; + ptr1 = cyclicPos + 1; + curMatch = this._son[ptr1]; + len1 = len; + } else { + this._son[ptr0] = curMatch; + ptr0 = cyclicPos; + curMatch = this._son[ptr0]; + len0 = len; + } + } + this.movePos(); + } while (--num !== 0); + }; + + BinTree.prototype.normalizeLinks = function (items, numItems, subValue) { + var i, value; + for (i = 0; i < numItems; i++) { + value = items[i]; + if (value <= subValue) { + value = kEmptyHashValue; + } else { + value -= subValue; + } + items[i] = value; + } + }; + + BinTree.prototype.normalize = function () { + var subValue = this._pos - this._cyclicBufferSize; + this.normalizeLinks(this._son, this._cyclicBufferSize * 2, subValue); + this.normalizeLinks(this._hash, this._hashSizeSum, subValue); + this.reduceOffsets(subValue); + }; + + BinTree.prototype.setCutValue = function (cutValue) { + this._cutValue = cutValue; + }; + + return { + 'BinTree': BinTree, + 'InWindow': InWindow, + 'OutWindow': OutWindow + }; + +})(); + +var RangeCoder = (function () { + + + /* lib/RangeCoder/Encoder.js */ + + + // largest 32/24/16/8-bit numbers. + var MAX32 = 0xFFFFFFFF; + var MAX24 = 0x00FFFFFF; + var MAX16 = 0x0000FFFF; + var MAX8 = 0x000000FF; + var MASK24 = 0xFF000000; // 32-bit inverse of MAX24 + + var kNumBitModelTotalBits = 11; + var kBitModelTotal = (1 << kNumBitModelTotalBits); + var kNumMoveBits = 5; + + var kNumMoveReducingBits = 2; + var kNumBitPriceShiftBits = 6; + + var Encoder = function (stream) { + this.init(); + if (stream) { + this.setStream(stream); + } + }; + Encoder.prototype.setStream = function (stream) { + this._stream = stream; + }; + Encoder.prototype.releaseStream = function () { + this._stream = null; + }; + Encoder.prototype.init = function () { + // The cache/cache_size variables are used to properly handle + // carries, and represent a number defined by a big-endian sequence + // starting with the cache value, and followed by cache_size 0xff + // bytes, which has been shifted out of the low register, but hasn't + // been written yet, because it could be incremented by one due to a + // carry. + + // Note that the first byte output will always be 0 due to the fact + // that cache and low are initialized to 0, and the encoder + // implementation; the decoder ignores this byte. + this._position = 0; + this.low = 0; // unsigned 33-bit integer + this.range = MAX32; // unsigned 32-bit integer + // cacheSize needs to be large enough to store the full uncompressed + // size; javascript's 2^53 limit should be enough + this._cacheSize = 1; + this._cache = 0; // unsigned 8-bit + }; + Encoder.prototype.flushData = function () { + var i; + for (i = 0; i < 5; i++) { + this.shiftLow(); + } + }; + Encoder.prototype.flushStream = function () { + if (this._stream.flush) { + this._stream.flush(); + } + }; + Encoder.prototype.shiftLow = function () { + // "normalization" + var overflow = (this.low > MAX32) ? 1 : 0; + if (this.low < MASK24 || overflow) { + this._position += this._cacheSize; + var temp = this._cache; + do { + this._stream.writeByte((temp + overflow) & MAX8); + temp = MAX8; + } while (--this._cacheSize !== 0); + // set cache to bits 24-31 of 'low' + this._cache = this.low >>> 24; // this truncates correctly + // set cache_size to 0 (do/while loop did this) + } + this._cacheSize++; + // 'lowest 24 bits of low, shifted left by 8' + // careful, '<< 8' can flip sign of 'low' + this.low = (this.low & MAX24) * 256; + }; + + Encoder.prototype.encodeDirectBits = function (v, numTotalBits) { + var i, mask; + mask = 1 << (numTotalBits - 1); + for (i = numTotalBits - 1; i >= 0; i--, mask >>>= 1) { + this.range >>>= 1; // range is unsigned 32-bit int + if (v & mask) { + this.low += this.range; + } + if (this.range <= MAX24) { + this.range *= 256; // careful not to flip sign + this.shiftLow(); + } + } + }; + + Encoder.prototype.getProcessedSizeAdd = function () { + return this._cacheSize + this._position + 4; + }; + + Encoder.initBitModels = function (probs, len) { + var i; + if (len && !probs) { + if (typeof (Uint16Array) !== 'undefined') { + probs = new Uint16Array(len); + } else { + probs = []; + probs.length = len; + } + } + for (i = 0; i < probs.length; i++) + probs[i] = (kBitModelTotal >>> 1); // 0.5 probability + return probs; + }; + + Encoder.prototype.encode = function (probs, index, symbol) { + var prob = probs[index]; + var newBound = (this.range >>> kNumBitModelTotalBits) * prob; + if (symbol === 0) { + this.range = newBound; + probs[index] = prob + ((kBitModelTotal - prob) >>> kNumMoveBits); + } else { + this.low += newBound; + this.range -= newBound; + probs[index] = prob - (prob >>> kNumMoveBits); + } + if (this.range <= MAX24) { + this.range *= 256; // careful not to flip sign + this.shiftLow(); + } + }; + + var ProbPrices = []; + if (typeof (Uint32Array) !== 'undefined') { + ProbPrices = new Uint32Array(kBitModelTotal >>> kNumMoveReducingBits); + } + (function () { + var kNumBits = (kNumBitModelTotalBits - kNumMoveReducingBits); + var i, j; + for (i = kNumBits - 1; i >= 0; i--) { + var start = 1 << (kNumBits - i - 1); + var end = 1 << (kNumBits - i); + for (j = start; j < end; j++) { + ProbPrices[j] = (i << kNumBitPriceShiftBits) + + (((end - j) << kNumBitPriceShiftBits) >>> (kNumBits - i - 1)); + } + } + })(); + + Encoder.getPrice = function (prob, symbol) { + return ProbPrices[(((prob - symbol) ^ ((-symbol))) & (kBitModelTotal - 1)) >>> kNumMoveReducingBits]; + }; + Encoder.getPrice0 = function (prob) { + return ProbPrices[prob >>> kNumMoveReducingBits]; + }; + Encoder.getPrice1 = function (prob) { + return ProbPrices[(kBitModelTotal - prob) >>> kNumMoveReducingBits]; + }; + + // export constants for use in Encoder. + Encoder.kNumBitPriceShiftBits = kNumBitPriceShiftBits; + + + /* lib/RangeCoder/BitTreeDecoder.js */ + var BitTreeDecoder = function (numBitLevels) { + this._numBitLevels = numBitLevels; + this.init(); + }; + + BitTreeDecoder.prototype.init = function () { + this._models = Encoder.initBitModels(null, 1 << this._numBitLevels); + }; + + BitTreeDecoder.prototype.decode = function (rangeDecoder) { + var m = 1, + i = this._numBitLevels; + + while (i--) { + m = (m << 1) | rangeDecoder.decodeBit(this._models, m); + } + return m - (1 << this._numBitLevels); + }; + + BitTreeDecoder.prototype.reverseDecode = function (rangeDecoder) { + var m = 1, + symbol = 0, + i = 0, + bit; + + for (; i < this._numBitLevels; ++i) { + bit = rangeDecoder.decodeBit(this._models, m); + m = (m << 1) | bit; + symbol |= (bit << i); + } + return symbol; + }; + + BitTreeDecoder.reverseDecode = function (models, startIndex, rangeDecoder, + numBitLevels) { + var m = 1, + symbol = 0, + i = 0, + bit; + + for (; i < numBitLevels; ++i) { + bit = rangeDecoder.decodeBit(models, startIndex + m); + m = (m << 1) | bit; + symbol |= (bit << i); + } + return symbol; + }; + + + /* lib/RangeCoder/BitTreeEncoder.js */ + var BitTreeEncoder = function (numBitLevels) { + this._numBitLevels = numBitLevels; + this.init(); + }; + BitTreeEncoder.prototype.init = function () { + this._models = Encoder.initBitModels(null, 1 << this._numBitLevels); + }; + + BitTreeEncoder.prototype.encode = function (rangeEncoder, symbol) { + var m = 1, + bitIndex; + for (bitIndex = this._numBitLevels; bitIndex > 0;) { + bitIndex--; + var bit = (symbol >>> bitIndex) & 1; + rangeEncoder.encode(this._models, m, bit); + m = (m << 1) | bit; + } + }; + + BitTreeEncoder.prototype.reverseEncode = function (rangeEncoder, symbol) { + var m = 1, + i; + for (i = 0; i < this._numBitLevels; i++) { + var bit = symbol & 1; + rangeEncoder.encode(this._models, m, bit); + m = (m << 1) | bit; + symbol >>>= 1; + } + }; + + BitTreeEncoder.reverseEncode = function (models, startIndex, rangeEncoder, numBitLevels, symbol) { + var m = 1, + i; + for (i = 0; i < numBitLevels; i++) { + var bit = symbol & 1; + rangeEncoder.encode(models, startIndex + m, bit); + m = (m << 1) | bit; + symbol >>>= 1; + } + }; + + BitTreeEncoder.prototype.getPrice = function (symbol) { + var price = 0, + m = 1, + bitIndex; + for (bitIndex = this._numBitLevels; bitIndex > 0;) { + bitIndex--; + var bit = (symbol >>> bitIndex) & 1; + price += Encoder.getPrice(this._models[m], bit); + m = (m << 1) | bit; + } + return price; + }; + + BitTreeEncoder.prototype.reverseGetPrice = function (symbol) { + var price = 0, + m = 1, + bitIndex; + for (bitIndex = this._numBitLevels; bitIndex > 0; bitIndex--) { + var bit = (symbol & 1); + symbol >>>= 1; + price += Encoder.getPrice(this._models[m], bit); + m = (m << 1) | bit; + } + return price; + }; + + BitTreeEncoder.reverseGetPrice = function (models, startIndex, + numBitLevels, symbol) { + var price = 0, + m = 1, + bitIndex; + for (bitIndex = numBitLevels; bitIndex > 0; bitIndex--) { + var bit = (symbol & 1); + symbol >>>= 1; + price += Encoder.getPrice(models[startIndex + m], bit); + m = (m << 1) | bit; + } + return price; + }; + + /* lib/RangeCoder/Decoder.js */ + + var Decoder = function (stream) { + if (stream) { + this.setStream(stream); + this.init(); + } + }; + + Decoder.prototype.setStream = function (stream) { + this._stream = stream; + }; + + Decoder.prototype.releaseStream = function () { + this._stream = null; + }; + + Decoder.prototype.init = function () { + var i = 5; + + this._code = 0; + this._range = -1; + + while (i--) { + this._code = (this._code << 8) | this._stream.readByte(); + } + }; + + Decoder.prototype.decodeDirectBits = function (numTotalBits) { + var result = 0, + i = numTotalBits, + t; + + while (i--) { + this._range >>>= 1; + t = (this._code - this._range) >>> 31; + this._code -= this._range & (t - 1); + result = (result << 1) | (1 - t); + + if ((this._range & 0xff000000) === 0) { + this._code = (this._code << 8) | this._stream.readByte(); + this._range <<= 8; + } + } + + return result; + }; + + Decoder.prototype.decodeBit = function (probs, index) { + var prob = probs[index], + newBound = (this._range >>> 11) * prob; + + if ((this._code ^ 0x80000000) < (newBound ^ 0x80000000)) { + this._range = newBound; + probs[index] += (2048 - prob) >>> 5; + if ((this._range & 0xff000000) === 0) { + this._code = (this._code << 8) | this._stream.readByte(); + this._range <<= 8; + } + return 0; + } + + this._range -= newBound; + this._code -= newBound; + probs[index] -= prob >>> 5; + if ((this._range & 0xff000000) === 0) { + this._code = (this._code << 8) | this._stream.readByte(); + this._range <<= 8; + } + return 1; + }; + + return { + 'BitTreeDecoder': BitTreeDecoder, + 'BitTreeEncoder': BitTreeEncoder, + 'Decoder': Decoder, + 'Encoder': Encoder + }; +})(); + + +var LZMA = (function () { + + /* lib/LZMA/Base.js */ + + /* +Copyright (c) 2011 Juan Mellado + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. +*/ + + /* +References: +- "LZMA SDK" by Igor Pavlov + http://www.7-zip.org/sdk.html +*/ + /* Original source found at: http://code.google.com/p/js-lzma/ */ + + var Base = Object.create(null); + Base.kNumRepDistances = 4; + Base.kNumStates = 12; + + Base.stateInit = function () { + return 0; + }; + + Base.stateUpdateChar = function (index) { + if (index < 4) { + return 0; + } + if (index < 10) { + return index - 3; + } + return index - 6; + }; + + Base.stateUpdateMatch = function (index) { + return (index < 7 ? 7 : 10); + }; + + Base.stateUpdateRep = function (index) { + return (index < 7 ? 8 : 11); + }; + + Base.stateUpdateShortRep = function (index) { + return (index < 7 ? 9 : 11); + }; + + Base.stateIsCharState = function (index) { + return index < 7; + }; + + Base.kNumPosSlotBits = 6; + Base.kDicLogSizeMin = 0; + // Base.kDicLogSizeMax = 28; + // Base.kDistTableSizeMax = Base.kDicLogSizeMax * 2; + + Base.kNumLenToPosStatesBits = 2; // it's for speed optimization + Base.kNumLenToPosStates = 1 << Base.kNumLenToPosStatesBits; + + Base.kMatchMinLen = 2; + + Base.getLenToPosState = function (len) { + len -= Base.kMatchMinLen; + if (len < Base.kNumLenToPosStates) { + return len; + } + return (Base.kNumLenToPosStates - 1); + }; + + Base.kNumAlignBits = 4; + Base.kAlignTableSize = 1 << Base.kNumAlignBits; + Base.kAlignMask = (Base.kAlignTableSize - 1); + + Base.kStartPosModelIndex = 4; + Base.kEndPosModelIndex = 14; + Base.kNumPosModels = Base.kEndPosModelIndex - Base.kStartPosModelIndex; + + Base.kNumFullDistances = 1 << (Base.kEndPosModelIndex / 2); + + Base.kNumLitPosStatesBitsEncodingMax = 4; + Base.kNumLitContextBitsMax = 8; + + Base.kNumPosStatesBitsMax = 4; + Base.kNumPosStatesMax = (1 << Base.kNumPosStatesBitsMax); + Base.kNumPosStatesBitsEncodingMax = 4; + Base.kNumPosStatesEncodingMax = (1 << Base.kNumPosStatesBitsEncodingMax); + + Base.kNumLowLenBits = 3; + Base.kNumMidLenBits = 3; + Base.kNumHighLenBits = 8; + Base.kNumLowLenSymbols = 1 << Base.kNumLowLenBits; + Base.kNumMidLenSymbols = 1 << Base.kNumMidLenBits; + Base.kNumLenSymbols = Base.kNumLowLenSymbols + Base.kNumMidLenSymbols + + (1 << Base.kNumHighLenBits); + Base.kMatchMaxLen = Base.kMatchMinLen + Base.kNumLenSymbols - 1; + + /* lib/LZMA/Decoder.js */ + + var initBitModels = RangeCoder.Encoder.initBitModels; + + var LenDecoder = function () { + this._choice = initBitModels(null, 2); + this._lowCoder = []; + this._midCoder = []; + this._highCoder = new RangeCoder.BitTreeDecoder(8); + this._numPosStates = 0; + }; + + LenDecoder.prototype.create = function (numPosStates) { + for (; this._numPosStates < numPosStates; ++this._numPosStates) { + this._lowCoder[this._numPosStates] = new RangeCoder.BitTreeDecoder(3); + this._midCoder[this._numPosStates] = new RangeCoder.BitTreeDecoder(3); + } + }; + + LenDecoder.prototype.init = function () { + var i = this._numPosStates; + initBitModels(this._choice); + while (i--) { + this._lowCoder[i].init(); + this._midCoder[i].init(); + } + this._highCoder.init(); + }; + + LenDecoder.prototype.decode = function (rangeDecoder, posState) { + if (rangeDecoder.decodeBit(this._choice, 0) === 0) { + return this._lowCoder[posState].decode(rangeDecoder); + } + if (rangeDecoder.decodeBit(this._choice, 1) === 0) { + return 8 + this._midCoder[posState].decode(rangeDecoder); + } + return 16 + this._highCoder.decode(rangeDecoder); + }; + + var LiteralDecoder = function () {}; + + LiteralDecoder.Decoder2 = function () { + this._decoders = initBitModels(null, 0x300); + }; + + LiteralDecoder.Decoder2.prototype.init = function () { + initBitModels(this._decoders); + }; + + LiteralDecoder.Decoder2.prototype.decodeNormal = function (rangeDecoder) { + var symbol = 1; + + do { + symbol = (symbol << 1) | rangeDecoder.decodeBit(this._decoders, symbol); + } while (symbol < 0x100); + + return symbol & 0xff; + }; + + LiteralDecoder.Decoder2.prototype.decodeWithMatchByte = function (rangeDecoder, matchByte) { + var symbol = 1, + matchBit, bit; + + do { + matchBit = (matchByte >> 7) & 1; + matchByte <<= 1; + bit = rangeDecoder.decodeBit(this._decoders, ((1 + matchBit) << 8) + symbol); + symbol = (symbol << 1) | bit; + if (matchBit !== bit) { + while (symbol < 0x100) { + symbol = (symbol << 1) | rangeDecoder.decodeBit(this._decoders, symbol); + } + break; + } + } while (symbol < 0x100); + + return symbol & 0xff; + }; + + LiteralDecoder.prototype.create = function (numPosBits, numPrevBits) { + var i; + + if (this._coders && + (this._numPrevBits === numPrevBits) && + (this._numPosBits === numPosBits)) { + return; + } + this._numPosBits = numPosBits; + this._posMask = (1 << numPosBits) - 1; + this._numPrevBits = numPrevBits; + + this._coders = []; + + i = 1 << (this._numPrevBits + this._numPosBits); + while (i--) { + this._coders[i] = new LiteralDecoder.Decoder2(); + } + }; + + LiteralDecoder.prototype.init = function () { + var i = 1 << (this._numPrevBits + this._numPosBits); + while (i--) { + this._coders[i].init(); + } + }; + + LiteralDecoder.prototype.getDecoder = function (pos, prevByte) { + return this._coders[((pos & this._posMask) << this._numPrevBits) + + ((prevByte & 0xff) >>> (8 - this._numPrevBits))]; + }; + + var Decoder = function () { + var i; + this._outWindow = new LZ.OutWindow(); + this._rangeDecoder = new RangeCoder.Decoder(); + + this._isMatchDecoders = + initBitModels(null, Base.kNumStates << Base.kNumPosStatesBitsMax); + this._isRepDecoders = initBitModels(null, Base.kNumStates); + this._isRepG0Decoders = initBitModels(null, Base.kNumStates); + this._isRepG1Decoders = initBitModels(null, Base.kNumStates); + this._isRepG2Decoders = initBitModels(null, Base.kNumStates); + this._isRep0LongDecoders = + initBitModels(null, Base.kNumStates << Base.kNumPosStatesBitsMax); + this._posSlotDecoder = []; + this._posDecoders = initBitModels(null, Base.kNumFullDistances - Base.kEndPosModelIndex); + this._posAlignDecoder = new RangeCoder.BitTreeDecoder(Base.kNumAlignBits); + this._lenDecoder = new LenDecoder(); + this._repLenDecoder = new LenDecoder(); + this._literalDecoder = new LiteralDecoder(); + this._dictionarySize = -1; + this._dictionarySizeCheck = -1; + this._posStateMask = 0; + + for (i = 0; i < Base.kNumLenToPosStates; i++) { + this._posSlotDecoder[i] = new RangeCoder.BitTreeDecoder(Base.kNumPosSlotBits); + } + }; + + Decoder.prototype.setDictionarySize = function (dictionarySize) { + if (dictionarySize < 0) { + return false; + } + if (this._dictionarySize !== dictionarySize) { + this._dictionarySize = dictionarySize; + this._dictionarySizeCheck = Math.max(this._dictionarySize, 1); + this._outWindow.create(Math.max(this._dictionarySizeCheck, (1 << 12))); + } + return true; + }; + + Decoder.prototype.setLcLpPb = function (lc, lp, pb) { + var numPosStates = 1 << pb; + + if (lc > Base.kNumLitContextBitsMax || lp > 4 || pb > Base.kNumPosStatesBitsMax) { + return false; + } + + this._literalDecoder.create(lp, lc); + + this._lenDecoder.create(numPosStates); + this._repLenDecoder.create(numPosStates); + this._posStateMask = numPosStates - 1; + + return true; + }; + + Decoder.prototype.init = function () { + var i = Base.kNumLenToPosStates; + + this._outWindow.init(false); + + initBitModels(this._isMatchDecoders); + initBitModels(this._isRepDecoders); + initBitModels(this._isRepG0Decoders); + initBitModels(this._isRepG1Decoders); + initBitModels(this._isRepG2Decoders); + initBitModels(this._isRep0LongDecoders); + initBitModels(this._posDecoders); + + this._literalDecoder.init(); + + while (i--) { + this._posSlotDecoder[i].init(); + } + + this._lenDecoder.init(); + this._repLenDecoder.init(); + this._posAlignDecoder.init(); + this._rangeDecoder.init(); + }; + + Decoder.prototype.code = function (inStream, outStream, outSize) { + // note that nowPos64 is actually only 53 bits long; that sets a limit + // on the amount of data we can decode + var state, rep0 = 0, + rep1 = 0, + rep2 = 0, + rep3 = 0, + nowPos64 = 0, + prevByte = 0, + posState, decoder2, len, distance, posSlot, numDirectBits; + + this._rangeDecoder.setStream(inStream); + this._outWindow.setStream(outStream); + + this.init(); + + state = Base.stateInit(); + while (outSize < 0 || nowPos64 < outSize) { + posState = nowPos64 & this._posStateMask; + + if (this._rangeDecoder.decodeBit(this._isMatchDecoders, (state << Base.kNumPosStatesBitsMax) + posState) === 0) { + decoder2 = this._literalDecoder.getDecoder(nowPos64, prevByte); + + if (!Base.stateIsCharState(state)) { + prevByte = decoder2.decodeWithMatchByte(this._rangeDecoder, this._outWindow.getByte(rep0)); + } else { + prevByte = decoder2.decodeNormal(this._rangeDecoder); + } + this._outWindow.putByte(prevByte); + state = Base.stateUpdateChar(state); + nowPos64++; + + } else { + + if (this._rangeDecoder.decodeBit(this._isRepDecoders, state) === 1) { + len = 0; + if (this._rangeDecoder.decodeBit(this._isRepG0Decoders, state) === 0) { + if (this._rangeDecoder.decodeBit(this._isRep0LongDecoders, (state << Base.kNumPosStatesBitsMax) + posState) === 0) { + state = Base.stateUpdateShortRep(state); + len = 1; + } + } else { + if (this._rangeDecoder.decodeBit(this._isRepG1Decoders, state) === 0) { + distance = rep1; + } else { + if (this._rangeDecoder.decodeBit(this._isRepG2Decoders, state) === 0) { + distance = rep2; + } else { + distance = rep3; + rep3 = rep2; + } + rep2 = rep1; + } + rep1 = rep0; + rep0 = distance; + } + if (len === 0) { + len = Base.kMatchMinLen + this._repLenDecoder.decode(this._rangeDecoder, posState); + state = Base.stateUpdateRep(state); + } + } else { + rep3 = rep2; + rep2 = rep1; + rep1 = rep0; + + len = Base.kMatchMinLen + this._lenDecoder.decode(this._rangeDecoder, posState); + state = Base.stateUpdateMatch(state); + + posSlot = this._posSlotDecoder[Base.getLenToPosState(len)].decode(this._rangeDecoder); + if (posSlot >= Base.kStartPosModelIndex) { + + numDirectBits = (posSlot >> 1) - 1; + rep0 = (2 | (posSlot & 1)) << numDirectBits; + + if (posSlot < Base.kEndPosModelIndex) { + rep0 += RangeCoder.BitTreeDecoder.reverseDecode(this._posDecoders, + rep0 - posSlot - 1, this._rangeDecoder, numDirectBits); + } else { + rep0 += this._rangeDecoder.decodeDirectBits(numDirectBits - Base.kNumAlignBits) << Base.kNumAlignBits; + rep0 += this._posAlignDecoder.reverseDecode(this._rangeDecoder); + if (rep0 < 0) { + if (rep0 === -1) { + break; + } + return false; + } + } + } else { + rep0 = posSlot; + } + } + + if (rep0 >= nowPos64 || rep0 >= this._dictionarySizeCheck) { + return false; + } + + this._outWindow.copyBlock(rep0, len); + nowPos64 += len; + prevByte = this._outWindow.getByte(0); + } + } + + this._outWindow.flush(); + this._outWindow.releaseStream(); + this._rangeDecoder.releaseStream(); + + return true; + }; + + Decoder.prototype.setDecoderProperties = function (properties) { + var value, lc, lp, pb, dictionarySize, i, shift; + + if (properties.length < 5) { + return false; + } + + value = properties[0] & 0xFF; + lc = value % 9; + value = ~~ (value / 9); + lp = value % 5; + pb = ~~ (value / 5); + + if (!this.setLcLpPb(lc, lp, pb)) { + return false; + } + + dictionarySize = 0; + for (i = 0, shift = 1; i < 4; i++, shift *= 256) + dictionarySize += (properties[1 + i] & 0xFF) * shift; + + return this.setDictionarySize(dictionarySize); + }; + Decoder.prototype.setDecoderPropertiesFromStream = function (stream) { + var buffer = [], + i; + for (i = 0; i < 5; i++) { + buffer[i] = stream.readByte(); + } + return this.setDecoderProperties(buffer); + }; + + /* lib/LZMA/Encoder.js */ + + // shortcuts + var initBitModels = RangeCoder.Encoder.initBitModels; + + // constants + var EMatchFinderTypeBT2 = 0; + var EMatchFinderTypeBT4 = 1; + + var kInfinityPrice = 0xFFFFFFF; + var kDefaultDictionaryLogSize = 22; + var kNumFastBytesDefault = 0x20; + + var kNumOpts = 1 << 12; + + var kPropSize = 5; + + var g_FastPos = (function () { + var g_FastPos = makeBuffer(1 << 11); + var kFastSlots = 22; + var c = 2; + var slotFast; + g_FastPos[0] = 0; + g_FastPos[1] = 1; + for (slotFast = 2; slotFast < kFastSlots; slotFast++) { + var j, k = 1 << ((slotFast >> 1) - 1); + for (j = 0; j < k; j++, c++) { + g_FastPos[c] = slotFast; + } + } + return g_FastPos; + })(); + + var getPosSlot = function (pos) { + if (pos < (1 << 11)) { + return g_FastPos[pos]; + } + if (pos < (1 << 21)) { + return g_FastPos[pos >>> 10] + 20; + } + return g_FastPos[pos >>> 20] + 40; + }; + + var getPosSlot2 = function (pos) { + if (pos < (1 << 17)) { + return g_FastPos[pos >>> 6] + 12; + } + if (pos < (1 << 27)) { + return g_FastPos[pos >>> 16] + 32; + } + return g_FastPos[pos >>> 26] + 52; + }; + + var Encoder = function () { + var i; + this._state = Base.stateInit(); + this._previousByte = 0; + this._repDistances = []; // XXX use Uint32Array? + this._repDistances.length = Base.kNumRepDistances; + + // these fields are defined much lower in the original Java source file + this._optimum = []; + this._matchFinder = null; + this._rangeEncoder = new RangeCoder.Encoder(); + + this._isMatch = initBitModels(null, Base.kNumStates << Base.kNumPosStatesBitsMax); + this._isRep = initBitModels(null, Base.kNumStates); + this._isRepG0 = initBitModels(null, Base.kNumStates); + this._isRepG1 = initBitModels(null, Base.kNumStates); + this._isRepG2 = initBitModels(null, Base.kNumStates); + this._isRep0Long = initBitModels(null, Base.kNumStates << Base.kNumPosStatesBitsMax); + + this._posSlotEncoder = []; + + this._posEncoders = initBitModels(null, Base.kNumFullDistances - Base.kEndPosModelIndex); + + this._posAlignEncoder = new RangeCoder.BitTreeEncoder(Base.kNumAlignBits); + + this._lenEncoder = new Encoder.LenPriceTableEncoder(); + this._repMatchLenEncoder = new Encoder.LenPriceTableEncoder(); + + this._literalEncoder = new Encoder.LiteralEncoder(); + + this._matchDistances = []; + this._matchDistances.length = Base.kMatchMaxLen * 2 + 2; + for (i = 0; i < this._matchDistances.length; i++) { + this._matchDistances[i] = 0; + } + + this._numFastBytes = kNumFastBytesDefault; + this._longestMatchLength = 0; + this._numDistancePairs = 0; + + this._additionalOffset = 0; + this._optimumEndIndex = 0; + this._optimumCurrentIndex = 0; + + this._longestMatchWasFound = false; + + this._posSlotPrices = []; + this._distancesPrices = []; + this._alignPrices = []; + this._alignPriceCount = 0; + + this._distTableSize = kDefaultDictionaryLogSize * 2; + + this._posStateBits = 2; + this._posStateMask = 4 - 1; + this._numLiteralPosStateBits = 0; + this._numLiteralContextBits = 3; + + this._dictionarySize = (1 << kDefaultDictionaryLogSize); + this._dictionarySizePrev = 0xFFFFFFFF; + this._numFastBytesPrev = 0xFFFFFFFF; + + // note that this is a 53-bit variable, not 64-bit. This sets the maximum + // encoded file size. + this.nowPos64 = 0; + this._finished = false; + this._inStream = null; + + this._matchFinderType = EMatchFinderTypeBT4; + this._writeEndMark = false; + this._needReleaseMFStream = false; + + // ...and even further down we find these: + this.reps = []; + this.repLens = []; + this.backRes = 0; + + // ...keep going, eventually we find the constructor: + for (i = 0; i < kNumOpts; i++) { + this._optimum[i] = new Encoder.Optimal(); + } + for (i = 0; i < Base.kNumLenToPosStates; i++) { + this._posSlotEncoder[i] = new RangeCoder.BitTreeEncoder(Base.kNumPosSlotBits); + } + + this._matchPriceCount = 0; + + // ...and just above the 'Code' method, we find: + this.processedInSize = [0]; + this.processedOutSize = [0]; + this.finished = [false]; + }; + Encoder.prototype.baseInit = function () { + var i; + this._state = Base.stateInit(); + this._previousByte = 0; + for (i = 0; i < Base.kNumRepDistances; i++) { + this._repDistances[i] = 0; + } + }; + + var LiteralEncoder = Encoder.LiteralEncoder = function () { + this._coders = null; + this._numPrevBits = -1; + this._numPosBits = -1; + this._posMask = 0; + }; + + LiteralEncoder.Encoder2 = function () { + this._encoders = initBitModels(null, 0x300); + }; + + LiteralEncoder.Encoder2.prototype.init = function () { + initBitModels(this._encoders); + }; + + LiteralEncoder.Encoder2.prototype.encode = function (rangeEncoder, symbol) { + var context = 1, + i; + for (i = 7; i >= 0; i--) { + var bit = (symbol >>> i) & 1; + rangeEncoder.encode(this._encoders, context, bit); + context = (context << 1) | bit; + } + }; + + LiteralEncoder.Encoder2.prototype.encodeMatched = function (rangeEncoder, matchByte, symbol) { + var context = 1, + same = true, + i; + for (i = 7; i >= 0; i--) { + var bit = (symbol >> i) & 1; + var state = context; + if (same) { + var matchBit = (matchByte >>> i) & 1; + state += (1 + matchBit) << 8; + same = (matchBit === bit); + } + rangeEncoder.encode(this._encoders, state, bit); + context = (context << 1) | bit; + } + }; + + LiteralEncoder.Encoder2.prototype.getPrice = function (matchMode, matchByte, symbol) { + var price = 0; + var context = 1; + var i = 7; + var bit, matchBit; + if (matchMode) { + for (; i >= 0; i--) { + matchBit = (matchByte >>> i) & 1; + bit = (symbol >>> i) & 1; + price += RangeCoder.Encoder.getPrice(this._encoders[((1 + matchBit) << 8) + context], bit); + context = (context << 1) | bit; + if (matchBit !== bit) { + i--; + break; + } + } + } + for (; i >= 0; i--) { + bit = (symbol >>> i) & 1; + price += RangeCoder.Encoder.getPrice(this._encoders[context], bit); + context = (context << 1) | bit; + } + return price; + }; + + LiteralEncoder.prototype.create = function (numPosBits, numPrevBits) { + var i; + if (this._coders && + this._numPrevBits === numPrevBits && + this._numPosBits === numPosBits) { + return; + } + + this._numPosBits = numPosBits; + this._posMask = (1 << numPosBits) - 1; + this._numPrevBits = numPrevBits; + var numStates = 1 << (this._numPrevBits + this._numPosBits); + this._coders = []; + for (i = 0; i < numStates; i++) { + this._coders[i] = new LiteralEncoder.Encoder2(); + } + }; + + LiteralEncoder.prototype.init = function () { + var numStates = 1 << (this._numPrevBits + this._numPosBits), + i; + for (i = 0; i < numStates; i++) { + this._coders[i].init(); + } + }; + + LiteralEncoder.prototype.getSubCoder = function (pos, prevByte) { + return this._coders[((pos & this._posMask) << this._numPrevBits) + (prevByte >> (8 - this._numPrevBits))]; + }; + + var LenEncoder = Encoder.LenEncoder = function () { + var posState; + this._choice = initBitModels(null, 2); + this._lowCoder = []; + this._midCoder = []; + this._highCoder = new RangeCoder.BitTreeEncoder(Base.kNumHighLenBits); + + for (posState = 0; posState < Base.kNumPosStatesEncodingMax; posState++) { + this._lowCoder[posState] = new RangeCoder.BitTreeEncoder(Base.kNumLowLenBits); + this._midCoder[posState] = new RangeCoder.BitTreeEncoder(Base.kNumMidLenBits); + } + }; + + LenEncoder.prototype.init = function (numPosStates) { + var posState; + initBitModels(this._choice); + for (posState = 0; posState < numPosStates; posState++) { + this._lowCoder[posState].init(); + this._midCoder[posState].init(); + } + this._highCoder.init(); + }; + + LenEncoder.prototype.encode = function (rangeEncoder, symbol, posState) { + if (symbol < Base.kNumLowLenSymbols) { + rangeEncoder.encode(this._choice, 0, 0); + this._lowCoder[posState].encode(rangeEncoder, symbol); + } else { + symbol -= Base.kNumLowLenSymbols; + rangeEncoder.encode(this._choice, 0, 1); + if (symbol < Base.kNumMidLenSymbols) { + rangeEncoder.encode(this._choice, 1, 0); + this._midCoder[posState].encode(rangeEncoder, symbol); + } else { + rangeEncoder.encode(this._choice, 1, 1); + this._highCoder.encode(rangeEncoder, symbol - Base.kNumMidLenSymbols); + } + } + }; + + LenEncoder.prototype.setPrices = function (posState, numSymbols, prices, st) { + var a0 = RangeCoder.Encoder.getPrice0(this._choice[0]); + var a1 = RangeCoder.Encoder.getPrice1(this._choice[0]); + var b0 = a1 + RangeCoder.Encoder.getPrice0(this._choice[1]); + var b1 = a1 + RangeCoder.Encoder.getPrice1(this._choice[1]); + var i; + for (i = 0; i < Base.kNumLowLenSymbols; i++) { + if (i >= numSymbols) { + return; + } + prices[st + i] = a0 + this._lowCoder[posState].getPrice(i); + } + for (; i < Base.kNumLowLenSymbols + Base.kNumMidLenSymbols; i++) { + if (i >= numSymbols) { + return; + } + prices[st + i] = b0 + this._midCoder[posState].getPrice(i - Base.kNumLowLenSymbols); + } + for (; i < numSymbols; i++) { + prices[st + i] = b1 + this._highCoder.getPrice(i - Base.kNumLowLenSymbols - Base.kNumMidLenSymbols); + } + }; + + var kNumLenSpecSymbols = Base.kNumLowLenSymbols + Base.kNumMidLenSymbols; + + var LenPriceTableEncoder = Encoder.LenPriceTableEncoder = function () { + LenEncoder.call(this); // superclass constructor + this._prices = []; + this._counters = []; + this._tableSize = 0; + }; + LenPriceTableEncoder.prototype = Object.create(LenEncoder.prototype); + LenPriceTableEncoder.prototype.setTableSize = function (tableSize) { + this._tableSize = tableSize; + }; + LenPriceTableEncoder.prototype.getPrice = function (symbol, posState) { + return this._prices[posState * Base.kNumLenSymbols + symbol]; + }; + LenPriceTableEncoder.prototype.updateTable = function (posState) { + this.setPrices(posState, this._tableSize, this._prices, + posState * Base.kNumLenSymbols); + this._counters[posState] = this._tableSize; + }; + LenPriceTableEncoder.prototype.updateTables = function (numPosStates) { + var posState; + for (posState = 0; posState < numPosStates; posState++) { + this.updateTable(posState); + } + }; + LenPriceTableEncoder.prototype.encode = (function (superEncode) { + return function (rangeEncoder, symbol, posState) { + superEncode.call(this, rangeEncoder, symbol, posState); + if (--this._counters[posState] === 0) { + this.updateTable(posState); + } + }; + })(LenPriceTableEncoder.prototype.encode); + + var Optimal = Encoder.Optimal = function () { + this.state = 0; + + this.prev1IsChar = false; + this.prev2 = false; + + this.posPrev2 = 0; + this.backPrev2 = 0; + + this.price = 0; + this.posPrev = 0; + this.backPrev = 0; + + this.backs0 = 0; + this.backs1 = 0; + this.backs2 = 0; + this.backs3 = 0; + }; + Optimal.prototype.makeAsChar = function () { + this.backPrev = -1; + this.prev1IsChar = false; + }; + Optimal.prototype.makeAsShortRep = function () { + this.backPrev = 0; + this.prev1IsChar = false; + }; + Optimal.prototype.isShortRep = function () { + return this.backPrev === 0; + }; + + // back to the Encoder class! + Encoder.prototype.create = function () { + var numHashBytes; + if (!this._matchFinder) { + var bt = new LZ.BinTree(); + numHashBytes = 4; + if (this._matchFinderType === EMatchFinderTypeBT2) { + numHashBytes = 2; + } + bt.setType(numHashBytes); + this._matchFinder = bt; + } + this._literalEncoder.create(this._numLiteralPosStateBits, + this._numLiteralContextBits); + + if (this._dictionarySize === this._dictionarySizePrev && + this._numFastBytesPrev === this._numFastBytes) { + return; + } + this._matchFinder.create(this._dictionarySize, kNumOpts, this._numFastBytes, + Base.kMatchMaxLen + 1); + this._dictionarySizePrev = this._dictionarySize; + this._numFastBytesPrev = this._numFastBytes; + }; + + Encoder.prototype.setWriteEndMarkerMode = function (writeEndMarker) { + this._writeEndMark = writeEndMarker; + }; + + Encoder.prototype.init = function () { + var i; + this.baseInit(); + this._rangeEncoder.init(); + + initBitModels(this._isMatch); + initBitModels(this._isRep0Long); + initBitModels(this._isRep); + initBitModels(this._isRepG0); + initBitModels(this._isRepG1); + initBitModels(this._isRepG2); + initBitModels(this._posEncoders); + + this._literalEncoder.init(); + for (i = 0; i < Base.kNumLenToPosStates; i++) { + this._posSlotEncoder[i].init(); + } + + this._lenEncoder.init(1 << this._posStateBits); + this._repMatchLenEncoder.init(1 << this._posStateBits); + + this._posAlignEncoder.init(); + + this._longestMatchWasFound = false; + this._optimumEndIndex = 0; + this._optimumCurrentIndex = 0; + this._additionalOffset = 0; + }; + + Encoder.prototype.readMatchDistances = function () { + var lenRes = 0; + this._numDistancePairs = this._matchFinder.getMatches(this._matchDistances); + if (this._numDistancePairs > 0) { + lenRes = this._matchDistances[this._numDistancePairs - 2]; + if (lenRes === this._numFastBytes) { + lenRes += this._matchFinder.getMatchLen(lenRes - 1, this._matchDistances[this._numDistancePairs - 1], Base.kMatchMaxLen - lenRes); + } + } + this._additionalOffset++; + // [csa] Gary Linscott thinks that numDistancePairs should be a retval here. + return lenRes; + }; + + Encoder.prototype.movePos = function (num) { + if (num > 0) { + this._matchFinder.skip(num); + this._additionalOffset += num; + } + }; + + Encoder.prototype.getRepLen1Price = function (state, posState) { + return RangeCoder.Encoder.getPrice0(this._isRepG0[state]) + + RangeCoder.Encoder.getPrice0(this._isRep0Long[(state << Base.kNumPosStatesBitsMax) + posState]); + }; + + Encoder.prototype.getPureRepPrice = function (repIndex, state, posState) { + var price; + if (repIndex === 0) { + price = RangeCoder.Encoder.getPrice0(this._isRepG0[state]); + price += RangeCoder.Encoder.getPrice1(this._isRep0Long[(state << Base.kNumPosStatesBitsMax) + posState]); + } else { + price = RangeCoder.Encoder.getPrice1(this._isRepG0[state]); + if (repIndex === 1) { + price += RangeCoder.Encoder.getPrice0(this._isRepG1[state]); + } else { + price += RangeCoder.Encoder.getPrice1(this._isRepG1[state]); + price += RangeCoder.Encoder.getPrice(this._isRepG2[state], repIndex - 2); + } + } + return price; + }; + + Encoder.prototype.getRepPrice = function (repIndex, len, state, posState) { + var price = this._repMatchLenEncoder.getPrice(len - Base.kMatchMinLen, posState); + return price + this.getPureRepPrice(repIndex, state, posState); + }; + + Encoder.prototype.getPosLenPrice = function (pos, len, posState) { + var price; + var lenToPosState = Base.getLenToPosState(len); + if (pos < Base.kNumFullDistances) { + price = this._distancesPrices[(lenToPosState * Base.kNumFullDistances) + pos]; + } else { + price = this._posSlotPrices[(lenToPosState << Base.kNumPosSlotBits) + getPosSlot2(pos)] + this._alignPrices[pos & Base.kAlignMask]; + } + return price + this._lenEncoder.getPrice(len - Base.kMatchMinLen, posState); + }; + + Encoder.prototype.backward = function (cur) { + this._optimumEndIndex = cur; + var posMem = this._optimum[cur].posPrev; + var backMem = this._optimum[cur].backPrev; + do { + if (this._optimum[cur].prev1IsChar) { + this._optimum[posMem].makeAsChar(); + this._optimum[posMem].posPrev = posMem - 1; + if (this._optimum[cur].prev2) { + this._optimum[posMem - 1].prev1IsChar = false; + this._optimum[posMem - 1].posPrev = this._optimum[cur].posPrev2; + this._optimum[posMem - 1].backPrev = this._optimum[cur].backPrev2; + } + } + var posPrev = posMem; + var backCur = backMem; + + backMem = this._optimum[posPrev].backPrev; + posMem = this._optimum[posPrev].posPrev; + + this._optimum[posPrev].backPrev = backCur; + this._optimum[posPrev].posPrev = cur; + cur = posPrev; + } while (cur > 0); + + this.backRes = this._optimum[0].backPrev; + this._optimumCurrentIndex = this._optimum[0].posPrev; + // [csa] Gary Linscott thinks that backRes should be a retval here. + return this._optimumCurrentIndex; + }; + + Encoder.prototype.getOptimum = function (position) { + + if (this._optimumEndIndex !== this._optimumCurrentIndex) { + var lenRes = this._optimum[this._optimumCurrentIndex].posPrev - this._optimumCurrentIndex; + this.backRes = this._optimum[this._optimumCurrentIndex].backPrev; + this._optimumCurrentIndex = this._optimum[this._optimumCurrentIndex].posPrev; + // [csa] Gary Linscott thinks that backRes should be a retval here. + return lenRes; + } + this._optimumCurrentIndex = this._optimumEndIndex = 0; + + var lenMain; + if (!this._longestMatchWasFound) { + lenMain = this.readMatchDistances(); + } else { + lenMain = this._longestMatchLength; + this._longestMatchWasFound = false; + } + var numDistancePairs = this._numDistancePairs; + + var numAvailableBytes = this._matchFinder.getNumAvailableBytes() + 1; + if (numAvailableBytes < 2) { + this.backRes = -1; + // [csa] Gary Linscott thinks that backRes should be a retval here. + return 1; + } + if (numAvailableBytes > Base.kMatchMaxLen) { + numAvailableBytes = Base.kMatchMaxLen; + } + + var repMaxIndex = 0, + i; + for (i = 0; i < Base.kNumRepDistances; i++) { + this.reps[i] = this._repDistances[i]; + this.repLens[i] = this._matchFinder.getMatchLen(-1, this.reps[i], Base.kMatchMaxLen); + if (this.repLens[i] > this.repLens[repMaxIndex]) { + repMaxIndex = i; + } + } + if (this.repLens[repMaxIndex] >= this._numFastBytes) { + this.backRes = repMaxIndex; + var lenRes2 = this.repLens[repMaxIndex]; + this.movePos(lenRes2 - 1); + // [csa] Gary Linscott thinks that backRes should be a retval here. + return lenRes2; + } + + if (lenMain >= this._numFastBytes) { + // [csa] Gary Linscott thinks that backRes should be a retval here. + this.backRes = this._matchDistances[numDistancePairs - 1] + Base.kNumRepDistances; + this.movePos(lenMain - 1); + return lenMain; + } + + var currentByte = this._matchFinder.getIndexByte(-1); + var matchByte = this._matchFinder.getIndexByte(-this._repDistances[0] - 2); + + if (lenMain < 2 && currentByte !== matchByte && this.repLens[repMaxIndex] < 2) { + // [csa] Gary Linscott thinks that backRes should be a retval here. + this.backRes = -1; + return 1; + } + + this._optimum[0].state = this._state; + + var posState = position & this._posStateMask; + + this._optimum[1].price = RangeCoder.Encoder.getPrice0(this._isMatch[(this._state << Base.kNumPosStatesBitsMax) + posState]) + + this._literalEncoder.getSubCoder(position, this._previousByte).getPrice(!Base.stateIsCharState(this._state), matchByte, currentByte); + this._optimum[1].makeAsChar(); + + var matchPrice = RangeCoder.Encoder.getPrice1(this._isMatch[(this._state << Base.kNumPosStatesBitsMax) + posState]); + var repMatchPrice = matchPrice + RangeCoder.Encoder.getPrice1(this._isRep[this._state]); + + if (matchByte === currentByte) { + var shortRepPrice = repMatchPrice + this.getRepLen1Price(this._state, posState); + if (shortRepPrice < this._optimum[1].price) { + this._optimum[1].price = shortRepPrice; + this._optimum[1].makeAsShortRep(); + } + } + + var lenEnd = (lenMain >= this.repLens[repMaxIndex]) ? + lenMain : this.repLens[repMaxIndex]; + if (lenEnd < 2) { + // [csa] Gary Linscott thinks that backRes should be a retval here. + this.backRes = this._optimum[1].backPrev; + return 1; + } + + this._optimum[1].posPrev = 0; + + this._optimum[0].backs0 = this.reps[0]; + this._optimum[0].backs1 = this.reps[1]; + this._optimum[0].backs2 = this.reps[2]; + this._optimum[0].backs3 = this.reps[3]; + + var len = lenEnd; + do { + this._optimum[len--].price = kInfinityPrice; + } while (len >= 2); + + for (i = 0; i < Base.kNumRepDistances; i++) { + var repLen = this.repLens[i]; + if (repLen < 2) { + continue; + } + var price = repMatchPrice + this.getPureRepPrice(i, this._state, posState); + do { + var curAndLenPrice = price + + this._repMatchLenEncoder.getPrice(repLen - 2, posState); + var optimum = this._optimum[repLen]; + if (curAndLenPrice < optimum.price) { + optimum.price = curAndLenPrice; + optimum.posPrev = 0; + optimum.backPrev = i; + optimum.prev1IsChar = false; + } + } while (--repLen >= 2); + } + + var normalMatchPrice = matchPrice + + RangeCoder.Encoder.getPrice0(this._isRep[this._state]); + + len = this.repLens[0] >= 2 ? this.repLens[0] + 1 : 2; + if (len <= lenMain) { + var offs = 0; + while (len > this._matchDistances[offs]) { + offs += 2; + } + for (;; len++) { + var distance = this._matchDistances[offs + 1]; + var curAndLenPrice2 = normalMatchPrice + + this.getPosLenPrice(distance, len, posState); + var optimum2 = this._optimum[len]; + if (curAndLenPrice2 < optimum2.price) { + optimum2.price = curAndLenPrice2; + optimum2.posPrev = 0; + optimum2.backPrev = distance + Base.kNumRepDistances; + optimum2.prev1IsChar = false; + } + if (len === this._matchDistances[offs]) { + offs += 2; + if (offs === numDistancePairs) { + break; + } + } + } + } + + var cur = 0; + while (true) { + cur++; + if (cur === lenEnd) { + return this.backward(cur); + } + var newLen = this.readMatchDistances(); + numDistancePairs = this._numDistancePairs; + if (newLen >= this._numFastBytes) { + this._longestMatchLength = newLen; + this._longestMatchWasFound = true; + return this.backward(cur); + } + position++; + var posPrev = this._optimum[cur].posPrev; + var state; + if (this._optimum[cur].prev1IsChar) { + posPrev--; + if (this._optimum[cur].prev2) { + state = this._optimum[this._optimum[cur].posPrev2].state; + if (this._optimum[cur].backPrev2 < Base.kNumRepDistances) { + state = Base.stateUpdateRep(state); + } else { + state = Base.stateUpdateMatch(state); + } + } else { + state = this._optimum[posPrev].state; + } + state = Base.stateUpdateChar(state); + } else { + state = this._optimum[posPrev].state; + } + if (posPrev === cur - 1) { + if (this._optimum[cur].isShortRep()) { + state = Base.stateUpdateShortRep(state); + } else { + state = Base.stateUpdateChar(state); + } + } else { + var pos; + if (this._optimum[cur].prev1IsChar && this._optimum[cur].prev2) { + posPrev = this._optimum[cur].posPrev2; + pos = this._optimum[cur].backPrev2; + state = Base.stateUpdateRep(state); + } else { + pos = this._optimum[cur].backPrev; + if (pos < Base.kNumRepDistances) { + state = Base.stateUpdateRep(state); + } else { + state = Base.stateUpdateMatch(state); + } + } + + var opt = this._optimum[posPrev]; + if (pos < Base.kNumRepDistances) { + if (pos === 0) { + this.reps[0] = opt.backs0; + this.reps[1] = opt.backs1; + this.reps[2] = opt.backs2; + this.reps[3] = opt.backs3; + } else if (pos === 1) { + this.reps[0] = opt.backs1; + this.reps[1] = opt.backs0; + this.reps[2] = opt.backs2; + this.reps[3] = opt.backs3; + } else if (pos === 2) { + this.reps[0] = opt.backs2; + this.reps[1] = opt.backs0; + this.reps[2] = opt.backs1; + this.reps[3] = opt.backs3; + } else { + this.reps[0] = opt.backs3; + this.reps[1] = opt.backs0; + this.reps[2] = opt.backs1; + this.reps[3] = opt.backs2; + } + } else { + this.reps[0] = pos - Base.kNumRepDistances; + this.reps[1] = opt.backs0; + this.reps[2] = opt.backs1; + this.reps[3] = opt.backs2; + } + } + this._optimum[cur].state = state; + this._optimum[cur].backs0 = this.reps[0]; + this._optimum[cur].backs1 = this.reps[1]; + this._optimum[cur].backs2 = this.reps[2]; + this._optimum[cur].backs3 = this.reps[3]; + var curPrice = this._optimum[cur].price; + + currentByte = this._matchFinder.getIndexByte(-1); + matchByte = this._matchFinder.getIndexByte(-this.reps[0] - 2); + posState = position & this._posStateMask; + + var curAnd1Price = curPrice + + RangeCoder.Encoder.getPrice0(this._isMatch[(state << Base.kNumPosStatesBitsMax) + posState]) + + this._literalEncoder.getSubCoder(position, this._matchFinder.getIndexByte(-2)). + getPrice(!Base.stateIsCharState(state), matchByte, currentByte); + + var nextOptimum = this._optimum[cur + 1]; + + var nextIsChar = false; + if (curAnd1Price < nextOptimum.price) { + nextOptimum.price = curAnd1Price; + nextOptimum.posPrev = cur; + nextOptimum.makeAsChar(); + nextIsChar = true; + } + + matchPrice = curPrice + RangeCoder.Encoder.getPrice1(this._isMatch[(state << Base.kNumPosStatesBitsMax) + posState]); + repMatchPrice = matchPrice + RangeCoder.Encoder.getPrice1(this._isRep[state]); + + if (matchByte === currentByte && + !(nextOptimum.posPrev < cur && nextOptimum.backPrev === 0)) { + var shortRepPrice2 = + repMatchPrice + this.getRepLen1Price(state, posState); + if (shortRepPrice2 <= nextOptimum.price) { + nextOptimum.price = shortRepPrice2; + nextOptimum.posPrev = cur; + nextOptimum.makeAsShortRep(); + nextIsChar = true; + } + } + + var numAvailableBytesFull = this._matchFinder.getNumAvailableBytes() + 1; + numAvailableBytesFull = Math.min(kNumOpts - 1 - cur, numAvailableBytesFull); + numAvailableBytes = numAvailableBytesFull; + + if (numAvailableBytes < 2) { + continue; + } + if (numAvailableBytes > this._numFastBytes) { + numAvailableBytes = this._numFastBytes; + } + if (!nextIsChar && matchByte !== currentByte) { + // Try Literal + rep0 + var t = Math.min(numAvailableBytesFull - 1, this._numFastBytes); + var lenTest2 = this._matchFinder.getMatchLen(0, this.reps[0], t); + if (lenTest2 >= 2) { + var state2 = Base.stateUpdateChar(state); + + var posStateNext = (position + 1) & this._posStateMask; + var nextRepMatchPrice = curAnd1Price + + RangeCoder.Encoder.getPrice1(this._isMatch[(state2 << Base.kNumPosStatesBitsMax) + posStateNext]) + + RangeCoder.Encoder.getPrice1(this._isRep[state2]); + + var offset = cur + 1 + lenTest2; + while (lenEnd < offset) { + this._optimum[++lenEnd].price = kInfinityPrice; + } + var curAndLenPrice3 = nextRepMatchPrice + this.getRepPrice(0, lenTest2, state2, posStateNext); + var optimum3 = this._optimum[offset]; + if (curAndLenPrice3 < optimum3.price) { + optimum3.price = curAndLenPrice3; + optimum3.posPrev = cur + 1; + optimum3.backPrev = 0; + optimum3.prev1IsChar = true; + optimum3.prev2 = false; + } + } + } + + var startLen = 2; // Speed optimization + + var repIndex; + for (repIndex = 0; repIndex < Base.kNumRepDistances; repIndex++) { + var lenTest = this._matchFinder.getMatchLen(-1, this.reps[repIndex], numAvailableBytes); + if (lenTest < 2) { + continue; + } + var lenTestTemp = lenTest; + do { + while (lenEnd < cur + lenTest) { + this._optimum[++lenEnd].price = kInfinityPrice; + } + var curAndLenPrice4 = repMatchPrice + this.getRepPrice(repIndex, lenTest, state, posState); + var optimum4 = this._optimum[cur + lenTest]; + if (curAndLenPrice4 < optimum4.price) { + optimum4.price = curAndLenPrice4; + optimum4.posPrev = cur; + optimum4.backPrev = repIndex; + optimum4.prev1IsChar = false; + } + } while (--lenTest >= 2); + lenTest = lenTestTemp; + + if (repIndex === 0) { + startLen = lenTest + 1; + } + + // if (_maxMode) + if (lenTest < numAvailableBytesFull) { + var t5 = Math.min(numAvailableBytesFull - 1 - lenTest, this._numFastBytes); + var lenTest25 = this._matchFinder.getMatchLen(lenTest, this.reps[repIndex], t5); + if (lenTest25 >= 2) { + var state25 = Base.stateUpdateRep(state); + var posStateNext5 = (position + lenTest) & this._posStateMask; + var curAndLenCharPrice = repMatchPrice + + this.getRepPrice(repIndex, lenTest, state, posState) + + RangeCoder.Encoder.getPrice0(this._isMatch[(state25 << Base.kNumPosStatesBitsMax) + posStateNext5]) + + this._literalEncoder.getSubCoder(position + lenTest, + this._matchFinder.getIndexByte(lenTest - 2)). + getPrice(true, + this._matchFinder.getIndexByte(lenTest - 1 - (this.reps[repIndex] + 1)), + this._matchFinder.getIndexByte(lenTest - 1)); + + state25 = Base.stateUpdateChar(state25); + posStateNext5 = (position + lenTest + 1) & this._posStateMask; + var nextMatchPrice5 = curAndLenCharPrice + + RangeCoder.Encoder.getPrice1(this._isMatch[(state25 << Base.kNumPosStatesBitsMax) + posStateNext5]); + var nextRepMatchPrice5 = nextMatchPrice5 + + RangeCoder.Encoder.getPrice1(this._isRep[state25]); + + // for(; lenTest2 >= 2; lenTest2--) { + var offset5 = lenTest + 1 + lenTest25; + while (lenEnd < cur + offset5) { + this._optimum[++lenEnd].price = kInfinityPrice; + } + var curAndLenPrice5 = nextRepMatchPrice5 + + this.getRepPrice(0, lenTest25, state25, posStateNext5); + var optimum5 = this._optimum[cur + offset5]; + if (curAndLenPrice5 < optimum5.price) { + optimum5.price = curAndLenPrice5; + optimum5.posPrev = cur + lenTest + 1; + optimum5.backPrev = 0; + optimum5.prev1IsChar = true; + optimum5.prev2 = true; + optimum5.posPrev2 = cur; + optimum5.backPrev2 = repIndex; + } + } + } + } + + if (newLen > numAvailableBytes) { + newLen = numAvailableBytes; + numDistancePairs = 0; + while (newLen > this._matchDistances[numDistancePairs]) { + numDistancePairs += 2; + } + this._matchDistances[numDistancePairs] = newLen; + numDistancePairs += 2; + } + if (newLen >= startLen) { + normalMatchPrice = matchPrice + + RangeCoder.Encoder.getPrice0(this._isRep[state]); + while (lenEnd < cur + newLen) { + this._optimum[++lenEnd].price = kInfinityPrice; + } + + var offs6 = 0; + while (startLen > this._matchDistances[offs6]) { + offs6 += 2; + } + + var lenTest6; + for (lenTest6 = startLen;; lenTest6++) { + var curBack = this._matchDistances[offs6 + 1]; + var curAndLenPrice6 = normalMatchPrice + + this.getPosLenPrice(curBack, lenTest6, posState); + var optimum6 = this._optimum[cur + lenTest6]; + if (curAndLenPrice6 < optimum6.price) { + optimum6.price = curAndLenPrice6; + optimum6.posPrev = cur; + optimum6.backPrev = curBack + Base.kNumRepDistances; + optimum6.prev1IsChar = false; + } + + if (lenTest6 === this._matchDistances[offs6]) { + if (lenTest6 < numAvailableBytesFull) { + var t7 = Math.min(numAvailableBytesFull - 1 - lenTest6, + this._numFastBytes); + var lenTest27 = this._matchFinder.getMatchLen(lenTest6, curBack, t7); + if (lenTest27 >= 2) { + var state27 = Base.stateUpdateMatch(state); + + var posStateNext7 = (position + lenTest6) & this._posStateMask; + var curAndLenCharPrice7 = curAndLenPrice6 + + RangeCoder.Encoder.getPrice0(this._isMatch[(state27 << Base.kNumPosStatesBitsMax) + posStateNext7]) + + this._literalEncoder.getSubCoder(position + lenTest6, + this._matchFinder.getIndexByte(lenTest6 - 2)). + getPrice(true, + this._matchFinder.getIndexByte(lenTest6 - (curBack + 1) - 1), + this._matchFinder.getIndexByte(lenTest6 - 1)); + state27 = Base.stateUpdateChar(state27); + posStateNext7 = (position + lenTest6 + 1) & this._posStateMask; + var nextMatchPrice7 = curAndLenCharPrice7 + + RangeCoder.Encoder.getPrice1(this._isMatch[(state27 << Base.kNumPosStatesBitsMax) + posStateNext7]); + var nextRepMatchPrice7 = nextMatchPrice7 + + RangeCoder.Encoder.getPrice1(this._isRep[state27]); + + var offset7 = lenTest6 + 1 + lenTest27; + while (lenEnd < cur + offset7) { + this._optimum[++lenEnd].price = kInfinityPrice; + } + var curAndLenPrice7 = nextRepMatchPrice7 + + this.getRepPrice(0, lenTest27, state27, posStateNext7); + var optimum7 = this._optimum[cur + offset7]; + if (curAndLenPrice7 < optimum7.price) { + optimum7.price = curAndLenPrice7; + optimum7.posPrev = cur + lenTest6 + 1; + optimum7.backPrev = 0; + optimum7.prev1IsChar = true; + optimum7.prev2 = true; + optimum7.posPrev2 = cur; + optimum7.backPrev2 = curBack + Base.kNumRepDistances; + } + } + } + offs6 += 2; + if (offs6 === numDistancePairs) { + break; + } + } + } + } + } + }; + + Encoder.prototype.changePair = function (smallDist, bigDist) { + var kDif = 7; + return (smallDist < (1 << (32 - kDif)) && bigDist >= (smallDist << kDif)); + }; + + Encoder.prototype.writeEndMarker = function (posState) { + if (!this._writeEndMark) { + return; + } + + this._rangeEncoder.encode(this._isMatch, (this._state << Base.kNumPosStatesBitsMax) + posState, 1); + this._rangeEncoder.encode(this._isRep, this._state, 0); + this._state = Base.stateUpdateMatch(this._state); + var len = Base.kMatchMinLen; + this._lenEncoder.encode(this._rangeEncoder, len - Base.kMatchMinLen, posState); + var posSlot = (1 << Base.kNumPosSlotBits) - 1; + var lenToPosState = Base.getLenToPosState(len); + this._posSlotEncoder[lenToPosState].encode(this._rangeEncoder, posSlot); + var footerBits = 30; + var posReduced = (1 << footerBits) - 1; + this._rangeEncoder.encodeDirectBits(posReduced >> Base.kNumAlignBits, + footerBits - Base.kNumAlignBits); + this._posAlignEncoder.reverseEncode(this._rangeEncoder, + posReduced & Base.kAlignMask); + }; + + Encoder.prototype.flush = function (nowPos) { + this.releaseMFStream(); + this.writeEndMarker(nowPos & this._posStateMask); + this._rangeEncoder.flushData(); + this._rangeEncoder.flushStream(); + }; + + Encoder.prototype.codeOneBlock = function (inSize, outSize, finished) { + inSize[0] = 0; + outSize[0] = 0; + finished[0] = true; + + if (this._inStream) { + this._matchFinder.setStream(this._inStream); + this._matchFinder.init(); + this._needReleaseMFStream = true; + this._inStream = null; + } + + if (this._finished) { + return; + } + this._finished = true; + + var progressPosValuePrev = this.nowPos64; + var posState, curByte, i; + + if (this.nowPos64 === 0) { + if (this._matchFinder.getNumAvailableBytes() === 0) { + this.flush(this.nowPos64); + return; + } + + this.readMatchDistances(); + posState = this.nowPos64 & this._posStateMask; + this._rangeEncoder.encode(this._isMatch, (this._state << Base.kNumPosStatesBitsMax) + posState, 0); + this._state = Base.stateUpdateChar(this._state); + curByte = this._matchFinder.getIndexByte(0 - this._additionalOffset); + this._literalEncoder.getSubCoder(this.nowPos64, this._previousByte). + encode(this._rangeEncoder, curByte); + this._previousByte = curByte; + this._additionalOffset--; + this.nowPos64++; + } + if (this._matchFinder.getNumAvailableBytes() === 0) { + this.flush(this.nowPos64); + return; + } + while (true) { + var len = this.getOptimum(this.nowPos64); + var pos = this.backRes; + posState = this.nowPos64 & this._posStateMask; + var complexState = (this._state << Base.kNumPosStatesBitsMax) + posState; + if (len === 1 && pos === -1) { + this._rangeEncoder.encode(this._isMatch, complexState, 0); + curByte = this._matchFinder.getIndexByte(-this._additionalOffset); + var subCoder = this._literalEncoder.getSubCoder(this.nowPos64, + this._previousByte); + if (!Base.stateIsCharState(this._state)) { + var matchByte = this._matchFinder.getIndexByte(-this._repDistances[0] - 1 - this._additionalOffset); + subCoder.encodeMatched(this._rangeEncoder, matchByte, curByte); + } else { + subCoder.encode(this._rangeEncoder, curByte); + } + this._previousByte = curByte; + this._state = Base.stateUpdateChar(this._state); + } else { + this._rangeEncoder.encode(this._isMatch, complexState, 1); + if (pos < Base.kNumRepDistances) { + this._rangeEncoder.encode(this._isRep, this._state, 1); + if (pos === 0) { + this._rangeEncoder.encode(this._isRepG0, this._state, 0); + if (len === 1) { + this._rangeEncoder.encode(this._isRep0Long, complexState, 0); + } else { + this._rangeEncoder.encode(this._isRep0Long, complexState, 1); + } + } else { + this._rangeEncoder.encode(this._isRepG0, this._state, 1); + if (pos === 1) { + this._rangeEncoder.encode(this._isRepG1, this._state, 0); + } else { + this._rangeEncoder.encode(this._isRepG1, this._state, 1); + this._rangeEncoder.encode(this._isRepG2, this._state, pos - 2); + } + } + if (len === 1) { + this._state = Base.stateUpdateShortRep(this._state); + } else { + this._repMatchLenEncoder.encode(this._rangeEncoder, + len - Base.kMatchMinLen, posState); + this._state = Base.stateUpdateRep(this._state); + } + var distance = this._repDistances[pos]; + if (pos !== 0) { + for (i = pos; i >= 1; i--) { + this._repDistances[i] = this._repDistances[i - 1]; + } + this._repDistances[0] = distance; + } + } else { + this._rangeEncoder.encode(this._isRep, this._state, 0); + this._state = Base.stateUpdateMatch(this._state); + this._lenEncoder.encode(this._rangeEncoder, len - Base.kMatchMinLen, + posState); + pos -= Base.kNumRepDistances; + var posSlot = getPosSlot(pos); + var lenToPosState = Base.getLenToPosState(len); + this._posSlotEncoder[lenToPosState].encode(this._rangeEncoder, posSlot); + + if (posSlot >= Base.kStartPosModelIndex) { + var footerBits = ((posSlot >>> 1) - 1); + var baseVal = ((2 | (posSlot & 1)) << footerBits); + var posReduced = pos - baseVal; + + if (posSlot < Base.kEndPosModelIndex) { + RangeCoder.BitTreeEncoder.reverseEncode(this._posEncoders, + baseVal - posSlot - 1, + this._rangeEncoder, + footerBits, posReduced); + } else { + this._rangeEncoder.encodeDirectBits(posReduced >> Base.kNumAlignBits, footerBits - Base.kNumAlignBits); + this._posAlignEncoder.reverseEncode(this._rangeEncoder, + posReduced & Base.kAlignMask); + this._alignPriceCount++; + } + } + var distance2 = pos; + for (i = Base.kNumRepDistances - 1; i >= 1; i--) { + this._repDistances[i] = this._repDistances[i - 1]; + } + this._repDistances[0] = distance2; + this._matchPriceCount++; + } + this._previousByte = + this._matchFinder.getIndexByte(len - 1 - this._additionalOffset); + } + this._additionalOffset -= len; + this.nowPos64 += len; + if (this._additionalOffset === 0) { + // if (!_fastMode) + if (this._matchPriceCount >= (1 << 7)) { + this.fillDistancesPrices(); + } + if (this._alignPriceCount >= Base.kAlignTableSize) { + this.fillAlignPrices(); + } + inSize[0] = this.nowPos64; + outSize[0] = this._rangeEncoder.getProcessedSizeAdd(); + + if (this._matchFinder.getNumAvailableBytes() === 0) { + this.flush(this.nowPos64); + return; + } + + if (this.nowPos64 - progressPosValuePrev >= (1 << 12)) { + this._finished = false; + finished[0] = false; + return; + } + } + } + }; + + Encoder.prototype.releaseMFStream = function () { + if (this._matchFinder && this._needReleaseMFStream) { + this._matchFinder.releaseStream(); + this._needReleaseMFStream = false; + } + }; + + Encoder.prototype.setOutStream = function (outStream) { + this._rangeEncoder.setStream(outStream); + }; + Encoder.prototype.releaseOutStream = function () { + this._rangeEncoder.releaseStream(); + }; + + Encoder.prototype.releaseStreams = function () { + this.releaseMFStream(); + this.releaseOutStream(); + }; + + Encoder.prototype.setStreams = function (inStream, outStream, inSize, outSize) { + this._inStream = inStream; + this._finished = false; + this.create(); + this.setOutStream(outStream); + this.init(); + + // if (!_fastMode) + if (true) { + this.fillDistancesPrices(); + this.fillAlignPrices(); + } + + this._lenEncoder.setTableSize(this._numFastBytes + 1 - Base.kMatchMinLen); + this._lenEncoder.updateTables(1 << this._posStateBits); + this._repMatchLenEncoder.setTableSize(this._numFastBytes + + 1 - Base.kMatchMinLen); + this._repMatchLenEncoder.updateTables(1 << this._posStateBits); + + this.nowPos64 = 0; + }; + + Encoder.prototype.code = function (inStream, outStream, inSize, outSize, progress) { + this._needReleaseMFStream = false; + try { + this.setStreams(inStream, outStream, inSize, outSize); + while (true) { + + this.codeOneBlock(this.processedInSize, this.processedOutSize, + this.finished); + if (this.finished[0]) { + return; + } + if (progress) { + progress.setProgress(this.processedInSize[0], this.processedOutSize[0]); + } + } + } finally { + this.releaseStreams(); + } + }; + + Encoder.prototype.writeCoderProperties = function (outStream) { + var properties = makeBuffer(kPropSize), + i; + properties[0] = ((this._posStateBits * 5 + this._numLiteralPosStateBits) * 9 + + this._numLiteralContextBits); + for (i = 0; i < 4; i++) { + properties[1 + i] = (this._dictionarySize >>> (8 * i)); + } + for (i = 0; i < kPropSize; i++) { + outStream.writeByte(properties[i]); + } + }; + + Encoder.prototype.fillDistancesPrices = function () { + var tempPrices = []; + tempPrices.length = Base.kNumFullDistances; + var i, posSlot; + for (i = Base.kStartPosModelIndex; i < Base.kNumFullDistances; i++) { + posSlot = getPosSlot(i); + var footerBits = ((posSlot >>> 1) - 1); + var baseVal = ((2 | (posSlot & 1)) << footerBits); + tempPrices[i] = + RangeCoder.BitTreeEncoder.reverseGetPrice(this._posEncoders, + baseVal - posSlot - 1, + footerBits, i - baseVal); + } + + var lenToPosState = 0; + for (; lenToPosState < Base.kNumLenToPosStates; lenToPosState++) { + var encoder = this._posSlotEncoder[lenToPosState]; + + var st = (lenToPosState << Base.kNumPosSlotBits); + for (posSlot = 0; posSlot < this._distTableSize; posSlot++) { + this._posSlotPrices[st + posSlot] = encoder.getPrice(posSlot); + } + for (posSlot = Base.kEndPosModelIndex; posSlot < this._distTableSize; posSlot++) { + this._posSlotPrices[st + posSlot] += ((((posSlot >>> 1) - 1) - Base.kNumAlignBits) << RangeCoder.Encoder.kNumBitPriceShiftBits); + } + + var st2 = lenToPosState * Base.kNumFullDistances; + for (i = 0; i < Base.kStartPosModelIndex; i++) { + this._distancesPrices[st2 + i] = this._posSlotPrices[st + i]; + } + for (; i < Base.kNumFullDistances; i++) { + this._distancesPrices[st2 + i] = + this._posSlotPrices[st + getPosSlot(i)] + tempPrices[i]; + } + } + this._matchPriceCount = 0; + }; + + Encoder.prototype.fillAlignPrices = function () { + var i; + for (i = 0; i < Base.kAlignTableSize; i++) { + this._alignPrices[i] = this._posAlignEncoder.reverseGetPrice(i); + } + this._alignPriceCount = 0; + }; + + Encoder.prototype.setAlgorithm = function (algorithm) { + /* + _fastMode = (algorithm == 0); + _maxMode = (algorithm >= 2); + */ + return true; + }; + + Encoder.prototype.setDictionarySize = function (dictionarySize) { + var kDicLogSizeMaxCompress = 29; + if (dictionarySize < (1 << Base.kDicLogSizeMin) || + dictionarySize > (1 << kDicLogSizeMaxCompress)) { + return false; + } + this._dictionarySize = dictionarySize; + var dicLogSize = 0; + while (dictionarySize > (1 << dicLogSize)) { + dicLogSize++; + } + this._distTableSize = dicLogSize * 2; + return true; + }; + + Encoder.prototype.setNumFastBytes = function (numFastBytes) { + if (numFastBytes < 5 || numFastBytes > Base.kMatchMaxLen) { + return false; + } + this._numFastBytes = numFastBytes; + return true; + }; + + Encoder.prototype.setMatchFinder = function (matchFinderIndex) { + if (matchFinderIndex < 0 || matchFinderIndex > 2) { + return false; + } + var matchFinderIndexPrev = this._matchFinderType; + this._matchFinderType = matchFinderIndex; + if (this._matchFinder && matchFinderIndexPrev != this._matchFinderType) { + this._dictionarySizePrev = -1; + this._matchFinder = null; + } + return true; + }; + + Encoder.prototype.setLcLpPb = function (lc, lp, pb) { + if (lp < 0 || lp > Base.kNumLitPosStatesBitsEncodingMax || + lc < 0 || lc > Base.kNumLitContextBitsMax || + pb < 0 || pb > Base.kNumPosStatesBitsEncodingMax) { + return false; + } + this._numLiteralPosStateBits = lp; + this._numLiteralContextBits = lc; + this._posStateBits = pb; + this._posStateMask = ((1) << this._posStateBits) - 1; + return true; + }; + + Encoder.prototype.setEndMarkerMode = function (endMarkerMode) { + this._writeEndMark = endMarkerMode; + }; + + Encoder.EMatchFinderTypeBT2 = EMatchFinderTypeBT2; + Encoder.EMatchFinderTypeBT4 = EMatchFinderTypeBT4; + + return { + 'Decoder': Decoder, + 'Encoder': Encoder + }; + +})(); + +/* lib/Stream.js */ + +/* very simple input/output stream interface */ +var Stream = function () {}; + +// input streams ////////////// +/** Returns the next byte, or -1 for EOF. */ +Stream.prototype.readByte = function () { + throw new Error("abstract method readByte() not implemented"); +}; +/** Attempts to fill the buffer; returns number of bytes read, or + * -1 for EOF. */ +Stream.prototype.read = function (buffer, bufOffset, length) { + var bytesRead = 0; + while (bytesRead < length) { + var c = this.readByte(); + if (c < 0) { // EOF + return (bytesRead === 0) ? -1 : bytesRead; + } + buffer[bufOffset++] = c; + bytesRead++; + } + return bytesRead; +}; +Stream.prototype.seek = function (new_pos) { + throw new Error("abstract method seek() not implemented"); +}; + +// output streams /////////// +Stream.prototype.writeByte = function (_byte) { + throw new Error("abstract method readByte() not implemented"); +}; +Stream.prototype.write = function (buffer, bufOffset, length) { + var i; + for (i = 0; i < length; i++) { + this.writeByte(buffer[bufOffset++]); + } + return length; +}; +Stream.prototype.flush = function () {}; + + +/* lib/Util.js */ + +/* +Copyright (c) 2011 Juan Mellado + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. +*/ + +/* +References: +- "LZMA SDK" by Igor Pavlov + http://www.7-zip.org/sdk.html +*/ +/* Original source found at: http://code.google.com/p/js-lzma/ */ + +var coerceInputStream = function (input) { + if ('readByte' in input) { + return input; + } + var inputStream = new Stream(); + inputStream.pos = 0; + inputStream.size = input.length; + inputStream.readByte = function () { + return this.eof() ? -1 : input[this.pos++]; + }; + inputStream.read = function (buffer, bufOffset, length) { + var bytesRead = 0; + while (bytesRead < length && this.pos < input.length) { + buffer[bufOffset++] = input[this.pos++]; + bytesRead++; + } + return bytesRead; + }; + inputStream.seek = function (pos) { + this.pos = pos; + }; + inputStream.eof = function () { + return this.pos >= input.length; + }; + return inputStream; +}; + +var coerceOutputStream = function (output) { + var outputStream = new Stream(); + var resizeOk = true; + if (output) { + if (typeof (output) === 'number') { + outputStream.buffer = new Buffer(output); + resizeOk = false; + } else if ('writeByte' in output) { + return output; + } else { + outputStream.buffer = output; + resizeOk = false; + } + } else { + outputStream.buffer = new Buffer(16384); + } + outputStream.pos = 0; + outputStream.writeByte = function (_byte) { + if (resizeOk && this.pos >= this.buffer.length) { + var newBuffer = new Buffer(this.buffer.length * 2); + this.buffer.copy(newBuffer); + this.buffer = newBuffer; + } + this.buffer[this.pos++] = _byte; + }; + outputStream.getBuffer = function () { + // trim buffer + if (this.pos !== this.buffer.length) { + if (!resizeOk) + throw new TypeError('outputsize does not match decoded input'); + var newBuffer = new Buffer(this.pos); + this.buffer.copy(newBuffer, 0, 0, this.pos); + this.buffer = newBuffer; + } + return this.buffer; + }; + outputStream._coerced = true; + return outputStream; +}; + +var Util = Object.create(null); + +Util.decompress = function (properties, inStream, outStream, outSize) { + var decoder = new LZMA.Decoder(); + + if (!decoder.setDecoderProperties(properties)) { + throw "Incorrect stream properties"; + } + + if (!decoder.code(inStream, outStream, outSize)) { + throw "Error in data stream"; + } + + return true; +}; + +/* Also accepts a Uint8Array/Buffer/array as first argument, in which case + * returns the decompressed file as a Uint8Array/Buffer/array. */ +Util.decompressFile = function(inStream, outStream){ + var decoder = new LZMA.Decoder(), i, mult; + + inStream = coerceInputStream(inStream); + + if ( !decoder.setDecoderPropertiesFromStream(inStream) ){ + throw "Incorrect stream properties"; + } + + // largest integer in javascript is 2^53 (unless we use typed arrays) + // but we don't explicitly check for overflow here. caveat user. + var outSizeLo = 0; + for (i=0, mult=1; i<4; i++, mult*=256) { + outSizeLo += (inStream.readByte() * mult); + } + var outSizeHi = 0; + for (i=0, mult=1; i<4; i++, mult*=256) { + outSizeHi += (inStream.readByte() * mult); + } + var outSize = outSizeLo + (outSizeHi * 0x100000000); + if (outSizeLo === 0xFFFFFFFF && outSizeHi === 0xFFFFFFFF) { + outSize = -1; + } else if (outSizeHi >= 0x200000) { + outSize = -1; // force streaming + } + + if (outSize >= 0 && !outStream) { outStream = outSize; } + outStream = coerceOutputStream(outStream); + + if ( !decoder.code(inStream, outStream, outSize) ){ + throw "Error in data stream"; + } + + return ('getBuffer' in outStream) ? outStream.getBuffer() : true; +}; + +/* The following is a mapping from gzip/bzip2 style -1 .. -9 compression modes + * to the corresponding LZMA compression modes. Thanks, Larhzu, for coining + * these. */ +/* [csa] lifted from lzmp.cpp in the LZMA SDK. */ +var option_mapping = [{ + a: 0, + d: 0, + fb: 0, + mf: null, + lc: 0, + lp: 0, + pb: 0 + }, // -0 (needed for indexing) + { + a: 0, + d: 16, + fb: 64, + mf: "hc4", + lc: 3, + lp: 0, + pb: 2 + }, // -1 + { + a: 0, + d: 20, + fb: 64, + mf: "hc4", + lc: 3, + lp: 0, + pb: 2 + }, // -2 + { + a: 1, + d: 19, + fb: 64, + mf: "bt4", + lc: 3, + lp: 0, + pb: 2 + }, // -3 + { + a: 2, + d: 20, + fb: 64, + mf: "bt4", + lc: 3, + lp: 0, + pb: 2 + }, // -4 + { + a: 2, + d: 21, + fb: 128, + mf: "bt4", + lc: 3, + lp: 0, + pb: 2 + }, // -5 + { + a: 2, + d: 22, + fb: 128, + mf: "bt4", + lc: 3, + lp: 0, + pb: 2 + }, // -6 + { + a: 2, + d: 23, + fb: 128, + mf: "bt4", + lc: 3, + lp: 0, + pb: 2 + }, // -7 + { + a: 2, + d: 24, + fb: 255, + mf: "bt4", + lc: 3, + lp: 0, + pb: 2 + }, // -8 + { + a: 2, + d: 25, + fb: 255, + mf: "bt4", + lc: 3, + lp: 0, + pb: 2 + } // -9 +]; + +/** Create and configure an Encoder, based on the given properties (which + * make be a simple number, for a compression level between 1 and 9. */ +var makeEncoder = function (props) { + var encoder = new LZMA.Encoder(); + var params = { // defaults! + a: 1, + /* algorithm */ + d: 23, + /* dictionary */ + fb: 128, + /* fast bytes */ + lc: 3, + /* literal context */ + lp: 0, + /* literal position */ + pb: 2, + /* position bits */ + mf: "bt4", + /* match finder (bt2/bt4) */ + eos: false /* write end of stream */ + }; + // override default params with props + if (props) { + if (typeof (props) === 'number') { // -1 through -9 options + props = option_mapping[props]; + } + var p; + for (p in props) { + if (Object.prototype.hasOwnProperty.call(props, p)) { + params[p] = props[p]; + } + } + } + encoder.setAlgorithm(params.a); + encoder.setDictionarySize(1 << (+params.d)); + encoder.setNumFastBytes(+params.fb); + encoder.setMatchFinder((params.mf === 'bt4') ? + LZMA.Encoder.EMatchFinderTypeBT4 : + LZMA.Encoder.EMatchFinderTypeBT2); + encoder.setLcLpPb(+params.lc, +params.lp, +params.pb); + encoder.setEndMarkerMode(!!params.eos); + return encoder; +}; + +Util.compress = function (inStream, outStream, props, progress) { + var encoder = makeEncoder(props); + + encoder.writeCoderProperties(outStream); + + encoder.code(inStream, outStream, -1, -1, { + setProgress: function (inSize, outSize) { + if (progress) { + progress(inSize, outSize); + } + } + }); + + return true; +}; + +/* Also accepts a Uint8Array/Buffer/array as first argument, in which case + * returns the compressed file as a Uint8Array/Buffer/array. */ +Util.compressFile = function(inStream, outStream, props, progress) { + var encoder = makeEncoder(props); + var i; + + inStream = coerceInputStream(inStream); + // if we know the size, write it; otherwise we need to use the 'eos' property + var fileSize; + if ('size' in inStream && inStream.size >= 0) { + fileSize = inStream.size; + } else { + fileSize = -1; + encoder.setEndMarkerMode(true); + } + + outStream = coerceOutputStream(outStream); + + encoder.writeCoderProperties(outStream); + + var out64 = function(s) { + // supports up to 53-bit integers + var i; + for (i=0;i<8;i++) { + outStream.writeByte(s & 0xFF); + s = Math.floor(s/256); + } + }; + out64(fileSize); + + encoder.code(inStream, outStream, fileSize, -1, { + setProgress: function(inSize, outSize) { + if (progress) { progress(inSize, outSize); } + } + }); + + return ('getBuffer' in outStream) ? outStream.getBuffer() : true; +}; + +/* github.com/arextar/browser-buffer */ + +// Based off of buffer.js in the Node project(copyright Joyent, Inc. and other Node contributors.) +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +(function () { + "use strict"; + + function isArrayIsh(subject) { + return Buffer.isBuffer(subject) || subject && typeof subject === 'object' && typeof subject.length === 'number'; + } + + function coerce(length) { + length = ~~Math.ceil(+length); + return length < 0 ? 0 : length; + } + + function ok(cond, msg) { + if (!cond) throw new Error(msg); + } + + var ArrayBuffer = self.ArrayBuffer || + function (len) { + this.length = len; + while (len--) this[len] = 0; + }, Uint8Array = self.Uint8Array || + function (parent, offset, length) { + this.buffer = parent; + this.offset = offset; + this.length = length; + }, __set = 'Uint8Array' in self && Uint8Array.prototype.set; + + self.Uint8Array || (Uint8Array.prototype = { + get: function (ind) { + return this.buffer[ind + this.offset]; + }, + set: function (ind, value) { + this.buffer[ind + this.offset] = value; + } + }) + + var makeBuffer = function (parent, offset, length) { + var buf = new Uint8Array(parent, offset, length); + buf.parent = parent; + buf.offset = offset; + return buf; + }, + + Buffer = function (subject, encoding, offset) { + var type, length, parent, ret; + + // Are we slicing? + if (typeof offset === 'number') { + length = coerce(encoding); + parent = subject; + } else { + // Find the length + switch (type = typeof subject) { + case 'number': + length = coerce(subject); + break; + + case 'string': + length = Buffer.byteLength(subject, encoding); + break; + + case 'object': + // Assume object is an array + length = coerce(subject.length); + break; + + default: + throw new Error('First argument needs to be a number, ' + 'array or string.'); + } + + if (length > Buffer.poolSize) { + // Big buffer, just alloc one. + parent = new ArrayBuffer(length); + offset = 0; + } else { + // Small buffer. + if (!pool || pool.byteLength - pool.used < length) allocPool(); + parent = pool; + offset = pool.used; + pool.used += length; + } + + // Treat array-ish objects as a byte array. + if (isArrayIsh(subject)) { + ret = makeBuffer(parent, offset, length); + var i = length; + while (i--) { + ret[i] = subject[i]; + } + } else if (type == 'string') { + ret = makeBuffer(parent, offset, length); + length = ret.write(subject, 0, encoding); + } + } + + return ret || makeBuffer(parent, offset, length); + }, + + proto = Buffer.prototype = Uint8Array.prototype; + + proto.toString = function (encoding, start, end) { + encoding = String(encoding || 'utf8').toLowerCase(); + start = +start || 0; + if (typeof end == 'undefined') end = this.length; + + // Fastpath empty strings + if (+end == start) { + return ''; + } + + switch (encoding) { + case 'hex': + return this.hexSlice(start, end); + + case 'utf8': + case 'utf-8': + return this.utf8Slice(start, end); + + case 'ascii': + return this.asciiSlice(start, end); + + case 'base64': + return this.base64Slice(start, end); + + /*case 'binary': + return this.binarySlice(start, end); + + case 'ucs2': + case 'ucs-2': + return this.ucs2Slice(start, end);*/ + + default: + throw new Error('Unknown encoding'); + } + } + + proto.write = function (string, offset, length, encoding) { + // Support both (string, offset, length, encoding) + // and the legacy (string, encoding, offset, length) + if (isFinite(offset)) { + if (!isFinite(length)) { + encoding = length; + length = undefined; + } + } else { // legacy + var swap = encoding; + encoding = offset; + offset = length; + length = swap; + } + + offset = +offset || 0; + var remaining = this.length - offset; + if (!length) { + length = remaining; + } else { + length = +length; + if (length > remaining) { + length = remaining; + } + } + encoding = String(encoding || 'utf8').toLowerCase(); + + switch (encoding) { + /*case 'hex': + return this.hexWrite(string, offset, length);*/ + + case 'utf8': + case 'utf-8': + return this.utf8Write(string, offset, length); + + case 'ascii': + return this.asciiWrite(string, offset, length); + + case 'base64': + return this.base64Write(string, offset, length); + + /*case 'binary': + return this.binaryWrite(string, offset, length); + + case 'ucs2': + case 'ucs-2': + return this.ucs2Write(string, offset, length);*/ + + default: + throw new Error('Unknown encoding'); + } + }; + + + var fCC = String.fromCharCode; + + proto.utf8Write = function (string, start, end) { + for (var i = 0, l = start, le = string.length, d = end - start; i < d && i < le; i++) { + var c = string.charCodeAt(i); + + if (c < 128) { + this[l++] = c; + } else if ((c > 127) && (c < 2048)) { + this[l++] = (c >> 6) | 192; + this[l++] = (c & 63) | 128; + } else { + this[l++] = (c >> 12) | 224; + this[l++] = ((c >> 6) & 63) | 128; + this[l++] = (c & 63) | 128; + } + } + this._charsWritten = l; + return le; + } + + proto.utf8Slice = function (start, end) { + for (var string = "", c, i = start, p = 0, c2, c3; p < end && (c = this[i]); i++) { + p++; + if (c < 128) { + string += fCC(c); + } else if ((c > 191) && (c < 224)) { + c2 = this[i + 1]; + string += fCC(((c & 31) << 6) | (c2 & 63)); + i++; + } else { + c2 = this[i + 1]; + c3 = this[i + 2]; + string += fCC(((c & 15) << 12) | ((c2 & 63) << 6) | (c3 & 63)); + i += 2; + } + } + return string; + } + + proto.asciiWrite = function (string, start, end) { + for (var i = 0, le = string.length; i < end && i < le; i++) { + this[i + start] = string.charCodeAt(i); + } + this._charsWritten = i; + return le; + } + + proto.asciiSlice = function (start, end) { + for (var string = "", i = start; i < end; i++) { + string += fCC(this[i]); + } + return string; + } + + function toHex(n) { + if (n < 16) return '0' + n.toString(16); + return n.toString(16); + } + + proto.hexSlice = function (start, end) { + var len = this.length; + + if (!start || start < 0) start = 0; + if (!end || end < 0 || end > len) end = len; + + var out = ''; + for (var i = start; i < end; i++) { + out += toHex(this[i]); + } + return out; + }; + + proto.copy = function (target, tStart, sStart, sEnd) { + for (var i = 0, d = sEnd - sStart; i < d; i++) { + target[i + tStart] = this[i + sStart]; + } + } + + + proto.base64Slice = function (start, end) { + var len = this.length; + + if (!start || start < 0) start = 0; + if (!end || end < 0 || end > len) end = len; + + var out = ''; + for (var i = start; i < end; i++) { + out += self.btoa(this[i]); + } + return out; + }; + + proto.base64Write = function (string, start, end) { + for (var i = 0, le = string.length; i < end && i < le; i++) { + this[i + start] = self.atob( string.charCodeAt(i) ); + } + this._charsWritten = i; + return le; + } + + + + proto.slice = function (start, end) { + if (end === undefined) end = this.length; + + if (end > this.length) { + throw new Error('oob'); + } + if (start > end) { + throw new Error('oob'); + } + + return makeBuffer(this.buffer, +start, end - start); + }; + + proto.toBlob = function () { + var b = new (self.BlobBuilder || self.WebKitBlobBuilder || self.MozBlobBuilder); + this.offset ? b.append(this.toString('utf8')) : b.append(this.buffer); + return b.getBlob(); + } + + proto.readUInt8 = function (offset, noAssert) { + var buffer = this; + + if (!noAssert) { + ok(offset !== undefined && offset !== null, + 'missing offset'); + + ok(offset < buffer.length, + 'Trying to read beyond buffer length'); + } + + return buffer[offset]; + }; + + function readUInt16(buffer, offset, isBigEndian, noAssert) { + var val = 0; + + + if (!noAssert) { + ok(typeof (isBigEndian) === 'boolean', + 'missing or invalid endian'); + + ok(offset !== undefined && offset !== null, + 'missing offset'); + + ok(offset + 1 < buffer.length, + 'Trying to read beyond buffer length'); + } + + if (isBigEndian) { + val = buffer[offset] << 8; + val |= buffer[offset + 1]; + } else { + val = buffer[offset]; + val |= buffer[offset + 1] << 8; + } + + return val; + } + + proto.readUInt16LE = function (offset, noAssert) { + return readUInt16(this, offset, false, noAssert); + }; + + proto.readUInt16BE = function (offset, noAssert) { + return readUInt16(this, offset, true, noAssert); + }; + + function readUInt32(buffer, offset, isBigEndian, noAssert) { + var val = 0; + + if (!noAssert) { + ok(typeof (isBigEndian) === 'boolean', + 'missing or invalid endian'); + + ok(offset !== undefined && offset !== null, + 'missing offset'); + + ok(offset + 3 < buffer.length, + 'Trying to read beyond buffer length'); + } + + if (isBigEndian) { + val = buffer[offset + 1] << 16; + val |= buffer[offset + 2] << 8; + val |= buffer[offset + 3]; + val = val + (buffer[offset] << 24 >>> 0); + } else { + val = buffer[offset + 2] << 16; + val |= buffer[offset + 1] << 8; + val |= buffer[offset]; + val = val + (buffer[offset + 3] << 24 >>> 0); + } + + return val; + } + + proto.readUInt32LE = function (offset, noAssert) { + return readUInt32(this, offset, false, noAssert); + }; + + proto.readUInt32BE = function (offset, noAssert) { + return readUInt32(this, offset, true, noAssert); + }; + + proto.readInt8 = function (offset, noAssert) { + var buffer = this; + var neg; + + if (!noAssert) { + ok(offset !== undefined && offset !== null, + 'missing offset'); + + ok(offset < buffer.length, + 'Trying to read beyond buffer length'); + } + + neg = buffer[offset] & 0x80; + if (!neg) { + return (buffer[offset]); + } + + return ((0xff - buffer[offset] + 1) * -1); + }; + + function readInt16(buffer, offset, isBigEndian, noAssert) { + var neg, val; + + if (!noAssert) { + ok(typeof (isBigEndian) === 'boolean', + 'missing or invalid endian'); + + ok(offset !== undefined && offset !== null, + 'missing offset'); + + ok(offset + 1 < buffer.length, + 'Trying to read beyond buffer length'); + } + + val = readUInt16(buffer, offset, isBigEndian, noAssert); + neg = val & 0x8000; + if (!neg) { + return val; + } + + return (0xffff - val + 1) * -1; + } + + proto.readInt16LE = function (offset, noAssert) { + return readInt16(this, offset, false, noAssert); + }; + + proto.readInt16BE = function (offset, noAssert) { + return readInt16(this, offset, true, noAssert); + }; + + function readInt32(buffer, offset, isBigEndian, noAssert) { + var neg, val; + + if (!noAssert) { + ok(typeof (isBigEndian) === 'boolean', + 'missing or invalid endian'); + + ok(offset !== undefined && offset !== null, + 'missing offset'); + + ok(offset + 3 < buffer.length, + 'Trying to read beyond buffer length'); + } + + val = readUInt32(buffer, offset, isBigEndian, noAssert); + neg = val & 0x80000000; + if (!neg) { + return (val); + } + + return (0xffffffff - val + 1) * -1; + } + + proto.readInt32LE = function (offset, noAssert) { + return readInt32(this, offset, false, noAssert); + }; + + proto.readInt32BE = function (offset, noAssert) { + return readInt32(this, offset, true, noAssert); + }; + + function readIEEE754(buffer, offset, isBE, mLen, nBytes) { + var e, m, + eLen = nBytes * 8 - mLen - 1, + eMax = (1 << eLen) - 1, + eBias = eMax >> 1, + nBits = -7, + i = isBE ? 0 : (nBytes - 1), + d = isBE ? 1 : -1, + s = buffer[offset + i]; + + i += d; + + e = s & ((1 << (-nBits)) - 1); + s >>= (-nBits); + nBits += eLen; + for (; nBits > 0; e = e * 256 + buffer[offset + i], i += d, nBits -= 8); + + m = e & ((1 << (-nBits)) - 1); + e >>= (-nBits); + nBits += mLen; + for (; nBits > 0; m = m * 256 + buffer[offset + i], i += d, nBits -= 8); + + if (e === 0) { + e = 1 - eBias; + } else if (e === eMax) { + return m ? NaN : ((s ? -1 : 1) * Infinity); + } else { + m = m + Math.pow(2, mLen); + e = e - eBias; + } + return (s ? -1 : 1) * m * Math.pow(2, e - mLen); + }; + + function readFloat(buffer, offset, isBigEndian, noAssert) { + if (!noAssert) { + ok(typeof (isBigEndian) === 'boolean', + 'missing or invalid endian'); + + ok(offset + 3 < buffer.length, + 'Trying to read beyond buffer length'); + } + + return readIEEE754(buffer, offset, isBigEndian, + 23, 4); + } + + proto.readFloatLE = function (offset, noAssert) { + return readFloat(this, offset, false, noAssert); + }; + + proto.readFloatBE = function (offset, noAssert) { + return readFloat(this, offset, true, noAssert); + }; + + function readDouble(buffer, offset, isBigEndian, noAssert) { + if (!noAssert) { + ok(typeof (isBigEndian) === 'boolean', + 'missing or invalid endian'); + + ok(offset + 7 < buffer.length, + 'Trying to read beyond buffer length'); + } + + return readIEEE754(buffer, offset, isBigEndian, + 52, 8); + } + + proto.readDoubleLE = function (offset, noAssert) { + return readDouble(this, offset, false, noAssert); + }; + + proto.readDoubleBE = function (offset, noAssert) { + return readDouble(this, offset, true, noAssert); + }; + + + function verifuint(value, max) { + ok(typeof (value) == 'number', + 'cannot write a non-number as a number'); + + ok(value >= 0, + 'specified a negative value for writing an unsigned value'); + + ok(value <= max, 'value is larger than maximum value for type'); + + ok(Math.floor(value) === value, 'value has a fractional component'); + } + + proto.writeUInt8 = function (value, offset, noAssert) { + var buffer = this; + + if (!noAssert) { + ok(value !== undefined && value !== null, + 'missing value'); + + ok(offset !== undefined && offset !== null, + 'missing offset'); + + ok(offset < buffer.length, + 'trying to write beyond buffer length'); + + verifuint(value, 0xff); + } + + buffer[offset] = value; + }; + + function writeUInt16(buffer, value, offset, isBigEndian, noAssert) { + if (!noAssert) { + ok(value !== undefined && value !== null, + 'missing value'); + + ok(typeof (isBigEndian) === 'boolean', + 'missing or invalid endian'); + + ok(offset !== undefined && offset !== null, + 'missing offset'); + + ok(offset + 1 < buffer.length, + 'trying to write beyond buffer length'); + + verifuint(value, 0xffff); + } + + if (isBigEndian) { + buffer[offset] = (value & 0xff00) >>> 8; + buffer[offset + 1] = value & 0x00ff; + } else { + buffer[offset + 1] = (value & 0xff00) >>> 8; + buffer[offset] = value & 0x00ff; + } + } + + proto.writeUInt16LE = function (value, offset, noAssert) { + writeUInt16(this, value, offset, false, noAssert); + }; + + proto.writeUInt16BE = function (value, offset, noAssert) { + writeUInt16(this, value, offset, true, noAssert); + }; + + function writeUInt32(buffer, value, offset, isBigEndian, noAssert) { + if (!noAssert) { + ok(value !== undefined && value !== null, + 'missing value'); + + ok(typeof (isBigEndian) === 'boolean', + 'missing or invalid endian'); + + ok(offset !== undefined && offset !== null, + 'missing offset'); + + ok(offset + 3 < buffer.length, + 'trying to write beyond buffer length'); + + verifuint(value, 0xffffffff); + } + + if (isBigEndian) { + buffer[offset] = (value >>> 24) & 0xff; + buffer[offset + 1] = (value >>> 16) & 0xff; + buffer[offset + 2] = (value >>> 8) & 0xff; + buffer[offset + 3] = value & 0xff; + } else { + buffer[offset + 3] = (value >>> 24) & 0xff; + buffer[offset + 2] = (value >>> 16) & 0xff; + buffer[offset + 1] = (value >>> 8) & 0xff; + buffer[offset] = value & 0xff; + } + } + + proto.writeUInt32LE = function (value, offset, noAssert) { + writeUInt32(this, value, offset, false, noAssert); + }; + + proto.writeUInt32BE = function (value, offset, noAssert) { + writeUInt32(this, value, offset, true, noAssert); + }; + + + /* + * We now move onto our friends in the signed number category. Unlike unsigned + * numbers, we're going to have to worry a bit more about how we put values into + * arrays. Since we are only worrying about signed 32-bit values, we're in + * slightly better shape. Unfortunately, we really can't do our favorite binary + * & in this system. It really seems to do the wrong thing. For example: + * + * > -32 & 0xff + * 224 + * + * What's happening above is really: 0xe0 & 0xff = 0xe0. However, the results of + * this aren't treated as a signed number. Ultimately a bad thing. + * + * What we're going to want to do is basically create the unsigned equivalent of + * our representation and pass that off to the wuint* functions. To do that + * we're going to do the following: + * + * - if the value is positive + * we can pass it directly off to the equivalent wuint + * - if the value is negative + * we do the following computation: + * mb + val + 1, where + * mb is the maximum unsigned value in that byte size + * val is the Javascript negative integer + * + * + * As a concrete value, take -128. In signed 16 bits this would be 0xff80. If + * you do out the computations: + * + * 0xffff - 128 + 1 + * 0xffff - 127 + * 0xff80 + * + * You can then encode this value as the signed version. This is really rather + * hacky, but it should work and get the job done which is our goal here. + */ + + /* + * A series of checks to make sure we actually have a signed 32-bit number + */ + function verifsint(value, max, min) { + ok(typeof (value) == 'number', + 'cannot write a non-number as a number'); + + ok(value <= max, 'value larger than maximum allowed value'); + + ok(value >= min, 'value smaller than minimum allowed value'); + + ok(Math.floor(value) === value, 'value has a fractional component'); + } + + function verifIEEE754(value, max, min) { + ok(typeof (value) == 'number', + 'cannot write a non-number as a number'); + + ok(value <= max, 'value larger than maximum allowed value'); + + ok(value >= min, 'value smaller than minimum allowed value'); + } + + proto.writeInt8 = function (value, offset, noAssert) { + var buffer = this; + + if (!noAssert) { + ok(value !== undefined && value !== null, + 'missing value'); + + ok(offset !== undefined && offset !== null, + 'missing offset'); + + ok(offset < buffer.length, + 'Trying to write beyond buffer length'); + + verifsint(value, 0x7f, -0x80); + } + + if (value >= 0) { + buffer.writeUInt8(value, offset, noAssert); + } else { + buffer.writeUInt8(0xff + value + 1, offset, noAssert); + } + }; + + function writeInt16(buffer, value, offset, isBigEndian, noAssert) { + if (!noAssert) { + ok(value !== undefined && value !== null, + 'missing value'); + + ok(typeof (isBigEndian) === 'boolean', + 'missing or invalid endian'); + + ok(offset !== undefined && offset !== null, + 'missing offset'); + + ok(offset + 1 < buffer.length, + 'Trying to write beyond buffer length'); + + verifsint(value, 0x7fff, -0x8000); + } + + if (value >= 0) { + writeUInt16(buffer, value, offset, isBigEndian, noAssert); + } else { + writeUInt16(buffer, 0xffff + value + 1, offset, isBigEndian, noAssert); + } + } + + proto.writeInt16LE = function (value, offset, noAssert) { + writeInt16(this, value, offset, false, noAssert); + }; + + proto.writeInt16BE = function (value, offset, noAssert) { + writeInt16(this, value, offset, true, noAssert); + }; + + function writeInt32(buffer, value, offset, isBigEndian, noAssert) { + if (!noAssert) { + ok(value !== undefined && value !== null, + 'missing value'); + + ok(typeof (isBigEndian) === 'boolean', + 'missing or invalid endian'); + + ok(offset !== undefined && offset !== null, + 'missing offset'); + + ok(offset + 3 < buffer.length, + 'Trying to write beyond buffer length'); + + verifsint(value, 0x7fffffff, -0x80000000); + } + + if (value >= 0) { + writeUInt32(buffer, value, offset, isBigEndian, noAssert); + } else { + writeUInt32(buffer, 0xffffffff + value + 1, offset, isBigEndian, noAssert); + } + } + + proto.writeInt32LE = function (value, offset, noAssert) { + writeInt32(this, value, offset, false, noAssert); + }; + + proto.writeInt32BE = function (value, offset, noAssert) { + writeInt32(this, value, offset, true, noAssert); + }; + + function writeIEEE754(buffer, value, offset, isBE, mLen, nBytes) { + var e, m, c, + eLen = nBytes * 8 - mLen - 1, + eMax = (1 << eLen) - 1, + eBias = eMax >> 1, + rt = (mLen === 23 ? Math.pow(2, -24) - Math.pow(2, -77) : 0), + i = isBE ? (nBytes - 1) : 0, + d = isBE ? -1 : 1, + s = value < 0 || (value === 0 && 1 / value < 0) ? 1 : 0; + + value = Math.abs(value); + + if (isNaN(value) || value === Infinity) { + m = isNaN(value) ? 1 : 0; + e = eMax; + } else { + e = Math.floor(Math.log(value) / Math.LN2); + if (value * (c = Math.pow(2, -e)) < 1) { + e--; + c *= 2; + } + if (e + eBias >= 1) { + value += rt / c; + } else { + value += rt * Math.pow(2, 1 - eBias); + } + if (value * c >= 2) { + e++; + c /= 2; + } + + if (e + eBias >= eMax) { + m = 0; + e = eMax; + } else if (e + eBias >= 1) { + m = (value * c - 1) * Math.pow(2, mLen); + e = e + eBias; + } else { + m = value * Math.pow(2, eBias - 1) * Math.pow(2, mLen); + e = 0; + } + } + + for (; mLen >= 8; buffer[offset + i] = m & 0xff, i += d, m /= 256, mLen -= 8); + + e = (e << mLen) | m; + eLen += mLen; + for (; eLen > 0; buffer[offset + i] = e & 0xff, i += d, e /= 256, eLen -= 8); + + buffer[offset + i - d] |= s * 128; + }; + + function writeFloat(buffer, value, offset, isBigEndian, noAssert) { + if (!noAssert) { + ok(value !== undefined && value !== null, + 'missing value'); + + ok(typeof (isBigEndian) === 'boolean', + 'missing or invalid endian'); + + ok(offset !== undefined && offset !== null, + 'missing offset'); + + ok(offset + 3 < buffer.length, + 'Trying to write beyond buffer length'); + + verifIEEE754(value, 3.4028234663852886e+38, -3.4028234663852886e+38); + } + + writeIEEE754(buffer, value, offset, isBigEndian, + 23, 4); + } + + proto.writeFloatLE = function (value, offset, noAssert) { + writeFloat(this, value, offset, false, noAssert); + }; + + proto.writeFloatBE = function (value, offset, noAssert) { + writeFloat(this, value, offset, true, noAssert); + }; + + function writeDouble(buffer, value, offset, isBigEndian, noAssert) { + if (!noAssert) { + ok(value !== undefined && value !== null, + 'missing value'); + + ok(typeof (isBigEndian) === 'boolean', + 'missing or invalid endian'); + + ok(offset !== undefined && offset !== null, + 'missing offset'); + + ok(offset + 7 < buffer.length, + 'Trying to write beyond buffer length'); + + verifIEEE754(value, 1.7976931348623157E+308, -1.7976931348623157E+308); + } + + writeIEEE754(buffer, value, offset, isBigEndian, + 52, 8); + } + + proto.writeDoubleLE = function (value, offset, noAssert) { + writeDouble(this, value, offset, false, noAssert); + }; + + proto.writeDoubleBE = function (value, offset, noAssert) { + writeDouble(this, value, offset, true, noAssert); + }; + + + var pool; + + Buffer.poolSize = 8 * 1024; + + function allocPool() { + pool = new ArrayBuffer(Buffer.poolSize); + pool.used = 0; + } + + Buffer.isBuffer = function isBuffer(b) { + return b instanceof Buffer || b instanceof ArrayBuffer; + }; + + Buffer.byteLength = function (string, encoding) { + switch (encoding) { + case "ascii": + return string.length; + } + for (var i = 0, l = 0, le = string.length, c; i < le; i++) { + c = string.charCodeAt(i); + if (c < 128) { + l++; + } else if ((c > 127) && (c < 2048)) { + l += 2; + } else { + l += 3; + } + } + return l; + } + + self.Buffer = Buffer; +})(); diff --git a/webworker/utf8.conv.js b/webworker/utf8.conv.js new file mode 100644 index 0000000..2bcebaf --- /dev/null +++ b/webworker/utf8.conv.js @@ -0,0 +1,66 @@ +// Copyright 2008 The Closure Library Authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS-IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Reference: https://github.com/google/closure-library + +/** + * Converts a JS string to a UTF-8 "byte" array. + * @param {string} str 16-bit unicode string. + * @return {!Array.} UTF-8 byte array. + */ +/*goog.crypt.*/ var stringToUtf8ByteArray = function(str) { + // TODO(user): Use native implementations if/when available + str = str.replace(/\r\n/g, '\n'); + var out = [], p = 0; + for (var i = 0; i < str.length; i++) { + var c = str.charCodeAt(i); + if (c < 128) { + out[p++] = c; + } else if (c < 2048) { + out[p++] = (c >> 6) | 192; + out[p++] = (c & 63) | 128; + } else { + out[p++] = (c >> 12) | 224; + out[p++] = ((c >> 6) & 63) | 128; + out[p++] = (c & 63) | 128; + } + } + return out; +}; + + +/** + * Converts a UTF-8 byte array to JavaScript's 16-bit Unicode. + * @param {Uint8Array|Int8Array|Array.} bytes UTF-8 byte array. + * @return {string} 16-bit Unicode string. + */ +/*goog.crypt.*/ var utf8ByteArrayToString = function(bytes) { + // TODO(user): Use native implementations if/when available + var out = [], pos = 0, c = 0; + while (pos < bytes.length) { + var c1 = bytes[pos++]; + if (c1 < 128) { + out[c++] = String.fromCharCode(c1); + } else if (c1 > 191 && c1 < 224) { + var c2 = bytes[pos++]; + out[c++] = String.fromCharCode((c1 & 31) << 6 | c2 & 63); + } else { + var c2 = bytes[pos++]; + var c3 = bytes[pos++]; + out[c++] = String.fromCharCode( + (c1 & 15) << 12 | (c2 & 63) << 6 | c3 & 63); + } + } + return out.join(''); +};