2023-09-03 02:37:51 +03:00
|
|
|
/* eslint-disable @typescript-eslint/no-explicit-any,@typescript-eslint/no-unsafe-assignment */
|
|
|
|
/* eslint-disable @typescript-eslint/no-unsafe-argument */
|
2023-06-05 03:30:48 +03:00
|
|
|
// ^^ because of performance reasons
|
|
|
|
import Long from 'long'
|
|
|
|
|
2021-11-23 00:03:59 +03:00
|
|
|
import { LongSet } from './long-utils'
|
|
|
|
|
2021-04-08 12:19:38 +03:00
|
|
|
interface OneWayLinkedList<T> {
|
|
|
|
v: T
|
|
|
|
n?: OneWayLinkedList<T>
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2021-11-23 00:03:59 +03:00
|
|
|
* 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
|
|
|
|
*/
|
2023-06-05 03:30:48 +03:00
|
|
|
export class LruSet<T extends string | number | Long> {
|
2021-04-08 12:19:38 +03:00
|
|
|
private _capacity: number
|
2021-11-23 00:03:59 +03:00
|
|
|
private _first?: OneWayLinkedList<T>
|
|
|
|
private _last?: OneWayLinkedList<T>
|
2021-04-08 12:19:38 +03:00
|
|
|
|
2021-11-23 00:03:59 +03:00
|
|
|
private _set?: Set<T> | LongSet
|
2023-06-05 03:30:48 +03:00
|
|
|
private _obj?: object
|
2021-04-08 12:19:38 +03:00
|
|
|
private _objSize?: number
|
|
|
|
|
2021-11-23 00:03:59 +03:00
|
|
|
constructor(capacity: number, useObject = false, forLong = false) {
|
2021-04-08 12:19:38 +03:00
|
|
|
this._capacity = capacity
|
|
|
|
|
2021-11-23 00:03:59 +03:00
|
|
|
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)
|
2021-11-23 00:03:59 +03:00
|
|
|
this.clear = this._clearForObj.bind(this)
|
2021-04-08 12:19:38 +03:00
|
|
|
} else {
|
2021-11-23 00:03:59 +03:00
|
|
|
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)
|
2021-11-23 00:03:59 +03:00
|
|
|
this.clear = this._clearForSet.bind(this)
|
2021-04-08 12:19:38 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-11-23 00:03:59 +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
|
|
|
|
2021-11-23 00:03:59 +03:00
|
|
|
private _addForSet(val: T) {
|
|
|
|
if (this._set!.has(val as any)) return
|
2021-04-08 12:19:38 +03:00
|
|
|
|
2021-11-23 00:03:59 +03:00
|
|
|
if (!this._first) this._first = { v: val }
|
2023-06-05 03:30:48 +03:00
|
|
|
|
2021-04-08 12:19:38 +03:00
|
|
|
if (!this._last) this._last = this._first
|
|
|
|
else {
|
2021-11-23 00:03:59 +03:00
|
|
|
this._last.n = { v: val }
|
2021-04-08 12:19:38 +03:00
|
|
|
this._last = this._last.n
|
|
|
|
}
|
|
|
|
|
2021-11-23 00:03:59 +03:00
|
|
|
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
|
2021-11-23 00:03:59 +03:00
|
|
|
this._set!.delete(this._first.v as any)
|
2021-04-08 12:19:38 +03:00
|
|
|
this._first = this._first.n
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-11-23 00:03:59 +03:00
|
|
|
private _hasForSet(val: T) {
|
|
|
|
return this._set!.has(val as any)
|
2021-04-08 12:19:38 +03:00
|
|
|
}
|
|
|
|
|
2021-11-23 00:03:59 +03:00
|
|
|
private _addForObj(val: T) {
|
2023-06-05 03:30:48 +03:00
|
|
|
if ((val as any) in this._obj!) return
|
2021-04-08 12:19:38 +03:00
|
|
|
|
2021-11-23 00:03:59 +03:00
|
|
|
if (!this._first) this._first = { v: val }
|
2023-06-05 03:30:48 +03:00
|
|
|
|
2021-04-08 12:19:38 +03:00
|
|
|
if (!this._last) this._last = this._first
|
|
|
|
else {
|
2021-11-23 00:03:59 +03:00
|
|
|
this._last.n = { v: val }
|
2021-04-08 12:19:38 +03:00
|
|
|
this._last = this._last.n
|
|
|
|
}
|
|
|
|
|
2023-06-05 03:30:48 +03:00
|
|
|
(this._obj as any)[val] = true
|
2021-04-08 12:19:38 +03:00
|
|
|
|
|
|
|
if (this._objSize === this._capacity) {
|
|
|
|
// remove least recently used
|
2023-06-05 03:30:48 +03:00
|
|
|
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
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-11-23 00:03:59 +03:00
|
|
|
private _hasForObj(val: T) {
|
2023-06-05 03:30:48 +03:00
|
|
|
return (val as any) in this._obj!
|
2021-04-08 12:19:38 +03:00
|
|
|
}
|
|
|
|
}
|