1// Copyright Joyent, Inc. and other Node contributors. 2// 3// Permission is hereby granted, free of charge, to any person obtaining a 4// copy of this software and associated documentation files (the 5// "Software"), to deal in the Software without restriction, including 6// without limitation the rights to use, copy, modify, merge, publish, 7// distribute, sublicense, and/or sell copies of the Software, and to permit 8// persons to whom the Software is furnished to do so, subject to the 9// following conditions: 10// 11// The above copyright notice and this permission notice shall be included 12// in all copies or substantial portions of the Software. 13// 14// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS 15// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 16// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN 17// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, 18// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR 19// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE 20// USE OR OTHER DEALINGS IN THE SOFTWARE. 21 22'use strict'; 23 24const { 25 ArrayBuffer, 26 ArrayPrototypeForEach, 27 ArrayPrototypeMap, 28 ArrayPrototypePush, 29 Error, 30 FunctionPrototypeBind, 31 MathMaxApply, 32 NumberIsFinite, 33 NumberIsNaN, 34 ObjectDefineProperties, 35 ObjectDefineProperty, 36 ObjectFreeze, 37 ObjectKeys, 38 ObjectSetPrototypeOf, 39 ReflectApply, 40 StringPrototypeStartsWith, 41 Symbol, 42 TypedArrayPrototypeFill, 43 Uint32Array, 44} = primordials; 45 46const { 47 codes: { 48 ERR_BROTLI_INVALID_PARAM, 49 ERR_BUFFER_TOO_LARGE, 50 ERR_INVALID_ARG_TYPE, 51 ERR_OUT_OF_RANGE, 52 ERR_ZLIB_INITIALIZATION_FAILED, 53 }, 54 hideStackFrames 55} = require('internal/errors'); 56const { Transform, finished } = require('stream'); 57const { 58 deprecate 59} = require('internal/util'); 60const { 61 isArrayBufferView, 62 isAnyArrayBuffer, 63 isUint8Array, 64} = require('internal/util/types'); 65const binding = internalBinding('zlib'); 66const assert = require('internal/assert'); 67const { 68 Buffer, 69 kMaxLength 70} = require('buffer'); 71const { owner_symbol } = require('internal/async_hooks').symbols; 72 73const kFlushFlag = Symbol('kFlushFlag'); 74const kError = Symbol('kError'); 75 76const constants = internalBinding('constants').zlib; 77const { 78 // Zlib flush levels 79 Z_NO_FLUSH, Z_BLOCK, Z_PARTIAL_FLUSH, Z_SYNC_FLUSH, Z_FULL_FLUSH, Z_FINISH, 80 // Zlib option values 81 Z_MIN_CHUNK, Z_MIN_WINDOWBITS, Z_MAX_WINDOWBITS, Z_MIN_LEVEL, Z_MAX_LEVEL, 82 Z_MIN_MEMLEVEL, Z_MAX_MEMLEVEL, Z_DEFAULT_CHUNK, Z_DEFAULT_COMPRESSION, 83 Z_DEFAULT_STRATEGY, Z_DEFAULT_WINDOWBITS, Z_DEFAULT_MEMLEVEL, Z_FIXED, 84 // Node's compression stream modes (node_zlib_mode) 85 DEFLATE, DEFLATERAW, INFLATE, INFLATERAW, GZIP, GUNZIP, UNZIP, 86 BROTLI_DECODE, BROTLI_ENCODE, 87 // Brotli operations (~flush levels) 88 BROTLI_OPERATION_PROCESS, BROTLI_OPERATION_FLUSH, 89 BROTLI_OPERATION_FINISH, BROTLI_OPERATION_EMIT_METADATA, 90} = constants; 91 92// Translation table for return codes. 93const codes = { 94 Z_OK: constants.Z_OK, 95 Z_STREAM_END: constants.Z_STREAM_END, 96 Z_NEED_DICT: constants.Z_NEED_DICT, 97 Z_ERRNO: constants.Z_ERRNO, 98 Z_STREAM_ERROR: constants.Z_STREAM_ERROR, 99 Z_DATA_ERROR: constants.Z_DATA_ERROR, 100 Z_MEM_ERROR: constants.Z_MEM_ERROR, 101 Z_BUF_ERROR: constants.Z_BUF_ERROR, 102 Z_VERSION_ERROR: constants.Z_VERSION_ERROR 103}; 104 105for (const ckey of ObjectKeys(codes)) { 106 codes[codes[ckey]] = ckey; 107} 108 109function zlibBuffer(engine, buffer, callback) { 110 if (typeof callback !== 'function') 111 throw new ERR_INVALID_ARG_TYPE('callback', 'function', callback); 112 // Streams do not support non-Uint8Array ArrayBufferViews yet. Convert it to a 113 // Buffer without copying. 114 if (isArrayBufferView(buffer) && !isUint8Array(buffer)) { 115 buffer = Buffer.from(buffer.buffer, buffer.byteOffset, buffer.byteLength); 116 } else if (isAnyArrayBuffer(buffer)) { 117 buffer = Buffer.from(buffer); 118 } 119 engine.buffers = null; 120 engine.nread = 0; 121 engine.cb = callback; 122 engine.on('data', zlibBufferOnData); 123 engine.on('error', zlibBufferOnError); 124 engine.on('end', zlibBufferOnEnd); 125 engine.end(buffer); 126} 127 128function zlibBufferOnData(chunk) { 129 if (!this.buffers) 130 this.buffers = [chunk]; 131 else 132 ArrayPrototypePush(this.buffers, chunk); 133 this.nread += chunk.length; 134 if (this.nread > this._maxOutputLength) { 135 this.close(); 136 this.removeAllListeners('end'); 137 this.cb(new ERR_BUFFER_TOO_LARGE(this._maxOutputLength)); 138 } 139} 140 141function zlibBufferOnError(err) { 142 this.removeAllListeners('end'); 143 this.cb(err); 144} 145 146function zlibBufferOnEnd() { 147 let buf; 148 if (this.nread === 0) { 149 buf = Buffer.alloc(0); 150 } else { 151 const bufs = this.buffers; 152 buf = (bufs.length === 1 ? bufs[0] : Buffer.concat(bufs, this.nread)); 153 } 154 this.close(); 155 if (this._info) 156 this.cb(null, { buffer: buf, engine: this }); 157 else 158 this.cb(null, buf); 159} 160 161function zlibBufferSync(engine, buffer) { 162 if (typeof buffer === 'string') { 163 buffer = Buffer.from(buffer); 164 } else if (!isArrayBufferView(buffer)) { 165 if (isAnyArrayBuffer(buffer)) { 166 buffer = Buffer.from(buffer); 167 } else { 168 throw new ERR_INVALID_ARG_TYPE( 169 'buffer', 170 ['string', 'Buffer', 'TypedArray', 'DataView', 'ArrayBuffer'], 171 buffer 172 ); 173 } 174 } 175 buffer = processChunkSync(engine, buffer, engine._finishFlushFlag); 176 if (engine._info) 177 return { buffer, engine }; 178 return buffer; 179} 180 181function zlibOnError(message, errno, code) { 182 const self = this[owner_symbol]; 183 // There is no way to cleanly recover. 184 // Continuing only obscures problems. 185 186 // eslint-disable-next-line no-restricted-syntax 187 const error = new Error(message); 188 error.errno = errno; 189 error.code = code; 190 self.destroy(error); 191 self[kError] = error; 192} 193 194// 1. Returns false for undefined and NaN 195// 2. Returns true for finite numbers 196// 3. Throws ERR_INVALID_ARG_TYPE for non-numbers 197// 4. Throws ERR_OUT_OF_RANGE for infinite numbers 198const checkFiniteNumber = hideStackFrames((number, name) => { 199 // Common case 200 if (number === undefined) { 201 return false; 202 } 203 204 if (NumberIsFinite(number)) { 205 return true; // Is a valid number 206 } 207 208 if (NumberIsNaN(number)) { 209 return false; 210 } 211 212 // Other non-numbers 213 if (typeof number !== 'number') { 214 throw new ERR_INVALID_ARG_TYPE(name, 'number', number); 215 } 216 217 // Infinite numbers 218 throw new ERR_OUT_OF_RANGE(name, 'a finite number', number); 219}); 220 221// 1. Returns def for number when it's undefined or NaN 222// 2. Returns number for finite numbers >= lower and <= upper 223// 3. Throws ERR_INVALID_ARG_TYPE for non-numbers 224// 4. Throws ERR_OUT_OF_RANGE for infinite numbers or numbers > upper or < lower 225const checkRangesOrGetDefault = hideStackFrames( 226 (number, name, lower, upper, def) => { 227 if (!checkFiniteNumber(number, name)) { 228 return def; 229 } 230 if (number < lower || number > upper) { 231 throw new ERR_OUT_OF_RANGE(name, 232 `>= ${lower} and <= ${upper}`, number); 233 } 234 return number; 235 } 236); 237 238const FLUSH_BOUND = [ 239 [ Z_NO_FLUSH, Z_BLOCK ], 240 [ BROTLI_OPERATION_PROCESS, BROTLI_OPERATION_EMIT_METADATA ], 241]; 242const FLUSH_BOUND_IDX_NORMAL = 0; 243const FLUSH_BOUND_IDX_BROTLI = 1; 244 245// The base class for all Zlib-style streams. 246function ZlibBase(opts, mode, handle, { flush, finishFlush, fullFlush }) { 247 let chunkSize = Z_DEFAULT_CHUNK; 248 let maxOutputLength = kMaxLength; 249 // The ZlibBase class is not exported to user land, the mode should only be 250 // passed in by us. 251 assert(typeof mode === 'number'); 252 assert(mode >= DEFLATE && mode <= BROTLI_ENCODE); 253 254 let flushBoundIdx; 255 if (mode !== BROTLI_ENCODE && mode !== BROTLI_DECODE) { 256 flushBoundIdx = FLUSH_BOUND_IDX_NORMAL; 257 } else { 258 flushBoundIdx = FLUSH_BOUND_IDX_BROTLI; 259 } 260 261 if (opts) { 262 chunkSize = opts.chunkSize; 263 if (!checkFiniteNumber(chunkSize, 'options.chunkSize')) { 264 chunkSize = Z_DEFAULT_CHUNK; 265 } else if (chunkSize < Z_MIN_CHUNK) { 266 throw new ERR_OUT_OF_RANGE('options.chunkSize', 267 `>= ${Z_MIN_CHUNK}`, chunkSize); 268 } 269 270 flush = checkRangesOrGetDefault( 271 opts.flush, 'options.flush', 272 FLUSH_BOUND[flushBoundIdx][0], FLUSH_BOUND[flushBoundIdx][1], flush); 273 274 finishFlush = checkRangesOrGetDefault( 275 opts.finishFlush, 'options.finishFlush', 276 FLUSH_BOUND[flushBoundIdx][0], FLUSH_BOUND[flushBoundIdx][1], 277 finishFlush); 278 279 maxOutputLength = checkRangesOrGetDefault( 280 opts.maxOutputLength, 'options.maxOutputLength', 281 1, kMaxLength, kMaxLength); 282 283 if (opts.encoding || opts.objectMode || opts.writableObjectMode) { 284 opts = { ...opts }; 285 opts.encoding = null; 286 opts.objectMode = false; 287 opts.writableObjectMode = false; 288 } 289 } 290 291 ReflectApply(Transform, this, [{ autoDestroy: true, ...opts }]); 292 this[kError] = null; 293 this.bytesWritten = 0; 294 this._handle = handle; 295 handle[owner_symbol] = this; 296 // Used by processCallback() and zlibOnError() 297 handle.onerror = zlibOnError; 298 this._outBuffer = Buffer.allocUnsafe(chunkSize); 299 this._outOffset = 0; 300 301 this._chunkSize = chunkSize; 302 this._defaultFlushFlag = flush; 303 this._finishFlushFlag = finishFlush; 304 this._defaultFullFlushFlag = fullFlush; 305 this._info = opts && opts.info; 306 this._maxOutputLength = maxOutputLength; 307} 308ObjectSetPrototypeOf(ZlibBase.prototype, Transform.prototype); 309ObjectSetPrototypeOf(ZlibBase, Transform); 310 311ObjectDefineProperty(ZlibBase.prototype, '_closed', { 312 configurable: true, 313 enumerable: true, 314 get() { 315 return !this._handle; 316 } 317}); 318 319// `bytesRead` made sense as a name when looking from the zlib engine's 320// perspective, but it is inconsistent with all other streams exposed by Node.js 321// that have this concept, where it stands for the number of bytes read 322// *from* the stream (that is, net.Socket/tls.Socket & file system streams). 323ObjectDefineProperty(ZlibBase.prototype, 'bytesRead', { 324 configurable: true, 325 enumerable: true, 326 get: deprecate(function() { 327 return this.bytesWritten; 328 }, 'zlib.bytesRead is deprecated and will change its meaning in the ' + 329 'future. Use zlib.bytesWritten instead.', 'DEP0108'), 330 set: deprecate(function(value) { 331 this.bytesWritten = value; 332 }, 'Setting zlib.bytesRead is deprecated. ' + 333 'This feature will be removed in the future.', 'DEP0108') 334}); 335 336ZlibBase.prototype.reset = function() { 337 if (!this._handle) 338 assert(false, 'zlib binding closed'); 339 return this._handle.reset(); 340}; 341 342// This is the _flush function called by the transform class, 343// internally, when the last chunk has been written. 344ZlibBase.prototype._flush = function(callback) { 345 this._transform(Buffer.alloc(0), '', callback); 346}; 347 348// If a flush is scheduled while another flush is still pending, a way to figure 349// out which one is the "stronger" flush is needed. 350// This is currently only used to figure out which flush flag to use for the 351// last chunk. 352// Roughly, the following holds: 353// Z_NO_FLUSH (< Z_TREES) < Z_BLOCK < Z_PARTIAL_FLUSH < 354// Z_SYNC_FLUSH < Z_FULL_FLUSH < Z_FINISH 355const flushiness = []; 356let i = 0; 357const kFlushFlagList = [Z_NO_FLUSH, Z_BLOCK, Z_PARTIAL_FLUSH, 358 Z_SYNC_FLUSH, Z_FULL_FLUSH, Z_FINISH]; 359for (const flushFlag of kFlushFlagList) { 360 flushiness[flushFlag] = i++; 361} 362 363function maxFlush(a, b) { 364 return flushiness[a] > flushiness[b] ? a : b; 365} 366 367// Set up a list of 'special' buffers that can be written using .write() 368// from the .flush() code as a way of introducing flushing operations into the 369// write sequence. 370const kFlushBuffers = []; 371{ 372 const dummyArrayBuffer = new ArrayBuffer(); 373 for (const flushFlag of kFlushFlagList) { 374 kFlushBuffers[flushFlag] = Buffer.from(dummyArrayBuffer); 375 kFlushBuffers[flushFlag][kFlushFlag] = flushFlag; 376 } 377} 378 379ZlibBase.prototype.flush = function(kind, callback) { 380 if (typeof kind === 'function' || (kind === undefined && !callback)) { 381 callback = kind; 382 kind = this._defaultFullFlushFlag; 383 } 384 385 if (this.writableFinished) { 386 if (callback) 387 process.nextTick(callback); 388 } else if (this.writableEnded) { 389 if (callback) 390 this.once('end', callback); 391 } else { 392 this.write(kFlushBuffers[kind], '', callback); 393 } 394}; 395 396ZlibBase.prototype.close = function(callback) { 397 if (callback) finished(this, callback); 398 this.destroy(); 399}; 400 401ZlibBase.prototype._destroy = function(err, callback) { 402 _close(this); 403 callback(err); 404}; 405 406ZlibBase.prototype._transform = function(chunk, encoding, cb) { 407 let flushFlag = this._defaultFlushFlag; 408 // We use a 'fake' zero-length chunk to carry information about flushes from 409 // the public API to the actual stream implementation. 410 if (typeof chunk[kFlushFlag] === 'number') { 411 flushFlag = chunk[kFlushFlag]; 412 } 413 414 // For the last chunk, also apply `_finishFlushFlag`. 415 if (this.writableEnded && this.writableLength === chunk.byteLength) { 416 flushFlag = maxFlush(flushFlag, this._finishFlushFlag); 417 } 418 processChunk(this, chunk, flushFlag, cb); 419}; 420 421ZlibBase.prototype._processChunk = function(chunk, flushFlag, cb) { 422 // _processChunk() is left for backwards compatibility 423 if (typeof cb === 'function') 424 processChunk(this, chunk, flushFlag, cb); 425 else 426 return processChunkSync(this, chunk, flushFlag); 427}; 428 429function processChunkSync(self, chunk, flushFlag) { 430 let availInBefore = chunk.byteLength; 431 let availOutBefore = self._chunkSize - self._outOffset; 432 let inOff = 0; 433 let availOutAfter; 434 let availInAfter; 435 436 let buffers = null; 437 let nread = 0; 438 let inputRead = 0; 439 const state = self._writeState; 440 const handle = self._handle; 441 let buffer = self._outBuffer; 442 let offset = self._outOffset; 443 const chunkSize = self._chunkSize; 444 445 let error; 446 self.on('error', function onError(er) { 447 error = er; 448 }); 449 450 while (true) { 451 handle.writeSync(flushFlag, 452 chunk, // in 453 inOff, // in_off 454 availInBefore, // in_len 455 buffer, // out 456 offset, // out_off 457 availOutBefore); // out_len 458 if (error) 459 throw error; 460 else if (self[kError]) 461 throw self[kError]; 462 463 availOutAfter = state[0]; 464 availInAfter = state[1]; 465 466 const inDelta = (availInBefore - availInAfter); 467 inputRead += inDelta; 468 469 const have = availOutBefore - availOutAfter; 470 if (have > 0) { 471 const out = buffer.slice(offset, offset + have); 472 offset += have; 473 if (!buffers) 474 buffers = [out]; 475 else 476 ArrayPrototypePush(buffers, out); 477 nread += out.byteLength; 478 479 if (nread > self._maxOutputLength) { 480 _close(self); 481 throw new ERR_BUFFER_TOO_LARGE(self._maxOutputLength); 482 } 483 484 } else { 485 assert(have === 0, 'have should not go down'); 486 } 487 488 // Exhausted the output buffer, or used all the input create a new one. 489 if (availOutAfter === 0 || offset >= chunkSize) { 490 availOutBefore = chunkSize; 491 offset = 0; 492 buffer = Buffer.allocUnsafe(chunkSize); 493 } 494 495 if (availOutAfter === 0) { 496 // Not actually done. Need to reprocess. 497 // Also, update the availInBefore to the availInAfter value, 498 // so that if we have to hit it a third (fourth, etc.) time, 499 // it'll have the correct byte counts. 500 inOff += inDelta; 501 availInBefore = availInAfter; 502 } else { 503 break; 504 } 505 } 506 507 self.bytesWritten = inputRead; 508 _close(self); 509 510 if (nread === 0) 511 return Buffer.alloc(0); 512 513 return (buffers.length === 1 ? buffers[0] : Buffer.concat(buffers, nread)); 514} 515 516function processChunk(self, chunk, flushFlag, cb) { 517 const handle = self._handle; 518 if (!handle) return process.nextTick(cb); 519 520 handle.buffer = chunk; 521 handle.cb = cb; 522 handle.availOutBefore = self._chunkSize - self._outOffset; 523 handle.availInBefore = chunk.byteLength; 524 handle.inOff = 0; 525 handle.flushFlag = flushFlag; 526 527 handle.write(flushFlag, 528 chunk, // in 529 0, // in_off 530 handle.availInBefore, // in_len 531 self._outBuffer, // out 532 self._outOffset, // out_off 533 handle.availOutBefore); // out_len 534} 535 536function processCallback() { 537 // This callback's context (`this`) is the `_handle` (ZCtx) object. It is 538 // important to null out the values once they are no longer needed since 539 // `_handle` can stay in memory long after the buffer is needed. 540 const handle = this; 541 const self = this[owner_symbol]; 542 const state = self._writeState; 543 544 if (self.destroyed) { 545 this.buffer = null; 546 this.cb(); 547 return; 548 } 549 550 const availOutAfter = state[0]; 551 const availInAfter = state[1]; 552 553 const inDelta = handle.availInBefore - availInAfter; 554 self.bytesWritten += inDelta; 555 556 const have = handle.availOutBefore - availOutAfter; 557 if (have > 0) { 558 const out = self._outBuffer.slice(self._outOffset, self._outOffset + have); 559 self._outOffset += have; 560 self.push(out); 561 } else { 562 assert(have === 0, 'have should not go down'); 563 } 564 565 if (self.destroyed) { 566 this.cb(); 567 return; 568 } 569 570 // Exhausted the output buffer, or used all the input create a new one. 571 if (availOutAfter === 0 || self._outOffset >= self._chunkSize) { 572 handle.availOutBefore = self._chunkSize; 573 self._outOffset = 0; 574 self._outBuffer = Buffer.allocUnsafe(self._chunkSize); 575 } 576 577 if (availOutAfter === 0) { 578 // Not actually done. Need to reprocess. 579 // Also, update the availInBefore to the availInAfter value, 580 // so that if we have to hit it a third (fourth, etc.) time, 581 // it'll have the correct byte counts. 582 handle.inOff += inDelta; 583 handle.availInBefore = availInAfter; 584 585 this.write(handle.flushFlag, 586 this.buffer, // in 587 handle.inOff, // in_off 588 handle.availInBefore, // in_len 589 self._outBuffer, // out 590 self._outOffset, // out_off 591 self._chunkSize); // out_len 592 return; 593 } 594 595 if (availInAfter > 0) { 596 // If we have more input that should be written, but we also have output 597 // space available, that means that the compression library was not 598 // interested in receiving more data, and in particular that the input 599 // stream has ended early. 600 // This applies to streams where we don't check data past the end of 601 // what was consumed; that is, everything except Gunzip/Unzip. 602 self.push(null); 603 } 604 605 // Finished with the chunk. 606 this.buffer = null; 607 this.cb(); 608} 609 610function _close(engine) { 611 // Caller may invoke .close after a zlib error (which will null _handle). 612 if (!engine._handle) 613 return; 614 615 engine._handle.close(); 616 engine._handle = null; 617} 618 619const zlibDefaultOpts = { 620 flush: Z_NO_FLUSH, 621 finishFlush: Z_FINISH, 622 fullFlush: Z_FULL_FLUSH 623}; 624// Base class for all streams actually backed by zlib and using zlib-specific 625// parameters. 626function Zlib(opts, mode) { 627 let windowBits = Z_DEFAULT_WINDOWBITS; 628 let level = Z_DEFAULT_COMPRESSION; 629 let memLevel = Z_DEFAULT_MEMLEVEL; 630 let strategy = Z_DEFAULT_STRATEGY; 631 let dictionary; 632 633 if (opts) { 634 // windowBits is special. On the compression side, 0 is an invalid value. 635 // But on the decompression side, a value of 0 for windowBits tells zlib 636 // to use the window size in the zlib header of the compressed stream. 637 if ((opts.windowBits == null || opts.windowBits === 0) && 638 (mode === INFLATE || 639 mode === GUNZIP || 640 mode === UNZIP)) { 641 windowBits = 0; 642 } else { 643 // `{ windowBits: 8 }` is valid for deflate but not gzip. 644 const min = Z_MIN_WINDOWBITS + (mode === GZIP ? 1 : 0); 645 windowBits = checkRangesOrGetDefault( 646 opts.windowBits, 'options.windowBits', 647 min, Z_MAX_WINDOWBITS, Z_DEFAULT_WINDOWBITS); 648 } 649 650 level = checkRangesOrGetDefault( 651 opts.level, 'options.level', 652 Z_MIN_LEVEL, Z_MAX_LEVEL, Z_DEFAULT_COMPRESSION); 653 654 memLevel = checkRangesOrGetDefault( 655 opts.memLevel, 'options.memLevel', 656 Z_MIN_MEMLEVEL, Z_MAX_MEMLEVEL, Z_DEFAULT_MEMLEVEL); 657 658 strategy = checkRangesOrGetDefault( 659 opts.strategy, 'options.strategy', 660 Z_DEFAULT_STRATEGY, Z_FIXED, Z_DEFAULT_STRATEGY); 661 662 dictionary = opts.dictionary; 663 if (dictionary !== undefined && !isArrayBufferView(dictionary)) { 664 if (isAnyArrayBuffer(dictionary)) { 665 dictionary = Buffer.from(dictionary); 666 } else { 667 throw new ERR_INVALID_ARG_TYPE( 668 'options.dictionary', 669 ['Buffer', 'TypedArray', 'DataView', 'ArrayBuffer'], 670 dictionary 671 ); 672 } 673 } 674 } 675 676 const handle = new binding.Zlib(mode); 677 // Ideally, we could let ZlibBase() set up _writeState. I haven't been able 678 // to come up with a good solution that doesn't break our internal API, 679 // and with it all supported npm versions at the time of writing. 680 this._writeState = new Uint32Array(2); 681 handle.init(windowBits, 682 level, 683 memLevel, 684 strategy, 685 this._writeState, 686 processCallback, 687 dictionary); 688 689 ReflectApply(ZlibBase, this, [opts, mode, handle, zlibDefaultOpts]); 690 691 this._level = level; 692 this._strategy = strategy; 693} 694ObjectSetPrototypeOf(Zlib.prototype, ZlibBase.prototype); 695ObjectSetPrototypeOf(Zlib, ZlibBase); 696 697// This callback is used by `.params()` to wait until a full flush happened 698// before adjusting the parameters. In particular, the call to the native 699// `params()` function should not happen while a write is currently in progress 700// on the threadpool. 701function paramsAfterFlushCallback(level, strategy, callback) { 702 assert(this._handle, 'zlib binding closed'); 703 this._handle.params(level, strategy); 704 if (!this.destroyed) { 705 this._level = level; 706 this._strategy = strategy; 707 if (callback) callback(); 708 } 709} 710 711Zlib.prototype.params = function params(level, strategy, callback) { 712 checkRangesOrGetDefault(level, 'level', Z_MIN_LEVEL, Z_MAX_LEVEL); 713 checkRangesOrGetDefault(strategy, 'strategy', Z_DEFAULT_STRATEGY, Z_FIXED); 714 715 if (this._level !== level || this._strategy !== strategy) { 716 this.flush(Z_SYNC_FLUSH, 717 FunctionPrototypeBind(paramsAfterFlushCallback, this, 718 level, strategy, callback)); 719 } else { 720 process.nextTick(callback); 721 } 722}; 723 724// generic zlib 725// minimal 2-byte header 726function Deflate(opts) { 727 if (!(this instanceof Deflate)) 728 return new Deflate(opts); 729 ReflectApply(Zlib, this, [opts, DEFLATE]); 730} 731ObjectSetPrototypeOf(Deflate.prototype, Zlib.prototype); 732ObjectSetPrototypeOf(Deflate, Zlib); 733 734function Inflate(opts) { 735 if (!(this instanceof Inflate)) 736 return new Inflate(opts); 737 ReflectApply(Zlib, this, [opts, INFLATE]); 738} 739ObjectSetPrototypeOf(Inflate.prototype, Zlib.prototype); 740ObjectSetPrototypeOf(Inflate, Zlib); 741 742function Gzip(opts) { 743 if (!(this instanceof Gzip)) 744 return new Gzip(opts); 745 ReflectApply(Zlib, this, [opts, GZIP]); 746} 747ObjectSetPrototypeOf(Gzip.prototype, Zlib.prototype); 748ObjectSetPrototypeOf(Gzip, Zlib); 749 750function Gunzip(opts) { 751 if (!(this instanceof Gunzip)) 752 return new Gunzip(opts); 753 ReflectApply(Zlib, this, [opts, GUNZIP]); 754} 755ObjectSetPrototypeOf(Gunzip.prototype, Zlib.prototype); 756ObjectSetPrototypeOf(Gunzip, Zlib); 757 758function DeflateRaw(opts) { 759 if (opts && opts.windowBits === 8) opts.windowBits = 9; 760 if (!(this instanceof DeflateRaw)) 761 return new DeflateRaw(opts); 762 ReflectApply(Zlib, this, [opts, DEFLATERAW]); 763} 764ObjectSetPrototypeOf(DeflateRaw.prototype, Zlib.prototype); 765ObjectSetPrototypeOf(DeflateRaw, Zlib); 766 767function InflateRaw(opts) { 768 if (!(this instanceof InflateRaw)) 769 return new InflateRaw(opts); 770 ReflectApply(Zlib, this, [opts, INFLATERAW]); 771} 772ObjectSetPrototypeOf(InflateRaw.prototype, Zlib.prototype); 773ObjectSetPrototypeOf(InflateRaw, Zlib); 774 775function Unzip(opts) { 776 if (!(this instanceof Unzip)) 777 return new Unzip(opts); 778 ReflectApply(Zlib, this, [opts, UNZIP]); 779} 780ObjectSetPrototypeOf(Unzip.prototype, Zlib.prototype); 781ObjectSetPrototypeOf(Unzip, Zlib); 782 783function createConvenienceMethod(ctor, sync) { 784 if (sync) { 785 return function syncBufferWrapper(buffer, opts) { 786 return zlibBufferSync(new ctor(opts), buffer); 787 }; 788 } 789 return function asyncBufferWrapper(buffer, opts, callback) { 790 if (typeof opts === 'function') { 791 callback = opts; 792 opts = {}; 793 } 794 return zlibBuffer(new ctor(opts), buffer, callback); 795 }; 796} 797 798const kMaxBrotliParam = MathMaxApply(ArrayPrototypeMap( 799 ObjectKeys(constants), 800 (key) => (StringPrototypeStartsWith(key, 'BROTLI_PARAM_') ? 801 constants[key] : 802 0) 803)); 804 805const brotliInitParamsArray = new Uint32Array(kMaxBrotliParam + 1); 806 807const brotliDefaultOpts = { 808 flush: BROTLI_OPERATION_PROCESS, 809 finishFlush: BROTLI_OPERATION_FINISH, 810 fullFlush: BROTLI_OPERATION_FLUSH 811}; 812function Brotli(opts, mode) { 813 assert(mode === BROTLI_DECODE || mode === BROTLI_ENCODE); 814 815 TypedArrayPrototypeFill(brotliInitParamsArray, -1); 816 if (opts?.params) { 817 ArrayPrototypeForEach(ObjectKeys(opts.params), (origKey) => { 818 const key = +origKey; 819 if (NumberIsNaN(key) || key < 0 || key > kMaxBrotliParam || 820 (brotliInitParamsArray[key] | 0) !== -1) { 821 throw new ERR_BROTLI_INVALID_PARAM(origKey); 822 } 823 824 const value = opts.params[origKey]; 825 if (typeof value !== 'number' && typeof value !== 'boolean') { 826 throw new ERR_INVALID_ARG_TYPE('options.params[key]', 827 'number', opts.params[origKey]); 828 } 829 brotliInitParamsArray[key] = value; 830 }); 831 } 832 833 const handle = mode === BROTLI_DECODE ? 834 new binding.BrotliDecoder(mode) : new binding.BrotliEncoder(mode); 835 836 this._writeState = new Uint32Array(2); 837 // TODO(addaleax): Sometimes we generate better error codes in C++ land, 838 // e.g. ERR_BROTLI_PARAM_SET_FAILED -- it's hard to access them with 839 // the current bindings setup, though. 840 if (!handle.init(brotliInitParamsArray, 841 this._writeState, 842 processCallback)) { 843 throw new ERR_ZLIB_INITIALIZATION_FAILED(); 844 } 845 846 ReflectApply(ZlibBase, this, [opts, mode, handle, brotliDefaultOpts]); 847} 848ObjectSetPrototypeOf(Brotli.prototype, Zlib.prototype); 849ObjectSetPrototypeOf(Brotli, Zlib); 850 851function BrotliCompress(opts) { 852 if (!(this instanceof BrotliCompress)) 853 return new BrotliCompress(opts); 854 ReflectApply(Brotli, this, [opts, BROTLI_ENCODE]); 855} 856ObjectSetPrototypeOf(BrotliCompress.prototype, Brotli.prototype); 857ObjectSetPrototypeOf(BrotliCompress, Brotli); 858 859function BrotliDecompress(opts) { 860 if (!(this instanceof BrotliDecompress)) 861 return new BrotliDecompress(opts); 862 ReflectApply(Brotli, this, [opts, BROTLI_DECODE]); 863} 864ObjectSetPrototypeOf(BrotliDecompress.prototype, Brotli.prototype); 865ObjectSetPrototypeOf(BrotliDecompress, Brotli); 866 867 868function createProperty(ctor) { 869 return { 870 configurable: true, 871 enumerable: true, 872 value: function(options) { 873 return new ctor(options); 874 } 875 }; 876} 877 878// Legacy alias on the C++ wrapper object. This is not public API, so we may 879// want to runtime-deprecate it at some point. There's no hurry, though. 880ObjectDefineProperty(binding.Zlib.prototype, 'jsref', { 881 get() { return this[owner_symbol]; }, 882 set(v) { return this[owner_symbol] = v; } 883}); 884 885module.exports = { 886 Deflate, 887 Inflate, 888 Gzip, 889 Gunzip, 890 DeflateRaw, 891 InflateRaw, 892 Unzip, 893 BrotliCompress, 894 BrotliDecompress, 895 896 // Convenience methods. 897 // compress/decompress a string or buffer in one step. 898 deflate: createConvenienceMethod(Deflate, false), 899 deflateSync: createConvenienceMethod(Deflate, true), 900 gzip: createConvenienceMethod(Gzip, false), 901 gzipSync: createConvenienceMethod(Gzip, true), 902 deflateRaw: createConvenienceMethod(DeflateRaw, false), 903 deflateRawSync: createConvenienceMethod(DeflateRaw, true), 904 unzip: createConvenienceMethod(Unzip, false), 905 unzipSync: createConvenienceMethod(Unzip, true), 906 inflate: createConvenienceMethod(Inflate, false), 907 inflateSync: createConvenienceMethod(Inflate, true), 908 gunzip: createConvenienceMethod(Gunzip, false), 909 gunzipSync: createConvenienceMethod(Gunzip, true), 910 inflateRaw: createConvenienceMethod(InflateRaw, false), 911 inflateRawSync: createConvenienceMethod(InflateRaw, true), 912 brotliCompress: createConvenienceMethod(BrotliCompress, false), 913 brotliCompressSync: createConvenienceMethod(BrotliCompress, true), 914 brotliDecompress: createConvenienceMethod(BrotliDecompress, false), 915 brotliDecompressSync: createConvenienceMethod(BrotliDecompress, true), 916}; 917 918ObjectDefineProperties(module.exports, { 919 createDeflate: createProperty(Deflate), 920 createInflate: createProperty(Inflate), 921 createDeflateRaw: createProperty(DeflateRaw), 922 createInflateRaw: createProperty(InflateRaw), 923 createGzip: createProperty(Gzip), 924 createGunzip: createProperty(Gunzip), 925 createUnzip: createProperty(Unzip), 926 createBrotliCompress: createProperty(BrotliCompress), 927 createBrotliDecompress: createProperty(BrotliDecompress), 928 constants: { 929 configurable: false, 930 enumerable: true, 931 value: constants 932 }, 933 codes: { 934 enumerable: true, 935 writable: false, 936 value: ObjectFreeze(codes) 937 } 938}); 939 940// These should be considered deprecated 941// expose all the zlib constants 942for (const bkey of ObjectKeys(constants)) { 943 if (StringPrototypeStartsWith(bkey, 'BROTLI')) continue; 944 ObjectDefineProperty(module.exports, bkey, { 945 enumerable: false, value: constants[bkey], writable: false 946 }); 947} 948