123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143 |
- 'use strict';
- const Assert = require('@hapi/hoek/lib/assert');
- const Clone = require('@hapi/hoek/lib/clone');
- const Common = require('./common');
- const internals = {
- max: 1000,
- supported: new Set(['undefined', 'boolean', 'number', 'string'])
- };
- exports.provider = {
- provision(options) {
- return new internals.Cache(options);
- }
- };
- // Least Recently Used (LRU) Cache
- internals.Cache = class {
- constructor(options = {}) {
- Common.assertOptions(options, ['max']);
- Assert(options.max === undefined || options.max && options.max > 0 && isFinite(options.max), 'Invalid max cache size');
- this._max = options.max || internals.max;
- this._map = new Map(); // Map of nodes by key
- this._list = new internals.List(); // List of nodes (most recently used in head)
- }
- get length() {
- return this._map.size;
- }
- set(key, value) {
- if (key !== null &&
- !internals.supported.has(typeof key)) {
- return;
- }
- let node = this._map.get(key);
- if (node) {
- node.value = value;
- this._list.first(node);
- return;
- }
- node = this._list.unshift({ key, value });
- this._map.set(key, node);
- this._compact();
- }
- get(key) {
- const node = this._map.get(key);
- if (node) {
- this._list.first(node);
- return Clone(node.value);
- }
- }
- _compact() {
- if (this._map.size > this._max) {
- const node = this._list.pop();
- this._map.delete(node.key);
- }
- }
- };
- internals.List = class {
- constructor() {
- this.tail = null;
- this.head = null;
- }
- unshift(node) {
- node.next = null;
- node.prev = this.head;
- if (this.head) {
- this.head.next = node;
- }
- this.head = node;
- if (!this.tail) {
- this.tail = node;
- }
- return node;
- }
- first(node) {
- if (node === this.head) {
- return;
- }
- this._remove(node);
- this.unshift(node);
- }
- pop() {
- return this._remove(this.tail);
- }
- _remove(node) {
- const { next, prev } = node;
- next.prev = prev;
- if (prev) {
- prev.next = next;
- }
- if (node === this.tail) {
- this.tail = next;
- }
- node.prev = null;
- node.next = null;
- return node;
- }
- };
|