Team:Heidelberg/js/gunzip.min.js

From 2014.igem.org

Revision as of 20:32, 17 October 2014 by Jan glx (Talk | contribs)

/**

* @fileoverview GZIP (RFC1952) 展開コンテナ実装.
*/

goog.provide('Zlib.Gunzip');

goog.require('USE_TYPEDARRAY'); goog.require('Zlib.CRC32'); goog.require('Zlib.Gzip'); goog.require('Zlib.RawInflate'); goog.require('Zlib.GunzipMember');

goog.scope(function() {

/**

* @constructor
* @param {!(Array|Uint8Array)} input input buffer.
* @param {Object=} opt_params option parameters.
*/

Zlib.Gunzip = function(input, opt_params) {

 /** @type {!(Array.<number>|Uint8Array)} input buffer. */
 this.input = input;
 /** @type {number} input buffer pointer. */
 this.ip = 0;
 /** @type {Array.<Zlib.GunzipMember>} */
 this.member = [];
 /** @type {boolean} */
 this.decompressed = false;

};

/**

* @return {Array.<Zlib.GunzipMember>}
*/

Zlib.Gunzip.prototype.getMembers = function() {

 if (!this.decompressed) {
   this.decompress();
 }
 return this.member.slice();

};

/**

* inflate gzip data.
* @return {!(Array.<number>|Uint8Array)} inflated buffer.
*/

Zlib.Gunzip.prototype.decompress = function() {

 /** @type {number} input length. */
 var il = this.input.length;
 while (this.ip < il) {
   this.decodeMember();
 }
 this.decompressed = true;
 return this.concatMember();

};

/**

* decode gzip member.
*/

Zlib.Gunzip.prototype.decodeMember = function() {

 /** @type {Zlib.GunzipMember} */
 var member = new Zlib.GunzipMember();
 /** @type {number} */
 var isize;
 /** @type {Zlib.RawInflate} RawInflate implementation. */
 var rawinflate;
 /** @type {!(Array.<number>|Uint8Array)} inflated data. */
 var inflated;
 /** @type {number} inflate size */
 var inflen;
 /** @type {number} character code */
 var c;
 /** @type {number} character index in string. */
 var ci;
 /** @type {Array.<string>} character array. */
 var str;
 /** @type {number} modification time. */
 var mtime;
 /** @type {number} */
 var crc32;
 var input = this.input;
 var ip = this.ip;
 member.id1 = input[ip++];
 member.id2 = input[ip++];
 // check signature
 if (member.id1 !== 0x1f || member.id2 !== 0x8b) {
   throw new Error('invalid file signature:' + member.id1 + ',' + member.id2);
 }
 // check compression method
 member.cm = input[ip++];
 switch (member.cm) {
   case 8: /* XXX: use Zlib const */
     break;
   default:
     throw new Error('unknown compression method: ' + member.cm);
 }
 // flags
 member.flg = input[ip++];
 // modification time
 mtime = (input[ip++])       |
         (input[ip++] << 8)  |
         (input[ip++] << 16) |
         (input[ip++] << 24);
 member.mtime = new Date(mtime * 1000);
 // extra flags
 member.xfl = input[ip++];
 // operating system
 member.os = input[ip++];
 // extra
 if ((member.flg & Zlib.Gzip.FlagsMask.FEXTRA) > 0) {
   member.xlen = input[ip++] | (input[ip++] << 8);
   ip = this.decodeSubField(ip, member.xlen);
 }
 // fname
 if ((member.flg & Zlib.Gzip.FlagsMask.FNAME) > 0) {
   for(str = [], ci = 0; (c = input[ip++]) > 0;) {
     str[ci++] = String.fromCharCode(c);
   }
   member.name = str.join();
 }
 // fcomment
 if ((member.flg & Zlib.Gzip.FlagsMask.FCOMMENT) > 0) {
   for(str = [], ci = 0; (c = input[ip++]) > 0;) {
     str[ci++] = String.fromCharCode(c);
   }
   member.comment = str.join();
 }
 // fhcrc
 if ((member.flg & Zlib.Gzip.FlagsMask.FHCRC) > 0) {
   member.crc16 = Zlib.CRC32.calc(input, 0, ip) & 0xffff;
   if (member.crc16 !== (input[ip++] | (input[ip++] << 8))) {
     throw new Error('invalid header crc16');
   }
 }
 // isize を事前に取得すると展開後のサイズが分かるため、
 // inflate処理のバッファサイズが事前に分かり、高速になる
 isize = (input[input.length - 4])       | (input[input.length - 3] << 8) |
         (input[input.length - 2] << 16) | (input[input.length - 1] << 24);
 // isize の妥当性チェック
 // ハフマン符号では最小 2-bit のため、最大で 1/4 になる
 // LZ77 符号では 長さと距離 2-Byte で最大 258-Byte を表現できるため、
 // 1/128 になるとする
 // ここから入力バッファの残りが isize の 512 倍以上だったら
 // サイズ指定のバッファ確保は行わない事とする
 if (input.length - ip - /* CRC-32 */4 - /* ISIZE */4 < isize * 512) {
   inflen = isize;
 }
 // compressed block
 rawinflate = new Zlib.RawInflate(input, {'index': ip, 'bufferSize': inflen});
 member.data = inflated = rawinflate.decompress();
 ip = rawinflate.ip;
 // crc32
 member.crc32 = crc32 =
   ((input[ip++])       | (input[ip++] << 8) |
    (input[ip++] << 16) | (input[ip++] << 24)) >>> 0;
 if (Zlib.CRC32.calc(inflated) !== crc32) {
   throw new Error('invalid CRC-32 checksum: 0x' +
       Zlib.CRC32.calc(inflated).toString(16) + ' / 0x' + crc32.toString(16));
 }
 // input size
 member.isize = isize =
   ((input[ip++])       | (input[ip++] << 8) |
    (input[ip++] << 16) | (input[ip++] << 24)) >>> 0;
 if ((inflated.length & 0xffffffff) !== isize) {
   throw new Error('invalid input size: ' +
       (inflated.length & 0xffffffff) + ' / ' + isize);
 }
 this.member.push(member);
 this.ip = ip;

};

/**

* サブフィールドのデコード
* XXX: 現在は何もせずスキップする
*/

Zlib.Gunzip.prototype.decodeSubField = function(ip, length) {

 return ip + length;

};

/**

* @return {!(Array.<number>|Uint8Array)}
*/

Zlib.Gunzip.prototype.concatMember = function() {

 /** @type {Array.<Zlib.GunzipMember>} */
 var member = this.member;
 /** @type {number} */
 var i;
 /** @type {number} */
 var il;
 /** @type {number} */
 var p = 0;
 /** @type {number} */
 var size = 0;
 /** @type {!(Array.<number>|Uint8Array)} */
 var buffer;
 for (i = 0, il = member.length; i < il; ++i) {
   size += member[i].data.length;
 }
 if (USE_TYPEDARRAY) {
   buffer = new Uint8Array(size);
   for (i = 0; i < il; ++i) {
     buffer.set(member[i].data, p);
     p += member[i].data.length;
   }
 } else {
   buffer = [];
   for (i = 0; i < il; ++i) {
     buffer[i] = member[i].data;
   }
   buffer = Array.prototype.concat.apply([], buffer);
 }
 return buffer;

};

}); /* vim:set expandtab ts=2 sw=2 tw=80: */