import { Dictionary } from './types'; 
 | 
  
 | 
// Simple LRU cache use doubly linked list 
 | 
// @module zrender/core/LRU 
 | 
  
 | 
export class Entry<T> { 
 | 
  
 | 
    value: T 
 | 
  
 | 
    key: string | number 
 | 
  
 | 
    next: Entry<T> 
 | 
  
 | 
    prev: Entry<T> 
 | 
  
 | 
    constructor(val: T) { 
 | 
        this.value = val; 
 | 
    } 
 | 
} 
 | 
/** 
 | 
 * Simple double linked list. Compared with array, it has O(1) remove operation. 
 | 
 * @constructor 
 | 
 */ 
 | 
export class LinkedList<T> { 
 | 
  
 | 
    head: Entry<T> 
 | 
    tail: Entry<T> 
 | 
  
 | 
    private _len = 0 
 | 
  
 | 
    /** 
 | 
     * Insert a new value at the tail 
 | 
     */ 
 | 
    insert(val: T): Entry<T> { 
 | 
        const entry = new Entry(val); 
 | 
        this.insertEntry(entry); 
 | 
        return entry; 
 | 
    } 
 | 
  
 | 
    /** 
 | 
     * Insert an entry at the tail 
 | 
     */ 
 | 
    insertEntry(entry: Entry<T>) { 
 | 
        if (!this.head) { 
 | 
            this.head = this.tail = entry; 
 | 
        } 
 | 
        else { 
 | 
            this.tail.next = entry; 
 | 
            entry.prev = this.tail; 
 | 
            entry.next = null; 
 | 
            this.tail = entry; 
 | 
        } 
 | 
        this._len++; 
 | 
    } 
 | 
  
 | 
    /** 
 | 
     * Remove entry. 
 | 
     */ 
 | 
    remove(entry: Entry<T>) { 
 | 
        const prev = entry.prev; 
 | 
        const next = entry.next; 
 | 
        if (prev) { 
 | 
            prev.next = next; 
 | 
        } 
 | 
        else { 
 | 
            // Is head 
 | 
            this.head = next; 
 | 
        } 
 | 
        if (next) { 
 | 
            next.prev = prev; 
 | 
        } 
 | 
        else { 
 | 
            // Is tail 
 | 
            this.tail = prev; 
 | 
        } 
 | 
        entry.next = entry.prev = null; 
 | 
        this._len--; 
 | 
    } 
 | 
  
 | 
    /** 
 | 
     * Get length 
 | 
     */ 
 | 
    len(): number { 
 | 
        return this._len; 
 | 
    } 
 | 
  
 | 
    /** 
 | 
     * Clear list 
 | 
     */ 
 | 
    clear() { 
 | 
        this.head = this.tail = null; 
 | 
        this._len = 0; 
 | 
    } 
 | 
  
 | 
} 
 | 
  
 | 
/** 
 | 
 * LRU Cache 
 | 
 */ 
 | 
export default class LRU<T> { 
 | 
  
 | 
    private _list = new LinkedList<T>() 
 | 
  
 | 
    private _maxSize = 10 
 | 
  
 | 
    private _lastRemovedEntry: Entry<T> 
 | 
  
 | 
    private _map: Dictionary<Entry<T>> = {} 
 | 
  
 | 
    constructor(maxSize: number) { 
 | 
        this._maxSize = maxSize; 
 | 
    } 
 | 
  
 | 
    /** 
 | 
     * @return Removed value 
 | 
     */ 
 | 
    put(key: string | number, value: T): T { 
 | 
        const list = this._list; 
 | 
        const map = this._map; 
 | 
        let removed = null; 
 | 
        if (map[key] == null) { 
 | 
            const len = list.len(); 
 | 
            // Reuse last removed entry 
 | 
            let entry = this._lastRemovedEntry; 
 | 
  
 | 
            if (len >= this._maxSize && len > 0) { 
 | 
                // Remove the least recently used 
 | 
                const leastUsedEntry = list.head; 
 | 
                list.remove(leastUsedEntry); 
 | 
                delete map[leastUsedEntry.key]; 
 | 
  
 | 
                removed = leastUsedEntry.value; 
 | 
                this._lastRemovedEntry = leastUsedEntry; 
 | 
            } 
 | 
  
 | 
            if (entry) { 
 | 
                entry.value = value; 
 | 
            } 
 | 
            else { 
 | 
                entry = new Entry(value); 
 | 
            } 
 | 
            entry.key = key; 
 | 
            list.insertEntry(entry); 
 | 
            map[key] = entry; 
 | 
        } 
 | 
  
 | 
        return removed; 
 | 
    } 
 | 
  
 | 
    get(key: string | number): T { 
 | 
        const entry = this._map[key]; 
 | 
        const list = this._list; 
 | 
        if (entry != null) { 
 | 
            // Put the latest used entry in the tail 
 | 
            if (entry !== list.tail) { 
 | 
                list.remove(entry); 
 | 
                list.insertEntry(entry); 
 | 
            } 
 | 
  
 | 
            return entry.value; 
 | 
        } 
 | 
    } 
 | 
  
 | 
    /** 
 | 
     * Clear the cache 
 | 
     */ 
 | 
    clear() { 
 | 
        this._list.clear(); 
 | 
        this._map = {}; 
 | 
    } 
 | 
  
 | 
    len() { 
 | 
        return this._list.len(); 
 | 
    } 
 | 
} 
 |