UNPKG

@stream-toolbox/search

Version:

Search for the given Buffer in a sequence of data (readable stream)

170 lines (169 loc) 6.03 kB
"use strict"; var buffer_1 = require("buffer"); var StreamSearch = (function () { function StreamSearch(opts, cb) { if (typeof cb !== "function") { throw new Error("Missing match callback"); } this.cb = cb; this.reset(opts); } StreamSearch.prototype.reset = function (opts) { this.pos = 0; this.lookback = []; this.matched = 0; if (typeof opts === "string" || buffer_1.Buffer.isBuffer(opts)) { this.limit = Infinity; this.abandon = true; this.resetNeedle(opts); } else { if (typeof opts.limit === "undefined") { this.limit = Infinity; } else if (Number.isInteger(opts.limit) && opts.limit > 0) { this.limit = opts.limit; } else { throw new Error("Expected positive interger for limit"); } this.abandon = opts.abandon !== false; this.resetNeedle(opts.needle); } }; StreamSearch.prototype.resetNeedle = function (needle) { if (typeof needle === "string") { needle = buffer_1.Buffer.from(needle, "utf-8"); } if (!buffer_1.Buffer.isBuffer(needle)) { throw new Error("Expected Buffer for needle, got ".concat(typeof needle)); } if (!needle.length) { throw new Error("Empty Buffer"); } if (buffer_1.Buffer.isBuffer(this.needle) && this.needle.equals(needle)) { return; } this.needle = needle; this.nLen = this.needle.length; this.nLast = this.nLen - 1; this.move = new Array(256).fill(this.nLen); for (var i = 0; i < this.nLast; i++) { this.move[this.needle[i]] = this.nLast - i; } }; StreamSearch.prototype.push = function (chunk) { if (!chunk) { return; } if (typeof chunk === "string") { chunk = buffer_1.Buffer.from(chunk, "utf-8"); } if (this.matched >= this.limit) { this.abandon || this.cb(false, chunk); return; } var offset = 0; while (this.pos <= chunk.length - this.nLen) { if (this.pos < 0) { var i = this.pos; var j = 0; while (j < this.nLen && this.needle[j] === (i < 0 ? this.lookbackAt(i) : chunk[i])) { i++; j++; } if (j === this.nLen) { this.lookbackUnload(this.pos); this.pos += this.nLen; offset = this.pos; if (++this.matched === this.limit) { return this.cb(true, this.abandon ? buffer_1.Buffer.alloc(0) : chunk.subarray(offset)); } else { this.cb(true, buffer_1.Buffer.alloc(0)); } } else { this.pos += this.move[chunk[this.pos + this.nLast]]; this.lookbackShift(this.pos); } } else { var idx = chunk.indexOf(this.needle, this.pos); if (idx !== -1) { idx !== offset && this.cb(false, chunk.subarray(offset, idx)); this.pos = idx + this.nLen; offset = this.pos; if (++this.matched === this.limit) { return this.cb(true, this.abandon ? buffer_1.Buffer.alloc(0) : chunk.subarray(offset)); } else { this.cb(true, buffer_1.Buffer.alloc(0)); } } else { var end = chunk.length - 1; var start = end - this.nLast; this.pos = start + this.move[chunk[end]]; } } } var remain = chunk.subarray(offset); if (remain.length) { this.pos < chunk.length ? this.lookback.push(remain) : this.cb(false, remain); } this.pos -= chunk.length; }; StreamSearch.prototype.flush = function () { while (this.lookback.length) { this.cb(false, this.lookback.shift()); } }; StreamSearch.prototype.lookbackAt = function (idx) { for (var i = this.lookback.length - 1, offset = 0; i > -1; i--) { var chunk = this.lookback[i]; offset -= chunk.length; if (offset <= idx) { return chunk[idx - offset]; } } }; StreamSearch.prototype.lookbackShift = function (idx) { if (idx >= 0) { while (this.lookback.length) { this.cb(false, this.lookback.shift()); } return; } for (var i = this.lookback.length - 1, offset = 0, shift = false; i > -1; i--) { if (shift) { this.cb(false, this.lookback.shift()); } else { var chunk = this.lookback[i]; offset -= chunk.length; shift = offset <= idx; } } }; StreamSearch.prototype.lookbackUnload = function (idx) { var lastChunk; for (var i = this.lookback.length - 1, offset = 0, shift = false; i > -1; i--) { if (shift) { this.cb(false, this.lookback.shift()); } else { var chunk = this.lookback[i]; offset -= chunk.length; shift = offset <= idx; if (shift && idx !== offset) { lastChunk = chunk.subarray(0, idx - offset); } } } lastChunk && this.cb(false, lastChunk); this.lookback = []; }; return StreamSearch; }()); module.exports = StreamSearch;