144 lines
		
	
	
		
			2.5 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
			
		
		
	
	
			144 lines
		
	
	
		
			2.5 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
'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;
 | 
						|
    }
 | 
						|
};
 |