transfer() semantics around preserving resizability · Issue #4 · tc39/proposal-arraybuffer-transfer (original) (raw)

    > * https://streams.spec.whatwg.org/#transfer-array-buffer

Also add https://html.spec.whatwg.org/multipage/structured-data.html to that list. We'll want this to preserve resizability:

const arrayBuffer = new ArrayBuffer(1024, { maxByteLength: 2048 }); const transferredArrayBuffer = structuredClone(arrayBuffer, { transfer: [arrayBuffer] }); console.log(transferredArrayBuffer.resizable === true);

when we transfer ArrayBuffers on the web platform, we should preserve their resizability. And apparently this spec has an algorithm which does that.

Looks like I misread the explainer. 😅 As currently written, transfer() always returns a non-resizable ArrayBuffer.

Should we change transfer() to preserve resizability? Or should there be a different method (or option for transfer()) which does that? I suppose this answers one of the open questions on this proposal:

Should there be a transferResizable() that reallocs into another resizable ArrayBuffer?

Are there compelling use cases for this?

So yes, there are compelling use cases! 😁

_Originally posted by @MattiasBuelens in #3


Splitting off this question into its own issue. Currently transfer() always produces a fixed-length ArrayBuffer.

It seems good to give it the additional capability to produce resizable buffers. I'd like to defer this to a follow-on proposal, but there is some future proofing to do here.

The two interlinked API design questions I think are:

  1. How to tell transfer() what kind of ArrayBuffer to return?
  2. Should transfer() preserve the resizability of its receiver by default? If so, how do you override that default behavior?

One natural way to address (1) is to add an options bag to transfer(), mirroring what the constructor accepts: transfer(newLen, { maxByteLength }). If a { maxByteLength } options bag is passed, then transfer returns a resizable buffer, just like the constructor.

However, this design cannot also satisfy (2) if it is decided (2) should preserve resizability by default. Preserving by default means that if the { maxByteLength } options bag is not passed, that it gets this value from the receiver. But if that's the case, there's no way to tell transfer to make a fixed-length ArrayBuffer from a resizable one. And moving a resizable buffer to a fixed one is one of the original use cases: to do a zero-copy move and also free up the reserved virtual memory after resizing is no longer needed.

There are a few choices moving forward:

A. Stick with the current design of transfer(), i.e. always return fixed buffers. When we extend transfer() to produce resizable buffers, an explicit options bag must always be passed. It does not preserve resizability by default.

B. Make transfer() preserve resizability if the options bag is undefined. Since this means you cannot transfer from resizable into a fixed length buffer, add a new method fix() (name up for bikeshed).

C. Have a transfer() preserve resizability if both parameters are undefined. That is, make the nullary transfer() a special overload that perform a "move as is". transfer(newLen) always produces a fixed-length buffer of newLen, and transfer(newLen, { maxByteLength }) always produces a resizable buffer.

I think I prefer A > C >>> B. Thoughts?