extract-base-iterator
Version:
Base iterator for extract iterators like tar-iterator and zip-iterator
262 lines • 9.67 kB
JavaScript
/**
* Buffer List for Streaming
*
* Simple linked list for accumulating buffer chunks during streaming.
* Provides efficient append, consume, and slice operations.
*/ "use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
Object.defineProperty(exports, "default", {
enumerable: true,
get: function() {
return BufferList;
}
});
var _extractbaseiterator = require("extract-base-iterator");
function _class_call_check(instance, Constructor) {
if (!(instance instanceof Constructor)) {
throw new TypeError("Cannot call a class as a function");
}
}
var BufferList = /*#__PURE__*/ function() {
"use strict";
function BufferList() {
_class_call_check(this, BufferList);
this.head = null;
this.tail = null;
/** Total bytes in the buffer list */ this.length = 0;
}
var _proto = BufferList.prototype;
/**
* Append a buffer to the end of the list
*/ _proto.append = function append(buf) {
if (buf.length === 0) return;
var node = {
data: buf,
next: null
};
if (this.tail) {
this.tail.next = node;
this.tail = node;
} else {
this.head = this.tail = node;
}
this.length += buf.length;
};
/**
* Prepend a buffer to the front of the list
*/ _proto.prepend = function prepend(buf) {
if (buf.length === 0) return;
var node = {
data: buf,
next: this.head
};
if (!this.tail) {
this.tail = node;
}
this.head = node;
this.length += buf.length;
};
/**
* Consume n bytes from the front of the list
* Returns a new buffer containing the consumed bytes
*/ _proto.consume = function consume(n) {
if (n <= 0) return (0, _extractbaseiterator.allocBuffer)(0);
if (n > this.length) n = this.length;
var result = (0, _extractbaseiterator.allocBuffer)(n);
var offset = 0;
while(offset < n && this.head){
var chunk = this.head.data;
var needed = n - offset;
if (chunk.length <= needed) {
// Use entire chunk
chunk.copy(result, offset);
offset += chunk.length;
this.head = this.head.next;
if (!this.head) this.tail = null;
} else {
// Use partial chunk
chunk.copy(result, offset, 0, needed);
this.head.data = chunk.slice(needed);
offset = n;
}
}
this.length -= n;
return result;
};
/**
* Get a slice of the buffer without consuming
* Returns a new buffer containing the bytes
*/ _proto.slice = function slice(start, end) {
var len = end - start;
if (len <= 0) return (0, _extractbaseiterator.allocBuffer)(0);
if (start >= this.length) return (0, _extractbaseiterator.allocBuffer)(0);
var result = (0, _extractbaseiterator.allocBuffer)(Math.min(len, this.length - start));
var resultOffset = 0;
var bufOffset = 0;
var node = this.head;
// Skip to start position
while(node && bufOffset + node.data.length <= start){
bufOffset += node.data.length;
node = node.next;
}
// Copy data
while(node && resultOffset < result.length){
var chunk = node.data;
var chunkStart = Math.max(0, start - bufOffset);
var chunkEnd = Math.min(chunk.length, end - bufOffset);
var toCopy = chunkEnd - chunkStart;
if (toCopy > 0) {
chunk.copy(result, resultOffset, chunkStart, chunkEnd);
resultOffset += toCopy;
}
bufOffset += chunk.length;
node = node.next;
}
return result;
};
/**
* Read a single byte at offset without consuming
*/ _proto.readByte = function readByte(offset) {
if (offset < 0 || offset >= this.length) return -1;
var bufOffset = 0;
var node = this.head;
while(node){
if (offset < bufOffset + node.data.length) {
return node.data[offset - bufOffset];
}
bufOffset += node.data.length;
node = node.next;
}
return -1;
};
/**
* Search for a byte sequence in the buffer
* Returns offset of first match, or -1 if not found
*/ _proto.indexOf = function indexOf(signature) {
var startOffset = arguments.length > 1 && arguments[1] !== void 0 ? arguments[1] : 0;
if (signature.length === 0) return startOffset;
if (startOffset + signature.length > this.length) return -1;
// Simple byte-by-byte search
// Could be optimized with KMP/Boyer-Moore for larger signatures
for(var i = startOffset; i <= this.length - signature.length; i++){
var match = true;
for(var j = 0; j < signature.length; j++){
if (this.readByte(i + j) !== signature[j]) {
match = false;
break;
}
}
if (match) return i;
}
return -1;
};
/**
* Skip (consume) n bytes without returning them
*/ _proto.skip = function skip(n) {
if (n <= 0) return;
if (n >= this.length) {
this.clear();
return;
}
var remaining = n;
while(remaining > 0 && this.head){
var chunk = this.head.data;
if (chunk.length <= remaining) {
remaining -= chunk.length;
this.head = this.head.next;
if (!this.head) this.tail = null;
} else {
this.head.data = chunk.slice(remaining);
remaining = 0;
}
}
this.length -= n;
};
/**
* Clear all buffers
*/ _proto.clear = function clear() {
this.head = null;
this.tail = null;
this.length = 0;
};
/**
* Check if buffer has at least n bytes available
*/ _proto.has = function has(n) {
return this.length >= n;
};
/**
* Check if the buffer starts with a signature at offset 0
*/ _proto.startsWith = function startsWith(signature) {
if (signature.length > this.length) return false;
for(var i = 0; i < signature.length; i++){
if (this.readByte(i) !== signature[i]) return false;
}
return true;
};
/**
* Get a consolidated buffer of the entire contents
* Note: This creates a copy, so use sparingly for large buffers
*/ _proto.toBuffer = function toBuffer() {
if (this.length === 0) return (0, _extractbaseiterator.allocBuffer)(0);
return this.slice(0, this.length);
};
/**
* Read UInt16 (little-endian) at offset without consuming
* Returns null if not enough data
*/ _proto.readUInt16LEAt = function readUInt16LEAt(offset) {
if (offset < 0 || offset + 2 > this.length) return null;
var bytes = this.readBytesAt(offset, 2);
if (bytes.length < 2) return null;
return bytes.readUInt16LE(0);
};
/**
* Read UInt32 (little-endian) at offset without consuming
* Returns null if not enough data
*/ _proto.readUInt32LEAt = function readUInt32LEAt(offset) {
if (offset < 0 || offset + 4 > this.length) return null;
var bytes = this.readBytesAt(offset, 4);
if (bytes.length < 4) return null;
return bytes.readUInt32LE(0);
};
/**
* Read bytes at offset without consuming.
* Returns a slice (zero-copy) when data fits within a single chunk,
* otherwise allocates and copies from multiple chunks.
*/ _proto.readBytesAt = function readBytesAt(offset, length) {
if (length <= 0) return (0, _extractbaseiterator.allocBuffer)(0);
if (offset < 0 || offset >= this.length) return (0, _extractbaseiterator.allocBuffer)(0);
// Clamp length to available data
var available = this.length - offset;
if (length > available) length = available;
// Find the node containing the offset
var bufOffset = 0;
var node = this.head;
while(node && bufOffset + node.data.length <= offset){
bufOffset += node.data.length;
node = node.next;
}
if (!node) return (0, _extractbaseiterator.allocBuffer)(0);
var startInChunk = offset - bufOffset;
// Single-buffer optimization: zero-copy slice
if (startInChunk + length <= node.data.length) {
return node.data.slice(startInChunk, startInChunk + length);
}
// Multi-buffer case: must allocate and copy
var result = (0, _extractbaseiterator.allocBuffer)(length);
var resultOffset = 0;
while(node && resultOffset < length){
var chunk = node.data;
var chunkStart = resultOffset === 0 ? startInChunk : 0;
var chunkAvailable = chunk.length - chunkStart;
var toCopy = Math.min(chunkAvailable, length - resultOffset);
chunk.copy(result, resultOffset, chunkStart, chunkStart + toCopy);
resultOffset += toCopy;
node = node.next;
}
return result;
};
return BufferList;
}();
/* CJS INTEROP */ if (exports.__esModule && exports.default) { try { Object.defineProperty(exports.default, '__esModule', { value: true }); for (var key in exports) { exports.default[key] = exports[key]; } } catch (_) {}; module.exports = exports.default; }