UNPKG

extract-base-iterator

Version:

Base iterator for extract iterators like tar-iterator and zip-iterator

262 lines 9.67 kB
/** * 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; }