mirror of
https://github.com/swc-project/swc.git
synced 2024-12-21 12:41:54 +03:00
f8aa0509ce
swc_bundler: - Prevent infinite recursions. (#1963)
196 lines
5.6 KiB
TypeScript
196 lines
5.6 KiB
TypeScript
// Loaded from https://deno.land/x/compress@v0.3.8/zlib/inflate.ts
|
|
|
|
|
|
// from https://github.com/nodeca/pako
|
|
import { concatUint8Array } from "../utils/uint8.ts";
|
|
import * as zlib_inflate from "./zlib/inflate.ts";
|
|
import STATUS from "./zlib/status.ts";
|
|
import { message as msg, CODE } from "./zlib/messages.ts";
|
|
import ZStream from "./zlib/zstream.ts";
|
|
import GZheader from "./zlib/gzheader.ts";
|
|
|
|
export interface InflateOptions {
|
|
windowBits?: number;
|
|
dictionary?: Uint8Array;
|
|
chunkSize?: number;
|
|
to?: string;
|
|
raw?: boolean;
|
|
}
|
|
|
|
export class Inflate {
|
|
err: STATUS = 0; // error code, if happens (0 = Z_OK)
|
|
msg: string = ""; // error message
|
|
ended: boolean = false; // used to avoid multiple onEnd() calls
|
|
strm: ZStream;
|
|
options: any;
|
|
header: GZheader;
|
|
|
|
constructor(options: InflateOptions) {
|
|
this.options = {
|
|
chunkSize: 16384,
|
|
windowBits: 0,
|
|
to: "",
|
|
...options,
|
|
};
|
|
|
|
const opt = this.options;
|
|
|
|
// Force window size for `raw` data, if not set directly,
|
|
// because we have no header for autodetect.
|
|
if (opt.raw && (opt.windowBits >= 0) && (opt.windowBits < 16)) {
|
|
opt.windowBits = -opt.windowBits;
|
|
if (opt.windowBits === 0) opt.windowBits = -15;
|
|
}
|
|
|
|
// If `windowBits` not defined (and mode not raw) - set autodetect flag for gzip/deflate
|
|
if (
|
|
(opt.windowBits >= 0) && (opt.windowBits < 16) &&
|
|
!(options && options.windowBits)
|
|
) {
|
|
opt.windowBits += 32;
|
|
}
|
|
|
|
// Gzip header has no info about windows size, we can do autodetect only
|
|
// for deflate. So, if window size not set, force it to max when gzip possible
|
|
if ((opt.windowBits > 15) && (opt.windowBits < 48)) {
|
|
// bit 3 (16) -> gzipped data
|
|
// bit 4 (32) -> autodetect gzip/deflate
|
|
if ((opt.windowBits & 15) === 0) {
|
|
opt.windowBits |= 15;
|
|
}
|
|
}
|
|
|
|
this.strm = new ZStream();
|
|
this.strm.avail_out = 0;
|
|
|
|
var status = zlib_inflate.inflateInit2(
|
|
this.strm,
|
|
opt.windowBits,
|
|
);
|
|
|
|
if (status !== STATUS.Z_OK) {
|
|
throw new Error(msg[status as CODE]);
|
|
}
|
|
|
|
this.header = new GZheader();
|
|
zlib_inflate.inflateGetHeader(this.strm, this.header);
|
|
|
|
// Setup dictionary
|
|
if (opt.dictionary) {
|
|
if (opt.raw) { //In raw mode we need to set the dictionary early
|
|
status = zlib_inflate.inflateSetDictionary(this.strm, opt.dictionary);
|
|
if (status !== STATUS.Z_OK) {
|
|
throw new Error(msg[status as CODE]);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
push(data: Uint8Array, mode: boolean | number): Uint8Array {
|
|
const strm = this.strm;
|
|
const chunkSize = this.options.chunkSize;
|
|
const dictionary = this.options.dictionary;
|
|
const chunks: Uint8Array[] = [];
|
|
let status;
|
|
|
|
// Flag to properly process Z_BUF_ERROR on testing inflate call
|
|
// when we check that all output data was flushed.
|
|
var allowBufError = false;
|
|
|
|
if (this.ended) {
|
|
throw new Error("can not call after ended");
|
|
}
|
|
|
|
let _mode = (mode === ~~mode)
|
|
? mode
|
|
: ((mode === true) ? STATUS.Z_FINISH : STATUS.Z_NO_FLUSH);
|
|
|
|
strm.input = data;
|
|
strm.next_in = 0;
|
|
strm.avail_in = strm.input.length;
|
|
|
|
do {
|
|
if (strm.avail_out === 0) {
|
|
strm.output = new Uint8Array(chunkSize);
|
|
strm.next_out = 0;
|
|
strm.avail_out = chunkSize;
|
|
}
|
|
|
|
status = zlib_inflate.inflate(
|
|
strm,
|
|
STATUS.Z_NO_FLUSH,
|
|
); /* no bad return value */
|
|
|
|
if (status === STATUS.Z_NEED_DICT && dictionary) {
|
|
status = zlib_inflate.inflateSetDictionary(this.strm, dictionary);
|
|
}
|
|
|
|
if (status === STATUS.Z_BUF_ERROR && allowBufError === true) {
|
|
status = STATUS.Z_OK;
|
|
allowBufError = false;
|
|
}
|
|
|
|
if (status !== STATUS.Z_STREAM_END && status !== STATUS.Z_OK) {
|
|
this.ended = true;
|
|
throw new Error(this.strm.msg);
|
|
}
|
|
|
|
if (strm.next_out) {
|
|
if (
|
|
strm.avail_out === 0 || status === STATUS.Z_STREAM_END ||
|
|
(strm.avail_in === 0 &&
|
|
(_mode === STATUS.Z_FINISH || _mode === STATUS.Z_SYNC_FLUSH))
|
|
) {
|
|
chunks.push(strm.output!.subarray(0, strm.next_out));
|
|
}
|
|
}
|
|
|
|
// When no more input data, we should check that internal inflate buffers
|
|
// are flushed. The only way to do it when avail_out = 0 - run one more
|
|
// inflate pass. But if output data not exists, inflate return Z_BUF_ERROR.
|
|
// Here we set flag to process this error properly.
|
|
//
|
|
// NOTE. Deflate does not return error in this case and does not needs such
|
|
// logic.
|
|
if (strm.avail_in === 0 && strm.avail_out === 0) {
|
|
allowBufError = true;
|
|
}
|
|
} while (
|
|
(strm.avail_in > 0 || strm.avail_out === 0) &&
|
|
status !== STATUS.Z_STREAM_END
|
|
);
|
|
|
|
if (status === STATUS.Z_STREAM_END) {
|
|
_mode = STATUS.Z_FINISH;
|
|
}
|
|
|
|
// Finalize on the last chunk.
|
|
if (_mode === STATUS.Z_FINISH) {
|
|
status = zlib_inflate.inflateEnd(this.strm);
|
|
this.ended = true;
|
|
if (status !== STATUS.Z_OK) throw new Error(this.strm.msg);
|
|
}
|
|
|
|
// callback interim results if Z_SYNC_FLUSH.
|
|
if (_mode === STATUS.Z_SYNC_FLUSH) {
|
|
strm.avail_out = 0;
|
|
}
|
|
|
|
return concatUint8Array(chunks);
|
|
}
|
|
}
|
|
|
|
export function inflate(input: Uint8Array, options: InflateOptions = {}) {
|
|
const inflator = new Inflate(options);
|
|
const result = inflator.push(input, true);
|
|
// That will never happens, if you don't cheat with options :)
|
|
if (inflator.err) throw inflator.msg || msg[inflator.err as CODE];
|
|
return result;
|
|
}
|
|
|
|
export function inflateRaw(input: Uint8Array, options: InflateOptions = {}) {
|
|
options.raw = true;
|
|
return inflate(input, options);
|
|
}
|
|
|
|
export const gunzip = inflate; |