ts-data-forge
Version:
[](https://www.npmjs.com/package/ts-data-forge) [](https://www.npmjs.com/package/ts-data-forge) [ • 11.8 kB
JavaScript
import { Optional } from '../functional/optional.mjs';
import '../functional/result.mjs';
import { range } from '../iterator/range.mjs';
import '../number/branded-types/finite-number.mjs';
import '../number/branded-types/int.mjs';
import '../number/branded-types/int16.mjs';
import '../number/branded-types/int32.mjs';
import '../number/branded-types/non-negative-finite-number.mjs';
import '../number/branded-types/non-negative-int16.mjs';
import '../number/branded-types/non-negative-int32.mjs';
import '../number/branded-types/non-zero-finite-number.mjs';
import '../number/branded-types/non-zero-int.mjs';
import '../number/branded-types/non-zero-int16.mjs';
import '../number/branded-types/non-zero-int32.mjs';
import '../number/branded-types/non-zero-safe-int.mjs';
import '../number/branded-types/non-zero-uint16.mjs';
import '../number/branded-types/non-zero-uint32.mjs';
import '../number/branded-types/positive-finite-number.mjs';
import '../number/branded-types/positive-int.mjs';
import '../number/branded-types/positive-int16.mjs';
import '../number/branded-types/positive-int32.mjs';
import '../number/branded-types/positive-safe-int.mjs';
import '../number/branded-types/positive-uint16.mjs';
import '../number/branded-types/positive-uint32.mjs';
import '../number/branded-types/safe-int.mjs';
import { asSafeUint } from '../number/branded-types/safe-uint.mjs';
import '../number/branded-types/uint.mjs';
import '../number/branded-types/uint16.mjs';
import { asUint32 } from '../number/branded-types/uint32.mjs';
import '../number/enum/int8.mjs';
import '../number/enum/uint8.mjs';
import '../number/num.mjs';
import '../number/refined-number-utils.mjs';
/**
* Class implementation for a queue with FIFO (First-In, First-Out) behavior using a circular buffer.
* This implementation provides O(1) enqueue and dequeue operations by using a fixed-size buffer
* with head and tail pointers that wrap around when they reach the buffer boundary.
*
* The circular buffer automatically resizes when it becomes full, ensuring that the queue
* can grow to accommodate any number of elements while maintaining efficient operations.
*
* @template T The type of elements in the queue.
* @implements Queue
*/
class QueueClass {
/** @internal Circular buffer to store queue elements. */
#buffer;
/** @internal Index of the first element (front of queue). */
#head;
/** @internal Index where the next element will be added (back of queue). */
#tail;
/** @internal Current number of elements in the queue. */
#mut_size;
/** @internal Current capacity of the buffer. */
#capacity;
/** @internal Initial capacity for new queues. */
static #INITIAL_CAPACITY = 8;
/**
* Constructs a new QueueClass instance.
* @param initialValues Optional initial values to populate the queue.
*/
constructor(initialValues = []) {
const initialCapacity = asUint32(Math.max(QueueClass.#INITIAL_CAPACITY, initialValues.length * 2));
this.#buffer = Array.from({ length: initialCapacity }, () => undefined);
this.#head = 0;
this.#tail = 0;
this.#mut_size = 0;
this.#capacity = initialCapacity;
// Add initial values
for (const value of initialValues) {
this.enqueue(value);
}
}
/** @inheritdoc */
get isEmpty() {
return this.#mut_size === 0;
}
/** @inheritdoc */
get size() {
return asUint32(this.#mut_size);
}
/**
* Removes and returns the element at the front of the queue (FIFO).
*
* This operation removes the element that was added earliest (first-in) and returns it.
* If the queue is empty, returns `Optional.none`. The operation is guaranteed to be O(1)
* and does not require any array shifting or copying.
*
* **Time Complexity:** O(1) - constant time operation
* **Space Complexity:** O(1) - no additional memory allocation
*
* @returns An Optional containing the removed element, or `Optional.none` if the queue is empty.
*
* @example
* ```typescript
* const queue = createQueue<string>();
*
* // Add some elements
* queue.enqueue("first");
* queue.enqueue("second");
* queue.enqueue("third");
*
* // Remove elements in FIFO order
* const first = queue.dequeue();
* if (first.isSome) {
* console.log(first.value); // "first"
* }
*
* const second = queue.dequeue().unwrap(); // "second"
* console.log(queue.size); // 1
*
* // Safe handling of empty queue
* const emptyQueue = createQueue<number>();
* const result = emptyQueue.dequeue();
* if (result.isNone) {
* console.log("Queue is empty");
* }
* ```
*/
dequeue() {
if (this.isEmpty) {
return Optional.none;
}
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
const element = this.#buffer[this.#head];
this.#buffer[this.#head] = undefined; // Clear reference for garbage collection
this.#head = (this.#head + 1) % this.#capacity;
this.#mut_size -= 1;
return Optional.some(element);
}
/**
* Adds an element to the back of the queue (FIFO).
*
* This operation adds the element to the end of the queue, where it will be the last
* to be dequeued (first-in, first-out ordering). The operation is amortized O(1),
* meaning it's O(1) for most operations with occasional O(n) when the buffer needs resizing.
*
* **Time Complexity:** O(1) amortized - O(n) only when buffer resize is needed
* **Space Complexity:** O(1) - constant additional memory per element
*
* **Buffer Resizing:** When the internal buffer becomes full, it automatically doubles
* in size and reorganizes elements to maintain the circular buffer structure.
*
* @param value The element to add to the back of the queue.
*
* @example
* ```typescript
* const taskQueue = createQueue<string>();
*
* // Add tasks in order of arrival
* taskQueue.enqueue("Process order #1001"); // O(1)
* taskQueue.enqueue("Send notification"); // O(1)
* taskQueue.enqueue("Update inventory"); // O(1)
*
* console.log(taskQueue.size); // 3
*
* // Tasks will be processed in the order they were added
* while (!taskQueue.isEmpty) {
* const task = taskQueue.dequeue().unwrap();
* console.log(`Executing: ${task}`);
* }
*
* // High-volume enqueueing (demonstrates amortized O(1) performance)
* const dataQueue = createQueue<number>();
*
* for (const i of range(1000000)) {
* dataQueue.enqueue(i); // Each operation is O(1) amortized
* }
*
* console.log(dataQueue.size); // 1000000
* ```
*/
enqueue(value) {
// Resize if buffer is full
if (this.#mut_size === this.#capacity) {
this.#resize();
}
this.#buffer[this.#tail] = value;
this.#tail = (this.#tail + 1) % this.#capacity;
this.#mut_size += 1;
}
/**
* @internal
* Resizes the circular buffer when it becomes full.
* Doubles the capacity and reorganizes elements to maintain queue order.
*/
#resize() {
const newCapacity = asUint32(this.#capacity * 2);
const newBuffer = Array.from({ length: newCapacity }, () => undefined);
// Copy elements in order from head to tail
for (const i of range(asSafeUint(this.#mut_size))) {
const sourceIndex = (this.#head + i) % this.#capacity;
newBuffer[i] = this.#buffer[sourceIndex];
}
this.#buffer = newBuffer;
this.#head = 0;
this.#tail = this.#mut_size;
this.#capacity = newCapacity;
}
}
/**
* Creates a new Queue instance with FIFO (First-In, First-Out) behavior using a high-performance circular buffer.
*
* This factory function creates an optimized queue implementation that maintains excellent performance
* characteristics for both enqueue and dequeue operations. The underlying circular buffer automatically
* resizes to accommodate growing workloads while providing predictable O(1) operations.
*
* **Implementation Features:**
* - **O(1) enqueue operations** (amortized - occasionally O(n) when resizing)
* - **O(1) dequeue operations** (always)
* - **Automatic buffer resizing** - starts at 8 elements, doubles when full
* - **Memory efficient** - garbage collects removed elements immediately
* - **Circular buffer design** - eliminates need for array shifting operations
*
* **Performance Benefits:**
* - No array copying during normal operations
* - Minimal memory allocation overhead
* - Predictable performance under high load
* - Efficient memory usage with automatic cleanup
*
* @template T The type of elements stored in the queue.
* @param initialValues Optional array of initial elements to populate the queue.
* Elements will be dequeued in the same order they appear in the array.
* If provided, the initial buffer capacity will be at least twice the array length.
* @returns A new Queue instance optimized for high-performance FIFO operations.
*
* @example
* ```typescript
* import { createQueue } from './queue';
*
* // Example 1: Basic FIFO workflow
* const requestQueue = createQueue<string>();
*
* // Add requests to the queue
* requestQueue.enqueue("GET /api/users"); // O(1)
* requestQueue.enqueue("POST /api/orders"); // O(1)
* requestQueue.enqueue("DELETE /api/cache"); // O(1)
*
* // Process requests in order
* while (!requestQueue.isEmpty) {
* const request = requestQueue.dequeue().unwrap(); // O(1)
* console.log(`Processing: ${request}`);
* }
* // Output:
* // Processing: GET /api/users
* // Processing: POST /api/orders
* // Processing: DELETE /api/cache
*
* // Example 2: High-throughput event processing
* type Event = { timestamp: number; type: string; data: any };
* const eventQueue = createQueue<Event>();
*
* // Simulate high-volume event ingestion
* for (const i of range(10000)) {
* eventQueue.enqueue({
* timestamp: Date.now(),
* type: `event-${i % 5}`,
* data: { value: i }
* }); // Each enqueue is O(1) amortized
* }
*
* // Process events efficiently
* let processedCount = 0;
* while (!eventQueue.isEmpty) {
* const event = eventQueue.dequeue().unwrap(); // O(1)
* // Process event...
* processedCount++;
* }
* console.log(`Processed ${processedCount} events`); // 10000
*
* // Example 3: Queue with pre-populated data
* const priorityTasks = createQueue<string>([
* "Initialize system",
* "Load configuration",
* "Start services",
* "Begin processing"
* ]);
*
* console.log(priorityTasks.size); // Output: 4
*
* // Execute tasks in initialization order
* while (!priorityTasks.isEmpty) {
* const task = priorityTasks.dequeue().unwrap();
* console.log(`Executing: ${task}`);
* }
*
* // Example 4: Producer-Consumer pattern
* const workQueue = createQueue<() => Promise<void>>();
*
* // Producer: Add work items
* const addWork = (workFn: () => Promise<void>) => {
* workQueue.enqueue(workFn);
* };
*
* // Consumer: Process work items
* const processWork = async () => {
* while (!workQueue.isEmpty) {
* const workItem = workQueue.dequeue().unwrap();
* await workItem();
* }
* };
*
* // Add some work
* addWork(async () => console.log("Work item 1"));
* addWork(async () => console.log("Work item 2"));
*
* // Process the work
* await processWork();
* ```
*/
const createQueue = (initialValues) => new QueueClass(initialValues);
export { createQueue };
//# sourceMappingURL=queue.mjs.map