4 var crc32 = require('crc32'),
5 deflate = require('deflate-js'),
6 // magic numbers marking this file as GZIP
20 'fat': 0, // FAT file system (DOS, OS/2, NT) + PKZIPW 2.50 VFAT, NTFS
22 'vmz': 2, // VMS (VAX or Alpha AXP)
24 'vm/cms': 4, // VM/CMS
26 'hpfs': 6, // HPFS file system (OS/2, NT 3.x)
27 'macintosh': 7, // Macintosh
28 'z-system': 8, // Z-System
30 'tops-20': 10, // TOPS-20
31 'ntfs': 11, // NTFS file system (NT)
32 'qdos': 12, // SMS/QDOS
33 'acorn': 13, // Acorn RISC OS
34 'vfat': 14, // VFAT file system (Win95, NT)
35 'vms': 15, // MVS (code also taken for PRIMOS)
36 'beos': 16, // BeOS (BeBox or PowerMac)
37 'tandem': 17, // Tandem/NSK
43 function putByte(n, arr) {
48 function putShort(n, arr) {
54 function putLong(n, arr) {
55 putShort(n & 0xffff, arr);
56 putShort(n >>> 16, arr);
59 function putString(s, arr) {
60 var i, len = s.length;
61 for (i = 0; i < len; i += 1) {
62 putByte(s.charCodeAt(i), arr);
66 function readByte(arr) {
70 function readShort(arr) {
71 return arr.shift() | (arr.shift() << 8);
74 function readLong(arr) {
75 var n1 = readShort(arr),
78 // JavaScript can't handle bits in the position 32
79 // we'll emulate this by removing the left-most bit (if it exists)
80 // and add it back in via multiplication, which does work
84 return ((n2 << 16) | n1) + 32768 * Math.pow(2, 16);
87 return (n2 << 16) | n1;
90 function readString(arr) {
93 // turn all bytes into chars until the terminating null
94 while (arr[0] !== 0) {
95 charArr.push(String.fromCharCode(arr.shift()));
98 // throw away terminating null
101 // join all characters into a cohesive string
102 return charArr.join('');
106 * Reads n number of bytes and return as an array.
108 * @param arr- Array of bytes to read from
109 * @param n- Number of bytes to read
111 function readBytes(arr, n) {
113 for (i = 0; i < n; i += 1) {
114 ret.push(arr.shift());
121 * ZIPs a file in GZIP format. The format is as given by the spec, found at:
122 * http://www.gzip.org/zlib/rfc-gzip.html
124 * Omitted parts in this implementation:
126 function zip(data, options) {
128 level = options.level || DEFAULT_LEVEL,
131 if (typeof data === 'string') {
132 data = Array.prototype.map.call(data, function (char) {
133 return char.charCodeAt(0);
137 // magic number marking this file as GZIP
141 putByte(compressionMethods['deflate'], out);
144 flags |= possibleFlags['FNAME'];
148 putLong(options.timestamp || parseInt(Date.now() / 1000, 10), out);
150 // put deflate args (extra flags)
154 } else if (level === 9) {
155 // maximum compression (fastest algorithm)
162 putByte(osMap[os], out);
165 // ignore the directory part
166 putString(options.name.substring(options.name.lastIndexOf('/') + 1), out);
172 deflate.deflate(data, level).forEach(function (byte) {
176 putLong(parseInt(crc32(data), 16), out);
177 putLong(data.length, out);
182 function unzip(data, options) {
183 // start with a copy of the array
184 var arr = Array.prototype.slice.call(data, 0),
196 // check the first two bytes for the magic numbers
197 if (readByte(arr) !== ID1 || readByte(arr) !== ID2) {
198 throw 'Not a GZIP file';
202 t = Object.keys(compressionMethods).some(function (key) {
203 compressionMethod = key;
204 return compressionMethods[key] === t;
208 throw 'Unsupported compression method';
211 flags = readByte(arr);
212 mtime = readLong(arr);
213 xFlags = readByte(arr);
215 Object.keys(osMap).some(function (key) {
216 if (osMap[key] === t) {
222 // just throw away the bytes for now
223 if (flags & possibleFlags['FEXTRA']) {
228 // just throw away for now
229 if (flags & possibleFlags['FNAME']) {
233 // just throw away for now
234 if (flags & possibleFlags['FCOMMENT']) {
238 // just throw away for now
239 if (flags & possibleFlags['FHCRC']) {
243 if (compressionMethod === 'deflate') {
244 // give deflate everything but the last 8 bytes
245 // the last 8 bytes are for the CRC32 checksum and filesize
246 res = deflate.inflate(arr.splice(0, arr.length - 8));
249 if (flags & possibleFlags['FTEXT']) {
250 res = Array.prototype.map.call(res, function (byte) {
251 return String.fromCharCode(byte);
256 if (crc !== parseInt(crc32(res), 16)) {
257 throw 'Checksum does not match';
260 size = readLong(arr);
261 if (size !== res.length) {
262 throw 'Size of decompressed file not correct';
271 get DEFAULT_LEVEL() {
272 return DEFAULT_LEVEL;