LoginSignup
86

More than 5 years have passed since last update.

[JavaScript] ArrayBufferについて調べてみた

Last updated at Posted at 2015-02-05

ArrayBuffer、DataView、TypedArrayはJSからバイナリデータを扱うための仕様です。
どういう操作があるのかを改めて確認してみました。

まずは簡単に各クラスの定義から整理。

ArrayBuffer

物理メモリの領域(バッファ)を確保するためのクラス。
ただし、確保するのみでバッファに対する操作は提供していません。
バッファの操作はTypedArrayDataViewいずれかのクラスを利用します。

ArrayBufferのプロパティ

  • byteLength ... バッファの総バイトサイズ。

ArrayBufferのメソッド

  • slice() ... バッファの開始位置と終了位置を指定して、新しいArrayBufferを生成します。

TypedArray

いわゆる型付き配列。Float32ArrayUint8Arrayなど、型を指定してバッファから配列を生成する。
通常の配列(Array)のように添字によってバッファにアクセス可能で、「高速に」読み書きが行えます。

ただ、配列構造を自身で把握し適切に処理を行わないとエラーが出るなど低レベルな機能のみを提供しています。
(アライメント(バイト境界)エンディアン方式の違いなどを自身で考慮する必要がある)

アライメント(バイト境界)

アライメントというのがあり、バイト数を適切に設定しないとエラーとなってしまいます。
具体的には生成するビューのビット数に応じて整数倍となるバイト数を設定しないとなりません。
例えば、Uint16Arrayを使ってビューを生成する場合、16bit = 2byte、つまり2の整数倍のバイト数を指定しないとエラーとなります。
(Float64Arrayの場合は64bit = 8byteなので、8の整数倍のバイト数が必要)

中途半端なバイト数で生成してしまうと、確保したメモリ領域を超えてしまう可能性があるためエラーとなるのでしょう。
このあたりはメモリをダイレクトに使わないJSではイメージしづらいかもしれません。

エンディアン方式

マルチバイトサイズとなる型のクラスを利用する場合、特に実行環境が読めないJSではしっかりと考慮する必要があります。

DataView

TypedArrayよりも高機能なバッファ操作用クラス。
getUint32などのように分かりやすいメソッドが用意されています。

DataViewのプロパティ

  • buffer ... 参照しているArrayBuffer
  • byteOffset ... ビューの開始位置(バイト数)を取得
  • byteLength ... ビューからアクセス可能なサイズ(バイト数)を取得

DataViewのメソッド

Getter

メソッド 意味
getInt8 バッファから 8bit整数値(符号あり)を読み込む
getUint8 バッファから 8bit整数値(符号なし)を読み込む
getInt16 バッファから 16bit整数値(符号あり)を読み込む
getUint16 バッファから 16bit整数値(符号なし)を読み込む
getInt32 バッファから 32bit整数値(符号あり)を読み込む
getUint32 バッファから 32bit整数値(符号なし)を読み込む
getFloat32 バッファから 32bit浮動小数点数を読み込む
getFloat64 バッファから 64bit浮動小数点数を読み込む

Setter

メソッド 意味
setInt8 バッファへ 8bit整数値(符号あり)を書き込む
setUint8 バッファへ 8bit整数値(符号なし)を書き込む
setInt16 バッファへ 16bit整数値(符号あり)を書き込む
setUint16 バッファへ 16bit整数値(符号なし)を書き込む
setInt32 バッファへ 32bit整数値(符号あり)を書き込む
setUint32 バッファへ 32bit整数値(符号なし)を書き込む
setFloat32 バッファへ 32bit浮動小数点数を書き込む
setFloat64 バッファへ 64bit浮動小数点数を書き込む

バイナリデータを扱う

TypedArrayから扱う

ビューを使う場合、バイナリデータから特定のバイト列を取り出したり、書き込んだりすることが主な目的となると思います。

例えば、4byte + 2byte + 4byteのデータ構造でそれぞれにデータを書き込みたいとします。
その場合は以下のようにバッファの生成と書き込みを行います。

create-buffer
var buffer = new ArrayBuffer(4 + 2 + 4); // 4 + 2 + 4byteのバッファを確保

// 4byteの書き込みを行うビューを生成
var ary_u16 = new Uint16Array(buffer, 0, 2);
ary_u16[0] = 0x0001;
ary_u16[1] = 0x0100;

// 1byteの書き込みを行うビューを生成
var ary_u8 = new Uint8Array(buffer, 4, 2);
ary_u8[0] = 0xff;
ary_u8[1] = 0x0f;

// 4byteの書き込みを行うビューを生成
var ary_i16 = new Int16Array(buffer, 6, 2);
ary_i16[0] = -50;
ary_i16[1] = -30;

var result = new Uint8Array(buffer);

console.log(result); // => [1, 0, 0, 1, 255, 15, 206, 255, 226, 255]

DataViewから扱う

バイナリに意図したデータを書き込んだり、色々な種類のデータをパックするのに使われるのがこちらのDataViewです。

上記と同じ処理を、DataViewを使って行うと以下のようになります。

create-buffer-dataview
var buffer = new ArrayBuffer(4 + 2 + 4);
var view = new DataView(buffer);

var offset = 0;

// 4byteの書き込み
view.setUint16(offset, 0x0001, true);
offset += 2;
view.setUint16(offset, 0x0100, true);
offset += 2;

// 2byteの書き込み
view.setUint8(offset, 0xff);
offset += 1;
view.setUint8(offset, 0x0f);
offset += 1;

// 4byteの書き込み
view.setInt16(offset, -50, true);
offset += 2;
view.setUint16(offset, -30, true);
offset += 2;

var result = new Uint8Array(buffer);

console.log(result); // =>  [1, 0, 0, 1, 255, 15, 206, 255, 226, 255]

CanvasのImagaDataはTypedArray

CanvasのgetImagaDataで取得されるImageDataオブジェクトのdataプロパティはUint8ClampedArrayになっています。
これもTypedArrayで、直に操作することで画像をバイナリデータとして扱うことができます。

こちらの配列は画素なのでイメージしやすく、実際に操作したことがある人も少なくないのではないでしょうか。
ともあれ、バイナリデータを扱えると色々と活用できそうなのでしっかりと把握しておきたいですね。

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
86