| 
									
										
										
										
											2018-10-09 20:30:59 +08:00
										 |  |  | /* | 
					
						
							|  |  |  | 	MIT License http://www.opensource.org/licenses/mit-license.php
 | 
					
						
							|  |  |  | */ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | "use strict"; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | const fs = require("fs"); | 
					
						
							|  |  |  | const mkdirp = require("mkdirp"); | 
					
						
							|  |  |  | const path = require("path"); | 
					
						
							| 
									
										
										
										
											2018-12-21 19:02:37 +08:00
										 |  |  | const Queue = require("../util/Queue"); | 
					
						
							|  |  |  | const memorize = require("../util/memorize"); | 
					
						
							| 
									
										
										
										
											2018-10-09 20:30:59 +08:00
										 |  |  | const SerializerMiddleware = require("./SerializerMiddleware"); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-01-24 23:51:05 +08:00
										 |  |  | /** @typedef {import("./types").BufferSerializableType} BufferSerializableType */ | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-10-09 20:30:59 +08:00
										 |  |  | class Section { | 
					
						
							|  |  |  | 	constructor(items) { | 
					
						
							|  |  |  | 		this.items = items; | 
					
						
							|  |  |  | 		this.parts = undefined; | 
					
						
							|  |  |  | 		this.length = NaN; | 
					
						
							|  |  |  | 		this.offset = NaN; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	resolve() { | 
					
						
							|  |  |  | 		let hasPromise = false; | 
					
						
							|  |  |  | 		let lastPart = undefined; | 
					
						
							|  |  |  | 		const parts = []; | 
					
						
							|  |  |  | 		let length = 0; | 
					
						
							|  |  |  | 		for (const item of this.items) { | 
					
						
							|  |  |  | 			if (typeof item === "function") { | 
					
						
							|  |  |  | 				const r = item(); | 
					
						
							|  |  |  | 				if (r instanceof Promise) { | 
					
						
							|  |  |  | 					parts.push(r.then(items => new Section(items).resolve())); | 
					
						
							|  |  |  | 					hasPromise = true; | 
					
						
							| 
									
										
										
										
											2018-10-18 21:52:22 +08:00
										 |  |  | 				} else if (r) { | 
					
						
							| 
									
										
										
										
											2018-10-09 20:30:59 +08:00
										 |  |  | 					parts.push(new Section(r).resolve()); | 
					
						
							| 
									
										
										
										
											2018-10-18 21:52:22 +08:00
										 |  |  | 				} else { | 
					
						
							|  |  |  | 					return null; | 
					
						
							| 
									
										
										
										
											2018-10-09 20:30:59 +08:00
										 |  |  | 				} | 
					
						
							|  |  |  | 				length += 12; // 0, offset, size
 | 
					
						
							|  |  |  | 				lastPart = undefined; | 
					
						
							|  |  |  | 			} else if (lastPart) { | 
					
						
							|  |  |  | 				lastPart.push(item); | 
					
						
							|  |  |  | 				length += item.length; | 
					
						
							|  |  |  | 			} else { | 
					
						
							|  |  |  | 				length += 4; // size
 | 
					
						
							|  |  |  | 				length += item.length; | 
					
						
							|  |  |  | 				lastPart = [item]; | 
					
						
							|  |  |  | 				parts.push(lastPart); | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		this.length = length; | 
					
						
							|  |  |  | 		if (hasPromise) { | 
					
						
							|  |  |  | 			return Promise.all(parts).then(parts => { | 
					
						
							|  |  |  | 				this.parts = parts; | 
					
						
							| 
									
										
										
										
											2018-10-18 21:52:22 +08:00
										 |  |  | 				if (!parts.every(Boolean)) return null; | 
					
						
							| 
									
										
										
										
											2018-10-09 20:30:59 +08:00
										 |  |  | 				return this; | 
					
						
							|  |  |  | 			}); | 
					
						
							|  |  |  | 		} else { | 
					
						
							|  |  |  | 			this.parts = parts; | 
					
						
							|  |  |  | 			return this; | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	getSections() { | 
					
						
							|  |  |  | 		return this.parts.filter(p => p instanceof Section); | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	emit(out) { | 
					
						
							|  |  |  | 		for (const part of this.parts) { | 
					
						
							|  |  |  | 			if (part instanceof Section) { | 
					
						
							| 
									
										
										
										
											2019-01-21 19:45:56 +08:00
										 |  |  | 				const pointerBuf = Buffer.allocUnsafe(12); | 
					
						
							| 
									
										
										
										
											2018-10-09 20:30:59 +08:00
										 |  |  | 				pointerBuf.writeUInt32LE(0, 0); | 
					
						
							|  |  |  | 				pointerBuf.writeUInt32LE(part.offset, 4); | 
					
						
							|  |  |  | 				pointerBuf.writeUInt32LE(part.length, 8); | 
					
						
							|  |  |  | 				out.push(pointerBuf); | 
					
						
							|  |  |  | 			} else { | 
					
						
							| 
									
										
										
										
											2019-01-21 19:45:56 +08:00
										 |  |  | 				const sizeBuf = Buffer.allocUnsafe(4); | 
					
						
							| 
									
										
										
										
											2018-10-09 20:30:59 +08:00
										 |  |  | 				out.push(sizeBuf); | 
					
						
							|  |  |  | 				let len = 0; | 
					
						
							|  |  |  | 				for (const buf of part) { | 
					
						
							|  |  |  | 					len += buf.length; | 
					
						
							|  |  |  | 					out.push(buf); | 
					
						
							|  |  |  | 				} | 
					
						
							|  |  |  | 				sizeBuf.writeUInt32LE(len, 0); | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-12-21 19:02:37 +08:00
										 |  |  | /** | 
					
						
							|  |  |  |  * @typedef {Object} FileJob | 
					
						
							|  |  |  |  * @property {boolean} write | 
					
						
							|  |  |  |  * @property {(fileHandle: number, callback: (err: Error?) => void) => void} fn | 
					
						
							|  |  |  |  * @property {(err: Error?) => void} errorHandler | 
					
						
							|  |  |  |  */ | 
					
						
							| 
									
										
										
										
											2018-10-09 20:30:59 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-12-21 19:02:37 +08:00
										 |  |  | class FileManager { | 
					
						
							|  |  |  | 	constructor() { | 
					
						
							|  |  |  | 		/** @type {Map<string, Queue<FileJob>>} */ | 
					
						
							|  |  |  | 		this.jobs = new Map(); | 
					
						
							|  |  |  | 		this.processing = new Map(); | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2018-10-09 20:30:59 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-12-21 19:02:37 +08:00
										 |  |  | 	addJob(filename, write, fn, errorHandler) { | 
					
						
							|  |  |  | 		let queue = this.jobs.get(filename); | 
					
						
							|  |  |  | 		let start = false; | 
					
						
							|  |  |  | 		if (queue === undefined) { | 
					
						
							|  |  |  | 			queue = new Queue(); | 
					
						
							|  |  |  | 			this.jobs.set(filename, queue); | 
					
						
							|  |  |  | 			start = true; | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		queue.enqueue({ | 
					
						
							|  |  |  | 			write, | 
					
						
							|  |  |  | 			fn, | 
					
						
							|  |  |  | 			errorHandler | 
					
						
							|  |  |  | 		}); | 
					
						
							|  |  |  | 		if (start) { | 
					
						
							|  |  |  | 			this._startProcessing(filename, queue); | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/** | 
					
						
							|  |  |  | 	 * @param {string} filename the filename | 
					
						
							|  |  |  | 	 * @param {Queue<FileJob>} queue the job queue | 
					
						
							|  |  |  | 	 * @returns {void} | 
					
						
							|  |  |  | 	 */ | 
					
						
							|  |  |  | 	_startProcessing(filename, queue) { | 
					
						
							|  |  |  | 		let fileHandle; | 
					
						
							|  |  |  | 		let write = false; | 
					
						
							|  |  |  | 		/** @type {FileJob | undefined} */ | 
					
						
							|  |  |  | 		let currentJob = undefined; | 
					
						
							|  |  |  | 		/** | 
					
						
							|  |  |  | 		 * Pull the next job from the queue, and process it | 
					
						
							|  |  |  | 		 * When queue empty and file open, close it | 
					
						
							|  |  |  | 		 * When queue (still) empty and file closed, exit processing | 
					
						
							|  |  |  | 		 * @returns {void} | 
					
						
							|  |  |  | 		 */ | 
					
						
							|  |  |  | 		const next = () => { | 
					
						
							|  |  |  | 			if (queue.length === 0) { | 
					
						
							|  |  |  | 				if (fileHandle !== undefined) { | 
					
						
							|  |  |  | 					closeFile(next); | 
					
						
							|  |  |  | 				} else { | 
					
						
							|  |  |  | 					this.jobs.delete(filename); | 
					
						
							|  |  |  | 					this.processing.delete(filename); | 
					
						
							|  |  |  | 				} | 
					
						
							|  |  |  | 				return; | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 			currentJob = queue.dequeue(); | 
					
						
							|  |  |  | 			// If file is already open but in the wrong mode
 | 
					
						
							|  |  |  | 			// close it and open it the other way
 | 
					
						
							|  |  |  | 			if (fileHandle !== undefined && write !== currentJob.write) { | 
					
						
							|  |  |  | 				closeFile(openFile); | 
					
						
							|  |  |  | 			} else { | 
					
						
							|  |  |  | 				openFile(); | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 		}; | 
					
						
							|  |  |  | 		/** | 
					
						
							|  |  |  | 		 * Close the file and continue with the passed next step | 
					
						
							|  |  |  | 		 * @param {function(): void} next next step | 
					
						
							|  |  |  | 		 * @returns {void} | 
					
						
							|  |  |  | 		 */ | 
					
						
							|  |  |  | 		const closeFile = next => { | 
					
						
							|  |  |  | 			fs.close(fileHandle, err => { | 
					
						
							|  |  |  | 				if (err) return handleError(err); | 
					
						
							|  |  |  | 				fileHandle = undefined; | 
					
						
							|  |  |  | 				next(); | 
					
						
							|  |  |  | 			}); | 
					
						
							|  |  |  | 		}; | 
					
						
							|  |  |  | 		/** | 
					
						
							|  |  |  | 		 * Open the file if needed and continue with job processing | 
					
						
							|  |  |  | 		 * @returns {void} | 
					
						
							|  |  |  | 		 */ | 
					
						
							|  |  |  | 		const openFile = () => { | 
					
						
							|  |  |  | 			if (fileHandle === undefined) { | 
					
						
							|  |  |  | 				write = currentJob.write; | 
					
						
							|  |  |  | 				fs.open(filename, write ? "w" : "r", (err, file) => { | 
					
						
							|  |  |  | 					if (err) return handleError(err); | 
					
						
							|  |  |  | 					fileHandle = file; | 
					
						
							|  |  |  | 					process(); | 
					
						
							| 
									
										
										
										
											2018-10-09 20:30:59 +08:00
										 |  |  | 				}); | 
					
						
							| 
									
										
										
										
											2018-12-21 19:02:37 +08:00
										 |  |  | 			} else { | 
					
						
							|  |  |  | 				process(); | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 		}; | 
					
						
							|  |  |  | 		/** | 
					
						
							|  |  |  | 		 * Process the job function and continue with the next job | 
					
						
							|  |  |  | 		 * @returns {void} | 
					
						
							|  |  |  | 		 */ | 
					
						
							|  |  |  | 		const process = () => { | 
					
						
							|  |  |  | 			currentJob.fn(fileHandle, err => { | 
					
						
							|  |  |  | 				if (err) return handleError(err); | 
					
						
							|  |  |  | 				currentJob = undefined; | 
					
						
							|  |  |  | 				next(); | 
					
						
							| 
									
										
										
										
											2018-10-09 20:30:59 +08:00
										 |  |  | 			}); | 
					
						
							| 
									
										
										
										
											2018-12-21 19:02:37 +08:00
										 |  |  | 		}; | 
					
						
							|  |  |  | 		/** | 
					
						
							|  |  |  | 		 * Handle any error, continue with the next job | 
					
						
							|  |  |  | 		 * @param {Error} err occured error | 
					
						
							|  |  |  | 		 * @returns {void} | 
					
						
							|  |  |  | 		 */ | 
					
						
							|  |  |  | 		const handleError = err => { | 
					
						
							|  |  |  | 			if (currentJob !== undefined) { | 
					
						
							|  |  |  | 				currentJob.errorHandler(err); | 
					
						
							|  |  |  | 			} else { | 
					
						
							|  |  |  | 				console.error(`Error in FileManager: ${err.message}`); | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 			next(); | 
					
						
							|  |  |  | 		}; | 
					
						
							|  |  |  | 		next(); | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | const fileManager = new FileManager(); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | const createPointer = (filename, offset, size) => { | 
					
						
							|  |  |  | 	return memorize(() => { | 
					
						
							|  |  |  | 		return new Promise((resolve, reject) => { | 
					
						
							|  |  |  | 			fileManager.addJob( | 
					
						
							|  |  |  | 				filename, | 
					
						
							|  |  |  | 				false, | 
					
						
							|  |  |  | 				(file, callback) => { | 
					
						
							|  |  |  | 					readSection(filename, file, offset, size, (err, parts) => { | 
					
						
							|  |  |  | 						if (err) return callback(err); | 
					
						
							|  |  |  | 						resolve(parts); | 
					
						
							|  |  |  | 						callback(); | 
					
						
							|  |  |  | 					}); | 
					
						
							|  |  |  | 				}, | 
					
						
							|  |  |  | 				reject | 
					
						
							|  |  |  | 			); | 
					
						
							| 
									
										
										
										
											2018-10-09 20:30:59 +08:00
										 |  |  | 		}); | 
					
						
							| 
									
										
										
										
											2018-12-21 19:02:37 +08:00
										 |  |  | 	}); | 
					
						
							| 
									
										
										
										
											2018-10-09 20:30:59 +08:00
										 |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-01-19 18:49:30 +08:00
										 |  |  | const readFileSectionToBuffer = (fd, buffer, offset, position, callback) => { | 
					
						
							|  |  |  | 	const remaining = buffer.length - offset; | 
					
						
							|  |  |  | 	fs.read(fd, buffer, offset, remaining, position, (err, bytesRead) => { | 
					
						
							|  |  |  | 		if (err) return callback(err); | 
					
						
							|  |  |  | 		if (bytesRead === 0) { | 
					
						
							|  |  |  | 			return callback( | 
					
						
							|  |  |  | 				new Error( | 
					
						
							|  |  |  | 					`Unexpected end of file (${remaining} bytes missing at pos ${position}` | 
					
						
							|  |  |  | 				) | 
					
						
							|  |  |  | 			); | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		if (bytesRead < remaining) { | 
					
						
							|  |  |  | 			return readFileSectionToBuffer( | 
					
						
							|  |  |  | 				fd, | 
					
						
							|  |  |  | 				buffer, | 
					
						
							|  |  |  | 				offset + bytesRead, | 
					
						
							|  |  |  | 				position + bytesRead, | 
					
						
							|  |  |  | 				callback | 
					
						
							|  |  |  | 			); | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		return callback(); | 
					
						
							|  |  |  | 	}); | 
					
						
							|  |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-10-09 20:30:59 +08:00
										 |  |  | const readSection = (filename, file, offset, size, callback) => { | 
					
						
							| 
									
										
										
										
											2019-01-21 19:45:56 +08:00
										 |  |  | 	const buffer = Buffer.allocUnsafe(size); | 
					
						
							| 
									
										
										
										
											2019-01-19 18:49:30 +08:00
										 |  |  | 	readFileSectionToBuffer(file, buffer, 0, offset, err => { | 
					
						
							| 
									
										
										
										
											2018-10-09 20:30:59 +08:00
										 |  |  | 		if (err) return callback(err); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		const result = []; | 
					
						
							|  |  |  | 		let pos = 0; | 
					
						
							|  |  |  | 		while (pos < buffer.length) { | 
					
						
							|  |  |  | 			const len = buffer.readUInt32LE(pos); | 
					
						
							|  |  |  | 			pos += 4; | 
					
						
							|  |  |  | 			if (len === 0) { | 
					
						
							|  |  |  | 				const pOffset = buffer.readUInt32LE(pos); | 
					
						
							|  |  |  | 				pos += 4; | 
					
						
							|  |  |  | 				const pSize = buffer.readUInt32LE(pos); | 
					
						
							|  |  |  | 				pos += 4; | 
					
						
							|  |  |  | 				result.push(createPointer(filename, pOffset, pSize)); | 
					
						
							|  |  |  | 			} else { | 
					
						
							|  |  |  | 				const buf = buffer.slice(pos, pos + len); | 
					
						
							|  |  |  | 				pos += len; | 
					
						
							|  |  |  | 				result.push(buf); | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		callback(null, result); | 
					
						
							|  |  |  | 	}); | 
					
						
							|  |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-01-15 00:31:33 +08:00
										 |  |  | const writeBuffers = (fileHandle, buffers, callback) => { | 
					
						
							|  |  |  | 	const stream = fs.createWriteStream(null, { | 
					
						
							|  |  |  | 		fd: fileHandle, | 
					
						
							|  |  |  | 		autoClose: false | 
					
						
							|  |  |  | 	}); | 
					
						
							|  |  |  | 	let index = 0; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	const doWrite = function() { | 
					
						
							|  |  |  | 		let canWriteMore = true; | 
					
						
							|  |  |  | 		while (canWriteMore && index < buffers.length) { | 
					
						
							|  |  |  | 			const chunk = buffers[index++]; | 
					
						
							|  |  |  | 			canWriteMore = stream.write(chunk); | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		if (index < buffers.length) { | 
					
						
							|  |  |  | 			stream.once("drain", doWrite); | 
					
						
							|  |  |  | 		} else { | 
					
						
							|  |  |  | 			stream.end(); | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	}; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	stream.on("error", err => callback(err)); | 
					
						
							|  |  |  | 	stream.on("finish", () => callback(null)); | 
					
						
							|  |  |  | 	doWrite(); | 
					
						
							|  |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-01-24 23:51:05 +08:00
										 |  |  | /** | 
					
						
							|  |  |  |  * @typedef {BufferSerializableType[]} DeserializedType | 
					
						
							|  |  |  |  * @typedef {undefined} SerializedType | 
					
						
							|  |  |  |  * @extends {SerializerMiddleware<DeserializedType, SerializedType>} | 
					
						
							|  |  |  |  */ | 
					
						
							| 
									
										
										
										
											2018-10-09 20:30:59 +08:00
										 |  |  | class FileMiddleware extends SerializerMiddleware { | 
					
						
							|  |  |  | 	/** | 
					
						
							| 
									
										
										
										
											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
										 |  |  | 	 */ | 
					
						
							|  |  |  | 	serialize(data, { filename }) { | 
					
						
							|  |  |  | 		const root = new Section(data); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		const r = root.resolve(); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-10-18 21:52:22 +08:00
										 |  |  | 		return Promise.resolve(r).then(root => { | 
					
						
							|  |  |  | 			if (!root) return null; | 
					
						
							| 
									
										
										
										
											2018-10-09 20:30:59 +08:00
										 |  |  | 			// calc positions in file
 | 
					
						
							|  |  |  | 			let currentOffset = 4; | 
					
						
							|  |  |  | 			const processOffsets = section => { | 
					
						
							|  |  |  | 				section.offset = currentOffset; | 
					
						
							|  |  |  | 				currentOffset += section.length; | 
					
						
							|  |  |  | 				for (const child of section.getSections()) { | 
					
						
							|  |  |  | 					processOffsets(child); | 
					
						
							|  |  |  | 				} | 
					
						
							|  |  |  | 			}; | 
					
						
							|  |  |  | 			processOffsets(root); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			// get buffers to write
 | 
					
						
							| 
									
										
										
										
											2019-01-21 19:45:56 +08:00
										 |  |  | 			const sizeBuf = Buffer.allocUnsafe(4); | 
					
						
							| 
									
										
										
										
											2018-10-09 20:30:59 +08:00
										 |  |  | 			sizeBuf.writeUInt32LE(root.length, 0); | 
					
						
							|  |  |  | 			const buffers = [sizeBuf]; | 
					
						
							|  |  |  | 			const emit = (section, out) => { | 
					
						
							|  |  |  | 				section.emit(out); | 
					
						
							|  |  |  | 				for (const child of section.getSections()) { | 
					
						
							|  |  |  | 					emit(child, out); | 
					
						
							|  |  |  | 				} | 
					
						
							|  |  |  | 			}; | 
					
						
							|  |  |  | 			emit(root, buffers); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			// write to file
 | 
					
						
							|  |  |  | 			return new Promise((resolve, reject) => { | 
					
						
							|  |  |  | 				mkdirp(path.dirname(filename), err => { | 
					
						
							|  |  |  | 					if (err) return reject(err); | 
					
						
							| 
									
										
										
										
											2018-12-21 19:02:37 +08:00
										 |  |  | 					fileManager.addJob(filename, true, (file, callback) => { | 
					
						
							| 
									
										
										
										
											2019-01-15 00:31:33 +08:00
										 |  |  | 						writeBuffers(file, buffers, err => { | 
					
						
							| 
									
										
										
										
											2018-12-21 19:02:37 +08:00
										 |  |  | 							if (err) return callback(err); | 
					
						
							|  |  |  | 							resolve(true); | 
					
						
							|  |  |  | 							callback(); | 
					
						
							|  |  |  | 						}); | 
					
						
							| 
									
										
										
										
											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
										 |  |  | 	 */ | 
					
						
							|  |  |  | 	deserialize(data, { filename }) { | 
					
						
							|  |  |  | 		return new Promise((resolve, reject) => { | 
					
						
							| 
									
										
										
										
											2018-12-21 19:02:37 +08:00
										 |  |  | 			fileManager.addJob( | 
					
						
							|  |  |  | 				filename, | 
					
						
							|  |  |  | 				false, | 
					
						
							|  |  |  | 				(file, callback) => { | 
					
						
							| 
									
										
										
										
											2019-01-21 19:45:56 +08:00
										 |  |  | 					const sizeBuf = Buffer.allocUnsafe(4); | 
					
						
							| 
									
										
										
										
											2019-01-19 18:49:30 +08:00
										 |  |  | 					readFileSectionToBuffer(file, sizeBuf, 0, 0, err => { | 
					
						
							| 
									
										
										
										
											2018-12-21 19:02:37 +08:00
										 |  |  | 						if (err) return callback(err); | 
					
						
							| 
									
										
										
										
											2018-10-09 20:30:59 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-12-21 19:02:37 +08:00
										 |  |  | 						const rootSize = sizeBuf.readUInt32LE(0); | 
					
						
							| 
									
										
										
										
											2018-10-09 20:30:59 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-12-21 19:02:37 +08:00
										 |  |  | 						readSection(filename, file, 4, rootSize, (err, parts) => { | 
					
						
							|  |  |  | 							if (err) return callback(err); | 
					
						
							| 
									
										
										
										
											2018-10-09 20:30:59 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | 							resolve(parts); | 
					
						
							| 
									
										
										
										
											2018-12-21 19:02:37 +08:00
										 |  |  | 							callback(); | 
					
						
							| 
									
										
										
										
											2018-10-09 20:30:59 +08:00
										 |  |  | 						}); | 
					
						
							|  |  |  | 					}); | 
					
						
							| 
									
										
										
										
											2018-12-21 19:02:37 +08:00
										 |  |  | 				}, | 
					
						
							|  |  |  | 				reject | 
					
						
							|  |  |  | 			); | 
					
						
							| 
									
										
										
										
											2018-10-09 20:30:59 +08:00
										 |  |  | 		}); | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | module.exports = FileMiddleware; |