| 
									
										
										
										
											2018-10-09 20:30:59 +08:00
										 |  |  | /* | 
					
						
							|  |  |  | 	MIT License http://www.opensource.org/licenses/mit-license.php
 | 
					
						
							|  |  |  | */ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | "use strict"; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-12-14 20:16:20 +08:00
										 |  |  | const { constants } = require("buffer"); | 
					
						
							| 
									
										
										
										
											2021-06-30 06:05:38 +08:00
										 |  |  | const { pipeline } = require("stream"); | 
					
						
							|  |  |  | const { | 
					
						
							|  |  |  | 	createBrotliCompress, | 
					
						
							|  |  |  | 	createBrotliDecompress, | 
					
						
							|  |  |  | 	createGzip, | 
					
						
							|  |  |  | 	createGunzip, | 
					
						
							|  |  |  | 	constants: zConstants | 
					
						
							|  |  |  | } = require("zlib"); | 
					
						
							| 
									
										
										
										
											2020-01-24 06:51:49 +08:00
										 |  |  | const createHash = require("../util/createHash"); | 
					
						
							|  |  |  | const { dirname, join, mkdirp } = require("../util/fs"); | 
					
						
							| 
									
										
										
										
											2020-12-27 05:32:57 +08:00
										 |  |  | const memoize = require("../util/memoize"); | 
					
						
							| 
									
										
										
										
											2018-10-09 20:30:59 +08:00
										 |  |  | const SerializerMiddleware = require("./SerializerMiddleware"); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-09-22 18:12:46 +08:00
										 |  |  | /** @typedef {typeof import("../util/Hash")} Hash */ | 
					
						
							| 
									
										
										
										
											2020-01-24 06:51:49 +08:00
										 |  |  | /** @typedef {import("../util/fs").IntermediateFileSystem} IntermediateFileSystem */ | 
					
						
							| 
									
										
										
										
											2019-01-24 23:51:05 +08:00
										 |  |  | /** @typedef {import("./types").BufferSerializableType} BufferSerializableType */ | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-02-01 00:53:50 +08:00
										 |  |  | /* | 
					
						
							|  |  |  | Format: | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | File -> Header Section* | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | Version -> u32 | 
					
						
							|  |  |  | AmountOfSections -> u32 | 
					
						
							|  |  |  | SectionSize -> i32 (if less than zero represents lazy value) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | Header -> Version AmountOfSections SectionSize* | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | Buffer -> n bytes | 
					
						
							|  |  |  | Section -> Buffer | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | */ | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-12-12 18:19:39 +08:00
										 |  |  | // "wpc" + 1 in little-endian
 | 
					
						
							|  |  |  | const VERSION = 0x01637077; | 
					
						
							| 
									
										
										
										
											2021-09-22 18:12:46 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | /** | 
					
						
							|  |  |  |  * @param {Buffer[]} buffers buffers | 
					
						
							|  |  |  |  * @param {string | Hash} hashFunction hash function to use | 
					
						
							|  |  |  |  * @returns {string} hash | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | const hashForName = (buffers, hashFunction) => { | 
					
						
							|  |  |  | 	const hash = createHash(hashFunction); | 
					
						
							| 
									
										
										
										
											2020-01-24 06:51:49 +08:00
										 |  |  | 	for (const buf of buffers) hash.update(buf); | 
					
						
							|  |  |  | 	return /** @type {string} */ (hash.digest("hex")); | 
					
						
							|  |  |  | }; | 
					
						
							| 
									
										
										
										
											2018-10-09 20:30:59 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-06-30 06:05:38 +08:00
										 |  |  | const COMPRESSION_CHUNK_SIZE = 100 * 1024 * 1024; | 
					
						
							|  |  |  | const DECOMPRESSION_CHUNK_SIZE = 100 * 1024 * 1024; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-12-14 17:48:21 +08:00
										 |  |  | const writeUInt64LE = Buffer.prototype.writeBigUInt64LE | 
					
						
							|  |  |  | 	? (buf, value, offset) => { | 
					
						
							|  |  |  | 			buf.writeBigUInt64LE(BigInt(value), offset); | 
					
						
							|  |  |  | 	  } | 
					
						
							|  |  |  | 	: (buf, value, offset) => { | 
					
						
							|  |  |  | 			const low = value % 0x100000000; | 
					
						
							|  |  |  | 			const high = (value - low) / 0x100000000; | 
					
						
							|  |  |  | 			buf.writeUInt32LE(low, offset); | 
					
						
							|  |  |  | 			buf.writeUInt32LE(high, offset + 4); | 
					
						
							|  |  |  | 	  }; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | const readUInt64LE = Buffer.prototype.readBigUInt64LE | 
					
						
							|  |  |  | 	? (buf, offset) => { | 
					
						
							|  |  |  | 			return Number(buf.readBigUInt64LE(offset)); | 
					
						
							|  |  |  | 	  } | 
					
						
							|  |  |  | 	: (buf, offset) => { | 
					
						
							|  |  |  | 			const low = buf.readUInt32LE(offset); | 
					
						
							|  |  |  | 			const high = buf.readUInt32LE(offset + 4); | 
					
						
							|  |  |  | 			return high * 0x100000000 + low; | 
					
						
							|  |  |  | 	  }; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-01-24 06:51:49 +08:00
										 |  |  | /** | 
					
						
							|  |  |  |  * @typedef {Object} SerializeResult | 
					
						
							|  |  |  |  * @property {string | false} name | 
					
						
							|  |  |  |  * @property {number} size | 
					
						
							|  |  |  |  * @property {Promise=} backgroundJob | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /** | 
					
						
							|  |  |  |  * @param {FileMiddleware} middleware this | 
					
						
							|  |  |  |  * @param {BufferSerializableType[] | Promise<BufferSerializableType[]>} data data to be serialized | 
					
						
							|  |  |  |  * @param {string | boolean} name file base name | 
					
						
							| 
									
										
										
										
											2021-06-30 06:05:38 +08:00
										 |  |  |  * @param {function(string | false, Buffer[]): Promise<void>} writeFile writes a file | 
					
						
							| 
									
										
										
										
											2021-09-22 18:12:46 +08:00
										 |  |  |  * @param {string | Hash} hashFunction hash function to use | 
					
						
							| 
									
										
										
										
											2020-01-24 06:51:49 +08:00
										 |  |  |  * @returns {Promise<SerializeResult>} resulting file pointer and promise | 
					
						
							|  |  |  |  */ | 
					
						
							| 
									
										
										
										
											2021-09-22 18:12:46 +08:00
										 |  |  | const serialize = async ( | 
					
						
							|  |  |  | 	middleware, | 
					
						
							|  |  |  | 	data, | 
					
						
							|  |  |  | 	name, | 
					
						
							|  |  |  | 	writeFile, | 
					
						
							|  |  |  | 	hashFunction = "md4" | 
					
						
							|  |  |  | ) => { | 
					
						
							| 
									
										
										
										
											2020-01-24 06:51:49 +08:00
										 |  |  | 	/** @type {(Buffer[] | Buffer | SerializeResult | Promise<SerializeResult>)[]} */ | 
					
						
							|  |  |  | 	const processedData = []; | 
					
						
							|  |  |  | 	/** @type {WeakMap<SerializeResult, function(): any | Promise<any>>} */ | 
					
						
							|  |  |  | 	const resultToLazy = new WeakMap(); | 
					
						
							|  |  |  | 	/** @type {Buffer[]} */ | 
					
						
							|  |  |  | 	let lastBuffers = undefined; | 
					
						
							|  |  |  | 	for (const item of await data) { | 
					
						
							|  |  |  | 		if (typeof item === "function") { | 
					
						
							|  |  |  | 			if (!SerializerMiddleware.isLazy(item)) | 
					
						
							|  |  |  | 				throw new Error("Unexpected function"); | 
					
						
							|  |  |  | 			if (!SerializerMiddleware.isLazy(item, middleware)) { | 
					
						
							|  |  |  | 				throw new Error( | 
					
						
							|  |  |  | 					"Unexpected lazy value with non-this target (can't pass through lazy values)" | 
					
						
							|  |  |  | 				); | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 			lastBuffers = undefined; | 
					
						
							|  |  |  | 			const serializedInfo = SerializerMiddleware.getLazySerializedValue(item); | 
					
						
							|  |  |  | 			if (serializedInfo) { | 
					
						
							|  |  |  | 				if (typeof serializedInfo === "function") { | 
					
						
							|  |  |  | 					throw new Error( | 
					
						
							|  |  |  | 						"Unexpected lazy value with non-this target (can't pass through lazy values)" | 
					
						
							|  |  |  | 					); | 
					
						
							| 
									
										
										
										
											2018-10-18 21:52:22 +08:00
										 |  |  | 				} else { | 
					
						
							| 
									
										
										
										
											2020-01-24 06:51:49 +08:00
										 |  |  | 					processedData.push(serializedInfo); | 
					
						
							| 
									
										
										
										
											2018-10-09 20:30:59 +08:00
										 |  |  | 				} | 
					
						
							|  |  |  | 			} else { | 
					
						
							| 
									
										
										
										
											2020-01-24 06:51:49 +08:00
										 |  |  | 				const content = item(); | 
					
						
							|  |  |  | 				if (content) { | 
					
						
							|  |  |  | 					const options = SerializerMiddleware.getLazyOptions(item); | 
					
						
							|  |  |  | 					processedData.push( | 
					
						
							|  |  |  | 						serialize( | 
					
						
							|  |  |  | 							middleware, | 
					
						
							|  |  |  | 							content, | 
					
						
							|  |  |  | 							(options && options.name) || true, | 
					
						
							| 
									
										
										
										
											2021-09-22 18:12:46 +08:00
										 |  |  | 							writeFile, | 
					
						
							|  |  |  | 							hashFunction | 
					
						
							| 
									
										
										
										
											2020-01-24 06:51:49 +08:00
										 |  |  | 						).then(result => { | 
					
						
							|  |  |  | 							/** @type {any} */ (item).options.size = result.size; | 
					
						
							|  |  |  | 							resultToLazy.set(result, item); | 
					
						
							|  |  |  | 							return result; | 
					
						
							|  |  |  | 						}) | 
					
						
							|  |  |  | 					); | 
					
						
							|  |  |  | 				} else { | 
					
						
							|  |  |  | 					throw new Error( | 
					
						
							|  |  |  | 						"Unexpected falsy value returned by lazy value function" | 
					
						
							|  |  |  | 					); | 
					
						
							|  |  |  | 				} | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 		} else if (item) { | 
					
						
							|  |  |  | 			if (lastBuffers) { | 
					
						
							|  |  |  | 				lastBuffers.push(item); | 
					
						
							|  |  |  | 			} else { | 
					
						
							|  |  |  | 				lastBuffers = [item]; | 
					
						
							|  |  |  | 				processedData.push(lastBuffers); | 
					
						
							| 
									
										
										
										
											2018-10-09 20:30:59 +08:00
										 |  |  | 			} | 
					
						
							|  |  |  | 		} else { | 
					
						
							| 
									
										
										
										
											2020-01-24 06:51:49 +08:00
										 |  |  | 			throw new Error("Unexpected falsy value in items array"); | 
					
						
							| 
									
										
										
										
											2018-10-09 20:30:59 +08:00
										 |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2020-01-24 06:51:49 +08:00
										 |  |  | 	/** @type {Promise<any>[]} */ | 
					
						
							|  |  |  | 	const backgroundJobs = []; | 
					
						
							|  |  |  | 	const resolvedData = ( | 
					
						
							|  |  |  | 		await Promise.all( | 
					
						
							| 
									
										
										
										
											2021-05-11 15:31:46 +08:00
										 |  |  | 			/** @type {Promise<Buffer[] | Buffer | SerializeResult>[]} */ ( | 
					
						
							|  |  |  | 				processedData | 
					
						
							|  |  |  | 			) | 
					
						
							| 
									
										
										
										
											2020-01-24 06:51:49 +08:00
										 |  |  | 		) | 
					
						
							|  |  |  | 	).map(item => { | 
					
						
							| 
									
										
										
										
											2020-02-01 00:53:50 +08:00
										 |  |  | 		if (Array.isArray(item) || Buffer.isBuffer(item)) return item; | 
					
						
							| 
									
										
										
										
											2018-10-09 20:30:59 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-01-24 06:51:49 +08:00
										 |  |  | 		backgroundJobs.push(item.backgroundJob); | 
					
						
							|  |  |  | 		// create pointer buffer from size and name
 | 
					
						
							|  |  |  | 		const name = /** @type {string} */ (item.name); | 
					
						
							|  |  |  | 		const nameBuffer = Buffer.from(name); | 
					
						
							| 
									
										
										
										
											2020-12-12 18:19:39 +08:00
										 |  |  | 		const buf = Buffer.allocUnsafe(8 + nameBuffer.length); | 
					
						
							| 
									
										
										
										
											2020-12-14 17:48:21 +08:00
										 |  |  | 		writeUInt64LE(buf, item.size, 0); | 
					
						
							| 
									
										
										
										
											2020-12-12 18:19:39 +08:00
										 |  |  | 		nameBuffer.copy(buf, 8, 0); | 
					
						
							| 
									
										
										
										
											2020-01-24 06:51:49 +08:00
										 |  |  | 		const lazy = resultToLazy.get(item); | 
					
						
							|  |  |  | 		SerializerMiddleware.setLazySerializedValue(lazy, buf); | 
					
						
							|  |  |  | 		return buf; | 
					
						
							|  |  |  | 	}); | 
					
						
							| 
									
										
										
										
											2020-12-12 18:19:39 +08:00
										 |  |  | 	const lengths = []; | 
					
						
							|  |  |  | 	for (const item of resolvedData) { | 
					
						
							| 
									
										
										
										
											2020-01-24 06:51:49 +08:00
										 |  |  | 		if (Array.isArray(item)) { | 
					
						
							|  |  |  | 			let l = 0; | 
					
						
							|  |  |  | 			for (const b of item) l += b.length; | 
					
						
							| 
									
										
										
										
											2020-12-12 18:19:39 +08:00
										 |  |  | 			while (l > 0x7fffffff) { | 
					
						
							|  |  |  | 				lengths.push(0x7fffffff); | 
					
						
							|  |  |  | 				l -= 0x7fffffff; | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 			lengths.push(l); | 
					
						
							| 
									
										
										
										
											2020-01-24 06:51:49 +08:00
										 |  |  | 		} else if (item) { | 
					
						
							| 
									
										
										
										
											2020-12-12 18:19:39 +08:00
										 |  |  | 			lengths.push(-item.length); | 
					
						
							| 
									
										
										
										
											2020-01-24 06:51:49 +08:00
										 |  |  | 		} else { | 
					
						
							|  |  |  | 			throw new Error("Unexpected falsy value in resolved data " + item); | 
					
						
							|  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2020-12-12 18:19:39 +08:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2020-01-24 06:51:49 +08:00
										 |  |  | 	const header = Buffer.allocUnsafe(8 + lengths.length * 4); | 
					
						
							| 
									
										
										
										
											2020-02-01 00:53:50 +08:00
										 |  |  | 	header.writeUInt32LE(VERSION, 0); | 
					
						
							| 
									
										
										
										
											2020-01-24 06:51:49 +08:00
										 |  |  | 	header.writeUInt32LE(lengths.length, 4); | 
					
						
							|  |  |  | 	for (let i = 0; i < lengths.length; i++) { | 
					
						
							|  |  |  | 		header.writeInt32LE(lengths[i], 8 + i * 4); | 
					
						
							| 
									
										
										
										
											2018-10-09 20:30:59 +08:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2020-01-24 06:51:49 +08:00
										 |  |  | 	const buf = [header]; | 
					
						
							|  |  |  | 	for (const item of resolvedData) { | 
					
						
							|  |  |  | 		if (Array.isArray(item)) { | 
					
						
							|  |  |  | 			for (const b of item) buf.push(b); | 
					
						
							|  |  |  | 		} else if (item) { | 
					
						
							|  |  |  | 			buf.push(item); | 
					
						
							| 
									
										
										
										
											2018-10-09 20:30:59 +08:00
										 |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2020-01-24 06:51:49 +08:00
										 |  |  | 	if (name === true) { | 
					
						
							| 
									
										
										
										
											2021-09-22 18:12:46 +08:00
										 |  |  | 		name = hashForName(buf, hashFunction); | 
					
						
							| 
									
										
										
										
											2020-01-24 06:51:49 +08:00
										 |  |  | 	} | 
					
						
							|  |  |  | 	backgroundJobs.push(writeFile(name, buf)); | 
					
						
							|  |  |  | 	let size = 0; | 
					
						
							|  |  |  | 	for (const b of buf) size += b.length; | 
					
						
							|  |  |  | 	return { | 
					
						
							|  |  |  | 		size, | 
					
						
							|  |  |  | 		name, | 
					
						
							|  |  |  | 		backgroundJob: | 
					
						
							|  |  |  | 			backgroundJobs.length === 1 | 
					
						
							|  |  |  | 				? backgroundJobs[0] | 
					
						
							|  |  |  | 				: Promise.all(backgroundJobs) | 
					
						
							|  |  |  | 	}; | 
					
						
							|  |  |  | }; | 
					
						
							| 
									
										
										
										
											2018-10-09 20:30:59 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-12-21 19:02:37 +08:00
										 |  |  | /** | 
					
						
							| 
									
										
										
										
											2020-01-24 06:51:49 +08:00
										 |  |  |  * @param {FileMiddleware} middleware this | 
					
						
							|  |  |  |  * @param {string | false} name filename | 
					
						
							| 
									
										
										
										
											2020-12-12 18:19:39 +08:00
										 |  |  |  * @param {function(string | false): Promise<Buffer[]>} readFile read content of a file | 
					
						
							| 
									
										
										
										
											2020-01-24 06:51:49 +08:00
										 |  |  |  * @returns {Promise<BufferSerializableType[]>} deserialized data | 
					
						
							| 
									
										
										
										
											2018-12-21 19:02:37 +08:00
										 |  |  |  */ | 
					
						
							| 
									
										
										
										
											2020-01-24 06:51:49 +08:00
										 |  |  | const deserialize = async (middleware, name, readFile) => { | 
					
						
							| 
									
										
										
										
											2020-12-12 18:19:39 +08:00
										 |  |  | 	const contents = await readFile(name); | 
					
						
							|  |  |  | 	if (contents.length === 0) throw new Error("Empty file " + name); | 
					
						
							|  |  |  | 	let contentsIndex = 0; | 
					
						
							|  |  |  | 	let contentItem = contents[0]; | 
					
						
							|  |  |  | 	let contentItemLength = contentItem.length; | 
					
						
							|  |  |  | 	let contentPosition = 0; | 
					
						
							|  |  |  | 	if (contentItemLength === 0) throw new Error("Empty file " + name); | 
					
						
							| 
									
										
										
										
											2020-12-12 20:32:58 +08:00
										 |  |  | 	const nextContent = () => { | 
					
						
							|  |  |  | 		contentsIndex++; | 
					
						
							|  |  |  | 		contentItem = contents[contentsIndex]; | 
					
						
							|  |  |  | 		contentItemLength = contentItem.length; | 
					
						
							|  |  |  | 		contentPosition = 0; | 
					
						
							|  |  |  | 	}; | 
					
						
							| 
									
										
										
										
											2020-12-12 18:19:39 +08:00
										 |  |  | 	const ensureData = n => { | 
					
						
							|  |  |  | 		if (contentPosition === contentItemLength) { | 
					
						
							| 
									
										
										
										
											2020-12-12 20:32:58 +08:00
										 |  |  | 			nextContent(); | 
					
						
							| 
									
										
										
										
											2020-12-12 18:19:39 +08:00
										 |  |  | 		} | 
					
						
							|  |  |  | 		while (contentItemLength - contentPosition < n) { | 
					
						
							|  |  |  | 			const remaining = contentItem.slice(contentPosition); | 
					
						
							|  |  |  | 			let lengthFromNext = n - remaining.length; | 
					
						
							|  |  |  | 			const buffers = [remaining]; | 
					
						
							|  |  |  | 			for (let i = contentsIndex + 1; i < contents.length; i++) { | 
					
						
							|  |  |  | 				const l = contents[i].length; | 
					
						
							|  |  |  | 				if (l > lengthFromNext) { | 
					
						
							|  |  |  | 					buffers.push(contents[i].slice(0, lengthFromNext)); | 
					
						
							|  |  |  | 					contents[i] = contents[i].slice(lengthFromNext); | 
					
						
							|  |  |  | 					lengthFromNext = 0; | 
					
						
							|  |  |  | 					break; | 
					
						
							|  |  |  | 				} else { | 
					
						
							|  |  |  | 					buffers.push(contents[i]); | 
					
						
							|  |  |  | 					contentsIndex = i; | 
					
						
							|  |  |  | 					lengthFromNext -= l; | 
					
						
							|  |  |  | 				} | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 			if (lengthFromNext > 0) throw new Error("Unexpected end of data"); | 
					
						
							|  |  |  | 			contentItem = Buffer.concat(buffers, n); | 
					
						
							|  |  |  | 			contentItemLength = n; | 
					
						
							|  |  |  | 			contentPosition = 0; | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	}; | 
					
						
							|  |  |  | 	const readUInt32LE = () => { | 
					
						
							|  |  |  | 		ensureData(4); | 
					
						
							|  |  |  | 		const value = contentItem.readUInt32LE(contentPosition); | 
					
						
							|  |  |  | 		contentPosition += 4; | 
					
						
							|  |  |  | 		return value; | 
					
						
							|  |  |  | 	}; | 
					
						
							|  |  |  | 	const readInt32LE = () => { | 
					
						
							|  |  |  | 		ensureData(4); | 
					
						
							|  |  |  | 		const value = contentItem.readInt32LE(contentPosition); | 
					
						
							|  |  |  | 		contentPosition += 4; | 
					
						
							|  |  |  | 		return value; | 
					
						
							|  |  |  | 	}; | 
					
						
							| 
									
										
										
										
											2020-12-12 20:32:58 +08:00
										 |  |  | 	const readSlice = l => { | 
					
						
							| 
									
										
										
										
											2020-12-12 18:19:39 +08:00
										 |  |  | 		ensureData(l); | 
					
						
							|  |  |  | 		if (contentPosition === 0 && contentItemLength === l) { | 
					
						
							|  |  |  | 			const result = contentItem; | 
					
						
							|  |  |  | 			if (contentsIndex + 1 < contents.length) { | 
					
						
							| 
									
										
										
										
											2020-12-12 20:32:58 +08:00
										 |  |  | 				nextContent(); | 
					
						
							| 
									
										
										
										
											2020-12-12 18:19:39 +08:00
										 |  |  | 			} else { | 
					
						
							|  |  |  | 				contentPosition = l; | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 			return result; | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		const result = contentItem.slice(contentPosition, contentPosition + l); | 
					
						
							|  |  |  | 		contentPosition += l; | 
					
						
							| 
									
										
										
										
											2020-12-12 20:32:58 +08:00
										 |  |  | 		// we clone the buffer here to allow the original content to be garbage collected
 | 
					
						
							|  |  |  | 		return l * 2 < contentItem.buffer.byteLength ? Buffer.from(result) : result; | 
					
						
							| 
									
										
										
										
											2020-12-12 18:19:39 +08:00
										 |  |  | 	}; | 
					
						
							|  |  |  | 	const version = readUInt32LE(); | 
					
						
							| 
									
										
										
										
											2020-02-01 00:53:50 +08:00
										 |  |  | 	if (version !== VERSION) { | 
					
						
							| 
									
										
										
										
											2020-01-24 06:51:49 +08:00
										 |  |  | 		throw new Error("Invalid file version"); | 
					
						
							| 
									
										
										
										
											2018-12-21 19:02:37 +08:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2020-12-12 18:19:39 +08:00
										 |  |  | 	const sectionCount = readUInt32LE(); | 
					
						
							| 
									
										
										
										
											2020-01-24 06:51:49 +08:00
										 |  |  | 	const lengths = []; | 
					
						
							| 
									
										
										
										
											2021-06-29 18:04:14 +08:00
										 |  |  | 	let lastLengthPositive = false; | 
					
						
							| 
									
										
										
										
											2020-01-24 06:51:49 +08:00
										 |  |  | 	for (let i = 0; i < sectionCount; i++) { | 
					
						
							| 
									
										
										
										
											2021-06-29 18:04:14 +08:00
										 |  |  | 		const value = readInt32LE(); | 
					
						
							|  |  |  | 		const valuePositive = value >= 0; | 
					
						
							|  |  |  | 		if (lastLengthPositive && valuePositive) { | 
					
						
							|  |  |  | 			lengths[lengths.length - 1] += value; | 
					
						
							|  |  |  | 		} else { | 
					
						
							|  |  |  | 			lengths.push(value); | 
					
						
							|  |  |  | 			lastLengthPositive = valuePositive; | 
					
						
							|  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2018-12-21 19:02:37 +08:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2020-12-12 20:32:58 +08:00
										 |  |  | 	const result = []; | 
					
						
							|  |  |  | 	for (let length of lengths) { | 
					
						
							| 
									
										
										
										
											2020-01-24 06:51:49 +08:00
										 |  |  | 		if (length < 0) { | 
					
						
							| 
									
										
										
										
											2020-12-12 20:32:58 +08:00
										 |  |  | 			const slice = readSlice(-length); | 
					
						
							| 
									
										
										
										
											2020-12-14 17:48:21 +08:00
										 |  |  | 			const size = Number(readUInt64LE(slice, 0)); | 
					
						
							| 
									
										
										
										
											2020-12-12 20:32:58 +08:00
										 |  |  | 			const nameBuffer = slice.slice(8); | 
					
						
							| 
									
										
										
										
											2020-01-24 06:51:49 +08:00
										 |  |  | 			const name = nameBuffer.toString(); | 
					
						
							| 
									
										
										
										
											2020-12-12 20:32:58 +08:00
										 |  |  | 			result.push( | 
					
						
							|  |  |  | 				SerializerMiddleware.createLazy( | 
					
						
							| 
									
										
										
										
											2020-12-27 05:32:57 +08:00
										 |  |  | 					memoize(() => deserialize(middleware, name, readFile)), | 
					
						
							| 
									
										
										
										
											2020-12-12 20:32:58 +08:00
										 |  |  | 					middleware, | 
					
						
							|  |  |  | 					{ | 
					
						
							|  |  |  | 						name, | 
					
						
							|  |  |  | 						size | 
					
						
							|  |  |  | 					}, | 
					
						
							|  |  |  | 					slice | 
					
						
							|  |  |  | 				) | 
					
						
							| 
									
										
										
										
											2019-01-19 18:49:30 +08:00
										 |  |  | 			); | 
					
						
							| 
									
										
										
										
											2019-01-15 00:31:33 +08:00
										 |  |  | 		} else { | 
					
						
							| 
									
										
										
										
											2020-12-12 20:32:58 +08:00
										 |  |  | 			if (contentPosition === contentItemLength) { | 
					
						
							|  |  |  | 				nextContent(); | 
					
						
							|  |  |  | 			} else if (contentPosition !== 0) { | 
					
						
							|  |  |  | 				if (length <= contentItemLength - contentPosition) { | 
					
						
							|  |  |  | 					result.push( | 
					
						
							| 
									
										
										
										
											2021-06-29 16:52:58 +08:00
										 |  |  | 						Buffer.from( | 
					
						
							|  |  |  | 							contentItem.buffer, | 
					
						
							|  |  |  | 							contentItem.byteOffset + contentPosition, | 
					
						
							|  |  |  | 							length | 
					
						
							|  |  |  | 						) | 
					
						
							| 
									
										
										
										
											2020-12-12 20:32:58 +08:00
										 |  |  | 					); | 
					
						
							|  |  |  | 					contentPosition += length; | 
					
						
							|  |  |  | 					length = 0; | 
					
						
							|  |  |  | 				} else { | 
					
						
							| 
									
										
										
										
											2021-06-29 19:02:57 +08:00
										 |  |  | 					const l = contentItemLength - contentPosition; | 
					
						
							| 
									
										
										
										
											2021-06-29 16:52:58 +08:00
										 |  |  | 					result.push( | 
					
						
							|  |  |  | 						Buffer.from( | 
					
						
							|  |  |  | 							contentItem.buffer, | 
					
						
							| 
									
										
										
										
											2021-06-29 19:02:57 +08:00
										 |  |  | 							contentItem.byteOffset + contentPosition, | 
					
						
							|  |  |  | 							l | 
					
						
							| 
									
										
										
										
											2021-06-29 16:52:58 +08:00
										 |  |  | 						) | 
					
						
							|  |  |  | 					); | 
					
						
							| 
									
										
										
										
											2021-06-29 19:02:57 +08:00
										 |  |  | 					length -= l; | 
					
						
							| 
									
										
										
										
											2020-12-12 20:32:58 +08:00
										 |  |  | 					contentPosition = contentItemLength; | 
					
						
							|  |  |  | 				} | 
					
						
							|  |  |  | 			} else { | 
					
						
							|  |  |  | 				if (length >= contentItemLength) { | 
					
						
							|  |  |  | 					result.push(contentItem); | 
					
						
							|  |  |  | 					length -= contentItemLength; | 
					
						
							|  |  |  | 					contentPosition = contentItemLength; | 
					
						
							|  |  |  | 				} else { | 
					
						
							| 
									
										
										
										
											2021-06-29 19:02:57 +08:00
										 |  |  | 					result.push( | 
					
						
							|  |  |  | 						Buffer.from(contentItem.buffer, contentItem.byteOffset, length) | 
					
						
							|  |  |  | 					); | 
					
						
							| 
									
										
										
										
											2020-12-12 20:32:58 +08:00
										 |  |  | 					contentPosition += length; | 
					
						
							|  |  |  | 					length = 0; | 
					
						
							|  |  |  | 				} | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 			while (length > 0) { | 
					
						
							|  |  |  | 				nextContent(); | 
					
						
							|  |  |  | 				if (length >= contentItemLength) { | 
					
						
							|  |  |  | 					result.push(contentItem); | 
					
						
							|  |  |  | 					length -= contentItemLength; | 
					
						
							|  |  |  | 					contentPosition = contentItemLength; | 
					
						
							|  |  |  | 				} else { | 
					
						
							| 
									
										
										
										
											2021-06-29 19:02:57 +08:00
										 |  |  | 					result.push( | 
					
						
							|  |  |  | 						Buffer.from(contentItem.buffer, contentItem.byteOffset, length) | 
					
						
							|  |  |  | 					); | 
					
						
							| 
									
										
										
										
											2020-12-12 20:32:58 +08:00
										 |  |  | 					contentPosition += length; | 
					
						
							|  |  |  | 					length = 0; | 
					
						
							|  |  |  | 				} | 
					
						
							|  |  |  | 			} | 
					
						
							| 
									
										
										
										
											2019-01-15 00:31:33 +08:00
										 |  |  | 		} | 
					
						
							| 
									
										
										
										
											2020-12-12 20:32:58 +08:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2020-01-24 06:51:49 +08:00
										 |  |  | 	return result; | 
					
						
							| 
									
										
										
										
											2019-01-15 00:31:33 +08:00
										 |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-01-24 23:51:05 +08:00
										 |  |  | /** | 
					
						
							|  |  |  |  * @typedef {BufferSerializableType[]} DeserializedType | 
					
						
							| 
									
										
										
										
											2019-06-11 19:09:42 +08:00
										 |  |  |  * @typedef {true} SerializedType | 
					
						
							| 
									
										
										
										
											2019-01-24 23:51:05 +08:00
										 |  |  |  * @extends {SerializerMiddleware<DeserializedType, SerializedType>} | 
					
						
							|  |  |  |  */ | 
					
						
							| 
									
										
										
										
											2018-10-09 20:30:59 +08:00
										 |  |  | class FileMiddleware extends SerializerMiddleware { | 
					
						
							| 
									
										
										
										
											2020-01-24 06:51:49 +08:00
										 |  |  | 	/** | 
					
						
							|  |  |  | 	 * @param {IntermediateFileSystem} fs filesystem | 
					
						
							| 
									
										
										
										
											2021-09-22 18:12:46 +08:00
										 |  |  | 	 * @param {string | Hash} hashFunction hash function to use | 
					
						
							| 
									
										
										
										
											2020-01-24 06:51:49 +08:00
										 |  |  | 	 */ | 
					
						
							| 
									
										
										
										
											2021-09-22 18:12:46 +08:00
										 |  |  | 	constructor(fs, hashFunction = "md4") { | 
					
						
							| 
									
										
										
										
											2019-06-11 19:09:42 +08:00
										 |  |  | 		super(); | 
					
						
							|  |  |  | 		this.fs = fs; | 
					
						
							| 
									
										
										
										
											2021-09-22 18:12:46 +08:00
										 |  |  | 		this._hashFunction = hashFunction; | 
					
						
							| 
									
										
										
										
											2019-06-11 19:09:42 +08:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2018-10-09 20:30:59 +08:00
										 |  |  | 	/** | 
					
						
							| 
									
										
										
										
											2019-01-24 23:51:05 +08:00
										 |  |  | 	 * @param {DeserializedType} data data | 
					
						
							|  |  |  | 	 * @param {Object} context context object | 
					
						
							|  |  |  | 	 * @returns {SerializedType|Promise<SerializedType>} serialized data | 
					
						
							| 
									
										
										
										
											2018-10-09 20:30:59 +08:00
										 |  |  | 	 */ | 
					
						
							| 
									
										
										
										
											2020-11-26 18:13:17 +08:00
										 |  |  | 	serialize(data, context) { | 
					
						
							|  |  |  | 		const { filename, extension = "" } = context; | 
					
						
							| 
									
										
										
										
											2020-01-24 06:51:49 +08:00
										 |  |  | 		return new Promise((resolve, reject) => { | 
					
						
							|  |  |  | 			mkdirp(this.fs, dirname(this.fs, filename), err => { | 
					
						
							|  |  |  | 				if (err) return reject(err); | 
					
						
							| 
									
										
										
										
											2018-10-09 20:30:59 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-01-24 06:51:49 +08:00
										 |  |  | 				// It's important that we don't touch existing files during serialization
 | 
					
						
							|  |  |  | 				// because serialize may read existing files (when deserializing)
 | 
					
						
							|  |  |  | 				const allWrittenFiles = new Set(); | 
					
						
							|  |  |  | 				const writeFile = async (name, content) => { | 
					
						
							|  |  |  | 					const file = name | 
					
						
							|  |  |  | 						? join(this.fs, filename, `../${name}${extension}`) | 
					
						
							|  |  |  | 						: filename; | 
					
						
							|  |  |  | 					await new Promise((resolve, reject) => { | 
					
						
							| 
									
										
										
										
											2021-06-30 06:05:38 +08:00
										 |  |  | 						let stream = this.fs.createWriteStream(file + "_"); | 
					
						
							|  |  |  | 						let compression; | 
					
						
							|  |  |  | 						if (file.endsWith(".gz")) { | 
					
						
							|  |  |  | 							compression = createGzip({ | 
					
						
							|  |  |  | 								chunkSize: COMPRESSION_CHUNK_SIZE, | 
					
						
							|  |  |  | 								level: zConstants.Z_BEST_SPEED | 
					
						
							|  |  |  | 							}); | 
					
						
							|  |  |  | 						} else if (file.endsWith(".br")) { | 
					
						
							|  |  |  | 							compression = createBrotliCompress({ | 
					
						
							|  |  |  | 								chunkSize: COMPRESSION_CHUNK_SIZE, | 
					
						
							|  |  |  | 								params: { | 
					
						
							|  |  |  | 									[zConstants.BROTLI_PARAM_MODE]: zConstants.BROTLI_MODE_TEXT, | 
					
						
							|  |  |  | 									[zConstants.BROTLI_PARAM_QUALITY]: 2, | 
					
						
							|  |  |  | 									[zConstants.BROTLI_PARAM_DISABLE_LITERAL_CONTEXT_MODELING]: true, | 
					
						
							|  |  |  | 									[zConstants.BROTLI_PARAM_SIZE_HINT]: content.reduce( | 
					
						
							|  |  |  | 										(size, b) => size + b.length, | 
					
						
							|  |  |  | 										0 | 
					
						
							|  |  |  | 									) | 
					
						
							|  |  |  | 								} | 
					
						
							|  |  |  | 							}); | 
					
						
							|  |  |  | 						} | 
					
						
							|  |  |  | 						if (compression) { | 
					
						
							|  |  |  | 							pipeline(compression, stream, reject); | 
					
						
							|  |  |  | 							stream = compression; | 
					
						
							|  |  |  | 							stream.on("finish", () => resolve()); | 
					
						
							|  |  |  | 						} else { | 
					
						
							|  |  |  | 							stream.on("error", err => reject(err)); | 
					
						
							|  |  |  | 							stream.on("finish", () => resolve()); | 
					
						
							|  |  |  | 						} | 
					
						
							| 
									
										
										
										
											2020-01-24 06:51:49 +08:00
										 |  |  | 						for (const b of content) stream.write(b); | 
					
						
							|  |  |  | 						stream.end(); | 
					
						
							|  |  |  | 					}); | 
					
						
							|  |  |  | 					if (name) allWrittenFiles.add(file); | 
					
						
							|  |  |  | 				}; | 
					
						
							| 
									
										
										
										
											2018-10-09 20:30:59 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-01-24 06:51:49 +08:00
										 |  |  | 				resolve( | 
					
						
							| 
									
										
										
										
											2021-09-22 18:12:46 +08:00
										 |  |  | 					serialize(this, data, false, writeFile, this._hashFunction).then( | 
					
						
							| 
									
										
										
										
											2020-01-24 06:51:49 +08:00
										 |  |  | 						async ({ backgroundJob }) => { | 
					
						
							|  |  |  | 							await backgroundJob; | 
					
						
							| 
									
										
										
										
											2018-10-09 20:30:59 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-01-24 06:51:49 +08:00
										 |  |  | 							// Rename the index file to disallow access during inconsistent file state
 | 
					
						
							|  |  |  | 							await new Promise(resolve => | 
					
						
							|  |  |  | 								this.fs.rename(filename, filename + ".old", err => { | 
					
						
							|  |  |  | 									resolve(); | 
					
						
							|  |  |  | 								}) | 
					
						
							|  |  |  | 							); | 
					
						
							| 
									
										
										
										
											2018-10-09 20:30:59 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-01-24 06:51:49 +08:00
										 |  |  | 							// update all written files
 | 
					
						
							|  |  |  | 							await Promise.all( | 
					
						
							|  |  |  | 								Array.from( | 
					
						
							|  |  |  | 									allWrittenFiles, | 
					
						
							|  |  |  | 									file => | 
					
						
							|  |  |  | 										new Promise((resolve, reject) => { | 
					
						
							|  |  |  | 											this.fs.rename(file + "_", file, err => { | 
					
						
							|  |  |  | 												if (err) return reject(err); | 
					
						
							|  |  |  | 												resolve(); | 
					
						
							|  |  |  | 											}); | 
					
						
							|  |  |  | 										}) | 
					
						
							|  |  |  | 								) | 
					
						
							|  |  |  | 							); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-03-13 00:51:26 +08:00
										 |  |  | 							// As final step automatically update the index file to have a consistent pack again
 | 
					
						
							| 
									
										
										
										
											2020-01-24 06:51:49 +08:00
										 |  |  | 							await new Promise(resolve => { | 
					
						
							|  |  |  | 								this.fs.rename(filename + "_", filename, err => { | 
					
						
							|  |  |  | 									if (err) return reject(err); | 
					
						
							|  |  |  | 									resolve(); | 
					
						
							|  |  |  | 								}); | 
					
						
							|  |  |  | 							}); | 
					
						
							|  |  |  | 							return /** @type {true} */ (true); | 
					
						
							|  |  |  | 						} | 
					
						
							|  |  |  | 					) | 
					
						
							|  |  |  | 				); | 
					
						
							| 
									
										
										
										
											2018-10-09 20:30:59 +08:00
										 |  |  | 			}); | 
					
						
							|  |  |  | 		}); | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/** | 
					
						
							| 
									
										
										
										
											2019-01-24 23:51:05 +08:00
										 |  |  | 	 * @param {SerializedType} data data | 
					
						
							|  |  |  | 	 * @param {Object} context context object | 
					
						
							|  |  |  | 	 * @returns {DeserializedType|Promise<DeserializedType>} deserialized data | 
					
						
							| 
									
										
										
										
											2018-10-09 20:30:59 +08:00
										 |  |  | 	 */ | 
					
						
							| 
									
										
										
										
											2020-11-26 18:13:17 +08:00
										 |  |  | 	deserialize(data, context) { | 
					
						
							|  |  |  | 		const { filename, extension = "" } = context; | 
					
						
							| 
									
										
										
										
											2020-01-24 06:51:49 +08:00
										 |  |  | 		const readFile = name => | 
					
						
							|  |  |  | 			new Promise((resolve, reject) => { | 
					
						
							|  |  |  | 				const file = name | 
					
						
							|  |  |  | 					? join(this.fs, filename, `../${name}${extension}`) | 
					
						
							|  |  |  | 					: filename; | 
					
						
							| 
									
										
										
										
											2020-12-14 18:06:15 +08:00
										 |  |  | 				this.fs.stat(file, (err, stats) => { | 
					
						
							|  |  |  | 					if (err) { | 
					
						
							|  |  |  | 						reject(err); | 
					
						
							|  |  |  | 						return; | 
					
						
							|  |  |  | 					} | 
					
						
							| 
									
										
										
										
											2021-01-13 07:09:19 +08:00
										 |  |  | 					let remaining = /** @type {number} */ (stats.size); | 
					
						
							| 
									
										
										
										
											2020-12-14 20:16:20 +08:00
										 |  |  | 					let currentBuffer; | 
					
						
							|  |  |  | 					let currentBufferUsed; | 
					
						
							| 
									
										
										
										
											2020-12-14 18:06:15 +08:00
										 |  |  | 					const buf = []; | 
					
						
							| 
									
										
										
										
											2021-06-30 06:05:38 +08:00
										 |  |  | 					let decompression; | 
					
						
							|  |  |  | 					if (file.endsWith(".gz")) { | 
					
						
							|  |  |  | 						decompression = createGunzip({ | 
					
						
							|  |  |  | 							chunkSize: DECOMPRESSION_CHUNK_SIZE | 
					
						
							|  |  |  | 						}); | 
					
						
							|  |  |  | 					} else if (file.endsWith(".br")) { | 
					
						
							|  |  |  | 						decompression = createBrotliDecompress({ | 
					
						
							|  |  |  | 							chunkSize: DECOMPRESSION_CHUNK_SIZE | 
					
						
							|  |  |  | 						}); | 
					
						
							|  |  |  | 					} | 
					
						
							|  |  |  | 					if (decompression) { | 
					
						
							|  |  |  | 						let newResolve, newReject; | 
					
						
							|  |  |  | 						resolve( | 
					
						
							|  |  |  | 							Promise.all([ | 
					
						
							|  |  |  | 								new Promise((rs, rj) => { | 
					
						
							|  |  |  | 									newResolve = rs; | 
					
						
							|  |  |  | 									newReject = rj; | 
					
						
							|  |  |  | 								}), | 
					
						
							|  |  |  | 								new Promise((resolve, reject) => { | 
					
						
							|  |  |  | 									decompression.on("data", chunk => buf.push(chunk)); | 
					
						
							|  |  |  | 									decompression.on("end", () => resolve()); | 
					
						
							|  |  |  | 									decompression.on("error", err => reject(err)); | 
					
						
							|  |  |  | 								}) | 
					
						
							|  |  |  | 							]).then(() => buf) | 
					
						
							|  |  |  | 						); | 
					
						
							|  |  |  | 						resolve = newResolve; | 
					
						
							|  |  |  | 						reject = newReject; | 
					
						
							|  |  |  | 					} | 
					
						
							| 
									
										
										
										
											2020-12-14 20:16:20 +08:00
										 |  |  | 					this.fs.open(file, "r", (err, fd) => { | 
					
						
							|  |  |  | 						if (err) { | 
					
						
							|  |  |  | 							reject(err); | 
					
						
							|  |  |  | 							return; | 
					
						
							|  |  |  | 						} | 
					
						
							|  |  |  | 						const read = () => { | 
					
						
							|  |  |  | 							if (currentBuffer === undefined) { | 
					
						
							|  |  |  | 								currentBuffer = Buffer.allocUnsafeSlow( | 
					
						
							| 
									
										
										
										
											2021-06-30 06:05:38 +08:00
										 |  |  | 									Math.min( | 
					
						
							|  |  |  | 										constants.MAX_LENGTH, | 
					
						
							|  |  |  | 										remaining, | 
					
						
							|  |  |  | 										decompression ? DECOMPRESSION_CHUNK_SIZE : Infinity | 
					
						
							|  |  |  | 									) | 
					
						
							| 
									
										
										
										
											2020-12-14 20:16:20 +08:00
										 |  |  | 								); | 
					
						
							|  |  |  | 								currentBufferUsed = 0; | 
					
						
							|  |  |  | 							} | 
					
						
							|  |  |  | 							let readBuffer = currentBuffer; | 
					
						
							|  |  |  | 							let readOffset = currentBufferUsed; | 
					
						
							|  |  |  | 							let readLength = currentBuffer.length - currentBufferUsed; | 
					
						
							| 
									
										
										
										
											2021-06-29 16:52:58 +08:00
										 |  |  | 							// values passed to fs.read must be valid int32 values
 | 
					
						
							| 
									
										
										
										
											2020-12-14 20:16:20 +08:00
										 |  |  | 							if (readOffset > 0x7fffffff) { | 
					
						
							|  |  |  | 								readBuffer = currentBuffer.slice(readOffset); | 
					
						
							|  |  |  | 								readOffset = 0; | 
					
						
							|  |  |  | 							} | 
					
						
							|  |  |  | 							if (readLength > 0x7fffffff) { | 
					
						
							|  |  |  | 								readLength = 0x7fffffff; | 
					
						
							|  |  |  | 							} | 
					
						
							|  |  |  | 							this.fs.read( | 
					
						
							|  |  |  | 								fd, | 
					
						
							|  |  |  | 								readBuffer, | 
					
						
							|  |  |  | 								readOffset, | 
					
						
							|  |  |  | 								readLength, | 
					
						
							|  |  |  | 								null, | 
					
						
							|  |  |  | 								(err, bytesRead) => { | 
					
						
							|  |  |  | 									if (err) { | 
					
						
							|  |  |  | 										this.fs.close(fd, () => { | 
					
						
							|  |  |  | 											reject(err); | 
					
						
							|  |  |  | 										}); | 
					
						
							|  |  |  | 										return; | 
					
						
							|  |  |  | 									} | 
					
						
							|  |  |  | 									currentBufferUsed += bytesRead; | 
					
						
							|  |  |  | 									remaining -= bytesRead; | 
					
						
							|  |  |  | 									if (currentBufferUsed === currentBuffer.length) { | 
					
						
							| 
									
										
										
										
											2021-06-30 06:05:38 +08:00
										 |  |  | 										if (decompression) { | 
					
						
							|  |  |  | 											decompression.write(currentBuffer); | 
					
						
							|  |  |  | 										} else { | 
					
						
							|  |  |  | 											buf.push(currentBuffer); | 
					
						
							|  |  |  | 										} | 
					
						
							| 
									
										
										
										
											2020-12-14 20:16:20 +08:00
										 |  |  | 										currentBuffer = undefined; | 
					
						
							|  |  |  | 										if (remaining === 0) { | 
					
						
							| 
									
										
										
										
											2021-06-30 06:05:38 +08:00
										 |  |  | 											if (decompression) { | 
					
						
							|  |  |  | 												decompression.end(); | 
					
						
							|  |  |  | 											} | 
					
						
							| 
									
										
										
										
											2020-12-14 20:16:20 +08:00
										 |  |  | 											this.fs.close(fd, err => { | 
					
						
							|  |  |  | 												if (err) { | 
					
						
							|  |  |  | 													reject(err); | 
					
						
							|  |  |  | 													return; | 
					
						
							|  |  |  | 												} | 
					
						
							|  |  |  | 												resolve(buf); | 
					
						
							|  |  |  | 											}); | 
					
						
							|  |  |  | 											return; | 
					
						
							|  |  |  | 										} | 
					
						
							|  |  |  | 									} | 
					
						
							|  |  |  | 									read(); | 
					
						
							|  |  |  | 								} | 
					
						
							|  |  |  | 							); | 
					
						
							|  |  |  | 						}; | 
					
						
							|  |  |  | 						read(); | 
					
						
							| 
									
										
										
										
											2020-12-14 18:06:15 +08:00
										 |  |  | 					}); | 
					
						
							| 
									
										
										
										
											2020-01-24 06:51:49 +08:00
										 |  |  | 				}); | 
					
						
							|  |  |  | 			}); | 
					
						
							|  |  |  | 		return deserialize(this, false, readFile); | 
					
						
							| 
									
										
										
										
											2018-10-09 20:30:59 +08:00
										 |  |  | 	} | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | module.exports = FileMiddleware; |