mtcute/packages/core/src/utils/lru-string-set.ts

112 lines
3.1 KiB
TypeScript
Raw Normal View History

/* eslint-disable @typescript-eslint/no-explicit-any,@typescript-eslint/no-unsafe-assignment */
/* eslint-disable @typescript-eslint/no-unsafe-argument */
// ^^ because of performance reasons
import Long from 'long'
import { LongSet } from './long-utils'
2021-04-08 12:19:38 +03:00
interface OneWayLinkedList<T> {
v: T
n?: OneWayLinkedList<T>
}
/**
* Simple class implementing LRU-like behaviour for a set,
* falling back to objects when `Set` is not available.
2021-04-08 12:19:38 +03:00
*
2022-08-29 16:22:57 +03:00
* Used to store recently received message IDs in {@link SessionConnection}
2021-04-08 12:19:38 +03:00
*
* Uses one-way linked list internally to keep track of insertion order
*/
export class LruSet<T extends string | number | Long> {
2021-04-08 12:19:38 +03:00
private _capacity: number
private _first?: OneWayLinkedList<T>
private _last?: OneWayLinkedList<T>
2021-04-08 12:19:38 +03:00
private _set?: Set<T> | LongSet
private _obj?: object
2021-04-08 12:19:38 +03:00
private _objSize?: number
constructor(capacity: number, useObject = false, forLong = false) {
2021-04-08 12:19:38 +03:00
this._capacity = capacity
if (!forLong && (typeof Set === 'undefined' || useObject)) {
this._obj = Object.create(null)
2021-04-08 12:19:38 +03:00
this._objSize = 0
this.add = this._addForObj.bind(this)
this.has = this._hasForObj.bind(this)
this.clear = this._clearForObj.bind(this)
2021-04-08 12:19:38 +03:00
} else {
this._set = forLong ? new LongSet(useObject) : new Set()
2021-04-08 12:19:38 +03:00
this.add = this._addForSet.bind(this)
this.has = this._hasForSet.bind(this)
this.clear = this._clearForSet.bind(this)
2021-04-08 12:19:38 +03:00
}
}
readonly add: (val: T) => void
readonly has: (val: T) => boolean
readonly clear: () => void
private _clearForSet() {
this._first = this._last = undefined
this._set!.clear()
}
private _clearForObj() {
this._first = this._last = undefined
this._obj = {}
this._objSize = 0
}
2021-04-08 12:19:38 +03:00
private _addForSet(val: T) {
if (this._set!.has(val as any)) return
2021-04-08 12:19:38 +03:00
if (!this._first) this._first = { v: val }
2021-04-08 12:19:38 +03:00
if (!this._last) this._last = this._first
else {
this._last.n = { v: val }
2021-04-08 12:19:38 +03:00
this._last = this._last.n
}
this._set!.add(val as any)
2021-04-08 12:19:38 +03:00
if (this._set!.size > this._capacity && this._first) {
// remove least recently used
this._set!.delete(this._first.v as any)
2021-04-08 12:19:38 +03:00
this._first = this._first.n
}
}
private _hasForSet(val: T) {
return this._set!.has(val as any)
2021-04-08 12:19:38 +03:00
}
private _addForObj(val: T) {
if ((val as any) in this._obj!) return
2021-04-08 12:19:38 +03:00
if (!this._first) this._first = { v: val }
2021-04-08 12:19:38 +03:00
if (!this._last) this._last = this._first
else {
this._last.n = { v: val }
2021-04-08 12:19:38 +03:00
this._last = this._last.n
}
(this._obj as any)[val] = true
2021-04-08 12:19:38 +03:00
if (this._objSize === this._capacity) {
// remove least recently used
delete (this._obj as any)[this._first.v]
2021-04-08 12:19:38 +03:00
this._first = this._first.n
} else {
this._objSize! += 1
}
}
private _hasForObj(val: T) {
return (val as any) in this._obj!
2021-04-08 12:19:38 +03:00
}
}