Skip to content

Instantly share code, notes, and snippets.

@syg
Last active June 18, 2020 00:25
Show Gist options
  • Save syg/b534ba9f26e03f562aaf4420d5ad3cf9 to your computer and use it in GitHub Desktop.
Save syg/b534ba9f26e03f562aaf4420d5ad3cf9 to your computer and use it in GitHub Desktop.
ResizableArrayBuffer and GrowableSharedArrayBuffer

ResizableArrayBuffer and GrowableSharedArrayBuffer

WebAssembly and Web APIs that heavily use buffers (e.g., WebGPU, WebAudio) benefit from having more expressive buffers in addition to the current fixed-size ArrayBuffer and SharedArrayBuffer.

This proposal introduces two new buffer objects, as well as an extension to TypedArrays that lets the programmer create views that track the length of their underlying buffers.

WebAssembly's JS integration would then expose a ResizableArrayBuffer or a GrowableSharedArrayBuffer to avoid having to update all JS views when wasm memory grows.

APIs like WebGPU that wish to re-point ArrayBuffers can vend ResizableArrayBuffers instead, and can explain re-pointing under the hood as an atomic resizing + overwriting of contents.

The rough idea is to let users who need this fundamentally new expressivity opt-in. Making existing ArrayBuffers resizable has consequences on API, security, and performance expectations. By offering new buffer types, we can make the different tradeoffs clearer: views on resizable buffers are likely to be slightly slower due to the double-dereference of the length, but lets the user reuse buffers without copying or creating new views.

ResizableArrayBuffer

ResizableArrayBuffer(length, [, maximumLength ]) creates a resizable buffer of length. An optional maximumLength may be given, which defaults to Infinity.

ResizableArrayBuffer.prototype.resize(newLength) resizes the resizable buffer to newLength.

  1. If a maximumLength was provided during construction and newLength is greater than maximumLength, throw a RangeError.
  2. The buffer is resized to newLength. The contents up to min(newLength, current length) are unchanged. Any newly allocated memory is zeroed.

GrowableSharedArrayBuffer

GrowableSharedArrayBuffer(length, maximumLength) creates a growable shared buffer of length. maximumLength is not optional, and must be greater than or equal to length.

GrowableSharedArrayBuffer.prototype.grow(newLength) grows to newLength.

  1. If newLength is greater than the maximumLength provided at construction time, throw a RangeError.
  2. The buffer is grown to newLength. The contents up to the current length are unchanged.

TODO: Memory model details.

TypedArray

TypedArray(buffer, [, byteOffset [, length ] ]) is extended to handle ResizableArrayBuffer and GrowableSharedArrayBuffer to automatically track the length.

Roughly,

  1. If buffer is a resizable or growable buffer, then
    1. If byteOffset is not undefined or 0, throw a TypeError.
    2. If length is not undefined, throw a TypeError.
    3. Set byteOffset to 0.
    4. Let O be a new typed array.
    5. Set O.[[ViewedArrayBuffer]] to buffer.
    6. Set O.[[ByteLength]] to auto.
    7. Set O.[[ByteOffset]] to 0.
    8. Set O.[[ArrayLength]] to auto.
    9. Return O.

TypedArray.prototype.length getter is extended to handle auto-length computation.

  1. Let O be the this value.
  2. If O.[[ByteLength]] is auto, then
    1. Let byteLength be O.[[ViewedArrayBuffer]].[[ByteLength]]
    2. Let arrayLength be floor(byteLength / element size).
    3. Return arrayLength
  3. ...

Indexed access would perform bounds checks on the auto-computed length.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment