@chickenjdk/byteutils
Version:
Advanced tools for manipulating binary data in JavaScript
194 lines (193 loc) • 12.6 kB
JavaScript
"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);