@stream-toolbox/split
Version:
Split a readable stream into multiple readable streams by size or Buffer
258 lines (257 loc) • 10.3 kB
JavaScript
"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;