UNPKG

rc-js-util

Version:

A collection of TS and C++ utilities to help writing performant and correct applications, achieved through strict typing and (removable) invariant checking.

103 lines 3.55 kB
import { CircularBuffer } from "./circular-buffer.js"; import { _Production } from "../production/_production.js"; /** * @public * Sets the behavior of {@link CircularFIFOStack} when a value is pushed which won't fit. * * @remarks * Does not affect underflow, which is always considered exceptional. */ export var ECircularStackOverflowMode; (function (ECircularStackOverflowMode) { /** * Do nothing. */ ECircularStackOverflowMode[ECircularStackOverflowMode["NoOp"] = 1] = "NoOp"; /** * Throw an error if the buffer overflows. */ ECircularStackOverflowMode[ECircularStackOverflowMode["Exception"] = 2] = "Exception"; /** * Overwrite the first value. */ ECircularStackOverflowMode[ECircularStackOverflowMode["Overwrite"] = 3] = "Overwrite"; /** * Doubles the stack size and copies in place, running in O(size). */ ECircularStackOverflowMode[ECircularStackOverflowMode["Grow"] = 4] = "Grow"; })(ECircularStackOverflowMode || (ECircularStackOverflowMode = {})); /** * @public * Circular first in first out stack. * * @remarks * See {@link ECircularStackOverflowMode} for details of overflow behavior. */ export class CircularFIFOStack { constructor(capacity, mode = ECircularStackOverflowMode.Grow) { this.start = 0; this.end = 0; this.capacity = capacity; this.mode = mode; this.buffer = CircularBuffer.createEmpty(capacity); } getCapacity() { return this.capacity; } /** * Pushes a value to the top of the stack (depending on `mode`). */ push(value) { if (this.start + this.buffer.size == this.end) { switch (this.mode) { case ECircularStackOverflowMode.NoOp: return; case ECircularStackOverflowMode.Exception: throw _Production.createError("Attempted to push to full stack."); break; case ECircularStackOverflowMode.Overwrite: this.pop(); break; case ECircularStackOverflowMode.Grow: this.growStack(); break; default: _Production.assertValueIsNever(this.mode); } } this.buffer.setValue(this.end++, value); } /** * Remove the bottom element in the stack and return it. * * @remarks * Attempting to pop an empty stack is considered exceptional regardless of `mode`. You can * call `getIsEmpty` or `getRemainingCapacity` to determine if pop is safe to call. */ pop() { if (this.getIsEmpty()) { throw _Production.createError("Attempted to pop empty stack."); } // null out the value to avoid memory leaks return this.buffer.getSetValue(this.start++, null); } getIsEmpty() { return this.start == this.end; } getRemainingCapacity() { return this.start + this.capacity - this.end; } growStack() { const largerCircularStack = new CircularFIFOStack(this.capacity * 2, this.mode); let size = this.capacity; while (size--) { const valueToCopy = this.pop(); largerCircularStack.push(valueToCopy); } this.buffer = largerCircularStack.buffer; this.capacity *= 2; this.start = largerCircularStack.start; this.end = largerCircularStack.end; } } //# sourceMappingURL=circular-fifo-stack.js.map