UNPKG

allume

Version:

A cross-platform package bootloader for javascript.

435 lines (390 loc) 17.2 kB
///////////////////////////////////////////////////////////////////////////////////// // // module 'cc.io.format.gzip.0.2.0/' // ///////////////////////////////////////////////////////////////////////////////////// (function(using, require) { define.parameters = {}; define.parameters.wrapped = true; define.parameters.system = "pkx"; define.parameters.id = "cc.io.format.gzip.0.2.0/"; define.parameters.pkx = { "name": "cc.io.format.gzip", "version": "0.2.0", "title": "IO GZIP Format Module", "description": "IO module that implements GZIP stream support.", "license": "Apache-2.0", "pkx": { "main": "gzip.js", "dependencies": [ "cc.type.0.2", "cc.string.0.2", "cc.event.0.2", "cc.io.0.2", "cc.inflate.0.2" ] } }; define.parameters.dependencies = [ "pkx", "module", "configuration", "requirer" ]; define.parameters.dependencies[0] = define.parameters.pkx; define.parameters.dependencies.push(define.cache.get("cc.type.0.2/")); define.parameters.dependencies.push(define.cache.get("cc.string.0.2/")); define.parameters.dependencies.push(define.cache.get("cc.event.0.2/")); define.parameters.dependencies.push(define.cache.get("cc.io.0.2/")); define.parameters.dependencies.push(define.cache.get("cc.inflate.0.2/")); using = define.getUsing(define.parameters.id); require = define.getRequire(define.parameters.id, require); ///////////////////////////////////////////////////////////////////////////////////////////// // // cc.io.format.gzip // // IO module that implements GZIP stream support. // // License // Apache License Version 2.0 // // Copyright Nick Verlinden (info@createconform.com) // ///////////////////////////////////////////////////////////////////////////////////////////// (function(){ function GZip(pkx, module) { var own = this; var type, event, inflate, io, string; if (typeof require === "function") { type = require("./cc.type"); event = require("./cc.event"); string = require("./cc.string"); io = require("./cc.io"); inflate = require("./cc.inflate"); } this.GZIP_TAG_TYPE_OBJECT = "gzip-tag-type-object"; this.ERROR_READER_NOT_READY = "gzip-error-reader-not-ready"; this.ERROR_CORRUPTED_GZIP_FILE = "gzip-error-corrupted-gzip-file"; var T_DEFLATE= 8; var F_TEXT= 1 << 0; var F_CRC= 1 << 1; var F_EXTRA= 1 << 2; var F_NAME= 1 << 3; var F_COMMENT= 1 << 4; this.GZipObject = function(header, stream) { var self = this; this.isValid = false; if (!header) { return; } //https://gist.github.com/kig/417483 //http://www.zlib.org/rfc-gzip.html this.id1 = header[0]; //BYTE 0 - BYTE this.id2 = header[1]; //BYTE 1 - BYTE this.compressionMethod = header[2]; //BYTE 2 - BYTE this.flags = header[3]; //BYTE 3 - BYTE this.mTime = null; //BYTE 4 - UInt32LE this.extraFlags = header[8]; //BYTE 8 - BYTE this.operatingSystem = header[9]; //BYTE 9 - BYTE var offset = 10; //if flags extra this.extraField = ""; if (self.flags & F_EXTRA) { var xLen = readInt(header, offset, 2); offset += 2; self.extraField = header.subarray(offset, offset + xLen); //BYTE 10 -> 11 = XLENGTH - UInt16LE, BYTE 12 -> XLENGTH = FIELD - STRING offset += xLen; } //if flags name this.name = ""; if (self.flags & F_NAME) { self.name = readString(header, offset); //BYTE 10 + 2 + XLENGTH + STRINGLENGTH UNTIL 0 BYTE offset += self.name.length + 1; } //if flags comment this.comment = ""; if (self.flags & F_COMMENT) { self.comment = readString(header, offset); //BYTE 10 + 2 + XLENGTH + FILENAMELENGTH + 1 offset += self.comment.length + 1; } //this.computedCRC16 = null; //CRC16(BYTE 10 + 2 + XLENGTH + FILENAMELENGTH + 1 + COMMENTLENGTH + 1); //if flags header checksum //this.headerCRC16 = null; //BYTE 10 + 2 + XLENGTH + FILENAMELENGTH + 1 + COMMENTLENGTH + 1 - UInt16LE if (self.flags & F_CRC) { // h.headerCRC16 = Bin.UInt16LE(s, offset); // if (h.computedHeaderCRC16 != null && h.headerCRC16 != h.computedHeaderCRC16) // throw("Header CRC16 check failed"); offset += 2; } this.offset = offset; //BYTE 10 + 2 + XLENGTH + FILENAMELENGTH + 1 + COMMENTLENGTH + 1 + 2 this.size = null; //CALCULATE FROM LENGTH self.isValid = validate(header); function validate(header) { // check compression method if (self.id1 != 0x1f || self.id2 != 0x8b || self.compressionMethod != T_DEFLATE) { return false; } // header checksum /*var sum = 0; for (var b = 0; b < header.length; b++) { if (b >= 148 && b < 148 + 8) { sum += 32; } else { sum += header[b]; } } return sum == self.checksum;*/ return true; } function readString(header, pos, length) { var zeroIdx = pos; for (; zeroIdx<header.length; zeroIdx++) { if (header[zeroIdx] == 0 || (length && zeroIdx == pos + length)) { break; } } if (zeroIdx == header.length && !length) { throw("No null byte encountered"); } return header.subarray(pos, zeroIdx); } function readInt(header, pos, length) { var l = 0; var str = readString(header, pos, length); if (str != "") { try { l = parseInt(str, 8); } catch(e) { //not an integer } } return l; } }; this.GZipReader = function(stream) { var self = this; var streamPosition = 0; var objectCount = 0; var initializing = false; var HEADER_SIZE = 10; this.err = []; this.metadata = new own.GZipMetadata(); this.getGZipObjectStream = function(obj) { return new own.GZipStream(stream, obj); }; this.then = function(resolve, refuse) { if (initializing) { refuse(new Error(own.ERROR_READER_NOT_READY, "Reader initialization already started.")); return; } initializing = true; init(resolve, refuse); }; this.close = function() { stream.close(); }; function init(resolve, refuse) { probe().then(function() { readMetadata().then(function() { resolve(self); }, refuse); }, refuse); } function probe() { return new Promise(function(resolve, refuse) { stream.read(HEADER_SIZE, 0).then(function (header) { var tObj = new own.GZipObject(header, stream); if (!tObj.isValid) { refuse(new Error(io.ERROR_UNSUPPORTED_STREAM, "The stream does not seem to contain a valid gzip file.")); return; } resolve(); }, refuse); }); } function readMetadata() { return new Promise(function(resolve, refuse) { readNextHeader().then(resolve, function handleGZipError(e) { // when implementing multi file support, change "resolve" to "readNext" if (e.name != io.ERROR_INVALID_LENGTH) { refuse(e); return; } resolve(); }); }); } function readNextHeader() { return new Promise(function readGZipHeader(resolve, refuse) { stream.read(HEADER_SIZE, streamPosition).then(function processGZipHeader(header) { var tT = new own.GZipTag("object" + objectCount, own.GZIP_TAG_TYPE_OBJECT, { "header" : header, "stream" : stream } ); objectCount++; streamPosition += HEADER_SIZE + tT.size; self.metadata.tags.push(tT); resolve(); }, refuse); }); } }; this.GZipTag = function(key, type, data) { this.key = key; this.type = type; this.value = convertDataToValue(type, data); this.data = data; function convertDataToValue() { switch(type) { case own.GZIP_TAG_TYPE_OBJECT: return new own.GZipObject(data.header, data.stream); break; default: throw "Not implemented"; break; } } }; this.GZipMetadata = function() { this.tags = []; }; this.GZipStream = function(stream, object) { if (!stream || !object) { return null; } var self = this; var inflatedBuffer = null; var closed = false; function inflateStream() { return new Promise(function(resolve, reject) { stream.read(null, object.offset).then(function(buff) { inflatedBuffer = inflate(buff); //TODO - After reading verify checksum in footer //if (!tObj.checksum) { // refuse(own.ERROR_CORRUPTED_GZIP_FILE); // return; //} if (!inflatedBuffer || inflatedBuffer.length == 0) { reject(new Error(own.ERROR_CORRUPTED_GZIP_FILE, "")); } else { resolve(); } }, reject); }); } this.getName = function() { return object.name; }; this.getLength = function() { return new Promise(function(resolve, reject) { if (closed) { reject(new Error(io.ERROR_STREAM_CLOSED, "")); return; } if (!inflatedBuffer) { inflateStream().then(function() { self.getLength().then(resolve, reject); }, reject); } else if (inflatedBuffer) { resolve(inflatedBuffer.length); } else { reject(new Error(own.ERROR_CORRUPTED_GZIP_FILE, "")); } }); }; this.read = function (len, position) { if (!position) { position = 0; } return new Promise(function(resolve, reject) { if (closed) { reject(new Error(io.ERROR_STREAM_CLOSED, "")); return; } if (len == null) { self.getLength().then(function (length) { doRead(length - position); }, function(err) { reject(err); }); } else { doRead(len); } function doRead(len) { if (!inflatedBuffer) { inflateStream().then(function() { self.read(len, position).then(resolve, reject); }, reject); } else if (inflatedBuffer) { if (position + len > inflatedBuffer.length) { reject(new Error(io.ERROR_INVALID_LENGTH, "")); } else if (len == inflatedBuffer.length) { resolve(inflatedBuffer); } else { var nBuf = inflatedBuffer.subarray(position, position + len); position += len; resolve(nBuf); } } else { reject(new Error(own.ERROR_CORRUPTED_GZIP_FILE, "")); } } }); }; this.close = function () { return new Promise(function(resolve, refuse) { if (closed) { refuse(new Error(io.ERROR_STREAM_CLOSED, "")); return; } inflatedBuffer = null; closed = true; resolve(); }); }; this.events = new event.Emitter(this); }; this.GZipStream.prototype = io.Stream; } var singleton; (function (obj, factory) { var supported = false; if (typeof define === "function" && (define.amd || define.using)) { define(factory); supported = true; } if (typeof module === "object" && module.exports && typeof require != "undefined" && typeof require.main != "undefined" && require.main !== module) { module.exports = factory(); supported = true; } if (!supported) { obj.returnExports = factory(); } }(this, function() { if (singleton) { return singleton; } singleton = new (Function.prototype.bind.apply(GZip, arguments)); return singleton; })); })(); })(typeof using != "undefined"? using : null, typeof require != "undefined"? require : null);