mozilla

Revision 5115 of Javascript typed arrays

  • リビジョンの URL スラグ: javascript_typed_arrays
  • リビジョンのタイトル: Javascript typed arrays
  • リビジョンの ID: 5115
  • 作成日:
  • 作成者: hATrayflood
  • 現行リビジョン いいえ
  • コメント one or more formatting changes

このリビジョンの内容

{{ gecko_minversion_header("2") }}{{ draft() }}

Web アプリケーションはますます強力になり、音声や動画の操作、WebSockets を用いた生データへのアクセスなどさまざまな機能が追加されることから、JavaScript コードがより速く動作することや生のバイナリデータの操作が容易になることが有益であると考えられるのは明らかです。以前はそれを、生データを string として扱うことと、データバッファからバイト単位のデータを取り出す charCodeAt() メソッドを用いることによりシミュレートされていました。

しかし、この方法は複雑なデータ変換が必要 (特に、バイナリデータが実際のバイト形式のデータではなく、32 ビット整数値や浮動小数点数値のようなものである場合) であったことから、動作が遅いうえに誤りを起こしやすいものでした。

JavaScript の型付き配列は、生のバイナリデータをより効率的にアクセスする機能を提供します。

バッファとビュー: 型付き配列の構造

最大限の柔軟性と効率性を達成するために、JavaScript の型付き配列では バッファビュー に実装を分けています。バッファ (ArrayBuffer クラスとして実装) は、データの塊を表すオブジェクトです。これは特に形式を持たず、またその中身にアクセスする手段を提供しません。バッファに格納されている情報にアクセスするには、ビューを使用することが必要です。ビューはコンテキスト (データの種類、開始位置のオフセット、要素の数) を提供し、データを実際の型付き配列に返します。ビューは ArrayBufferView クラスとこのサブクラスとして実装されています。

{{ tree() }}

ビューをバッファと合わせて用いる

16 バイトのバッファを作成してみましょう:

var buffer = new ArrayBuffer(16);

これで、全体が 0 で初期化されたメモリ領域ができました。しかし、そのバッファに対してできることはあまりありません。バッファが実際に 16 バイトの大きさであることを確認する程度のことはできます:

if (buffer.byteLength == 16) {
  alert("Yes, it's 16 bytes.");
} else {
  alert("Oh no, it's the wrong size!");
} 

このバッファで実際の作業を行う前に、ビューを作成することが必要です。バッファ内のデータを 32 ビット符号付き整数値の配列として扱うビューを作成してみましょう:

var int32View = new Int32Array(buffer);

この時点で配列のフィールドへ、通常の配列と同じようにアクセスすることが可能になります:

for (var i=0; i<int32View.length; i++) {
  int32View[i] = i*2;
}

これは配列を値 0, 2, 4, 6 の 4つのエントリ (4 つのエントリが各 4 バイトで、合計 16 バイト) で埋めます。

同一データに対する複数のビュー

同一のデータに対して複数のビューを作成できることを考えると、それらは実に興味深いものになります。例えば、前出のコードの続きを以下のようにします:

var int16View = new Int16Array(buffer);

for (var i=0; i<int16View.length; i++) {
  console.log("Entry " + i + ": " + int16View[i]);
}

ここでは、同一のバッファを既存の 32 ビット値のビューと共有する 16 ビット整数値のビューを作成して、バッファ内の値すべてを 16 ビット整数値として出力しています。すると、0, 0, 2, 0, 4, 0, 6, 0 という出力を得ます。

ここで一歩進みましょう。以下のコードについて考えてみてください:

int16View[0] = 32;
console.log("Entry 0 in the 32-bit array is now " + int32View[0]);

このコードの出力は "Entry 0 in the 32-bit array is now 32" になります。言い換えると、2 つの配列は同じデータバッファを異なる形式で取り扱う単純なビューであるということです。同様のことを、任意の ビュータイプ で行うことができます。

複合データ構造を扱う

1 つのバッファを、バッファ内において異なるオフセットで始まり、またタイプが異なる複数のビューと結びつけることで、複数の種類のデータを持つデータオブジェクトを取り扱うことが可能になります。これにより、例えば WebGL 由来の複合データ構造、データファイル、js-ctypes を用いる際に必要になる C 構造体を取り扱うことが可能になります。

以下の C 構造体について考えてみましょう:

struct someStruct {
  unsigned long id;
  char username[16];
  float amountDue;
}; 

このような形式のデータを含むバッファは、以下のようにアクセスできます:

var buffer = new ArrayBuffer(24);

// ... read the data into the buffer ...

var idView = new Uint32Array(buffer, 0, 1);
var usernameView = new Uint8Array(buffer, 4, 16);
var amountDueView = new Float32Array(buffer, 20, 1);

例えば合計金額には amountDueView[0] でアクセスできます。

注意: C 構造体における データ構造の配置 は機種依存です。これらのデータ埋め込みの違いに注意および配慮してください。

互換性

型付き配列は WebKit でも利用可能です。Chrome 7 では ArrayBufferFloat32ArrayInt16ArrayUint8Array がサポートされています。Chrome 9 では DataView オブジェクトのサポートが追加されています。

仕様書

{{ languages ( {"en": "en/javascript_typed_arrays", "zh-tw": "zh_tw/JavaScript_typed_arrays"} ) }}

このリビジョンのソースコード

<p>{{ gecko_minversion_header("2") }}{{ draft() }}</p>
<p>Web アプリケーションはますます強力になり、音声や動画の操作、WebSockets を用いた生データへのアクセスなどさまざまな機能が追加されることから、JavaScript コードがより速く動作することや生のバイナリデータの操作が容易になることが有益であると考えられるのは明らかです。以前はそれを、生データを <a href="/ja/JavaScript/Reference/Global_Objects/String" title="ja/Core JavaScript 1.5 Reference/Global Objects/String">string</a> として扱うことと、データバッファからバイト単位のデータを取り出す <a href="/ja/JavaScript/Reference/Global_Objects/String/charCodeAt" title="ja/Core JavaScript 1.5 Reference/Global Objects/String/charCodeAt"><code>charCodeAt()</code></a> メソッドを用いることによりシミュレートされていました。</p>
<p>しかし、この方法は複雑なデータ変換が必要 (特に、バイナリデータが実際のバイト形式のデータではなく、32 ビット整数値や浮動小数点数値のようなものである場合) であったことから、動作が遅いうえに誤りを起こしやすいものでした。</p>
<p>JavaScript の型付き配列は、生のバイナリデータをより効率的にアクセスする機能を提供します。</p>
<h2>バッファとビュー: 型付き配列の構造</h2>
<p>最大限の柔軟性と効率性を達成するために、JavaScript の型付き配列では <strong>バッファ</strong> と <strong>ビュー</strong> に実装を分けています。バッファ (<a href="/ja/javascript_typed_arrays/ArrayBuffer" title="ja/JavaScript typed arrays/ArrayBuffer"><code>ArrayBuffer</code></a> クラスとして実装) は、データの塊を表すオブジェクトです。これは特に形式を持たず、またその中身にアクセスする手段を提供しません。バッファに格納されている情報にアクセスするには、ビューを使用することが必要です。ビューはコンテキスト (データの種類、開始位置のオフセット、要素の数) を提供し、データを実際の型付き配列に返します。ビューは <a href="/ja/javascript_typed_arrays/ArrayBufferView" title="ja/JavaScript typed arrays/ArrayBufferView"><code>ArrayBufferView</code></a> クラスとこのサブクラスとして実装されています。</p>
<p>{{ tree() }}</p>
<h2>ビューをバッファと合わせて用いる</h2>
<p>16 バイトのバッファを作成してみましょう:</p>
<pre class="deki-transform">var buffer = new ArrayBuffer(16);
</pre>
<p>これで、全体が 0 で初期化されたメモリ領域ができました。しかし、そのバッファに対してできることはあまりありません。バッファが実際に 16 バイトの大きさであることを確認する程度のことはできます:</p>
<pre class="brush: js">if (buffer.byteLength == 16) {
  alert("Yes, it's 16 bytes.");
} else {
  alert("Oh no, it's the wrong size!");
} 
</pre>
<p>このバッファで実際の作業を行う前に、ビューを作成することが必要です。バッファ内のデータを 32 ビット符号付き整数値の配列として扱うビューを作成してみましょう:</p>
<pre class="brush: js">var int32View = new Int32Array(buffer);
</pre>
<p>この時点で配列のフィールドへ、通常の配列と同じようにアクセスすることが可能になります:</p>
<pre class="brush: js">for (var i=0; i&lt;int32View.length; i++) {
  int32View[i] = i*2;
}
</pre>
<p>これは配列を値 0, 2, 4, 6 の 4つのエントリ (4 つのエントリが各 4 バイトで、合計 16 バイト) で埋めます。</p>
<h3>同一データに対する複数のビュー</h3>
<p>同一のデータに対して複数のビューを作成できることを考えると、それらは実に興味深いものになります。例えば、前出のコードの続きを以下のようにします:</p>
<pre class="brush: js">var int16View = new Int16Array(buffer);

for (var i=0; i&lt;int16View.length; i++) {
  console.log("Entry " + i + ": " + int16View[i]);
}
</pre>
<p>ここでは、同一のバッファを既存の 32 ビット値のビューと共有する 16 ビット整数値のビューを作成して、バッファ内の値すべてを 16 ビット整数値として出力しています。すると、0, 0, 2, 0, 4, 0, 6, 0 という出力を得ます。</p>
<p>ここで一歩進みましょう。以下のコードについて考えてみてください:</p>
<pre class="brush: js">int16View[0] = 32;
console.log("Entry 0 in the 32-bit array is now " + int32View[0]);
</pre>
<p>このコードの出力は "Entry 0 in the 32-bit array is now 32" になります。言い換えると、2 つの配列は同じデータバッファを異なる形式で取り扱う単純なビューであるということです。同様のことを、任意の <a href="/ja/javascript_typed_arrays/ArrayBufferView#Typed_array_subclasses" title="ja/JavaScript typed arrays/ArrayBufferView#Typed array subclasses">ビュータイプ</a> で行うことができます。</p>
<h2>複合データ構造を扱う</h2>
<p>1 つのバッファを、バッファ内において異なるオフセットで始まり、またタイプが異なる複数のビューと結びつけることで、複数の種類のデータを持つデータオブジェクトを取り扱うことが可能になります。これにより、例えば <a href="/ja/WebGL" title="ja/WebGL">WebGL</a> 由来の複合データ構造、データファイル、<a href="/ja/js-ctypes" title="ja/js-ctypes">js-ctypes</a> を用いる際に必要になる C 構造体を取り扱うことが可能になります。</p>
<p>以下の C 構造体について考えてみましょう:</p>
<pre class="brush: cpp">struct someStruct {
  unsigned long id;
  char username[16];
  float amountDue;
}; 
</pre>
<p>このような形式のデータを含むバッファは、以下のようにアクセスできます:</p>
<pre class="brush: js">var buffer = new ArrayBuffer(24);

// ... read the data into the buffer ...

var idView = new Uint32Array(buffer, 0, 1);
var usernameView = new Uint8Array(buffer, 4, 16);
var amountDueView = new Float32Array(buffer, 20, 1);
</pre>
<p>例えば合計金額には <code>amountDueView[0]</code> でアクセスできます。</p>
<div class="note"><strong>注意: </strong>C 構造体における <a class="external" href="http://en.wikipedia.org/wiki/Data_structure_alignment" title="http://en.wikipedia.org/wiki/Data_structure_alignment">データ構造の配置</a> は機種依存です。これらのデータ埋め込みの違いに注意および配慮してください。</div>
<h2>互換性</h2>
<p>型付き配列は WebKit でも利用可能です。Chrome 7 では <code>ArrayBuffer</code>、<code>Float32Array</code>、<code>Int16Array</code>、<code>Uint8Array</code> がサポートされています。Chrome 9 では <code>DataView</code> オブジェクトのサポートが追加されています。</p>
<h2>仕様書</h2>
<ul> <li><a class="link-https" href="https://cvs.khronos.org/svn/repos/registry/trunk/public/webgl/doc/spec/TypedArray-spec.html" title="https://cvs.khronos.org/svn/repos/registry/trunk/public/webgl/doc/spec/TypedArray-spec.html">Typed Arrays</a></li>
</ul>
<p>{{ languages ( {"en": "en/javascript_typed_arrays", "zh-tw": "zh_tw/JavaScript_typed_arrays"} ) }}</p>
このリビジョンへ戻す