Skip to main content
Module

std/node/_zlib.mjs

Deno standard library
Go to Latest
File
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629
// Copyright 2018-2022 the Deno authors. All rights reserved. MIT license.// Copyright (c) 2014-2015 Devon Govett <devongovett@gmail.com>// Forked from https://github.com/browserify/browserify-zlib
// deno-lint-ignore-file
import { Buffer } from "./buffer.ts";import { Transform } from "./stream.ts";import * as binding from "./_zlib_binding.mjs";import util from "./util.ts";import { ok as assert } from "./assert.ts";import { kMaxLength } from "./_buffer.mjs";import { zlib as zlibConstants } from "./internal_binding/constants.ts";
var kRangeErrorMessage = "Cannot create final Buffer. It would be larger " + "than 0x" + kMaxLength.toString(16) + " bytes";
// translation table for return codes.export const codes = Object.freeze({ Z_OK: binding.Z_OK, Z_STREAM_END: binding.Z_STREAM_END, Z_NEED_DICT: binding.Z_NEED_DICT, Z_ERRNO: binding.Z_ERRNO, Z_STREAM_ERROR: binding.Z_STREAM_ERROR, Z_DATA_ERROR: binding.Z_DATA_ERROR, Z_MEM_ERROR: binding.Z_MEM_ERROR, Z_BUF_ERROR: binding.Z_BUF_ERROR, Z_VERSION_ERROR: binding.Z_VERSION_ERROR, [binding.Z_OK]: "Z_OK", [binding.Z_STREAM_END]: "Z_STREAM_END", [binding.Z_NEED_DICT]: "Z_NEED_DICT", [binding.Z_ERRNO]: "Z_ERRNO", [binding.Z_STREAM_ERROR]: "Z_STREAM_ERROR", [binding.Z_DATA_ERROR]: "Z_DATA_ERROR", [binding.Z_MEM_ERROR]: "Z_MEM_ERROR", [binding.Z_BUF_ERROR]: "Z_BUF_ERROR", [binding.Z_VERSION_ERRO]: "Z_VERSION_ERROR",});
export const createDeflate = function (o) { return new Deflate(o);};
export const createInflate = function (o) { return new Inflate(o);};
export const createDeflateRaw = function (o) { return new DeflateRaw(o);};
export const createInflateRaw = function (o) { return new InflateRaw(o);};
export const createGzip = function (o) { return new Gzip(o);};
export const createGunzip = function (o) { return new Gunzip(o);};
export const createUnzip = function (o) { return new Unzip(o);};
// Convenience methods.// compress/decompress a string or buffer in one step.export const deflate = function (buffer, opts, callback) { if (typeof opts === "function") { callback = opts; opts = {}; } return zlibBuffer(new Deflate(opts), buffer, callback);};
export const deflateSync = function (buffer, opts) { return zlibBufferSync(new Deflate(opts), buffer);};
export const gzip = function (buffer, opts, callback) { if (typeof opts === "function") { callback = opts; opts = {}; } return zlibBuffer(new Gzip(opts), buffer, callback);};
export const gzipSync = function (buffer, opts) { return zlibBufferSync(new Gzip(opts), buffer);};
export const deflateRaw = function (buffer, opts, callback) { if (typeof opts === "function") { callback = opts; opts = {}; } return zlibBuffer(new DeflateRaw(opts), buffer, callback);};
export const deflateRawSync = function (buffer, opts) { return zlibBufferSync(new DeflateRaw(opts), buffer);};
export const unzip = function (buffer, opts, callback) { if (typeof opts === "function") { callback = opts; opts = {}; } return zlibBuffer(new Unzip(opts), buffer, callback);};
export const unzipSync = function (buffer, opts) { return zlibBufferSync(new Unzip(opts), buffer);};
export const inflate = function (buffer, opts, callback) { if (typeof opts === "function") { callback = opts; opts = {}; } return zlibBuffer(new Inflate(opts), buffer, callback);};
export const inflateSync = function (buffer, opts) { return zlibBufferSync(new Inflate(opts), buffer);};
export const gunzip = function (buffer, opts, callback) { if (typeof opts === "function") { callback = opts; opts = {}; } return zlibBuffer(new Gunzip(opts), buffer, callback);};
export const gunzipSync = function (buffer, opts) { return zlibBufferSync(new Gunzip(opts), buffer);};
export const inflateRaw = function (buffer, opts, callback) { if (typeof opts === "function") { callback = opts; opts = {}; } return zlibBuffer(new InflateRaw(opts), buffer, callback);};
export const inflateRawSync = function (buffer, opts) { return zlibBufferSync(new InflateRaw(opts), buffer);};
function zlibBuffer(engine, buffer, callback) { var buffers = []; var nread = 0;
engine.on("error", onError); engine.on("end", onEnd);
engine.end(buffer); flow();
function flow() { var chunk; while (null !== (chunk = engine.read())) { buffers.push(chunk); nread += chunk.length; } engine.once("readable", flow); }
function onError(err) { engine.removeListener("end", onEnd); engine.removeListener("readable", flow); callback(err); }
function onEnd() { var buf; var err = null;
if (nread >= kMaxLength) { err = new RangeError(kRangeErrorMessage); } else { buf = Buffer.concat(buffers, nread); }
buffers = []; engine.close(); callback(err, buf); }}
function zlibBufferSync(engine, buffer) { if (typeof buffer === "string") buffer = Buffer.from(buffer);
if (!Buffer.isBuffer(buffer)) throw new TypeError("Not a string or buffer");
var flushFlag = engine._finishFlushFlag;
return engine._processChunk(buffer, flushFlag);}
// generic zlib// minimal 2-byte headerfunction Deflate(opts) { if (!(this instanceof Deflate)) return new Deflate(opts); Zlib.call(this, opts, binding.DEFLATE);}
function Inflate(opts) { if (!(this instanceof Inflate)) return new Inflate(opts); Zlib.call(this, opts, binding.INFLATE);}
// gzip - bigger header, same deflate compressionfunction Gzip(opts) { if (!(this instanceof Gzip)) return new Gzip(opts); Zlib.call(this, opts, binding.GZIP);}
function Gunzip(opts) { if (!(this instanceof Gunzip)) return new Gunzip(opts); Zlib.call(this, opts, binding.GUNZIP);}
// raw - no headerfunction DeflateRaw(opts) { if (!(this instanceof DeflateRaw)) return new DeflateRaw(opts); Zlib.call(this, opts, binding.DEFLATERAW);}
function InflateRaw(opts) { if (!(this instanceof InflateRaw)) return new InflateRaw(opts); Zlib.call(this, opts, binding.INFLATERAW);}
// auto-detect header.function Unzip(opts) { if (!(this instanceof Unzip)) return new Unzip(opts); Zlib.call(this, opts, binding.UNZIP);}
function isValidFlushFlag(flag) { return flag === binding.Z_NO_FLUSH || flag === binding.Z_PARTIAL_FLUSH || flag === binding.Z_SYNC_FLUSH || flag === binding.Z_FULL_FLUSH || flag === binding.Z_FINISH || flag === binding.Z_BLOCK;}
// the Zlib class they all inherit from// This thing manages the queue of requests, and returns// true or false if there is anything in the queue when// you call the .write() method.
function Zlib(opts, mode) { var _this = this;
this._opts = opts = opts || {}; this._chunkSize = opts.chunkSize || zlibConstants.Z_DEFAULT_CHUNK;
Transform.call(this, opts);
if (opts.flush && !isValidFlushFlag(opts.flush)) { throw new Error("Invalid flush flag: " + opts.flush); } if (opts.finishFlush && !isValidFlushFlag(opts.finishFlush)) { throw new Error("Invalid flush flag: " + opts.finishFlush); }
this._flushFlag = opts.flush || binding.Z_NO_FLUSH; this._finishFlushFlag = typeof opts.finishFlush !== "undefined" ? opts.finishFlush : binding.Z_FINISH;
if (opts.chunkSize) { if ( opts.chunkSize < zlibConstants.Z_MIN_CHUNK || opts.chunkSize > zlibConstants.Z_MAX_CHUNK ) { throw new Error("Invalid chunk size: " + opts.chunkSize); } }
if (opts.windowBits) { if ( opts.windowBits < zlibConstants.Z_MIN_WINDOWBITS || opts.windowBits > zlibConstants.Z_MAX_WINDOWBITS ) { throw new Error("Invalid windowBits: " + opts.windowBits); } }
if (opts.level) { if ( opts.level < zlibConstants.Z_MIN_LEVEL || opts.level > zlibConstants.Z_MAX_LEVEL ) { throw new Error("Invalid compression level: " + opts.level); } }
if (opts.memLevel) { if ( opts.memLevel < zlibConstants.Z_MIN_MEMLEVEL || opts.memLevel > zlibConstants.Z_MAX_MEMLEVEL ) { throw new Error("Invalid memLevel: " + opts.memLevel); } }
if (opts.strategy) { if ( opts.strategy != zlibConstants.Z_FILTERED && opts.strategy != zlibConstants.Z_HUFFMAN_ONLY && opts.strategy != zlibConstants.Z_RLE && opts.strategy != zlibConstants.Z_FIXED && opts.strategy != zlibConstants.Z_DEFAULT_STRATEGY ) { throw new Error("Invalid strategy: " + opts.strategy); } }
if (opts.dictionary) { if (!Buffer.isBuffer(opts.dictionary)) { throw new Error("Invalid dictionary: it should be a Buffer instance"); } }
this._handle = new binding.Zlib(mode);
var self = this; this._hadError = false; this._handle.onerror = function (message, errno) { // there is no way to cleanly recover. // continuing only obscures problems. _close(self); self._hadError = true;
var error = new Error(message); error.errno = errno; error.code = codes[errno]; self.emit("error", error); };
var level = zlibConstants.Z_DEFAULT_COMPRESSION; if (typeof opts.level === "number") level = opts.level;
var strategy = zlibConstants.Z_DEFAULT_STRATEGY; if (typeof opts.strategy === "number") strategy = opts.strategy;
this._handle.init( opts.windowBits || zlibConstants.Z_DEFAULT_WINDOWBITS, level, opts.memLevel || zlibConstants.Z_DEFAULT_MEMLEVEL, strategy, opts.dictionary, );
this._buffer = Buffer.allocUnsafe(this._chunkSize); this._offset = 0; this._level = level; this._strategy = strategy;
this.once("end", this.close);
Object.defineProperty(this, "_closed", { get: function () { return !_this._handle; }, configurable: true, enumerable: true, });}
util.inherits(Zlib, Transform);
Zlib.prototype.params = function (level, strategy, callback) { if (level < zlibConstants.Z_MIN_LEVEL || level > zlibConstants.Z_MAX_LEVEL) { throw new RangeError("Invalid compression level: " + level); } if ( strategy != zlibConstants.Z_FILTERED && strategy != zlibConstants.Z_HUFFMAN_ONLY && strategy != zlibConstants.Z_RLE && strategy != zlibConstants.Z_FIXED && strategy != zlibConstants.Z_DEFAULT_STRATEGY ) { throw new TypeError("Invalid strategy: " + strategy); }
if (this._level !== level || this._strategy !== strategy) { var self = this; this.flush(binding.Z_SYNC_FLUSH, function () { assert(self._handle, "zlib binding closed"); self._handle.params(level, strategy); if (!self._hadError) { self._level = level; self._strategy = strategy; if (callback) callback(); } }); } else { process.nextTick(callback); }};
Zlib.prototype.reset = function () { assert(this._handle, "zlib binding closed"); return this._handle.reset();};
// This is the _flush function called by the transform class,// internally, when the last chunk has been written.Zlib.prototype._flush = function (callback) { this._transform(Buffer.alloc(0), "", callback);};
Zlib.prototype.flush = function (kind, callback) { var _this2 = this;
var ws = this._writableState;
if (typeof kind === "function" || kind === undefined && !callback) { callback = kind; kind = binding.Z_FULL_FLUSH; }
if (ws.ended) { if (callback) process.nextTick(callback); } else if (ws.ending) { if (callback) this.once("end", callback); } else if (ws.needDrain) { if (callback) { this.once("drain", function () { return _this2.flush(kind, callback); }); } } else { this._flushFlag = kind; this.write(Buffer.alloc(0), "", callback); }};
Zlib.prototype.close = function (callback) { _close(this, callback); process.nextTick(emitCloseNT, this);};
function _close(engine, callback) { if (callback) process.nextTick(callback);
// Caller may invoke .close after a zlib error (which will null _handle). if (!engine._handle) return;
engine._handle.close(); engine._handle = null;}
function emitCloseNT(self) { self.emit("close");}
Zlib.prototype._transform = function (chunk, encoding, cb) { var flushFlag; var ws = this._writableState; var ending = ws.ending || ws.ended; var last = ending && (!chunk || ws.length === chunk.length);
if (chunk !== null && !Buffer.isBuffer(chunk)) { return cb(new Error("invalid input")); }
if (!this._handle) return cb(new Error("zlib binding closed"));
// If it's the last chunk, or a final flush, we use the Z_FINISH flush flag // (or whatever flag was provided using opts.finishFlush). // If it's explicitly flushing at some other time, then we use // Z_FULL_FLUSH. Otherwise, use Z_NO_FLUSH for maximum compression // goodness. if (last) flushFlag = this._finishFlushFlag; else { flushFlag = this._flushFlag; // once we've flushed the last of the queue, stop flushing and // go back to the normal behavior. if (chunk.length >= ws.length) { this._flushFlag = this._opts.flush || binding.Z_NO_FLUSH; } }
this._processChunk(chunk, flushFlag, cb);};
Zlib.prototype._processChunk = function (chunk, flushFlag, cb) { var availInBefore = chunk && chunk.length; var availOutBefore = this._chunkSize - this._offset; var inOff = 0;
var self = this;
var async = typeof cb === "function";
if (!async) { var buffers = []; var nread = 0;
var error; this.on("error", function (er) { error = er; });
assert(this._handle, "zlib binding closed"); do { var res = this._handle.writeSync( flushFlag, chunk, // in inOff, // in_off availInBefore, // in_len this._buffer, // out this._offset, //out_off availOutBefore, ); // out_len } while (!this._hadError && callback(res[0], res[1]));
if (this._hadError) { throw error; }
if (nread >= kMaxLength) { _close(this); throw new RangeError(kRangeErrorMessage); }
var buf = Buffer.concat(buffers, nread); _close(this);
return buf; }
assert(this._handle, "zlib binding closed"); var req = this._handle.write( flushFlag, chunk, // in inOff, // in_off availInBefore, // in_len this._buffer, // out this._offset, //out_off availOutBefore, ); // out_len
req.buffer = chunk; req.callback = callback;
function callback(availInAfter, availOutAfter) { // When the callback is used in an async write, the callback's // context is the `req` object that was created. The req object // is === this._handle, and that's why it's important to null // out the values after they are done being used. `this._handle` // can stay in memory longer than the callback and buffer are needed. if (this) { this.buffer = null; this.callback = null; }
if (self._hadError) return;
var have = availOutBefore - availOutAfter; assert(have >= 0, "have should not go down");
if (have > 0) { var out = self._buffer.slice(self._offset, self._offset + have); self._offset += have; // serve some output to the consumer. if (async) { self.push(out); } else { buffers.push(out); nread += out.length; } }
// exhausted the output buffer, or used all the input create a new one. if (availOutAfter === 0 || self._offset >= self._chunkSize) { availOutBefore = self._chunkSize; self._offset = 0; self._buffer = Buffer.allocUnsafe(self._chunkSize); }
if (availOutAfter === 0) { // Not actually done. Need to reprocess. // Also, update the availInBefore to the availInAfter value, // so that if we have to hit it a third (fourth, etc.) time, // it'll have the correct byte counts. inOff += availInBefore - availInAfter; availInBefore = availInAfter;
if (!async) return true;
var newReq = self._handle.write( flushFlag, chunk, inOff, availInBefore, self._buffer, self._offset, self._chunkSize, ); newReq.callback = callback; // this same function newReq.buffer = chunk; return; }
if (!async) return false;
// finished with the chunk. cb(); }};
util.inherits(Deflate, Zlib);util.inherits(Inflate, Zlib);util.inherits(Gzip, Zlib);util.inherits(Gunzip, Zlib);util.inherits(DeflateRaw, Zlib);util.inherits(InflateRaw, Zlib);util.inherits(Unzip, Zlib);
export { Deflate, DeflateRaw, Gunzip, Gzip, Inflate, InflateRaw, Unzip };