UNPKG

spica

Version:

Supervisor, Coroutine, Channel, select, AtomicPromise, Cancellation, Cache, List, Queue, Stack, and some utils.

215 lines (212 loc) 5.8 kB
import { max } from './alias'; import { IterableDict } from './dict'; import { List } from './clist'; class Entry<K, V> implements List.Node { constructor( public key: K, public value: V, ) { } public next?: this = undefined; public prev?: this = undefined; } export class TLRU<K, V> implements IterableDict<K, V> { constructor( private readonly capacity: number, private readonly demotion: number = 2, private readonly window: number = 0, private readonly retrial: boolean = true, ) { assert(capacity > 0); } private readonly dict = new Map<K, Entry<K, V>>(); private readonly list = new List<Entry<K, V>>(); private handM?: Entry<K, V> = undefined; private handG?: Entry<K, V> = undefined; private count = 0; private state = false; public get length(): number { return this.list.length; } public get size(): number { return this.list.length; } private return(): void { const { list } = this; if (this.count !== -1 && this.handM === list.last && this.handG !== list.last && this.handG !== undefined) { if (this.count >= 0) { // 先頭の次からなので1周-1 assert(this.count < this.capacity); this.count = -max( //list.length * this.demotion / 100 / max(this.count / list.length * this.demotion, 1) | 0, (list.length - this.count) * this.demotion / 100 | 0, list.length * this.window / 100 - this.count | 0, 1) - 1; assert(this.count < 0); } } else { this.handM = undefined; this.count = 0; } } private replace(key: K, value: V): void { const { dict, list } = this; if (this.handM === list.last) { this.return(); } this.handM ??= this.handG; // 非延命 if (this.count < 0 && !this.retrial) { const entry = this.handG!; dict.delete(entry.key); dict.set(key, entry); entry.key = key; entry.value = value; this.handG = this.handG !== list.head ? entry.prev : undefined; } // 延命 else if (this.count < 0) { const entry = list.last!; dict.delete(entry.key); dict.set(key, entry); entry.key = key; entry.value = value; this.escape(entry); list.delete(entry); if (this.handG !== list.head && this.handG !== undefined) { list.insert(entry, this.handG); } else { list.unshift(entry); } this.handG = this.handG !== list.head ? entry.prev : undefined; } else { assert(this.count >= 0); const entry = list.last!; dict.delete(entry.key); dict.set(key, entry); entry.key = key; entry.value = value; this.escape(entry); list.delete(entry); if (this.handG !== list.head && this.handG !== undefined) { list.insert(entry, this.handG.next); } else { list.unshift(entry); } } if (this.handM === this.handG && this.handM !== list.last && this.handM !== undefined) { this.handM = this.handM.next; } assert(this.count >= 0 || this.handM === list.last); if (this.handM !== undefined) { ++this.count; } } public evict(): [K, V] | undefined { const { list } = this; const entry = list.last; if (entry === undefined) return; this.delete(entry.key); return [entry.key, entry.value]; } public add(key: K, value: V): boolean { const { dict, list } = this; if (list.length === this.capacity) { this.state = true; this.replace(key, value); } else { const entry = new Entry(key, value); dict.set(key, entry); if (this.state && this.handG !== list.head && this.handG !== undefined) { list.insert(entry, this.handG.next); } else { list.unshift(entry); } } assert(dict.size <= this.capacity); assert(list.length <= this.capacity); return true; } public set(key: K, value: V): this { const entry = this.dict.get(key); if (entry === undefined) { this.add(key, value); } else { entry.value = value; } assert(this.dict.size <= this.capacity); assert(this.list.length <= this.capacity); return this; } private escape(entry: Entry<K, V>): void { const { list } = this; assert(list.length !== 0); if (list.length === 1) { this.handM = undefined; this.handG = undefined; this.count = 0; return; } if (entry === this.handM) { this.handM = this.handM !== list.last ? this.handM.next : this.handM.prev; } if (entry === this.handG) { this.handG = this.handG !== list.head ? this.handG.prev : this.handG.next; } } public get(key: K): V | undefined { const { dict, list } = this; const entry = dict.get(key); if (entry === undefined) return; if (entry !== list.head) { this.escape(entry); list.delete(entry); list.unshift(entry); } this.handG ??= entry; return entry.value; } public has(key: K): boolean { return this.dict.has(key); } public delete(key: K): boolean { const { dict, list } = this; const entry = dict.get(key); if (entry === undefined) return false; this.escape(entry); list.delete(entry); assert(entry !== this.handM); assert(entry !== this.handG); return dict.delete(key); } public clear(): void { this.dict.clear(); this.list.clear(); this.handM = undefined; this.handG = undefined; this.count = 0; this.state = false; } public *[Symbol.iterator](): Iterator<[K, V], undefined, undefined> { for (const { key, value } of this.list) { yield [key, value]; } } }