From 5e269495606511787ec2dff814682a4ea9264af3 Mon Sep 17 00:00:00 2001 From: alina sireneva Date: Fri, 20 Sep 2024 02:43:43 +0300 Subject: [PATCH] chore!: use maps/sets from fuman --- .../methods/messages/_business-connection.ts | 2 +- .../methods/stickers/get-custom-emojis.ts | 2 +- .../src/highlevel/storage/service/peers.ts | 2 +- .../highlevel/storage/service/ref-messages.ts | 3 +- packages/core/src/network/mtproto-session.ts | 8 +- .../src/network/multi-session-connection.ts | 2 +- packages/core/src/utils/index.ts | 2 - packages/core/src/utils/long-utils.test.ts | 4 +- packages/core/src/utils/long-utils.ts | 74 +-------- packages/core/src/utils/lru-map.test.ts | 74 --------- packages/core/src/utils/lru-map.ts | 145 ------------------ packages/core/src/utils/lru-set.test.ts | 94 ------------ packages/core/src/utils/lru-set.ts | 65 -------- packages/dispatcher/src/state/service.ts | 3 +- 14 files changed, 21 insertions(+), 459 deletions(-) delete mode 100644 packages/core/src/utils/lru-map.test.ts delete mode 100644 packages/core/src/utils/lru-map.ts delete mode 100644 packages/core/src/utils/lru-set.test.ts delete mode 100644 packages/core/src/utils/lru-set.ts diff --git a/packages/core/src/highlevel/methods/messages/_business-connection.ts b/packages/core/src/highlevel/methods/messages/_business-connection.ts index a2a0388a..c9736ffb 100644 --- a/packages/core/src/highlevel/methods/messages/_business-connection.ts +++ b/packages/core/src/highlevel/methods/messages/_business-connection.ts @@ -1,8 +1,8 @@ import type { tl } from '@mtcute/tl' +import { LruMap } from '@fuman/utils' import type { RpcCallOptions } from '../../../network/network-manager.js' import type { MustEqual } from '../../../types/utils.js' -import { LruMap } from '../../../utils/lru-map.js' import type { ITelegramClient } from '../../client.types.js' import { getBusinessConnection } from '../premium/get-business-connection.js' diff --git a/packages/core/src/highlevel/methods/stickers/get-custom-emojis.ts b/packages/core/src/highlevel/methods/stickers/get-custom-emojis.ts index 478fe78a..0411155d 100644 --- a/packages/core/src/highlevel/methods/stickers/get-custom-emojis.ts +++ b/packages/core/src/highlevel/methods/stickers/get-custom-emojis.ts @@ -53,7 +53,7 @@ export async function getCustomEmojisFromMessages( } } - const arr = set.toArray() + const arr = [...set] if (!arr.length) return [] return getCustomEmojis(client, arr) diff --git a/packages/core/src/highlevel/storage/service/peers.ts b/packages/core/src/highlevel/storage/service/peers.ts index b3253de9..181a0d1b 100644 --- a/packages/core/src/highlevel/storage/service/peers.ts +++ b/packages/core/src/highlevel/storage/service/peers.ts @@ -1,10 +1,10 @@ import Long from 'long' import type { tl } from '@mtcute/tl' +import { LruMap } from '@fuman/utils' import type { ServiceOptions } from '../../../storage/service/base.js' import { BaseService } from '../../../storage/service/base.js' import { longFromFastString, longToFastString } from '../../../utils/long-utils.js' -import { LruMap } from '../../../utils/lru-map.js' import { getAllPeersFrom, parseMarkedPeerId, toggleChannelIdMark } from '../../../utils/peer-utils.js' import { extractUsernames } from '../../utils/peer-utils.js' import type { IPeersRepository } from '../repository/peers.js' diff --git a/packages/core/src/highlevel/storage/service/ref-messages.ts b/packages/core/src/highlevel/storage/service/ref-messages.ts index e25315f9..bc95deaa 100644 --- a/packages/core/src/highlevel/storage/service/ref-messages.ts +++ b/packages/core/src/highlevel/storage/service/ref-messages.ts @@ -1,6 +1,7 @@ +import { LruMap } from '@fuman/utils' + import type { ServiceOptions } from '../../../storage/service/base.js' import { BaseService } from '../../../storage/service/base.js' -import { LruMap } from '../../../utils/lru-map.js' import type { IReferenceMessagesRepository } from '../repository/ref-messages.js' export interface RefMessagesServiceOptions { diff --git a/packages/core/src/network/mtproto-session.ts b/packages/core/src/network/mtproto-session.ts index 4ad50b6d..d9440085 100644 --- a/packages/core/src/network/mtproto-session.ts +++ b/packages/core/src/network/mtproto-session.ts @@ -2,7 +2,7 @@ import Long from 'long' import type { mtp, tl } from '@mtcute/tl' import type { TlBinaryWriter, TlReaderMap, TlWriterMap } from '@mtcute/tl-runtime' import { TlSerializationCounter } from '@mtcute/tl-runtime' -import { type Deferred, Deque } from '@fuman/utils' +import { type Deferred, Deque, LruSet } from '@fuman/utils' import { MtcuteError } from '../types/index.js' import type { @@ -11,7 +11,7 @@ import type { } from '../utils/index.js' import { LongMap, - LruSet, + LongSet, SortedArray, compareLongs, getRandomInt, @@ -104,8 +104,8 @@ export class MtprotoSession { /// state /// // recent msg ids - recentOutgoingMsgIds: LruSet = new LruSet(1000, true) - recentIncomingMsgIds: LruSet = new LruSet(1000, true) + recentOutgoingMsgIds: LruSet = new LruSet(1000, LongSet) + recentIncomingMsgIds: LruSet = new LruSet(1000, LongSet) // queues queuedRpc: Deque = new Deque() diff --git a/packages/core/src/network/multi-session-connection.ts b/packages/core/src/network/multi-session-connection.ts index 2034213e..a98da6f4 100644 --- a/packages/core/src/network/multi-session-connection.ts +++ b/packages/core/src/network/multi-session-connection.ts @@ -228,7 +228,7 @@ export class MultiSessionConnection extends EventEmitter { for (let i = 0; i < this._connections.length; i++) { const conn = this._connections[i] - const total = conn._session.queuedRpc.length + conn._session.pendingMessages.size() + const total = conn._session.queuedRpc.length + conn._session.pendingMessages.size if (total < min) { min = total diff --git a/packages/core/src/utils/index.ts b/packages/core/src/utils/index.ts index 0a71698e..db307cdb 100644 --- a/packages/core/src/utils/index.ts +++ b/packages/core/src/utils/index.ts @@ -17,8 +17,6 @@ export * from './linked-list.js' export * from './links/index.js' export * from './logger.js' export * from './long-utils.js' -export * from './lru-map.js' -export * from './lru-set.js' export * from './misc-utils.js' export * from './peer-utils.js' export * from './sorted-array.js' diff --git a/packages/core/src/utils/long-utils.test.ts b/packages/core/src/utils/long-utils.test.ts index 9bf25852..1ee2f8b7 100644 --- a/packages/core/src/utils/long-utils.test.ts +++ b/packages/core/src/utils/long-utils.test.ts @@ -157,7 +157,7 @@ describe('LongMap', () => { map.set(Long.fromInt(123), 'test') map.set(Long.fromInt(456), 'test2') - expect(map.size()).toEqual(2) + expect(map.size).toEqual(2) }) }) @@ -216,6 +216,6 @@ describe('LongSet', () => { set.add(Long.fromInt(123)) set.add(Long.fromInt(456)) - expect(set.toArray()).toEqual([Long.fromInt(123), Long.fromInt(456)]) + expect([...set]).toEqual([Long.fromInt(123), Long.fromInt(456)]) }) }) diff --git a/packages/core/src/utils/long-utils.ts b/packages/core/src/utils/long-utils.ts index a1c969e8..20589220 100644 --- a/packages/core/src/utils/long-utils.ts +++ b/packages/core/src/utils/long-utils.ts @@ -1,5 +1,5 @@ import Long from 'long' -import { typed } from '@fuman/utils' +import { CustomMap, CustomSet, typed } from '@fuman/utils' import { getRandomInt } from './misc-utils.js' @@ -109,41 +109,9 @@ export function longFromFastString(val: string, unsigned = false): Long { * * Uses fast string representation internally. */ -export class LongMap { - private _map = new Map() - - set(key: Long, value: V): void { - this._map.set(longToFastString(key), value) - } - - has(key: Long): boolean { - return this._map.has(longToFastString(key)) - } - - get(key: Long): V | undefined { - return this._map.get(longToFastString(key)) - } - - delete(key: Long): void { - this._map.delete(longToFastString(key)) - } - - *keys(unsigned?: boolean): IterableIterator { - for (const v of this._map.keys()) { - yield longFromFastString(v, unsigned) - } - } - - values(): IterableIterator { - return this._map.values() - } - - clear(): void { - this._map.clear() - } - - size(): number { - return this._map.size +export class LongMap extends CustomMap { + constructor() { + super(longToFastString, longFromFastString) } } @@ -152,36 +120,8 @@ export class LongMap { * * Uses fast string representation internally */ -export class LongSet { - private _set = new Set() - - get size(): number { - return this._set.size - } - - add(val: Long): void { - this._set.add(longToFastString(val)) - } - - delete(val: Long): void { - this._set.delete(longToFastString(val)) - } - - has(val: Long): boolean { - return this._set.has(longToFastString(val)) - } - - clear(): void { - this._set.clear() - } - - toArray(): Long[] { - const arr: Long[] = [] - - for (const v of this._set) { - arr.push(longFromFastString(v)) - } - - return arr +export class LongSet extends CustomSet { + constructor() { + super(longToFastString, longFromFastString) } } diff --git a/packages/core/src/utils/lru-map.test.ts b/packages/core/src/utils/lru-map.test.ts deleted file mode 100644 index c0c8797e..00000000 --- a/packages/core/src/utils/lru-map.test.ts +++ /dev/null @@ -1,74 +0,0 @@ -import { describe, expect, it } from 'vitest' - -import { LruMap } from './lru-map.js' - -describe('LruMap', () => { - it('should maintain maximum size by removing oldest added', () => { - const lru = new LruMap(2) - - lru.set('first', 1) - lru.set('second', 2) - lru.set('third', 3) - - expect(lru.has('first')).toBeFalsy() - }) - - it('should update the last added item', () => { - const lru = new LruMap(2) - - lru.set('first', 1) - lru.set('second', 2) - lru.set('first', 42) - lru.set('third', 3) - - expect(lru.get('first')).toEqual(42) - expect(lru.has('second')).toBeFalsy() - }) - - it('should update the last used item', () => { - const lru = new LruMap(2) - - lru.set('first', 1) - lru.set('second', 2) - lru.get('first') - lru.set('third', 3) - lru.get('third') - - expect(lru.get('first')).toEqual(1) - expect(lru.has('second')).toBeFalsy() - }) - - it('should allow deleting items', () => { - const lru = new LruMap(2) - - lru.set('first', 1) - lru.set('second', 2) - lru.set('third', 3) // first is now deleted - lru.delete('second') - - expect(lru.has('first')).toBeFalsy() - expect(lru.has('second')).toBeFalsy() - expect(lru.has('third')).toBeTruthy() - }) - - it('should handle deleting all items', () => { - const lru = new LruMap(2) - - lru.set('first', 1) - lru.set('second', 2) - lru.delete('second') - lru.delete('first') - - expect(lru.has('first')).toBeFalsy() - expect(lru.has('second')).toBeFalsy() - }) - - it('should return undefined for non-existing items', () => { - const lru = new LruMap(2) - - lru.set('first', 1) - lru.set('second', 2) - - expect(lru.get('third')).toEqual(undefined) - }) -}) diff --git a/packages/core/src/utils/lru-map.ts b/packages/core/src/utils/lru-map.ts deleted file mode 100644 index 39d5301f..00000000 --- a/packages/core/src/utils/lru-map.ts +++ /dev/null @@ -1,145 +0,0 @@ -/* eslint-disable ts/no-unsafe-assignment */ -/* eslint-disable ts/no-unsafe-argument */ -// ^^ because of performance reasons -import { LongMap } from './long-utils.js' - -interface TwoWayLinkedList { - // k = key - k: K - // v = value - v: T - // p = previous - p?: TwoWayLinkedList - // n = next - n?: TwoWayLinkedList -} - -/** - * Simple class implementing LRU-like behaviour for a Map - * - * Can be used to handle local cache of *something* - * - * Uses two-way linked list internally to keep track of insertion/access order - */ -export class LruMap { - private _capacity: number - private _first?: TwoWayLinkedList - private _last?: TwoWayLinkedList - - private _map: Map> - - private _size = 0 - - constructor(capacity: number, forLong = false) { - this._capacity = capacity - - this._map = forLong ? (new LongMap() as any) : new Map() - } - - private _markUsed(item: TwoWayLinkedList): void { - if (item === this._first) { - return // already the most recently used - } - - if (item.p) { - if (item === this._last) { - this._last = item.p - } - item.p.n = item.n - } - - if (item.n) { - item.n.p = item.p - } - - item.p = undefined - item.n = this._first - - if (this._first) { - this._first.p = item - } - this._first = item - } - - get(key: K): V | undefined { - const item = this._map.get(key) - if (!item) return undefined - - this._markUsed(item) - - return item.v - } - - has(key: K): boolean { - return this._map.has(key) - } - - private _remove(item: TwoWayLinkedList): void { - if (item.p) { - this._last = item.p - this._last.n = undefined - } else { - // exhausted - this._last = undefined - this._first = undefined - } - - // remove strong refs to and from the item - item.p = item.n = undefined - this._map.delete(item.k) - this._size -= 1 - } - - set(key: K, value: V): void { - let item = this._map.get(key) - - if (item) { - // already in cache, update - item.v = value - this._markUsed(item) - - return - } - - item = { - k: key, - v: value, - // for jit to optimize stuff - n: undefined, - p: undefined, - } - this._map.set(key, item as any) - - if (this._first) { - this._first.p = item - item.n = this._first - } else { - // first item ever - this._last = item - } - - this._first = item - this._size += 1 - - if (this._size > this._capacity) { - // remove the last item - const oldest = this._last - - if (oldest) { - this._remove(oldest) - } - } - } - - delete(key: K): void { - const item = this._map.get(key) - if (item) this._remove(item) - } - - clear(): void { - this._map.clear() - this._first = undefined - this._last = undefined - this._size = 0 - } -} diff --git a/packages/core/src/utils/lru-set.test.ts b/packages/core/src/utils/lru-set.test.ts deleted file mode 100644 index 7285e06f..00000000 --- a/packages/core/src/utils/lru-set.test.ts +++ /dev/null @@ -1,94 +0,0 @@ -import Long from 'long' -import { describe, expect, it } from 'vitest' - -import { LruSet } from './lru-set.js' - -describe('LruSet', () => { - describe('for strings', () => { - it('when 1 item is added, it is in the set', () => { - const set = new LruSet(2) - - set.add('first') - expect(set.has('first')).toEqual(true) - }) - - it('when =capacity items are added, they are all in the set', () => { - const set = new LruSet(2) - - set.add('first') - set.add('second') - - expect(set.has('first')).toEqual(true) - expect(set.has('second')).toEqual(true) - }) - - it('when >capacity items are added, only the last are in the set', () => { - const set = new LruSet(2) - - set.add('first') - set.add('second') - set.add('third') - - expect(set.has('first')).toEqual(false) - expect(set.has('second')).toEqual(true) - expect(set.has('third')).toEqual(true) - }) - - it('when the same added is while not eliminated, it is ignored', () => { - const set = new LruSet(2) - - set.add('first') - set.add('second') - set.add('first') - set.add('third') - - expect(set.has('first')).toEqual(false) - expect(set.has('second')).toEqual(true) - expect(set.has('third')).toEqual(true) - }) - }) - - describe('for Longs', () => { - it('when 1 item is added, it is in the set', () => { - const set = new LruSet(2, true) - - set.add(Long.fromNumber(1)) - expect(set.has(Long.fromNumber(1))).toEqual(true) - }) - - it('when =capacity items are added, they are all in the set', () => { - const set = new LruSet(2, true) - - set.add(Long.fromNumber(1)) - set.add(Long.fromNumber(2)) - - expect(set.has(Long.fromNumber(1))).toEqual(true) - expect(set.has(Long.fromNumber(2))).toEqual(true) - }) - - it('when >capacity items are added, only the last are in the set', () => { - const set = new LruSet(2, true) - - set.add(Long.fromNumber(1)) - set.add(Long.fromNumber(2)) - set.add(Long.fromNumber(3)) - - expect(set.has(Long.fromNumber(1))).toEqual(false) - expect(set.has(Long.fromNumber(2))).toEqual(true) - expect(set.has(Long.fromNumber(3))).toEqual(true) - }) - - it('when the same added is while not eliminated, it is ignored', () => { - const set = new LruSet(2, true) - - set.add(Long.fromNumber(1)) - set.add(Long.fromNumber(2)) - set.add(Long.fromNumber(1)) - set.add(Long.fromNumber(3)) - - expect(set.has(Long.fromNumber(1))).toEqual(false) - expect(set.has(Long.fromNumber(2))).toEqual(true) - expect(set.has(Long.fromNumber(3))).toEqual(true) - }) - }) -}) diff --git a/packages/core/src/utils/lru-set.ts b/packages/core/src/utils/lru-set.ts deleted file mode 100644 index d80bab0a..00000000 --- a/packages/core/src/utils/lru-set.ts +++ /dev/null @@ -1,65 +0,0 @@ -/* eslint-disable ts/no-unsafe-argument */ -// ^^ because of performance reasons -import type Long from 'long' - -import { LongSet } from './long-utils.js' - -interface OneWayLinkedList { - v: T - n?: OneWayLinkedList -} - -/** - * Simple class implementing LRU-like behaviour for a Set. - * - * Note: this is not exactly LRU, but rather "least recently added" - * and doesn't mark items as recently added if they are already in the set. - * This is enough for our use case, so we don't bother with more complex implementation. - * - * Used to store recently received message IDs in {@link SessionConnection} - * - * Uses one-way linked list internally to keep track of insertion order - */ -export class LruSet { - private _capacity: number - private _first?: OneWayLinkedList - private _last?: OneWayLinkedList - - private _set: Set | LongSet - - constructor(capacity: number, forLong = false) { - this._capacity = capacity - - this._set = forLong ? new LongSet() : new Set() - } - - clear(): void { - this._first = this._last = undefined - this._set.clear() - } - - add(val: T): void { - if (this._set.has(val as any)) return - - if (!this._first) this._first = { v: val } - - if (!this._last) { - this._last = this._first - } else { - this._last.n = { v: val } - this._last = this._last.n - } - - this._set.add(val as any) - - if (this._set.size > this._capacity && this._first) { - // remove least recently used - this._set.delete(this._first.v as any) - this._first = this._first.n - } - } - - has(val: T): boolean { - return this._set.has(val as any) - } -} diff --git a/packages/dispatcher/src/state/service.ts b/packages/dispatcher/src/state/service.ts index 53731f1b..5bf4454d 100644 --- a/packages/dispatcher/src/state/service.ts +++ b/packages/dispatcher/src/state/service.ts @@ -1,5 +1,6 @@ -import { LruMap, asyncResettable, timers } from '@mtcute/core/utils.js' +import { asyncResettable, timers } from '@mtcute/core/utils.js' import type { MaybePromise } from '@mtcute/core' +import { LruMap } from '@fuman/utils' import type { IStateStorageProvider } from './provider.js'