mirror of
				https://github.com/janeczku/calibre-web
				synced 2025-10-31 07:13:02 +00:00 
			
		
		
		
	Downgrade bitjs to es5 branch
This commit is contained in:
		| @@ -11,21 +11,65 @@ | ||||
| var bitjs = bitjs || {}; | ||||
| 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 = class { | ||||
| bitjs.archive.UnarchiveEvent = function(type) { | ||||
|   /** | ||||
|    * @param {string} type The event type. | ||||
|    * The event type. | ||||
|    * | ||||
|    * @type {string} | ||||
|    */ | ||||
|   constructor(type) { | ||||
|     /** | ||||
|      * The event type. | ||||
|      * @type {string} | ||||
|      */ | ||||
|     this.type = type; | ||||
|   } | ||||
| } | ||||
|   this.type = type; | ||||
| }; | ||||
|  | ||||
| /** | ||||
|  * The UnarchiveEvent types. | ||||
| @@ -41,102 +85,78 @@ bitjs.archive.UnarchiveEvent.Type = { | ||||
|  | ||||
| /** | ||||
|  * Useful for passing info up to the client (for debugging). | ||||
|  * | ||||
|  * @param {string} msg The info message. | ||||
|  */ | ||||
| bitjs.archive.UnarchiveInfoEvent = class extends bitjs.archive.UnarchiveEvent { | ||||
|   /** | ||||
|    * @param {string} msg The info message. | ||||
|    */ | ||||
|   constructor(msg) { | ||||
|     super(bitjs.archive.UnarchiveEvent.Type.INFO); | ||||
| bitjs.archive.UnarchiveInfoEvent = function(msg) { | ||||
|   bitjs.base(this, bitjs.archive.UnarchiveEvent.Type.INFO); | ||||
|  | ||||
|     /** | ||||
|      * The information message. | ||||
|      * @type {string} | ||||
|      */ | ||||
|     this.msg = msg; | ||||
|   } | ||||
| } | ||||
|   /** | ||||
|    * 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 = class extends bitjs.archive.UnarchiveEvent { | ||||
|   /** | ||||
|    * @param {string} msg The error message. | ||||
|    */ | ||||
|   constructor(msg) { | ||||
|     super(bitjs.archive.UnarchiveEvent.Type.ERROR); | ||||
| bitjs.archive.UnarchiveErrorEvent = function(msg) { | ||||
|   bitjs.base(this, bitjs.archive.UnarchiveEvent.Type.ERROR); | ||||
|  | ||||
|     /** | ||||
|      * The information message. | ||||
|      * @type {string} | ||||
|      */ | ||||
|     this.msg = msg; | ||||
|   } | ||||
| } | ||||
|   /** | ||||
|    * 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 = class extends bitjs.archive.UnarchiveEvent { | ||||
|   constructor() { | ||||
|     super(bitjs.archive.UnarchiveEvent.Type.START); | ||||
|   } | ||||
| } | ||||
| 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 = class extends bitjs.archive.UnarchiveEvent { | ||||
|   constructor() { | ||||
|     super(bitjs.archive.UnarchiveEvent.Type.FINISH); | ||||
|   } | ||||
| } | ||||
| 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 = class extends bitjs.archive.UnarchiveEvent { | ||||
|   /** | ||||
|    * @param {string} currentFilename | ||||
|    * @param {number} currentFileNumber | ||||
|    * @param {number} currentBytesUnarchivedInFile | ||||
|    * @param {number} currentBytesUnarchived | ||||
|    * @param {number} totalUncompressedBytesInArchive | ||||
|    * @param {number} totalFilesInArchive | ||||
|    * @param {number} totalCompressedBytesRead | ||||
|    */ | ||||
|   constructor(currentFilename, currentFileNumber, currentBytesUnarchivedInFile, | ||||
|       currentBytesUnarchived, totalUncompressedBytesInArchive, totalFilesInArchive, | ||||
|       totalCompressedBytesRead) { | ||||
|     super(bitjs.archive.UnarchiveEvent.Type.PROGRESS); | ||||
| 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; | ||||
|     this.totalCompressedBytesRead = totalCompressedBytesRead; | ||||
|   } | ||||
| } | ||||
|  | ||||
| /** | ||||
|  * Extract event. | ||||
|  */ | ||||
| bitjs.archive.UnarchiveExtractEvent = class extends bitjs.archive.UnarchiveEvent { | ||||
|   /** | ||||
|    * @param {UnarchivedFile} unarchivedFile | ||||
|    */ | ||||
|   constructor(unarchivedFile) { | ||||
|     super(bitjs.archive.UnarchiveEvent.Type.EXTRACT); | ||||
|  | ||||
|     /** | ||||
|      * @type {UnarchivedFile} | ||||
|      */ | ||||
|     this.unarchivedFile = unarchivedFile; | ||||
|   } | ||||
| } | ||||
|   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 | ||||
| @@ -150,190 +170,185 @@ bitjs.archive.UnarchiveExtractEvent = class extends bitjs.archive.UnarchiveEvent | ||||
|  */ | ||||
|  | ||||
| /** | ||||
|  * Base class for all Unarchivers. | ||||
|  * Extract event. | ||||
|  */ | ||||
| bitjs.archive.Unarchiver = class { | ||||
| bitjs.archive.UnarchiveExtractEvent = function(unarchivedFile) { | ||||
|   bitjs.base(this, bitjs.archive.UnarchiveEvent.Type.EXTRACT); | ||||
|  | ||||
|   /** | ||||
|    * @param {ArrayBuffer} arrayBuffer The Array Buffer. | ||||
|    * @param {string} opt_pathToBitJS Optional string for where the BitJS files are located. | ||||
|    * @type {UnarchivedFile} | ||||
|    */ | ||||
|   constructor(arrayBuffer, opt_pathToBitJS) { | ||||
|     /** | ||||
|      * The ArrayBuffer object. | ||||
|      * @type {ArrayBuffer} | ||||
|      * @protected | ||||
|      */ | ||||
|     this.ab = arrayBuffer; | ||||
|   this.unarchivedFile = unarchivedFile; | ||||
| }; | ||||
| bitjs.inherits(bitjs.archive.UnarchiveExtractEvent, bitjs.archive.UnarchiveEvent); | ||||
|  | ||||
|     /** | ||||
|      * 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.<string, Array>} | ||||
|      */ | ||||
|     this.listeners_ = {}; | ||||
|     for (let type in bitjs.archive.UnarchiveEvent.Type) { | ||||
|       this.listeners_[bitjs.archive.UnarchiveEvent.Type[type]] = []; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Private web worker initialized during start(). | ||||
|      * @type {Worker} | ||||
|      * @private | ||||
|      */ | ||||
|     this.worker_ = null; | ||||
|   } | ||||
|  | ||||
| /** | ||||
|  * 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) { | ||||
|   /** | ||||
|    * This method must be overridden by the subclass to return the script filename. | ||||
|    * @return {string} The script filename. | ||||
|    * @protected. | ||||
|    * The ArrayBuffer object. | ||||
|    * @type {ArrayBuffer} | ||||
|    * @protected | ||||
|    */ | ||||
|   getScriptFileName() { | ||||
|     throw 'Subclasses of AbstractUnarchiver must overload getScriptFileName()'; | ||||
|   } | ||||
|   this.ab = arrayBuffer; | ||||
|  | ||||
|   /** | ||||
|    * Adds an event listener for UnarchiveEvents. | ||||
|    * | ||||
|    * @param {string} Event type. | ||||
|    * @param {function} An event handler function. | ||||
|    */ | ||||
|   addEventListener(type, listener) { | ||||
|     if (type in this.listeners_) { | ||||
|       if (this.listeners_[type].indexOf(listener) == -1) { | ||||
|         this.listeners_[type].push(listener); | ||||
|       } | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   /** | ||||
|    * Removes an event listener. | ||||
|    * | ||||
|    * @param {string} Event type. | ||||
|    * @param {EventListener|function} An event listener or handler function. | ||||
|    */ | ||||
|   removeEventListener(type, listener) { | ||||
|     if (type in this.listeners_) { | ||||
|       const index = this.listeners_[type].indexOf(listener); | ||||
|       if (index != -1) { | ||||
|         this.listeners_[type].splice(index, 1); | ||||
|       } | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   /** | ||||
|    * Receive an event and pass it to the listener functions. | ||||
|    * | ||||
|    * @param {bitjs.archive.UnarchiveEvent} e | ||||
|    * The path to the BitJS files. | ||||
|    * @type {string} | ||||
|    * @private | ||||
|    */ | ||||
|   handleWorkerEvent_(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); | ||||
|     } | ||||
|   } | ||||
|   this.pathToBitJS_ = opt_pathToBitJS || '/'; | ||||
|  | ||||
|   /** | ||||
|    * Starts the unarchive in a separate Web Worker thread and returns immediately. | ||||
|    * A map from event type to an array of listeners. | ||||
|    * @type {Map.<string, Array>} | ||||
|    */ | ||||
|   start() { | ||||
|     const me = this; | ||||
|     const scriptFileName = this.pathToBitJS_ + this.getScriptFileName(); | ||||
|     if (scriptFileName) { | ||||
|       this.worker_ = new Worker(scriptFileName); | ||||
|   this.listeners_ = {}; | ||||
|   for (var type in bitjs.archive.UnarchiveEvent.Type) { | ||||
|     this.listeners_[bitjs.archive.UnarchiveEvent.Type[type]] = []; | ||||
|   } | ||||
| }; | ||||
|  | ||||
|       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.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 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()'; | ||||
| }; | ||||
|  | ||||
|       const ab = this.ab; | ||||
|       this.worker_.postMessage({ | ||||
|         file: ab, | ||||
|         logToConsole: false, | ||||
|       }); | ||||
|       this.ab = null; | ||||
| /** | ||||
|  * 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); | ||||
|     } | ||||
|   } | ||||
| }; | ||||
|  | ||||
|   /** | ||||
|    * Adds more bytes to the unarchiver's Worker thread. | ||||
|    */ | ||||
|   update(ab) { | ||||
|     if (this.worker_) { | ||||
|       this.worker_.postMessage({bytes: 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. | ||||
|    */ | ||||
|   stop() { | ||||
|     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}); | ||||
|   } | ||||
| }; | ||||
|  | ||||
| /** | ||||
|  * Terminates the Web Worker for this Unarchiver and returns immediately. | ||||
|  */ | ||||
| bitjs.archive.Unarchiver.prototype.stop = function() { | ||||
|   if (this.worker_) { | ||||
|     this.worker_.terminate(); | ||||
|   } | ||||
| }; | ||||
|  | ||||
|  | ||||
| /** | ||||
|  * Unzipper | ||||
|  * @extends {bitjs.archive.Unarchiver} | ||||
|  * @constructor | ||||
|  */ | ||||
| bitjs.archive.Unzipper = class extends bitjs.archive.Unarchiver { | ||||
|   constructor(arrayBuffer, opt_pathToBitJS) { | ||||
|     super(arrayBuffer, opt_pathToBitJS); | ||||
|   } | ||||
|  | ||||
|   getScriptFileName() { return 'archive/unzip.js'; } | ||||
| } | ||||
|  | ||||
| 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 = class extends bitjs.archive.Unarchiver { | ||||
|   constructor(arrayBuffer, opt_pathToBitJS) { | ||||
|     super(arrayBuffer, opt_pathToBitJS); | ||||
|   } | ||||
|  | ||||
|   getScriptFileName() { return 'archive/unrar.js'; } | ||||
| } | ||||
| 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 = class extends bitjs.archive.Unarchiver { | ||||
|   constructor(arrayBuffer, opt_pathToBitJS) { | ||||
|     super(arrayBuffer, opt_pathToBitJS); | ||||
|   } | ||||
|  | ||||
|   getScriptFileName() { return 'archive/untar.js'; }; | ||||
| } | ||||
| 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' }; | ||||
|  | ||||
| /** | ||||
|  * Factory method that creates an unarchiver based on the byte signature found | ||||
| @@ -343,20 +358,18 @@ bitjs.archive.Untarrer = class extends bitjs.archive.Unarchiver { | ||||
|  * @return {bitjs.archive.Unarchiver} | ||||
|  */ | ||||
| bitjs.archive.GetUnarchiver = function(ab, opt_pathToBitJS) { | ||||
|   if (ab.byteLength < 10) { | ||||
|     return null; | ||||
|   } | ||||
|  | ||||
|   let unarchiver = null; | ||||
|   const pathToBitJS = opt_pathToBitJS || ''; | ||||
|   const h = new Uint8Array(ab, 0, 10); | ||||
|   var unarchiver = null; | ||||
|   var pathToBitJS = opt_pathToBitJS || ''; | ||||
|   var h = new Uint8Array(ab, 0, 10); | ||||
|  | ||||
|   if (h[0] == 0x52 && h[1] == 0x61 && h[2] == 0x72 && h[3] == 0x21) { // Rar! | ||||
|     unarchiver = new bitjs.archive.Unrarrer(ab, pathToBitJS); | ||||
|   } else if (h[0] == 0x50 && h[1] == 0x4B) { // PK (Zip) | ||||
|   } else if (h[0] == 80 && h[1] == 75) { // PK (Zip) | ||||
|     unarchiver = new bitjs.archive.Unzipper(ab, pathToBitJS); | ||||
|   } else { // Try with tar | ||||
|     unarchiver = new bitjs.archive.Untarrer(ab, pathToBitJS); | ||||
|   } | ||||
|   return unarchiver; | ||||
| }; | ||||
|  | ||||
| })(); | ||||
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							| @@ -14,193 +14,177 @@ | ||||
| importScripts('../io/bytestream.js'); | ||||
| importScripts('archive.js'); | ||||
|  | ||||
| const UnarchiveState = { | ||||
|   NOT_STARTED: 0, | ||||
|   UNARCHIVING: 1, | ||||
|   WAITING: 2, | ||||
|   FINISHED: 3, | ||||
| }; | ||||
|  | ||||
| // State - consider putting these into a class. | ||||
| let unarchiveState = UnarchiveState.NOT_STARTED; | ||||
| let bytestream = null; | ||||
| let allLocalFiles = null; | ||||
| let logToConsole = false; | ||||
|  | ||||
| // Progress variables. | ||||
| let currentFilename = ""; | ||||
| let currentFileNumber = 0; | ||||
| let currentBytesUnarchivedInFile = 0; | ||||
| let currentBytesUnarchived = 0; | ||||
| let totalUncompressedBytesInArchive = 0; | ||||
| let totalFilesInArchive = 0; | ||||
| var currentFilename = ""; | ||||
| var currentFileNumber = 0; | ||||
| var currentBytesUnarchivedInFile = 0; | ||||
| var currentBytesUnarchived = 0; | ||||
| var totalUncompressedBytesInArchive = 0; | ||||
| var totalFilesInArchive = 0; | ||||
|  | ||||
| // Helper functions. | ||||
| const info = function(str) { | ||||
| var info = function(str) { | ||||
|   postMessage(new bitjs.archive.UnarchiveInfoEvent(str)); | ||||
| }; | ||||
| const err = function(str) { | ||||
| var err = function(str) { | ||||
|   postMessage(new bitjs.archive.UnarchiveErrorEvent(str)); | ||||
| }; | ||||
| const postProgress = function() { | ||||
| var postProgress = function() { | ||||
|   postMessage(new bitjs.archive.UnarchiveProgressEvent( | ||||
|       currentFilename, | ||||
|       currentFileNumber, | ||||
|       currentBytesUnarchivedInFile, | ||||
|       currentBytesUnarchived, | ||||
|       totalUncompressedBytesInArchive, | ||||
|       totalFilesInArchive, | ||||
|       bytestream.getNumBytesRead(), | ||||
|   )); | ||||
|       totalFilesInArchive)); | ||||
| }; | ||||
|  | ||||
| // Removes all characters from the first zero-byte in the string onwards. | ||||
| const readCleanString = function(bstr, numBytes) { | ||||
|   const str = bstr.readString(numBytes); | ||||
|   const zIndex = str.indexOf(String.fromCharCode(0)); | ||||
| var readCleanString = function(bstr, numBytes) { | ||||
|   var str = bstr.readString(numBytes); | ||||
|   var zIndex = str.indexOf(String.fromCharCode(0)); | ||||
|   return zIndex != -1 ? str.substr(0, zIndex) : str; | ||||
| }; | ||||
|  | ||||
| class TarLocalFile { | ||||
|   // takes a ByteStream and parses out the local file information | ||||
|   constructor(bstream) { | ||||
|     this.isValid = false; | ||||
| // takes a ByteStream and parses out the local file information | ||||
| var TarLocalFile = function(bstream) { | ||||
|   this.isValid = false; | ||||
|  | ||||
|     let bytesRead = 0; | ||||
|   // Read in the header block | ||||
|   this.name = readCleanString(bstream, 100); | ||||
|   this.mode = readCleanString(bstream, 8); | ||||
|   this.uid = readCleanString(bstream, 8); | ||||
|   this.gid = readCleanString(bstream, 8); | ||||
|   this.size = parseInt(readCleanString(bstream, 12), 8); | ||||
|   this.mtime = readCleanString(bstream, 12); | ||||
|   this.chksum = readCleanString(bstream, 8); | ||||
|   this.typeflag = readCleanString(bstream, 1); | ||||
|   this.linkname = readCleanString(bstream, 100); | ||||
|   this.maybeMagic = readCleanString(bstream, 6); | ||||
|  | ||||
|     // Read in the header block | ||||
|     this.name = readCleanString(bstream, 100); | ||||
|     this.mode = readCleanString(bstream, 8); | ||||
|     this.uid = readCleanString(bstream, 8); | ||||
|     this.gid = readCleanString(bstream, 8); | ||||
|     this.size = parseInt(readCleanString(bstream, 12), 8); | ||||
|     this.mtime = readCleanString(bstream, 12); | ||||
|     this.chksum = readCleanString(bstream, 8); | ||||
|     this.typeflag = readCleanString(bstream, 1); | ||||
|     this.linkname = readCleanString(bstream, 100); | ||||
|     this.maybeMagic = readCleanString(bstream, 6); | ||||
|   if (this.maybeMagic == "ustar") { | ||||
|     this.version = readCleanString(bstream, 2); | ||||
|     this.uname = readCleanString(bstream, 32); | ||||
|     this.gname = readCleanString(bstream, 32); | ||||
|     this.devmajor = readCleanString(bstream, 8); | ||||
|     this.devminor = readCleanString(bstream, 8); | ||||
|     this.prefix = readCleanString(bstream, 155); | ||||
|  | ||||
|     if (this.maybeMagic == "ustar") { | ||||
|       this.version = readCleanString(bstream, 2); | ||||
|       this.uname = readCleanString(bstream, 32); | ||||
|       this.gname = readCleanString(bstream, 32); | ||||
|       this.devmajor = readCleanString(bstream, 8); | ||||
|       this.devminor = readCleanString(bstream, 8); | ||||
|       this.prefix = readCleanString(bstream, 155); | ||||
|  | ||||
|       if (this.prefix.length) { | ||||
|         this.name = this.prefix + this.name; | ||||
|       } | ||||
|       bstream.readBytes(12); // 512 - 500 | ||||
|     } else { | ||||
|       bstream.readBytes(255); // 512 - 257 | ||||
|     } | ||||
|  | ||||
|     bytesRead += 512; | ||||
|  | ||||
|     // Done header, now rest of blocks are the file contents. | ||||
|     this.filename = this.name; | ||||
|     this.fileData = null; | ||||
|  | ||||
|     info("Untarring file '" + this.filename + "'"); | ||||
|     info("  size = " + this.size); | ||||
|     info("  typeflag = " + this.typeflag); | ||||
|  | ||||
|     // A regular file. | ||||
|     if (this.typeflag == 0) { | ||||
|       info("  This is a regular file."); | ||||
|       const sizeInBytes = parseInt(this.size); | ||||
|       this.fileData = new Uint8Array(bstream.readBytes(sizeInBytes)); | ||||
|       bytesRead += sizeInBytes; | ||||
|       if (this.name.length > 0 && this.size > 0 && this.fileData && this.fileData.buffer) { | ||||
|         this.isValid = true; | ||||
|       } | ||||
|  | ||||
|       // Round up to 512-byte blocks. | ||||
|       const remaining = 512 - bytesRead % 512; | ||||
|       if (remaining > 0 && remaining < 512) { | ||||
|         bstream.readBytes(remaining); | ||||
|       } | ||||
|     } else if (this.typeflag == 5) { | ||||
|        info("  This is a directory.") | ||||
|     if (this.prefix.length) { | ||||
|       this.name = this.prefix + this.name; | ||||
|     } | ||||
|     bstream.readBytes(12); // 512 - 500 | ||||
|   } else { | ||||
|     bstream.readBytes(255); // 512 - 257 | ||||
|   } | ||||
| } | ||||
|    | ||||
|   // Done header, now rest of blocks are the file contents. | ||||
|   this.filename = this.name; | ||||
|   this.fileData = null; | ||||
|  | ||||
| const untar = function() { | ||||
|   let bstream = bytestream.tee(); | ||||
|   info("Untarring file '" + this.filename + "'"); | ||||
|   info("  size = " + this.size); | ||||
|   info("  typeflag = " + this.typeflag); | ||||
|  | ||||
|   // A regular file. | ||||
|   if (this.typeflag == 0) { | ||||
|     info("  This is a regular file."); | ||||
|     var sizeInBytes = parseInt(this.size); | ||||
|     this.fileData = new Uint8Array(bstream.bytes.buffer, bstream.ptr, this.size); | ||||
|     if (this.name.length > 0 && this.size > 0 && this.fileData && this.fileData.buffer) { | ||||
|       this.isValid = true; | ||||
|     } | ||||
|  | ||||
|     bstream.readBytes(this.size); | ||||
|  | ||||
|     // Round up to 512-byte blocks. | ||||
|     var remaining = 512 - bstream.ptr % 512; | ||||
|     if (remaining > 0 && remaining < 512) { | ||||
|       bstream.readBytes(remaining); | ||||
|     } | ||||
|   } else if (this.typeflag == 5) { | ||||
|      info("  This is a directory.") | ||||
|   } | ||||
| }; | ||||
|  | ||||
| // Takes an ArrayBuffer of a tar file in | ||||
| // returns null on error | ||||
| // returns an array of DecompressedFile objects on success | ||||
| var untar = function(arrayBuffer) { | ||||
|   currentFilename = ""; | ||||
|   currentFileNumber = 0; | ||||
|   currentBytesUnarchivedInFile = 0; | ||||
|   currentBytesUnarchived = 0; | ||||
|   totalUncompressedBytesInArchive = 0; | ||||
|   totalFilesInArchive = 0; | ||||
|  | ||||
|   postMessage(new bitjs.archive.UnarchiveStartEvent()); | ||||
|   var bstream = new bitjs.io.ByteStream(arrayBuffer); | ||||
|   var localFiles = []; | ||||
|  | ||||
|   // While we don't encounter an empty block, keep making TarLocalFiles. | ||||
|   while (bstream.peekNumber(4) != 0) { | ||||
|     const oneLocalFile = new TarLocalFile(bstream); | ||||
|     var oneLocalFile = new TarLocalFile(bstream); | ||||
|     if (oneLocalFile && oneLocalFile.isValid) { | ||||
|       // If we make it to this point and haven't thrown an error, we have successfully | ||||
|       // read in the data for a local file, so we can update the actual bytestream. | ||||
|       bytestream = bstream.tee(); | ||||
|  | ||||
|       allLocalFiles.push(oneLocalFile); | ||||
|       localFiles.push(oneLocalFile); | ||||
|       totalUncompressedBytesInArchive += oneLocalFile.size; | ||||
|  | ||||
|       // update progress | ||||
|       currentFilename = oneLocalFile.filename; | ||||
|       currentFileNumber = totalFilesInArchive++; | ||||
|       currentBytesUnarchivedInFile = oneLocalFile.size; | ||||
|       currentBytesUnarchived += oneLocalFile.size; | ||||
|       postMessage(new bitjs.archive.UnarchiveExtractEvent(oneLocalFile)); | ||||
|       postProgress(); | ||||
|     } | ||||
|   } | ||||
|   totalFilesInArchive = allLocalFiles.length; | ||||
|   totalFilesInArchive = localFiles.length; | ||||
|  | ||||
|   postProgress(); | ||||
|   // got all local files, now sort them | ||||
|   localFiles.sort(function(a,b) { | ||||
|       var aname = a.filename; | ||||
|       var bname = b.filename; | ||||
|       return aname > bname ? 1 : -1; | ||||
|  | ||||
|   bytestream = bstream.tee(); | ||||
| }; | ||||
|       // extract the number at the end of both filenames | ||||
|       /* | ||||
|       var aname = a.filename; | ||||
|       var bname = b.filename; | ||||
|       var aindex = aname.length, bindex = bname.length; | ||||
|  | ||||
| // event.data.file has the first ArrayBuffer. | ||||
| // event.data.bytes has all subsequent ArrayBuffers. | ||||
| onmessage = function(event) { | ||||
|   const bytes = event.data.file || event.data.bytes; | ||||
|   logToConsole = !!event.data.logToConsole; | ||||
|       // Find the last number character from the back of the filename. | ||||
|       while (aname[aindex-1] < '0' || aname[aindex-1] > '9') --aindex; | ||||
|       while (bname[bindex-1] < '0' || bname[bindex-1] > '9') --bindex; | ||||
|  | ||||
|   // This is the very first time we have been called. Initialize the bytestream. | ||||
|   if (!bytestream) { | ||||
|     bytestream = new bitjs.io.ByteStream(bytes); | ||||
|   } else { | ||||
|     bytestream.push(bytes); | ||||
|   } | ||||
|       // Find the first number character from the back of the filename | ||||
|       while (aname[aindex-1] >= '0' && aname[aindex-1] <= '9') --aindex; | ||||
|       while (bname[bindex-1] >= '0' && bname[bindex-1] <= '9') --bindex; | ||||
|  | ||||
|   if (unarchiveState === UnarchiveState.NOT_STARTED) { | ||||
|     currentFilename = ""; | ||||
|     currentFileNumber = 0; | ||||
|     currentBytesUnarchivedInFile = 0; | ||||
|     currentBytesUnarchived = 0; | ||||
|     totalUncompressedBytesInArchive = 0; | ||||
|     totalFilesInArchive = 0; | ||||
|     allLocalFiles = []; | ||||
|    | ||||
|     postMessage(new bitjs.archive.UnarchiveStartEvent()); | ||||
|  | ||||
|     unarchiveState = UnarchiveState.UNARCHIVING; | ||||
|       // parse them into numbers and return comparison | ||||
|       var anum = parseInt(aname.substr(aindex), 10), | ||||
|           bnum = parseInt(bname.substr(bindex), 10); | ||||
|       return anum - bnum; | ||||
|       */ | ||||
|   }); | ||||
|  | ||||
|   // report # files and total length | ||||
|   if (localFiles.length > 0) { | ||||
|     postProgress(); | ||||
|   } | ||||
|  | ||||
|   if (unarchiveState === UnarchiveState.UNARCHIVING || | ||||
|     unarchiveState === UnarchiveState.WAITING) { | ||||
|     try { | ||||
|       untar(); | ||||
|       unarchiveState = UnarchiveState.FINISHED; | ||||
|       postMessage(new bitjs.archive.UnarchiveFinishEvent()); | ||||
|     } catch (e) { | ||||
|       if (typeof e === 'string' && e.startsWith('Error!  Overflowed')) { | ||||
|         // Overrun the buffer. | ||||
|         unarchiveState = UnarchiveState.WAITING; | ||||
|       } else { | ||||
|         console.error('Found an error while untarring'); | ||||
|         console.dir(e); | ||||
|         throw e; | ||||
|       } | ||||
|     } | ||||
|   // now do the shipping of each file | ||||
|   for (var i = 0; i < localFiles.length; ++i) { | ||||
|     var localfile = localFiles[i]; | ||||
|     info("Sending file '" + localfile.filename + "' up"); | ||||
|  | ||||
|     // update progress | ||||
|     currentFilename = localfile.filename; | ||||
|     currentFileNumber = i; | ||||
|     currentBytesUnarchivedInFile = localfile.size; | ||||
|     currentBytesUnarchived += localfile.size; | ||||
|     postMessage(new bitjs.archive.UnarchiveExtractEvent(localfile)); | ||||
|     postProgress(); | ||||
|   } | ||||
|  | ||||
|   postProgress(); | ||||
|  | ||||
|   postMessage(new bitjs.archive.UnarchiveFinishEvent()); | ||||
| }; | ||||
|  | ||||
| // event.data.file has the ArrayBuffer. | ||||
| onmessage = function(event) { | ||||
|   var ab = event.data.file; | ||||
|   untar(ab); | ||||
| }; | ||||
|   | ||||
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							| @@ -12,277 +12,224 @@ | ||||
| var bitjs = bitjs || {}; | ||||
| bitjs.io = bitjs.io || {}; | ||||
|  | ||||
| (function() { | ||||
|  | ||||
| /** | ||||
|  * This object allows you to peek and consume bits and bytes out of a stream. | ||||
|  * Note that this stream is optimized, and thus, will *NOT* throw an error if | ||||
|  * the end of the stream is reached.  Only use this in scenarios where you | ||||
|  * already have all the bits you need. | ||||
|  */ | ||||
| bitjs.io.BitStream = class { | ||||
|   /** | ||||
|    * @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 | ||||
|    */ | ||||
|   constructor(ab, rtl, opt_offset, opt_length) { | ||||
|     if (!(ab instanceof ArrayBuffer)) { | ||||
|       throw 'Error! BitArray constructed with an invalid ArrayBuffer object'; | ||||
|     } | ||||
|  | ||||
|     const offset = opt_offset || 0; | ||||
|     const length = opt_length || ab.byteLength; | ||||
|  | ||||
|     /** | ||||
|      * The bytes in the stream. | ||||
|      * @type {Uint8Array} | ||||
|      * @private | ||||
|      */ | ||||
|     this.bytes = new Uint8Array(ab, offset, length); | ||||
|  | ||||
|     /** | ||||
|      * The byte in the stream that we are currently on. | ||||
|      * @type {Number} | ||||
|      * @private | ||||
|      */ | ||||
|     this.bytePtr = 0; | ||||
|  | ||||
|     /** | ||||
|      * The bit in the current byte that we will read next (can have values 0 through 7). | ||||
|      * @type {Number} | ||||
|      * @private | ||||
|      */ | ||||
|     this.bitPtr = 0; // tracks which bit we are on (can have values 0 through 7) | ||||
|  | ||||
|     /** | ||||
|      * An ever-increasing number. | ||||
|      * @type {Number} | ||||
|      * @private | ||||
|      */ | ||||
|     this.bitsRead_ = 0; | ||||
|  | ||||
|     this.peekBits = rtl ? this.peekBits_rtl : this.peekBits_ltr; | ||||
|   } | ||||
|  | ||||
|   /** | ||||
|    * Returns how many bites have been read in the stream since the beginning of time. | ||||
|    */ | ||||
|   getNumBitsRead() { | ||||
|     return this.bitsRead_; | ||||
|   } | ||||
|  | ||||
|   /** | ||||
|    * Returns how many bits are currently in the stream left to be read. | ||||
|    */ | ||||
|   getNumBitsLeft() { | ||||
|     const bitsLeftInByte = 8 - this.bitPtr; | ||||
|     return (this.bytes.byteLength - this.bytePtr - 1) * 8 + bitsLeftInByte; | ||||
|   } | ||||
|  | ||||
|   /** | ||||
|    *   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, must be a positive integer. | ||||
|    * @param {boolean=} movePointers Whether to move the pointer, defaults false. | ||||
|    * @return {number} The peeked bits, as an unsigned number. | ||||
|    */ | ||||
|   peekBits_ltr(n, opt_movePointers) { | ||||
|     const NUM = parseInt(n, 10); | ||||
|     let num = NUM; | ||||
|     if (n !== num || num <= 0) { | ||||
|       return 0; | ||||
|     } | ||||
|  | ||||
|     const BITMASK = bitjs.io.BitStream.BITMASK; | ||||
|     const movePointers = opt_movePointers || false; | ||||
|     let bytes = this.bytes; | ||||
|     let bytePtr = this.bytePtr; | ||||
|     let bitPtr = this.bitPtr; | ||||
|     let result = 0; | ||||
|     let bitsIn = 0; | ||||
|  | ||||
|     // keep going until we have no more bits left to peek at | ||||
|     while (num > 0) { | ||||
|       // We overflowed the stream, so just return what we got. | ||||
|       if (bytePtr >= bytes.length) { | ||||
|         break; | ||||
|       } | ||||
|  | ||||
|       const numBitsLeftInThisByte = (8 - bitPtr); | ||||
|       if (num >= numBitsLeftInThisByte) { | ||||
|         const mask = (BITMASK[numBitsLeftInThisByte] << bitPtr); | ||||
|         result |= (((bytes[bytePtr] & mask) >> bitPtr) << bitsIn); | ||||
|  | ||||
|         bytePtr++; | ||||
|         bitPtr = 0; | ||||
|         bitsIn += numBitsLeftInThisByte; | ||||
|         num -= numBitsLeftInThisByte; | ||||
|       } else { | ||||
|         const mask = (BITMASK[num] << bitPtr); | ||||
|         result |= (((bytes[bytePtr] & mask) >> bitPtr) << bitsIn); | ||||
|  | ||||
|         bitPtr += num; | ||||
|         break; | ||||
|       } | ||||
|     } | ||||
|  | ||||
|     if (movePointers) { | ||||
|       this.bitPtr = bitPtr; | ||||
|       this.bytePtr = bytePtr; | ||||
|       this.bitsRead_ += NUM; | ||||
|     } | ||||
|  | ||||
|     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.  Must be a positive integer. | ||||
|    * @param {boolean=} movePointers Whether to move the pointer, defaults false. | ||||
|    * @return {number} The peeked bits, as an unsigned number. | ||||
|    */ | ||||
|   peekBits_rtl(n, opt_movePointers) { | ||||
|     const NUM = parseInt(n, 10); | ||||
|     let num = NUM; | ||||
|     if (n !== num || num <= 0) { | ||||
|       return 0; | ||||
|     } | ||||
|  | ||||
|     const BITMASK = bitjs.io.BitStream.BITMASK; | ||||
|     const movePointers = opt_movePointers || false; | ||||
|     let bytes = this.bytes; | ||||
|     let bytePtr = this.bytePtr; | ||||
|     let bitPtr = this.bitPtr; | ||||
|     let result = 0; | ||||
|  | ||||
|     // keep going until we have no more bits left to peek at | ||||
|     while (num > 0) { | ||||
|       // We overflowed the stream, so just return the bits we got. | ||||
|       if (bytePtr >= bytes.length) { | ||||
|         break; | ||||
|       } | ||||
|  | ||||
|       const numBitsLeftInThisByte = (8 - bitPtr); | ||||
|       if (num >= numBitsLeftInThisByte) { | ||||
|         result <<= numBitsLeftInThisByte; | ||||
|         result |= (BITMASK[numBitsLeftInThisByte] & bytes[bytePtr]); | ||||
|         bytePtr++; | ||||
|         bitPtr = 0; | ||||
|         num -= numBitsLeftInThisByte; | ||||
|       } else { | ||||
|         result <<= num; | ||||
|         const numBits = 8 - num - bitPtr; | ||||
|         result |= ((bytes[bytePtr] & (BITMASK[num] << numBits)) >> numBits); | ||||
|  | ||||
|         bitPtr += num; | ||||
|         break; | ||||
|       } | ||||
|     } | ||||
|  | ||||
|     if (movePointers) { | ||||
|       this.bitPtr = bitPtr; | ||||
|       this.bytePtr = bytePtr; | ||||
|       this.bitsRead_ += NUM; | ||||
|     } | ||||
|  | ||||
|     return result; | ||||
|   } | ||||
|  | ||||
|   /** | ||||
|    * Peek at 16 bits from current position in the buffer. | ||||
|    * Bit at (bytePtr,bitPtr) has the highest position in returning data. | ||||
|    * Taken from getbits.hpp in unrar. | ||||
|    * TODO: Move this out of BitStream and into unrar. | ||||
|    */ | ||||
|   getBits() { | ||||
|     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.  Must be a positive integer. | ||||
|    * @return {number} The read bits, as an unsigned number. | ||||
|    */ | ||||
|   readBits(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.  Must be a positive integer. | ||||
|    * @param {boolean=} movePointers Whether to move the pointer, defaults false. | ||||
|    * @return {Uint8Array} The subarray. | ||||
|    */ | ||||
|   peekBytes(n, opt_movePointers) { | ||||
|     const num = parseInt(n, 10); | ||||
|     if (n !== num || num < 0) { | ||||
|       throw 'Error!  Called peekBytes() with a non-positive integer: ' + n; | ||||
|     } else if (num === 0) { | ||||
|       return new Uint8Array(); | ||||
|     } | ||||
|  | ||||
|     // Flush bits until we are byte-aligned. | ||||
|     // 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); | ||||
|     } | ||||
|  | ||||
|     const numBytesLeft = this.getNumBitsLeft() / 8; | ||||
|     if (num > numBytesLeft) { | ||||
|       throw 'Error!  Overflowed the bit stream! n=' + num + ', bytePtr=' + this.bytePtr + | ||||
|           ', bytes.length=' + this.bytes.length + ', bitPtr=' + this.bitPtr; | ||||
|     } | ||||
|  | ||||
|     const movePointers = opt_movePointers || false; | ||||
|     const result = new Uint8Array(num); | ||||
|     let bytes = this.bytes; | ||||
|     let ptr = this.bytePtr; | ||||
|     let bytesLeftToCopy = num; | ||||
|     while (bytesLeftToCopy > 0) { | ||||
|       const bytesLeftInStream = bytes.length - ptr; | ||||
|       const sourceLength = Math.min(bytesLeftToCopy, bytesLeftInStream); | ||||
|  | ||||
|       result.set(bytes.subarray(ptr, ptr + sourceLength), num - bytesLeftToCopy); | ||||
|  | ||||
|       ptr += sourceLength; | ||||
|       // Overflowed the stream, just return what we got. | ||||
|       if (ptr >= bytes.length) { | ||||
|         break; | ||||
|       } | ||||
|  | ||||
|       bytesLeftToCopy -= sourceLength; | ||||
|     } | ||||
|  | ||||
|     if (movePointers) { | ||||
|       this.bytePtr += num; | ||||
|       this.bitsRead_ += (num * 8); | ||||
|     } | ||||
|  | ||||
|     return result; | ||||
|   } | ||||
|  | ||||
|   /** | ||||
|    * @param {number} n The number of bytes to read. | ||||
|    * @return {Uint8Array} The subarray. | ||||
|    */ | ||||
|   readBytes(n) { | ||||
|     return this.peekBytes(n, true); | ||||
|   } | ||||
| } | ||||
| // 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) | ||||
| bitjs.io.BitStream.BITMASK = [0, 0x01, 0x03, 0x07, 0x0F, 0x1F, 0x3F, 0x7F, 0xFF ]; | ||||
| 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; | ||||
| }; | ||||
|  | ||||
|  | ||||
| /** | ||||
|  * Peek at 16 bits from current position in the buffer. | ||||
|  * Bit at (bytePtr,bitPtr) has the highest position in returning data. | ||||
|  * Taken from getbits.hpp in unrar. | ||||
|  * TODO: Move this out of BitStream and into unrar. | ||||
|  */ | ||||
| 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); | ||||
| }; | ||||
|  | ||||
| })(); | ||||
|   | ||||
| @@ -12,106 +12,111 @@ | ||||
| var bitjs = bitjs || {}; | ||||
| bitjs.io = bitjs.io || {}; | ||||
|  | ||||
| (function() { | ||||
|  | ||||
|  | ||||
| /** | ||||
|  * 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 = class { | ||||
|   /** | ||||
|    * @param {number} numBytes The number of bytes to allocate. | ||||
|    */ | ||||
|   constructor(numBytes) { | ||||
|     if (typeof numBytes != typeof 1 || numBytes <= 0) { | ||||
|       throw "Error! ByteBuffer initialized with '" + numBytes + "'"; | ||||
|     } | ||||
|     this.data = new Uint8Array(numBytes); | ||||
|     this.ptr = 0; | ||||
| 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.<number>|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'; | ||||
|   } | ||||
|  | ||||
|  | ||||
|   /** | ||||
|    * @param {number} b The byte to insert. | ||||
|    */ | ||||
|   insertByte(b) { | ||||
|     // TODO: throw if byte is invalid? | ||||
|     this.data[this.ptr++] = b; | ||||
|   // 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; | ||||
|   } | ||||
|  | ||||
|   /** | ||||
|    * @param {Array.<number>|Uint8Array|Int8Array} bytes The bytes to insert. | ||||
|    */ | ||||
|   insertBytes(bytes) { | ||||
|     // TODO: throw if bytes is invalid? | ||||
|     this.data.set(bytes, this.ptr); | ||||
|     this.ptr += bytes.length; | ||||
|   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; | ||||
|   } | ||||
|  | ||||
|   /** | ||||
|    * 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. | ||||
|    */ | ||||
|   writeNumber(num, numBytes) { | ||||
|     if (numBytes < 1 || !numBytes) { | ||||
|       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. | ||||
|     const bytes = []; | ||||
|     while (numBytes-- > 0) { | ||||
|       const eightBits = num & 255; | ||||
|       bytes.push(eightBits); | ||||
|       num >>= 8; | ||||
|     } | ||||
|  | ||||
|     this.insertBytes(bytes); | ||||
|   var HALF = Math.pow(2, (numBytes * 8) - 1); | ||||
|   if (num >= HALF || num < -HALF) { | ||||
|     throw 'Trying to write ' + num + ' into only ' + numBytes + ' 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. | ||||
|    */ | ||||
|   writeSignedNumber(num, numBytes) { | ||||
|     if (numBytes < 1) { | ||||
|       throw 'Trying to write into too few bytes: ' + numBytes; | ||||
|     } | ||||
|  | ||||
|     const 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. | ||||
|     const bytes = []; | ||||
|     while (numBytes-- > 0) { | ||||
|       const eightBits = num & 255; | ||||
|       bytes.push(eightBits); | ||||
|       num >>= 8; | ||||
|     } | ||||
|  | ||||
|     this.insertBytes(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; | ||||
|   } | ||||
|  | ||||
|   /** | ||||
|    * @param {string} str The ASCII string to write. | ||||
|    */ | ||||
|   writeASCIIString(str) { | ||||
|     for (let i = 0; i < str.length; ++i) { | ||||
|       const curByte = str.charCodeAt(i); | ||||
|       if (curByte < 0 || curByte > 255) { | ||||
|         throw 'Trying to write a non-ASCII string!'; | ||||
|       } | ||||
|       this.insertByte(curByte); | ||||
|   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); | ||||
|   } | ||||
| }; | ||||
|  | ||||
| })(); | ||||
|   | ||||
| @@ -12,297 +12,153 @@ | ||||
| var bitjs = bitjs || {}; | ||||
| bitjs.io = bitjs.io || {}; | ||||
|  | ||||
| (function() { | ||||
|  | ||||
|  | ||||
| /** | ||||
|  * This object allows you to peek and consume bytes as numbers and strings out | ||||
|  * of a stream.  More bytes can be pushed into the back of the stream via the | ||||
|  * push() method. | ||||
|  * 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 = class { | ||||
|   /** | ||||
|    * @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(ab, opt_offset, opt_length) { | ||||
|     if (!(ab instanceof ArrayBuffer)) { | ||||
|       throw 'Error! BitArray constructed with an invalid ArrayBuffer object'; | ||||
|     } | ||||
| 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; | ||||
| }; | ||||
|  | ||||
|     const offset = opt_offset || 0; | ||||
|     const length = opt_length || ab.byteLength; | ||||
|  | ||||
|     /** | ||||
|      * The current page of bytes in the stream. | ||||
|      * @type {Uint8Array} | ||||
|      * @private | ||||
|      */ | ||||
|     this.bytes = new Uint8Array(ab, offset, length); | ||||
| /** | ||||
|  * 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; | ||||
|  | ||||
|     /** | ||||
|      * The next pages of bytes in the stream. | ||||
|      * @type {Array<Uint8Array>} | ||||
|      * @private | ||||
|      */ | ||||
|     this.pages_ = []; | ||||
|   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; | ||||
| }; | ||||
|  | ||||
|     /** | ||||
|      * The byte in the current page that we will read next. | ||||
|      * @type {Number} | ||||
|      * @private | ||||
|      */ | ||||
|     this.ptr = 0; | ||||
|  | ||||
|     /** | ||||
|      * An ever-increasing number. | ||||
|      * @type {Number} | ||||
|      * @private | ||||
|      */ | ||||
|     this.bytesRead_ = 0; | ||||
| /** | ||||
|  * 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; | ||||
|   } | ||||
|  | ||||
|   /** | ||||
|    * Returns how many bytes have been read in the stream since the beginning of time. | ||||
|    */ | ||||
|   getNumBytesRead() { | ||||
|     return this.bytesRead_; | ||||
|   } | ||||
|   var result = this.bytes.subarray(this.ptr, this.ptr + n); | ||||
|  | ||||
|   /** | ||||
|    * Returns how many bytes are currently in the stream left to be read. | ||||
|    */ | ||||
|   getNumBytesLeft() { | ||||
|     const bytesInCurrentPage = (this.bytes.byteLength - this.ptr); | ||||
|     return this.pages_.reduce((acc, arr) => acc + arr.length, bytesInCurrentPage); | ||||
|   } | ||||
|  | ||||
|   /** | ||||
|    * Move the pointer ahead n bytes.  If the pointer is at the end of the current array | ||||
|    * of bytes and we have another page of bytes, point at the new page.  This is a private | ||||
|    * method, no validation is done. | ||||
|    * @param {number} n Number of bytes to increment. | ||||
|    * @private | ||||
|    */ | ||||
|   movePointer_(n) { | ||||
|   if (movePointers) { | ||||
|     this.ptr += n; | ||||
|     this.bytesRead_ += n; | ||||
|     while (this.ptr >= this.bytes.length && this.pages_.length > 0) { | ||||
|       this.ptr -= this.bytes.length; | ||||
|       this.bytes = this.pages_.shift(); | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   /** | ||||
|    * Peeks at the next n bytes as an unsigned number but does not advance the | ||||
|    * pointer. | ||||
|    * @param {number} n The number of bytes to peek at.  Must be a positive integer. | ||||
|    * @return {number} The n bytes interpreted as an unsigned number. | ||||
|    */ | ||||
|   peekNumber(n) { | ||||
|     const num = parseInt(n, 10); | ||||
|     if (n !== num || num < 0) { | ||||
|       throw 'Error!  Called peekNumber() with a non-positive integer'; | ||||
|     } else if (num === 0) { | ||||
|       return 0; | ||||
|     } | ||||
|   return result; | ||||
| }; | ||||
|  | ||||
|     if (n > 4) { | ||||
|       throw 'Error!  Called peekNumber(' + n + | ||||
|           ') but this method can only reliably read numbers up to 4 bytes long'; | ||||
|     } | ||||
|  | ||||
|     if (this.getNumBytesLeft() < num) { | ||||
|       throw 'Error!  Overflowed the byte stream while peekNumber()! n=' + num + | ||||
|       ', ptr=' + this.ptr + ', bytes.length=' + this.getNumBytesLeft(); | ||||
|     } | ||||
| /** | ||||
|  * 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); | ||||
| }; | ||||
|  | ||||
|     let result = 0; | ||||
|     let curPage = this.bytes; | ||||
|     let pageIndex = 0; | ||||
|     let ptr = this.ptr; | ||||
|     for (let i = 0; i < num; ++i) { | ||||
|       result |= (curPage[ptr++] << (i * 8)); | ||||
|  | ||||
|       if (ptr >= curPage.length) { | ||||
|         curPage = this.pages_[pageIndex++]; | ||||
|         ptr = 0; | ||||
|       } | ||||
|     } | ||||
|  | ||||
|     return result; | ||||
| /** | ||||
|  * 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 ""; | ||||
|   } | ||||
|  | ||||
|  | ||||
|   /** | ||||
|    * 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.  Must be a positive integer. | ||||
|    * @return {number} The n bytes interpreted as an unsigned number. | ||||
|    */ | ||||
|   readNumber(n) { | ||||
|     const num = this.peekNumber(n); | ||||
|     this.movePointer_(n); | ||||
|     return num; | ||||
|   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 a signed number but does not advance the | ||||
|    * pointer. | ||||
|    * @param {number} n The number of bytes to read.  Must be a positive integer. | ||||
|    * @return {number} The bytes interpreted as a signed number. | ||||
|    */ | ||||
|   peekSignedNumber(n) { | ||||
|     let num = this.peekNumber(n); | ||||
|     const HALF = Math.pow(2, (n * 8) - 1); | ||||
|     const FULL = HALF * 2; | ||||
|  | ||||
|     if (num >= HALF) num -= FULL; | ||||
|  | ||||
|     return num; | ||||
|   } | ||||
| /** | ||||
|  * 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; | ||||
| }; | ||||
|  | ||||
|  | ||||
|   /** | ||||
|    * Returns the next n bytes as a signed number and advances the stream pointer. | ||||
|    * @param {number} n The number of bytes to read.  Must be a positive integer. | ||||
|    * @return {number} The bytes interpreted as a signed number. | ||||
|    */ | ||||
|   readSignedNumber(n) { | ||||
|     const num = this.peekSignedNumber(n); | ||||
|     this.movePointer_(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.  Must be a positive integer. | ||||
|    * @param {boolean} movePointers Whether to move the pointers. | ||||
|    * @return {Uint8Array} The subarray. | ||||
|    */ | ||||
|   peekBytes(n, movePointers) { | ||||
|     const num = parseInt(n, 10); | ||||
|     if (n !== num || num < 0) { | ||||
|       throw 'Error!  Called peekBytes() with a non-positive integer'; | ||||
|     } else if (num === 0) { | ||||
|       return new Uint8Array(); | ||||
|     } | ||||
|  | ||||
|     const totalBytesLeft = this.getNumBytesLeft(); | ||||
|     if (num > totalBytesLeft) { | ||||
|       throw 'Error!  Overflowed the byte stream during peekBytes! n=' + num + | ||||
|           ', ptr=' + this.ptr + ', bytes.length=' + this.getNumBytesLeft(); | ||||
|     } | ||||
|  | ||||
|     const result = new Uint8Array(num); | ||||
|     let curPage = this.bytes; | ||||
|     let ptr = this.ptr; | ||||
|     let bytesLeftToCopy = num; | ||||
|     let pageIndex = 0; | ||||
|     while (bytesLeftToCopy > 0) { | ||||
|       const bytesLeftInPage = curPage.length - ptr; | ||||
|       const sourceLength = Math.min(bytesLeftToCopy, bytesLeftInPage); | ||||
|  | ||||
|       result.set(curPage.subarray(ptr, ptr + sourceLength), num - bytesLeftToCopy); | ||||
|  | ||||
|       ptr += sourceLength; | ||||
|       if (ptr >= curPage.length) { | ||||
|         curPage = this.pages_[pageIndex++]; | ||||
|         ptr = 0; | ||||
|       } | ||||
|  | ||||
|       bytesLeftToCopy -= sourceLength; | ||||
|     } | ||||
|  | ||||
|     if (movePointers) { | ||||
|       this.movePointer_(num); | ||||
|     } | ||||
|  | ||||
|     return result; | ||||
|   } | ||||
|  | ||||
|   /** | ||||
|    * Reads the next n bytes as a sub-array. | ||||
|    * @param {number} n The number of bytes to read.  Must be a positive integer. | ||||
|    * @return {Uint8Array} The subarray. | ||||
|    */ | ||||
|   readBytes(n) { | ||||
|     return this.peekBytes(n, true); | ||||
|   } | ||||
|  | ||||
|   /** | ||||
|    * Peeks at the next n bytes as an ASCII string but does not advance the pointer. | ||||
|    * @param {number} n The number of bytes to peek at.  Must be a positive integer. | ||||
|    * @return {string} The next n bytes as a string. | ||||
|    */ | ||||
|   peekString(n) { | ||||
|     const num = parseInt(n, 10); | ||||
|     if (n !== num || num < 0) { | ||||
|       throw 'Error!  Called peekString() with a non-positive integer'; | ||||
|     } else if (num === 0) { | ||||
|       return ''; | ||||
|     } | ||||
|  | ||||
|     const totalBytesLeft = this.getNumBytesLeft(); | ||||
|     if (num > totalBytesLeft) { | ||||
|       throw 'Error!  Overflowed the byte stream while peekString()! n=' + num + | ||||
|       ', ptr=' + this.ptr + ', bytes.length=' + this.getNumBytesLeft(); | ||||
|     } | ||||
|  | ||||
|     let result = new Array(num); | ||||
|     let curPage = this.bytes; | ||||
|     let pageIndex = 0; | ||||
|     let ptr = this.ptr; | ||||
|     for (let i = 0; i < num; ++i) { | ||||
|       result[i] = String.fromCharCode(curPage[ptr++]); | ||||
|       if (ptr >= curPage.length) { | ||||
|         curPage = this.pages_[pageIndex++]; | ||||
|         ptr = 0; | ||||
|       } | ||||
|     } | ||||
|  | ||||
|     return result.join(''); | ||||
|   } | ||||
|  | ||||
|   /** | ||||
|    * 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.  Must be a positive integer. | ||||
|    * @return {string} The next n bytes as a string. | ||||
|    */ | ||||
|   readString(n) { | ||||
|     const strToReturn = this.peekString(n); | ||||
|     this.movePointer_(n); | ||||
|     return strToReturn; | ||||
|   } | ||||
|  | ||||
|   /** | ||||
|    * Feeds more bytes into the back of the stream. | ||||
|    * @param {ArrayBuffer} ab  | ||||
|    */ | ||||
|   push(ab) { | ||||
|     if (!(ab instanceof ArrayBuffer)) { | ||||
|       throw 'Error! ByteStream.push() called with an invalid ArrayBuffer object'; | ||||
|     } | ||||
|  | ||||
|     this.pages_.push(new Uint8Array(ab)); | ||||
|     // If the pointer is at the end of the current page of bytes, this will advance | ||||
|     // to the next page. | ||||
|     this.movePointer_(0); | ||||
|   } | ||||
|  | ||||
|   /** | ||||
|    * Creates a new ByteStream from this ByteStream that can be read / peeked. | ||||
|    * @return {bitjs.io.ByteStream} A clone of this ByteStream. | ||||
|    */ | ||||
|   tee() { | ||||
|     const clone = new bitjs.io.ByteStream(this.bytes.buffer); | ||||
|     clone.bytes = this.bytes; | ||||
|     clone.ptr = this.ptr; | ||||
|     clone.pages_ = this.pages_.slice(); | ||||
|     clone.bytesRead_ = this.bytesRead_; | ||||
|     return clone; | ||||
|   } | ||||
| } | ||||
| })(); | ||||
|   | ||||
| @@ -160,7 +160,7 @@ function initProgressClick() { | ||||
| function loadFromArrayBuffer(ab) { | ||||
|     var start = (new Date).getTime(); | ||||
|     var h = new Uint8Array(ab, 0, 10); | ||||
|     var pathToBitJS = "../../static/js/"; | ||||
|     var pathToBitJS = "../../static/js/archive/"; | ||||
|     if (h[0] === 0x52 && h[1] === 0x61 && h[2] === 0x72 && h[3] === 0x21) { //Rar! | ||||
|         unarchiver = new bitjs.archive.Unrarrer(ab, pathToBitJS); | ||||
|     } else if (h[0] === 80 && h[1] === 75) { //PK (Zip) | ||||
|   | ||||
		Reference in New Issue
	
	Block a user
	 subdiox
					subdiox