Skip to content

Instantly share code, notes, and snippets.

@WebReflection
Last active March 3, 2025 21:05

Revisions

  1. WebReflection revised this gist Mar 3, 2025. 1 changed file with 1 addition and 1 deletion.
    2 changes: 1 addition & 1 deletion ten-tdec.md
    Original file line number Diff line number Diff line change
    @@ -31,7 +31,7 @@ rbuff.set(ref, 0); // ✅

    ### TextDecoder

    Only an `ArrayBuffer`, *resiable* or not, can be used. `SharedArray` needs to be sliced as static `ArrayBuffer`.
    Only an `ArrayBuffer`, *resizable* or not, can be used. `SharedArray` needs to be sliced as static `ArrayBuffer`.

    ```js
    const decoder = new TextDecoder;
  2. WebReflection created this gist Mar 3, 2025.
    76 changes: 76 additions & 0 deletions ten-tdec.md
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,76 @@
    # TextEncoder & TextDecoder shenanigans

    This gist is to remind developers that resizable or growable *buffers* don't play nice with `TextEncoder` or `TextDecoder` instances.

    ### Setup

    ```js
    // setup
    const ref = new TextEncoder().encode("hello");

    // SharedArrayBuffer (static) View
    const sab = new Uint8Array(new SharedArrayBuffer(5));
    // optional pre-populate
    sab.set(ref, 0); //

    // SharedArrayBuffer (growable) View
    const gsab = new Uint8Array(new SharedArrayBuffer(5, { maxByteLength: 16 }));
    // optional pre-populate
    gsab.set(ref, 0); //

    // ArrayBuffer (static) View
    const buff = new Uint8Array(new ArrayBuffer(5));
    // optional pre-populate
    buff.set(ref, 0); //

    // ArrayBuffer (resizable) View
    const rbuff = new Uint8Array(new ArrayBuffer(5, { maxByteLength: 16 }));
    // optional pre-populate
    rbuff.set(ref, 0); //
    ```

    ### TextDecoder

    Only an `ArrayBuffer`, *resiable* or not, can be used. `SharedArray` needs to be sliced as static `ArrayBuffer`.

    ```js
    const decoder = new TextDecoder;

    decoder.decode(buff); //
    decoder.decode(rbuff); //
    decoder.decode(sab.slice(0)); // ✅ - not shared
    decoder.decode(gsab.slice(0)); // ✅ - not shared

    decoder.decode(sab); //
    // Failed to execute 'decode' on 'TextDecoder':
    // The provided ArrayBufferView value must not be shared.

    decoder.decode(gsab); //
    decoder.decode(gsab.subarray(0)); //
    // Failed to execute 'decode' on 'TextDecoder':
    // The provided ArrayBufferView value must not be shared.
    ```

    ### TextEncoder encodeInto

    Only **static** `ArrayBuffer` can be used, making it impossible to use for linear, resizable, serializations purposes.

    ```js
    const encoder = new TextEncoder;

    encoder.encodeInto("hello", buff); //

    encoder.encodeInto("hello", gsab); //
    encoder.encodeInto("hello", gsab.subarray(0)); //
    // Failed to execute 'encodeInto' on 'TextEncoder':
    // The provided Uint8Array value must not be shared.

    encoder.encodeInto("hello", rbuff); //
    encoder.encodeInto("hello", rbuff.subarray(0)); //
    // Failed to execute 'encodeInto' on 'TextEncoder':
    // The provided Uint8Array value must not be resizable.
    ```

    - - -

    Related topic in TC39 around the fact we can't even know AOT the size of a string before deciding to use awkward magic behind the scene to make `encodeInto` happen: https://es.discourse.group/t/string-bytelength-count/2315