chore!: use deque from fuman
This commit is contained in:
parent
8b4f4984a1
commit
19ab208be0
10 changed files with 13 additions and 442 deletions
|
@ -1,9 +1,8 @@
|
||||||
import type { tl } from '@mtcute/tl'
|
import type { tl } from '@mtcute/tl'
|
||||||
import { AsyncLock, Deferred, timers } from '@fuman/utils'
|
import { AsyncLock, Deferred, Deque, timers } from '@fuman/utils'
|
||||||
|
|
||||||
import { MtArgumentError, MtTimeoutError } from '../../types/errors.js'
|
import { MtArgumentError, MtTimeoutError } from '../../types/errors.js'
|
||||||
import type { MaybePromise } from '../../types/utils.js'
|
import type { MaybePromise } from '../../types/utils.js'
|
||||||
import { Deque } from '../../utils/deque.js'
|
|
||||||
import { getMarkedPeerId } from '../../utils/peer-utils.js'
|
import { getMarkedPeerId } from '../../utils/peer-utils.js'
|
||||||
import type { ITelegramClient } from '../client.types.js'
|
import type { ITelegramClient } from '../client.types.js'
|
||||||
import { getPeerDialogs } from '../methods/dialogs/get-peer-dialogs.js'
|
import { getPeerDialogs } from '../methods/dialogs/get-peer-dialogs.js'
|
||||||
|
@ -55,7 +54,7 @@ export class Conversation {
|
||||||
private _lock = new AsyncLock()
|
private _lock = new AsyncLock()
|
||||||
|
|
||||||
private _pendingEditMessage: Map<number, QueuedHandler<Message>> = new Map()
|
private _pendingEditMessage: Map<number, QueuedHandler<Message>> = new Map()
|
||||||
private _recentEdits = new Deque<Message>(10)
|
private _recentEdits = new Deque<Message>(undefined, { capacity: 10 })
|
||||||
|
|
||||||
private _pendingRead: Map<number, QueuedHandler<void>> = new Map()
|
private _pendingRead: Map<number, QueuedHandler<void>> = new Map()
|
||||||
|
|
||||||
|
@ -627,7 +626,7 @@ export class Conversation {
|
||||||
private _processRecentEdits() {
|
private _processRecentEdits() {
|
||||||
if (!this._recentEdits.length) return
|
if (!this._recentEdits.length) return
|
||||||
|
|
||||||
const iter = this._recentEdits.iter()
|
const iter = this._recentEdits[Symbol.iterator]()
|
||||||
let it
|
let it
|
||||||
|
|
||||||
while (!(it = iter.next()).done) {
|
while (!(it = iter.next()).done) {
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
import { tl } from '@mtcute/tl'
|
import { tl } from '@mtcute/tl'
|
||||||
import { AsyncLock, ConditionVariable, timers } from '@fuman/utils'
|
|
||||||
import Long from 'long'
|
import Long from 'long'
|
||||||
|
import { AsyncLock, ConditionVariable, Deque, timers } from '@fuman/utils'
|
||||||
|
|
||||||
import { MtArgumentError } from '../../types/errors.js'
|
import { MtArgumentError } from '../../types/errors.js'
|
||||||
import type { MaybePromise } from '../../types/utils.js'
|
import type { MaybePromise } from '../../types/utils.js'
|
||||||
|
@ -9,7 +9,6 @@ import type {
|
||||||
Logger,
|
Logger,
|
||||||
} from '../../utils/index.js'
|
} from '../../utils/index.js'
|
||||||
import {
|
import {
|
||||||
Deque,
|
|
||||||
EarlyTimer,
|
EarlyTimer,
|
||||||
SortedLinkedList,
|
SortedLinkedList,
|
||||||
getBarePeerId,
|
getBarePeerId,
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
import type { tl } from '@mtcute/tl'
|
import type { tl } from '@mtcute/tl'
|
||||||
import type { AsyncLock, ConditionVariable, timers } from '@fuman/utils'
|
import type { AsyncLock, ConditionVariable, Deque, timers } from '@fuman/utils'
|
||||||
|
|
||||||
import type { Deque, EarlyTimer, Logger, SortedLinkedList } from '../../utils/index.js'
|
import type { EarlyTimer, Logger, SortedLinkedList } from '../../utils/index.js'
|
||||||
import type { CurrentUserInfo } from '../storage/service/current-user.js'
|
import type { CurrentUserInfo } from '../storage/service/current-user.js'
|
||||||
import type { PeersIndex } from '../types/peers/peers-index.js'
|
import type { PeersIndex } from '../types/peers/peers-index.js'
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
import { Deque } from '../../utils/deque.js'
|
import { Deque } from '@fuman/utils'
|
||||||
|
|
||||||
import type { ITelegramClient } from '../client.types.js'
|
import type { ITelegramClient } from '../client.types.js'
|
||||||
|
|
||||||
type Resolve<T> = (value: T | PromiseLike<T>) => void
|
type Resolve<T> = (value: T | PromiseLike<T>) => void
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
import { Deque } from '../../utils/deque.js'
|
import { Deque } from '@fuman/utils'
|
||||||
|
|
||||||
export class RpsMeter {
|
export class RpsMeter {
|
||||||
_hits: Deque<bigint>
|
_hits: Deque<bigint>
|
||||||
|
@ -12,7 +12,7 @@ export class RpsMeter {
|
||||||
throw new Error('RPS meter is not supported on this platform')
|
throw new Error('RPS meter is not supported on this platform')
|
||||||
}
|
}
|
||||||
|
|
||||||
this._hits = new Deque<bigint>(size)
|
this._hits = new Deque<bigint>(undefined, { capacity: size })
|
||||||
this.time = BigInt(time) * BigInt(1e6)
|
this.time = BigInt(time) * BigInt(1e6)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -27,7 +27,7 @@ export class RpsMeter {
|
||||||
const now = process.hrtime.bigint()
|
const now = process.hrtime.bigint()
|
||||||
const window = now - this.time
|
const window = now - this.time
|
||||||
// find the first hit within the last `time` ms
|
// find the first hit within the last `time` ms
|
||||||
const iter = this._hits.iter()
|
const iter = this._hits[Symbol.iterator]()
|
||||||
let first = iter.next()
|
let first = iter.next()
|
||||||
let idx = 0
|
let idx = 0
|
||||||
|
|
||||||
|
|
|
@ -2,7 +2,7 @@ import Long from 'long'
|
||||||
import type { mtp, tl } from '@mtcute/tl'
|
import type { mtp, tl } from '@mtcute/tl'
|
||||||
import type { TlBinaryWriter, TlReaderMap, TlWriterMap } from '@mtcute/tl-runtime'
|
import type { TlBinaryWriter, TlReaderMap, TlWriterMap } from '@mtcute/tl-runtime'
|
||||||
import { TlSerializationCounter } from '@mtcute/tl-runtime'
|
import { TlSerializationCounter } from '@mtcute/tl-runtime'
|
||||||
import type { Deferred } from '@fuman/utils'
|
import { type Deferred, Deque } from '@fuman/utils'
|
||||||
|
|
||||||
import { MtcuteError } from '../types/index.js'
|
import { MtcuteError } from '../types/index.js'
|
||||||
import type {
|
import type {
|
||||||
|
@ -10,8 +10,6 @@ import type {
|
||||||
Logger,
|
Logger,
|
||||||
} from '../utils/index.js'
|
} from '../utils/index.js'
|
||||||
import {
|
import {
|
||||||
Deque,
|
|
||||||
|
|
||||||
LongMap,
|
LongMap,
|
||||||
LruSet,
|
LruSet,
|
||||||
SortedArray,
|
SortedArray,
|
||||||
|
|
|
@ -1489,7 +1489,7 @@ export class SessionConnection extends PersistentConnection {
|
||||||
} else {
|
} else {
|
||||||
// in case rpc wasn't sent yet (or had some error),
|
// in case rpc wasn't sent yet (or had some error),
|
||||||
// we can simply remove it from queue
|
// we can simply remove it from queue
|
||||||
this._session.queuedRpc.remove(rpc)
|
this._session.queuedRpc.removeBy(it => it === rpc)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,173 +0,0 @@
|
||||||
import { describe, expect, it } from 'vitest'
|
|
||||||
|
|
||||||
import { Deque } from './deque.js'
|
|
||||||
|
|
||||||
describe('Deque', () => {
|
|
||||||
function setupWrapping() {
|
|
||||||
const d = new Deque<number>(Infinity, /* capacity: */ 8)
|
|
||||||
|
|
||||||
for (let i = 1; i <= 7; i++) {
|
|
||||||
d.pushBack(i)
|
|
||||||
}
|
|
||||||
|
|
||||||
// [1, 2, 3, 4, 5, 6, 7, _]
|
|
||||||
|
|
||||||
d.popFront()
|
|
||||||
d.popFront()
|
|
||||||
d.popFront()
|
|
||||||
|
|
||||||
// [_, _, _, 4, 5, 6, 7, _]
|
|
||||||
|
|
||||||
d.pushBack(8)
|
|
||||||
d.pushBack(9)
|
|
||||||
d.pushBack(10)
|
|
||||||
|
|
||||||
// [9, 10, _, 4, 5, 6, 7, 8]
|
|
||||||
return d
|
|
||||||
}
|
|
||||||
|
|
||||||
it('should push items to the end correctly', () => {
|
|
||||||
const d = new Deque<number>()
|
|
||||||
|
|
||||||
d.pushBack(1)
|
|
||||||
d.pushBack(2)
|
|
||||||
d.pushBack(3)
|
|
||||||
|
|
||||||
expect(d.toArray()).eql([1, 2, 3])
|
|
||||||
expect([...d.iter()]).eql([1, 2, 3])
|
|
||||||
})
|
|
||||||
|
|
||||||
it('should push items to the start correctly', () => {
|
|
||||||
const d = new Deque<number>()
|
|
||||||
|
|
||||||
d.pushFront(1)
|
|
||||||
d.pushFront(2)
|
|
||||||
d.pushFront(3)
|
|
||||||
|
|
||||||
expect(d.toArray()).eql([3, 2, 1])
|
|
||||||
})
|
|
||||||
|
|
||||||
it('should pop items from the end correctly', () => {
|
|
||||||
const d = new Deque<number>()
|
|
||||||
|
|
||||||
d.pushBack(1)
|
|
||||||
d.pushBack(2)
|
|
||||||
d.pushBack(3)
|
|
||||||
|
|
||||||
expect(d.popBack()).eql(3)
|
|
||||||
expect(d.popBack()).eql(2)
|
|
||||||
expect(d.popBack()).eql(1)
|
|
||||||
})
|
|
||||||
|
|
||||||
it('should pop items from the start correctly', () => {
|
|
||||||
const d = new Deque<number>()
|
|
||||||
|
|
||||||
d.pushFront(1)
|
|
||||||
d.pushFront(2)
|
|
||||||
d.pushFront(3)
|
|
||||||
|
|
||||||
expect(d.popFront()).eql(3)
|
|
||||||
expect(d.popFront()).eql(2)
|
|
||||||
expect(d.popFront()).eql(1)
|
|
||||||
})
|
|
||||||
|
|
||||||
it('should return the correct length', () => {
|
|
||||||
const d = new Deque<number>()
|
|
||||||
|
|
||||||
d.pushBack(1)
|
|
||||||
d.pushBack(2)
|
|
||||||
d.pushBack(3)
|
|
||||||
|
|
||||||
expect(d.length).eql(3)
|
|
||||||
})
|
|
||||||
|
|
||||||
it('accessors should correctly wrap around', () => {
|
|
||||||
const d = setupWrapping()
|
|
||||||
|
|
||||||
expect(d.toArray()).eql([4, 5, 6, 7, 8, 9, 10])
|
|
||||||
expect([...d.iter()]).eql([4, 5, 6, 7, 8, 9, 10])
|
|
||||||
expect(d.at(6)).eql(10)
|
|
||||||
})
|
|
||||||
|
|
||||||
it('should handle maxLength by removing items from the other end', () => {
|
|
||||||
const d = new Deque<number>(4)
|
|
||||||
|
|
||||||
d.pushBack(1)
|
|
||||||
d.pushBack(2)
|
|
||||||
d.pushBack(3)
|
|
||||||
d.pushBack(4)
|
|
||||||
|
|
||||||
d.pushBack(5)
|
|
||||||
expect(d.toArray()).eql([2, 3, 4, 5])
|
|
||||||
|
|
||||||
d.pushFront(6)
|
|
||||||
expect(d.toArray()).eql([6, 2, 3, 4])
|
|
||||||
})
|
|
||||||
|
|
||||||
it('should correctly resize', () => {
|
|
||||||
const d = new Deque<number>(Infinity, /* capacity: */ 8)
|
|
||||||
|
|
||||||
for (let i = 0; i <= 16; i++) {
|
|
||||||
d.pushBack(i)
|
|
||||||
}
|
|
||||||
|
|
||||||
const expected = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16]
|
|
||||||
|
|
||||||
expect(d.length).eql(17)
|
|
||||||
expect(d.toArray()).eql(expected)
|
|
||||||
expect([...d.iter()]).eql(expected)
|
|
||||||
expect(d.at(16)).eql(16)
|
|
||||||
})
|
|
||||||
|
|
||||||
it('should correctly remove items', () => {
|
|
||||||
const d = new Deque<number>(Infinity, /* capacity: */ 16)
|
|
||||||
|
|
||||||
d.pushBack(1)
|
|
||||||
d.pushBack(2)
|
|
||||||
d.pushBack(3)
|
|
||||||
d.pushBack(4)
|
|
||||||
d.pushBack(5)
|
|
||||||
|
|
||||||
d.remove(1)
|
|
||||||
d.remove(4)
|
|
||||||
|
|
||||||
expect(d.toArray()).eql([2, 3, 5])
|
|
||||||
})
|
|
||||||
|
|
||||||
it('should correctly remove items by predicate (the first matched)', () => {
|
|
||||||
const d = new Deque<number>(Infinity, /* capacity: */ 4)
|
|
||||||
|
|
||||||
d.pushBack(1)
|
|
||||||
d.pushBack(2)
|
|
||||||
d.pushBack(3)
|
|
||||||
d.pushBack(4)
|
|
||||||
d.pushBack(5)
|
|
||||||
|
|
||||||
d.removeBy(it => it >= 2)
|
|
||||||
|
|
||||||
expect(d.toArray()).eql([1, 3, 4, 5])
|
|
||||||
})
|
|
||||||
|
|
||||||
it('should correctly peek at edges', () => {
|
|
||||||
const d = new Deque<number>()
|
|
||||||
|
|
||||||
d.pushBack(1)
|
|
||||||
d.pushBack(2)
|
|
||||||
|
|
||||||
expect(d.peekFront()).eql(1)
|
|
||||||
expect(d.peekBack()).eql(2)
|
|
||||||
})
|
|
||||||
|
|
||||||
it('should correctly clear', () => {
|
|
||||||
const d = new Deque<number>()
|
|
||||||
|
|
||||||
d.pushBack(1)
|
|
||||||
d.pushBack(2)
|
|
||||||
|
|
||||||
d.clear()
|
|
||||||
|
|
||||||
expect(d.length).eql(0)
|
|
||||||
expect(d.toArray()).eql([])
|
|
||||||
expect([...d.iter()]).eql([])
|
|
||||||
})
|
|
||||||
})
|
|
|
@ -1,252 +0,0 @@
|
||||||
// ^^ because of performance reasons
|
|
||||||
const MIN_INITIAL_CAPACITY = 8
|
|
||||||
|
|
||||||
// System.arraycopy from java
|
|
||||||
function arraycopy<T>(src: T[], srcPos: number, dest: T[], destPos: number, length: number) {
|
|
||||||
for (let i = 0; i < length; i++) {
|
|
||||||
dest[destPos + i] = src[srcPos + i]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Deque implementation.
|
|
||||||
* `undefined` values are not allowed
|
|
||||||
*
|
|
||||||
* Based on Java implementation
|
|
||||||
*/
|
|
||||||
export class Deque<T> {
|
|
||||||
// another implementation variant would be to use
|
|
||||||
// blocks of fixed size instead of a single array
|
|
||||||
// to avoid copying stuff around
|
|
||||||
protected _elements: (T | undefined)[]
|
|
||||||
protected _head = 0
|
|
||||||
protected _tail = 0
|
|
||||||
protected _capacity: number
|
|
||||||
|
|
||||||
constructor(
|
|
||||||
readonly maxLength: number = Infinity,
|
|
||||||
minCapacity: number = maxLength === Infinity ? MIN_INITIAL_CAPACITY : maxLength,
|
|
||||||
) {
|
|
||||||
let capacity = minCapacity
|
|
||||||
|
|
||||||
if (capacity < MIN_INITIAL_CAPACITY) {
|
|
||||||
capacity = MIN_INITIAL_CAPACITY
|
|
||||||
}
|
|
||||||
if (capacity !== MIN_INITIAL_CAPACITY) {
|
|
||||||
// Find the best power of two to hold elements.
|
|
||||||
capacity |= capacity >>> 1
|
|
||||||
capacity |= capacity >>> 2
|
|
||||||
capacity |= capacity >>> 4
|
|
||||||
capacity |= capacity >>> 8
|
|
||||||
capacity |= capacity >>> 16
|
|
||||||
capacity += 1
|
|
||||||
|
|
||||||
if (capacity < 0) {
|
|
||||||
// too many
|
|
||||||
capacity >>>= 1
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
this._elements = new Array<T | undefined>(capacity)
|
|
||||||
this._capacity = capacity
|
|
||||||
}
|
|
||||||
|
|
||||||
protected _resize(): void {
|
|
||||||
const p = this._head
|
|
||||||
const n = this._capacity
|
|
||||||
const r = n - p // number of elements to the right of the head
|
|
||||||
|
|
||||||
const newCapacity = n << 1
|
|
||||||
if (newCapacity < 0) throw new Error('Deque is too big')
|
|
||||||
|
|
||||||
const arr = new Array<T | undefined>(newCapacity)
|
|
||||||
|
|
||||||
// copy items to the new array
|
|
||||||
// copy head till the end of arr
|
|
||||||
arraycopy(this._elements, p, arr, 0, r)
|
|
||||||
// copy from start to tail
|
|
||||||
arraycopy(this._elements, 0, arr, r, p)
|
|
||||||
|
|
||||||
this._elements = arr
|
|
||||||
this._head = 0
|
|
||||||
this._tail = n
|
|
||||||
this._capacity = newCapacity
|
|
||||||
}
|
|
||||||
|
|
||||||
pushBack(item: T): void {
|
|
||||||
if (item === undefined) throw new Error('item can not be undefined')
|
|
||||||
|
|
||||||
this._elements[this._tail] = item
|
|
||||||
|
|
||||||
if ((this._tail = (this._tail + 1) & (this._capacity - 1)) === this._head) {
|
|
||||||
this._resize()
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this.length > this.maxLength) {
|
|
||||||
this.popFront()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pushFront(item: T): void {
|
|
||||||
if (item === undefined) throw new Error('item can not be undefined')
|
|
||||||
|
|
||||||
this._elements[(this._head = (this._head - 1) & (this._capacity - 1))] = item
|
|
||||||
|
|
||||||
if (this._head === this._tail) {
|
|
||||||
this._resize()
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this.length > this.maxLength) {
|
|
||||||
this.popBack()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
popFront(): T | undefined {
|
|
||||||
const h = this._head
|
|
||||||
const res = this._elements[h]
|
|
||||||
if (res === undefined) return undefined
|
|
||||||
|
|
||||||
this._elements[h] = undefined
|
|
||||||
this._head = (h + 1) & (this._capacity - 1)
|
|
||||||
|
|
||||||
return res
|
|
||||||
}
|
|
||||||
|
|
||||||
popBack(): T | undefined {
|
|
||||||
const t = (this._tail - 1) & (this._capacity - 1)
|
|
||||||
const res = this._elements[t]
|
|
||||||
if (res === undefined) return undefined
|
|
||||||
|
|
||||||
this._elements[t] = undefined
|
|
||||||
this._tail = t
|
|
||||||
|
|
||||||
return res
|
|
||||||
}
|
|
||||||
|
|
||||||
peekFront(): T | undefined {
|
|
||||||
return this._elements[this._head]
|
|
||||||
}
|
|
||||||
|
|
||||||
peekBack(): T | undefined {
|
|
||||||
return this._elements[(this._tail - 1) & (this._capacity - 1)]
|
|
||||||
}
|
|
||||||
|
|
||||||
get length(): number {
|
|
||||||
return (this._tail - this._head) & (this._capacity - 1)
|
|
||||||
}
|
|
||||||
|
|
||||||
toArray(): T[] {
|
|
||||||
const sz = this.length
|
|
||||||
if (sz === 0) return []
|
|
||||||
|
|
||||||
const arr = new Array(sz)
|
|
||||||
|
|
||||||
if (this._head < this._tail) {
|
|
||||||
// copy as-is, head to tail
|
|
||||||
arraycopy(this._elements, this._head, arr, 0, sz)
|
|
||||||
} else {
|
|
||||||
const headPortion = this._capacity - this._head
|
|
||||||
|
|
||||||
// copy from head to end
|
|
||||||
arraycopy(this._elements, this._head, arr, 0, headPortion)
|
|
||||||
// copy from start to tail
|
|
||||||
arraycopy(this._elements, 0, arr, headPortion, this._tail)
|
|
||||||
}
|
|
||||||
|
|
||||||
return arr as T[]
|
|
||||||
}
|
|
||||||
|
|
||||||
protected _delete(i: number): void {
|
|
||||||
const els = this._elements
|
|
||||||
const mask = this._capacity - 1
|
|
||||||
const h = this._head
|
|
||||||
const t = this._tail
|
|
||||||
const front = (i - h) & mask
|
|
||||||
const back = (t - i) & mask
|
|
||||||
|
|
||||||
if (front < back) {
|
|
||||||
if (h <= i) {
|
|
||||||
arraycopy(els, h, els, h + 1, front)
|
|
||||||
} else {
|
|
||||||
// wrap
|
|
||||||
arraycopy(els, 0, els, 1, i)
|
|
||||||
els[0] = els[mask]
|
|
||||||
arraycopy(els, h, els, h + 1, mask - h)
|
|
||||||
}
|
|
||||||
els[h] = undefined
|
|
||||||
this._head = (h + 1) & mask
|
|
||||||
} else if (i < t) {
|
|
||||||
// copy null tail as well
|
|
||||||
arraycopy(els, i + 1, els, i, back)
|
|
||||||
this._tail = t - 1
|
|
||||||
} else {
|
|
||||||
// wrap
|
|
||||||
arraycopy(els, i + 1, els, i, mask - i)
|
|
||||||
els[mask] = els[0]
|
|
||||||
arraycopy(els, 1, els, 0, t)
|
|
||||||
this._tail = (t - 1) & mask
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
remove(item: T): void {
|
|
||||||
const mask = this._capacity - 1
|
|
||||||
let i = this._head
|
|
||||||
let val: T | undefined
|
|
||||||
|
|
||||||
while ((val = this._elements[i]) !== undefined) {
|
|
||||||
if (item === val) {
|
|
||||||
this._delete(i)
|
|
||||||
|
|
||||||
return
|
|
||||||
}
|
|
||||||
i = (i + 1) & mask
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
removeBy(pred: (it: T) => boolean): void {
|
|
||||||
const mask = this._capacity - 1
|
|
||||||
let i = this._head
|
|
||||||
let val: T | undefined
|
|
||||||
|
|
||||||
while ((val = this._elements[i]) !== undefined) {
|
|
||||||
if (pred(val)) {
|
|
||||||
this._delete(i)
|
|
||||||
|
|
||||||
return
|
|
||||||
}
|
|
||||||
i = (i + 1) & mask
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
at(idx: number): T | undefined {
|
|
||||||
return this._elements[(this._head + idx) & (this._capacity - 1)]
|
|
||||||
}
|
|
||||||
|
|
||||||
*iter(): IterableIterator<T> {
|
|
||||||
const sz = this.length
|
|
||||||
if (sz === 0) return
|
|
||||||
|
|
||||||
if (this._head < this._tail) {
|
|
||||||
// head to tail
|
|
||||||
for (let i = 0; i < sz; i++) {
|
|
||||||
yield this._elements[this._head + i]!
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
const headPortion = this._capacity - this._head
|
|
||||||
|
|
||||||
// head to end
|
|
||||||
for (let i = 0; i < headPortion; i++) {
|
|
||||||
yield this._elements[this._head + i]!
|
|
||||||
}
|
|
||||||
// start to tail
|
|
||||||
for (let i = 0; i < this._tail; i++) {
|
|
||||||
yield this._elements[i]!
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
clear(): void {
|
|
||||||
this._elements = new Array<T | undefined>(this._capacity)
|
|
||||||
this._head = this._tail = 0
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -11,7 +11,6 @@ export * from './bigint-utils.js'
|
||||||
export * from './composer.js'
|
export * from './composer.js'
|
||||||
export * from './crypto/index.js'
|
export * from './crypto/index.js'
|
||||||
export * from './dcs.js'
|
export * from './dcs.js'
|
||||||
export * from './deque.js'
|
|
||||||
export * from './early-timer.js'
|
export * from './early-timer.js'
|
||||||
export * from './function-utils.js'
|
export * from './function-utils.js'
|
||||||
export * from './linked-list.js'
|
export * from './linked-list.js'
|
||||||
|
|
Loading…
Reference in a new issue