TypedArray のサイズが可変になる ES2024 Resizable ArrayBuffers
変更情報
【2024/06/28】
- ES2024 として仕様に入ったためタイトル、本文を更新
ArrayBuffer
の問題
今までの 今まで JavaScript の ArrayBuffer
はサイズが変わることを想定していませんでした。そのためサイズを変える必要がある API の仕様を作るとなると ECMAScript サイドではなく、その API サイドでの対応が必要な状況になっていました。
例えば WebAssembly.Memory
を見てみましょう。
[LegacyNamespace=WebAssembly, Exposed=(Window,Worker,Worklet)]
interface Memory {
constructor(MemoryDescriptor descriptor);
unsigned long grow([EnforceRange] unsigned long delta);
readonly attribute ArrayBuffer buffer;
};
バッファのサイズを大きくしたいとき grow
メソッドを呼び出します。しかし ArrayBuffer
はサイズが変わることを想定していなかったため、buffer
プロパティで取得していた ArrayBuffer
が detached されていました。再度 buffer
プロパティにアクセスして新しい ArrayBuffer
を取得しなければなりませんでした。
いつ detached されるかわからない場合、以下のようにバッファにアクセスするたびに確認する必要があります。これでは扱いにくい上に処理速度がかなり遅くなります。
let uint8 = new Uint8Array(memory.buffer);
function getUint8MemoryValue(index) {
// memory.buffer が detached されていたら、再度 Uint8Array を作り直す
if (uint8.length === 0) {
uint8 = new Uint8Array(memory.buffer);
}
return uint8[index];
}
ES2024 Resizable ArrayBuffers
ArrayBuffer
のサイズを可変にするのが ES2024 Resizable ArrayBuffers です。
扱い方
ArrayBuffer
コンストラクタの第二引数にオプションが追加され、maxByteLength
が指定できるようになります。それ以下の数値なら resize
メソッドでサイズを変更できます。
const buffer = new ArrayBuffer(100, { maxByteLength: 200 });
console.log(buffer.length); // 100
console.log(buffer.maxByteLength); // 200
console.log(buffer.resizable); // true
buffer.resize(150);
console.log(buffer.length); // 150
これが ECMAScript に入ることによって WebAssembly.Memory
で grow
メソッドが呼ばれても、 buffer
プロパティが detached されず常に同じ ArrayBuffer
オブジェクトを返すようになることが期待されます。
注意点
今まで ArrayBuffer
は固定サイズを扱うものだったため、予めそのサイズをキャッシュしておいてそれを使い回すコードを書いている場合注意が必要そうです。
特に注意が必要な点として TypedArray
や DataView
をコンストラクタから作る際に第一引数にサイズが可変な ArrayBuffer
を入力し、かつ第三引数(length
)が undefined
の場合、ArrayBuffer
のサイズ変更に引きずられることが挙げられます。
const buffer = new ArrayBuffer(100, { maxByteLength: 200 });
const uint8 = new Uint8Array(buffer);
console.log(uint8.length); // 100
buffer.resize(150);
console.log(uint8.length); // 150
解決したい問題のことを考えるとこうならざるを得ませんが、今までの前提と異なる挙動となります。今一度既存のコードを確認して、その都度 length
プロパティにアクセスする方針に修正したほうがいいかもしれません。
Growable SharedArrayBuffers
SharedArrayBuffer
はサイズを小さくすることが想定されないため少し異なりますが、ほとんど Resizable ArrayBuffers と同じような機能追加がなされます。
関連する仕様
ES2024 ArrayBuffer transfer
C における realloc
のように、メモリを再確保するメソッドを追加する仕様です。このメソッドが呼ばれたあとは元の ArrayBuffer
は detached されます。
結び
今回は Resizable ArrayBuffers について取り上げてみました。この仕様は人によっては対応が必要そうかなと思い記事にしてみました。
その他の提案の記事も Zenn に書いているのでよかったら読んでください。最後まで読んでいただきありがとうございました!
Discussion