opengenerals/shared/utility/async-queue.mjs

91 lines
2.0 KiB
JavaScript

import { Buffer } from 'buffer';
import { AsyncLock } from './async-lock.mjs';
/**
* Represents an asynchronous byte queue for managing byte data.
*/
export class AsyncByteQueue {
/**
* Creates an instance of AsyncByteQueue.
*/
constructor() {
/**
* The buffer to store byte data.
* @type {Buffer}
* @private
*/
this.data = Buffer.alloc(0);
/**
* The index indicating the head of the byte queue.
* @type {number}
* @private
*/
this.head = 0;
/**
* The lock used to manage access to the byte queue.
* @type {AsyncLock}
* @private
*/
this.lock = new AsyncLock();
}
/**
* Checks if the byte queue is empty.
* @returns {boolean} True if the byte queue is empty; otherwise, false.
*/
empty() {
return this.head === this.data.byteLength;
}
/**
* Retrieves the byte at the front of the queue asynchronously.
* @returns {Promise<number>} A promise that resolves with the byte at the front of the queue.
*/
async front() {
while (this.empty()) {
await this.lock.acquire();
}
this.lock.forceRelease();
return this.data[this.head];
}
/**
* Removes and retrieves the byte at the front of the queue asynchronously.
* @returns {Promise<number>} A promise that resolves with the byte removed from the front of the queue.
*/
async popFront() {
while (this.empty()) {
await this.lock.acquire();
}
let res = this.data[this.head];
this.head += 1;
this._refactor();
if (!this.empty()) {
this.lock.forceRelease();
}
return res;
}
/**
* Appends byte data to the end of the queue.
* @param {Buffer} data The byte data to append to the queue.
*/
pushBack(data) {
this.data = Buffer.concat([this.data, data]);
this.lock.forceRelease();
}
/**
* Refactors the internal data buffer by discarding processed bytes if necessary.
* @private
*/
_refactor() {
if (this.head >= (this.data.byteLength >> 1) && this.head >= 16) {
this.data = this.data.subarray(this.head);
}
}
}