UNPKG

streaming

Version:

Transforms and other streaming helpers

141 lines (132 loc) 5.18 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 __()); }; })(); Object.defineProperty(exports, "__esModule", { value: true }); exports.Splitter = void 0; var stream_1 = require("stream"); function byteAdvancer(splitByte) { // just a closure around a generic byte read-loop return function (buffer) { var cursor = 0; for (var i = 0, l = buffer.length; i < l; i++) { if (buffer[i] === splitByte) { this.flushBuffer(buffer.subarray(cursor, i)); cursor = i + 1; } } return buffer.subarray(cursor); }; } /** Splitter is a stream.Transform that rechunks a stream into sub-buffers. The output (readable) part is set to objectMode true, and emits Buffer objects. The input (writable) part should be plain buffers (have no encoding). By default, it splits on the universal newline (\r, \r\n, or \n). The split byte can be specified in the options. _writableState.decodeStrings defaults to true, so we should get Buffers regardless of what's pushed in. If `opts.decodeStrings` is set to `false`, the behavior is undefined. (TODO: force decodeStrings: true maybe?) In other words, the Splitter should have the following values: { _writableState: { decodeStrings: true, objectMode: false }, _readableState: { objectMode: true } } */ /** A splitter stream rechunks a stream at every `split` byte, if `split` is defined. If `split` is not defined, it will split at the universal newline (\r, \r\n, or \n). _writableState.decodeStrings = true _writableState.objectMode = false _readableState.objectMode = true Node.js 'stream' API calls _transform and _flush: _transform calls _advance: _advance calls flushBuffer (maybe multiple times) flushBuffer calls push() _flush calls flushBuffer, either once or not at all flushBuffer calls push() */ var Splitter = /** @class */ (function (_super) { __extends(Splitter, _super); function Splitter(options) { var _this = _super.call(this, options) || this; _this._buffer = Buffer.alloc(0); _this._encoding = null; // we set the readable side to objectMode, in any case, so that the // individual buffers we emit will not be fused to each other _this['_readableState'].objectMode = true; if (options && options.split) { // if we are given a split string, use the byte code of the first character to split _this._advance = byteAdvancer(options.split.charCodeAt(0)); } return _this; } /** calling this will call toString on all emitted chunks, instead of returning buffers. */ Splitter.prototype.setEncoding = function (encoding) { this._encoding = encoding; return this; }; /** Handle each split part */ Splitter.prototype.flushBuffer = function (buffer) { if (this._encoding !== null) { this.push(buffer.toString(this._encoding)); } else { this.push(buffer); } }; /** Decide where the split points are */ Splitter.prototype._advance = function (buffer) { var cursor = 0; for (var i = 0, l = buffer.length; i < l; i++) { // smart handling of \r and \n if (buffer[i] === 13 || buffer[i] === 10) { this.flushBuffer(buffer.subarray(cursor, i)); if (buffer[i] === 13 && buffer[i + 1] === 10) { // '\r\n' i++; } cursor = i + 1; } } return buffer.subarray(cursor); }; /** `encoding` describes the type of `chunk` -- if the _writableState.decodeStrings option is true, this will be useful; otherwise, `chunk` will be just a buffer, or if objectMode is true, it'll be an arbitrary object, and `encoding` will just be 'buffer'. */ Splitter.prototype._transform = function (chunk, encoding, callback) { // assert encoding == 'buffer' var buffer = Buffer.concat([this._buffer, chunk]); this._buffer = this._advance(buffer); callback(); }; Splitter.prototype._flush = function (callback) { this._advance(this._buffer); if (this._buffer && this._buffer.length > 0) { this.flushBuffer(this._buffer); } callback(); }; return Splitter; }(stream_1.Transform)); exports.Splitter = Splitter;