Downgrade bitjs to es5 branch

This commit is contained in:
subdiox 2019-05-13 14:53:25 +09:00
parent c0d136ccd8
commit 7982ed877c
9 changed files with 2242 additions and 2757 deletions

View File

@ -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.
*/
constructor(msg) {
super(bitjs.archive.UnarchiveEvent.Type.INFO);
/** /**
* The information message. * The information message.
* @type {string} *
*/ * @type {string}
this.msg = msg; */
} this.msg = msg;
} };
bitjs.inherits(bitjs.archive.UnarchiveInfoEvent, bitjs.archive.UnarchiveEvent);
/** /**
* 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.
*/
constructor(msg) {
super(bitjs.archive.UnarchiveEvent.Type.ERROR);
/** /**
* The information message. * The information message.
* @type {string} *
*/ * @type {string}
this.msg = msg; */
} this.msg = msg;
} };
bitjs.inherits(bitjs.archive.UnarchiveErrorEvent, bitjs.archive.UnarchiveEvent);
/** /**
* 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
*/
constructor(currentFilename, currentFileNumber, currentBytesUnarchivedInFile,
currentBytesUnarchived, totalUncompressedBytesInArchive, totalFilesInArchive,
totalCompressedBytesRead) {
super(bitjs.archive.UnarchiveEvent.Type.PROGRESS);
this.currentFilename = currentFilename; this.currentFilename = currentFilename;
this.currentFileNumber = currentFileNumber; this.currentFileNumber = currentFileNumber;
this.currentBytesUnarchivedInFile = currentBytesUnarchivedInFile; this.currentBytesUnarchivedInFile = currentBytesUnarchivedInFile;
this.totalFilesInArchive = totalFilesInArchive; this.totalFilesInArchive = totalFilesInArchive;
this.currentBytesUnarchived = currentBytesUnarchived; this.currentBytesUnarchived = currentBytesUnarchived;
this.totalUncompressedBytesInArchive = totalUncompressedBytesInArchive; this.totalUncompressedBytesInArchive = totalUncompressedBytesInArchive;
this.totalCompressedBytesRead = totalCompressedBytesRead; };
} bitjs.inherits(bitjs.archive.UnarchiveProgressEvent, bitjs.archive.UnarchiveEvent);
}
/**
* 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.
* @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
* @private * @private
*/ */
handleWorkerEvent_(e) { this.pathToBitJS_ = opt_pathToBitJS || '/';
if ((e instanceof bitjs.archive.UnarchiveEvent || e.type) &&
this.listeners_[e.type] instanceof Array) { /**
this.listeners_[e.type].forEach(function (listener) { listener(e) }); * A map from event type to an array of listeners.
if (e.type == bitjs.archive.UnarchiveEvent.Type.FINISH) { * @type {Map.<string, Array>}
this.worker_.terminate(); */
this.listeners_ = {};
for (var type in bitjs.archive.UnarchiveEvent.Type) {
this.listeners_[bitjs.archive.UnarchiveEvent.Type[type]] = [];
}
};
/**
* Private web worker initialized during start().
* @type {Worker}
* @private
*/
bitjs.archive.Unarchiver.prototype.worker_ = null;
/**
* This method must be overridden by the subclass to return the script filename.
* @return {string} The script filename.
* @protected.
*/
bitjs.archive.Unarchiver.prototype.getScriptFileName = function() {
throw 'Subclasses of AbstractUnarchiver must overload getScriptFileName()';
};
/**
* Adds an event listener for UnarchiveEvents.
*
* @param {string} Event type.
* @param {function} An event handler function.
*/
bitjs.archive.Unarchiver.prototype.addEventListener = function(type, listener) {
if (type in this.listeners_) {
if (this.listeners_[type].indexOf(listener) == -1) {
this.listeners_[type].push(listener);
}
}
};
/**
* 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);
}
}
};
/**
* 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);
} }
} else { };
console.log(e);
} this.worker_.postMessage({file: this.ab});
} }
};
/** /**
* Starts the unarchive in a separate Web Worker thread and returns immediately. * Terminates the Web Worker for this Unarchiver and returns immediately.
*/ */
start() { bitjs.archive.Unarchiver.prototype.stop = function() {
const me = this; if (this.worker_) {
const scriptFileName = this.pathToBitJS_ + this.getScriptFileName(); this.worker_.terminate();
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);
}
};
const ab = this.ab;
this.worker_.postMessage({
file: ab,
logToConsole: false,
});
this.ab = null;
}
} }
};
/**
* Adds more bytes to the unarchiver's Worker thread.
*/
update(ab) {
if (this.worker_) {
this.worker_.postMessage({bytes: ab});
}
}
/**
* Terminates the Web Worker for this Unarchiver and returns immediately.
*/
stop() {
if (this.worker_) {
this.worker_.terminate();
}
}
}
/** /**
* Unzipper * Unzipper
* @extends {bitjs.archive.Unarchiver}
* @constructor
*/ */
bitjs.archive.Unzipper = class extends bitjs.archive.Unarchiver { bitjs.archive.Unzipper = function(arrayBuffer, opt_pathToBitJS) {
constructor(arrayBuffer, opt_pathToBitJS) { bitjs.base(this, arrayBuffer, opt_pathToBitJS);
super(arrayBuffer, opt_pathToBitJS); };
} bitjs.inherits(bitjs.archive.Unzipper, bitjs.archive.Unarchiver);
bitjs.archive.Unzipper.prototype.getScriptFileName = function() { return 'unzip.js' };
getScriptFileName() { return 'archive/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

View File

@ -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;
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 if (this.maybeMagic == "ustar") {
this.name = readCleanString(bstream, 100); this.version = readCleanString(bstream, 2);
this.mode = readCleanString(bstream, 8); this.uname = readCleanString(bstream, 32);
this.uid = readCleanString(bstream, 8); this.gname = readCleanString(bstream, 32);
this.gid = readCleanString(bstream, 8); this.devmajor = readCleanString(bstream, 8);
this.size = parseInt(readCleanString(bstream, 12), 8); this.devminor = readCleanString(bstream, 8);
this.mtime = readCleanString(bstream, 12); this.prefix = readCleanString(bstream, 155);
this.chksum = readCleanString(bstream, 8);
this.typeflag = readCleanString(bstream, 1);
this.linkname = readCleanString(bstream, 100);
this.maybeMagic = readCleanString(bstream, 6);
if (this.maybeMagic == "ustar") { if (this.prefix.length) {
this.version = readCleanString(bstream, 2); this.name = this.prefix + this.name;
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.")
} }
bstream.readBytes(12); // 512 - 500
} else {
bstream.readBytes(255); // 512 - 257
} }
}
const untar = function() { // Done header, now rest of blocks are the file contents.
let bstream = bytestream.tee(); 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;
}
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 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;
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. // Find the last number character from the back of the filename.
// event.data.bytes has all subsequent ArrayBuffers. while (aname[aindex-1] < '0' || aname[aindex-1] > '9') --aindex;
onmessage = function(event) { while (bname[bindex-1] < '0' || bname[bindex-1] > '9') --bindex;
const bytes = event.data.file || event.data.bytes;
logToConsole = !!event.data.logToConsole;
// This is the very first time we have been called. Initialize the bytestream. // Find the first number character from the back of the filename
if (!bytestream) { while (aname[aindex-1] >= '0' && aname[aindex-1] <= '9') --aindex;
bytestream = new bitjs.io.ByteStream(bytes); while (bname[bindex-1] >= '0' && bname[bindex-1] <= '9') --bindex;
} else {
bytestream.push(bytes);
}
if (unarchiveState === UnarchiveState.NOT_STARTED) { // parse them into numbers and return comparison
currentFilename = ""; var anum = parseInt(aname.substr(aindex), 10),
currentFileNumber = 0; bnum = parseInt(bname.substr(bindex), 10);
currentBytesUnarchivedInFile = 0; return anum - bnum;
currentBytesUnarchived = 0; */
totalUncompressedBytesInArchive = 0; });
totalFilesInArchive = 0;
allLocalFiles = [];
postMessage(new bitjs.archive.UnarchiveStartEvent());
unarchiveState = UnarchiveState.UNARCHIVING;
// report # files and total length
if (localFiles.length > 0) {
postProgress(); postProgress();
} }
if (unarchiveState === UnarchiveState.UNARCHIVING || // now do the shipping of each file
unarchiveState === UnarchiveState.WAITING) { for (var i = 0; i < localFiles.length; ++i) {
try { var localfile = localFiles[i];
untar(); info("Sending file '" + localfile.filename + "' up");
unarchiveState = UnarchiveState.FINISHED;
postMessage(new bitjs.archive.UnarchiveFinishEvent()); // update progress
} catch (e) { currentFilename = localfile.filename;
if (typeof e === 'string' && e.startsWith('Error! Overflowed')) { currentFileNumber = i;
// Overrun the buffer. currentBytesUnarchivedInFile = localfile.size;
unarchiveState = UnarchiveState.WAITING; currentBytesUnarchived += localfile.size;
} else { postMessage(new bitjs.archive.UnarchiveExtractEvent(localfile));
console.error('Found an error while untarring'); postProgress();
console.dir(e);
throw e;
}
}
} }
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

View File

@ -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)
* This object allows you to peek and consume bits and bytes out of a stream. bitjs.BIT = [ 0x01, 0x02, 0x04, 0x08,
* Note that this stream is optimized, and thus, will *NOT* throw an error if 0x10, 0x20, 0x40, 0x80,
* the end of the stream is reached. Only use this in scenarios where you 0x100, 0x200, 0x400, 0x800,
* already have all the bits you need. 0x1000, 0x2000, 0x4000, 0x8000];
*/
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 N number of bits (0-8) // 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);
};
})();

View File

@ -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) { this.data = new Uint8Array(numBytes);
if (typeof numBytes != typeof 1 || numBytes <= 0) { this.ptr = 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';
} }
// Roll 8-bits at a time into an array of bytes.
/** var bytes = [];
* @param {number} b The byte to insert. while (numBytes-- > 0) {
*/ var eightBits = num & 255;
insertByte(b) { bytes.push(eightBits);
// TODO: throw if byte is invalid? num >>= 8;
this.data[this.ptr++] = b;
} }
/** this.insertBytes(bytes);
* @param {Array.<number>|Uint8Array|Int8Array} bytes The bytes to insert. };
*/
insertBytes(bytes) {
// TODO: throw if bytes is invalid? /**
this.data.set(bytes, this.ptr); * Writes a signed number into the next n bytes. If the number is too large
this.ptr += bytes.length; * 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 an unsigned number into the next n bytes. If the number is too large if (num >= HALF || num < -HALF) {
* to fit into n bytes or is negative, an error is thrown. throw 'Trying to write ' + num + ' into only ' + numBytes + ' bytes';
* @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);
} }
/** // Roll 8-bits at a time into an array of bytes.
* Writes a signed number into the next n bytes. If the number is too large var bytes = [];
* to fit into n bytes, an error is thrown. while (numBytes-- > 0) {
* @param {number} num The signed number to write. var eightBits = num & 255;
* @param {number} numBytes The number of bytes to write the number into. bytes.push(eightBits);
*/ num >>= 8;
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);
} }
/** this.insertBytes(bytes);
* @param {string} str The ASCII string to write. };
*/
writeASCIIString(str) {
for (let i = 0; i < str.length; ++i) { /**
const curByte = str.charCodeAt(i); * @param {string} str The ASCII string to write.
if (curByte < 0 || curByte > 255) { */
throw 'Trying to write a non-ASCII string!'; bitjs.io.ByteBuffer.prototype.writeASCIIString = function(str) {
} for (var i = 0; i < str.length; ++i) {
this.insertByte(curByte); var curByte = str.charCodeAt(i);
if (curByte < 0 || curByte > 255) {
throw 'Trying to write a non-ASCII string!';
} }
}; this.insertByte(curByte);
} }
};
})();

View File

@ -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. * Peeks at the next n bytes as an unsigned number but does not advance the
* @type {Uint8Array} * pointer
* @private * TODO: This apparently cannot read more than 4 bytes as a number?
*/ * @param {number} n The number of bytes to peek at.
this.bytes = new Uint8Array(ab, offset, length); * @return {number} The n bytes interpreted as an unsigned number.
*/
bitjs.io.ByteStream.prototype.peekNumber = function(n) {
// TODO: return error if n would go past the end of the stream?
if (n <= 0 || typeof n != typeof 1)
return -1;
/** var result = 0;
* The next pages of bytes in the stream. // read from last byte to first byte and roll them in
* @type {Array<Uint8Array>} var curByte = this.ptr + n - 1;
* @private while (curByte >= this.ptr) {
*/ result <<= 8;
this.pages_ = []; 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. * Returns the next n bytes as an unsigned number (or -1 on error)
* @type {Number} * and advances the stream pointer n bytes.
* @private * @param {number} n The number of bytes to read.
*/ * @return {number} The n bytes interpreted as an unsigned number.
this.bytesRead_ = 0; */
bitjs.io.ByteStream.prototype.readNumber = function(n) {
var num = this.peekNumber( n );
this.ptr += n;
return num;
};
/**
* Returns the next n bytes as a signed number but does not advance the
* pointer.
* @param {number} n The number of bytes to read.
* @return {number} The bytes interpreted as a signed number.
*/
bitjs.io.ByteStream.prototype.peekSignedNumber = function(n) {
var num = this.peekNumber(n);
var HALF = Math.pow(2, (n * 8) - 1);
var FULL = HALF * 2;
if (num >= HALF) num -= FULL;
return num;
};
/**
* Returns the next n bytes as a signed number and advances the stream pointer.
* @param {number} n The number of bytes to read.
* @return {number} The bytes interpreted as a signed number.
*/
bitjs.io.ByteStream.prototype.readSignedNumber = function(n) {
var num = this.peekSignedNumber(n);
this.ptr += n;
return num;
};
/**
* This returns n bytes as a sub-array, advancing the pointer if movePointers
* is true.
* @param {number} n The number of bytes to read.
* @param {boolean} movePointers Whether to move the pointers.
* @return {Uint8Array} The subarray.
*/
bitjs.io.ByteStream.prototype.peekBytes = function(n, movePointers) {
if (n <= 0 || typeof n != typeof 1) {
return null;
} }
/** var result = this.bytes.subarray(this.ptr, this.ptr + n);
* Returns how many bytes have been read in the stream since the beginning of time.
*/
getNumBytesRead() {
return this.bytesRead_;
}
/** if (movePointers) {
* 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) {
this.ptr += n; 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();
}
} }
/** 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 + * Reads the next n bytes as a sub-array.
', ptr=' + this.ptr + ', bytes.length=' + this.getNumBytesLeft(); * @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++]; * Peeks at the next n bytes as a string but does not advance the pointer.
ptr = 0; * @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) {
return result; if (n <= 0 || typeof n != typeof 1) {
return "";
} }
var result = "";
/** for (var p = this.ptr, end = this.ptr + n; p < end; ++p) {
* Returns the next n bytes as an unsigned number (or -1 on error) result += String.fromCharCode(this.bytes[p]);
* 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;
} }
return result;
};
/** /**
* Returns the next n bytes as a signed number but does not advance the * Returns the next n bytes as an ASCII string and advances the stream pointer
* pointer. * n bytes.
* @param {number} n The number of bytes to read. Must be a positive integer. * @param {number} n The number of bytes to read.
* @return {number} The bytes interpreted as a signed number. * @return {string} The next n bytes as a string.
*/ */
peekSignedNumber(n) { bitjs.io.ByteStream.prototype.readString = function(n) {
let num = this.peekNumber(n); var strToReturn = this.peekString(n);
const HALF = Math.pow(2, (n * 8) - 1); this.ptr += n;
const FULL = HALF * 2; return strToReturn;
};
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. 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;
}
}

View File

@ -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)