JavaScript: TextDecoder on SharedArrayBuffer

1.1k Views Asked by At

I am facing a problem with decode() on SharedArrayBuffer.

Code:

  var sharedArrayBuffer = new SharedArrayBuffer(2);
  var uint8Array = new Uint8Array(sharedArrayBuffer);
  uint8Array[0] = 20;
  var decoder = new TextDecoder();
  decoder.decode(uint8Array);

Error:

Failed to execute 'decode' on 'TextDecoder': The provided ArrayBufferView value must not be shared.

There is a specification here that warns developers about race-condition on this type of memory. Can I somehow force decoding? I am sure data will not be changed during decoding. Or is there a workaround for this?

Reasons: I want to create a single copy of Uint8Array and pass it without copying via postmessage (which copies by default if transferrable is not specified) to several(>3) IFrames(with sandbox tag). Maybe there are other possible solutions?

3

There are 3 best solutions below

3
On

Looks like using ArrayBuffer works fine.

  var arrayBuffer = new ArrayBuffer(2);
  var uint8Array = new Uint8Array(arrayBuffer);
  uint8Array[0] = 20;
  var decoder = new TextDecoder();
  decoder.decode(uint8Array);
2
On

Js

var sharedArrayBuffer = new SharedArrayBuffer(32);
var ary = new Int32Array(sharedArrayBuffer);
ary[0] = 20;
var encoder = new TextEncoder();
var EncodedArray = encoder.encode(ary);
console.log("Encoded Array : ", EncodedArray)
var decoder = new TextDecoder();
var DecodedArray = decoder.decode(EncodedArray);
console.log("Decoded Array is : ", DecodedArray)
0
On

Sadly, today you have to incur the extra buffer + copy in order to consistently do this across browsers. It's plausible that any given browser may allow this, but it isn't portable as of writing this.

For context, the ability for TextDecoder.decode(...) to take a SharedArrayBuffer is desired functionality for WebAssembly and other low-level performance scenarios, but it's stuck in standards and implementation-related discussions.

Here's an example of a buffer-intermediate code path that works to bypass this failing today:

function decodeFromSharedBuffer(sharedBuffer, maxLength) {
  const decoder = new TextDecoder()
  const copyLength = Math.min(sharedBuffer.byteLength, maxLength)

  // Create a temporary ArrayBuffer and copy the contents of the shared buffer
  // into it.
  const tempBuffer = new ArrayBuffer(copyLength)
  const tempView = new Uint8Array(tempBuffer)

  let sharedView = new Uint8Array(sharedBuffer)
  if (sharedBuffer.byteLength != copyLength) {
    sharedView = sharedView.subarray(0, copyLength)
  }
  tempView.set(sharedView)

  return decoder.decode(tempBuffer)
}