UNPKG

@woosh/meep-engine

Version:

Pure JavaScript game engine. Fully featured and production ready.

168 lines (128 loc) 3.87 kB
import { CacheElement } from "../../cache/CacheElement.js"; import { HashMap } from "../../collection/map/HashMap.js"; import Signal from "../../events/signal/Signal.js"; /** * @template Key, Value */ export class ImmutableObjectPool { /** * @readonly * @type {Signal<Key,Value>} */ onRemoved = new Signal() /** * * @type {HashMap<Key, CacheElement<Key,Value>[]>} */ data = new HashMap(); /** * * @type {CacheElement<Key,Value>|null} * @private */ __first = null; /** * * @type {CacheElement<Key,Value>|null} * @private */ __last = null; constructor({ capacity = 100, perKeyCapacity = 10 } = {}) { /** * How many items in total the pool can hold * @type {number} */ this.capacity = capacity; /** * Maximum number of items that can be stored for the same key * @type {number} */ this.perKeyCapacity = perKeyCapacity; /** * Current number of elements in the pool * @type {number} */ this.size = 0; } get(key) { // find the right bucket const elements = this.data.get(key); if (elements === undefined) { return undefined; } let element; if (elements.length > 1) { element = elements.pop(); } else { // last element element = elements[0]; this.data.delete(key); } element.unlink(); this.size--; return element.value; } /** * Removed all elements from cache * NOTE: {@link onRemoved} signal *does* get triggered for each element */ clear() { while (this.__first !== null) { this.__removeElement(this.__first); } } /** * * @param {CacheElement<Key,Value>} el * @private */ __removeElement(el) { if (el === this.__first) { this.__first = el.next; } if (el === this.__last) { this.__last = el.previous; } el.unlink(); const key = el.key; const elements = this.data.get(key); const i = elements.indexOf(el); elements.splice(i, 1); if (elements.length === 0) { this.data.delete(key); } this.size--; this.onRemoved.send2(key, el.value); } add(key, value) { let elements = this.data.get(key); if (elements === undefined) { // no elements for this key, create container elements = []; this.data.set(key, elements); } else if (elements.length >= this.perKeyCapacity) { // reached capacity for this key, discard element return false; } if (this.size >= this.capacity) { // at the capacity, need to evict one element // eviction allows the pool to adapt to changing usage patterns, preventing it from getting saturated with values that are no longer relevant this.__removeElement(this.__first); } const cache_element = new CacheElement(); cache_element.value = value; cache_element.key = key; // attach at the end of the list cache_element.previous = this.__last; if (this.__last !== null) { this.__last.next = cache_element; } this.__last = cache_element; if (this.__first === null) { this.__first = cache_element; } elements.push(cache_element); this.size++; return true; } }