@stream-toolbox/search
Version:
Search for the given Buffer in a sequence of data (readable stream)
170 lines (169 loc) • 6.03 kB
JavaScript
"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;