ts-data-forge
Version:
[](https://www.npmjs.com/package/ts-data-forge) [](https://www.npmjs.com/package/ts-data-forge) [ • 13.4 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 '../number/branded-types/safe-uint.mjs';
import '../number/branded-types/uint.mjs';
import '../number/branded-types/uint16.mjs';
import { asUint32, Uint32 } 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 stack with LIFO (Last-In, First-Out) behavior using a dynamic array.
* This implementation provides O(1) amortized push and O(1) pop operations by using a resizable buffer
* that grows as needed.
*
* The underlying array automatically resizes when it becomes full, ensuring that the stack
* can grow to accommodate any number of elements while maintaining efficient operations.
*
* @template T The type of elements in the stack.
* @implements Stack
*/
class StackClass {
/** @internal Dynamic array to store stack elements. */
#buffer;
/** @internal Current number of elements in the stack. */
#mut_size;
/** @internal Current capacity of the buffer. */
#capacity;
/** @internal Initial capacity for new stacks. */
static #INITIAL_CAPACITY = 8;
/**
* Constructs a new StackClass instance.
* @param initialValues Optional initial values to populate the stack.
*/
constructor(initialValues = []) {
const initialCapacity = asUint32(Math.max(StackClass.#INITIAL_CAPACITY, initialValues.length * 2));
this.#buffer = Array.from({ length: initialCapacity }, () => undefined);
this.#mut_size = asUint32(0);
this.#capacity = initialCapacity;
// Add initial values
for (const value of initialValues) {
this.push(value);
}
}
/** @inheritdoc */
get isEmpty() {
return this.#mut_size === 0;
}
/** @inheritdoc */
get size() {
return asUint32(this.#mut_size);
}
/**
* Removes and returns the element at the top of the stack (LIFO).
*
* This operation removes the element that was added most recently (last-in) and returns it.
* If the stack is empty, returns `Optional.none`. The operation is guaranteed to be O(1)
* and does not require any array resizing or memory reallocation.
*
* **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 stack is empty.
*
* @example
* ```typescript
* const stack = createStack<string>();
*
* // Add some elements
* stack.push("bottom");
* stack.push("middle");
* stack.push("top");
*
* // Remove elements in LIFO order
* const top = stack.pop();
* if (top.isSome) {
* console.log(top.value); // "top" (last pushed, first popped)
* }
*
* const middle = stack.pop().unwrap(); // "middle"
* console.log(stack.size); // 1
*
* // Safe handling of empty stack
* const emptyStack = createStack<number>();
* const result = emptyStack.pop();
* if (result.isNone) {
* console.log("Stack is empty");
* }
*
* // Typical usage in algorithms
* const pathStack = createStack<string>();
* pathStack.push("/home");
* pathStack.push("/users");
* pathStack.push("/documents");
*
* // Backtrack one level
* const currentDir = pathStack.pop().unwrap(); // "/documents"
* const parentDir = pathStack.pop().unwrap(); // "/users"
* ```
*/
pop() {
if (this.isEmpty) {
return Optional.none;
}
this.#mut_size = Uint32.sub(this.#mut_size, 1);
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
const element = this.#buffer[this.#mut_size];
this.#buffer[this.#mut_size] = undefined; // Clear reference for garbage collection
return Optional.some(element);
}
/**
* Adds an element to the top of the stack (LIFO).
*
* This operation adds the element to the top of the stack, where it will be the first
* to be popped (last-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 copies existing elements to maintain the stack structure.
*
* @param value The element to add to the top of the stack.
*
* @example
* ```typescript
* const actionStack = createStack<string>();
*
* // Add actions in chronological order
* actionStack.push("open file"); // O(1)
* actionStack.push("edit content"); // O(1)
* actionStack.push("save file"); // O(1)
*
* console.log(actionStack.size); // 3
*
* // Actions will be undone in reverse order (LIFO)
* while (!actionStack.isEmpty) {
* const action = actionStack.pop().unwrap();
* console.log(`Undoing: ${action}`);
* }
* // Output:
* // Undoing: save file
* // Undoing: edit content
* // Undoing: open file
*
* // High-volume pushing (demonstrates amortized O(1) performance)
* const dataStack = createStack<number>();
*
* for (const i of range(1000000)) {
* dataStack.push(i); // Each operation is O(1) amortized
* }
*
* console.log(dataStack.size); // 1000000
*
* // Function call stack simulation
* type StackFrame = { function: string; variables: Record<string, any> };
* const callStack = createStack<StackFrame>();
*
* callStack.push({ function: "main", variables: { argc: 1, argv: ["program"] } });
* callStack.push({ function: "process", variables: { data: [1, 2, 3] } });
* callStack.push({ function: "validate", variables: { input: "test" } });
*
* // Current function context is at the top
* const currentFrame = callStack.pop().unwrap();
* console.log(`Current function: ${currentFrame.function}`);
* ```
*/
push(value) {
// Resize if buffer is full
if (this.#mut_size === this.#capacity) {
this.#resize();
}
this.#buffer[this.#mut_size] = value;
this.#mut_size = Uint32.add(this.#mut_size, 1);
}
/**
* @internal
* Resizes the buffer when it becomes full.
* Doubles the capacity while preserving all elements.
*/
#resize() {
const newCapacity = asUint32(this.#capacity * 2);
const newBuffer = Array.from({ length: newCapacity }, () => undefined);
// Copy existing elements
for (const i of range(this.#mut_size)) {
newBuffer[i] = this.#buffer[i];
}
this.#buffer = newBuffer;
this.#capacity = newCapacity;
}
}
/**
* Creates a new Stack instance with LIFO (Last-In, First-Out) behavior using a high-performance dynamic array.
*
* This factory function creates an optimized stack implementation that maintains excellent performance
* characteristics for both push and pop operations. The underlying dynamic array automatically
* resizes to accommodate growing workloads while providing predictable O(1) operations.
*
* **Implementation Features:**
* - **O(1) push operations** (amortized - occasionally O(n) when resizing)
* - **O(1) pop operations** (always)
* - **Automatic buffer resizing** - starts at 8 elements, doubles when full
* - **Memory efficient** - garbage collects removed elements immediately
* - **Dynamic array design** - eliminates need for complex memory management
*
* **Performance Benefits:**
* - No array shifting required for stack 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 stack.
* @param initialValues Optional array of initial elements to populate the stack.
* Elements will be popped in reverse order of how they appear in the array
* (last array element will be popped first).
* If provided, the initial buffer capacity will be at least twice the array length.
* @returns A new Stack instance optimized for high-performance LIFO operations.
*
* @example
* ```typescript
* import { createStack } from './stack';
*
* // Example 1: Function call simulation
* type FunctionCall = { name: string; args: any[]; context: any };
* const callStack = createStack<FunctionCall>();
*
* // Simulate function calls (push onto stack)
* callStack.push({ name: "main", args: [], context: {} }); // O(1)
* callStack.push({ name: "processData", args: [data], context: {} }); // O(1)
* callStack.push({ name: "validateInput", args: [input], context: {} }); // O(1)
*
* // Simulate function returns (pop from stack)
* while (!callStack.isEmpty) {
* const call = callStack.pop().unwrap(); // O(1)
* console.log(`Returning from: ${call.name}`);
* }
* // Output:
* // Returning from: validateInput
* // Returning from: processData
* // Returning from: main
*
* // Example 2: Expression evaluation with operator precedence
* const operatorStack = createStack<string>();
* const operandStack = createStack<number>();
*
* // Simulate parsing "3 + 4 * 2"
* operandStack.push(3);
* operatorStack.push("+");
* operandStack.push(4);
* operatorStack.push("*"); // Higher precedence
* operandStack.push(2);
*
* // Process higher precedence first (LIFO)
* const op = operatorStack.pop().unwrap(); // "*"
* const b = operandStack.pop().unwrap(); // 2
* const a = operandStack.pop().unwrap(); // 4
* operandStack.push(a * b); // Push result: 8
*
* // Example 3: Undo/Redo functionality
* type EditAction = {
* type: 'insert' | 'delete' | 'modify';
* position: number;
* oldValue: string;
* newValue: string;
* };
*
* const undoStack = createStack<EditAction>();
* const redoStack = createStack<EditAction>();
*
* // Perform edits (push to undo stack)
* const edit1: EditAction = { type: 'insert', position: 0, oldValue: '', newValue: 'Hello' };
* const edit2: EditAction = { type: 'insert', position: 5, oldValue: '', newValue: ' World' };
*
* undoStack.push(edit1);
* undoStack.push(edit2);
*
* // Undo last edit
* if (!undoStack.isEmpty) {
* const lastEdit = undoStack.pop().unwrap();
* redoStack.push(lastEdit);
* console.log(`Undid: ${lastEdit.type} at position ${lastEdit.position}`);
* }
*
* // Example 4: High-throughput data processing
* const processingStack = createStack<number>();
*
* // Add large amount of data (demonstrates amortized O(1) performance)
* for (const i of range(100000)) {
* processingStack.push(i); // Each push is O(1) amortized
* }
*
* // Process data in LIFO order
* let processedCount = 0;
* while (!processingStack.isEmpty) {
* const value = processingStack.pop().unwrap(); // O(1)
* // Process value...
* processedCount++;
* }
* console.log(`Processed ${processedCount} items`); // 100000
*
* // Example 5: Stack with pre-populated data
* const historyStack = createStack<string>([
* "page1.html",
* "page2.html",
* "page3.html",
* "page4.html"
* ]);
*
* console.log(historyStack.size); // Output: 4
*
* // Navigate back through history (LIFO order)
* while (!historyStack.isEmpty) {
* const page = historyStack.pop().unwrap();
* console.log(`Going back to: ${page}`);
* }
* // Output:
* // Going back to: page4.html (last added, first removed)
* // Going back to: page3.html
* // Going back to: page2.html
* // Going back to: page1.html
* ```
*/
const createStack = (initialValues) => new StackClass(initialValues);
export { createStack };
//# sourceMappingURL=stack.mjs.map