UNPKG

ts-data-forge

Version:

[![npm version](https://img.shields.io/npm/v/ts-data-forge.svg)](https://www.npmjs.com/package/ts-data-forge) [![npm downloads](https://img.shields.io/npm/dm/ts-data-forge.svg)](https://www.npmjs.com/package/ts-data-forge) [![License](https://img.shields.

211 lines (208 loc) 8.13 kB
import { none } from '../functional/optional/impl/optional-none.mjs'; import { some } from '../functional/optional/impl/optional-some.mjs'; import '@sindresorhus/is'; 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. */ pop() { if (this.isEmpty) { return 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 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. */ 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 * * @example * * ```ts * const stack = createStack<string>(); * * assert.isTrue(stack.isEmpty); * * assert.isTrue(stack.size === 0); * * stack.push('first'); * * // eslint-disable-next-line unicorn/prefer-single-call * stack.push('second'); * * assert.isFalse(stack.isEmpty); * * assert.isTrue(stack.size === 2); * * assert.deepStrictEqual(stack.pop(), Optional.some('second')); * * assert.deepStrictEqual(stack.pop(), Optional.some('first')); * * assert.deepStrictEqual(stack.pop(), Optional.none); * * const seededStack = createStack([10, 20, 30]); * * assert.isTrue(seededStack.size === 3); * * assert.deepStrictEqual(seededStack.pop(), Optional.some(30)); * ``` * * @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. */ const createStack = (initialValues) => new StackClass(initialValues); export { createStack }; //# sourceMappingURL=stack.mjs.map