UNPKG

jjb-lc-designable

Version:

基于alibaba-designable源码二次封装的表单设计器。

323 lines (292 loc) 7.57 kB
/** * A doubly linked list-based Least Recently Used (LRU) cache. Will keep most * recently used items while discarding least recently used items when its limit * is reached. * * Licensed under MIT. Copyright (c) 2010 Rasmus Andersson <http://hunch.se/> * See README.md for details. * * Illustration of the design: * * entry entry entry entry * ______ ______ ______ ______ * | head |.newer => | |.newer => | |.newer => | tail | * | A | | B | | C | | D | * |______| <= older.|______| <= older.|______| <= older.|______| * * removed <-- <-- <-- <-- <-- <-- <-- <-- <-- <-- <-- added */ /* eslint-disable */ const NEWER = Symbol('newer') const OLDER = Symbol('older') function Entry(key: any, value: any) { this.key = key this.value = value this[NEWER] = undefined this[OLDER] = undefined } export class LRUMap<K, V> { size: number limit: number oldest: any newest: any _keymap: Map<K, { key: K; value: V }> constructor(limit: number, entries?: any) { if (typeof limit !== 'number') { // called as (entries) entries = limit limit = 0 } this.size = 0 this.limit = limit this.oldest = this.newest = undefined this._keymap = new Map() if (entries) { this.assign(entries) if (limit < 1) { this.limit = this.size } } } private _markEntryAsUsed(entry: any) { if (entry === this.newest) { // Already the most recenlty used entry, so no need to update the list return } // HEAD--------------TAIL // <.older .newer> // <--- add direction -- // A B C <D> E if (entry[NEWER]) { if (entry === this.oldest) { this.oldest = entry[NEWER] } entry[NEWER][OLDER] = entry[OLDER] // C <-- E. } if (entry[OLDER]) { entry[OLDER][NEWER] = entry[NEWER] // C. --> E } entry[NEWER] = undefined // D --x entry[OLDER] = this.newest // D. --> E if (this.newest) { this.newest[NEWER] = entry // E. <-- D } this.newest = entry } assign(entries: any) { let entry: any let limit = this.limit || Number.MAX_VALUE this._keymap.clear() const it = entries[Symbol.iterator]() for (let itv = it.next(); !itv.done; itv = it.next()) { const e = new Entry(itv.value[0], itv.value[1]) this._keymap.set(e.key, e) if (!entry) { this.oldest = e } else { entry[NEWER] = e e[OLDER] = entry } entry = e if (limit-- === 0) { throw new Error('overflow') } } this.newest = entry this.size = this._keymap.size } get(key: K) { // First, find our cache entry const entry = this._keymap.get(key) if (!entry) { return } // Not cached. Sorry. // As <key> was found in the cache, register it as being requested recently this._markEntryAsUsed(entry) return entry.value } set(key: K, value: V) { let entry = this._keymap.get(key) if (entry) { // update existing entry.value = value this._markEntryAsUsed(entry) return this } // new entry this._keymap.set(key, (entry = new Entry(key, value))) if (this.newest) { // link previous tail to the new tail (entry) this.newest[NEWER] = entry entry[OLDER] = this.newest } else { // we're first in -- yay this.oldest = entry } // add new entry to the end of the linked list -- it's now the freshest entry. this.newest = entry ++this.size if (this.size > this.limit) { // we hit the limit -- remove the head this.shift() } return this } shift() { // todo: handle special case when limit == 1 const entry = this.oldest if (entry) { if (this.oldest[NEWER]) { // advance the list this.oldest = this.oldest[NEWER] this.oldest[OLDER] = undefined } else { // the cache is exhausted this.oldest = undefined this.newest = undefined } // Remove last strong reference to <entry> and remove links from the purged // entry being returned: entry[NEWER] = entry[OLDER] = undefined this._keymap.delete(entry.key) --this.size return [entry.key, entry.value] } } find(key: K) { const e = this._keymap.get(key) return e ? e.value : undefined } has(key: K) { return this._keymap.has(key) } delete(key: any) { const entry = this._keymap.get(key) if (!entry) { return } this._keymap.delete(entry.key) if (entry[NEWER] && entry[OLDER]) { // relink the older entry with the newer entry entry[OLDER][NEWER] = entry[NEWER] entry[NEWER][OLDER] = entry[OLDER] } else if (entry[NEWER]) { // remove the link to us entry[NEWER][OLDER] = undefined // link the newer entry to head this.oldest = entry[NEWER] } else if (entry[OLDER]) { // remove the link to us entry[OLDER][NEWER] = undefined // link the newer entry to head this.newest = entry[OLDER] } else { // if(entry[OLDER] === undefined && entry.newer === undefined) { this.oldest = this.newest = undefined } this.size-- return entry.value } clear() { // Not clearing links should be safe, as we don't expose live links to user this.oldest = this.newest = undefined this.size = 0 this._keymap.clear() } keys() { return new KeyIterator(this.oldest) } values() { return new ValueIterator(this.oldest) } entries() {} forEach(fun: (value: any, key: any, ctx: object) => void, thisObj: any) { if (typeof thisObj !== 'object') { thisObj = this } let entry = this.oldest while (entry) { fun.call(thisObj, entry.value, entry.key, this) entry = entry[NEWER] } } toJSON() { const s = new Array(this.size) let i = 0 let entry = this.oldest while (entry) { s[i++] = { key: entry.key, value: entry.value } entry = entry[NEWER] } return s } toString() { let s = '' let entry = this.oldest while (entry) { s += String(entry.key) + ':' + entry.value entry = entry[NEWER] if (entry) { s += ' < ' } } return s } [Symbol.iterator]() { return new EntryIterator(this.oldest) } } class EntryIterator { entry: any constructor(oldestEntry: any) { this.entry = oldestEntry } [Symbol.iterator]() { return this } next() { const ent = this.entry if (ent) { this.entry = ent[NEWER] return { done: false, value: [ent.key, ent.value] } } else { return { done: true, value: undefined } } } } class KeyIterator { entry: any constructor(oldestEntry: any) { this.entry = oldestEntry } [Symbol.iterator]() { return this } next() { const ent = this.entry if (ent) { this.entry = ent[NEWER] return { done: false, value: ent.key } } else { return { done: true, value: undefined } } } } class ValueIterator { entry: any constructor(oldestEntry: any) { this.entry = oldestEntry } [Symbol.iterator]() { return this } next() { const ent = this.entry if (ent) { this.entry = ent[NEWER] return { done: false, value: ent.value } } else { return { done: true, value: undefined } } } }