Compare commits
No commits in common. "5ab27259478c851b70a46357f82b2687fe1e4f82" and "c350abc5f6f9c91d153eee749906afe23b9f441f" have entirely different histories.
5ab2725947
...
c350abc5f6
@ -1,82 +0,0 @@
|
|||||||
import { Queue } from './queue.mjs';
|
|
||||||
|
|
||||||
import { Queue } from './queue.mjs';
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Represents an asynchronous lock for managing access to a shared resource.
|
|
||||||
* @class
|
|
||||||
*/
|
|
||||||
export class AsyncLock {
|
|
||||||
/**
|
|
||||||
* Creates an instance of AsyncLock.
|
|
||||||
* @constructor
|
|
||||||
*/
|
|
||||||
constructor() {
|
|
||||||
/**
|
|
||||||
* A queue to store pending requests for acquiring the lock.
|
|
||||||
* @type {Queue}
|
|
||||||
* @private
|
|
||||||
*/
|
|
||||||
this.pending_queue = new Queue();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The current state of the lock.
|
|
||||||
* @type {boolean}
|
|
||||||
* @private
|
|
||||||
*/
|
|
||||||
this.state = false;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The ID associated with the current lock holder.
|
|
||||||
* @type {number}
|
|
||||||
* @private
|
|
||||||
*/
|
|
||||||
this.lock_id = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Checks the current state of the lock.
|
|
||||||
* @returns {boolean} True if the lock is acquired; otherwise, false.
|
|
||||||
*/
|
|
||||||
query() {
|
|
||||||
return this.state;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Acquires the lock asynchronously.
|
|
||||||
* @returns {Promise<number>} A promise that resolves with the unique ID associated with the acquired lock.
|
|
||||||
*/
|
|
||||||
acquire() {
|
|
||||||
if (!this.state) {
|
|
||||||
this.state = true;
|
|
||||||
this.lock_id += 1;
|
|
||||||
return Promise.resolve(this.lock_id);
|
|
||||||
}
|
|
||||||
return new Promise((resolve, _reject) => this.pending_queue.push(resolve));
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Releases the lock with the specified ID.
|
|
||||||
* @param {number} lock_id The ID of the lock to release.
|
|
||||||
*/
|
|
||||||
release(lock_id) {
|
|
||||||
if (!this.state || lock_id !== this.lock_id) { return; }
|
|
||||||
if (this.pending_queue.empty()) {
|
|
||||||
this.state = false;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
let resolve = this.pending_queue.front();
|
|
||||||
this.pending_queue.popFront();
|
|
||||||
this.lock_id += 1;
|
|
||||||
resolve(this.lock_id);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Releases the lock forcefully even you are not the owner.
|
|
||||||
* BE CAREFUL WHAT YOU WISH FOR!!
|
|
||||||
*/
|
|
||||||
forceRelease() {
|
|
||||||
this.release(this.lock_id);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
@ -1,90 +0,0 @@
|
|||||||
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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,4 +1 @@
|
|||||||
export { areArrayBuffersEqual } from './buffer.mjs';
|
export { areArrayBuffersEqual } from './buffer.mjs';
|
||||||
export { Queue, QueueEmptyError } from './queue.mjs';
|
|
||||||
export { AsyncLock } from './async-lock.mjs';
|
|
||||||
export { AsyncByteQueue } from './async-queue.mjs';
|
|
||||||
|
|||||||
@ -2,7 +2,5 @@
|
|||||||
"name": "@og/utility",
|
"name": "@og/utility",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"main": "index.mjs",
|
"main": "index.mjs",
|
||||||
"dependencies": {
|
"dependencies": {}
|
||||||
"buffer": "^6.0.3"
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,127 +0,0 @@
|
|||||||
/**
|
|
||||||
* Represents a queue data structure.
|
|
||||||
* @class
|
|
||||||
*/
|
|
||||||
export class Queue {
|
|
||||||
/**
|
|
||||||
* Creates an instance of Queue.
|
|
||||||
* @constructor
|
|
||||||
*/
|
|
||||||
constructor() {
|
|
||||||
/**
|
|
||||||
* The data array to store elements.
|
|
||||||
* @type {Array}
|
|
||||||
* @private
|
|
||||||
*/
|
|
||||||
this.data = [];
|
|
||||||
/**
|
|
||||||
* The index of the front element in the queue.
|
|
||||||
* @type {number}
|
|
||||||
* @private
|
|
||||||
*/
|
|
||||||
this.head = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Retrieves the front element of the queue.
|
|
||||||
* @returns {*} The front element of the queue.
|
|
||||||
*/
|
|
||||||
front() {
|
|
||||||
return this.data[this.head];
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Retrieves the back element of the queue.
|
|
||||||
* @returns {*} The back element of the queue.
|
|
||||||
*/
|
|
||||||
back() {
|
|
||||||
return this.data[this.data.length - 1];
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Removes the back element from the queue.
|
|
||||||
* @throws {QueueEmptyError} Throws an error if the queue is empty.
|
|
||||||
*/
|
|
||||||
popBack() {
|
|
||||||
if (this.data.length == this.head) {
|
|
||||||
throw new QueueEmptyError();
|
|
||||||
}
|
|
||||||
this.data.length -= 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Checks if the queue is empty.
|
|
||||||
* @returns {boolean} True if the queue is empty, otherwise false.
|
|
||||||
*/
|
|
||||||
empty() {
|
|
||||||
return this.data.length == this.head;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the size of the queue.
|
|
||||||
* @returns {number} The size of the queue.
|
|
||||||
*/
|
|
||||||
size() {
|
|
||||||
return this.data.length - this.head;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Alias for the size() method.
|
|
||||||
* @type {number}
|
|
||||||
*/
|
|
||||||
get length() {
|
|
||||||
return this.size();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Pushes elements into the queue.
|
|
||||||
* @param {...*} elements The elements to push into the queue.
|
|
||||||
*/
|
|
||||||
push() {
|
|
||||||
this.data.push.apply(this.data, arguments);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Removes the front element from the queue.
|
|
||||||
* @throws {QueueEmptyError} Throws an error if the queue is empty.
|
|
||||||
*/
|
|
||||||
popFront() {
|
|
||||||
if (this.empty()) { throw new QueueEmptyError(); }
|
|
||||||
|
|
||||||
this.head += 1;
|
|
||||||
if (this.head == this.data.length) {
|
|
||||||
this.data = [];
|
|
||||||
this.head = 0;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (this.head >= this.data.length >> 1 && this.head >= 16) {
|
|
||||||
this.data = this.data.slice(this.head);
|
|
||||||
this.head = 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns an iterator that allows iterating through the elements of the queue.
|
|
||||||
* @returns {*} elements of the queue.
|
|
||||||
* @generator
|
|
||||||
*/
|
|
||||||
*[Symbol.iterator]() {
|
|
||||||
for (let i = this.head; i < this.data.length; i += 1) {
|
|
||||||
yield this.data[i];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Represents an error thrown when attempting operations on an empty queue.
|
|
||||||
* @class
|
|
||||||
*/
|
|
||||||
export class QueueEmptyError extends Error {
|
|
||||||
/**
|
|
||||||
* Creates an instance of QueueEmptyError.
|
|
||||||
* @constructor
|
|
||||||
*/
|
|
||||||
constructor() {
|
|
||||||
super('Queue is empty');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Loading…
x
Reference in New Issue
Block a user