Skip to content
This repository was archived by the owner on Feb 12, 2024. It is now read-only.

Commit b09a18c

Browse files
Gozalaicidassetachingbrain
authored
fix: transfer set (#3573)
Co-authored-by: Steven Vandevelde <[email protected]> Co-authored-by: achingbrain <[email protected]>
1 parent 3ce2f76 commit b09a18c

File tree

23 files changed

+121
-99
lines changed

23 files changed

+121
-99
lines changed

packages/ipfs-message-port-client/src/client/query.js

+2-2
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
* @typedef {Object} QueryOptions
33
* @property {AbortSignal} [signal]
44
* @property {number} [timeout]
5-
* @property {Transferable[]} [transfer]
5+
* @property {Set<Transferable>} [transfer]
66
*/
77

88
/**
@@ -49,7 +49,7 @@ export class Query {
4949
/**
5050
* Data that will be transferred over message channel.
5151
*
52-
* @returns {Transferable[]|void}
52+
* @returns {Set<Transferable>|void}
5353
*/
5454
transfer () {
5555
return this.input.transfer

packages/ipfs-message-port-client/src/client/transport.js

+2-2
Original file line numberDiff line numberDiff line change
@@ -180,8 +180,8 @@ export class MessageTransport {
180180
id,
181181
input: query.toJSON()
182182
},
183-
// @ts-ignore - TS seems to want second arg to postMessage to not be undefined
184-
[...new Set(query.transfer() || [])]
183+
// @ts-expect-error - Type signature does not expect 2nd undefined arg
184+
query.transfer()
185185
)
186186
}
187187

packages/ipfs-message-port-client/src/core.js

+8-8
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,7 @@ export class CoreClient extends Client {
7171
*/
7272
CoreClient.prototype.addAll = async function * addAll (input, options = {}) {
7373
const { timeout, signal } = options
74-
const transfer = [...(options.transfer || [])]
74+
const transfer = options.transfer || new Set()
7575
const progressCallback = options.progress
7676
? encodeCallback(options.progress, transfer)
7777
: undefined
@@ -99,7 +99,7 @@ CoreClient.prototype.addAll = async function * addAll (input, options = {}) {
9999
*/
100100
CoreClient.prototype.add = async function add (input, options = {}) {
101101
const { timeout, signal } = options
102-
const transfer = [...(options.transfer || [])]
102+
const transfer = options.transfer || new Set()
103103
const progressCallback = options.progress
104104
? encodeCallback(options.progress, transfer)
105105
: undefined
@@ -184,7 +184,7 @@ const identity = (v) => v
184184
* given input.
185185
*
186186
* @param {ImportCandidate} input
187-
* @param {Transferable[]} transfer
187+
* @param {Set<Transferable>} transfer
188188
* @returns {Promise<EncodedAddInput>}
189189
*/
190190
const encodeAddInput = async (input, transfer) => {
@@ -242,7 +242,7 @@ const encodeAddInput = async (input, transfer) => {
242242
* given input.
243243
*
244244
* @param {ImportCandidateStream} input
245-
* @param {Transferable[]} transfer
245+
* @param {Set<Transferable>} transfer
246246
* @returns {EncodedAddAllInput}
247247
*/
248248
const encodeAddAllInput = (input, transfer) => {
@@ -279,7 +279,7 @@ const encodeAddAllInput = (input, transfer) => {
279279
* effective strategy.
280280
*
281281
* @param {ImportCandidate} content
282-
* @param {Transferable[]} transfer
282+
* @param {Set<Transferable>} transfer
283283
* @returns {EncodedAddInput}
284284
*/
285285
const encodeAsyncIterableContent = (content, transfer) => {
@@ -303,7 +303,7 @@ const encodeAsyncIterableContent = (content, transfer) => {
303303

304304
/**
305305
* @param {ImportCandidate} content
306-
* @param {Transferable[]} transfer
306+
* @param {Set<Transferable>} transfer
307307
* @returns {EncodedAddInput}
308308
*/
309309
const encodeIterableContent = (content, transfer) => {
@@ -329,7 +329,7 @@ const encodeIterableContent = (content, transfer) => {
329329

330330
/**
331331
* @param {ToFile | ToDirectory} file
332-
* @param {Transferable[]} transfer
332+
* @param {Set<Transferable>} transfer
333333
* @returns {EncodedFileInput | EncodedDirectoryInput}
334334
*/
335335
const encodeFileObject = ({ path, mode, mtime, content }, transfer) => {
@@ -349,7 +349,7 @@ const encodeFileObject = ({ path, mode, mtime, content }, transfer) => {
349349

350350
/**
351351
* @param {ToContent|undefined} content
352-
* @param {Transferable[]} transfer
352+
* @param {Set<Transferable>} transfer
353353
* @returns {EncodedFileContent}
354354
*/
355355
const encodeFileContent = (content, transfer) => {

packages/ipfs-message-port-client/src/dag.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,7 @@ DAGClient.prototype.resolve = async function resolve (cid, options = {}) {
6262

6363
/**
6464
* @param {string|CID} input
65-
* @param {Transferable[]} [transfer]
65+
* @param {Set<Transferable>} [transfer]
6666
* @returns {string|EncodedCID}
6767
*/
6868
const encodeCIDOrPath = (input, transfer) => {

packages/ipfs-message-port-client/src/interface.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -2,5 +2,5 @@
22
// JSDoc syntax or that result in a different behaviour when typed in JSDoc.
33

44
export interface MessagePortClientOptions {
5-
transfer?: Transferable[]
5+
transfer?: Set<Transferable>
66
}

packages/ipfs-message-port-protocol/README.md

+1-2
Original file line numberDiff line numberDiff line change
@@ -146,7 +146,7 @@ port2.onmessage = async ({data}) => {
146146

147147
### Callback
148148

149-
Primitive callbacks that take single parameter supported by [structured cloning algorithm][] like progress callback used across IPFS APIs can be encoded / decoded. Unilke most encoders `transfer` argument is required (because value is encoded to a [MessagePort][] that can only be transferred)
149+
Primitive callbacks that take single parameter supported by [structured cloning algorithm][] like progress callback used across IPFS APIs can be encoded / decoded. Unlike most encoders `transfer` argument is required (because value is encoded to a [MessagePort][] that can only be transferred)
150150

151151
```js
152152
import { encodeCallback, decodeCallback } from 'ipfs-message-port-protocol/core'
@@ -186,4 +186,3 @@ Check out our [contributing document](https://github.com/ipfs/community/blob/mas
186186
## License
187187

188188
[![FOSSA Status](https://app.fossa.io/api/projects/git%2Bgithub.jpy.wang%2Fipfs%2Fjs-ipfs.svg?type=large)](https://app.fossa.io/projects/git%2Bgithub.jpy.wang%2Fipfs%2Fjs-ipfs?ref=badge_large)
189-

packages/ipfs-message-port-protocol/src/block.js

+2-2
Original file line numberDiff line numberDiff line change
@@ -15,11 +15,11 @@
1515
* copy.
1616
*
1717
* @param {Uint8Array} data
18-
* @param {Transferable[]} [transfer]
18+
* @param {Set<Transferable>} [transfer]
1919
*/
2020
export const encodeBlock = (data, transfer) => {
2121
if (transfer) {
22-
transfer.push(data.buffer)
22+
transfer.add(data.buffer)
2323
}
2424
return data
2525
}

packages/ipfs-message-port-protocol/src/cid.js

+2-2
Original file line numberDiff line numberDiff line change
@@ -14,12 +14,12 @@ import { CID } from 'multiformats/cid'
1414
* will be added for the transfer list.
1515
*
1616
* @param {CID} cid
17-
* @param {Transferable[]} [transfer]
17+
* @param {Set<Transferable>} [transfer]
1818
* @returns {EncodedCID}
1919
*/
2020
export const encodeCID = (cid, transfer) => {
2121
if (transfer) {
22-
transfer.push(cid.multihash.bytes.buffer)
22+
transfer.add(cid.multihash.bytes.buffer)
2323
}
2424
return cid
2525
}

packages/ipfs-message-port-protocol/src/core.js

+30-14
Original file line numberDiff line numberDiff line change
@@ -92,16 +92,19 @@ export const decodeIterable = async function * ({ port }, decode) {
9292
/**
9393
* @template I,O
9494
* @param {AsyncIterable<I>|Iterable<I>} iterable
95-
* @param {function(I, Transferable[]):O} encode
96-
* @param {Transferable[]} transfer
95+
* @param {function(I, Set<Transferable>):O} encode
96+
* @param {Set<Transferable>} transfer
9797
* @returns {RemoteIterable<O>}
9898
*/
9999
export const encodeIterable = (iterable, encode, transfer) => {
100100
const { port1: port, port2: remote } = new MessageChannel()
101-
/** @type {Transferable[]} */
102-
const itemTransfer = []
103101
/** @type {Iterator<I>|AsyncIterator<I>} */
104102
const iterator = toIterator(iterable)
103+
// Note that port.onmessage will receive multiple 'next' method messages.
104+
// Instead of allocating set every time we allocate one here and recycle
105+
// it on each 'next' message.
106+
/** @type {Set<Transferable>} */
107+
const itemTransfer = new Set()
105108

106109
port.onmessage = async ({ data: { method } }) => {
107110
switch (method) {
@@ -112,12 +115,15 @@ export const encodeIterable = (iterable, encode, transfer) => {
112115
port.postMessage({ type: 'next', done: true })
113116
port.close()
114117
} else {
115-
itemTransfer.length = 0
116-
port.postMessage(
118+
itemTransfer.clear()
119+
const encodedValue = encode(value, itemTransfer)
120+
121+
postMessage(
122+
port,
117123
{
118124
type: 'next',
119125
done: false,
120-
value: encode(value, itemTransfer)
126+
value: encodedValue
121127
},
122128
itemTransfer
123129
)
@@ -144,7 +150,7 @@ export const encodeIterable = (iterable, encode, transfer) => {
144150
}
145151
}
146152
port.start()
147-
transfer.push(remote)
153+
transfer.add(remote)
148154

149155
return { type: 'RemoteIterable', port: remote }
150156
}
@@ -170,31 +176,41 @@ const toIterator = iterable => {
170176

171177
/**
172178
* @param {Function} callback
173-
* @param {Transferable[]} transfer
179+
* @param {Set<Transferable>} transfer
174180
* @returns {RemoteCallback}
175181
*/
176182
export const encodeCallback = (callback, transfer) => {
177183
// eslint-disable-next-line no-undef
178184
const { port1: port, port2: remote } = new MessageChannel()
179185
port.onmessage = ({ data }) => callback.apply(null, data)
180-
transfer.push(remote)
186+
transfer.add(remote)
181187
return { type: 'RemoteCallback', port: remote }
182188
}
183189

184190
/**
185191
* @template T
186192
* @param {RemoteCallback} remote
187-
* @returns {function(T[]):void | function(T[], Transferable[]):void}
193+
* @returns {function(T[]):void | function(T[], Set<Transferable>):void}
188194
*/
189195
export const decodeCallback = ({ port }) => {
190196
/**
191197
* @param {T[]} args
192-
* @param {Transferable[]} [transfer]
198+
* @param {Set<Transferable>} [transfer]
193199
* @returns {void}
194200
*/
195-
const callback = (args, transfer = []) => {
196-
port.postMessage(args, [...new Set(transfer)])
201+
const callback = (args, transfer) => {
202+
postMessage(port, args, transfer)
197203
}
198204

199205
return callback
200206
}
207+
208+
/**
209+
* @param {MessagePort} port
210+
* @param {any} message
211+
* @param {Iterable<Transferable>} [transfer]
212+
*/
213+
const postMessage = (port, message, transfer) =>
214+
// @ts-expect-error - Built in types expect Transferable[] but it really
215+
// should be Iterable<Transferable>
216+
port.postMessage(message, transfer)

packages/ipfs-message-port-protocol/src/dag.js

+4-4
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ export const decodeNode = ({ dagNode, cids }) => {
4141
* this node will be added to transfer so they are moved across without copy.
4242
*
4343
* @param {DAGNode} dagNode
44-
* @param {Transferable[]} [transfer]
44+
* @param {Set<Transferable>} [transfer]
4545
* @returns {EncodedDAGNode}
4646
*/
4747
export const encodeNode = (dagNode, transfer) => {
@@ -58,7 +58,7 @@ export const encodeNode = (dagNode, transfer) => {
5858
*
5959
* @param {DAGNode} value
6060
* @param {CID[]} cids
61-
* @param {Transferable[]} [transfer]
61+
* @param {Set<Transferable>} [transfer]
6262
* @returns {void}
6363
*/
6464
const collectNode = (value, cids, transfer) => {
@@ -70,11 +70,11 @@ const collectNode = (value, cids, transfer) => {
7070
encodeCID(cid, transfer)
7171
} else if (value instanceof ArrayBuffer) {
7272
if (transfer) {
73-
transfer.push(value)
73+
transfer.add(value)
7474
}
7575
} else if (ArrayBuffer.isView(value)) {
7676
if (transfer) {
77-
transfer.push(value.buffer)
77+
transfer.add(value.buffer)
7878
}
7979
} else if (Array.isArray(value)) {
8080
for (const member of value) {

packages/ipfs-message-port-protocol/src/rpc.ts

+3-3
Original file line numberDiff line numberDiff line change
@@ -6,18 +6,18 @@ export type Remote<T extends Record<string, unknown>> = {
66
[K in keyof T]: Procedure<T[K]>
77
}
88

9-
type Return<T> = T extends Promise<infer U>
9+
export type Return<T> = T extends Promise<infer U>
1010
? Promise<U & TransferOptions>
1111
: Promise<T & TransferOptions>
1212

1313
export interface QueryOptions {
1414
signal?: AbortSignal
1515
timeout?: number
16-
transfer?: Transferable[]
16+
transfer?: Set<Transferable>
1717
}
1818

1919
export interface TransferOptions {
20-
transfer?: Transferable[]
20+
transfer?: Set<Transferable>
2121
}
2222

2323
export type NonUndefined<A> = A extends undefined ? never : A

packages/ipfs-message-port-protocol/test/block.browser.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ describe('block (browser)', function () {
2222
it('should decode Block over message channel & transfer bytes', async () => {
2323
const blockIn = uint8ArrayFromString('hello')
2424

25-
const transfer = []
25+
const transfer = new Set()
2626

2727
const blockOut = await move(encodeBlock(blockIn, transfer), transfer)
2828

packages/ipfs-message-port-protocol/test/cid.browser.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ describe('cid (browser)', function () {
2626

2727
it('should decode CID and transfer bytes', async () => {
2828
const cidIn = CID.parse('Qmd7xRhW5f29QuBFtqu3oSD27iVy35NRB91XFjmKFhtgMr')
29-
const transfer = []
29+
const transfer = new Set()
3030
const cidDataIn = encodeCID(cidIn, transfer)
3131
const cidDataOut = await move(cidDataIn, transfer)
3232
const cidOut = decodeCID(cidDataOut)

0 commit comments

Comments
 (0)