JavaScript typed arrays

  • Revision slug: JavaScript_typed_arrays
  • Revision title: JavaScript typed arrays
  • Revision id: 8105
  • Created:
  • Creator: vjeux
  • Is current revision? No
  • Comment Reverted to earlier version; 80 words removed

Revision Content

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

As web applications become more and more powerful, adding features such as audio and video manipulation, access to raw data using WebSockets, and so forth, it has become clear that there are times when it would be helpful for JavaScript code to be able to quickly and easily manipulate raw binary data. In the past, this had to be simulated by treating the raw data as a string and using the charCodeAt() method to read the bytes from the data buffer.

However, this is slow and error-prone, due to the need for multiple conversions (especially if the binary data is not actually byte-format data, but, for example, 32-bit integers or floats).

JavaScript typed arrays provide a mechanism for accessing raw binary data much more efficiently.

Buffers and views: typed array architecture

To achieve maximum flexibility and efficiency, JavaScript typed arrays split the implementation into a buffer and a view. A buffer (implemented by the ArrayBuffer class) is an object representing a chunk of data; it has no format to speak of, and offers no mechanism for accessing its contents. In order to access the memory contained in a buffer, you need to use a view. A view provides a context—that is, a data type, starting offset, and number of elements—that turns the data into an actual typed array. Views are implemented by the ArrayBufferView class and its subclasses.

Using views with buffers

Let's create a 16-byte buffer:

var buffer = new ArrayBuffer(16);

At this point, we have a chunk of memory whose bytes are all pre-initialized to 0. There's not a lot we can do with it, though. We can confirm that it is indeed 16 bytes long, and that's about it:

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

Before we can really work with this buffer, we need to create a view. Let's create a view that treats the data in the buffer as an array of 32-bit signed integers:

var int32View = new Int32Array(buffer);

Now we can access the fields in the array just like a normal array:

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

This fills out the 4 entries in the array (4 entries at 4 bytes each makes 16 total bytes) with the values 0, 2, 4, and 6.

Multiple views on the same data

Things start to get really interesting when you consider that you can create multiple views onto the same data. For example, given the code above, we can continue like this:

var int16View = new Int16Array(buffer);

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

Here we create a 16-bit integer view that shares the same buffer as the existing 32-bit view and we output all the values in the buffer as 16-bit integers. Now we get the output 0, 0, 2, 0, 4, 0, 6, 0.

You can go a step farther, though. Consider this:

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

The output from this is "Entry 0 in the 32-bit array is now 32". In other words, the two arrays are indeed simply views on the same data buffer, treating it as different formats. You can do this with any view types.

Working with complex data structures

By combining a single buffer with multiple views of different types, starting at different offsets into the buffer, you can interact with data objects containing multiple data types. This lets you, for example, interact with complex data structures from WebGL, data files, or C structures you need to use while using js-ctypes.

Consider this C structure:

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

You can access a buffer containing data in this format like this:

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);

Then you can access, for example, the amount due with amountDueView[0].

Compatibility

Typed arrays are available in WebKit as well. Chrome 7 includes support for ArrayBuffer, Float32Array, Int16Array, and Uint8Array. Chrome 9 adds support for DataView objects.

Revision Source

<p>{{ gecko_minversion_header("2") }}{{ draft() }}</p>
<p>As web applications become more and more powerful, adding features such as audio and video manipulation, access to raw data using WebSockets, and so forth, it has become clear that there are times when it would be helpful for JavaScript code to be able to quickly and easily manipulate raw binary data. In the past, this had to be simulated by treating the raw data as a <a href="/En/Core_JavaScript_1.5_Reference/Global_Objects/String" title="en/Core JavaScript 1.5 Reference/Global Objects/String">string</a> and using the <a href="/en/JavaScript/Reference/Global_Objects/String/charCodeAt" title="en/Core JavaScript 1.5 Reference/Global Objects/String/charCodeAt"><code>charCodeAt()</code></a> method to read the bytes from the data buffer.</p>
<p>However, this is slow and error-prone, due to the need for multiple conversions (especially if the binary data is not actually byte-format data, but, for example, 32-bit integers or floats).</p>
<p>JavaScript typed arrays provide a mechanism for accessing raw binary data much more efficiently.</p>
<h2>Buffers and views: typed array architecture</h2>
<p>To achieve maximum flexibility and efficiency, JavaScript typed arrays split the implementation into a <strong>buffer</strong> and a <strong>view</strong>. A buffer (implemented by the <a href="/en/JavaScript_typed_arrays/ArrayBuffer" title="en/JavaScript typed arrays/ArrayBuffer"><code>ArrayBuffer</code></a> class) is an object representing a chunk of data; it has no format to speak of, and offers no mechanism for accessing its contents. In order to access the memory contained in a buffer, you need to use a view. A view provides a context—that is, a data type, starting offset, and number of elements—that turns the data into an actual typed array. Views are implemented by the <a href="/en/JavaScript_typed_arrays/ArrayBufferView" title="en/JavaScript typed arrays/ArrayBufferView"><code>ArrayBufferView</code></a> class and its subclasses.</p>
<h2>Using views with buffers</h2>
<p>Let's create a 16-byte buffer:</p>
<pre>var buffer = new ArrayBuffer(16);
</pre>
<p>At this point, we have a chunk of memory whose bytes are all pre-initialized to 0. There's not a lot we can do with it, though. We can confirm that it is indeed 16 bytes long, and that's about it:</p>
<pre>if (buffer.byteLength == 16) {
  alert("Yes, it's 16 bytes.");
} else {
  alert("Oh no, it's the wrong size!");
} 
</pre>
<p>Before we can really work with this buffer, we need to create a view. Let's create a view that treats the data in the buffer as an array of 32-bit signed integers:</p>
<pre>var int32View = new Int32Array(buffer);
</pre>
<p>Now we can access the fields in the array just like a normal array:</p>
<pre>for (var i=0; i&lt;int32View.length; i++) {
  int32View[i] = i*2;
}
</pre>
<p>This fills out the 4 entries in the array (4 entries at 4 bytes each makes 16 total bytes) with the values 0, 2, 4, and 6.</p>
<h4>Multiple views on the same data</h4>
<p>Things start to get really interesting when you consider that you can create multiple views onto the same data. For example, given the code above, we can continue like this:</p>
<pre>var int16View = new Int16Array(buffer);

for (var i=0; i&lt;int16View.length; i++) {
  console.log("Entry " + i + ": " + int16View[i]);
}
</pre>
<p>Here we create a 16-bit integer view that shares the same buffer as the existing 32-bit view and we output all the values in the buffer as 16-bit integers. Now we get the output 0, 0, 2, 0, 4, 0, 6, 0.</p>
<p>You can go a step farther, though. Consider this:</p>
<pre>int16View[0] = 32;
console.log("Entry 0 in the 32-bit array is now " + int32View[0]);
</pre>
<p>The output from this is "Entry 0 in the 32-bit array is now 32". In other words, the two arrays are indeed simply views on the same data buffer, treating it as different formats. You can do this with any <a href="/en/JavaScript_typed_arrays/ArrayBufferView#Typed_array_subclasses" title="en/JavaScript typed arrays/ArrayBufferView#Typed array subclasses">view types</a>.</p>
<h2>Working with complex data structures</h2>
<p>By combining a single buffer with multiple views of different types, starting at different offsets into the buffer, you can interact with data objects containing multiple data types. This lets you, for example, interact with complex data structures from <a href="/en/WebGL" title="en/WebGL">WebGL</a>, data files, or C structures you need to use while using <a href="/en/js-ctypes" title="en/js-ctypes">js-ctypes</a>.</p>
<p>Consider this C structure:</p>
<pre>struct someStruct {
  unsigned long id;
  char username[16];
  float amountDue;
}; 
</pre>
<p>You can access a buffer containing data in this format like this:</p>
<pre>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>Then you can access, for example, the amount due with <code>amountDueView[0]</code>.</p><h2>Compatibility</h2>
<p>Typed arrays are available in WebKit as well. Chrome 7 includes support for <code>ArrayBuffer</code>, <code>Float32Array</code>, <code>Int16Array</code>, and <code>Uint8Array</code>. Chrome 9 adds support for <code>DataView</code> objects.</p>
Revert to this revision