You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
234 lines
6.1 KiB
JavaScript
234 lines
6.1 KiB
JavaScript
|
|
var gzip = gzip || {};
|
|
var zip = zip || require('./zip');
|
|
|
|
gzip.Archive = class {
|
|
|
|
static open(data) {
|
|
const stream = data instanceof Uint8Array ? new gzip.BinaryReader(data) : data;
|
|
const signature = [ 0x1f, 0x8b ];
|
|
if (stream.length > 18 && stream.peek(2).every((value, index) => value === signature[index])) {
|
|
return new gzip.Archive(stream);
|
|
}
|
|
return null;
|
|
}
|
|
|
|
constructor(stream) {
|
|
const position = stream.position;
|
|
const entry = new gzip.Entry(stream);
|
|
this._entries = new Map([ [ entry.name, entry.stream ] ]);
|
|
stream.seek(position);
|
|
}
|
|
|
|
get entries() {
|
|
return this._entries;
|
|
}
|
|
};
|
|
|
|
gzip.Entry = class {
|
|
|
|
constructor(stream) {
|
|
const signature = [ 0x1f, 0x8b ];
|
|
if (stream.position + 2 > stream.length ||
|
|
!stream.read(2).every((value, index) => value === signature[index])) {
|
|
throw new gzip.Error('Invalid gzip signature.');
|
|
}
|
|
const string = () => {
|
|
let content = '';
|
|
while (stream.position < stream.length) {
|
|
const value = stream.byte();
|
|
if (value === 0x00) {
|
|
break;
|
|
}
|
|
content += String.fromCharCode(value);
|
|
}
|
|
return content;
|
|
};
|
|
const reader = new gzip.BinaryReader(stream.read(8));
|
|
const compressionMethod = reader.byte();
|
|
if (compressionMethod != 8) {
|
|
throw new gzip.Error("Invalid compression method '" + compressionMethod.toString() + "'.");
|
|
}
|
|
const flags = reader.byte();
|
|
reader.uint32(); // MTIME
|
|
reader.byte(); // XFL
|
|
reader.byte(); // OS
|
|
if ((flags & 4) != 0) { // FEXTRA
|
|
const xlen = stream.byte() | (stream.byte() << 8);
|
|
stream.skip(xlen);
|
|
}
|
|
this._name = (flags & 8) != 0 ? string() : ''; // FNAME
|
|
if ((flags & 16) != 0) { // FCOMMENT
|
|
string();
|
|
}
|
|
if ((flags & 1) != 0) { // FHCRC
|
|
stream.skip(2);
|
|
}
|
|
this._stream = new gzip.InflaterStream(stream);
|
|
}
|
|
|
|
get name() {
|
|
return this._name;
|
|
}
|
|
|
|
get stream() {
|
|
return this._stream;
|
|
}
|
|
};
|
|
|
|
gzip.InflaterStream = class {
|
|
|
|
constructor(stream) {
|
|
this._stream = stream.stream(stream.length - stream.position - 8);
|
|
const reader = new gzip.BinaryReader(stream.read(8));
|
|
reader.uint32(); // CRC32
|
|
this._length = reader.uint32(); // ISIZE
|
|
this._position = 0;
|
|
}
|
|
|
|
get position() {
|
|
return this._position;
|
|
}
|
|
|
|
get length() {
|
|
return this._length;
|
|
}
|
|
|
|
seek(position) {
|
|
if (this._buffer === undefined) {
|
|
this._inflate();
|
|
}
|
|
this._position = position >= 0 ? position : this._length + position;
|
|
}
|
|
|
|
skip(offset) {
|
|
if (this._buffer === undefined) {
|
|
this._inflate();
|
|
}
|
|
this._position += offset;
|
|
}
|
|
|
|
stream(length) {
|
|
return new gzip.BinaryReader(this.read(length));
|
|
}
|
|
|
|
peek(length) {
|
|
const position = this._position;
|
|
length = length !== undefined ? length : this._length - this._position;
|
|
this.skip(length);
|
|
const end = this._position;
|
|
this.seek(position);
|
|
if (position === 0 && length === this._length) {
|
|
return this._buffer;
|
|
}
|
|
return this._buffer.subarray(position, end);
|
|
}
|
|
|
|
read(length) {
|
|
const position = this._position;
|
|
length = length !== undefined ? length : this._length - this._position;
|
|
this.skip(length);
|
|
if (position === 0 && length === this._length) {
|
|
return this._buffer;
|
|
}
|
|
return this._buffer.subarray(position, this._position);
|
|
}
|
|
|
|
byte() {
|
|
const position = this._position;
|
|
this.skip(1);
|
|
return this._buffer[position];
|
|
}
|
|
|
|
_inflate() {
|
|
if (this._buffer === undefined) {
|
|
const buffer = this._stream.peek();
|
|
this._buffer = new zip.Inflater().inflateRaw(buffer, this._length);
|
|
delete this._stream;
|
|
}
|
|
}
|
|
};
|
|
|
|
gzip.BinaryReader = class {
|
|
|
|
constructor(buffer) {
|
|
this._buffer = buffer;
|
|
this._length = buffer.length;
|
|
this._position = 0;
|
|
this._view = new DataView(buffer.buffer, buffer.byteOffset, buffer.byteLength);
|
|
}
|
|
|
|
get position() {
|
|
return this._position;
|
|
}
|
|
|
|
get length() {
|
|
return this._length;
|
|
}
|
|
|
|
create(buffer) {
|
|
return new gzip.BinaryReader(buffer);
|
|
}
|
|
|
|
stream(length) {
|
|
return this.create(this.read(length));
|
|
}
|
|
|
|
seek(position) {
|
|
this._position = position >= 0 ? position : this._length + position;
|
|
}
|
|
|
|
skip(offset) {
|
|
this._position += offset;
|
|
}
|
|
|
|
peek(length) {
|
|
if (this._position === 0 && length === undefined) {
|
|
return this._buffer;
|
|
}
|
|
const position = this._position;
|
|
this.skip(length !== undefined ? length : this._length - this._position);
|
|
const end = this._position;
|
|
this.seek(position);
|
|
return this._buffer.subarray(position, end);
|
|
}
|
|
|
|
read(length) {
|
|
if (this._position === 0 && length === undefined) {
|
|
this._position = this._length;
|
|
return this._buffer;
|
|
}
|
|
const position = this._position;
|
|
this.skip(length !== undefined ? length : this._length - this._position);
|
|
return this._buffer.subarray(position, this._position);
|
|
}
|
|
|
|
byte() {
|
|
const position = this._position;
|
|
this.skip(1);
|
|
return this._buffer[position];
|
|
}
|
|
|
|
uint16() {
|
|
const position = this._position;
|
|
this.skip(2);
|
|
return this._view.getUint16(position, true);
|
|
}
|
|
|
|
uint32() {
|
|
const position = this._position;
|
|
this.skip(4);
|
|
return this._view.getUint32(position, true);
|
|
}
|
|
};
|
|
|
|
gzip.Error = class extends Error {
|
|
constructor(message) {
|
|
super(message);
|
|
this.name = 'Gzip Error';
|
|
}
|
|
};
|
|
|
|
if (typeof module !== 'undefined' && typeof module.exports === 'object') {
|
|
module.exports.Archive = gzip.Archive;
|
|
} |