Skip to content

Commit cc4f3de

Browse files
committed
websocket: use linkedlist instead of Set
1 parent af3379f commit cc4f3de

File tree

1 file changed

+82
-44
lines changed

1 file changed

+82
-44
lines changed

lib/web/websocket/sender.js

Lines changed: 82 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -3,14 +3,33 @@
33
const { WebsocketFrameSend } = require('./frame')
44
const { opcodes, sendHints } = require('./constants')
55

6-
/** @type {Uint8Array} */
6+
/** @type {typeof Uint8Array} */
77
const FastBuffer = Buffer[Symbol.species]
88

9-
class SendQueue {
10-
#queued = new Set()
11-
#size = 0
9+
/**
10+
* @typedef {object} SendQueueNode
11+
* @property {SendQueueNode | null} next
12+
* @property {Promise<void> | null} promise
13+
* @property {((...args: any[]) => any)} callback
14+
* @property {Buffer | null} frame
15+
*/
1216

13-
/** @type {import('net').Socket} */
17+
class SendQueue {
18+
/**
19+
* @type {SendQueueNode | null}
20+
*/
21+
#head = null
22+
/**
23+
* @type {SendQueueNode | null}
24+
*/
25+
#tail = null
26+
27+
/**
28+
* @type {boolean}
29+
*/
30+
#running = false
31+
32+
/** @type {import('node:net').Socket} */
1433
#socket
1534

1635
constructor (socket) {
@@ -19,66 +38,85 @@ class SendQueue {
1938

2039
add (item, cb, hint) {
2140
if (hint !== sendHints.blob) {
22-
const data = clone(item, hint)
23-
24-
if (this.#size === 0) {
25-
this.#dispatch(data, cb, hint)
41+
const frame = createFrame(item, hint)
42+
if (!this.#running) {
43+
// fast-path
44+
this.#socket.write(frame, cb)
2645
} else {
27-
this.#queued.add([data, cb, true, hint])
28-
this.#size++
29-
30-
this.#run()
46+
/** @type {SendQueueNode} */
47+
const node = {
48+
next: null,
49+
promise: null,
50+
callback: cb,
51+
frame
52+
}
53+
if (this.#tail !== null) {
54+
this.#tail.next = node
55+
}
56+
this.#tail = node
3157
}
32-
3358
return
3459
}
3560

36-
const promise = item.arrayBuffer()
37-
const queue = [null, cb, false, hint]
38-
promise.then((ab) => {
39-
queue[0] = clone(ab, hint)
40-
queue[2] = true
41-
42-
this.#run()
43-
})
44-
45-
this.#queued.add(queue)
46-
this.#size++
47-
}
48-
49-
#run () {
50-
for (const queued of this.#queued) {
51-
const [data, cb, done, hint] = queued
61+
/** @type {SendQueueNode} */
62+
const node = {
63+
next: null,
64+
promise: item.arrayBuffer().then((ab) => {
65+
node.promise = null
66+
node.frame = createFrame(ab, hint)
67+
}),
68+
callback: cb,
69+
frame: null
70+
}
5271

53-
if (!done) return
72+
if (this.#tail === null) {
73+
this.#tail = node
74+
}
5475

55-
this.#queued.delete(queued)
56-
this.#size--
76+
if (this.#head === null) {
77+
this.#head = node
78+
}
5779

58-
this.#dispatch(data, cb, hint)
80+
if (!this.#running) {
81+
this.#run()
5982
}
6083
}
6184

62-
#dispatch (data, cb, hint) {
63-
const frame = new WebsocketFrameSend()
64-
const opcode = hint === sendHints.string ? opcodes.TEXT : opcodes.BINARY
65-
66-
frame.frameData = data
67-
const buffer = frame.createFrame(opcode)
68-
69-
this.#socket.write(buffer, cb)
85+
async #run () {
86+
this.#running = true
87+
/** @type {SendQueueNode | null} */
88+
let node = this.#head
89+
while (node !== null) {
90+
// wait pending promise
91+
if (node.promise !== null) {
92+
await node.promise
93+
}
94+
// write
95+
this.#socket.write(node.frame, node.callback)
96+
// cleanup
97+
node.callback = node.frame = null
98+
// set next
99+
node = node.next
100+
}
101+
this.#head = null
102+
this.#tail = null
103+
this.#running = false
70104
}
71105
}
72106

73-
function clone (data, hint) {
107+
function createFrame (data, hint) {
108+
return new WebsocketFrameSend(toBuffer(data, hint)).createFrame(hint === sendHints.string ? opcodes.TEXT : opcodes.BINARY)
109+
}
110+
111+
function toBuffer (data, hint) {
74112
switch (hint) {
75113
case sendHints.string:
76114
return Buffer.from(data)
77115
case sendHints.arrayBuffer:
78116
case sendHints.blob:
79117
return new FastBuffer(data)
80118
case sendHints.typedArray:
81-
return Buffer.copyBytesFrom(data)
119+
return new FastBuffer(data.buffer, data.byteOffset, data.byteLength)
82120
}
83121
}
84122

0 commit comments

Comments
 (0)