UNPKG

zlib-streams

Version:

WASM-based Compression Streams API implementation using zlib, with support for deflate64 decompression.

157 lines (152 loc) 4.69 kB
/* eslint-disable no-unused-vars */ /* global Buffer, process, TransformStream */ let wasm, malloc, free, memory; export function setWasmExports(wasmAPI) { wasm = wasmAPI; ({ malloc, free, memory } = wasm); } function _make(isCompress, type, options = {}) { const level = (typeof options.level === "number") ? options.level : -1; const outBufferSize = (typeof options.outBuffer === "number") ? options.outBuffer : 64 * 1024; const inBufferSize = (typeof options.inBufferSize === "number") ? options.inBufferSize : 64 * 1024; return new TransformStream({ start() { let result; this.out = malloc(outBufferSize); this.in = malloc(inBufferSize); this.inBufferSize = inBufferSize; this._scratch = new Uint8Array(outBufferSize); if (isCompress) { this._process = wasm.deflate_process; this._last_consumed = wasm.deflate_last_consumed; this._end = wasm.deflate_end; this.streamHandle = wasm.deflate_new(); if (type === "gzip") { result = wasm.deflate_init_gzip(this.streamHandle, level); } else if (type === "deflate-raw") { result = wasm.deflate_init_raw(this.streamHandle, level); } else { result = wasm.deflate_init(this.streamHandle, level); } } else { if (type === "deflate64-raw") { this._process = wasm.inflate9_process; this._last_consumed = wasm.inflate9_last_consumed; this._end = wasm.inflate9_end; this.streamHandle = wasm.inflate9_new(); result = wasm.inflate9_init_raw(this.streamHandle); } else { this._process = wasm.inflate_process; this._last_consumed = wasm.inflate_last_consumed; this._end = wasm.inflate_end; this.streamHandle = wasm.inflate_new(); if (type === "deflate-raw") { result = wasm.inflate_init_raw(this.streamHandle); } else if (type === "gzip") { result = wasm.inflate_init_gzip(this.streamHandle); } else { result = wasm.inflate_init(this.streamHandle); } } } if (result !== 0) { throw new Error("init failed:" + result); } }, transform(chunk, controller) { try { const buffer = chunk; const heap = new Uint8Array(memory.buffer); const process = this._process; const last_consumed = this._last_consumed; const out = this.out; const scratch = this._scratch; let offset = 0; while (offset < buffer.length) { const toRead = Math.min(buffer.length - offset, 32 * 1024); if (!this.in || this.inBufferSize < toRead) { if (this.in && free) { free(this.in); } this.in = malloc(toRead); this.inBufferSize = toRead; } heap.set(buffer.subarray(offset, offset + toRead), this.in); const result = process(this.streamHandle, this.in, toRead, out, outBufferSize, 0); if (!isCompress && result < 0) { throw new Error("process error:" + result); } const prod = result & 0x00ffffff; if (prod) { scratch.set(heap.subarray(out, out + prod), 0); controller.enqueue(scratch.slice(0, prod)); } const consumed = last_consumed(this.streamHandle); if (consumed === 0) { break; } offset += consumed; } } catch (error) { if (this._end && this.streamHandle) { this._end(this.streamHandle); } if (this.in && free) { free(this.in); } if (this.out && free) { free(this.out); } controller.error(error); } }, flush(controller) { try { const heap = new Uint8Array(memory.buffer); const process = this._process; const out = this.out; const scratch = this._scratch; while (true) { const result = process(this.streamHandle, 0, 0, out, outBufferSize, 4); if (!isCompress && result < 0) { throw new Error("process error:" + result); } const produced = result & 0x00ffffff; const code = (result >> 24) & 0xff; if (produced) { scratch.set(heap.subarray(out, out + produced), 0); controller.enqueue(scratch.slice(0, produced)); } if (code === 1 || produced === 0) { break; } } } catch (error) { controller.error(error); } finally { if (this._end && this.streamHandle) { const result = this._end(this.streamHandle); if (result !== 0) { controller.error(new Error("end error:" + result)); } } if (this.in && free) { free(this.in); } if (this.out && free) { free(this.out); } } } }); } export class CompressionStreamZlib { constructor(type = "deflate", options) { return _make(true, type, options); } } export class DecompressionStreamZlib { constructor(type = "deflate", options) { return _make(false, type, options); } }