Downgrade bitjs to es5 branch

pull/911/head
subdiox 6 years ago
parent c0d136ccd8
commit 7982ed877c

@ -11,21 +11,65 @@
var bitjs = bitjs || {}; var bitjs = bitjs || {};
bitjs.archive = bitjs.archive || {}; 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. * 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) { this.type = type;
/** };
* The event type.
* @type {string}
*/
this.type = type;
}
}
/** /**
* The UnarchiveEvent types. * The UnarchiveEvent types.
@ -41,102 +85,78 @@ bitjs.archive.UnarchiveEvent.Type = {
/** /**
* Useful for passing info up to the client (for debugging). * Useful for passing info up to the client (for debugging).
*
* @param {string} msg The info message.
*/ */
bitjs.archive.UnarchiveInfoEvent = class extends bitjs.archive.UnarchiveEvent { bitjs.archive.UnarchiveInfoEvent = function(msg) {
bitjs.base(this, bitjs.archive.UnarchiveEvent.Type.INFO);
/** /**
* @param {string} msg The info message. * The information message.
*
* @type {string}
*/ */
constructor(msg) { this.msg = msg;
super(bitjs.archive.UnarchiveEvent.Type.INFO); };
bitjs.inherits(bitjs.archive.UnarchiveInfoEvent, bitjs.archive.UnarchiveEvent);
/**
* The information message.
* @type {string}
*/
this.msg = msg;
}
}
/** /**
* An unrecoverable error has occured. * An unrecoverable error has occured.
*
* @param {string} msg The error message.
*/ */
bitjs.archive.UnarchiveErrorEvent = class extends bitjs.archive.UnarchiveEvent { bitjs.archive.UnarchiveErrorEvent = function(msg) {
bitjs.base(this, bitjs.archive.UnarchiveEvent.Type.ERROR);
/** /**
* @param {string} msg The error message. * The information message.
*
* @type {string}
*/ */
constructor(msg) { this.msg = msg;
super(bitjs.archive.UnarchiveEvent.Type.ERROR); };
bitjs.inherits(bitjs.archive.UnarchiveErrorEvent, bitjs.archive.UnarchiveEvent);
/**
* The information message.
* @type {string}
*/
this.msg = msg;
}
}
/** /**
* Start event. * Start event.
*
* @param {string} msg The info message.
*/ */
bitjs.archive.UnarchiveStartEvent = class extends bitjs.archive.UnarchiveEvent { bitjs.archive.UnarchiveStartEvent = function() {
constructor() { bitjs.base(this, bitjs.archive.UnarchiveEvent.Type.START);
super(bitjs.archive.UnarchiveEvent.Type.START); };
} bitjs.inherits(bitjs.archive.UnarchiveStartEvent, bitjs.archive.UnarchiveEvent);
}
/** /**
* Finish event. * Finish event.
*
* @param {string} msg The info message.
*/ */
bitjs.archive.UnarchiveFinishEvent = class extends bitjs.archive.UnarchiveEvent { bitjs.archive.UnarchiveFinishEvent = function() {
constructor() { bitjs.base(this, bitjs.archive.UnarchiveEvent.Type.FINISH);
super(bitjs.archive.UnarchiveEvent.Type.FINISH); };
} bitjs.inherits(bitjs.archive.UnarchiveFinishEvent, bitjs.archive.UnarchiveEvent);
}
/** /**
* Progress event. * Progress event.
*/ */
bitjs.archive.UnarchiveProgressEvent = class extends bitjs.archive.UnarchiveEvent { bitjs.archive.UnarchiveProgressEvent = function(
/** currentFilename,
* @param {string} currentFilename currentFileNumber,
* @param {number} currentFileNumber currentBytesUnarchivedInFile,
* @param {number} currentBytesUnarchivedInFile currentBytesUnarchived,
* @param {number} currentBytesUnarchived totalUncompressedBytesInArchive,
* @param {number} totalUncompressedBytesInArchive totalFilesInArchive) {
* @param {number} totalFilesInArchive bitjs.base(this, bitjs.archive.UnarchiveEvent.Type.PROGRESS);
* @param {number} totalCompressedBytesRead
*/ this.currentFilename = currentFilename;
constructor(currentFilename, currentFileNumber, currentBytesUnarchivedInFile, this.currentFileNumber = currentFileNumber;
currentBytesUnarchived, totalUncompressedBytesInArchive, totalFilesInArchive, this.currentBytesUnarchivedInFile = currentBytesUnarchivedInFile;
totalCompressedBytesRead) { this.totalFilesInArchive = totalFilesInArchive;
super(bitjs.archive.UnarchiveEvent.Type.PROGRESS); this.currentBytesUnarchived = currentBytesUnarchived;
this.totalUncompressedBytesInArchive = totalUncompressedBytesInArchive;
this.currentFilename = currentFilename; };
this.currentFileNumber = currentFileNumber; bitjs.inherits(bitjs.archive.UnarchiveProgressEvent, bitjs.archive.UnarchiveEvent);
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;
}
}
/** /**
* All extracted files returned by an Unarchiver will implement * 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. * @type {UnarchivedFile}
* @param {string} opt_pathToBitJS Optional string for where the BitJS files are located.
*/ */
constructor(arrayBuffer, opt_pathToBitJS) { this.unarchivedFile = unarchivedFile;
/** };
* The ArrayBuffer object. bitjs.inherits(bitjs.archive.UnarchiveExtractEvent, bitjs.archive.UnarchiveEvent);
* @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.<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. * The ArrayBuffer object.
* @return {string} The script filename. * @type {ArrayBuffer}
* @protected. * @protected
*/ */
getScriptFileName() { this.ab = arrayBuffer;
throw 'Subclasses of AbstractUnarchiver must overload getScriptFileName()';
}
/** /**
* Adds an event listener for UnarchiveEvents. * The path to the BitJS files.
* * @type {string}
* @param {string} Event type. * @private
* @param {function} An event handler function.
*/ */
addEventListener(type, listener) { this.pathToBitJS_ = opt_pathToBitJS || '/';
if (type in this.listeners_) {
if (this.listeners_[type].indexOf(listener) == -1) {
this.listeners_[type].push(listener);
}
}
}
/** /**
* Removes an event listener. * A map from event type to an array of listeners.
* * @type {Map.<string, Array>}
* @param {string} Event type.
* @param {EventListener|function} An event listener or handler function.
*/ */
removeEventListener(type, listener) { this.listeners_ = {};
if (type in this.listeners_) { for (var type in bitjs.archive.UnarchiveEvent.Type) {
const index = this.listeners_[type].indexOf(listener); this.listeners_[bitjs.archive.UnarchiveEvent.Type[type]] = [];
if (index != -1) {
this.listeners_[type].splice(index, 1);
}
}
} }
};
/** /**
* Receive an event and pass it to the listener functions. * Private web worker initialized during start().
* * @type {Worker}
* @param {bitjs.archive.UnarchiveEvent} e * @private
* @private */
*/ bitjs.archive.Unarchiver.prototype.worker_ = null;
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);
}
}
/** /**
* Starts the unarchive in a separate Web Worker thread and returns immediately. * This method must be overridden by the subclass to return the script filename.
*/ * @return {string} The script filename.
start() { * @protected.
const me = this; */
const scriptFileName = this.pathToBitJS_ + this.getScriptFileName(); bitjs.archive.Unarchiver.prototype.getScriptFileName = function() {
if (scriptFileName) { throw 'Subclasses of AbstractUnarchiver must overload getScriptFileName()';
this.worker_ = new Worker(scriptFileName); };
this.worker_.onerror = function(e) { /**
console.log('Worker error: message = ' + e.message); * Adds an event listener for UnarchiveEvents.
throw e; *
}; * @param {string} Event type.
* @param {function} An event handler function.
this.worker_.onmessage = function(e) { */
if (typeof e.data == 'string') { bitjs.archive.Unarchiver.prototype.addEventListener = function(type, listener) {
// Just log any strings the workers pump our way. if (type in this.listeners_) {
console.log(e.data); if (this.listeners_[type].indexOf(listener) == -1) {
} else { this.listeners_[type].push(listener);
// 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);
}
};
const ab = this.ab;
this.worker_.postMessage({
file: ab,
logToConsole: false,
});
this.ab = null;
} }
} }
};
/** /**
* Adds more bytes to the unarchiver's Worker thread. * Removes an event listener.
*/ *
update(ab) { * @param {string} Event type.
if (this.worker_) { * @param {EventListener|function} An event listener or handler function.
this.worker_.postMessage({bytes: ab}); */
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. * Receive an event and pass it to the listener functions.
*/ *
stop() { * @param {bitjs.archive.UnarchiveEvent} e
if (this.worker_) { * @private
this.worker_.terminate(); */
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 * Terminates the Web Worker for this Unarchiver and returns immediately.
*/ */
bitjs.archive.Unzipper = class extends bitjs.archive.Unarchiver { bitjs.archive.Unarchiver.prototype.stop = function() {
constructor(arrayBuffer, opt_pathToBitJS) { if (this.worker_) {
super(arrayBuffer, opt_pathToBitJS); this.worker_.terminate();
} }
};
getScriptFileName() { return 'archive/unzip.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 * Unrarrer
* @extends {bitjs.archive.Unarchiver}
* @constructor
*/ */
bitjs.archive.Unrarrer = class extends bitjs.archive.Unarchiver { bitjs.archive.Unrarrer = function(arrayBuffer, opt_pathToBitJS) {
constructor(arrayBuffer, opt_pathToBitJS) { bitjs.base(this, arrayBuffer, opt_pathToBitJS);
super(arrayBuffer, opt_pathToBitJS); };
} bitjs.inherits(bitjs.archive.Unrarrer, bitjs.archive.Unarchiver);
bitjs.archive.Unrarrer.prototype.getScriptFileName = function() { return 'unrar.js' };
getScriptFileName() { return 'archive/unrar.js'; }
}
/** /**
* Untarrer * Untarrer
* @extends {bitjs.archive.Unarchiver} * @extends {bitjs.archive.Unarchiver}
* @constructor * @constructor
*/ */
bitjs.archive.Untarrer = class extends bitjs.archive.Unarchiver { bitjs.archive.Untarrer = function(arrayBuffer, opt_pathToBitJS) {
constructor(arrayBuffer, opt_pathToBitJS) { bitjs.base(this, arrayBuffer, opt_pathToBitJS);
super(arrayBuffer, opt_pathToBitJS); };
} bitjs.inherits(bitjs.archive.Untarrer, bitjs.archive.Unarchiver);
bitjs.archive.Untarrer.prototype.getScriptFileName = function() { return 'untar.js' };
getScriptFileName() { return 'archive/untar.js'; };
}
/** /**
* Factory method that creates an unarchiver based on the byte signature found * 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} * @return {bitjs.archive.Unarchiver}
*/ */
bitjs.archive.GetUnarchiver = function(ab, opt_pathToBitJS) { bitjs.archive.GetUnarchiver = function(ab, opt_pathToBitJS) {
if (ab.byteLength < 10) { var unarchiver = null;
return null; var pathToBitJS = opt_pathToBitJS || '';
} var h = new Uint8Array(ab, 0, 10);
let unarchiver = null;
const pathToBitJS = opt_pathToBitJS || '';
const h = new Uint8Array(ab, 0, 10);
if (h[0] == 0x52 && h[1] == 0x61 && h[2] == 0x72 && h[3] == 0x21) { // Rar! if (h[0] == 0x52 && h[1] == 0x61 && h[2] == 0x72 && h[3] == 0x21) { // Rar!
unarchiver = new bitjs.archive.Unrarrer(ab, pathToBitJS); 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); unarchiver = new bitjs.archive.Unzipper(ab, pathToBitJS);
} else { // Try with tar } else { // Try with tar
unarchiver = new bitjs.archive.Untarrer(ab, pathToBitJS); unarchiver = new bitjs.archive.Untarrer(ab, pathToBitJS);
} }
return unarchiver; 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('../io/bytestream.js');
importScripts('archive.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. // Progress variables.
let currentFilename = ""; var currentFilename = "";
let currentFileNumber = 0; var currentFileNumber = 0;
let currentBytesUnarchivedInFile = 0; var currentBytesUnarchivedInFile = 0;
let currentBytesUnarchived = 0; var currentBytesUnarchived = 0;
let totalUncompressedBytesInArchive = 0; var totalUncompressedBytesInArchive = 0;
let totalFilesInArchive = 0; var totalFilesInArchive = 0;
// Helper functions. // Helper functions.
const info = function(str) { var info = function(str) {
postMessage(new bitjs.archive.UnarchiveInfoEvent(str)); postMessage(new bitjs.archive.UnarchiveInfoEvent(str));
}; };
const err = function(str) { var err = function(str) {
postMessage(new bitjs.archive.UnarchiveErrorEvent(str)); postMessage(new bitjs.archive.UnarchiveErrorEvent(str));
}; };
const postProgress = function() { var postProgress = function() {
postMessage(new bitjs.archive.UnarchiveProgressEvent( postMessage(new bitjs.archive.UnarchiveProgressEvent(
currentFilename, currentFilename,
currentFileNumber, currentFileNumber,
currentBytesUnarchivedInFile, currentBytesUnarchivedInFile,
currentBytesUnarchived, currentBytesUnarchived,
totalUncompressedBytesInArchive, totalUncompressedBytesInArchive,
totalFilesInArchive, totalFilesInArchive));
bytestream.getNumBytesRead(),
));
}; };
// Removes all characters from the first zero-byte in the string onwards. // Removes all characters from the first zero-byte in the string onwards.
const readCleanString = function(bstr, numBytes) { var readCleanString = function(bstr, numBytes) {
const str = bstr.readString(numBytes); var str = bstr.readString(numBytes);
const zIndex = str.indexOf(String.fromCharCode(0)); var zIndex = str.indexOf(String.fromCharCode(0));
return zIndex != -1 ? str.substr(0, zIndex) : str; return zIndex != -1 ? str.substr(0, zIndex) : str;
}; };
class TarLocalFile { // takes a ByteStream and parses out the local file information
// takes a ByteStream and parses out the local file information var TarLocalFile = function(bstream) {
constructor(bstream) { this.isValid = false;
this.isValid = false;
// Read in the header block
let bytesRead = 0; this.name = readCleanString(bstream, 100);
this.mode = readCleanString(bstream, 8);
// Read in the header block this.uid = readCleanString(bstream, 8);
this.name = readCleanString(bstream, 100); this.gid = readCleanString(bstream, 8);
this.mode = readCleanString(bstream, 8); this.size = parseInt(readCleanString(bstream, 12), 8);
this.uid = readCleanString(bstream, 8); this.mtime = readCleanString(bstream, 12);
this.gid = readCleanString(bstream, 8); this.chksum = readCleanString(bstream, 8);
this.size = parseInt(readCleanString(bstream, 12), 8); this.typeflag = readCleanString(bstream, 1);
this.mtime = readCleanString(bstream, 12); this.linkname = readCleanString(bstream, 100);
this.chksum = readCleanString(bstream, 8); this.maybeMagic = readCleanString(bstream, 6);
this.typeflag = readCleanString(bstream, 1);
this.linkname = readCleanString(bstream, 100); if (this.maybeMagic == "ustar") {
this.maybeMagic = readCleanString(bstream, 6); this.version = readCleanString(bstream, 2);
this.uname = readCleanString(bstream, 32);
if (this.maybeMagic == "ustar") { this.gname = readCleanString(bstream, 32);
this.version = readCleanString(bstream, 2); this.devmajor = readCleanString(bstream, 8);
this.uname = readCleanString(bstream, 32); this.devminor = readCleanString(bstream, 8);
this.gname = readCleanString(bstream, 32); this.prefix = readCleanString(bstream, 155);
this.devmajor = readCleanString(bstream, 8);
this.devminor = readCleanString(bstream, 8); if (this.prefix.length) {
this.prefix = readCleanString(bstream, 155); this.name = this.prefix + this.name;
}
if (this.prefix.length) { bstream.readBytes(12); // 512 - 500
this.name = this.prefix + this.name; } else {
} bstream.readBytes(255); // 512 - 257
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;
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;
} }
bytesRead += 512; bstream.readBytes(this.size);
// Done header, now rest of blocks are the file contents. // Round up to 512-byte blocks.
this.filename = this.name; var remaining = 512 - bstream.ptr % 512;
this.fileData = null; if (remaining > 0 && remaining < 512) {
bstream.readBytes(remaining);
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.")
} }
} else if (this.typeflag == 5) {
info(" This is a directory.")
} }
} };
const untar = function() { // Takes an ArrayBuffer of a tar file in
let bstream = bytestream.tee(); // 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 we don't encounter an empty block, keep making TarLocalFiles.
while (bstream.peekNumber(4) != 0) { while (bstream.peekNumber(4) != 0) {
const oneLocalFile = new TarLocalFile(bstream); var oneLocalFile = new TarLocalFile(bstream);
if (oneLocalFile && oneLocalFile.isValid) { if (oneLocalFile && oneLocalFile.isValid) {
// If we make it to this point and haven't thrown an error, we have successfully localFiles.push(oneLocalFile);
// read in the data for a local file, so we can update the actual bytestream.
bytestream = bstream.tee();
allLocalFiles.push(oneLocalFile);
totalUncompressedBytesInArchive += oneLocalFile.size; 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;
// 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;
// extract the number at the end of both filenames
/*
var aname = a.filename;
var bname = b.filename;
var aindex = aname.length, bindex = bname.length;
// 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;
// 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;
// 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();
}
// 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(); postProgress();
bytestream = bstream.tee(); postMessage(new bitjs.archive.UnarchiveFinishEvent());
}; };
// event.data.file has the first ArrayBuffer. // event.data.file has the ArrayBuffer.
// event.data.bytes has all subsequent ArrayBuffers.
onmessage = function(event) { onmessage = function(event) {
const bytes = event.data.file || event.data.bytes; var ab = event.data.file;
logToConsole = !!event.data.logToConsole; untar(ab);
// 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);
}
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;
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;
}
}
}
}; };

File diff suppressed because it is too large Load Diff

@ -12,277 +12,224 @@
var bitjs = bitjs || {}; var bitjs = bitjs || {};
bitjs.io = bitjs.io || {}; 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 object allows you to peek and consume bits and bytes out of a stream. * This bit stream peeks and consumes bits out of a binary 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 * @param {ArrayBuffer} ab An ArrayBuffer object or a Uint8Array.
* already have all the bits you need. * @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 = class { bitjs.io.BitStream = function(ab, rtl, opt_offset, opt_length) {
/** if (!ab || !ab.toString || ab.toString() !== "[object ArrayBuffer]") {
* @param {ArrayBuffer} ab An ArrayBuffer object or a Uint8Array. throw "Error! BitArray constructed with an invalid ArrayBuffer object";
* @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;
} }
/** var offset = opt_offset || 0;
* Returns how many bites have been read in the stream since the beginning of time. var length = opt_length || ab.byteLength;
*/ this.bytes = new Uint8Array(ab, offset, length);
getNumBitsRead() { this.bytePtr = 0; // tracks which byte we are on
return this.bitsRead_; this.bitPtr = 0; // tracks which bit we are on (can have values 0 through 7)
} this.peekBits = rtl ? this.peekBits_rtl : this.peekBits_ltr;
};
/** /**
* Returns how many bits are currently in the stream left to be read. * byte0 byte1 byte2 byte3
*/ * 7......0 | 7......0 | 7......0 | 7......0
getNumBitsLeft() { *
const bitsLeftInByte = 8 - this.bitPtr; * The bit pointer starts at bit0 of byte0 and moves left until it reaches
return (this.bytes.byteLength - this.bytePtr - 1) * 8 + bitsLeftInByte; * 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,
* byte0 byte1 byte2 byte3 bytePtr = this.bytePtr,
* 7......0 | 7......0 | 7......0 | 7......0 bitPtr = this.bitPtr,
* result = 0,
* The bit pointer starts at bit0 of byte0 and moves left until it reaches bitsIn = 0,
* bit7 of byte0, then jumps to bit0 of byte1, etc. bytes = this.bytes;
* @param {number} n The number of bits to peek, must be a positive integer.
* @param {boolean=} movePointers Whether to move the pointer, defaults false. // keep going until we have no more bits left to peek at
* @return {number} The peeked bits, as an unsigned number. // 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.
peekBits_ltr(n, opt_movePointers) { // This could be considerably faster when reading more than 3 or 4 bits at a time.
const NUM = parseInt(n, 10); while (n > 0) {
let num = NUM; if (bytePtr >= bytes.length) {
if (n !== num || num <= 0) { throw "Error! Overflowed the bit stream! n=" + n + ", bytePtr=" + bytePtr + ", bytes.length=" +
return 0; bytes.length + ", bitPtr=" + bitPtr;
return -1;
} }
const BITMASK = bitjs.io.BitStream.BITMASK; var numBitsLeftInThisByte = (8 - bitPtr);
const movePointers = opt_movePointers || false; if (n >= numBitsLeftInThisByte) {
let bytes = this.bytes; var mask = (BITMASK[numBitsLeftInThisByte] << bitPtr);
let bytePtr = this.bytePtr; result |= (((bytes[bytePtr] & mask) >> bitPtr) << bitsIn);
let bitPtr = this.bitPtr;
let result = 0; bytePtr++;
let bitsIn = 0; bitPtr = 0;
bitsIn += numBitsLeftInThisByte;
// keep going until we have no more bits left to peek at n -= numBitsLeftInThisByte;
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;
}
} }
else {
var mask = (BITMASK[n] << bitPtr);
result |= (((bytes[bytePtr] & mask) >> bitPtr) << bitsIn);
if (movePointers) { bitPtr += n;
this.bitPtr = bitPtr; bitsIn += n;
this.bytePtr = bytePtr; n = 0;
this.bitsRead_ += NUM;
} }
}
return result; 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. 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; * byte0 byte1 byte2 byte3
let bytes = this.bytes; * 7......0 | 7......0 | 7......0 | 7......0
let bytePtr = this.bytePtr; *
let bitPtr = this.bitPtr; * The bit pointer starts at bit7 of byte0 and moves right until it reaches
let result = 0; * bit0 of byte0, then goes to bit7 of byte1, etc.
* @param {number} n The number of bits to peek.
// keep going until we have no more bits left to peek at * @param {boolean=} movePointers Whether to move the pointer, defaults false.
while (num > 0) { * @return {number} The peeked bits, as an unsigned number.
// We overflowed the stream, so just return the bits we got. */
if (bytePtr >= bytes.length) { bitjs.io.BitStream.prototype.peekBits_rtl = function(n, movePointers) {
break; if (n <= 0 || typeof n != typeof 1) {
} return 0;
}
const numBitsLeftInThisByte = (8 - bitPtr);
if (num >= numBitsLeftInThisByte) { var movePointers = movePointers || false,
result <<= numBitsLeftInThisByte; bytePtr = this.bytePtr,
result |= (BITMASK[numBitsLeftInThisByte] & bytes[bytePtr]); bitPtr = this.bitPtr,
bytePtr++; result = 0,
bitPtr = 0; bytes = this.bytes;
num -= numBitsLeftInThisByte;
} else { // keep going until we have no more bits left to peek at
result <<= num; // TODO: Consider putting all bits from bytes we will need into a variable and then
const numBits = 8 - num - bitPtr; // shifting/masking it to just extract the bits we want.
result |= ((bytes[bytePtr] & (BITMASK[num] << numBits)) >> numBits); // This could be considerably faster when reading more than 3 or 4 bits at a time.
while (n > 0) {
bitPtr += num;
break; if (bytePtr >= bytes.length) {
} throw "Error! Overflowed the bit stream! n=" + n + ", bytePtr=" + bytePtr + ", bytes.length=" +
bytes.length + ", bitPtr=" + bitPtr;
return -1;
} }
if (movePointers) { var numBitsLeftInThisByte = (8 - bitPtr);
this.bitPtr = bitPtr; if (n >= numBitsLeftInThisByte) {
this.bytePtr = bytePtr; result <<= numBitsLeftInThisByte;
this.bitsRead_ += NUM; result |= (BITMASK[numBitsLeftInThisByte] & bytes[bytePtr]);
bytePtr++;
bitPtr = 0;
n -= numBitsLeftInThisByte;
} }
else {
result <<= n;
result |= ((bytes[bytePtr] & (BITMASK[n] << (8 - n - bitPtr))) >> (8 - n - bitPtr));
return result; bitPtr += n;
n = 0;
}
} }
/** if (movePointers) {
* Peek at 16 bits from current position in the buffer. this.bitPtr = bitPtr;
* Bit at (bytePtr,bitPtr) has the highest position in returning data. this.bytePtr = bytePtr;
* 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);
} }
/** return result;
* 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 * Peek at 16 bits from current position in the buffer.
// "Any bits of input up to the next byte boundary are ignored." * Bit at (bytePtr,bitPtr) has the highest position in returning data.
while (this.bitPtr != 0) { * Taken from getbits.hpp in unrar.
this.readBits(1); * 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);
};
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); * Reads n bits out of the stream, consuming them (moving the bit pointer).
let bytes = this.bytes; * @param {number} n The number of bits to read.
let ptr = this.bytePtr; * @return {number} The read bits, as an unsigned number.
let bytesLeftToCopy = num; */
while (bytesLeftToCopy > 0) { bitjs.io.BitStream.prototype.readBits = function(n) {
const bytesLeftInStream = bytes.length - ptr; return this.peekBits(n, true);
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. * This returns n bytes as a sub-array, advancing the pointer if movePointers
if (ptr >= bytes.length) { * is true. Only use this for uncompressed blocks as this throws away remaining
break; * 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;
}
bytesLeftToCopy -= sourceLength; // 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);
}
if (movePointers) { var movePointers = movePointers || false;
this.bytePtr += num; var bytePtr = this.bytePtr,
this.bitsRead_ += (num * 8); bitPtr = this.bitPtr;
}
return result; var result = this.bytes.subarray(bytePtr, bytePtr + n);
}
/** if (movePointers) {
* @param {number} n The number of bytes to read. this.bytePtr += n;
* @return {Uint8Array} The subarray.
*/
readBytes(n) {
return this.peekBytes(n, true);
} }
}
// mask for getting N number of bits (0-8) return result;
bitjs.io.BitStream.BITMASK = [0, 0x01, 0x03, 0x07, 0x0F, 0x1F, 0x3F, 0x7F, 0xFF ]; };
/**
* @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 || {}; var bitjs = bitjs || {};
bitjs.io = bitjs.io || {}; bitjs.io = bitjs.io || {};
(function() {
/** /**
* A write-only Byte buffer which uses a Uint8 Typed Array as a backing store. * 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 { bitjs.io.ByteBuffer = function(numBytes) {
/** if (typeof numBytes != typeof 1 || numBytes <= 0) {
* @param {number} numBytes The number of bytes to allocate. throw "Error! ByteBuffer initialized with '" + numBytes + "'";
*/
constructor(numBytes) {
if (typeof numBytes != typeof 1 || numBytes <= 0) {
throw "Error! ByteBuffer initialized with '" + numBytes + "'";
}
this.data = new Uint8Array(numBytes);
this.ptr = 0;
} }
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 {number} b The byte to insert. * @param {Array.<number>|Uint8Array|Int8Array} bytes The bytes to insert.
*/ */
insertByte(b) { bitjs.io.ByteBuffer.prototype.insertBytes = function(bytes) {
// TODO: throw if byte is invalid? // TODO: throw if bytes is invalid?
this.data[this.ptr++] = b; 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.
* @param {Array.<number>|Uint8Array|Int8Array} bytes The bytes to insert. var bytes = [];
*/ while (numBytes-- > 0) {
insertBytes(bytes) { var eightBits = num & 255;
// TODO: throw if bytes is invalid? bytes.push(eightBits);
this.data.set(bytes, this.ptr); num >>= 8;
this.ptr += bytes.length;
} }
/** this.insertBytes(bytes);
* 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); /**
* 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);
* Writes a signed number into the next n bytes. If the number is too large if (num >= HALF || num < -HALF) {
* to fit into n bytes, an error is thrown. throw 'Trying to write ' + num + ' into only ' + numBytes + ' bytes';
* @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); // Roll 8-bits at a time into an array of bytes.
if (num >= HALF || num < -HALF) { var bytes = [];
throw 'Trying to write ' + num + ' into only ' + numBytes + ' bytes'; while (numBytes-- > 0) {
} var eightBits = num & 255;
bytes.push(eightBits);
num >>= 8;
}
// Roll 8-bits at a time into an array of bytes. this.insertBytes(bytes);
const bytes = []; };
while (numBytes-- > 0) {
const eightBits = num & 255;
bytes.push(eightBits);
num >>= 8;
}
this.insertBytes(bytes);
}
/** /**
* @param {string} str The ASCII string to write. * @param {string} str The ASCII string to write.
*/ */
writeASCIIString(str) { bitjs.io.ByteBuffer.prototype.writeASCIIString = function(str) {
for (let i = 0; i < str.length; ++i) { for (var i = 0; i < str.length; ++i) {
const curByte = str.charCodeAt(i); var curByte = str.charCodeAt(i);
if (curByte < 0 || curByte > 255) { if (curByte < 0 || curByte > 255) {
throw 'Trying to write a non-ASCII string!'; throw 'Trying to write a non-ASCII string!';
}
this.insertByte(curByte);
} }
}; this.insertByte(curByte);
} }
};
})();

@ -12,297 +12,153 @@
var bitjs = bitjs || {}; var bitjs = bitjs || {};
bitjs.io = bitjs.io || {}; bitjs.io = bitjs.io || {};
(function() {
/** /**
* This object allows you to peek and consume bytes as numbers and strings out * This object allows you to peek and consume bytes as numbers and strings
* of a stream. More bytes can be pushed into the back of the stream via the * out of an ArrayBuffer. In this buffer, everything must be byte-aligned.
* push() method. *
* @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 { bitjs.io.ByteStream = function(ab, opt_offset, opt_length) {
/** var offset = opt_offset || 0;
* @param {ArrayBuffer} ab The ArrayBuffer object. var length = opt_length || ab.byteLength;
* @param {number=} opt_offset The offset into the ArrayBuffer this.bytes = new Uint8Array(ab, offset, length);
* @param {number=} opt_length The length of this BitStream this.ptr = 0;
*/ };
constructor(ab, 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 current page of bytes in the stream.
* @type {Uint8Array}
* @private
*/
this.bytes = new Uint8Array(ab, offset, length);
/**
* The next pages of bytes in the stream.
* @type {Array<Uint8Array>}
* @private
*/
this.pages_ = [];
/**
* 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 how many bytes have been read in the stream since the beginning of time.
*/
getNumBytesRead() {
return this.bytesRead_;
}
/** /**
* Returns how many bytes are currently in the stream left to be read. * Peeks at the next n bytes as an unsigned number but does not advance the
*/ * pointer
getNumBytesLeft() { * TODO: This apparently cannot read more than 4 bytes as a number?
const bytesInCurrentPage = (this.bytes.byteLength - this.ptr); * @param {number} n The number of bytes to peek at.
return this.pages_.reduce((acc, arr) => acc + arr.length, bytesInCurrentPage); * @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?
* Move the pointer ahead n bytes. If the pointer is at the end of the current array if (n <= 0 || typeof n != typeof 1)
* of bytes and we have another page of bytes, point at the new page. This is a private return -1;
* method, no validation is done.
* @param {number} n Number of bytes to increment. var result = 0;
* @private // read from last byte to first byte and roll them in
*/ var curByte = this.ptr + n - 1;
movePointer_(n) { while (curByte >= this.ptr) {
this.ptr += n; result <<= 8;
this.bytesRead_ += n; result |= this.bytes[curByte];
while (this.ptr >= this.bytes.length && this.pages_.length > 0) { --curByte;
this.ptr -= this.bytes.length;
this.bytes = this.pages_.shift();
}
} }
return result;
};
/**
* 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;
}
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 + * Returns the next n bytes as an unsigned number (or -1 on error)
', ptr=' + this.ptr + ', bytes.length=' + this.getNumBytesLeft(); * 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.
let result = 0; */
let curPage = this.bytes; bitjs.io.ByteStream.prototype.readNumber = function(n) {
let pageIndex = 0; var num = this.peekNumber( n );
let ptr = this.ptr; this.ptr += n;
for (let i = 0; i < num; ++i) { return num;
result |= (curPage[ptr++] << (i * 8)); };
if (ptr >= curPage.length) {
curPage = this.pages_[pageIndex++];
ptr = 0;
}
}
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.
* @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 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;
}
/** /**
* Returns the next n bytes as a signed number but does not advance the * Returns the next n bytes as a signed number and advances the stream pointer.
* pointer. * @param {number} n The number of bytes to read.
* @param {number} n The number of bytes to read. Must be a positive integer. * @return {number} The bytes interpreted as a signed number.
* @return {number} The bytes interpreted as a signed number. */
*/ bitjs.io.ByteStream.prototype.readSignedNumber = function(n) {
peekSignedNumber(n) { var num = this.peekSignedNumber(n);
let num = this.peekNumber(n); this.ptr += n;
const HALF = Math.pow(2, (n * 8) - 1); return num;
const FULL = HALF * 2; };
if (num >= HALF) num -= FULL;
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) {
* Returns the next n bytes as a signed number and advances the stream pointer. this.ptr += n;
* @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;
} }
return result;
};
/**
* 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); /**
* Reads the next n bytes as a sub-array.
ptr += sourceLength; * @param {number} n The number of bytes to read.
if (ptr >= curPage.length) { * @return {Uint8Array} The subarray.
curPage = this.pages_[pageIndex++]; */
ptr = 0; bitjs.io.ByteStream.prototype.readBytes = function(n) {
} return this.peekBytes(n, true);
};
bytesLeftToCopy -= sourceLength;
}
if (movePointers) {
this.movePointer_(num);
}
return result;
}
/** /**
* Reads the next n bytes as a sub-array. * Peeks at the next n bytes as a string but does not advance the pointer.
* @param {number} n The number of bytes to read. Must be a positive integer. * @param {number} n The number of bytes to peek at.
* @return {Uint8Array} The subarray. * @return {string} The next n bytes as a string.
*/ */
readBytes(n) { bitjs.io.ByteStream.prototype.peekString = function(n) {
return this.peekBytes(n, true); if (n <= 0 || typeof n != typeof 1) {
return "";
} }
/** var result = "";
* Peeks at the next n bytes as an ASCII string but does not advance the pointer. for (var p = this.ptr, end = this.ptr + n; p < end; ++p) {
* @param {number} n The number of bytes to peek at. Must be a positive integer. result += String.fromCharCode(this.bytes[p]);
* @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('');
} }
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. 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. * Returns the next n bytes as an ASCII string and advances the stream pointer
* @param {ArrayBuffer} ab * n bytes.
*/ * @param {number} n The number of bytes to read.
push(ab) { * @return {string} The next n bytes as a string.
if (!(ab instanceof ArrayBuffer)) { */
throw 'Error! ByteStream.push() called with an invalid ArrayBuffer object'; bitjs.io.ByteStream.prototype.readString = function(n) {
} var strToReturn = this.peekString(n);
this.ptr += n;
return strToReturn;
};
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) { function loadFromArrayBuffer(ab) {
var start = (new Date).getTime(); var start = (new Date).getTime();
var h = new Uint8Array(ab, 0, 10); 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! if (h[0] === 0x52 && h[1] === 0x61 && h[2] === 0x72 && h[3] === 0x21) { //Rar!
unarchiver = new bitjs.archive.Unrarrer(ab, pathToBitJS); unarchiver = new bitjs.archive.Unrarrer(ab, pathToBitJS);
} else if (h[0] === 80 && h[1] === 75) { //PK (Zip) } else if (h[0] === 80 && h[1] === 75) { //PK (Zip)

Loading…
Cancel
Save