Compare commits

...

7 Commits

Author SHA1 Message Date
fiatjaf
a55fb8465f mergeFilters() 2023-06-09 23:00:57 -03:00
fiatjaf
472a01af6a fix infinite loop bug caused by malformed TLVs on nip19. 2023-06-08 10:33:28 -03:00
fiatjaf_
bb5acfc197 Merge pull request #214 from Egge7/messagequeue
Replace array list queue with a linked list one
2023-05-20 08:20:56 -03:00
Egge
5b15237b95 replace ArrayList with Queue 2023-05-16 17:16:38 +02:00
Egge
4184609a00 added test cases for MessageQueue 2023-05-13 09:44:52 +02:00
Egge
97287cad74 comply with eslint config 2023-05-13 09:44:41 +02:00
Egge
fa21f71ab5 added queue classes 2023-05-12 23:20:03 +02:00
7 changed files with 198 additions and 13 deletions

View File

@@ -1,4 +1,4 @@
import {matchFilter, matchFilters} from './filter.ts'
import {matchFilter, matchFilters, mergeFilters} from './filter.ts'
import {buildEvent} from './test-helpers.ts'
describe('Filter', () => {
@@ -18,7 +18,7 @@ describe('Filter', () => {
kind: 1,
pubkey: 'abc',
created_at: 150,
tags: [['tag', 'value']],
tags: [['tag', 'value']]
})
const result = matchFilter(filter, event)
@@ -162,7 +162,12 @@ describe('Filter', () => {
{authors: ['abc'], limit: 3}
]
const event = buildEvent({id: '123', kind: 1, pubkey: 'abc', created_at: 150})
const event = buildEvent({
id: '123',
kind: 1,
pubkey: 'abc',
created_at: 150
})
const result = matchFilters(filters, event)
@@ -189,11 +194,35 @@ describe('Filter', () => {
{kinds: [1], limit: 2},
{authors: ['abc'], limit: 3}
]
const event = buildEvent({id: '456', kind: 2, pubkey: 'def', created_at: 200})
const event = buildEvent({
id: '456',
kind: 2,
pubkey: 'def',
created_at: 200
})
const result = matchFilters(filters, event)
expect(result).toEqual(false)
})
})
describe('mergeFilters', () => {
it('should merge filters', () => {
expect(
mergeFilters(
{ids: ['a', 'b'], limit: 3},
{authors: ['x'], ids: ['b', 'c']}
)
).toEqual({ids: ['a', 'b', 'c'], limit: 3, authors: ['x']})
expect(
mergeFilters(
{kinds: [1], since: 15, until: 30},
{since: 10, kinds: [7], until: 15},
{kinds: [9, 10]}
)
).toEqual({kinds: [1, 7, 9, 10], since: 10, until: 30})
})
})
})

View File

@@ -56,3 +56,37 @@ export function matchFilters(
}
return false
}
export function mergeFilters(...filters: Filter<number>[]): Filter<number> {
let result: Filter<number> = {}
for (let i = 0; i < filters.length; i++) {
let filter = filters[i]
Object.entries(filter).forEach(([property, values]) => {
if (
property === 'kinds' ||
property === 'ids' ||
property === 'authors' ||
property[0] === '#'
) {
// @ts-ignore
result[property] = result[property] || []
// @ts-ignore
for (let v = 0; v < values.length; v++) {
// @ts-ignore
let value = values[v]
// @ts-ignore
if (!result[property].includes(value)) result[property].push(value)
}
}
})
if (filter.limit && (!result.limit || filter.limit > result.limit))
result.limit = filter.limit
if (filter.until && (!result.until || filter.until > result.until))
result.until = filter.until
if (filter.since && (!result.since || filter.since < result.since))
result.since = filter.since
}
return result
}

View File

@@ -69,9 +69,7 @@ export function decode(nip19: string): DecodeResult {
data: {
id: bytesToHex(tlv[0][0]),
relays: tlv[1] ? tlv[1].map(d => utf8Decoder.decode(d)) : [],
author: tlv[2]?.[0]
? bytesToHex(tlv[2][0])
: undefined
author: tlv[2]?.[0] ? bytesToHex(tlv[2][0]) : undefined
}
}
}
@@ -123,9 +121,10 @@ function parseTLV(data: Uint8Array): TLV {
while (rest.length > 0) {
let t = rest[0]
let l = rest[1]
if (!l) throw new Error(`malformed TLV ${t}`)
let v = rest.slice(2, 2 + l)
rest = rest.slice(2 + l)
if (v.length < l) continue
if (v.length < l) throw new Error(`not enough data to read on TLV ${t}`)
result[t] = result[t] || []
result[t].push(v)
}

View File

@@ -1,6 +1,6 @@
{
"name": "nostr-tools",
"version": "1.11.1",
"version": "1.12.0",
"description": "Tools for making a Nostr client.",
"repository": {
"type": "git",

View File

@@ -3,6 +3,7 @@
import {verifySignature, validateEvent, type Event} from './event.ts'
import {matchFilters, type Filter} from './filter.ts'
import {getHex64, getSubscriptionId} from './fakejson.ts'
import { MessageQueue } from './utils.ts'
type RelayEvent = {
connect: () => void | Promise<void>
@@ -122,24 +123,24 @@ export function relayInit(
listeners.disconnect.forEach(cb => cb())
}
let incomingMessageQueue: string[] = []
let incomingMessageQueue: MessageQueue = new MessageQueue()
let handleNextInterval: any
ws.onmessage = e => {
incomingMessageQueue.push(e.data)
incomingMessageQueue.enqueue(e.data)
if (!handleNextInterval) {
handleNextInterval = setInterval(handleNext, 0)
}
}
function handleNext() {
if (incomingMessageQueue.length === 0) {
if (incomingMessageQueue.size === 0) {
clearInterval(handleNextInterval)
handleNextInterval = null
return
}
var json = incomingMessageQueue.shift()
var json = incomingMessageQueue.dequeue()
if (!json) return
let subid = getSubscriptionId(json)

View File

@@ -1,5 +1,6 @@
import {buildEvent} from './test-helpers.ts'
import {
MessageQueue,
insertEventIntoAscendingList,
insertEventIntoDescendingList,
} from './utils.ts'
@@ -191,3 +192,48 @@ describe('inserting into a asc sorted list of events', () => {
expect(list1).toHaveLength(3)
})
})
describe('enque a message into MessageQueue', () => {
test('enque into an empty queue', () => {
const queue = new MessageQueue()
queue.enqueue('node1')
expect(queue.first!.value).toBe('node1')
})
test('enque into a non-empty queue', () => {
const queue = new MessageQueue()
queue.enqueue('node1')
queue.enqueue('node3')
queue.enqueue('node2')
expect(queue.first!.value).toBe('node1')
expect(queue.last!.value).toBe('node2')
expect(queue.size).toBe(3)
})
test('dequeue from an empty queue', () => {
const queue = new MessageQueue()
const item1 = queue.dequeue()
expect(item1).toBe(null)
expect(queue.size).toBe(0)
})
test('dequeue from a non-empty queue', () => {
const queue = new MessageQueue()
queue.enqueue('node1')
queue.enqueue('node3')
queue.enqueue('node2')
const item1 = queue.dequeue()
expect(item1).toBe('node1')
const item2 = queue.dequeue()
expect(item2).toBe('node3')
})
test('dequeue more than in queue', () => {
const queue = new MessageQueue()
queue.enqueue('node1')
queue.enqueue('node3')
const item1 = queue.dequeue()
expect(item1).toBe('node1')
const item2 = queue.dequeue()
expect(item2).toBe('node3')
expect(queue.size).toBe(0)
const item3 = queue.dequeue()
expect(item3).toBe(null)
})
})

View File

@@ -109,3 +109,79 @@ export function insertEventIntoAscendingList(
return sortedArray
}
export class MessageNode {
private _value: string
private _next: MessageNode | null
public get value(): string {
return this._value
}
public set value(message: string) {
this._value = message
}
public get next(): MessageNode | null {
return this._next
}
public set next(node: MessageNode | null) {
this._next = node
}
constructor(message: string) {
this._value = message
this._next = null
}
}
export class MessageQueue {
private _first: MessageNode | null
private _last: MessageNode | null
public get first(): MessageNode | null {
return this._first
}
public set first(messageNode: MessageNode | null) {
this._first = messageNode
}
public get last(): MessageNode | null {
return this._last
}
public set last(messageNode: MessageNode | null) {
this._last = messageNode
}
private _size: number
public get size(): number {
return this._size
}
public set size(v: number) {
this._size = v
}
constructor() {
this._first = null
this._last = null
this._size = 0
}
enqueue(message: string): boolean {
const newNode = new MessageNode(message)
if (this._size === 0 || !this._last) {
this._first = newNode
this._last = newNode
} else {
this._last.next = newNode
this._last = newNode
}
this._size++
return true
}
dequeue(): string | null {
if (this._size === 0 || !this._first) return null
let prev = this._first
this._first = prev.next
prev.next = null
this._size--
return prev.value
}
}