// Tweaked version of nathan7's binary-parse-stream // (see https://github.com/nathan7/binary-parse-stream) // Uses NoFilter instead of the readable in the original. Removes // the ability to read -1, which was odd and un-needed. // License for binary-parse-stream: MIT // binary-parse-stream is now unmaintained, so I have rewritten it as // more modern JS so I can get tsc to help check types. 'use strict' const stream = require('stream') const NoFilter = require('nofilter') /** * BinaryParseStream is a TransformStream that consumes buffers and outputs * objects on the other end. It expects your subclass to implement a `_parse` * method that is a generator. When your generator yields a number, it'll be * fed a buffer of that length from the input. When your generator returns, * the return value will be pushed to the output side. * * @extends stream.Transform */ class BinaryParseStream extends stream.Transform { /** * Creates an instance of BinaryParseStream. * * @memberof BinaryParseStream * @param {stream.TransformOptions} options Stream options. */ constructor(options) { super(options) // Doesn't work to pass these in as opts, for some reason // also, work around typescript not knowing TransformStream internals // eslint-disable-next-line dot-notation this['_writableState'].objectMode = false // eslint-disable-next-line dot-notation this['_readableState'].objectMode = true this.bs = new NoFilter() this.__restart() } /** * Transforming. * * @param {any} fresh Buffer to transcode. * @param {BufferEncoding} encoding Name of encoding. * @param {stream.TransformCallback} cb Callback when done. * @ignore */ _transform(fresh, encoding, cb) { this.bs.write(fresh) while (this.bs.length >= this.__needed) { let ret = null const chunk = (this.__needed === null) ? undefined : this.bs.read(this.__needed) try { ret = this.__parser.next(chunk) } catch (e) { return cb(e) } if (this.__needed) { this.__fresh = false } if (ret.done) { this.push(ret.value) this.__restart() } else { this.__needed = ret.value || Infinity } } return cb() } /** * Subclasses must override this to set their parsing behavior. Yield a * number to receive a Buffer of that many bytes. * * @abstract * @returns {Generator} */ /* istanbul ignore next */ *_parse() { // eslint-disable-line class-methods-use-this, require-yield throw new Error('Must be implemented in subclass') } __restart() { this.__needed = null this.__parser = this._parse() this.__fresh = true } /** * Flushing. * * @param {stream.TransformCallback} cb Callback when done. * @ignore */ _flush(cb) { cb(this.__fresh ? null : new Error('unexpected end of input')) } } module.exports = BinaryParseStream