spica
Version:
Supervisor, Coroutine, Channel, select, AtomicPromise, Cancellation, Cache, List, Queue, Stack, and some utils.
105 lines (102 loc) • 2.71 kB
text/typescript
import { List } from './clist';
class Entry<K, V> implements List.Node {
constructor(
public key: K,
public value: V,
) {
}
public partition: 1 | 2 = 1;
public next?: this = undefined;
public prev?: this = undefined;
}
export class SLRU<K, V> {
constructor(
private readonly capacity: number,
private readonly partition = 80,
) {
}
private readonly dict = new Map<K, Entry<K, V>>();
private readonly T1 = new List<Entry<K, V>>();
private readonly T2 = new List<Entry<K, V>>();
private add_T1(entry: Entry<K, V>): void {
assert(!entry.next);
if (this.length === this.capacity) {
assert(entry.partition === 1);
this.dict.delete(this.del_T1_LRU().key);
}
entry.partition = 1;
this.T1.unshift(entry);
}
private add_T2(entry: Entry<K, V>): void {
assert(!entry.next);
if (entry.partition === 1 &&
this.T2.length >= this.capacity * this.partition / 100 >>> 0) {
this.add_T1(this.del_T2_LRU());
}
entry.partition = 2;
this.T2.unshift(entry);
assert(this.T2.length <= this.capacity * this.partition / 100 >>> 0);
}
private del_T1(entry: Entry<K, V>): void {
assert(entry.next);
this.T1.delete(entry);
}
private del_T2(entry: Entry<K, V>): void {
assert(entry.next);
this.T2.delete(entry);
}
private del_T1_LRU(): Entry<K, V> {
assert(this.T1.length);
return this.T1.pop()!;
}
private del_T2_LRU(): Entry<K, V> {
assert(this.T2.length);
return this.T2.pop()!;
}
public get length(): number {
return this.T1.length + this.T2.length;
}
public set(key: K, value: V): this {
const entry = this.dict.get(key);
switch (entry?.partition) {
case 1:
case 2:
entry.value = value;
break;
default:
this.add_T1(new Entry(key, value));
this.dict.set(key, this.T1.head!);
assert(this.length <= this.capacity);
assert(this.length === this.dict.size);
break;
}
return this;
}
public get(key: K): V | undefined {
const entry = this.dict.get(key);
switch (entry?.partition) {
case 1:
this.del_T1(entry);
this.add_T2(entry);
assert(this.length <= this.capacity);
assert(this.length === this.dict.size);
return entry.value;
case 2:
this.del_T2(entry);
this.add_T2(entry);
assert(this.length <= this.capacity);
assert(this.length === this.dict.size);
return entry.value;
default:
return;
}
}
public has(key: K): boolean {
return this.dict.has(key);
}
public clear() {
this.dict.clear();
this.T1.clear();
this.T2.clear();
}
}