convert LRUCache to ts

This commit is contained in:
Bruno Windels 2021-10-20 15:24:58 +02:00
parent 041cedbc58
commit 4fa285e85a
3 changed files with 40 additions and 29 deletions

View file

@ -14,7 +14,7 @@ See the License for the specific language governing permissions and
limitations under the License. limitations under the License.
*/ */
import {BaseLRUCache} from "../../../../utils/LRUCache.js"; import {BaseLRUCache} from "../../../../utils/LRUCache";
const DEFAULT_CACHE_SIZE = 10; const DEFAULT_CACHE_SIZE = 10;
/** /**

View file

@ -15,7 +15,7 @@ limitations under the License.
*/ */
import {MemberChange, RoomMember, EVENT_TYPE as MEMBER_EVENT_TYPE} from "../../members/RoomMember.js"; import {MemberChange, RoomMember, EVENT_TYPE as MEMBER_EVENT_TYPE} from "../../members/RoomMember.js";
import {LRUCache} from "../../../../utils/LRUCache.js"; import {LRUCache} from "../../../../utils/LRUCache";
export class MemberWriter { export class MemberWriter {
constructor(roomId) { constructor(roomId) {

View file

@ -18,8 +18,12 @@ limitations under the License.
* Very simple least-recently-used cache implementation * Very simple least-recently-used cache implementation
* that should be fast enough for very small cache sizes * that should be fast enough for very small cache sizes
*/ */
export class BaseLRUCache { export class BaseLRUCache<T> {
constructor(limit) {
private _limit: number;
private _entries: T[];
constructor(limit: number) {
this._limit = limit; this._limit = limit;
this._entries = []; this._entries = [];
} }
@ -27,7 +31,7 @@ export class BaseLRUCache {
get size() { return this._entries.length; } get size() { return this._entries.length; }
get limit() { return this._limit; } get limit() { return this._limit; }
_get(findEntryFn) { _get(findEntryFn: (T) => boolean) {
const idx = this._entries.findIndex(findEntryFn); const idx = this._entries.findIndex(findEntryFn);
if (idx !== -1) { if (idx !== -1) {
const entry = this._entries[idx]; const entry = this._entries[idx];
@ -40,7 +44,7 @@ export class BaseLRUCache {
} }
} }
_set(value, findEntryFn) { _set(value: T, findEntryFn: (T) => boolean) {
let indexToRemove = this._entries.findIndex(findEntryFn); let indexToRemove = this._entries.findIndex(findEntryFn);
this._entries.unshift(value); this._entries.unshift(value);
if (indexToRemove === -1) { if (indexToRemove === -1) {
@ -57,7 +61,7 @@ export class BaseLRUCache {
} }
} }
find(callback) { find(callback: (T) => boolean) {
// iterate backwards so least recently used items are found first // iterate backwards so least recently used items are found first
for (let i = this._entries.length - 1; i >= 0; i -= 1) { for (let i = this._entries.length - 1; i >= 0; i -= 1) {
const entry = this._entries[i]; const entry = this._entries[i];
@ -67,69 +71,76 @@ export class BaseLRUCache {
} }
} }
_onEvictEntry() {} _onEvictEntry(entry: T) {}
} }
export class LRUCache extends BaseLRUCache { export class LRUCache<T, K> extends BaseLRUCache<T> {
constructor(limit, keyFn) { private _keyFn: (T) => K;
constructor(limit, keyFn: (T) => K) {
super(limit); super(limit);
this._keyFn = keyFn; this._keyFn = keyFn;
} }
get(key) { get(key: K): T | undefined {
return this._get(e => this._keyFn(e) === key); return this._get(e => this._keyFn(e) === key);
} }
set(value) { set(value: T) {
const key = this._keyFn(value); const key = this._keyFn(value);
this._set(value, e => this._keyFn(e) === key); this._set(value, e => this._keyFn(e) === key);
} }
} }
export function tests() { export function tests() {
interface NameTuple {
id: number;
name: string;
}
return { return {
"can retrieve added entries": assert => { "can retrieve added entries": assert => {
const cache = new LRUCache(2, e => e.id); const cache = new LRUCache<NameTuple, number>(2, e => e.id);
cache.set({id: 1, name: "Alice"}); cache.set({id: 1, name: "Alice"});
cache.set({id: 2, name: "Bob"}); cache.set({id: 2, name: "Bob"});
assert.equal(cache.get(1).name, "Alice"); assert.equal(cache.get(1)!.name, "Alice");
assert.equal(cache.get(2).name, "Bob"); assert.equal(cache.get(2)!.name, "Bob");
}, },
"first entry is evicted first": assert => { "first entry is evicted first": assert => {
const cache = new LRUCache(2, e => e.id); const cache = new LRUCache<NameTuple, number>(2, e => e.id);
cache.set({id: 1, name: "Alice"}); cache.set({id: 1, name: "Alice"});
cache.set({id: 2, name: "Bob"}); cache.set({id: 2, name: "Bob"});
cache.set({id: 3, name: "Charly"}); cache.set({id: 3, name: "Charly"});
assert.equal(cache.get(1), undefined); assert.equal(cache.get(1), undefined);
assert.equal(cache.get(2).name, "Bob"); assert.equal(cache.get(2)!.name, "Bob");
assert.equal(cache.get(3).name, "Charly"); assert.equal(cache.get(3)!.name, "Charly");
assert.equal(cache._entries.length, 2); assert.equal(cache.size, 2);
}, },
"second entry is evicted if first is requested": assert => { "second entry is evicted if first is requested": assert => {
const cache = new LRUCache(2, e => e.id); const cache = new LRUCache<NameTuple, number>(2, e => e.id);
cache.set({id: 1, name: "Alice"}); cache.set({id: 1, name: "Alice"});
cache.set({id: 2, name: "Bob"}); cache.set({id: 2, name: "Bob"});
cache.get(1); cache.get(1);
cache.set({id: 3, name: "Charly"}); cache.set({id: 3, name: "Charly"});
assert.equal(cache.get(1).name, "Alice"); assert.equal(cache.get(1)!.name, "Alice");
assert.equal(cache.get(2), undefined); assert.equal(cache.get(2), undefined);
assert.equal(cache.get(3).name, "Charly"); assert.equal(cache.get(3)!.name, "Charly");
assert.equal(cache._entries.length, 2); assert.equal(cache.size, 2);
}, },
"setting an entry twice removes the first": assert => { "setting an entry twice removes the first": assert => {
const cache = new LRUCache(2, e => e.id); const cache = new LRUCache<NameTuple, number>(2, e => e.id);
cache.set({id: 1, name: "Alice"}); cache.set({id: 1, name: "Alice"});
cache.set({id: 2, name: "Bob"}); cache.set({id: 2, name: "Bob"});
cache.set({id: 1, name: "Al Ice"}); cache.set({id: 1, name: "Al Ice"});
cache.set({id: 3, name: "Charly"}); cache.set({id: 3, name: "Charly"});
assert.equal(cache.get(1).name, "Al Ice"); assert.equal(cache.get(1)!.name, "Al Ice");
assert.equal(cache.get(2), undefined); assert.equal(cache.get(2), undefined);
assert.equal(cache.get(3).name, "Charly"); assert.equal(cache.get(3)!.name, "Charly");
assert.equal(cache._entries.length, 2); assert.equal(cache.size, 2);
}, },
"evict callback is called": assert => { "evict callback is called": assert => {
let evictions = 0; let evictions = 0;
class CustomCache extends LRUCache { class CustomCache extends LRUCache<NameTuple, number> {
_onEvictEntry(entry) { _onEvictEntry(entry) {
assert.equal(entry.name, "Alice"); assert.equal(entry.name, "Alice");
evictions += 1; evictions += 1;
@ -143,7 +154,7 @@ export function tests() {
}, },
"evict callback is called when replacing entry with same identity": assert => { "evict callback is called when replacing entry with same identity": assert => {
let evictions = 0; let evictions = 0;
class CustomCache extends LRUCache { class CustomCache extends LRUCache<NameTuple, number> {
_onEvictEntry(entry) { _onEvictEntry(entry) {
assert.equal(entry.name, "Alice"); assert.equal(entry.name, "Alice");
evictions += 1; evictions += 1;