JavaScriptにおけるArrayBufferとDataViewの完全なガイド

メモリとバイナリデータの基礎

まず、コンピュータメモリの基本的な概念を理解することが重要です。16進数では1桁が4ビットを表すため、0xFは1111に相当し、0xFFは8ビット(1バイト)になります。つまり、2桁の16進数が1バイトを表します。

例えば、8バイトのメモリ領域を確保し、所有するビットをすべて0で初期化すると、以下のようになります:

00 00 00 00 00 00 00 00

データ型のサイズ

メモリにアクセスする際の数据类型は、偏移(オフセット)バイト数决定了します:

  • BYTE(バイト):1バイト、偏移1
  • WORD(ワード):2バイト、偏移2
  • DWORD(ダブルワード):4バイト、偏移4
  • QWORD(クアッドワード):8バイト、偏移8

JavaScriptのTypedArrayでは、Int8Arrayは8ビット(1バイト)型であり、偏移値は1バイトです。同様に、Int16Arrayは16ビット(2バイト)型で、偏移値は2バイトです。

バイト順序(エンディアン)

計算機にはデータを保存する方法endelawareつの異なる方式があります:

  • リトルエンディアン:データの低位がアドレスの低位に保存される
  • ビッグエンディアン:データの高位がアドレスの低位に保存される

例として、4バイトのメモリ空間(アドレス0x100-0x103)に0x0000000Aを格納する場合を考えます:

// リトルエンディアンの場合
アドレス: 0x100 0x101 0x102 0x103
データ:   0x0A  0x00  0x00  0x00

実際に確認してみましょう:

let buffer = new ArrayBuffer(4);
let view = new Uint32Array(buffer);
view[0] = 0xA;

console.log(buffer);
// 表示結果
// [[Int8Array]]: Int8Array(4) [10, 0, 0, 0]
// [[Int16Array]]: Int16Array(2) [10, 0]
// [[Int32Array]]: Int32Array [10]
// [[Uint8Array]]: Uint8Array(4) [10, 0, 0, 0]
// byteLength: 4

Int32Arrayでは0x0000000Aと表示され、Int8Arrayでは0x0A000000のように表示されます。これはリトルエンディアン方式でデータが 저장されているためです。

別の例を見てみましょう:

let buffer = new ArrayBuffer(4);
let view = new Int8Array(buffer);
view[1] = 0x01;  // 0A 01 00 00

console.log(view);
// Int8Array(4) [0, 1, 0, 0]

let intView = new Uint32Array(buffer);
console.log(intView[0]); // 266 (0x010A)

byteLengthとlengthの違いを理解することは重要です:

  • byteLength:バッファのサイズ(バイト単位)。不変。
  • length:TypedArrayの要素数。数据类型によって変化する。
let buffer = new ArrayBuffer(8);

let int8 = new Int8Array(buffer);
console.log(int8.byteLength, int8.length);  // 8, 8

let int16 = new Int16Array(buffer);
console.log(int16.byteLength, int16.length);  // 8, 4

let int32 = new Int32Array(buffer);
console.log(int32.byteLength, int32.length);  // 8, 2

ArrayBuffer API

ArrayBufferは固定長のバイナリデータを表現しますが、直接操作することはできません。操作にはTypedArrayまたはDataViewを使用する必要があります。

// 8バイトのバッファを確保
let buffer = new ArrayBuffer(8);
console.log(buffer.byteLength);  // 8

// DataViewがビューかどうかを確認
console.log(ArrayBuffer.isView(new DataView(buffer)));  // true

// Int32Arrayで参照(4バイト × 2 = 8バイト)
let view = new Int32Array(buffer);
console.log(view);

// 値を設定
view[0] = 0xFFFF;  // 65535
view[1] = 0xFFFE;  // 65534

console.log(view);  // Int32Array(2) [65535, 65534]

TypedArray(型付き配列)

TypedArrayにはいくつかのコンストラクタがあります:

// 長さから作成
let arr1 = new Int32Array(4);  // 4要素の配列(16バイト)

// 既存のバッファから作成
let buffer = new ArrayBuffer(8);
let arr2 = new Int16Array(buffer);

// バッファ、オフセット、长度から作成
let arr3 = new Int8Array(buffer, 2, 3);  // 2バイト偏移、3要素

// 反復可能オブジェクトから作成
let arr4 = new Uint8Array([1, 2, 3, 4]);

代表的なTypedArrayの種類:

  • 8ビット整数:Int8Array、Uint8Array、Uint8ClampedArray
  • 16ビット整数:Int16Array、Uint16Array
  • 32ビット整数:Int32Array、Uint32Array
  • 浮動小数点数:Float32Array、Float64Array

要素へのアクセス:

let buffer = new ArrayBuffer(8);
let arr = new Int8Array(buffer);

for (let i = 0; i < buffer.byteLength; i++) {
    arr[i] = i * 2;
}

console.log(arr);  // Int8Array(8) [0, 2, 4, 6, 8, 10, 12, 14]
console.log(arr.length);    // 8
console.log(arr[arr.length - 1]);  // 14

// 偏移付で作成
let partial = new Int8Array(buffer, 2);
console.log(partial);  // Int8Array(6) [4, 6, 8, 10, 12, 14]

DataView

DataViewは、異なるデータ型を同じバッファから読み書きできる低レベルインターフェースです。プラットフォームのバイト順序を意識せずに操作できる点が特徴です。

let buffer = new ArrayBuffer(8);
let dataView = new DataView(buffer, 0, 8);

// 値の書き込み(リトルエンディアン)
dataView.setInt16(0, 14, true);
console.log(dataView.getInt16(0, true));   // 14
console.log(dataView.getInt8(0));          // 14 (0x0E)
console.log(dataView.getInt32(0, true));   // 14

// 異なるオフセットでの操作
dataView.setUint8(2, 0xFF);
console.log(dataView.getUint8(2));  // 255

// プロパティ
console.log(dataView.buffer);      // ArrayBuffer
console.log(dataView.byteLength);  // 8
console.log(dataView.byteOffset);  // 0

主要なメソッド:

  • getInt8, getUint8, getInt16, getUint16, getInt32, getUint32, getFloat32, getFloat64
  • setInt8, setUint8, setInt16, setUint16, setInt32, setUint32, setFloat32, setFloat64

各メソッドの3番目の引数には、littleEndianフラグを指定できます(trueでリトルエンディアン)。

バイト順序の検出

実行環境のバイト順序を検出する必要がある場合があります:

function isLittleEndian() {
    let testBuffer = new ArrayBuffer(2);
    new DataView(testBuffer).setInt16(0, 1, true);
    
    let testArray = new Int16Array(testBuffer);
    return testArray[0] === 1;
}

console.log("リトルエンディアン:", isLittleEndian());  // trueまたはfalse

Node.jsのBufferとの互換性

ブラウザ環境でNode.js同様のBuffer APIを使用したい場合は、bufferライブラリ可以利用できます:

<script src="https://bundle.run/buffer@5.6.0"></script>
<script>
  const { Buffer } = buffer;
  
  let buf = Buffer.alloc(4);
  buf.writeInt32LE(0x0A, 0);
  
  console.log(buf);
  console.log(buf.readInt32LE().toString(16));
</script>

まとめ

ArrayBufferはバイナリデータの的基础となるオブジェクトであり、TypedArrayとDataViewを使用して効果的に操作できます。バイト順序 мясо正しく理解し、適切なAPIを選択することが重要です。

タグ: javascript arraybuffer dataview TypedArray binary-data

9月29日 12:17 投稿