UNPKG

recoil

Version:

Recoil - A state management library for React

160 lines (126 loc) 2.87 kB
/** * Copyright (c) Meta Platforms, Inc. and affiliates. * * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * * @flow strict * @format * @oncall recoil */ 'use strict'; const nullthrows = require('recoil-shared/util/Recoil_nullthrows'); type CacheNode<K, V> = { key: K, value: V, left: ?CacheNode<K, V>, right: ?CacheNode<K, V>, }; type Options<K> = { maxSize: number, mapKey?: K => mixed, }; class LRUCache<K = mixed, V = mixed> { _maxSize: number; _size: number; _head: ?CacheNode<K, V>; _tail: ?CacheNode<K, V>; _map: Map<mixed, CacheNode<K, V>>; _keyMapper: K => mixed; constructor(options: Options<K>) { this._maxSize = options.maxSize; this._size = 0; this._head = null; this._tail = null; this._map = new Map<mixed, CacheNode<K, V>>(); this._keyMapper = options.mapKey ?? (v => v); } head(): ?CacheNode<K, V> { return this._head; } tail(): ?CacheNode<K, V> { return this._tail; } size(): number { return this._size; } maxSize(): number { return this._maxSize; } has(key: K): boolean { return this._map.has(this._keyMapper(key)); } get(key: K): ?V { const mappedKey = this._keyMapper(key); const node = this._map.get(mappedKey); if (!node) { return undefined; } this.set(key, node.value); return node.value; } set(key: K, val: V): void { const mappedKey = this._keyMapper(key); const existingNode = this._map.get(mappedKey); if (existingNode) { this.delete(key); } const head = this.head(); const node = { key, right: head, left: null, value: val, }; if (head) { head.left = node; } else { this._tail = node; } this._map.set(mappedKey, node); this._head = node; this._size++; this._maybeDeleteLRU(); } _maybeDeleteLRU() { if (this.size() > this.maxSize()) { this.deleteLru(); } } deleteLru(): void { const tail = this.tail(); if (tail) { this.delete(tail.key); } } delete(key: K): void { const mappedKey = this._keyMapper(key); if (!this._size || !this._map.has(mappedKey)) { return; } const node = nullthrows(this._map.get(mappedKey)); const right = node.right; const left = node.left; if (right) { right.left = node.left; } if (left) { left.right = node.right; } if (node === this.head()) { this._head = right; } if (node === this.tail()) { this._tail = left; } this._map.delete(mappedKey); this._size--; } clear(): void { this._size = 0; this._head = null; this._tail = null; this._map = new Map<mixed, CacheNode<K, V>>(); } } module.exports = {LRUCache};