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