1
0
Fork 0
mirror of https://github.com/codedread/bitjs synced 2025-10-04 18:19:15 +02:00

converting to spaces

This commit is contained in:
Kevin Kwok 2015-06-14 13:14:39 -04:00 committed by Jeff Schiller
parent b451b46828
commit f460784a48
3 changed files with 486 additions and 486 deletions

2
io.js
View file

@ -15,7 +15,7 @@ bitjs.io = bitjs.io || {};
(function() { (function() {
// mask for getting the Nth bit (zero-based) // mask for getting the Nth bit (zero-based)
bitjs.BIT = [ 0x01, 0x02, 0x04, 0x08, bitjs.BIT = [ 0x01, 0x02, 0x04, 0x08,
0x10, 0x20, 0x40, 0x80, 0x10, 0x20, 0x40, 0x80,
0x100, 0x200, 0x400, 0x800, 0x100, 0x200, 0x400, 0x800,
0x1000, 0x2000, 0x4000, 0x8000]; 0x1000, 0x2000, 0x4000, 0x8000];

View file

@ -61,19 +61,19 @@ var TarLocalFile = function(bstream) {
this.maybeMagic = readCleanString(bstream, 6); this.maybeMagic = readCleanString(bstream, 6);
if (this.maybeMagic == "ustar") { if (this.maybeMagic == "ustar") {
this.version = readCleanString(bstream, 2); this.version = readCleanString(bstream, 2);
this.uname = readCleanString(bstream, 32); this.uname = readCleanString(bstream, 32);
this.gname = readCleanString(bstream, 32); this.gname = readCleanString(bstream, 32);
this.devmajor = readCleanString(bstream, 8); this.devmajor = readCleanString(bstream, 8);
this.devminor = readCleanString(bstream, 8); this.devminor = readCleanString(bstream, 8);
this.prefix = readCleanString(bstream, 155); this.prefix = readCleanString(bstream, 155);
if (this.prefix.length) { if (this.prefix.length) {
this.name = this.prefix + this.name; this.name = this.prefix + this.name;
} }
bstream.readBytes(12); // 512 - 500 bstream.readBytes(12); // 512 - 500
} else { } else {
bstream.readBytes(255); // 512 - 257 bstream.readBytes(255); // 512 - 257
} }
// Done header, now rest of blocks are the file contents. // Done header, now rest of blocks are the file contents.
@ -86,22 +86,22 @@ var TarLocalFile = function(bstream) {
// A regular file. // A regular file.
if (this.typeflag == 0) { if (this.typeflag == 0) {
info(" This is a regular file."); info(" This is a regular file.");
var sizeInBytes = parseInt(this.size); var sizeInBytes = parseInt(this.size);
this.fileData = new Uint8Array(bstream.bytes.buffer, bstream.ptr, 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) { if (this.name.length > 0 && this.size > 0 && this.fileData && this.fileData.buffer) {
this.isValid = true; this.isValid = true;
} }
bstream.readBytes(this.size); bstream.readBytes(this.size);
// Round up to 512-byte blocks. // Round up to 512-byte blocks.
var remaining = 512 - this.size % 512; var remaining = 512 - this.size % 512;
if (remaining > 0 && remaining < 512) { if (remaining > 0 && remaining < 512) {
bstream.readBytes(remaining); bstream.readBytes(remaining);
} }
} else if (this.typeflag == 5) { } else if (this.typeflag == 5) {
info(" This is a directory.") info(" This is a directory.")
} }
}; };
@ -122,11 +122,11 @@ var untar = function(arrayBuffer) {
// 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) {
var oneLocalFile = new TarLocalFile(bstream); var oneLocalFile = new TarLocalFile(bstream);
if (oneLocalFile && oneLocalFile.isValid) { if (oneLocalFile && oneLocalFile.isValid) {
localFiles.push(oneLocalFile); localFiles.push(oneLocalFile);
totalUncompressedBytesInArchive += oneLocalFile.size; totalUncompressedBytesInArchive += oneLocalFile.size;
} }
} }
totalFilesInArchive = localFiles.length; totalFilesInArchive = localFiles.length;

926
unzip.js
View file

@ -48,62 +48,62 @@ var zEndOfCentralDirLocatorSignature = 0x07064b50;
// takes a ByteStream and parses out the local file information // takes a ByteStream and parses out the local file information
var ZipLocalFile = function(bstream) { var ZipLocalFile = function(bstream) {
if (typeof bstream != typeof {} || !bstream.readNumber || typeof bstream.readNumber != typeof function(){}) { if (typeof bstream != typeof {} || !bstream.readNumber || typeof bstream.readNumber != typeof function(){}) {
return null; return null;
} }
bstream.readNumber(4); // swallow signature bstream.readNumber(4); // swallow signature
this.version = bstream.readNumber(2); this.version = bstream.readNumber(2);
this.generalPurpose = bstream.readNumber(2); this.generalPurpose = bstream.readNumber(2);
this.compressionMethod = bstream.readNumber(2); this.compressionMethod = bstream.readNumber(2);
this.lastModFileTime = bstream.readNumber(2); this.lastModFileTime = bstream.readNumber(2);
this.lastModFileDate = bstream.readNumber(2); this.lastModFileDate = bstream.readNumber(2);
this.crc32 = bstream.readNumber(4); this.crc32 = bstream.readNumber(4);
this.compressedSize = bstream.readNumber(4); this.compressedSize = bstream.readNumber(4);
this.uncompressedSize = bstream.readNumber(4); this.uncompressedSize = bstream.readNumber(4);
this.fileNameLength = bstream.readNumber(2); this.fileNameLength = bstream.readNumber(2);
this.extraFieldLength = bstream.readNumber(2); this.extraFieldLength = bstream.readNumber(2);
this.filename = null; this.filename = null;
if (this.fileNameLength > 0) { if (this.fileNameLength > 0) {
this.filename = bstream.readString(this.fileNameLength); this.filename = bstream.readString(this.fileNameLength);
} }
info("Zip Local File Header:"); info("Zip Local File Header:");
info(" version=" + this.version); info(" version=" + this.version);
info(" general purpose=" + this.generalPurpose); info(" general purpose=" + this.generalPurpose);
info(" compression method=" + this.compressionMethod); info(" compression method=" + this.compressionMethod);
info(" last mod file time=" + this.lastModFileTime); info(" last mod file time=" + this.lastModFileTime);
info(" last mod file date=" + this.lastModFileDate); info(" last mod file date=" + this.lastModFileDate);
info(" crc32=" + this.crc32); info(" crc32=" + this.crc32);
info(" compressed size=" + this.compressedSize); info(" compressed size=" + this.compressedSize);
info(" uncompressed size=" + this.uncompressedSize); info(" uncompressed size=" + this.uncompressedSize);
info(" file name length=" + this.fileNameLength); info(" file name length=" + this.fileNameLength);
info(" extra field length=" + this.extraFieldLength); info(" extra field length=" + this.extraFieldLength);
info(" filename = '" + this.filename + "'"); info(" filename = '" + this.filename + "'");
this.extraField = null; this.extraField = null;
if (this.extraFieldLength > 0) { if (this.extraFieldLength > 0) {
this.extraField = bstream.readString(this.extraFieldLength); this.extraField = bstream.readString(this.extraFieldLength);
info(" extra field=" + this.extraField); info(" extra field=" + this.extraField);
} }
// read in the compressed data // read in the compressed data
this.fileData = null; this.fileData = null;
if (this.compressedSize > 0) { if (this.compressedSize > 0) {
this.fileData = new Uint8Array(bstream.bytes.buffer, bstream.ptr, this.compressedSize); this.fileData = new Uint8Array(bstream.bytes.buffer, bstream.ptr, this.compressedSize);
bstream.ptr += this.compressedSize; bstream.ptr += this.compressedSize;
} }
// TODO: deal with data descriptor if present (we currently assume no data descriptor!) // TODO: deal with data descriptor if present (we currently assume no data descriptor!)
// "This descriptor exists only if bit 3 of the general purpose bit flag is set" // "This descriptor exists only if bit 3 of the general purpose bit flag is set"
// But how do you figure out how big the file data is if you don't know the compressedSize // But how do you figure out how big the file data is if you don't know the compressedSize
// from the header?!? // from the header?!?
if ((this.generalPurpose & bitjs.BIT[3]) != 0) { if ((this.generalPurpose & bitjs.BIT[3]) != 0) {
this.crc32 = bstream.readNumber(4); this.crc32 = bstream.readNumber(4);
this.compressedSize = bstream.readNumber(4); this.compressedSize = bstream.readNumber(4);
this.uncompressedSize = bstream.readNumber(4); this.uncompressedSize = bstream.readNumber(4);
} }
}; };
// determine what kind of compressed data we have and decompress // determine what kind of compressed data we have and decompress
@ -144,119 +144,119 @@ var unzip = function(arrayBuffer) {
var bstream = new bitjs.io.ByteStream(arrayBuffer); var bstream = new bitjs.io.ByteStream(arrayBuffer);
// detect local file header signature or return null // detect local file header signature or return null
if (bstream.peekNumber(4) == zLocalFileHeaderSignature) { if (bstream.peekNumber(4) == zLocalFileHeaderSignature) {
var localFiles = []; var localFiles = [];
// loop until we don't see any more local files // loop until we don't see any more local files
while (bstream.peekNumber(4) == zLocalFileHeaderSignature) { while (bstream.peekNumber(4) == zLocalFileHeaderSignature) {
var oneLocalFile = new ZipLocalFile(bstream); var oneLocalFile = new ZipLocalFile(bstream);
// this should strip out directories/folders // this should strip out directories/folders
if (oneLocalFile && oneLocalFile.uncompressedSize > 0 && oneLocalFile.fileData) { if (oneLocalFile && oneLocalFile.uncompressedSize > 0 && oneLocalFile.fileData) {
localFiles.push(oneLocalFile); localFiles.push(oneLocalFile);
totalUncompressedBytesInArchive += oneLocalFile.uncompressedSize; totalUncompressedBytesInArchive += oneLocalFile.uncompressedSize;
} }
} }
totalFilesInArchive = localFiles.length; totalFilesInArchive = localFiles.length;
// got all local files, now sort them // got all local files, now sort them
localFiles.sort(function(a,b) { localFiles.sort(function(a,b) {
var aname = a.filename; var aname = a.filename;
var bname = b.filename; var bname = b.filename;
return aname > bname ? 1 : -1; return aname > bname ? 1 : -1;
// extract the number at the end of both filenames // extract the number at the end of both filenames
/* /*
var aname = a.filename; var aname = a.filename;
var bname = b.filename; var bname = b.filename;
var aindex = aname.length, bindex = bname.length; var aindex = aname.length, bindex = bname.length;
// Find the last number character from the back of the filename. // Find the last number character from the back of the filename.
while (aname[aindex-1] < '0' || aname[aindex-1] > '9') --aindex; while (aname[aindex-1] < '0' || aname[aindex-1] > '9') --aindex;
while (bname[bindex-1] < '0' || bname[bindex-1] > '9') --bindex; while (bname[bindex-1] < '0' || bname[bindex-1] > '9') --bindex;
// Find the first number character from the back of the filename // Find the first number character from the back of the filename
while (aname[aindex-1] >= '0' && aname[aindex-1] <= '9') --aindex; while (aname[aindex-1] >= '0' && aname[aindex-1] <= '9') --aindex;
while (bname[bindex-1] >= '0' && bname[bindex-1] <= '9') --bindex; while (bname[bindex-1] >= '0' && bname[bindex-1] <= '9') --bindex;
// parse them into numbers and return comparison // parse them into numbers and return comparison
var anum = parseInt(aname.substr(aindex), 10), var anum = parseInt(aname.substr(aindex), 10),
bnum = parseInt(bname.substr(bindex), 10); bnum = parseInt(bname.substr(bindex), 10);
return anum - bnum; return anum - bnum;
*/ */
}); });
// archive extra data record // archive extra data record
if (bstream.peekNumber(4) == zArchiveExtraDataSignature) { if (bstream.peekNumber(4) == zArchiveExtraDataSignature) {
info(" Found an Archive Extra Data Signature"); info(" Found an Archive Extra Data Signature");
// skipping this record for now // skipping this record for now
bstream.readNumber(4); bstream.readNumber(4);
var archiveExtraFieldLength = bstream.readNumber(4); var archiveExtraFieldLength = bstream.readNumber(4);
bstream.readString(archiveExtraFieldLength); bstream.readString(archiveExtraFieldLength);
} }
// central directory structure // central directory structure
// TODO: handle the rest of the structures (Zip64 stuff) // TODO: handle the rest of the structures (Zip64 stuff)
if (bstream.peekNumber(4) == zCentralFileHeaderSignature) { if (bstream.peekNumber(4) == zCentralFileHeaderSignature) {
info(" Found a Central File Header"); info(" Found a Central File Header");
// read all file headers // read all file headers
while (bstream.peekNumber(4) == zCentralFileHeaderSignature) { while (bstream.peekNumber(4) == zCentralFileHeaderSignature) {
bstream.readNumber(4); // signature bstream.readNumber(4); // signature
bstream.readNumber(2); // version made by bstream.readNumber(2); // version made by
bstream.readNumber(2); // version needed to extract bstream.readNumber(2); // version needed to extract
bstream.readNumber(2); // general purpose bit flag bstream.readNumber(2); // general purpose bit flag
bstream.readNumber(2); // compression method bstream.readNumber(2); // compression method
bstream.readNumber(2); // last mod file time bstream.readNumber(2); // last mod file time
bstream.readNumber(2); // last mod file date bstream.readNumber(2); // last mod file date
bstream.readNumber(4); // crc32 bstream.readNumber(4); // crc32
bstream.readNumber(4); // compressed size bstream.readNumber(4); // compressed size
bstream.readNumber(4); // uncompressed size bstream.readNumber(4); // uncompressed size
var fileNameLength = bstream.readNumber(2); // file name length var fileNameLength = bstream.readNumber(2); // file name length
var extraFieldLength = bstream.readNumber(2); // extra field length var extraFieldLength = bstream.readNumber(2); // extra field length
var fileCommentLength = bstream.readNumber(2); // file comment length var fileCommentLength = bstream.readNumber(2); // file comment length
bstream.readNumber(2); // disk number start bstream.readNumber(2); // disk number start
bstream.readNumber(2); // internal file attributes bstream.readNumber(2); // internal file attributes
bstream.readNumber(4); // external file attributes bstream.readNumber(4); // external file attributes
bstream.readNumber(4); // relative offset of local header bstream.readNumber(4); // relative offset of local header
bstream.readString(fileNameLength); // file name bstream.readString(fileNameLength); // file name
bstream.readString(extraFieldLength); // extra field bstream.readString(extraFieldLength); // extra field
bstream.readString(fileCommentLength); // file comment bstream.readString(fileCommentLength); // file comment
} }
} }
// digital signature // digital signature
if (bstream.peekNumber(4) == zDigitalSignatureSignature) { if (bstream.peekNumber(4) == zDigitalSignatureSignature) {
info(" Found a Digital Signature"); info(" Found a Digital Signature");
bstream.readNumber(4); bstream.readNumber(4);
var sizeOfSignature = bstream.readNumber(2); var sizeOfSignature = bstream.readNumber(2);
bstream.readString(sizeOfSignature); // digital signature data bstream.readString(sizeOfSignature); // digital signature data
} }
// report # files and total length // report # files and total length
if (localFiles.length > 0) { if (localFiles.length > 0) {
postProgress(); postProgress();
} }
// now do the unzipping of each file // now do the unzipping of each file
for (var i = 0; i < localFiles.length; ++i) { for (var i = 0; i < localFiles.length; ++i) {
var localfile = localFiles[i]; var localfile = localFiles[i];
// update progress // update progress
currentFilename = localfile.filename; currentFilename = localfile.filename;
currentFileNumber = i; currentFileNumber = i;
currentBytesUnarchivedInFile = 0; currentBytesUnarchivedInFile = 0;
// actually do the unzipping // actually do the unzipping
localfile.unzip(); localfile.unzip();
if (localfile.fileData != null) { if (localfile.fileData != null) {
postMessage(new bitjs.archive.UnarchiveExtractEvent(localfile)); postMessage(new bitjs.archive.UnarchiveExtractEvent(localfile));
postProgress(); postProgress();
} }
} }
postProgress(); postProgress();
postMessage(new bitjs.archive.UnarchiveFinishEvent()); postMessage(new bitjs.archive.UnarchiveFinishEvent());
} }
} }
@ -264,253 +264,253 @@ var unzip = function(arrayBuffer) {
// each entry's index is its code and its value is a JavaScript object // each entry's index is its code and its value is a JavaScript object
// containing {length: 6, symbol: X} // containing {length: 6, symbol: X}
function getHuffmanCodes(bitLengths) { function getHuffmanCodes(bitLengths) {
// ensure bitLengths is an array containing at least one element // ensure bitLengths is an array containing at least one element
if (typeof bitLengths != typeof [] || bitLengths.length < 1) { if (typeof bitLengths != typeof [] || bitLengths.length < 1) {
err("Error! getHuffmanCodes() called with an invalid array"); err("Error! getHuffmanCodes() called with an invalid array");
return null; return null;
} }
// Reference: http://tools.ietf.org/html/rfc1951#page-8 // Reference: http://tools.ietf.org/html/rfc1951#page-8
var numLengths = bitLengths.length, var numLengths = bitLengths.length,
bl_count = [], bl_count = [],
MAX_BITS = 1; MAX_BITS = 1;
// Step 1: count up how many codes of each length we have // Step 1: count up how many codes of each length we have
for (var i = 0; i < numLengths; ++i) { for (var i = 0; i < numLengths; ++i) {
var length = bitLengths[i]; var length = bitLengths[i];
// test to ensure each bit length is a positive, non-zero number // test to ensure each bit length is a positive, non-zero number
if (typeof length != typeof 1 || length < 0) { if (typeof length != typeof 1 || length < 0) {
err("bitLengths contained an invalid number in getHuffmanCodes(): " + length + " of type " + (typeof length)); err("bitLengths contained an invalid number in getHuffmanCodes(): " + length + " of type " + (typeof length));
return null; return null;
} }
// increment the appropriate bitlength count // increment the appropriate bitlength count
if (bl_count[length] == undefined) bl_count[length] = 0; if (bl_count[length] == undefined) bl_count[length] = 0;
// a length of zero means this symbol is not participating in the huffman coding // a length of zero means this symbol is not participating in the huffman coding
if (length > 0) bl_count[length]++; if (length > 0) bl_count[length]++;
if (length > MAX_BITS) MAX_BITS = length; if (length > MAX_BITS) MAX_BITS = length;
} }
// Step 2: Find the numerical value of the smallest code for each code length // Step 2: Find the numerical value of the smallest code for each code length
var next_code = [], var next_code = [],
code = 0; code = 0;
for (var bits = 1; bits <= MAX_BITS; ++bits) { for (var bits = 1; bits <= MAX_BITS; ++bits) {
var length = bits-1; var length = bits-1;
// ensure undefined lengths are zero // ensure undefined lengths are zero
if (bl_count[length] == undefined) bl_count[length] = 0; if (bl_count[length] == undefined) bl_count[length] = 0;
code = (code + bl_count[bits-1]) << 1; code = (code + bl_count[bits-1]) << 1;
next_code[bits] = code; next_code[bits] = code;
} }
// Step 3: Assign numerical values to all codes // Step 3: Assign numerical values to all codes
var table = {}, tableLength = 0; var table = {}, tableLength = 0;
for (var n = 0; n < numLengths; ++n) { for (var n = 0; n < numLengths; ++n) {
var len = bitLengths[n]; var len = bitLengths[n];
if (len != 0) { if (len != 0) {
table[next_code[len]] = { length: len, symbol: n }; //, bitstring: binaryValueToString(next_code[len],len) }; table[next_code[len]] = { length: len, symbol: n }; //, bitstring: binaryValueToString(next_code[len],len) };
tableLength++; tableLength++;
next_code[len]++; next_code[len]++;
} }
} }
table.maxLength = tableLength; table.maxLength = tableLength;
return table; return table;
} }
/* /*
The Huffman codes for the two alphabets are fixed, and are not The Huffman codes for the two alphabets are fixed, and are not
represented explicitly in the data. The Huffman code lengths represented explicitly in the data. The Huffman code lengths
for the literal/length alphabet are: for the literal/length alphabet are:
Lit Value Bits Codes Lit Value Bits Codes
--------- ---- ----- --------- ---- -----
0 - 143 8 00110000 through 0 - 143 8 00110000 through
10111111 10111111
144 - 255 9 110010000 through 144 - 255 9 110010000 through
111111111 111111111
256 - 279 7 0000000 through 256 - 279 7 0000000 through
0010111 0010111
280 - 287 8 11000000 through 280 - 287 8 11000000 through
11000111 11000111
*/ */
// fixed Huffman codes go from 7-9 bits, so we need an array whose index can hold up to 9 bits // fixed Huffman codes go from 7-9 bits, so we need an array whose index can hold up to 9 bits
var fixedHCtoLiteral = null; var fixedHCtoLiteral = null;
var fixedHCtoDistance = null; var fixedHCtoDistance = null;
function getFixedLiteralTable() { function getFixedLiteralTable() {
// create once // create once
if (!fixedHCtoLiteral) { if (!fixedHCtoLiteral) {
var bitlengths = new Array(288); var bitlengths = new Array(288);
for (var i = 0; i <= 143; ++i) bitlengths[i] = 8; for (var i = 0; i <= 143; ++i) bitlengths[i] = 8;
for (i = 144; i <= 255; ++i) bitlengths[i] = 9; for (i = 144; i <= 255; ++i) bitlengths[i] = 9;
for (i = 256; i <= 279; ++i) bitlengths[i] = 7; for (i = 256; i <= 279; ++i) bitlengths[i] = 7;
for (i = 280; i <= 287; ++i) bitlengths[i] = 8; for (i = 280; i <= 287; ++i) bitlengths[i] = 8;
// get huffman code table // get huffman code table
fixedHCtoLiteral = getHuffmanCodes(bitlengths); fixedHCtoLiteral = getHuffmanCodes(bitlengths);
} }
return fixedHCtoLiteral; return fixedHCtoLiteral;
} }
function getFixedDistanceTable() { function getFixedDistanceTable() {
// create once // create once
if (!fixedHCtoDistance) { if (!fixedHCtoDistance) {
var bitlengths = new Array(32); var bitlengths = new Array(32);
for (var i = 0; i < 32; ++i) { bitlengths[i] = 5; } for (var i = 0; i < 32; ++i) { bitlengths[i] = 5; }
// get huffman code table // get huffman code table
fixedHCtoDistance = getHuffmanCodes(bitlengths); fixedHCtoDistance = getHuffmanCodes(bitlengths);
} }
return fixedHCtoDistance; return fixedHCtoDistance;
} }
// extract one bit at a time until we find a matching Huffman Code // extract one bit at a time until we find a matching Huffman Code
// then return that symbol // then return that symbol
function decodeSymbol(bstream, hcTable) { function decodeSymbol(bstream, hcTable) {
var code = 0, len = 0; var code = 0, len = 0;
var match = false; var match = false;
// loop until we match // loop until we match
for (;;) { for (;;) {
// read in next bit // read in next bit
var bit = bstream.readBits(1); var bit = bstream.readBits(1);
code = (code<<1) | bit; code = (code<<1) | bit;
++len; ++len;
// check against Huffman Code table and break if found // check against Huffman Code table and break if found
if (hcTable.hasOwnProperty(code) && hcTable[code].length == len) { if (hcTable.hasOwnProperty(code) && hcTable[code].length == len) {
break; break;
} }
if (len > hcTable.maxLength) { if (len > hcTable.maxLength) {
err("Bit stream out of sync, didn't find a Huffman Code, length was " + len + err("Bit stream out of sync, didn't find a Huffman Code, length was " + len +
" and table only max code length of " + hcTable.maxLength); " and table only max code length of " + hcTable.maxLength);
break; break;
} }
} }
return hcTable[code].symbol; return hcTable[code].symbol;
} }
var CodeLengthCodeOrder = [16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15]; var CodeLengthCodeOrder = [16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15];
/* /*
Extra Extra Extra Extra Extra Extra
Code Bits Length(s) Code Bits Lengths Code Bits Length(s) Code Bits Length(s) Code Bits Lengths Code Bits Length(s)
---- ---- ------ ---- ---- ------- ---- ---- ------- ---- ---- ------ ---- ---- ------- ---- ---- -------
257 0 3 267 1 15,16 277 4 67-82 257 0 3 267 1 15,16 277 4 67-82
258 0 4 268 1 17,18 278 4 83-98 258 0 4 268 1 17,18 278 4 83-98
259 0 5 269 2 19-22 279 4 99-114 259 0 5 269 2 19-22 279 4 99-114
260 0 6 270 2 23-26 280 4 115-130 260 0 6 270 2 23-26 280 4 115-130
261 0 7 271 2 27-30 281 5 131-162 261 0 7 271 2 27-30 281 5 131-162
262 0 8 272 2 31-34 282 5 163-194 262 0 8 272 2 31-34 282 5 163-194
263 0 9 273 3 35-42 283 5 195-226 263 0 9 273 3 35-42 283 5 195-226
264 0 10 274 3 43-50 284 5 227-257 264 0 10 274 3 43-50 284 5 227-257
265 1 11,12 275 3 51-58 285 0 258 265 1 11,12 275 3 51-58 285 0 258
266 1 13,14 276 3 59-66 266 1 13,14 276 3 59-66
*/ */
var LengthLookupTable = [ var LengthLookupTable = [
[0,3], [0,4], [0,5], [0,6], [0,3], [0,4], [0,5], [0,6],
[0,7], [0,8], [0,9], [0,10], [0,7], [0,8], [0,9], [0,10],
[1,11], [1,13], [1,15], [1,17], [1,11], [1,13], [1,15], [1,17],
[2,19], [2,23], [2,27], [2,31], [2,19], [2,23], [2,27], [2,31],
[3,35], [3,43], [3,51], [3,59], [3,35], [3,43], [3,51], [3,59],
[4,67], [4,83], [4,99], [4,115], [4,67], [4,83], [4,99], [4,115],
[5,131], [5,163], [5,195], [5,227], [5,131], [5,163], [5,195], [5,227],
[0,258] [0,258]
]; ];
/* /*
Extra Extra Extra Extra Extra Extra
Code Bits Dist Code Bits Dist Code Bits Distance Code Bits Dist Code Bits Dist Code Bits Distance
---- ---- ---- ---- ---- ------ ---- ---- -------- ---- ---- ---- ---- ---- ------ ---- ---- --------
0 0 1 10 4 33-48 20 9 1025-1536 0 0 1 10 4 33-48 20 9 1025-1536
1 0 2 11 4 49-64 21 9 1537-2048 1 0 2 11 4 49-64 21 9 1537-2048
2 0 3 12 5 65-96 22 10 2049-3072 2 0 3 12 5 65-96 22 10 2049-3072
3 0 4 13 5 97-128 23 10 3073-4096 3 0 4 13 5 97-128 23 10 3073-4096
4 1 5,6 14 6 129-192 24 11 4097-6144 4 1 5,6 14 6 129-192 24 11 4097-6144
5 1 7,8 15 6 193-256 25 11 6145-8192 5 1 7,8 15 6 193-256 25 11 6145-8192
6 2 9-12 16 7 257-384 26 12 8193-12288 6 2 9-12 16 7 257-384 26 12 8193-12288
7 2 13-16 17 7 385-512 27 12 12289-16384 7 2 13-16 17 7 385-512 27 12 12289-16384
8 3 17-24 18 8 513-768 28 13 16385-24576 8 3 17-24 18 8 513-768 28 13 16385-24576
9 3 25-32 19 8 769-1024 29 13 24577-32768 9 3 25-32 19 8 769-1024 29 13 24577-32768
*/ */
var DistLookupTable = [ var DistLookupTable = [
[0,1], [0,2], [0,3], [0,4], [0,1], [0,2], [0,3], [0,4],
[1,5], [1,7], [1,5], [1,7],
[2,9], [2,13], [2,9], [2,13],
[3,17], [3,25], [3,17], [3,25],
[4,33], [4,49], [4,33], [4,49],
[5,65], [5,97], [5,65], [5,97],
[6,129], [6,193], [6,129], [6,193],
[7,257], [7,385], [7,257], [7,385],
[8,513], [8,769], [8,513], [8,769],
[9,1025], [9,1537], [9,1025], [9,1537],
[10,2049], [10,3073], [10,2049], [10,3073],
[11,4097], [11,6145], [11,4097], [11,6145],
[12,8193], [12,12289], [12,8193], [12,12289],
[13,16385], [13,24577] [13,16385], [13,24577]
]; ];
function inflateBlockData(bstream, hcLiteralTable, hcDistanceTable, buffer) { function inflateBlockData(bstream, hcLiteralTable, hcDistanceTable, buffer) {
/* /*
loop (until end of block code recognized) loop (until end of block code recognized)
decode literal/length value from input stream decode literal/length value from input stream
if value < 256 if value < 256
copy value (literal byte) to output stream copy value (literal byte) to output stream
otherwise otherwise
if value = end of block (256) if value = end of block (256)
break from loop break from loop
otherwise (value = 257..285) otherwise (value = 257..285)
decode distance from input stream decode distance from input stream
move backwards distance bytes in the output move backwards distance bytes in the output
stream, and copy length bytes from this stream, and copy length bytes from this
position to the output stream. position to the output stream.
*/ */
var numSymbols = 0, blockSize = 0; var numSymbols = 0, blockSize = 0;
for (;;) { for (;;) {
var symbol = decodeSymbol(bstream, hcLiteralTable); var symbol = decodeSymbol(bstream, hcLiteralTable);
++numSymbols; ++numSymbols;
if (symbol < 256) { if (symbol < 256) {
// copy literal byte to output // copy literal byte to output
buffer.insertByte(symbol); buffer.insertByte(symbol);
blockSize++; blockSize++;
} }
else { else {
// end of block reached // end of block reached
if (symbol == 256) { if (symbol == 256) {
break; break;
} }
else { else {
var lengthLookup = LengthLookupTable[symbol-257], var lengthLookup = LengthLookupTable[symbol-257],
length = lengthLookup[1] + bstream.readBits(lengthLookup[0]), length = lengthLookup[1] + bstream.readBits(lengthLookup[0]),
distLookup = DistLookupTable[decodeSymbol(bstream, hcDistanceTable)], distLookup = DistLookupTable[decodeSymbol(bstream, hcDistanceTable)],
distance = distLookup[1] + bstream.readBits(distLookup[0]); distance = distLookup[1] + bstream.readBits(distLookup[0]);
// now apply length and distance appropriately and copy to output // now apply length and distance appropriately and copy to output
// TODO: check that backward distance < data.length? // TODO: check that backward distance < data.length?
// http://tools.ietf.org/html/rfc1951#page-11 // http://tools.ietf.org/html/rfc1951#page-11
// "Note also that the referenced string may overlap the current // "Note also that the referenced string may overlap the current
// position; for example, if the last 2 bytes decoded have values // position; for example, if the last 2 bytes decoded have values
// X and Y, a string reference with <length = 5, distance = 2> // X and Y, a string reference with <length = 5, distance = 2>
// adds X,Y,X,Y,X to the output stream." // adds X,Y,X,Y,X to the output stream."
// //
// loop for each character // loop for each character
var ch = buffer.ptr - distance; var ch = buffer.ptr - distance;
blockSize += length; blockSize += length;
if(length > distance) { if(length > distance) {
var data = buffer.data; var data = buffer.data;
while (length--) { while (length--) {
buffer.insertByte(data[ch++]); buffer.insertByte(data[ch++]);
} }
} else { } else {
buffer.insertBytes(buffer.data.subarray(ch, ch + length)) buffer.insertBytes(buffer.data.subarray(ch, ch + length))
} }
} // length-distance pair } // length-distance pair
} // length-distance pair or end-of-block } // length-distance pair or end-of-block
} // loop until we reach end of block } // loop until we reach end of block
return blockSize; return blockSize;
} }
// {Uint8Array} compressedData A Uint8Array of the compressed file data. // {Uint8Array} compressedData A Uint8Array of the compressed file data.
@ -527,42 +527,42 @@ function inflate(compressedData, numDecompressedBytes) {
// block format: http://tools.ietf.org/html/rfc1951#page-9 // block format: http://tools.ietf.org/html/rfc1951#page-9
do { do {
var bFinal = bstream.readBits(1), var bFinal = bstream.readBits(1),
bType = bstream.readBits(2); bType = bstream.readBits(2);
blockSize = 0; blockSize = 0;
++numBlocks; ++numBlocks;
// no compression // no compression
if (bType == 0) { if (bType == 0) {
// skip remaining bits in this byte // skip remaining bits in this byte
while (bstream.bitPtr != 0) bstream.readBits(1); while (bstream.bitPtr != 0) bstream.readBits(1);
var len = bstream.readBits(16), var len = bstream.readBits(16),
nlen = bstream.readBits(16); nlen = bstream.readBits(16);
// TODO: check if nlen is the ones-complement of len? // TODO: check if nlen is the ones-complement of len?
if(len > 0) buffer.insertBytes(bstream.readBytes(len)); if(len > 0) buffer.insertBytes(bstream.readBytes(len));
blockSize = len; blockSize = len;
} }
// fixed Huffman codes // fixed Huffman codes
else if(bType == 1) { else if(bType == 1) {
blockSize = inflateBlockData(bstream, getFixedLiteralTable(), getFixedDistanceTable(), buffer); blockSize = inflateBlockData(bstream, getFixedLiteralTable(), getFixedDistanceTable(), buffer);
} }
// dynamic Huffman codes // dynamic Huffman codes
else if(bType == 2) { else if(bType == 2) {
var numLiteralLengthCodes = bstream.readBits(5) + 257; var numLiteralLengthCodes = bstream.readBits(5) + 257;
var numDistanceCodes = bstream.readBits(5) + 1, var numDistanceCodes = bstream.readBits(5) + 1,
numCodeLengthCodes = bstream.readBits(4) + 4; numCodeLengthCodes = bstream.readBits(4) + 4;
// populate the array of code length codes (first de-compaction) // populate the array of code length codes (first de-compaction)
var codeLengthsCodeLengths = [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0]; var codeLengthsCodeLengths = [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0];
for (var i = 0; i < numCodeLengthCodes; ++i) { for (var i = 0; i < numCodeLengthCodes; ++i) {
codeLengthsCodeLengths[ CodeLengthCodeOrder[i] ] = bstream.readBits(3); codeLengthsCodeLengths[ CodeLengthCodeOrder[i] ] = bstream.readBits(3);
} }
// get the Huffman Codes for the code lengths // get the Huffman Codes for the code lengths
var codeLengthsCodes = getHuffmanCodes(codeLengthsCodeLengths); var codeLengthsCodes = getHuffmanCodes(codeLengthsCodeLengths);
// now follow this mapping // now follow this mapping
/* /*
0 - 15: Represent code lengths of 0 - 15 0 - 15: Represent code lengths of 0 - 15
16: Copy the previous code length 3 - 6 times. 16: Copy the previous code length 3 - 6 times.
The next 2 bits indicate repeat length The next 2 bits indicate repeat length
@ -574,59 +574,59 @@ function inflate(compressedData, numDecompressedBytes) {
(3 bits of length) (3 bits of length)
18: Repeat a code length of 0 for 11 - 138 times 18: Repeat a code length of 0 for 11 - 138 times
(7 bits of length) (7 bits of length)
*/ */
// to generate the true code lengths of the Huffman Codes for the literal // to generate the true code lengths of the Huffman Codes for the literal
// and distance tables together // and distance tables together
var literalCodeLengths = []; var literalCodeLengths = [];
var prevCodeLength = 0; var prevCodeLength = 0;
while (literalCodeLengths.length < numLiteralLengthCodes + numDistanceCodes) { while (literalCodeLengths.length < numLiteralLengthCodes + numDistanceCodes) {
var symbol = decodeSymbol(bstream, codeLengthsCodes); var symbol = decodeSymbol(bstream, codeLengthsCodes);
if (symbol <= 15) { if (symbol <= 15) {
literalCodeLengths.push(symbol); literalCodeLengths.push(symbol);
prevCodeLength = symbol; prevCodeLength = symbol;
} }
else if (symbol == 16) { else if (symbol == 16) {
var repeat = bstream.readBits(2) + 3; var repeat = bstream.readBits(2) + 3;
while (repeat--) { while (repeat--) {
literalCodeLengths.push(prevCodeLength); literalCodeLengths.push(prevCodeLength);
} }
} }
else if (symbol == 17) { else if (symbol == 17) {
var repeat = bstream.readBits(3) + 3; var repeat = bstream.readBits(3) + 3;
while (repeat--) { while (repeat--) {
literalCodeLengths.push(0); literalCodeLengths.push(0);
} }
} }
else if (symbol == 18) { else if (symbol == 18) {
var repeat = bstream.readBits(7) + 11; var repeat = bstream.readBits(7) + 11;
while (repeat--) { while (repeat--) {
literalCodeLengths.push(0); literalCodeLengths.push(0);
} }
} }
} }
// now split the distance code lengths out of the literal code array // now split the distance code lengths out of the literal code array
var distanceCodeLengths = literalCodeLengths.splice(numLiteralLengthCodes, numDistanceCodes); var distanceCodeLengths = literalCodeLengths.splice(numLiteralLengthCodes, numDistanceCodes);
// now generate the true Huffman Code tables using these code lengths // now generate the true Huffman Code tables using these code lengths
var hcLiteralTable = getHuffmanCodes(literalCodeLengths), var hcLiteralTable = getHuffmanCodes(literalCodeLengths),
hcDistanceTable = getHuffmanCodes(distanceCodeLengths); hcDistanceTable = getHuffmanCodes(distanceCodeLengths);
blockSize = inflateBlockData(bstream, hcLiteralTable, hcDistanceTable, buffer); blockSize = inflateBlockData(bstream, hcLiteralTable, hcDistanceTable, buffer);
} }
// error // error
else { else {
err("Error! Encountered deflate block of type 3"); err("Error! Encountered deflate block of type 3");
return null; return null;
} }
// update progress // update progress
currentBytesUnarchivedInFile += blockSize; currentBytesUnarchivedInFile += blockSize;
currentBytesUnarchived += blockSize; currentBytesUnarchived += blockSize;
postProgress(); postProgress();
} while (bFinal != 1); } while (bFinal != 1);
// we are done reading blocks if the bFinal bit was set for this block // we are done reading blocks if the bFinal bit was set for this block
// return the buffer data bytes // return the buffer data bytes
return buffer.data; return buffer.data;
} }