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.
107 lines • 3.85 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.CircularFIFOStack = exports.ECircularStackOverflowMode = void 0;
const circular_buffer_js_1 = require("./circular-buffer.js");
const _production_js_1 = require("../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.
*/
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 || (exports.ECircularStackOverflowMode = ECircularStackOverflowMode = {}));
/**
* @public
* Circular first in first out stack.
*
* @remarks
* See {@link ECircularStackOverflowMode} for details of overflow behavior.
*/
class CircularFIFOStack {
constructor(capacity, mode = ECircularStackOverflowMode.Grow) {
this.start = 0;
this.end = 0;
this.capacity = capacity;
this.mode = mode;
this.buffer = circular_buffer_js_1.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_js_1._Production.createError("Attempted to push to full stack.");
break;
case ECircularStackOverflowMode.Overwrite:
this.pop();
break;
case ECircularStackOverflowMode.Grow:
this.growStack();
break;
default:
_production_js_1._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_js_1._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;
}
}
exports.CircularFIFOStack = CircularFIFOStack;
//# sourceMappingURL=circular-fifo-stack.js.map