Team:Heidelberg/js/gunzip.min.js
From 2014.igem.org
(Difference between revisions)
m (Created page with "- →* @license zlib.js 2012 - imaya [ https://github.com/imaya/zlib.js ] The MIT License: (function() {'use strict';function n(e){throw e;}var q=void 0,aa=this;function r(e,c){var...") |
m |
||
Line 1: | Line 1: | ||
- | /** @ | + | /** |
- | + | * @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: */ |
Revision as of 20:32, 17 October 2014
/**
* @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: */