170 lines
3.6 KiB
JavaScript
170 lines
3.6 KiB
JavaScript
'use strict';
|
|
|
|
const Hoek = require('@hapi/hoek');
|
|
|
|
|
|
const internals = {};
|
|
|
|
|
|
exports.Vise = class Vise {
|
|
|
|
constructor(chunks) {
|
|
|
|
this.length = 0;
|
|
this._chunks = [];
|
|
this._offset = 0;
|
|
|
|
if (chunks) {
|
|
chunks = [].concat(chunks);
|
|
for (let i = 0; i < chunks.length; ++i) {
|
|
this.push(chunks[i]);
|
|
}
|
|
}
|
|
}
|
|
|
|
push(chunk) {
|
|
|
|
Hoek.assert(Buffer.isBuffer(chunk), 'Chunk must be a buffer');
|
|
|
|
const item = {
|
|
data: chunk,
|
|
length: chunk.length,
|
|
offset: this.length + this._offset,
|
|
index: this._chunks.length
|
|
};
|
|
|
|
this._chunks.push(item);
|
|
this.length += chunk.length;
|
|
}
|
|
|
|
shift(length) {
|
|
|
|
if (!length) {
|
|
return [];
|
|
}
|
|
|
|
const prevOffset = this._offset;
|
|
const item = this.#chunkAt(length);
|
|
|
|
let dropTo = this._chunks.length;
|
|
this._offset = 0;
|
|
|
|
if (item) {
|
|
dropTo = item.chunk.index;
|
|
this._offset = item.offset;
|
|
}
|
|
|
|
// Drop lower chunks
|
|
|
|
const chunks = [];
|
|
for (let i = 0; i < dropTo; ++i) {
|
|
const chunk = this._chunks.shift();
|
|
if (i === 0 &&
|
|
prevOffset) {
|
|
|
|
chunks.push(chunk.data.slice(prevOffset));
|
|
}
|
|
else {
|
|
chunks.push(chunk.data);
|
|
}
|
|
}
|
|
|
|
if (this._offset) {
|
|
chunks.push(item.chunk.data.slice(dropTo ? 0 : prevOffset, this._offset));
|
|
}
|
|
|
|
// Recalculate existing chunks
|
|
|
|
this.length = 0;
|
|
for (let i = 0; i < this._chunks.length; ++i) {
|
|
const chunk = this._chunks[i];
|
|
chunk.offset = this.length,
|
|
chunk.index = i;
|
|
|
|
this.length += chunk.length;
|
|
}
|
|
|
|
this.length -= this._offset;
|
|
|
|
return chunks;
|
|
}
|
|
|
|
readUInt8(pos) {
|
|
|
|
const item = this.#chunkAt(pos);
|
|
return item ? item.chunk.data[item.offset] : undefined;
|
|
}
|
|
|
|
at(pos) {
|
|
|
|
return this.readUInt8(pos);
|
|
}
|
|
|
|
#chunkAt(pos) {
|
|
|
|
if (pos < 0) {
|
|
return null;
|
|
}
|
|
|
|
pos = pos + this._offset;
|
|
|
|
for (let i = 0; i < this._chunks.length; ++i) {
|
|
const chunk = this._chunks[i];
|
|
const offset = pos - chunk.offset;
|
|
if (offset < chunk.length) {
|
|
return { chunk, offset };
|
|
}
|
|
}
|
|
|
|
return null;
|
|
}
|
|
|
|
chunks() {
|
|
|
|
const chunks = [];
|
|
|
|
for (let i = 0; i < this._chunks.length; ++i) {
|
|
const chunk = this._chunks[i];
|
|
if (i === 0 &&
|
|
this._offset) {
|
|
|
|
chunks.push(chunk.data.slice(this._offset));
|
|
}
|
|
else {
|
|
chunks.push(chunk.data);
|
|
}
|
|
}
|
|
|
|
return chunks;
|
|
}
|
|
|
|
startsWith(value, pos, length) {
|
|
|
|
pos = pos ?? 0;
|
|
|
|
length = length ? Math.min(value.length, length) : value.length;
|
|
if (pos + length > this.length) { // Not enough length to fit value
|
|
return false;
|
|
}
|
|
|
|
const start = this.#chunkAt(pos);
|
|
if (!start) {
|
|
return false;
|
|
}
|
|
|
|
let j = start.chunk.index;
|
|
for (let i = 0; j < this._chunks.length && i < length; ++j) {
|
|
const chunk = this._chunks[j];
|
|
|
|
let k = (j === start.chunk.index ? start.offset : 0);
|
|
for (; k < chunk.length && i < length; ++k, ++i) {
|
|
if (chunk.data[k] !== value[i]) {
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
};
|