UNPKG

@stream-toolbox/split

Version:

Split a readable stream into multiple readable streams by size or Buffer

258 lines (257 loc) 10.3 kB
"use strict"; var __extends = (this && this.__extends) || (function () { var extendStatics = function (d, b) { extendStatics = Object.setPrototypeOf || ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || function (d, b) { for (var p in b) if (Object.prototype.hasOwnProperty.call(b, p)) d[p] = b[p]; }; return extendStatics(d, b); }; return function (d, b) { if (typeof b !== "function" && b !== null) throw new TypeError("Class extends value " + String(b) + " is not a constructor or null"); extendStatics(d, b); function __() { this.constructor = d; } d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); }; })(); var __assign = (this && this.__assign) || function () { __assign = Object.assign || function(t) { for (var s, i = 1, n = arguments.length; i < n; i++) { s = arguments[i]; for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p)) t[p] = s[p]; } return t; }; return __assign.apply(this, arguments); }; var StreamSearch = require("@stream-toolbox/search"); var stream_1 = require("stream"); var Part = (function (_super) { __extends(Part, _super); function Part(config) { var _this = _super.call(this, config.readableOptions) || this; _this.kickoff = false; _this.cutted = false; _this.called = 0; _this.size = 0; if (typeof config.separator === "number") { _this.space = config.separator - config.tail.length; } else { _this.space = Infinity; } _this.config = config; return _this; } return Part; }(stream_1.Readable)); var Spliter = (function (_super) { __extends(Spliter, _super); function Spliter(partOptions, onPart, opts) { var _this = _super.call(this, __assign(__assign({}, opts), { objectMode: false })) || this; _this.partCount = 0; _this.forwardable = false; _this.freeze = null; _this.remain = []; _this.finalCb = null; _this.finalCbed = false; _this.onPart = onPart.bind(_this); process.nextTick(function () { return _this.createPart(partOptions); }); return _this; } Spliter.prototype.createPart = function (partOptions) { var _this = this; if (this.finalCbed || this.destroyed) { return; } if (this.finalCb && this.remain.length === 0 && this.partCount) { this.finalCbed = true; this.finalCb(null); return; } if (this.part) { if (!this.part.cutted) { this.destroy(new Error("The data in current part has not been all read out, you should wait for the 'end' event emit!")); return; } if (this.part.called !== 1) { this.destroy(new Error("'callback' can only be called once! If 'onPart' is an async function, you don't need call 'callback' manually!")); return; } } var partConfig = this.normalizeConfig(partOptions); if (partConfig instanceof Error) { this.destroy(partConfig); return; } partConfig.readableOptions = __assign(__assign({}, partConfig.readableOptions), { objectMode: false, read: function () { _this.forwardable = true; if (!_this.part.kickoff) { _this.part.kickoff = true; if (partConfig.head.length) { _this.remain.unshift(partConfig.head); } var chunk = Buffer.concat(_this.remain); _this.remain = []; _this.forward(chunk, "buffer", function () { if (_this.finalCb && _this.remain.length === 0) { _this.cut(); } }); } if (_this.forwardable && _this.freeze) { var freeze = _this.freeze; _this.freeze = null; _this.forward.apply(_this, freeze); } } }); if (Buffer.isBuffer(partConfig.separator)) { this.initSearcher(partConfig.separator); } this.part = new Part(partConfig); this.part.index = this.partCount++; var ret = this.onPart(this.part, function (error, nextConfig) { if (nextConfig === void 0) { nextConfig = partConfig; } _this.part.called++; if (error != null) { _this.destroy(error); } else { process.nextTick(function () { return _this.createPart(nextConfig); }); } }); if (ret instanceof Promise) { ret .then(function (nextConfig) { if (nextConfig === void 0) { nextConfig = partConfig; } _this.part.called++; process.nextTick(function () { return _this.createPart(nextConfig); }); }) .catch(function (error) { _this.part.called++; _this.destroy(error); }); } }; Spliter.prototype.normalizeConfig = function (partOptions) { var _a, _b, _c; var separator, keepSeparator, head, tail, readableOptions; if (Object.prototype.toString.call(partOptions) === "[object Object]") { separator = partOptions.separator; keepSeparator = partOptions.keepSeparator === true; head = (_a = partOptions.head) !== null && _a !== void 0 ? _a : Buffer.alloc(0); tail = (_b = partOptions.tail) !== null && _b !== void 0 ? _b : Buffer.alloc(0); readableOptions = (_c = partOptions.readableOptions) !== null && _c !== void 0 ? _c : {}; } else { separator = partOptions; keepSeparator = false; head = Buffer.alloc(0); tail = Buffer.alloc(0); readableOptions = {}; } typeof separator === "string" && (separator = Buffer.from(separator, "utf-8")); typeof head === "string" && (head = Buffer.from(head, "utf-8")); typeof tail === "string" && (tail = Buffer.from(tail, "utf-8")); if ((Buffer.isBuffer(separator) && separator.length > 0) || (typeof separator === "number" && separator > 0)) { if (!Buffer.isBuffer(head)) { return new Error("head can only be buffer or string"); } if (!Buffer.isBuffer(tail)) { return new Error("tail can only be buffer or string"); } if (typeof separator === "number" && head.length + tail.length > separator) { return new Error("head.length + tail.length show be less than part limit size"); } return { separator: separator, head: head, tail: tail, keepSeparator: keepSeparator, readableOptions: readableOptions }; } return new Error("separator can only be non-empty buffer (string) or positive integer!"); }; Spliter.prototype.initSearcher = function (separator) { var _this = this; if (this.searcher) { this.searcher.reset({ needle: separator, limit: 1, abandon: false }); } else { this.searcher = new StreamSearch({ needle: separator, limit: 1, abandon: false }, function (isMatch, chunk) { if (isMatch) { if (_this.part.config.keepSeparator) { _this.part.size += _this.part.config.separator.length; _this.part.push(_this.part.config.separator); } if (chunk.length) { _this.remain.push(chunk); } _this.cut(); } else if (chunk.length) { _this.part.size += chunk.length; _this.forwardable = _this.part.push(chunk); } }); } }; Spliter.prototype.cut = function () { if (!this.part || this.part.cutted) { return; } this.part.cutted = true; this.forwardable = false; if (Buffer.isBuffer(this.part.config.separator)) { this.searcher.flush(); } this.part.size += this.part.config.tail.length; this.part.push(this.part.config.tail); this.part.push(null); }; Spliter.prototype.forward = function (chunk, encoding, callback) { if (Buffer.isBuffer(this.part.config.separator)) { this.searcher.push(chunk); } else if (this.part.space > chunk.length) { this.part.size += chunk.length; this.part.space -= chunk.length; this.forwardable = this.part.push(chunk); } else { var last = chunk.subarray(0, this.part.space); var remain = chunk.subarray(this.part.space); this.part.size += last.length; this.part.space = 0; if (remain.length) { this.remain.push(remain); } this.part.push(last); this.cut(); } callback(null); }; Spliter.prototype._write = function () { var args = []; for (var _i = 0; _i < arguments.length; _i++) { args[_i] = arguments[_i]; } if (this.forwardable) { this.forward.apply(this, args); } else { this.freeze = args; } }; Spliter.prototype._final = function (callback) { this.finalCb = callback; this.cut(); }; Spliter.prototype._destroy = function (error, callback) { if (error != null && this.part && !this.part.destroyed) { this.part.destroy(error); } callback(error); }; return Spliter; }(stream_1.Writable)); function createSpliter(partOptions, onPart, opts) { return new Spliter(partOptions, onPart, opts); } module.exports = createSpliter;