You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
blockvote/node_modules/minizlib/index.js

421 lines
24 KiB

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

'use strict'
// 引入Node.js的内置断言模块用于编写测试用例时进行各种断言检查确保代码符合预期的行为
const assert = require('assert');
// 引入Node.js的Buffer模块用于处理二进制数据如创建、操作字节缓冲区等
const Buffer = require('buffer').Buffer;
// 引入Node.js的zlib模块用于进行数据的压缩和解压缩操作
const realZlib = require('zlib');
// 引入自定义的常量模块(./constants.js并将其赋值给当前模块的exports.constants方便其他模块使用这些常量
const constants = exports.constants = require('./constants.js');
// 引入Minipass模块Minipass可能是一个实现了流相关功能的基础类用于构建自定义的流对象此处为推测具体功能依赖其自身实现
const Minipass = require('minipass');
// 保存原始的Buffer.concat方法后续代码中会对Buffer.concat进行临时修改这里先保存以便恢复
const OriginalBufferConcat = Buffer.concat;
// 自定义的错误类ZlibError继承自Error类用于包装zlib相关操作出现的错误提供更详细和统一的错误信息格式
class ZlibError extends Error {
constructor (err) {
// 调用父类Error的构造函数传入格式化后的错误消息消息前缀为 'zlib: ',后面跟上原始错误的消息内容
super('zlib: ' + err.message);
// 将原始错误的错误码赋值给当前错误对象的code属性方便外部根据错误码判断具体错误类型
this.code = err.code;
// 将原始错误的系统错误号errno赋值给当前错误对象的errno属性用于更底层的错误追踪和处理如果适用
this.errno = err.errno;
/* istanbul ignore if */
// 以下代码块在测试覆盖时可能会被忽略(通常是一些特殊情况或者难以触发的逻辑),
// 如果当前错误对象没有设置错误码code属性为空则将错误码默认设置为 'ZLIB_ERROR'
if (!this.code)
this.code = 'ZLIB_ERROR';
// 再次设置错误消息,确保消息格式符合预期,这里与构造函数开头设置的消息内容一致,可能是为了代码的清晰性和一致性
this.message = 'zlib: ' + err.message;
// 捕获当前错误对象的堆栈信息,方便在调试时查看错误发生的调用栈情况,第二个参数指定了构造函数作为起始位置
Error.captureStackTrace(this, this.constructor);
}
// 重写name属性的访问器返回自定义的错误名称 'ZlibError',用于更清晰地标识错误类型
get name () {
return 'ZlibError';
}
}
// 以下定义了一系列Symbol类型的私有属性名用于在ZlibBase类中标记一些内部使用的属性
// 通过Symbol可以创建唯一的标识符避免属性名冲突实现一定程度的封装和数据隐藏
const _opts = Symbol('opts');
const _flushFlag = Symbol('flushFlag');
const _finishFlushFlag = Symbol('finishFlushFlag');
const _fullFlushFlag = Symbol('fullFlushFlag');
const _handle = Symbol('handle');
const _onError = Symbol('onError');
const _sawError = Symbol('sawError');
const _level = Symbol('level');
const _strategy = Symbol('strategy');
const _ended = Symbol('ended');
const _defaultFullFlush = Symbol('_defaultFullFlush');
// ZlibBase类继承自Minipass类作为其他与zlib相关的类的基类用于管理压缩/解压操作的请求队列等功能
class ZlibBase extends Minipass {
constructor (opts, mode) {
// 首先检查传入的opts参数如果不存在或者不是对象类型抛出TypeError异常提示传入的ZlibBase构造函数参数无效
if (!opts || typeof opts!== 'object')
throw new TypeError('invalid options for ZlibBase constructor');
// 调用父类Minipass的构造函数传入opts参数完成父类的初始化操作
super(opts);
// 标记当前对象是否已经结束初始化为false表示尚未结束通过私有属性 _ended 来记录
this[_ended] = false;
// 将传入的opts参数保存到私有属性 _opts 中,方便后续在类中使用这些配置选项
this[_opts] = opts;
// 将opts中的flush属性值赋给私有属性 _flushFlag可能用于控制数据的刷新方式具体依赖zlib相关逻辑
this[_flushFlag] = opts.flush;
// 将opts中的finishFlush属性值赋给私有属性 _finishFlushFlag可能与结束时的刷新操作相关同样依赖具体逻辑
this[_finishFlushFlag] = opts.finishFlush;
// 尝试根据传入的mode创建对应的zlib实例通过realZlib[mode]这里假设realZlib对象有根据不同模式创建实例的功能
// 如果创建过程中出现错误捕获异常并抛出一个经过包装的ZlibError错误对象确保错误信息符合自定义的格式和处理逻辑
try {
this[_handle] = new realZlib[mode](opts);
} catch (er) {
// make sure that all errors get decorated properly
throw new ZlibError(er);
}
// 定义一个内部的错误处理函数 _onError用于处理zlib实例触发的错误事件
this[_onError] = (err) => {
// 标记已经出现错误,通过私有属性 _sawError 记录,后续可以根据此标记进行相应的处理判断
this[_sawError] = true;
// 在出现错误的情况下认为无法进行干净的恢复操作直接关闭相关资源调用close方法
// 继续执行可能会掩盖问题,所以选择关闭并触发 'error' 事件向外传递错误信息
this.close();
this.emit('error', err);
};
// 监听zlib实例的 'error' 事件,当触发时调用内部的 _onError 函数来处理错误将原始错误包装为ZlibError类型后再处理
this[_handle].on('error', er => this[_onError](new ZlibError(er)));
// 监听当前对象继承自Minipass可能是流相关的结束事件的 'end' 事件当触发时调用close方法进行关闭相关操作
this.once('end', () => this.close);
}
// close方法用于关闭与zlib相关的资源可能是释放底层的句柄等操作
close () {
// 如果存在zlib实例的句柄_handle属性不为null则调用其close方法进行关闭操作
// 然后将 _handle 属性设置为null表示已经关闭最后触发 'close' 事件通知外部相关操作已完成
if (this[_handle]) {
this[_handle].close();
this[_handle] = null;
this.emit('close');
}
}
// reset方法用于重置zlib相关的状态前提是没有出现过错误通过 _sawError 属性判断),
// 如果没有错误且存在zlib实例的句柄调用句柄的reset方法进行重置操作具体重置的内容依赖zlib底层实现
reset () {
if (!this[_sawError]) {
assert(this[_handle], 'zlib binding closed');
return this[_handle].reset();
}
}
// flush方法用于执行数据的刷新操作根据传入的参数或默认的刷新标志来决定刷新方式
flush (flushFlag) {
// 如果已经标记为结束状态ended为真则直接返回不进行刷新操作
if (this.ended)
return;
// 如果传入的flushFlag参数不是数字类型使用默认的完全刷新标志_fullFlushFlag具体值应在类的其他地方定义或初始化
if (typeof flushFlag!== 'number')
flushFlag = this[_fullFlushFlag];
// 创建一个空的Buffer对象并将刷新标志_flushFlag作为属性添加到该对象上然后调用write方法将其写入流中进行刷新操作
this.write(Object.assign(Buffer.alloc(0), { [_flushFlag]: flushFlag }));
}
// end方法用于标记流的结束操作并进行一些相关的清理和刷新操作
end (chunk, encoding, cb) {
// 如果传入了数据chunk先调用write方法将其写入流中进行相应的编码转换等处理后面的代码会实现
if (chunk)
this.write(chunk, encoding);
// 调用flush方法进行结束时的刷新操作使用 _finishFlushFlag 作为刷新标志
this.flush(this[_finishFlushFlag]);
// 标记当前对象已经结束(通过私有属性 _ended 设置为true
this[_ended] = true;
// 调用父类Minipass的end方法传入null表示没有额外的数据要写入结束流操作并传递回调函数cb如果有的话
return super.end(null, null, cb);
}
// ended属性访问器用于获取当前对象是否已经结束的状态返回存储在 _ended 属性中的值
get ended () {
return this[_ended];
}
// write方法用于向流中写入数据涉及到对数据的处理、调用zlib实例进行压缩/解压操作以及将结果写入父类流等复杂逻辑
write (chunk, encoding, cb) {
// 如果传入的第二个参数encoding是函数类型将其作为回调函数cb同时将encoding默认设置为 'utf8'
// 这是一种常见的参数处理方式,用于统一参数格式,方便后续对数据进行处理
if (typeof encoding === 'function')
cb = encoding, encoding = 'utf8';
// 如果传入的第一个参数chunk是字符串类型将其转换为Buffer类型使用指定的encoding进行编码转换
if (typeof chunk ==='string')
chunk = Buffer.from(chunk, encoding);
// 如果已经出现过错误_sawError为真直接返回不再进行写入操作
if (this[_sawError])
return;
// 通过断言确保存在zlib实例的句柄如果不存在则抛出异常提示zlib绑定已关闭无法进行写入操作
assert(this[_handle], 'zlib binding closed');
// 以下代码块对zlib实例的原生句柄_handle._handle进行一些临时操作主要是拦截其close方法使其变为空操作noop
// 目的是避免在处理数据过程中意外关闭句柄同时也保存了原始的close方法方便后续恢复
const nativeHandle = this[_handle]._handle;
const originalNativeClose = nativeHandle.close;
nativeHandle.close = () => {};
const originalClose = this[_handle].close;
this[_handle].close = () => {};
// 临时修改Buffer.concat方法为一个简单返回参数的函数这里修改的目的可能是为了优化性能或者避免一些不必要的操作
// 因为原有的Buffer.concat可能会有一些额外的逻辑如合并缓冲区等而此处不需要这些功能
Buffer.concat = (args) => args;
let result;
try {
// 根据传入的chunk数据是否包含 _flushFlag 属性以及其值来确定刷新标志,如果没有则使用类中保存的 _flushFlag 属性值
const flushFlag = typeof chunk[_flushFlag] === 'number'
? chunk[_flushFlag] : this[_flushFlag];
// 调用zlib实例的 _processChunk方法处理数据chunk传入数据和刷新标志获取处理结果可能是经过压缩/解压后的缓冲区数据等)
result = this[_handle]._processChunk(chunk, flushFlag);
// 如果没有抛出异常说明处理成功将Buffer.concat方法恢复为原始的方法之前保存的OriginalBufferConcat
Buffer.concat = OriginalBufferConcat;
} catch (err) {
// 如果在处理过程中抛出异常先将Buffer.concat方法恢复为原始的方法
// 然后调用内部的 _onError 方法处理错误将原始错误包装为ZlibError类型并触发 'error' 事件向外通知错误情况
// 之所以要先恢复Buffer.concat方法是因为后续的错误处理可能会涉及到用户代码调用Buffer.concat方法如果不恢复可能导致错误
Buffer.concat = OriginalBufferConcat;
this[_onError](new ZlibError(err));
} finally {
if (this[_handle]) {
// 在finally块中确保无论是否出现异常都要进行一些清理和恢复操作
// 将zlib实例的 _handle 属性重新指向原始的原生句柄之前保存的nativeHandle恢复原生句柄的close方法为原始的方法
this[_handle]._handle = nativeHandle;
nativeHandle.close = originalNativeClose;
this[_handle].close = originalClose;
// 移除zlib实例对 'error' 事件的所有监听器,因为 _processChunk方法每次调用可能会添加 'error' 监听器,
// 如果不及时移除,这些监听器会不断累积,导致意外的行为
this[_handle].removeAllListeners('error');
}
}
let writeReturn;
// 如果有处理结果result进行后续的写入操作
if (result) {
// 如果处理结果是数组且长度大于0说明可能有多个缓冲区数据需要写入
// 先将第一个缓冲区通常是zlib实例内部的输出缓冲区可能会被复用所以需要复制一份通过父类的write方法写入流中
// 然后遍历数组的其余元素依次调用父类的write方法将数据写入流中
if (Array.isArray(result) && result.length > 0) {
writeReturn = super.write(Buffer.from(result[0]));
for (let i = 1; i < result.length; i++) {
writeReturn = super.write(result[i]);
}
} else {
// 如果结果不是数组或者数组长度为0直接将结果单个缓冲区数据通过父类的write方法写入流中
writeReturn = super.write(Buffer.from(result));
}
}
// 如果有回调函数cb执行回调函数通常用于通知写入操作完成等情况
if (cb)
cb();
// 返回写入操作的返回值可能是父类write方法的返回值具体含义依赖父类的实现逻辑
return writeReturn;
}
}
// Zlib类继承自ZlibBase类用于进一步定制基于zlib库的压缩/解压相关操作,添加特定的默认参数设置等功能
class Zlib extends ZlibBase {
constructor (opts, mode) {
// 如果没有传入opts参数则创建一个空对象作为默认值确保后续对opts属性的访问不会出现问题
opts = opts || {};
// 如果opts中没有设置flush属性将其默认设置为 constants.Z_NO_FLUSH从之前引入的常量模块中获取可能表示不进行刷新操作的常量值
opts.flush = opts.flush || constants.Z_NO_FLUSH;
// 如果opts中没有设置finishFlush属性将其默认设置为 constants.Z_FINISH可能表示完成时的刷新操作相关常量值
opts.finishFlush = opts.finishFlush || constants.Z_FINISH;
// 调用父类ZlibBase的构造函数传入处理后的opts参数和mode参数完成父类的初始化以及与zlib底层实例的创建等操作
super(opts, mode);
// 设置完全刷新标志_fullFlushFlag为 constants.Z_FULL_FLUSH具体含义与zlib的刷新机制相关可能是一种较为彻底的刷新方式对应的常量值
this[_fullFlushFlag] = constants.Z_FULL_FLUSH;
// 将opts中的level属性值保存到私有属性 _level 中,可能用于记录当前压缩/解压操作的级别设置具体依赖zlib相关逻辑
this[_level] = opts.level;
// 将opts中的strategy属性值保存到私有属性 _strategy 中,可能用于记录当前采用的压缩/解压策略(同样依赖具体逻辑)
this[_strategy] = opts.strategy;
}
// params方法用于修改当前zlib实例的参数如压缩级别、策略等但需要满足一定条件并进行一些复杂的内部处理
params (level, strategy) {
// 如果已经出现过错误_sawError为真直接返回不进行参数修改操作可能是在出现错误后认为当前状态不适合修改参数
if (this[_sawError])
return;
// 如果不存在zlib实例的句柄_handle为null抛出一个错误提示在绑定已关闭的情况下无法切换参数
if (!this[_handle])
throw new Error('cannot switch params when binding is closed');
// 以下代码块在测试覆盖时可能会被忽略(通常是一些难以触发或者不适合在常规测试中执行的逻辑),
// 如果当前zlib实例不支持params方法即 _handle.params不存在抛出一个错误提示在当前实现中不支持此操作
// 这里可能是因为不同版本或者不同底层实现对参数修改的支持情况不一致导致的判断
// no way to test this without also not supporting params at all
/* istanbul ignore if */
if (!this[_handle].params)
throw new Error('not supported in this implementation');
// 如果传入的新参数level或strategy与当前保存的参数_level、_strategy不一致进行参数修改操作
if (this[_level]!== level || this[_strategy]!== strategy) {
// 先调用flush方法进行同步刷新操作使用 constants.Z_SYNC_FLUSH 作为刷新标志,确保数据处于合适的状态以便修改参数
this.flush(constants.Z_SYNC_FLUSH);
// 通过断言确保存在zlib实例的句柄如果不存在则抛出异常提示zlib绑定已关闭无法进行参数修改操作
assert(this[_handle], 'zlib binding closed');
// 以下代码是为了处理核心zlib库中.params() 方法调用.flush() 方法且后者是异步的情况,
// 在这里临时重写zlib实例的.flush() 方法使其在内部调用当前类的flush方法实现同步刷新并执行传入的回调函数cb
// 这样可以拦截并改变原本异步的刷新行为,使其符合当前参数修改时的同步需求
const origFlush = this[_handle].flush;
this[_handle].flush = (flushFlag, cb) => {
this.flush(flushFlag);
cb();
};
try {
// 调用zlib实例的.params() 方法传入新的参数level和strategy实际执行参数修改操作具体修改的内容依赖zlib底层实现
this[_handle].params(level, strategy);
} finally {
// 在无论是否出现异常的情况下都要将zlib实例的.flush() 方法恢复为原始的方法之前保存的origFlush确保后续操作不受影响
this[_handle].flush = origFlush;
}
/* istanbul ignore else */
// 以下代码块在测试覆盖时可能会被默认忽略(可能是一些正常流程下必然会执行的逻辑),
// 如果zlib实例的句柄仍然存在即没有在参数修改过程中出现意外关闭等情况更新当前对象保存的参数_level和 _strategy为新传入的值
if (this[_handle]) {
this[_level] = level;
this[_strategy] = strategy;
}
}
}
}
// Deflate类继承自Zlib类代表使用deflate压缩算法的相关操作构造函数中传入opts参数并调用父类Zlib的构造函数指定模式为 'Deflate'
// 它可能会在创建实例时根据Zlib类以及更上层的ZlibBase类的逻辑初始化与deflate算法相关的配置和底层zlib实例等内容
class Deflate extends Zlib {
constructor (opts) {
super(opts, 'Deflate');
}
}
// Inflate类继承自Zlib类代表使用inflate解压算法的相关操作构造函数中传入opts参数并调用父类Zlib的构造函数指定模式为 'Inflate'
// 用于初始化与inflate算法相关的配置和底层zlib实例等以便后续进行解压相关的操作
class Inflate extends Zlib {
constructor (opts) {
super(opts, 'Inflate');
}
}
// Gzip类继承自Zlib类用于处理gzip格式的压缩/解压相关操作构造函数中传入opts参数并调用父类Zlib的构造函数指定模式为 'Gzip'
// gzip是一种常见的带有特定头部格式的压缩文件格式该类在初始化时会基于Zlib类的基础配置和对应模式来准备相关操作
class Gzip extends Zlib {
constructor (opts) {
super(opts, 'Gzip');
}
}
// Gunzip类继承自Zlib类主要用于对gzip格式文件进行解压操作构造函数中传入opts参数并调用父类Zlib的构造函数指定模式为 'Gunzip'
class Gunzip extends Zlib {
constructor (opts) {
super(opts, 'Gunzip');
}
}
// DeflateRaw类继承自Zlib类用于使用原始的deflate算法进行操作可能不包含像gzip等格式那样的额外头部信息
// 构造函数传入opts参数并调用父类Zlib的构造函数指定模式为 'DeflateRaw',以便初始化相关配置和底层实例
class DeflateRaw extends Zlib {
constructor (opts) {
super(opts, 'DeflateRaw');
}
}
// InflateRaw类继承自Zlib类用于使用原始的inflate算法进行解压操作对应前面的DeflateRaw去除格式头部等信息的解压情况
// 构造函数传入opts参数并调用父类Zlib的构造函数指定模式为 'InflateRaw',来初始化相应的配置和底层实例
class InflateRaw extends Zlib {
constructor (opts) {
super(opts, 'InflateRaw');
}
}
// Unzip类继承自Zlib类用于自动检测文件头部并进行相应的解压操作可以处理多种格式通过自动识别头部来确定解压方式
// 构造函数传入opts参数并调用父类Zlib的构造函数指定模式为 'Unzip',以此来初始化相关配置和底层实例
class Unzip extends Zlib {
constructor (opts) {
super(opts, 'Unzip');
}
}
// Brotli类继承自ZlibBase类用于基于Brotli压缩算法进行相关操作和Zlib类类似不过是针对Brotli算法的特定处理
class Brotli extends ZlibBase {
constructor (opts, mode) {
opts = opts || {};
// 如果opts中没有设置flush属性将其默认设置为 constants.BROTLI_OPERATION_PROCESS与Brotli算法的操作类型相关的常量可能表示常规处理阶段的刷新方式
opts.flush = opts.flush || constants.BROTLI_OPERATION_PROCESS;
// 如果opts中没有设置finishFlush属性将其默认设置为 constants.BROTLI_OPERATION_FINISH可能表示Brotli算法完成操作时的刷新方式相关常量值
opts.finishFlush = opts.finishFlush || constants.BROTLI_OPERATION_FINISH;
// 调用父类ZlibBase的构造函数传入处理后的opts参数和mode参数完成父类的初始化以及与Brotli相关的底层实例创建等操作
super(opts, mode);
// 设置完全刷新标志_fullFlushFlag为 constants.BROTLI_OPERATION_FLUSH与Brotli算法的刷新操作类型相关的常量值
this[_fullFlushFlag] = constants.BROTLI_OPERATION_FLUSH;
}
}
// BrotliCompress类继承自Brotli类专门用于使用Brotli算法进行压缩操作构造函数传入opts参数并调用父类Brotli的构造函数指定模式为 'BrotliCompress'
// 以此来初始化与Brotli压缩相关的配置和底层实例等内容
class BrotliCompress extends Brotli {
constructor (opts) {
super(opts, 'BrotliCompress');
}
}
// BrotliDecompress类继承自Brotli类专门用于使用Brotli算法进行解压操作构造函数传入opts参数并调用父类Brotli的构造函数指定模式为 'BrotliDecompress'
// 用于初始化与Brotli解压相关的配置和底层实例等以便后续进行解压相关的操作
class BrotliDecompress extends Brotli {
constructor (opts) {
super(opts, 'BrotliDecompress');
}
}
// 将上述定义的各个类作为模块的导出内容,方便其他模块引入并使用这些类来进行相应的压缩、解压等操作
exports.Deflate = Deflate;
exports.Inflate = Inflate;
exports.Gzip = Gzip;
exports.Gunzip = Gunzip;
exports.DeflateRaw = DeflateRaw;
exports.InflateRaw = InflateRaw;
exports.Unzip = Unzip;
/* istanbul ignore else */
// 以下代码块在测试覆盖时可能会被默认忽略可能是根据不同Node.js版本的兼容性判断等情况
// 如果Node.js的 realZlib 模块中存在 BrotliCompress 函数即支持Brotli压缩算法则将 BrotliCompress 和 BrotliDecompress 类作为模块导出内容,
// 否则创建一个在构造函数中抛出错误的类作为它们的导出内容提示在当前版本的Node.js中不支持Brotli算法
if (typeof realZlib.BrotliCompress === 'function') {
exports.BrotliCompress = BrotliCompress;
exports.BrotliDecompress = BrotliDecompress;
} else {
exports.BrotliCompress = exports.BrotliDecompress = class {
constructor () {
throw new Error('Brotli is not supported in this version of Node.js');
}
};
}