UNPKG

@chickenjdk/byteutils

Version:

Advanced tools for manipulating binary data in JavaScript

194 lines (193 loc) 12.6 kB
"use strict"; var __classPrivateFieldGet = (this && this.__classPrivateFieldGet) || function (receiver, state, kind, f) { if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a getter"); if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot read private member from an object whose class did not declare it"); return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver); }; var __classPrivateFieldSet = (this && this.__classPrivateFieldSet) || function (receiver, state, value, kind, f) { if (kind === "m") throw new TypeError("Private method is not writable"); if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a setter"); if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot write private member to an object whose class did not declare it"); return (kind === "a" ? f.call(receiver, value) : f ? f.value = value : state.set(receiver, value)), value; }; var _readableStream_instances, _readableStream_stream, _readableStream_chunkQuete, _readableStream_chunkIdx, _readableStream_locked, _readableStream_lockQueue, _readableStream_onDataListerers, _readableStream_onDrainListeners, _readableStream_onceDrainListeners, _readableStream_aquireLock, _readableStream_relaseLock, _readableStream_waitChunk; Object.defineProperty(exports, "__esModule", { value: true }); exports.readableStreamLE = exports.readableStream = void 0; const readableBuffer_1 = require("../readableBuffer"); const common_1 = require("../common"); class readableStream extends readableBuffer_1.readableBufferBaseAsync { get stream() { return __classPrivateFieldGet(this, _readableStream_stream, "f"); } get _chunks() { return __classPrivateFieldGet(this, _readableStream_chunkQuete, "f"); } onDrain(listener) { __classPrivateFieldGet(this, _readableStream_onDrainListeners, "f").push(listener); } onceDrain(listener) { __classPrivateFieldGet(this, _readableStream_onceDrainListeners, "f").push(listener); } constructor(stream) { super(); _readableStream_instances.add(this); _readableStream_stream.set(this, void 0); _readableStream_chunkQuete.set(this, []); _readableStream_chunkIdx.set(this, 0); _readableStream_locked.set(this, false); _readableStream_lockQueue.set(this, []); _readableStream_onDataListerers.set(this, []); _readableStream_onDrainListeners.set(this, []); _readableStream_onceDrainListeners.set(this, []); this.drained = true; this.destroyed = false; __classPrivateFieldSet(this, _readableStream_stream, stream, "f"); __classPrivateFieldGet(this, _readableStream_stream, "f").on("data", (chunk) => { __classPrivateFieldGet(this, _readableStream_chunkQuete, "f").push(chunk); this.drained = false; __classPrivateFieldGet(this, _readableStream_onDataListerers, "f").forEach(([listener]) => listener()); }); __classPrivateFieldGet(this, _readableStream_stream, "f").on("end", () => { this.destroyed = true; const nukeReads = () => { // Abort all listeners because there is no more data [...__classPrivateFieldGet(this, _readableStream_lockQueue, "f"), ...__classPrivateFieldGet(this, _readableStream_onDataListerers, "f")].forEach(([, reject]) => { reject(new Error("Stream ended before listener could be satisfied")); }); // Disable all methods that would read from the stream // All read calls pass through those functions, so we can just disable all reads with this // This is a hack, but it is the only way to make sure that all calls to #aquireLock will be rejected while keeping not checking for the stream being destroyed in those functions // I would just override the methods but private methods can not be overwritten // Empty the queue to allow the contents to gc __classPrivateFieldSet(this, _readableStream_lockQueue, [], "f"); // Lock the stream so we can reject all calls to #aquireLock __classPrivateFieldSet(this, _readableStream_locked, true, "f"); // Reject all calls to #aquireLock via rejecting the promise listeners passed to this array __classPrivateFieldGet(this, _readableStream_lockQueue, "f").push = (value) => { value[1](new Error("Lock not avalible, stream has ended")); return 1; }; // Keep the stream locked so the promise listeners will be passed to above __classPrivateFieldGet(this, _readableStream_lockQueue, "f").shift = () => { __classPrivateFieldSet(this, _readableStream_locked, true, "f"); return undefined; }; }; if (__classPrivateFieldGet(this, _readableStream_onDataListerers, "f").length > 0) { // If there are listeners waiting for data, well there is no data so end nuke the stream nukeReads(); } // When a onDataListener is registered, we know there is no more data to be had, so we nuke the stream const oldPush = __classPrivateFieldGet(this, _readableStream_onDataListerers, "f").push; __classPrivateFieldGet(this, _readableStream_onDataListerers, "f").push = (...args) => { const res = oldPush.apply(__classPrivateFieldGet(this, _readableStream_onDataListerers, "f"), args); nukeReads(); return res; }; }); } async shift() { var _a, _b; await __classPrivateFieldGet(this, _readableStream_instances, "m", _readableStream_aquireLock).call(this); await __classPrivateFieldGet(this, _readableStream_instances, "m", _readableStream_waitChunk).call(this); const chunk = __classPrivateFieldGet(this, _readableStream_chunkQuete, "f")[0]; const byte = chunk[__classPrivateFieldSet(this, _readableStream_chunkIdx, (_b = __classPrivateFieldGet(this, _readableStream_chunkIdx, "f"), _a = _b++, _b), "f"), _a]; await __classPrivateFieldGet(this, _readableStream_instances, "m", _readableStream_relaseLock).call(this); return byte; } async readUint8Array(bytes) { await __classPrivateFieldGet(this, _readableStream_instances, "m", _readableStream_aquireLock).call(this); const chunks = []; let bytesLeft = bytes; while (bytesLeft !== 0) { await __classPrivateFieldGet(this, _readableStream_instances, "m", _readableStream_waitChunk).call(this); const chunk = __classPrivateFieldGet(this, _readableStream_chunkQuete, "f")[0]; if (chunk.length - __classPrivateFieldGet(this, _readableStream_chunkIdx, "f") > bytesLeft) { chunks.push(chunk.subarray(__classPrivateFieldGet(this, _readableStream_chunkIdx, "f"), __classPrivateFieldGet(this, _readableStream_chunkIdx, "f") + bytesLeft)); __classPrivateFieldSet(this, _readableStream_chunkIdx, __classPrivateFieldGet(this, _readableStream_chunkIdx, "f") + bytesLeft, "f"); bytesLeft = 0; } else { chunks.push(chunk.subarray(__classPrivateFieldGet(this, _readableStream_chunkIdx, "f"))); bytesLeft -= chunk.length - __classPrivateFieldGet(this, _readableStream_chunkIdx, "f"); // Consuming the whole chunk, so just delete it __classPrivateFieldGet(this, _readableStream_chunkQuete, "f").shift(); } } await __classPrivateFieldGet(this, _readableStream_instances, "m", _readableStream_relaseLock).call(this); return (0, common_1.joinUint8Arrays)(chunks, bytes); } async readUint8ArrayBackwards(bytes) { return (await this.readUint8Array(bytes)).reverse(); } async readArray(bytes) { const uint8Array = await this.readUint8Array(bytes); return Array.from(uint8Array); } async readArrayBackwards(bytes) { return (await this.readArray(bytes)).reverse(); } } exports.readableStream = readableStream; _readableStream_stream = new WeakMap(), _readableStream_chunkQuete = new WeakMap(), _readableStream_chunkIdx = new WeakMap(), _readableStream_locked = new WeakMap(), _readableStream_lockQueue = new WeakMap(), _readableStream_onDataListerers = new WeakMap(), _readableStream_onDrainListeners = new WeakMap(), _readableStream_onceDrainListeners = new WeakMap(), _readableStream_instances = new WeakSet(), _readableStream_aquireLock = // Help preserve order of reads. Be careful with changing login, it is kind of hacked when the stream ends async function _readableStream_aquireLock() { if (__classPrivateFieldGet(this, _readableStream_locked, "f")) { return new Promise((resolve, reject) => { __classPrivateFieldGet(this, _readableStream_lockQueue, "f").push([resolve, reject]); }); } __classPrivateFieldSet(this, _readableStream_locked, true, "f"); }, _readableStream_relaseLock = async function _readableStream_relaseLock() { __classPrivateFieldSet(this, _readableStream_locked, false, "f"); const listener = __classPrivateFieldGet(this, _readableStream_lockQueue, "f").shift(); if (listener) { listener[0](); } // Run drain listeners because waitChunk is not called at the end of a function const listeners = [...__classPrivateFieldGet(this, _readableStream_onDrainListeners, "f"), ...__classPrivateFieldGet(this, _readableStream_onceDrainListeners, "f")]; if (listeners.length > 0 && !this.drained && __classPrivateFieldGet(this, _readableStream_stream, "f").readableLength === 0 && (__classPrivateFieldGet(this, _readableStream_chunkQuete, "f").length === 0 || __classPrivateFieldGet(this, _readableStream_chunkIdx, "f") >= __classPrivateFieldGet(this, _readableStream_chunkQuete, "f")[0].length)) { this.drained = true; listeners.forEach((value) => value()); __classPrivateFieldSet(this, _readableStream_onceDrainListeners, [], "f"); } }, _readableStream_waitChunk = // Handle the hard parts of waiting for data async function _readableStream_waitChunk() { // Handle used buffer first that way the data awaiter knows that the quete realy is empty if (__classPrivateFieldGet(this, _readableStream_chunkQuete, "f").length > 0 && __classPrivateFieldGet(this, _readableStream_chunkIdx, "f") >= __classPrivateFieldGet(this, _readableStream_chunkQuete, "f")[0].length) { __classPrivateFieldSet(this, _readableStream_chunkIdx, 0, "f"); __classPrivateFieldGet(this, _readableStream_chunkQuete, "f").shift(); } if (__classPrivateFieldGet(this, _readableStream_chunkQuete, "f").length === 0) { if (!this.drained && __classPrivateFieldGet(this, _readableStream_stream, "f").readableLength === 0) { this.drained = true; const listeners = [ ...__classPrivateFieldGet(this, _readableStream_onDrainListeners, "f"), ...__classPrivateFieldGet(this, _readableStream_onceDrainListeners, "f"), ]; if (listeners.length > 0) { listeners.forEach((value) => value()); __classPrivateFieldSet(this, _readableStream_onceDrainListeners, [], "f"); } } await new Promise((resolve, reject) => { const onData = () => { __classPrivateFieldGet(this, _readableStream_onDataListerers, "f").splice(__classPrivateFieldGet(this, _readableStream_onDataListerers, "f").findIndex(([value]) => value === onData), 1); resolve(); }; __classPrivateFieldGet(this, _readableStream_onDataListerers, "f").push([onData, reject]); }); __classPrivateFieldSet(this, _readableStream_chunkIdx, 0, "f"); } }; /** * Little-endian version of readableStream * @remarks You can generate this class yourself with `addDefaultEndianness(readableStream, true)` or make a already created instance little endian via `instance.isLe = true` */ exports.readableStreamLE = (0, common_1.addDefaultEndianness)(readableStream, true);