From a159bb0b97354561961e057a55f2797cd562b1e1 Mon Sep 17 00:00:00 2001 From: OzzieIsaacs Date: Sun, 17 Sep 2017 15:06:01 +0200 Subject: [PATCH] Code Csmetics kthoom integration --- cps/static/js/archive.js | 651 ++++++++++++------------ cps/static/js/io.js | 931 +++++++++++++++++----------------- cps/static/js/unzip.js | 1020 +++++++++++++++++++------------------- 3 files changed, 1301 insertions(+), 1301 deletions(-) diff --git a/cps/static/js/archive.js b/cps/static/js/archive.js index 1596b74b..2e774ce4 100644 --- a/cps/static/js/archive.js +++ b/cps/static/js/archive.js @@ -13,341 +13,342 @@ bitjs.archive = bitjs.archive || {}; (function() { -// =========================================================================== -// Stolen from Closure because it's the best way to do Java-like inheritance. -bitjs.base = function(me, opt_methodName, var_args) { - var caller = arguments.callee.caller; - if (caller.superClass_) { - // This is a constructor. Call the superclass constructor. - return caller.superClass_.constructor.apply( - me, Array.prototype.slice.call(arguments, 1)); - } - - var args = Array.prototype.slice.call(arguments, 2); - var foundCaller = false; - for (var ctor = me.constructor; - ctor; ctor = ctor.superClass_ && ctor.superClass_.constructor) { - if (ctor.prototype[opt_methodName] === caller) { - foundCaller = true; - } else if (foundCaller) { - return ctor.prototype[opt_methodName].apply(me, args); - } - } - - // If we did not find the caller in the prototype chain, - // then one of two things happened: - // 1) The caller is an instance method. - // 2) This method was not called by the right caller. - if (me[opt_methodName] === caller) { - return me.constructor.prototype[opt_methodName].apply(me, args); - } else { - throw Error( - 'goog.base called from a method of one name ' + - 'to a method of a different name'); - } -}; -bitjs.inherits = function(childCtor, parentCtor) { - /** @constructor */ - function tempCtor() {}; - tempCtor.prototype = parentCtor.prototype; - childCtor.superClass_ = parentCtor.prototype; - childCtor.prototype = new tempCtor(); - childCtor.prototype.constructor = childCtor; -}; -// =========================================================================== - -/** - * An unarchive event. - * - * @param {string} type The event type. - * @constructor - */ -bitjs.archive.UnarchiveEvent = function(type) { - /** - * The event type. - * - * @type {string} - */ - this.type = type; -}; - -/** - * The UnarchiveEvent types. - */ -bitjs.archive.UnarchiveEvent.Type = { - START: 'start', - PROGRESS: 'progress', - EXTRACT: 'extract', - FINISH: 'finish', - INFO: 'info', - ERROR: 'error' -}; - -/** - * Useful for passing info up to the client (for debugging). - * - * @param {string} msg The info message. - */ -bitjs.archive.UnarchiveInfoEvent = function(msg) { - bitjs.base(this, bitjs.archive.UnarchiveEvent.Type.INFO); - - /** - * The information message. - * - * @type {string} - */ - this.msg = msg; -}; -bitjs.inherits(bitjs.archive.UnarchiveInfoEvent, bitjs.archive.UnarchiveEvent); - -/** - * An unrecoverable error has occured. - * - * @param {string} msg The error message. - */ -bitjs.archive.UnarchiveErrorEvent = function(msg) { - bitjs.base(this, bitjs.archive.UnarchiveEvent.Type.ERROR); - - /** - * The information message. - * - * @type {string} - */ - this.msg = msg; -}; -bitjs.inherits(bitjs.archive.UnarchiveErrorEvent, bitjs.archive.UnarchiveEvent); - -/** - * Start event. - * - * @param {string} msg The info message. - */ -bitjs.archive.UnarchiveStartEvent = function() { - bitjs.base(this, bitjs.archive.UnarchiveEvent.Type.START); -}; -bitjs.inherits(bitjs.archive.UnarchiveStartEvent, bitjs.archive.UnarchiveEvent); - -/** - * Finish event. - * - * @param {string} msg The info message. - */ -bitjs.archive.UnarchiveFinishEvent = function() { - bitjs.base(this, bitjs.archive.UnarchiveEvent.Type.FINISH); -}; -bitjs.inherits(bitjs.archive.UnarchiveFinishEvent, bitjs.archive.UnarchiveEvent); - -/** - * Progress event. - */ -bitjs.archive.UnarchiveProgressEvent = function( - currentFilename, - currentFileNumber, - currentBytesUnarchivedInFile, - currentBytesUnarchived, - totalUncompressedBytesInArchive, - totalFilesInArchive) { - bitjs.base(this, bitjs.archive.UnarchiveEvent.Type.PROGRESS); - - this.currentFilename = currentFilename; - this.currentFileNumber = currentFileNumber; - this.currentBytesUnarchivedInFile = currentBytesUnarchivedInFile; - this.totalFilesInArchive = totalFilesInArchive; - this.currentBytesUnarchived = currentBytesUnarchived; - this.totalUncompressedBytesInArchive = totalUncompressedBytesInArchive; -}; -bitjs.inherits(bitjs.archive.UnarchiveProgressEvent, bitjs.archive.UnarchiveEvent); - -/** - * All extracted files returned by an Unarchiver will implement - * the following interface: - * - * interface UnarchivedFile { - * string filename - * TypedArray fileData - * } - * - */ - -/** - * Extract event. - */ -bitjs.archive.UnarchiveExtractEvent = function(unarchivedFile) { - bitjs.base(this, bitjs.archive.UnarchiveEvent.Type.EXTRACT); - - /** - * @type {UnarchivedFile} - */ - this.unarchivedFile = unarchivedFile; -}; -bitjs.inherits(bitjs.archive.UnarchiveExtractEvent, bitjs.archive.UnarchiveEvent); - - -/** - * Base class for all Unarchivers. - * - * @param {ArrayBuffer} arrayBuffer The Array Buffer. - * @param {string} opt_pathToBitJS Optional string for where the BitJS files are located. - * @constructor - */ -bitjs.archive.Unarchiver = function(arrayBuffer, opt_pathToBitJS) { - /** - * The ArrayBuffer object. - * @type {ArrayBuffer} - * @protected - */ - this.ab = arrayBuffer; - - /** - * The path to the BitJS files. - * @type {string} - * @private - */ - this.pathToBitJS_ = opt_pathToBitJS || ''; - - /** - * A map from event type to an array of listeners. - * @type {Map.} - */ - this.listeners_ = {}; - for (var type in bitjs.archive.UnarchiveEvent.Type) { - this.listeners_[bitjs.archive.UnarchiveEvent.Type[type]] = []; - } -}; - -/** - * Private web worker initialized during start(). - * @type {Worker} - * @private - */ -bitjs.archive.Unarchiver.prototype.worker_ = null; - -/** - * This method must be overridden by the subclass to return the script filename. - * @return {string} The script filename. - * @protected. - */ -bitjs.archive.Unarchiver.prototype.getScriptFileName = function() { - throw 'Subclasses of AbstractUnarchiver must overload getScriptFileName()'; -}; - -/** - * Adds an event listener for UnarchiveEvents. - * - * @param {string} Event type. - * @param {function} An event handler function. - */ -bitjs.archive.Unarchiver.prototype.addEventListener = function(type, listener) { - if (type in this.listeners_) { - if (this.listeners_[type].indexOf(listener) == -1) { - this.listeners_[type].push(listener); - } - } -}; + // =========================================================================== + // Stolen from Closure because it's the best way to do Java-like inheritance. + bitjs.base = function(me, opt_methodName, var_args) { + var caller = arguments.callee.caller; + if (caller.superClass_) { + // This is a constructor. Call the superclass constructor. + return caller.superClass_.constructor.apply( + me, Array.prototype.slice.call(arguments, 1)); + } + + var args = Array.prototype.slice.call(arguments, 2); + var foundCaller = false; + for (var ctor = me.constructor; + ctor; ctor = ctor.superClass_ && ctor.superClass_.constructor) { + if (ctor.prototype[opt_methodName] === caller) { + foundCaller = true; + } else if (foundCaller) { + return ctor.prototype[opt_methodName].apply(me, args); + } + } + + // If we did not find the caller in the prototype chain, + // then one of two things happened: + // 1) The caller is an instance method. + // 2) This method was not called by the right caller. + if (me[opt_methodName] === caller) { + return me.constructor.prototype[opt_methodName].apply(me, args); + } else { + throw Error( + 'goog.base called from a method of one name ' + + 'to a method of a different name'); + } + }; + bitjs.inherits = function(childCtor, parentCtor) { + /** @constructor */ + function tempCtor() {}; + tempCtor.prototype = parentCtor.prototype; + childCtor.superClass_ = parentCtor.prototype; + childCtor.prototype = new tempCtor(); + childCtor.prototype.constructor = childCtor; + }; + // =========================================================================== + + /** + * An unarchive event. + * + * @param {string} type The event type. + * @constructor + */ + bitjs.archive.UnarchiveEvent = function(type) { + /** + * The event type. + * + * @type {string} + */ + this.type = type; + }; -/** - * Removes an event listener. - * - * @param {string} Event type. - * @param {EventListener|function} An event listener or handler function. - */ -bitjs.archive.Unarchiver.prototype.removeEventListener = function(type, listener) { - if (type in this.listeners_) { - var index = this.listeners_[type].indexOf(listener); - if (index != -1) { - this.listeners_[type].splice(index, 1); - } - } -}; + /** + * The UnarchiveEvent types. + */ + bitjs.archive.UnarchiveEvent.Type = { + START: 'start', + PROGRESS: 'progress', + EXTRACT: 'extract', + FINISH: 'finish', + INFO: 'info', + ERROR: 'error' + }; -/** - * Receive an event and pass it to the listener functions. - * - * @param {bitjs.archive.UnarchiveEvent} e - * @private - */ -bitjs.archive.Unarchiver.prototype.handleWorkerEvent_ = function(e) { - if ((e instanceof bitjs.archive.UnarchiveEvent || e.type) && - this.listeners_[e.type] instanceof Array) { - this.listeners_[e.type].forEach(function (listener) { listener(e) }); - if (e.type == bitjs.archive.UnarchiveEvent.Type.FINISH) { - this.worker_.terminate(); - } - } else { - console.log(e); - } -}; + /** + * Useful for passing info up to the client (for debugging). + * + * @param {string} msg The info message. + */ + bitjs.archive.UnarchiveInfoEvent = function(msg) { + bitjs.base(this, bitjs.archive.UnarchiveEvent.Type.INFO); + + /** + * The information message. + * + * @type {string} + */ + this.msg = msg; + }; + bitjs.inherits(bitjs.archive.UnarchiveInfoEvent, bitjs.archive.UnarchiveEvent); + + /** + * An unrecoverable error has occured. + * + * @param {string} msg The error message. + */ + bitjs.archive.UnarchiveErrorEvent = function(msg) { + bitjs.base(this, bitjs.archive.UnarchiveEvent.Type.ERROR); + + /** + * The information message. + * + * @type {string} + */ + this.msg = msg; + }; + bitjs.inherits(bitjs.archive.UnarchiveErrorEvent, bitjs.archive.UnarchiveEvent); + + /** + * Start event. + * + * @param {string} msg The info message. + */ + bitjs.archive.UnarchiveStartEvent = function() { + bitjs.base(this, bitjs.archive.UnarchiveEvent.Type.START); + }; + bitjs.inherits(bitjs.archive.UnarchiveStartEvent, bitjs.archive.UnarchiveEvent); + + /** + * Finish event. + * + * @param {string} msg The info message. + */ + bitjs.archive.UnarchiveFinishEvent = function() { + bitjs.base(this, bitjs.archive.UnarchiveEvent.Type.FINISH); + }; + bitjs.inherits(bitjs.archive.UnarchiveFinishEvent, bitjs.archive.UnarchiveEvent); + + /** + * Progress event. + */ + bitjs.archive.UnarchiveProgressEvent = function( + currentFilename, + currentFileNumber, + currentBytesUnarchivedInFile, + currentBytesUnarchived, + totalUncompressedBytesInArchive, + totalFilesInArchive) + { + bitjs.base(this, bitjs.archive.UnarchiveEvent.Type.PROGRESS); + + this.currentFilename = currentFilename; + this.currentFileNumber = currentFileNumber; + this.currentBytesUnarchivedInFile = currentBytesUnarchivedInFile; + this.totalFilesInArchive = totalFilesInArchive; + this.currentBytesUnarchived = currentBytesUnarchived; + this.totalUncompressedBytesInArchive = totalUncompressedBytesInArchive; + }; + bitjs.inherits(bitjs.archive.UnarchiveProgressEvent, bitjs.archive.UnarchiveEvent); + + /** + * All extracted files returned by an Unarchiver will implement + * the following interface: + * + * interface UnarchivedFile { + * string filename + * TypedArray fileData + * } + * + */ + + /** + * Extract event. + */ + bitjs.archive.UnarchiveExtractEvent = function(unarchivedFile) { + bitjs.base(this, bitjs.archive.UnarchiveEvent.Type.EXTRACT); + + /** + * @type {UnarchivedFile} + */ + this.unarchivedFile = unarchivedFile; + }; + bitjs.inherits(bitjs.archive.UnarchiveExtractEvent, bitjs.archive.UnarchiveEvent); + + + /** + * Base class for all Unarchivers. + * + * @param {ArrayBuffer} arrayBuffer The Array Buffer. + * @param {string} opt_pathToBitJS Optional string for where the BitJS files are located. + * @constructor + */ + bitjs.archive.Unarchiver = function(arrayBuffer, opt_pathToBitJS) { + /** + * The ArrayBuffer object. + * @type {ArrayBuffer} + * @protected + */ + this.ab = arrayBuffer; + + /** + * The path to the BitJS files. + * @type {string} + * @private + */ + this.pathToBitJS_ = opt_pathToBitJS || ''; + + /** + * A map from event type to an array of listeners. + * @type {Map.} + */ + this.listeners_ = {}; + for (var type in bitjs.archive.UnarchiveEvent.Type) { + this.listeners_[bitjs.archive.UnarchiveEvent.Type[type]] = []; + } + }; -/** - * Starts the unarchive in a separate Web Worker thread and returns immediately. - */ - bitjs.archive.Unarchiver.prototype.start = function() { - var me = this; - var scriptFileName = this.pathToBitJS_ + this.getScriptFileName(); - if (scriptFileName) { - this.worker_ = new Worker(scriptFileName); - - this.worker_.onerror = function(e) { - console.log('Worker error: message = ' + e.message); - throw e; + /** + * Private web worker initialized during start(). + * @type {Worker} + * @private + */ + bitjs.archive.Unarchiver.prototype.worker_ = null; + + /** + * This method must be overridden by the subclass to return the script filename. + * @return {string} The script filename. + * @protected. + */ + bitjs.archive.Unarchiver.prototype.getScriptFileName = function() { + throw 'Subclasses of AbstractUnarchiver must overload getScriptFileName()'; }; - this.worker_.onmessage = function(e) { - if (typeof e.data == 'string') { - // Just log any strings the workers pump our way. - console.log(e.data); - } else { - // Assume that it is an UnarchiveEvent. Some browsers preserve the 'type' - // so that instanceof UnarchiveEvent returns true, but others do not. - me.handleWorkerEvent_(e.data); - } + /** + * Adds an event listener for UnarchiveEvents. + * + * @param {string} Event type. + * @param {function} An event handler function. + */ + bitjs.archive.Unarchiver.prototype.addEventListener = function(type, listener) { + if (type in this.listeners_) { + if (this.listeners_[type].indexOf(listener) == -1) { + this.listeners_[type].push(listener); + } + } }; - this.worker_.postMessage({file: this.ab}); - } -}; + /** + * Removes an event listener. + * + * @param {string} Event type. + * @param {EventListener|function} An event listener or handler function. + */ + bitjs.archive.Unarchiver.prototype.removeEventListener = function(type, listener) { + if (type in this.listeners_) { + var index = this.listeners_[type].indexOf(listener); + if (index != -1) { + this.listeners_[type].splice(index, 1); + } + } + }; -/** - * Terminates the Web Worker for this Unarchiver and returns immediately. - */ -bitjs.archive.Unarchiver.prototype.stop = function() { - if (this.worker_) { - this.worker_.terminate(); - } -}; + /** + * Receive an event and pass it to the listener functions. + * + * @param {bitjs.archive.UnarchiveEvent} e + * @private + */ + bitjs.archive.Unarchiver.prototype.handleWorkerEvent_ = function(e) { + if ((e instanceof bitjs.archive.UnarchiveEvent || e.type) && + this.listeners_[e.type] instanceof Array) { + this.listeners_[e.type].forEach(function (listener) { listener(e) }); + if (e.type == bitjs.archive.UnarchiveEvent.Type.FINISH) { + this.worker_.terminate(); + } + } else { + console.log(e); + } + }; + /** + * Starts the unarchive in a separate Web Worker thread and returns immediately. + */ + bitjs.archive.Unarchiver.prototype.start = function() { + var me = this; + var scriptFileName = this.pathToBitJS_ + this.getScriptFileName(); + if (scriptFileName) { + this.worker_ = new Worker(scriptFileName); + + this.worker_.onerror = function(e) { + console.log('Worker error: message = ' + e.message); + throw e; + }; + + this.worker_.onmessage = function(e) { + if (typeof e.data == 'string') { + // Just log any strings the workers pump our way. + console.log(e.data); + } else { + // Assume that it is an UnarchiveEvent. Some browsers preserve the 'type' + // so that instanceof UnarchiveEvent returns true, but others do not. + me.handleWorkerEvent_(e.data); + } + }; + + this.worker_.postMessage({file: this.ab}); + } + }; -/** - * Unzipper - * @extends {bitjs.archive.Unarchiver} - * @constructor - */ -bitjs.archive.Unzipper = function(arrayBuffer, opt_pathToBitJS) { - bitjs.base(this, arrayBuffer, opt_pathToBitJS); -}; -bitjs.inherits(bitjs.archive.Unzipper, bitjs.archive.Unarchiver); -bitjs.archive.Unzipper.prototype.getScriptFileName = function() { return 'unzip.js' }; + /** + * Terminates the Web Worker for this Unarchiver and returns immediately. + */ + bitjs.archive.Unarchiver.prototype.stop = function() { + if (this.worker_) { + this.worker_.terminate(); + } + }; -/** - * Unrarrer - * @extends {bitjs.archive.Unarchiver} - * @constructor - */ -bitjs.archive.Unrarrer = function(arrayBuffer, opt_pathToBitJS) { - bitjs.base(this, arrayBuffer, opt_pathToBitJS); -}; -bitjs.inherits(bitjs.archive.Unrarrer, bitjs.archive.Unarchiver); -bitjs.archive.Unrarrer.prototype.getScriptFileName = function() { return 'unrar.js' }; -/** - * Untarrer - * @extends {bitjs.archive.Unarchiver} - * @constructor - */ -bitjs.archive.Untarrer = function(arrayBuffer, opt_pathToBitJS) { - bitjs.base(this, arrayBuffer, opt_pathToBitJS); -}; -bitjs.inherits(bitjs.archive.Untarrer, bitjs.archive.Unarchiver); -bitjs.archive.Untarrer.prototype.getScriptFileName = function() { return 'untar.js' }; + /** + * Unzipper + * @extends {bitjs.archive.Unarchiver} + * @constructor + */ + bitjs.archive.Unzipper = function(arrayBuffer, opt_pathToBitJS) { + bitjs.base(this, arrayBuffer, opt_pathToBitJS); + }; + bitjs.inherits(bitjs.archive.Unzipper, bitjs.archive.Unarchiver); + bitjs.archive.Unzipper.prototype.getScriptFileName = function() { return 'unzip.js' }; + + /** + * Unrarrer + * @extends {bitjs.archive.Unarchiver} + * @constructor + */ + bitjs.archive.Unrarrer = function(arrayBuffer, opt_pathToBitJS) { + bitjs.base(this, arrayBuffer, opt_pathToBitJS); + }; + bitjs.inherits(bitjs.archive.Unrarrer, bitjs.archive.Unarchiver); + bitjs.archive.Unrarrer.prototype.getScriptFileName = function() { return 'unrar.js' }; + + /** + * Untarrer + * @extends {bitjs.archive.Unarchiver} + * @constructor + */ + bitjs.archive.Untarrer = function(arrayBuffer, opt_pathToBitJS) { + bitjs.base(this, arrayBuffer, opt_pathToBitJS); + }; + bitjs.inherits(bitjs.archive.Untarrer, bitjs.archive.Unarchiver); + bitjs.archive.Untarrer.prototype.getScriptFileName = function() { return 'untar.js' }; -})(); \ No newline at end of file +})(); diff --git a/cps/static/js/io.js b/cps/static/js/io.js index 6eabfe9a..6bc03936 100644 --- a/cps/static/js/io.js +++ b/cps/static/js/io.js @@ -14,470 +14,469 @@ bitjs.io = bitjs.io || {}; (function() { -// mask for getting the Nth bit (zero-based) -bitjs.BIT = [ 0x01, 0x02, 0x04, 0x08, - 0x10, 0x20, 0x40, 0x80, - 0x100, 0x200, 0x400, 0x800, - 0x1000, 0x2000, 0x4000, 0x8000]; - -// mask for getting N number of bits (0-8) -var BITMASK = [0, 0x01, 0x03, 0x07, 0x0F, 0x1F, 0x3F, 0x7F, 0xFF ]; - - -/** - * This bit stream peeks and consumes bits out of a binary stream. - * - * @param {ArrayBuffer} ab An ArrayBuffer object or a Uint8Array. - * @param {boolean} rtl Whether the stream reads bits from the byte starting - * from bit 7 to 0 (true) or bit 0 to 7 (false). - * @param {Number} opt_offset The offset into the ArrayBuffer - * @param {Number} opt_length The length of this BitStream - */ -bitjs.io.BitStream = function(ab, rtl, opt_offset, opt_length) { - if (!ab || !ab.toString || ab.toString() !== "[object ArrayBuffer]") { - throw "Error! BitArray constructed with an invalid ArrayBuffer object"; - } - - var offset = opt_offset || 0; - var length = opt_length || ab.byteLength; - this.bytes = new Uint8Array(ab, offset, length); - this.bytePtr = 0; // tracks which byte we are on - this.bitPtr = 0; // tracks which bit we are on (can have values 0 through 7) - this.peekBits = rtl ? this.peekBits_rtl : this.peekBits_ltr; -}; - - -/** - * byte0 byte1 byte2 byte3 - * 7......0 | 7......0 | 7......0 | 7......0 - * - * The bit pointer starts at bit0 of byte0 and moves left until it reaches - * bit7 of byte0, then jumps to bit0 of byte1, etc. - * @param {number} n The number of bits to peek. - * @param {boolean=} movePointers Whether to move the pointer, defaults false. - * @return {number} The peeked bits, as an unsigned number. - */ -bitjs.io.BitStream.prototype.peekBits_ltr = function(n, movePointers) { - if (n <= 0 || typeof n != typeof 1) { - return 0; - } - - var movePointers = movePointers || false, - bytePtr = this.bytePtr, - bitPtr = this.bitPtr, - result = 0, - bitsIn = 0, - bytes = this.bytes; - - // keep going until we have no more bits left to peek at - // TODO: Consider putting all bits from bytes we will need into a variable and then - // shifting/masking it to just extract the bits we want. - // This could be considerably faster when reading more than 3 or 4 bits at a time. - while (n > 0) { - if (bytePtr >= bytes.length) { - throw "Error! Overflowed the bit stream! n=" + n + ", bytePtr=" + bytePtr + ", bytes.length=" + - bytes.length + ", bitPtr=" + bitPtr; - return -1; - } - - var numBitsLeftInThisByte = (8 - bitPtr); - if (n >= numBitsLeftInThisByte) { - var mask = (BITMASK[numBitsLeftInThisByte] << bitPtr); - result |= (((bytes[bytePtr] & mask) >> bitPtr) << bitsIn); - - bytePtr++; - bitPtr = 0; - bitsIn += numBitsLeftInThisByte; - n -= numBitsLeftInThisByte; - } - else { - var mask = (BITMASK[n] << bitPtr); - result |= (((bytes[bytePtr] & mask) >> bitPtr) << bitsIn); - - bitPtr += n; - bitsIn += n; - n = 0; - } - } - - if (movePointers) { - this.bitPtr = bitPtr; - this.bytePtr = bytePtr; - } - - return result; -}; - - -/** - * byte0 byte1 byte2 byte3 - * 7......0 | 7......0 | 7......0 | 7......0 - * - * The bit pointer starts at bit7 of byte0 and moves right until it reaches - * bit0 of byte0, then goes to bit7 of byte1, etc. - * @param {number} n The number of bits to peek. - * @param {boolean=} movePointers Whether to move the pointer, defaults false. - * @return {number} The peeked bits, as an unsigned number. - */ -bitjs.io.BitStream.prototype.peekBits_rtl = function(n, movePointers) { - if (n <= 0 || typeof n != typeof 1) { - return 0; - } - - var movePointers = movePointers || false, - bytePtr = this.bytePtr, - bitPtr = this.bitPtr, - result = 0, - bytes = this.bytes; - - // keep going until we have no more bits left to peek at - // TODO: Consider putting all bits from bytes we will need into a variable and then - // shifting/masking it to just extract the bits we want. - // This could be considerably faster when reading more than 3 or 4 bits at a time. - while (n > 0) { - - if (bytePtr >= bytes.length) { - throw "Error! Overflowed the bit stream! n=" + n + ", bytePtr=" + bytePtr + ", bytes.length=" + - bytes.length + ", bitPtr=" + bitPtr; - return -1; - } - - var numBitsLeftInThisByte = (8 - bitPtr); - if (n >= numBitsLeftInThisByte) { - result <<= numBitsLeftInThisByte; - result |= (BITMASK[numBitsLeftInThisByte] & bytes[bytePtr]); - bytePtr++; - bitPtr = 0; - n -= numBitsLeftInThisByte; - } - else { - result <<= n; - result |= ((bytes[bytePtr] & (BITMASK[n] << (8 - n - bitPtr))) >> (8 - n - bitPtr)); - - bitPtr += n; - n = 0; - } - } - - if (movePointers) { - this.bitPtr = bitPtr; - this.bytePtr = bytePtr; - } - - return result; -}; - - -/** - * Some voodoo magic. - */ -bitjs.io.BitStream.prototype.getBits = function() { - return (((((this.bytes[this.bytePtr] & 0xff) << 16) + - ((this.bytes[this.bytePtr+1] & 0xff) << 8) + - ((this.bytes[this.bytePtr+2] & 0xff))) >>> (8-this.bitPtr)) & 0xffff); -}; - - -/** - * Reads n bits out of the stream, consuming them (moving the bit pointer). - * @param {number} n The number of bits to read. - * @return {number} The read bits, as an unsigned number. - */ -bitjs.io.BitStream.prototype.readBits = function(n) { - return this.peekBits(n, true); -}; - - -/** - * This returns n bytes as a sub-array, advancing the pointer if movePointers - * is true. Only use this for uncompressed blocks as this throws away remaining - * bits in the current byte. - * @param {number} n The number of bytes to peek. - * @param {boolean=} movePointers Whether to move the pointer, defaults false. - * @return {Uint8Array} The subarray. - */ -bitjs.io.BitStream.prototype.peekBytes = function(n, movePointers) { - if (n <= 0 || typeof n != typeof 1) { - return 0; - } - - // from http://tools.ietf.org/html/rfc1951#page-11 - // "Any bits of input up to the next byte boundary are ignored." - while (this.bitPtr != 0) { - this.readBits(1); - } - - var movePointers = movePointers || false; - var bytePtr = this.bytePtr, - bitPtr = this.bitPtr; - - var result = this.bytes.subarray(bytePtr, bytePtr + n); - - if (movePointers) { - this.bytePtr += n; - } - - return result; -}; - - -/** - * @param {number} n The number of bytes to read. - * @return {Uint8Array} The subarray. - */ -bitjs.io.BitStream.prototype.readBytes = function(n) { - return this.peekBytes(n, true); -}; - - -/** - * This object allows you to peek and consume bytes as numbers and strings - * out of an ArrayBuffer. In this buffer, everything must be byte-aligned. - * - * @param {ArrayBuffer} ab The ArrayBuffer object. - * @param {number=} opt_offset The offset into the ArrayBuffer - * @param {number=} opt_length The length of this BitStream - * @constructor - */ -bitjs.io.ByteStream = function(ab, opt_offset, opt_length) { - var offset = opt_offset || 0; - var length = opt_length || ab.byteLength; - this.bytes = new Uint8Array(ab, offset, length); - this.ptr = 0; -}; - - -/** - * Peeks at the next n bytes as an unsigned number but does not advance the - * pointer - * TODO: This apparently cannot read more than 4 bytes as a number? - * @param {number} n The number of bytes to peek at. - * @return {number} The n bytes interpreted as an unsigned number. - */ -bitjs.io.ByteStream.prototype.peekNumber = function(n) { - // TODO: return error if n would go past the end of the stream? - if (n <= 0 || typeof n != typeof 1) - return -1; - - var result = 0; - // read from last byte to first byte and roll them in - var curByte = this.ptr + n - 1; - while (curByte >= this.ptr) { - result <<= 8; - result |= this.bytes[curByte]; - --curByte; - } - return result; -}; - - -/** - * Returns the next n bytes as an unsigned number (or -1 on error) - * and advances the stream pointer n bytes. - * @param {number} n The number of bytes to read. - * @return {number} The n bytes interpreted as an unsigned number. - */ -bitjs.io.ByteStream.prototype.readNumber = function(n) { - var num = this.peekNumber( n ); - this.ptr += n; - return num; -}; - - -/** - * Returns the next n bytes as a signed number but does not advance the - * pointer. - * @param {number} n The number of bytes to read. - * @return {number} The bytes interpreted as a signed number. - */ -bitjs.io.ByteStream.prototype.peekSignedNumber = function(n) { - var num = this.peekNumber(n); - var HALF = Math.pow(2, (n * 8) - 1); - var FULL = HALF * 2; - - if (num >= HALF) num -= FULL; - - return num; -}; - - -/** - * Returns the next n bytes as a signed number and advances the stream pointer. - * @param {number} n The number of bytes to read. - * @return {number} The bytes interpreted as a signed number. - */ -bitjs.io.ByteStream.prototype.readSignedNumber = function(n) { - var num = this.peekSignedNumber(n); - this.ptr += n; - return num; -}; - - -/** - * This returns n bytes as a sub-array, advancing the pointer if movePointers - * is true. - * @param {number} n The number of bytes to read. - * @param {boolean} movePointers Whether to move the pointers. - * @return {Uint8Array} The subarray. - */ -bitjs.io.ByteStream.prototype.peekBytes = function(n, movePointers) { - if (n <= 0 || typeof n != typeof 1) { - return null; - } - - var result = this.bytes.subarray(this.ptr, this.ptr + n); - - if (movePointers) { - this.ptr += n; - } - - return result; -}; - - -/** - * Reads the next n bytes as a sub-array. - * @param {number} n The number of bytes to read. - * @return {Uint8Array} The subarray. - */ -bitjs.io.ByteStream.prototype.readBytes = function(n) { - return this.peekBytes(n, true); -}; - - -/** - * Peeks at the next n bytes as a string but does not advance the pointer. - * @param {number} n The number of bytes to peek at. - * @return {string} The next n bytes as a string. - */ -bitjs.io.ByteStream.prototype.peekString = function(n) { - if (n <= 0 || typeof n != typeof 1) { - return ""; - } - - var result = ""; - for (var p = this.ptr, end = this.ptr + n; p < end; ++p) { - result += String.fromCharCode(this.bytes[p]); - } - return result; -}; - - -/** - * Returns the next n bytes as an ASCII string and advances the stream pointer - * n bytes. - * @param {number} n The number of bytes to read. - * @return {string} The next n bytes as a string. - */ -bitjs.io.ByteStream.prototype.readString = function(n) { - var strToReturn = this.peekString(n); - this.ptr += n; - return strToReturn; -}; - - -/** - * A write-only Byte buffer which uses a Uint8 Typed Array as a backing store. - * @param {number} numBytes The number of bytes to allocate. - * @constructor - */ -bitjs.io.ByteBuffer = function(numBytes) { - if (typeof numBytes != typeof 1 || numBytes <= 0) { - throw "Error! ByteBuffer initialized with '" + numBytes + "'"; - } - this.data = new Uint8Array(numBytes); - this.ptr = 0; -}; - - -/** - * @param {number} b The byte to insert. - */ -bitjs.io.ByteBuffer.prototype.insertByte = function(b) { - // TODO: throw if byte is invalid? - this.data[this.ptr++] = b; -}; - - -/** - * @param {Array.|Uint8Array|Int8Array} bytes The bytes to insert. - */ -bitjs.io.ByteBuffer.prototype.insertBytes = function(bytes) { - // TODO: throw if bytes is invalid? - this.data.set(bytes, this.ptr); - this.ptr += bytes.length; -}; - - -/** - * Writes an unsigned number into the next n bytes. If the number is too large - * to fit into n bytes or is negative, an error is thrown. - * @param {number} num The unsigned number to write. - * @param {number} numBytes The number of bytes to write the number into. - */ -bitjs.io.ByteBuffer.prototype.writeNumber = function(num, numBytes) { - if (numBytes < 1) { - throw 'Trying to write into too few bytes: ' + numBytes; - } - if (num < 0) { - throw 'Trying to write a negative number (' + num + - ') as an unsigned number to an ArrayBuffer'; - } - if (num > (Math.pow(2, numBytes * 8) - 1)) { - throw 'Trying to write ' + num + ' into only ' + numBytes + ' bytes'; - } - - // Roll 8-bits at a time into an array of bytes. - var bytes = []; - while (numBytes-- > 0) { - var eightBits = num & 255; - bytes.push(eightBits); - num >>= 8; - } - - this.insertBytes(bytes); -}; - - -/** - * Writes a signed number into the next n bytes. If the number is too large - * to fit into n bytes, an error is thrown. - * @param {number} num The signed number to write. - * @param {number} numBytes The number of bytes to write the number into. - */ -bitjs.io.ByteBuffer.prototype.writeSignedNumber = function(num, numBytes) { - if (numBytes < 1) { - throw 'Trying to write into too few bytes: ' + numBytes; - } - - var HALF = Math.pow(2, (numBytes * 8) - 1); - if (num >= HALF || num < -HALF) { - throw 'Trying to write ' + num + ' into only ' + numBytes + ' bytes'; - } - - // Roll 8-bits at a time into an array of bytes. - var bytes = []; - while (numBytes-- > 0) { - var eightBits = num & 255; - bytes.push(eightBits); - num >>= 8; - } - - this.insertBytes(bytes); -}; - - -/** - * @param {string} str The ASCII string to write. - */ -bitjs.io.ByteBuffer.prototype.writeASCIIString = function(str) { - for (var i = 0; i < str.length; ++i) { - var curByte = str.charCodeAt(i); - if (curByte < 0 || curByte > 255) { - throw 'Trying to write a non-ASCII string!'; - } - this.insertByte(curByte); - } -}; - + // mask for getting the Nth bit (zero-based) + bitjs.BIT = [ 0x01, 0x02, 0x04, 0x08, + 0x10, 0x20, 0x40, 0x80, + 0x100, 0x200, 0x400, 0x800, + 0x1000, 0x2000, 0x4000, 0x8000]; + + // mask for getting N number of bits (0-8) + var BITMASK = [0, 0x01, 0x03, 0x07, 0x0F, 0x1F, 0x3F, 0x7F, 0xFF ]; + + + /** + * This bit stream peeks and consumes bits out of a binary stream. + * + * @param {ArrayBuffer} ab An ArrayBuffer object or a Uint8Array. + * @param {boolean} rtl Whether the stream reads bits from the byte starting + * from bit 7 to 0 (true) or bit 0 to 7 (false). + * @param {Number} opt_offset The offset into the ArrayBuffer + * @param {Number} opt_length The length of this BitStream + */ + bitjs.io.BitStream = function(ab, rtl, opt_offset, opt_length) { + if (!ab || !ab.toString || ab.toString() !== "[object ArrayBuffer]") { + throw "Error! BitArray constructed with an invalid ArrayBuffer object"; + } + + var offset = opt_offset || 0; + var length = opt_length || ab.byteLength; + this.bytes = new Uint8Array(ab, offset, length); + this.bytePtr = 0; // tracks which byte we are on + this.bitPtr = 0; // tracks which bit we are on (can have values 0 through 7) + this.peekBits = rtl ? this.peekBits_rtl : this.peekBits_ltr; + }; + + + /** + * byte0 byte1 byte2 byte3 + * 7......0 | 7......0 | 7......0 | 7......0 + * + * The bit pointer starts at bit0 of byte0 and moves left until it reaches + * bit7 of byte0, then jumps to bit0 of byte1, etc. + * @param {number} n The number of bits to peek. + * @param {boolean=} movePointers Whether to move the pointer, defaults false. + * @return {number} The peeked bits, as an unsigned number. + */ + bitjs.io.BitStream.prototype.peekBits_ltr = function(n, movePointers) { + if (n <= 0 || typeof n != typeof 1) { + return 0; + } + + var movePointers = movePointers || false, + bytePtr = this.bytePtr, + bitPtr = this.bitPtr, + result = 0, + bitsIn = 0, + bytes = this.bytes; + + // keep going until we have no more bits left to peek at + // TODO: Consider putting all bits from bytes we will need into a variable and then + // shifting/masking it to just extract the bits we want. + // This could be considerably faster when reading more than 3 or 4 bits at a time. + while (n > 0) { + if (bytePtr >= bytes.length) { + throw "Error! Overflowed the bit stream! n=" + n + ", bytePtr=" + bytePtr + ", bytes.length=" + + bytes.length + ", bitPtr=" + bitPtr; + return -1; + } + + var numBitsLeftInThisByte = (8 - bitPtr); + if (n >= numBitsLeftInThisByte) { + var mask = (BITMASK[numBitsLeftInThisByte] << bitPtr); + result |= (((bytes[bytePtr] & mask) >> bitPtr) << bitsIn); + + bytePtr++; + bitPtr = 0; + bitsIn += numBitsLeftInThisByte; + n -= numBitsLeftInThisByte; + } + else { + var mask = (BITMASK[n] << bitPtr); + result |= (((bytes[bytePtr] & mask) >> bitPtr) << bitsIn); + + bitPtr += n; + bitsIn += n; + n = 0; + } + } + + if (movePointers) { + this.bitPtr = bitPtr; + this.bytePtr = bytePtr; + } + + return result; + }; + + + /** + * byte0 byte1 byte2 byte3 + * 7......0 | 7......0 | 7......0 | 7......0 + * + * The bit pointer starts at bit7 of byte0 and moves right until it reaches + * bit0 of byte0, then goes to bit7 of byte1, etc. + * @param {number} n The number of bits to peek. + * @param {boolean=} movePointers Whether to move the pointer, defaults false. + * @return {number} The peeked bits, as an unsigned number. + */ + bitjs.io.BitStream.prototype.peekBits_rtl = function(n, movePointers) { + if (n <= 0 || typeof n != typeof 1) { + return 0; + } + + var movePointers = movePointers || false, + bytePtr = this.bytePtr, + bitPtr = this.bitPtr, + result = 0, + bytes = this.bytes; + + // keep going until we have no more bits left to peek at + // TODO: Consider putting all bits from bytes we will need into a variable and then + // shifting/masking it to just extract the bits we want. + // This could be considerably faster when reading more than 3 or 4 bits at a time. + while (n > 0) { + + if (bytePtr >= bytes.length) { + throw "Error! Overflowed the bit stream! n=" + n + ", bytePtr=" + bytePtr + ", bytes.length=" + + bytes.length + ", bitPtr=" + bitPtr; + return -1; + } + + var numBitsLeftInThisByte = (8 - bitPtr); + if (n >= numBitsLeftInThisByte) { + result <<= numBitsLeftInThisByte; + result |= (BITMASK[numBitsLeftInThisByte] & bytes[bytePtr]); + bytePtr++; + bitPtr = 0; + n -= numBitsLeftInThisByte; + } + else { + result <<= n; + result |= ((bytes[bytePtr] & (BITMASK[n] << (8 - n - bitPtr))) >> (8 - n - bitPtr)); + + bitPtr += n; + n = 0; + } + } + + if (movePointers) { + this.bitPtr = bitPtr; + this.bytePtr = bytePtr; + } + + return result; + }; + + + /** + * Some voodoo magic. + */ + bitjs.io.BitStream.prototype.getBits = function() { + return (((((this.bytes[this.bytePtr] & 0xff) << 16) + + ((this.bytes[this.bytePtr+1] & 0xff) << 8) + + ((this.bytes[this.bytePtr+2] & 0xff))) >>> (8-this.bitPtr)) & 0xffff); + }; + + + /** + * Reads n bits out of the stream, consuming them (moving the bit pointer). + * @param {number} n The number of bits to read. + * @return {number} The read bits, as an unsigned number. + */ + bitjs.io.BitStream.prototype.readBits = function(n) { + return this.peekBits(n, true); + }; + + + /** + * This returns n bytes as a sub-array, advancing the pointer if movePointers + * is true. Only use this for uncompressed blocks as this throws away remaining + * bits in the current byte. + * @param {number} n The number of bytes to peek. + * @param {boolean=} movePointers Whether to move the pointer, defaults false. + * @return {Uint8Array} The subarray. + */ + bitjs.io.BitStream.prototype.peekBytes = function(n, movePointers) { + if (n <= 0 || typeof n != typeof 1) { + return 0; + } + + // from http://tools.ietf.org/html/rfc1951#page-11 + // "Any bits of input up to the next byte boundary are ignored." + while (this.bitPtr != 0) { + this.readBits(1); + } + + var movePointers = movePointers || false; + var bytePtr = this.bytePtr, + bitPtr = this.bitPtr; + + var result = this.bytes.subarray(bytePtr, bytePtr + n); + + if (movePointers) { + this.bytePtr += n; + } + + return result; + }; + + + /** + * @param {number} n The number of bytes to read. + * @return {Uint8Array} The subarray. + */ + bitjs.io.BitStream.prototype.readBytes = function(n) { + return this.peekBytes(n, true); + }; + + + /** + * This object allows you to peek and consume bytes as numbers and strings + * out of an ArrayBuffer. In this buffer, everything must be byte-aligned. + * + * @param {ArrayBuffer} ab The ArrayBuffer object. + * @param {number=} opt_offset The offset into the ArrayBuffer + * @param {number=} opt_length The length of this BitStream + * @constructor + */ + bitjs.io.ByteStream = function(ab, opt_offset, opt_length) { + var offset = opt_offset || 0; + var length = opt_length || ab.byteLength; + this.bytes = new Uint8Array(ab, offset, length); + this.ptr = 0; + }; + + + /** + * Peeks at the next n bytes as an unsigned number but does not advance the + * pointer + * TODO: This apparently cannot read more than 4 bytes as a number? + * @param {number} n The number of bytes to peek at. + * @return {number} The n bytes interpreted as an unsigned number. + */ + bitjs.io.ByteStream.prototype.peekNumber = function(n) { + // TODO: return error if n would go past the end of the stream? + if (n <= 0 || typeof n != typeof 1) + return -1; + + var result = 0; + // read from last byte to first byte and roll them in + var curByte = this.ptr + n - 1; + while (curByte >= this.ptr) { + result <<= 8; + result |= this.bytes[curByte]; + --curByte; + } + return result; + }; + + + /** + * Returns the next n bytes as an unsigned number (or -1 on error) + * and advances the stream pointer n bytes. + * @param {number} n The number of bytes to read. + * @return {number} The n bytes interpreted as an unsigned number. + */ + bitjs.io.ByteStream.prototype.readNumber = function(n) { + var num = this.peekNumber( n ); + this.ptr += n; + return num; + }; + + + /** + * Returns the next n bytes as a signed number but does not advance the + * pointer. + * @param {number} n The number of bytes to read. + * @return {number} The bytes interpreted as a signed number. + */ + bitjs.io.ByteStream.prototype.peekSignedNumber = function(n) { + var num = this.peekNumber(n); + var HALF = Math.pow(2, (n * 8) - 1); + var FULL = HALF * 2; + + if (num >= HALF) num -= FULL; + + return num; + }; + + + /** + * Returns the next n bytes as a signed number and advances the stream pointer. + * @param {number} n The number of bytes to read. + * @return {number} The bytes interpreted as a signed number. + */ + bitjs.io.ByteStream.prototype.readSignedNumber = function(n) { + var num = this.peekSignedNumber(n); + this.ptr += n; + return num; + }; + + + /** + * This returns n bytes as a sub-array, advancing the pointer if movePointers + * is true. + * @param {number} n The number of bytes to read. + * @param {boolean} movePointers Whether to move the pointers. + * @return {Uint8Array} The subarray. + */ + bitjs.io.ByteStream.prototype.peekBytes = function(n, movePointers) { + if (n <= 0 || typeof n != typeof 1) { + return null; + } + + var result = this.bytes.subarray(this.ptr, this.ptr + n); + + if (movePointers) { + this.ptr += n; + } + + return result; + }; + + + /** + * Reads the next n bytes as a sub-array. + * @param {number} n The number of bytes to read. + * @return {Uint8Array} The subarray. + */ + bitjs.io.ByteStream.prototype.readBytes = function(n) { + return this.peekBytes(n, true); + }; + + + /** + * Peeks at the next n bytes as a string but does not advance the pointer. + * @param {number} n The number of bytes to peek at. + * @return {string} The next n bytes as a string. + */ + bitjs.io.ByteStream.prototype.peekString = function(n) { + if (n <= 0 || typeof n != typeof 1) { + return ""; + } + + var result = ""; + for (var p = this.ptr, end = this.ptr + n; p < end; ++p) { + result += String.fromCharCode(this.bytes[p]); + } + return result; + }; + + + /** + * Returns the next n bytes as an ASCII string and advances the stream pointer + * n bytes. + * @param {number} n The number of bytes to read. + * @return {string} The next n bytes as a string. + */ + bitjs.io.ByteStream.prototype.readString = function(n) { + var strToReturn = this.peekString(n); + this.ptr += n; + return strToReturn; + }; + + + /** + * A write-only Byte buffer which uses a Uint8 Typed Array as a backing store. + * @param {number} numBytes The number of bytes to allocate. + * @constructor + */ + bitjs.io.ByteBuffer = function(numBytes) { + if (typeof numBytes != typeof 1 || numBytes <= 0) { + throw "Error! ByteBuffer initialized with '" + numBytes + "'"; + } + this.data = new Uint8Array(numBytes); + this.ptr = 0; + }; + + + /** + * @param {number} b The byte to insert. + */ + bitjs.io.ByteBuffer.prototype.insertByte = function(b) { + // TODO: throw if byte is invalid? + this.data[this.ptr++] = b; + }; + + + /** + * @param {Array.|Uint8Array|Int8Array} bytes The bytes to insert. + */ + bitjs.io.ByteBuffer.prototype.insertBytes = function(bytes) { + // TODO: throw if bytes is invalid? + this.data.set(bytes, this.ptr); + this.ptr += bytes.length; + }; + + + /** + * Writes an unsigned number into the next n bytes. If the number is too large + * to fit into n bytes or is negative, an error is thrown. + * @param {number} num The unsigned number to write. + * @param {number} numBytes The number of bytes to write the number into. + */ + bitjs.io.ByteBuffer.prototype.writeNumber = function(num, numBytes) { + if (numBytes < 1) { + throw 'Trying to write into too few bytes: ' + numBytes; + } + if (num < 0) { + throw 'Trying to write a negative number (' + num + + ') as an unsigned number to an ArrayBuffer'; + } + if (num > (Math.pow(2, numBytes * 8) - 1)) { + throw 'Trying to write ' + num + ' into only ' + numBytes + ' bytes'; + } + + // Roll 8-bits at a time into an array of bytes. + var bytes = []; + while (numBytes-- > 0) { + var eightBits = num & 255; + bytes.push(eightBits); + num >>= 8; + } + + this.insertBytes(bytes); + }; + + + /** + * Writes a signed number into the next n bytes. If the number is too large + * to fit into n bytes, an error is thrown. + * @param {number} num The signed number to write. + * @param {number} numBytes The number of bytes to write the number into. + */ + bitjs.io.ByteBuffer.prototype.writeSignedNumber = function(num, numBytes) { + if (numBytes < 1) { + throw 'Trying to write into too few bytes: ' + numBytes; + } + + var HALF = Math.pow(2, (numBytes * 8) - 1); + if (num >= HALF || num < -HALF) { + throw 'Trying to write ' + num + ' into only ' + numBytes + ' bytes'; + } + + // Roll 8-bits at a time into an array of bytes. + var bytes = []; + while (numBytes-- > 0) { + var eightBits = num & 255; + bytes.push(eightBits); + num >>= 8; + } + + this.insertBytes(bytes); + }; + + + /** + * @param {string} str The ASCII string to write. + */ + bitjs.io.ByteBuffer.prototype.writeASCIIString = function(str) { + for (var i = 0; i < str.length; ++i) { + var curByte = str.charCodeAt(i); + if (curByte < 0 || curByte > 255) { + throw 'Trying to write a non-ASCII string!'; + } + this.insertByte(curByte); + } + }; })(); diff --git a/cps/static/js/unzip.js b/cps/static/js/unzip.js index f81d9810..9077a5ed 100644 --- a/cps/static/js/unzip.js +++ b/cps/static/js/unzip.js @@ -24,19 +24,19 @@ var totalFilesInArchive = 0; // Helper functions. var info = function(str) { - postMessage(new bitjs.archive.UnarchiveInfoEvent(str)); + postMessage(new bitjs.archive.UnarchiveInfoEvent(str)); }; var err = function(str) { - postMessage(new bitjs.archive.UnarchiveErrorEvent(str)); + postMessage(new bitjs.archive.UnarchiveErrorEvent(str)); }; var postProgress = function() { - postMessage(new bitjs.archive.UnarchiveProgressEvent( - currentFilename, - currentFileNumber, - currentBytesUnarchivedInFile, - currentBytesUnarchived, - totalUncompressedBytesInArchive, - totalFilesInArchive)); + postMessage(new bitjs.archive.UnarchiveProgressEvent( + currentFilename, + currentFileNumber, + currentBytesUnarchivedInFile, + currentBytesUnarchived, + totalUncompressedBytesInArchive, + totalFilesInArchive)); }; var zLocalFileHeaderSignature = 0x04034b50; @@ -48,82 +48,82 @@ var zEndOfCentralDirLocatorSignature = 0x07064b50; // takes a ByteStream and parses out the local file information var ZipLocalFile = function(bstream) { - if (typeof bstream != typeof {} || !bstream.readNumber || typeof bstream.readNumber != typeof function(){}) { - return null; - } - - bstream.readNumber(4); // swallow signature - this.version = bstream.readNumber(2); - this.generalPurpose = bstream.readNumber(2); - this.compressionMethod = bstream.readNumber(2); - this.lastModFileTime = bstream.readNumber(2); - this.lastModFileDate = bstream.readNumber(2); - this.crc32 = bstream.readNumber(4); - this.compressedSize = bstream.readNumber(4); - this.uncompressedSize = bstream.readNumber(4); - this.fileNameLength = bstream.readNumber(2); - this.extraFieldLength = bstream.readNumber(2); - - this.filename = null; - if (this.fileNameLength > 0) { - this.filename = bstream.readString(this.fileNameLength); - } - - info("Zip Local File Header:"); - info(" version=" + this.version); - info(" general purpose=" + this.generalPurpose); - info(" compression method=" + this.compressionMethod); - info(" last mod file time=" + this.lastModFileTime); - info(" last mod file date=" + this.lastModFileDate); - info(" crc32=" + this.crc32); - info(" compressed size=" + this.compressedSize); - info(" uncompressed size=" + this.uncompressedSize); - info(" file name length=" + this.fileNameLength); - info(" extra field length=" + this.extraFieldLength); - info(" filename = '" + this.filename + "'"); - - this.extraField = null; - if (this.extraFieldLength > 0) { - this.extraField = bstream.readString(this.extraFieldLength); - info(" extra field=" + this.extraField); - } - - // read in the compressed data - this.fileData = null; - if (this.compressedSize > 0) { - this.fileData = new Uint8Array(bstream.bytes.buffer, bstream.ptr, this.compressedSize); - bstream.ptr += this.compressedSize; - } - - // TODO: deal with data descriptor if present (we currently assume no data descriptor!) - // "This descriptor exists only if bit 3 of the general purpose bit flag is set" - // But how do you figure out how big the file data is if you don't know the compressedSize - // from the header?!? - if ((this.generalPurpose & bitjs.BIT[3]) != 0) { - this.crc32 = bstream.readNumber(4); - this.compressedSize = bstream.readNumber(4); - this.uncompressedSize = bstream.readNumber(4); - } + if (typeof bstream != typeof {} || !bstream.readNumber || typeof bstream.readNumber != typeof function(){}) { + return null; + } + + bstream.readNumber(4); // swallow signature + this.version = bstream.readNumber(2); + this.generalPurpose = bstream.readNumber(2); + this.compressionMethod = bstream.readNumber(2); + this.lastModFileTime = bstream.readNumber(2); + this.lastModFileDate = bstream.readNumber(2); + this.crc32 = bstream.readNumber(4); + this.compressedSize = bstream.readNumber(4); + this.uncompressedSize = bstream.readNumber(4); + this.fileNameLength = bstream.readNumber(2); + this.extraFieldLength = bstream.readNumber(2); + + this.filename = null; + if (this.fileNameLength > 0) { + this.filename = bstream.readString(this.fileNameLength); + } + + info("Zip Local File Header:"); + info(" version=" + this.version); + info(" general purpose=" + this.generalPurpose); + info(" compression method=" + this.compressionMethod); + info(" last mod file time=" + this.lastModFileTime); + info(" last mod file date=" + this.lastModFileDate); + info(" crc32=" + this.crc32); + info(" compressed size=" + this.compressedSize); + info(" uncompressed size=" + this.uncompressedSize); + info(" file name length=" + this.fileNameLength); + info(" extra field length=" + this.extraFieldLength); + info(" filename = '" + this.filename + "'"); + + this.extraField = null; + if (this.extraFieldLength > 0) { + this.extraField = bstream.readString(this.extraFieldLength); + info(" extra field=" + this.extraField); + } + + // read in the compressed data + this.fileData = null; + if (this.compressedSize > 0) { + this.fileData = new Uint8Array(bstream.bytes.buffer, bstream.ptr, this.compressedSize); + bstream.ptr += this.compressedSize; + } + + // TODO: deal with data descriptor if present (we currently assume no data descriptor!) + // "This descriptor exists only if bit 3 of the general purpose bit flag is set" + // But how do you figure out how big the file data is if you don't know the compressedSize + // from the header?!? + if ((this.generalPurpose & bitjs.BIT[3]) != 0) { + this.crc32 = bstream.readNumber(4); + this.compressedSize = bstream.readNumber(4); + this.uncompressedSize = bstream.readNumber(4); + } }; // determine what kind of compressed data we have and decompress ZipLocalFile.prototype.unzip = function() { - // Zip Version 1.0, no compression (store only) - if (this.compressionMethod == 0 ) { - info("ZIP v"+this.version+", store only: " + this.filename + " (" + this.compressedSize + " bytes)"); - currentBytesUnarchivedInFile = this.compressedSize; - currentBytesUnarchived += this.compressedSize; - } - // version == 20, compression method == 8 (DEFLATE) - else if (this.compressionMethod == 8) { - info("ZIP v2.0, DEFLATE: " + this.filename + " (" + this.compressedSize + " bytes)"); - this.fileData = inflate(this.fileData, this.uncompressedSize); - } - else { - err("UNSUPPORTED VERSION/FORMAT: ZIP v" + this.version + ", compression method=" + this.compressionMethod + ": " + this.filename + " (" + this.compressedSize + " bytes)"); - this.fileData = null; - } + // Zip Version 1.0, no compression (store only) + if (this.compressionMethod == 0 ) { + info("ZIP v"+this.version+", store only: " + this.filename + " (" + this.compressedSize + " bytes)"); + currentBytesUnarchivedInFile = this.compressedSize; + currentBytesUnarchived += this.compressedSize; + } + // version == 20, compression method == 8 (DEFLATE) + else if (this.compressionMethod == 8) { + info("ZIP v2.0, DEFLATE: " + this.filename + " (" + this.compressedSize + " bytes)"); + this.fileData = inflate(this.fileData, this.uncompressedSize); + } + else { + err("UNSUPPORTED VERSION/FORMAT: ZIP v" + this.version + ", compression method=" + this.compressionMethod + ": " + this.filename + " (" + this.compressedSize + " bytes)"); + this.fileData = null; + } }; @@ -131,112 +131,112 @@ ZipLocalFile.prototype.unzip = function() { // returns null on error // returns an array of DecompressedFile objects on success var unzip = function(arrayBuffer) { - postMessage(new bitjs.archive.UnarchiveStartEvent()); - - currentFilename = ""; - currentFileNumber = 0; - currentBytesUnarchivedInFile = 0; - currentBytesUnarchived = 0; - totalUncompressedBytesInArchive = 0; - totalFilesInArchive = 0; - currentBytesUnarchived = 0; - - var bstream = new bitjs.io.ByteStream(arrayBuffer); - // detect local file header signature or return null - if (bstream.peekNumber(4) == zLocalFileHeaderSignature) { - var localFiles = []; - // loop until we don't see any more local files - while (bstream.peekNumber(4) == zLocalFileHeaderSignature) { - var oneLocalFile = new ZipLocalFile(bstream); - // this should strip out directories/folders - if (oneLocalFile && oneLocalFile.uncompressedSize > 0 && oneLocalFile.fileData) { - localFiles.push(oneLocalFile); - totalUncompressedBytesInArchive += oneLocalFile.uncompressedSize; - } - } - totalFilesInArchive = localFiles.length; - - // got all local files, now sort them - localFiles.sort(function(a,b) { - var aname = a.filename.toLowerCase(); - var bname = b.filename.toLowerCase(); - return aname > bname ? 1 : -1; - }); - - // archive extra data record - if (bstream.peekNumber(4) == zArchiveExtraDataSignature) { - info(" Found an Archive Extra Data Signature"); - - // skipping this record for now - bstream.readNumber(4); - var archiveExtraFieldLength = bstream.readNumber(4); - bstream.readString(archiveExtraFieldLength); - } - - // central directory structure - // TODO: handle the rest of the structures (Zip64 stuff) - if (bstream.peekNumber(4) == zCentralFileHeaderSignature) { - info(" Found a Central File Header"); - - // read all file headers - while (bstream.peekNumber(4) == zCentralFileHeaderSignature) { - bstream.readNumber(4); // signature - bstream.readNumber(2); // version made by - bstream.readNumber(2); // version needed to extract - bstream.readNumber(2); // general purpose bit flag - bstream.readNumber(2); // compression method - bstream.readNumber(2); // last mod file time - bstream.readNumber(2); // last mod file date - bstream.readNumber(4); // crc32 - bstream.readNumber(4); // compressed size - bstream.readNumber(4); // uncompressed size - var fileNameLength = bstream.readNumber(2); // file name length - var extraFieldLength = bstream.readNumber(2); // extra field length - var fileCommentLength = bstream.readNumber(2); // file comment length - bstream.readNumber(2); // disk number start - bstream.readNumber(2); // internal file attributes - bstream.readNumber(4); // external file attributes - bstream.readNumber(4); // relative offset of local header - - bstream.readString(fileNameLength); // file name - bstream.readString(extraFieldLength); // extra field - bstream.readString(fileCommentLength); // file comment - } - } - - // digital signature - if (bstream.peekNumber(4) == zDigitalSignatureSignature) { - info(" Found a Digital Signature"); - - bstream.readNumber(4); - var sizeOfSignature = bstream.readNumber(2); - bstream.readString(sizeOfSignature); // digital signature data - } - - // report # files and total length - if (localFiles.length > 0) { - postProgress(); - } - - // now do the unzipping of each file - for (var i = 0; i < localFiles.length; ++i) { - var localfile = localFiles[i]; - - // update progress - currentFilename = localfile.filename; - currentFileNumber = i; - currentBytesUnarchivedInFile = 0; - - // actually do the unzipping - localfile.unzip(); - - if (localfile.fileData != null) { - postMessage(new bitjs.archive.UnarchiveExtractEvent(localfile)); - postProgress(); - } - } - postProgress(); - postMessage(new bitjs.archive.UnarchiveFinishEvent()); + postMessage(new bitjs.archive.UnarchiveStartEvent()); + + currentFilename = ""; + currentFileNumber = 0; + currentBytesUnarchivedInFile = 0; + currentBytesUnarchived = 0; + totalUncompressedBytesInArchive = 0; + totalFilesInArchive = 0; + currentBytesUnarchived = 0; + + var bstream = new bitjs.io.ByteStream(arrayBuffer); + // detect local file header signature or return null + if (bstream.peekNumber(4) == zLocalFileHeaderSignature) { + var localFiles = []; + // loop until we don't see any more local files + while (bstream.peekNumber(4) == zLocalFileHeaderSignature) { + var oneLocalFile = new ZipLocalFile(bstream); + // this should strip out directories/folders + if (oneLocalFile && oneLocalFile.uncompressedSize > 0 && oneLocalFile.fileData) { + localFiles.push(oneLocalFile); + totalUncompressedBytesInArchive += oneLocalFile.uncompressedSize; + } + } + totalFilesInArchive = localFiles.length; + + // got all local files, now sort them + localFiles.sort(function(a,b) { + var aname = a.filename.toLowerCase(); + var bname = b.filename.toLowerCase(); + return aname > bname ? 1 : -1; + }); + + // archive extra data record + if (bstream.peekNumber(4) == zArchiveExtraDataSignature) { + info(" Found an Archive Extra Data Signature"); + + // skipping this record for now + bstream.readNumber(4); + var archiveExtraFieldLength = bstream.readNumber(4); + bstream.readString(archiveExtraFieldLength); + } + + // central directory structure + // TODO: handle the rest of the structures (Zip64 stuff) + if (bstream.peekNumber(4) == zCentralFileHeaderSignature) { + info(" Found a Central File Header"); + + // read all file headers + while (bstream.peekNumber(4) == zCentralFileHeaderSignature) { + bstream.readNumber(4); // signature + bstream.readNumber(2); // version made by + bstream.readNumber(2); // version needed to extract + bstream.readNumber(2); // general purpose bit flag + bstream.readNumber(2); // compression method + bstream.readNumber(2); // last mod file time + bstream.readNumber(2); // last mod file date + bstream.readNumber(4); // crc32 + bstream.readNumber(4); // compressed size + bstream.readNumber(4); // uncompressed size + var fileNameLength = bstream.readNumber(2); // file name length + var extraFieldLength = bstream.readNumber(2); // extra field length + var fileCommentLength = bstream.readNumber(2); // file comment length + bstream.readNumber(2); // disk number start + bstream.readNumber(2); // internal file attributes + bstream.readNumber(4); // external file attributes + bstream.readNumber(4); // relative offset of local header + + bstream.readString(fileNameLength); // file name + bstream.readString(extraFieldLength); // extra field + bstream.readString(fileCommentLength); // file comment + } + } + + // digital signature + if (bstream.peekNumber(4) == zDigitalSignatureSignature) { + info(" Found a Digital Signature"); + + bstream.readNumber(4); + var sizeOfSignature = bstream.readNumber(2); + bstream.readString(sizeOfSignature); // digital signature data + } + + // report # files and total length + if (localFiles.length > 0) { + postProgress(); + } + + // now do the unzipping of each file + for (var i = 0; i < localFiles.length; ++i) { + var localfile = localFiles[i]; + + // update progress + currentFilename = localfile.filename; + currentFileNumber = i; + currentBytesUnarchivedInFile = 0; + + // actually do the unzipping + localfile.unzip(); + + if (localfile.fileData != null) { + postMessage(new bitjs.archive.UnarchiveExtractEvent(localfile)); + postProgress(); + } + } + postProgress(); + postMessage(new bitjs.archive.UnarchiveFinishEvent()); } } @@ -244,305 +244,305 @@ var unzip = function(arrayBuffer) { // each entry's index is its code and its value is a JavaScript object // containing {length: 6, symbol: X} function getHuffmanCodes(bitLengths) { - // ensure bitLengths is an array containing at least one element - if (typeof bitLengths != typeof [] || bitLengths.length < 1) { - err("Error! getHuffmanCodes() called with an invalid array"); - return null; - } - - // Reference: http://tools.ietf.org/html/rfc1951#page-8 - var numLengths = bitLengths.length, - bl_count = [], - MAX_BITS = 1; - - // Step 1: count up how many codes of each length we have - for (var i = 0; i < numLengths; ++i) { - var length = bitLengths[i]; - // test to ensure each bit length is a positive, non-zero number - if (typeof length != typeof 1 || length < 0) { - err("bitLengths contained an invalid number in getHuffmanCodes(): " + length + " of type " + (typeof length)); - return null; - } - // increment the appropriate bitlength count - if (bl_count[length] == undefined) bl_count[length] = 0; - // a length of zero means this symbol is not participating in the huffman coding - if (length > 0) bl_count[length]++; - - if (length > MAX_BITS) MAX_BITS = length; - } - - // Step 2: Find the numerical value of the smallest code for each code length - var next_code = [], - code = 0; - for (var bits = 1; bits <= MAX_BITS; ++bits) { - var length = bits-1; - // ensure undefined lengths are zero - if (bl_count[length] == undefined) bl_count[length] = 0; - code = (code + bl_count[bits-1]) << 1; - next_code[bits] = code; - } - - // Step 3: Assign numerical values to all codes - var table = {}, tableLength = 0; - for (var n = 0; n < numLengths; ++n) { - var len = bitLengths[n]; - if (len != 0) { - table[next_code[len]] = { length: len, symbol: n }; //, bitstring: binaryValueToString(next_code[len],len) }; - tableLength++; - next_code[len]++; - } - } - table.maxLength = tableLength; - - return table; + // ensure bitLengths is an array containing at least one element + if (typeof bitLengths != typeof [] || bitLengths.length < 1) { + err("Error! getHuffmanCodes() called with an invalid array"); + return null; + } + + // Reference: http://tools.ietf.org/html/rfc1951#page-8 + var numLengths = bitLengths.length, + bl_count = [], + MAX_BITS = 1; + + // Step 1: count up how many codes of each length we have + for (var i = 0; i < numLengths; ++i) { + var length = bitLengths[i]; + // test to ensure each bit length is a positive, non-zero number + if (typeof length != typeof 1 || length < 0) { + err("bitLengths contained an invalid number in getHuffmanCodes(): " + length + " of type " + (typeof length)); + return null; + } + // increment the appropriate bitlength count + if (bl_count[length] == undefined) bl_count[length] = 0; + // a length of zero means this symbol is not participating in the huffman coding + if (length > 0) bl_count[length]++; + + if (length > MAX_BITS) MAX_BITS = length; + } + + // Step 2: Find the numerical value of the smallest code for each code length + var next_code = [], + code = 0; + for (var bits = 1; bits <= MAX_BITS; ++bits) { + var length = bits-1; + // ensure undefined lengths are zero + if (bl_count[length] == undefined) bl_count[length] = 0; + code = (code + bl_count[bits-1]) << 1; + next_code[bits] = code; + } + + // Step 3: Assign numerical values to all codes + var table = {}, tableLength = 0; + for (var n = 0; n < numLengths; ++n) { + var len = bitLengths[n]; + if (len != 0) { + table[next_code[len]] = { length: len, symbol: n }; //, bitstring: binaryValueToString(next_code[len],len) }; + tableLength++; + next_code[len]++; + } + } + table.maxLength = tableLength; + + return table; } /* - The Huffman codes for the two alphabets are fixed, and are not - represented explicitly in the data. The Huffman code lengths - for the literal/length alphabet are: - - Lit Value Bits Codes - --------- ---- ----- - 0 - 143 8 00110000 through - 10111111 - 144 - 255 9 110010000 through - 111111111 - 256 - 279 7 0000000 through - 0010111 - 280 - 287 8 11000000 through - 11000111 + The Huffman codes for the two alphabets are fixed, and are not + represented explicitly in the data. The Huffman code lengths + for the literal/length alphabet are: + + Lit Value Bits Codes + --------- ---- ----- + 0 - 143 8 00110000 through + 10111111 + 144 - 255 9 110010000 through + 111111111 + 256 - 279 7 0000000 through + 0010111 + 280 - 287 8 11000000 through + 11000111 */ // fixed Huffman codes go from 7-9 bits, so we need an array whose index can hold up to 9 bits var fixedHCtoLiteral = null; var fixedHCtoDistance = null; function getFixedLiteralTable() { - // create once - if (!fixedHCtoLiteral) { - var bitlengths = new Array(288); - for (var i = 0; i <= 143; ++i) bitlengths[i] = 8; - for (i = 144; i <= 255; ++i) bitlengths[i] = 9; - for (i = 256; i <= 279; ++i) bitlengths[i] = 7; - for (i = 280; i <= 287; ++i) bitlengths[i] = 8; - - // get huffman code table - fixedHCtoLiteral = getHuffmanCodes(bitlengths); - } - return fixedHCtoLiteral; + // create once + if (!fixedHCtoLiteral) { + var bitlengths = new Array(288); + for (var i = 0; i <= 143; ++i) bitlengths[i] = 8; + for (i = 144; i <= 255; ++i) bitlengths[i] = 9; + for (i = 256; i <= 279; ++i) bitlengths[i] = 7; + for (i = 280; i <= 287; ++i) bitlengths[i] = 8; + + // get huffman code table + fixedHCtoLiteral = getHuffmanCodes(bitlengths); + } + return fixedHCtoLiteral; } function getFixedDistanceTable() { - // create once - if (!fixedHCtoDistance) { - var bitlengths = new Array(32); - for (var i = 0; i < 32; ++i) { bitlengths[i] = 5; } - - // get huffman code table - fixedHCtoDistance = getHuffmanCodes(bitlengths); - } - return fixedHCtoDistance; + // create once + if (!fixedHCtoDistance) { + var bitlengths = new Array(32); + for (var i = 0; i < 32; ++i) { bitlengths[i] = 5; } + + // get huffman code table + fixedHCtoDistance = getHuffmanCodes(bitlengths); + } + return fixedHCtoDistance; } // extract one bit at a time until we find a matching Huffman Code // then return that symbol function decodeSymbol(bstream, hcTable) { - var code = 0, len = 0; - var match = false; - - // loop until we match - for (;;) { - // read in next bit - var bit = bstream.readBits(1); - code = (code<<1) | bit; - ++len; - - // check against Huffman Code table and break if found - if (hcTable.hasOwnProperty(code) && hcTable[code].length == len) { - - break; - } - if (len > hcTable.maxLength) { - err("Bit stream out of sync, didn't find a Huffman Code, length was " + len + - " and table only max code length of " + hcTable.maxLength); - break; - } - } - return hcTable[code].symbol; + var code = 0, len = 0; + var match = false; + + // loop until we match + for (;;) { + // read in next bit + var bit = bstream.readBits(1); + code = (code<<1) | bit; + ++len; + + // check against Huffman Code table and break if found + if (hcTable.hasOwnProperty(code) && hcTable[code].length == len) { + + break; + } + if (len > hcTable.maxLength) { + err("Bit stream out of sync, didn't find a Huffman Code, length was " + len + + " and table only max code length of " + hcTable.maxLength); + break; + } + } + return hcTable[code].symbol; } var CodeLengthCodeOrder = [16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15]; - /* - Extra Extra Extra - Code Bits Length(s) Code Bits Lengths Code Bits Length(s) - ---- ---- ------ ---- ---- ------- ---- ---- ------- - 257 0 3 267 1 15,16 277 4 67-82 - 258 0 4 268 1 17,18 278 4 83-98 - 259 0 5 269 2 19-22 279 4 99-114 - 260 0 6 270 2 23-26 280 4 115-130 - 261 0 7 271 2 27-30 281 5 131-162 - 262 0 8 272 2 31-34 282 5 163-194 - 263 0 9 273 3 35-42 283 5 195-226 - 264 0 10 274 3 43-50 284 5 227-257 - 265 1 11,12 275 3 51-58 285 0 258 - 266 1 13,14 276 3 59-66 - - */ + /* + Extra Extra Extra + Code Bits Length(s) Code Bits Lengths Code Bits Length(s) + ---- ---- ------ ---- ---- ------- ---- ---- ------- + 257 0 3 267 1 15,16 277 4 67-82 + 258 0 4 268 1 17,18 278 4 83-98 + 259 0 5 269 2 19-22 279 4 99-114 + 260 0 6 270 2 23-26 280 4 115-130 + 261 0 7 271 2 27-30 281 5 131-162 + 262 0 8 272 2 31-34 282 5 163-194 + 263 0 9 273 3 35-42 283 5 195-226 + 264 0 10 274 3 43-50 284 5 227-257 + 265 1 11,12 275 3 51-58 285 0 258 + 266 1 13,14 276 3 59-66 + + */ var LengthLookupTable = [ - [0,3], [0,4], [0,5], [0,6], - [0,7], [0,8], [0,9], [0,10], - [1,11], [1,13], [1,15], [1,17], - [2,19], [2,23], [2,27], [2,31], - [3,35], [3,43], [3,51], [3,59], - [4,67], [4,83], [4,99], [4,115], - [5,131], [5,163], [5,195], [5,227], - [0,258] + [0,3], [0,4], [0,5], [0,6], + [0,7], [0,8], [0,9], [0,10], + [1,11], [1,13], [1,15], [1,17], + [2,19], [2,23], [2,27], [2,31], + [3,35], [3,43], [3,51], [3,59], + [4,67], [4,83], [4,99], [4,115], + [5,131], [5,163], [5,195], [5,227], + [0,258] ]; - /* - Extra Extra Extra - Code Bits Dist Code Bits Dist Code Bits Distance - ---- ---- ---- ---- ---- ------ ---- ---- -------- - 0 0 1 10 4 33-48 20 9 1025-1536 - 1 0 2 11 4 49-64 21 9 1537-2048 - 2 0 3 12 5 65-96 22 10 2049-3072 - 3 0 4 13 5 97-128 23 10 3073-4096 - 4 1 5,6 14 6 129-192 24 11 4097-6144 - 5 1 7,8 15 6 193-256 25 11 6145-8192 - 6 2 9-12 16 7 257-384 26 12 8193-12288 - 7 2 13-16 17 7 385-512 27 12 12289-16384 - 8 3 17-24 18 8 513-768 28 13 16385-24576 - 9 3 25-32 19 8 769-1024 29 13 24577-32768 - */ + /* + Extra Extra Extra + Code Bits Dist Code Bits Dist Code Bits Distance + ---- ---- ---- ---- ---- ------ ---- ---- -------- + 0 0 1 10 4 33-48 20 9 1025-1536 + 1 0 2 11 4 49-64 21 9 1537-2048 + 2 0 3 12 5 65-96 22 10 2049-3072 + 3 0 4 13 5 97-128 23 10 3073-4096 + 4 1 5,6 14 6 129-192 24 11 4097-6144 + 5 1 7,8 15 6 193-256 25 11 6145-8192 + 6 2 9-12 16 7 257-384 26 12 8193-12288 + 7 2 13-16 17 7 385-512 27 12 12289-16384 + 8 3 17-24 18 8 513-768 28 13 16385-24576 + 9 3 25-32 19 8 769-1024 29 13 24577-32768 + */ var DistLookupTable = [ - [0,1], [0,2], [0,3], [0,4], - [1,5], [1,7], - [2,9], [2,13], - [3,17], [3,25], - [4,33], [4,49], - [5,65], [5,97], - [6,129], [6,193], - [7,257], [7,385], - [8,513], [8,769], - [9,1025], [9,1537], - [10,2049], [10,3073], - [11,4097], [11,6145], - [12,8193], [12,12289], - [13,16385], [13,24577] + [0,1], [0,2], [0,3], [0,4], + [1,5], [1,7], + [2,9], [2,13], + [3,17], [3,25], + [4,33], [4,49], + [5,65], [5,97], + [6,129], [6,193], + [7,257], [7,385], + [8,513], [8,769], + [9,1025], [9,1537], + [10,2049], [10,3073], + [11,4097], [11,6145], + [12,8193], [12,12289], + [13,16385], [13,24577] ]; function inflateBlockData(bstream, hcLiteralTable, hcDistanceTable, buffer) { - /* - loop (until end of block code recognized) - decode literal/length value from input stream - if value < 256 - copy value (literal byte) to output stream - otherwise - if value = end of block (256) - break from loop - otherwise (value = 257..285) - decode distance from input stream - - move backwards distance bytes in the output - stream, and copy length bytes from this - position to the output stream. - */ - var numSymbols = 0, blockSize = 0; - for (;;) { - var symbol = decodeSymbol(bstream, hcLiteralTable); - ++numSymbols; - if (symbol < 256) { - // copy literal byte to output - buffer.insertByte(symbol); - blockSize++; - } - else { - // end of block reached - if (symbol == 256) { - break; - } - else { - var lengthLookup = LengthLookupTable[symbol-257], - length = lengthLookup[1] + bstream.readBits(lengthLookup[0]), - distLookup = DistLookupTable[decodeSymbol(bstream, hcDistanceTable)], - distance = distLookup[1] + bstream.readBits(distLookup[0]); - - // now apply length and distance appropriately and copy to output - - // TODO: check that backward distance < data.length? - - // http://tools.ietf.org/html/rfc1951#page-11 - // "Note also that the referenced string may overlap the current - // position; for example, if the last 2 bytes decoded have values - // X and Y, a string reference with - // adds X,Y,X,Y,X to the output stream." - // - // loop for each character - var ch = buffer.ptr - distance; - blockSize += length; - if(length > distance) { - var data = buffer.data; - while (length--) { - buffer.insertByte(data[ch++]); - } - } else { - buffer.insertBytes(buffer.data.subarray(ch, ch + length)) - } - - } // length-distance pair - } // length-distance pair or end-of-block - } // loop until we reach end of block - return blockSize; + /* + loop (until end of block code recognized) + decode literal/length value from input stream + if value < 256 + copy value (literal byte) to output stream + otherwise + if value = end of block (256) + break from loop + otherwise (value = 257..285) + decode distance from input stream + + move backwards distance bytes in the output + stream, and copy length bytes from this + position to the output stream. + */ + var numSymbols = 0, blockSize = 0; + for (;;) { + var symbol = decodeSymbol(bstream, hcLiteralTable); + ++numSymbols; + if (symbol < 256) { + // copy literal byte to output + buffer.insertByte(symbol); + blockSize++; + } + else { + // end of block reached + if (symbol == 256) { + break; + } + else { + var lengthLookup = LengthLookupTable[symbol-257], + length = lengthLookup[1] + bstream.readBits(lengthLookup[0]), + distLookup = DistLookupTable[decodeSymbol(bstream, hcDistanceTable)], + distance = distLookup[1] + bstream.readBits(distLookup[0]); + + // now apply length and distance appropriately and copy to output + + // TODO: check that backward distance < data.length? + + // http://tools.ietf.org/html/rfc1951#page-11 + // "Note also that the referenced string may overlap the current + // position; for example, if the last 2 bytes decoded have values + // X and Y, a string reference with + // adds X,Y,X,Y,X to the output stream." + // + // loop for each character + var ch = buffer.ptr - distance; + blockSize += length; + if(length > distance) { + var data = buffer.data; + while (length--) { + buffer.insertByte(data[ch++]); + } + } else { + buffer.insertBytes(buffer.data.subarray(ch, ch + length)) + } + + } // length-distance pair + } // length-distance pair or end-of-block + } // loop until we reach end of block + return blockSize; } // {Uint8Array} compressedData A Uint8Array of the compressed file data. // compression method 8 // deflate: http://tools.ietf.org/html/rfc1951 function inflate(compressedData, numDecompressedBytes) { - // Bit stream representing the compressed data. - var bstream = new bitjs.io.BitStream(compressedData.buffer, - false /* rtl */, - compressedData.byteOffset, - compressedData.byteLength); - var buffer = new bitjs.io.ByteBuffer(numDecompressedBytes); - var numBlocks = 0, blockSize = 0; - - // block format: http://tools.ietf.org/html/rfc1951#page-9 - do { - var bFinal = bstream.readBits(1), - bType = bstream.readBits(2); - blockSize = 0; - ++numBlocks; - // no compression - if (bType == 0) { - // skip remaining bits in this byte - while (bstream.bitPtr != 0) bstream.readBits(1); - var len = bstream.readBits(16), - nlen = bstream.readBits(16); - // TODO: check if nlen is the ones-complement of len? - - if(len > 0) buffer.insertBytes(bstream.readBytes(len)); - blockSize = len; - } - // fixed Huffman codes - else if(bType == 1) { - blockSize = inflateBlockData(bstream, getFixedLiteralTable(), getFixedDistanceTable(), buffer); - } - // dynamic Huffman codes - else if(bType == 2) { - var numLiteralLengthCodes = bstream.readBits(5) + 257; - var numDistanceCodes = bstream.readBits(5) + 1, - numCodeLengthCodes = bstream.readBits(4) + 4; - - // populate the array of code length codes (first de-compaction) - var codeLengthsCodeLengths = [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0]; - for (var i = 0; i < numCodeLengthCodes; ++i) { - codeLengthsCodeLengths[ CodeLengthCodeOrder[i] ] = bstream.readBits(3); - } - - // get the Huffman Codes for the code lengths - var codeLengthsCodes = getHuffmanCodes(codeLengthsCodeLengths); - - // now follow this mapping - /* + // Bit stream representing the compressed data. + var bstream = new bitjs.io.BitStream(compressedData.buffer, + false /* rtl */, + compressedData.byteOffset, + compressedData.byteLength); + var buffer = new bitjs.io.ByteBuffer(numDecompressedBytes); + var numBlocks = 0, blockSize = 0; + + // block format: http://tools.ietf.org/html/rfc1951#page-9 + do { + var bFinal = bstream.readBits(1), + bType = bstream.readBits(2); + blockSize = 0; + ++numBlocks; + // no compression + if (bType == 0) { + // skip remaining bits in this byte + while (bstream.bitPtr != 0) bstream.readBits(1); + var len = bstream.readBits(16), + nlen = bstream.readBits(16); + // TODO: check if nlen is the ones-complement of len? + + if(len > 0) buffer.insertBytes(bstream.readBytes(len)); + blockSize = len; + } + // fixed Huffman codes + else if(bType == 1) { + blockSize = inflateBlockData(bstream, getFixedLiteralTable(), getFixedDistanceTable(), buffer); + } + // dynamic Huffman codes + else if(bType == 2) { + var numLiteralLengthCodes = bstream.readBits(5) + 257; + var numDistanceCodes = bstream.readBits(5) + 1, + numCodeLengthCodes = bstream.readBits(4) + 4; + + // populate the array of code length codes (first de-compaction) + var codeLengthsCodeLengths = [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0]; + for (var i = 0; i < numCodeLengthCodes; ++i) { + codeLengthsCodeLengths[ CodeLengthCodeOrder[i] ] = bstream.readBits(3); + } + + // get the Huffman Codes for the code lengths + var codeLengthsCodes = getHuffmanCodes(codeLengthsCodeLengths); + + // now follow this mapping + /* 0 - 15: Represent code lengths of 0 - 15 16: Copy the previous code length 3 - 6 times. The next 2 bits indicate repeat length @@ -554,64 +554,64 @@ function inflate(compressedData, numDecompressedBytes) { (3 bits of length) 18: Repeat a code length of 0 for 11 - 138 times (7 bits of length) - */ - // to generate the true code lengths of the Huffman Codes for the literal - // and distance tables together - var literalCodeLengths = []; - var prevCodeLength = 0; - while (literalCodeLengths.length < numLiteralLengthCodes + numDistanceCodes) { - var symbol = decodeSymbol(bstream, codeLengthsCodes); - if (symbol <= 15) { - literalCodeLengths.push(symbol); - prevCodeLength = symbol; - } - else if (symbol == 16) { - var repeat = bstream.readBits(2) + 3; - while (repeat--) { - literalCodeLengths.push(prevCodeLength); - } - } - else if (symbol == 17) { - var repeat = bstream.readBits(3) + 3; - while (repeat--) { - literalCodeLengths.push(0); - } - } - else if (symbol == 18) { - var repeat = bstream.readBits(7) + 11; - while (repeat--) { - literalCodeLengths.push(0); - } - } - } - - // now split the distance code lengths out of the literal code array - var distanceCodeLengths = literalCodeLengths.splice(numLiteralLengthCodes, numDistanceCodes); - - // now generate the true Huffman Code tables using these code lengths - var hcLiteralTable = getHuffmanCodes(literalCodeLengths), - hcDistanceTable = getHuffmanCodes(distanceCodeLengths); - blockSize = inflateBlockData(bstream, hcLiteralTable, hcDistanceTable, buffer); - } - // error - else { - err("Error! Encountered deflate block of type 3"); - return null; - } - - // update progress - currentBytesUnarchivedInFile += blockSize; - currentBytesUnarchived += blockSize; - postProgress(); - - } while (bFinal != 1); - // we are done reading blocks if the bFinal bit was set for this block - - // return the buffer data bytes - return buffer.data; + */ + // to generate the true code lengths of the Huffman Codes for the literal + // and distance tables together + var literalCodeLengths = []; + var prevCodeLength = 0; + while (literalCodeLengths.length < numLiteralLengthCodes + numDistanceCodes) { + var symbol = decodeSymbol(bstream, codeLengthsCodes); + if (symbol <= 15) { + literalCodeLengths.push(symbol); + prevCodeLength = symbol; + } + else if (symbol == 16) { + var repeat = bstream.readBits(2) + 3; + while (repeat--) { + literalCodeLengths.push(prevCodeLength); + } + } + else if (symbol == 17) { + var repeat = bstream.readBits(3) + 3; + while (repeat--) { + literalCodeLengths.push(0); + } + } + else if (symbol == 18) { + var repeat = bstream.readBits(7) + 11; + while (repeat--) { + literalCodeLengths.push(0); + } + } + } + + // now split the distance code lengths out of the literal code array + var distanceCodeLengths = literalCodeLengths.splice(numLiteralLengthCodes, numDistanceCodes); + + // now generate the true Huffman Code tables using these code lengths + var hcLiteralTable = getHuffmanCodes(literalCodeLengths), + hcDistanceTable = getHuffmanCodes(distanceCodeLengths); + blockSize = inflateBlockData(bstream, hcLiteralTable, hcDistanceTable, buffer); + } + // error + else { + err("Error! Encountered deflate block of type 3"); + return null; + } + + // update progress + currentBytesUnarchivedInFile += blockSize; + currentBytesUnarchived += blockSize; + postProgress(); + + } while (bFinal != 1); + // we are done reading blocks if the bFinal bit was set for this block + + // return the buffer data bytes + return buffer.data; } // event.data.file has the ArrayBuffer. onmessage = function(event) { - unzip(event.data.file, true); + unzip(event.data.file, true); };