Abstract

This specification provides an API for interoperability with
native binary data. It defines a generic fixed-length buffer
type, as well as accessor types that allow access to the data
stored within the buffer.

The functionality described here originated in the WebGL
specification [WEBGL].

Status of this document

This document is an editor's draft. Do not cite this document as other than work in
progress. Public discussion of this specification is welcome on the
(archived) WebGL
mailing list
public_webgl@khronos.org
(see
instructions).

The latest version of this document, including all revision history, may be obtained via
Subversion:

Table of contents

Introduction

ECMAScript [ECMA-262] has
traditionally been used in contexts where there is no access to
binary data. Where binary data has needed to be manipulated, it
is often stored as a String and accessed using charCodeAt(), or
stored as an Array with conversion to and from base64 for
transmission. Both of these methods are slow and error-prone.
For example, reading binary data as 32-bit integers requires
manual conversion of 4 source bytes to and from the target type.
Reading floating-point data is even more expensive.

As web applications gain access to new functionality, working
with binary data has become a much-demanded feature. Current
specifications such as the File
API [FILEAPI] and Web
Sockets [WEBSOCKETS] would benefit
from being able to read and write binary data directly in its
native form. Specifications such as
WebGL [WEBGL] require this
functionality to meet acceptable performance characteristics.

This specification defines a minimal set of functionality for
accessing binary data from ECMAScript.

Overview

This specification defines an ArrayBuffer type, representing a generic fixed-length binary buffer. It is not possible to manipulate the contents of an ArrayBuffer directly. Instead, a group of types are used to create views of the ArrayBuffer. For example, to access the buffer as an array of 32-bit signed integers, an Int32Array would be created that refers to the ArrayBuffer.

Multiple typed array views can refer to the same ArrayBuffer, of different types, lengths, and offsets. This allows for complex data structures to be built up in the ArrayBuffer. As an example, given the following code:

// create an 8-byte ArrayBuffer
var b = new ArrayBuffer(8);
// create a view v1 referring to b, of type Int32, starting at
// the default byte index (0) and extending until the end of the buffer
var v1 = new Int32Array(b);
// create a view v2 referring to b, of type Uint8, starting at
// byte index 2 and extending until the end of the buffer
var v2 = new Uint8Array(b, 2);
// create a view v3 referring to b, of type Int16, starting at
// byte index 2 and having a length of 2
var v3 = new Int16Array(b, 2, 2);

The following buffer and view layout is created:

var

index

bytes (not indexable)

b =

0

1

2

3

4

5

6

7

indices

v1 =

0

1

v2 =

0

1

2

3

4

5

v3 =

0

1

This defines an 8-byte buffer b, and three views of
that buffer, v1, v2, and v3. Each of
the views refers to the same buffer -- so v1[0] refers
to bytes 0..3 as a signed 32-bit integer, v2[0] refers
to byte 2 as a unsigned 8-bit integer, and v3[0] refers
to bytes 2..3 as a signed 16-bit integer. Any modification to
one view is immediately visible in the other: for example,
after v2[0] = 0xff; v2[1] = 0xff; then v3[0] ==
-1 (where -1 is represented as 0xffff).

Computer memory is fundamentally organized as a linear series of numbers. On all contemporary
computers, each of these values is an 8-bit binary number (a "byte"). Larger numbers are represented
by interpreting multiple bytes at a time as a single value. For example, this specification discusses
16-bit and 32-bit signed and unsigned integers, and 32-bit and 64-bit floating-point numbers.

There exists an ambiguity in the interpretation of such multi-byte values: specifically, which order
the bytes are assembled. Little-endian architectures treat the byte with the lowest address as
the least significant byte; big-endian architectures treat the byte with the lowest address as
the most significant byte. More possibilities exist, but are not commonly used. Little-endian
architectures are currently used in most consumer computing devices, but big-endian architectures are
still used in important use cases.

The following diagram illustrates the difference in storage of values between little-endian and
big-endian architectures. If the value 305419896 (0x12345678 in
hexadecimal) were stored into an ArrayBuffer using a Uint32Array, then depending on the host
computer's endianness, the component bytes of the ArrayBuffer would contain the following (organized
from low address to high address):

Little-endian:

78

56

34

12

Big-endian:

12

34

56

78

In this specification, differences in endianness only become apparent in certain situations: for
example, when overlaying multiple types of views on the same region of
an ArrayBuffer. The following normative rules apply to implementations of
the typed array views.

The DataView type operates upon data with a specified endianness (big-endian
or little-endian).

The typed array view types (Uint8Array, Float32Array, etc.) are designed for in-memory assembly of
large blocks of data to be sent to the graphics card, audio system, etc. For these use cases, it is
required to use the native endianness of the host machine when writing the data to main memory. If
this specification had mandated for consistency that the typed array view types used a specified
endianness (for example, little-endian), the data would either be misinterpreted by the graphics card
on some processor architectures (in this example, big-endian architectures), or impose an
unacceptably high performance overhead on such architectures.

When receiving data from other computers, or reading files from disk, it is critical to specify the
byte order of the data so that it can be properly interpreted regardless of the endianness of the
host computer. Essentially every file format or network protocol in existence has a clearly specified
data format, including the byte order of all multi-byte data contained in the file or network
payload. The DataView view type is designed for input/output tasks and therefore operates upon data
with a specified byte order. The host computer's endianness is irrelevant when working with DataView;
it always reads or writes data assuming that data is stored with a particular endianness.

The specification below implicitly references certain type conversion rules; for example,
conversion of floating-point values to integer values of various sizes. The Web IDL
specification [WEBIDL] defines these rules, and references the
ECMA-262 specification [ECMA-262] for conversion algorithms such
as ToInt32.

The Web IDL specification does not currently define all of the numerical types referenced in
this specification; for example, byte, which is a signed 8-bit integer type. For
these types, the rules for the type of the closest size and signedness shall be extrapolated.

As a hint to implementors of the algorithms in the ECMA-262 specification, conversion of
floating-point numbers to integer values uses the truncate, or round-to-zero, rounding mode.

When the not-a-number (NaN) value is stored into a Float32Array
or Float64Array, or into a DataView using the setFloat32
or setFloat64 methods, the bit pattern written into the underlying ArrayBuffer is
not specified, but shall be one of the IEEE 754 bit patterns that represent
NaN [IEEE-754].

When a bit pattern representing an IEEE 754 NaN is loaded from a Float32Array
or Float64Array, or from a DataView using the getFloat32
or getFloat64 methods, the language binding (for example, ECMAScript) may use an
alternative bit pattern to represent the NaN value.

The Web IDL [WEBIDL] and ECMA-262
[ECMA-262] specifications govern all other handling of NaN values, in
particular the conversion to 0 when converting NaN to an integer value.

Creates a new ArrayBuffer of the given length in bytes. The contents of the
ArrayBuffer are initialized to 0. If the requested number of bytes could not be
allocated an exception is raised.

Properties

unsigned long byteLength

Read-only property.

The length of the ArrayBuffer in bytes, as fixed at construction time.

Reading this property returns 0 if this ArrayBuffer has been neutered.

Methods

ArrayBuffer slice(long begin, optional long end)

Returns a new ArrayBuffer whose contents are a copy of this ArrayBuffer's bytes
from begin, inclusive, up to end, exclusive. If
either begin or end is negative, it refers to an index
from the end of the array, as opposed to from the beginning.

If end is unspecified, the new ArrayBuffer contains all bytes
from begin to the end of this ArrayBuffer.

The range specified by the begin and end values is clamped to
the valid index range for the current array. If the computed length of the new
ArrayBuffer would be negative, it is clamped to zero.

static boolean isView(any value)

Returns true if value is an object implementing
the ArrayBufferView interface,
and false otherwise.

The typed array view types represent a view of
an ArrayBuffer that allows for indexing and manipulation.
The length of each of these is fixed. Each of the typed array
view types follows the same template.

The following typed array view types are defined by this specification. The size below is given in bytes, and corresponds to the BYTES_PER_ELEMENT constant for the given type.

Type

Size

Description

Web IDL type

Equivalent C Type

Int8Array

1

8-bit 2's complement signed integer

byte

signed char

Uint8Array

1

8-bit unsigned integer

octet

unsigned char

Uint8ClampedArray

1

8-bit unsigned integer (clamped)

octet

unsigned char

Int16Array

2

16-bit 2's complement signed integer

short

short

Uint16Array

2

16-bit unsigned integer

unsigned short

unsigned short

Int32Array

4

32-bit 2's complement signed integer

long

int

Uint32Array

4

32-bit unsigned integer

unsigned long

unsigned int

Float32Array

4

32-bit IEEE floating point

unrestricted float

float

Float64Array

8

64-bit IEEE floating point

unrestricted double

double

Each of the typed array types has the following constructors, properties, constants and
methods. In the descriptions below, the generic term TypedArray is used to indicate that
any valid typed array view type is allowed. Uint8ClampedArray is defined in the
next section.

Create a new ArrayBuffer with enough bytes to
hold length elements of this typed array, then
creates a typed array view referring to the full buffer. As
with a directly constructed ArrayBuffer, the contents are
initialized to 0. If the requested number of bytes could not
be allocated an exception is raised.

TypedArray(TypedArray array)

TypedArray(type[] array)

Create a new ArrayBuffer with enough bytes to
hold array.length elements of this typed array,
then creates a typed array view referring to the full
buffer. The contents of the new view are initialized to
the contents of the given array or typed array, with
each element converted to the appropriate typed array
type.

Create a new TypedArray object using the passed
ArrayBuffer for its storage. Optional byteOffset and
length can be used to limit the section of the buffer
referenced. The byteOffset indicates the offset in bytes
from the start of the ArrayBuffer, and the length is the
count of elements from the offset that
this TypedArray will reference. If both byteOffset
and length are omitted, the TypedArray spans the
entire ArrayBuffer range. If the length is omitted, the
TypedArray extends from the given byteOffset until
the end of the ArrayBuffer.

The given byteOffset must be a multiple of the element size
of the specific type, otherwise an exception is raised.

If a given byteOffset and length references an area beyond
the end of the ArrayBuffer an exception is raised.

If length is not explicitly specified, the length of the
ArrayBuffer minus the byteOffset must be a multiple of the
element size of the specific type, or an exception is raised.

Constants

unsigned long BYTES_PER_ELEMENT

The size in bytes of each element in the array.

Properties

unsigned long length

Read-only property.

The length of the TypedArray in elements, as fixed at construction time.

Reading this property returns 0 if the referenced ArrayBuffer has been neutered.

The optional offset value indicates the index in the current array where values are
written. If omitted, it is assumed to be 0.

If the input array is a TypedArray, the two arrays may
use the same underlying ArrayBuffer. In this
situation, setting the values takes place as if all the data
is first copied into a temporary buffer that does not overlap
either of the arrays, and then the data from the temporary
buffer is copied into the current array.

If the offset plus the length of the given array is out of
range for the current TypedArray, an exception is raised.

TypedArraysubarray(long begin, optional long end)

Returns a new TypedArray view of the ArrayBuffer store for
this TypedArray, referencing the elements at begin, inclusive,
up to end, exclusive. If either begin or end
is negative, it refers to an index from the end of the array, as opposed to from the
beginning.

If end is unspecified, the subarray contains all elements
from begin to the end of the TypedArray.

The range specified by the begin and end values is clamped
to the valid index range for the current array. If the computed length of the
new TypedArray would be negative, it is clamped to zero.

The returned TypedArray will be of the same type as the array on which this
method is invoked.

Uint8ClampedArray is defined in order to replace CanvasPixelArray. It
behaves identically to the other typed array views, except that the setters and constructor use
clamping[WEBIDL]
rather than modulo arithmetic when converting incoming number values. The IDL
for Uint8ClampedArray follows.

An ArrayBuffer is a useful object for representing an arbitrary chunk of data. In many cases, such data will be read from disk or from the network, and will not follow the alignment restrictions that are imposed on the typed array views described earlier. In addition, the data will often be heterogeneous in nature and have a defined byte order. The DataView view provides a low-level interface for reading such data from and writing it to an ArrayBuffer.

Regardless of the host computer's endianness, DataView reads or writes
values to or from main memory with a specified endianness: big or little.

[
Constructor(ArrayBuffer buffer,
optional unsigned long byteOffset,
optional unsigned long byteLength)
]
interface DataView {
// Gets the value of the given type at the specified byte offset
// from the start of the view. There is no alignment constraint;
// multi-byte values may be fetched from any offset.
//
// For multi-byte values, the optional littleEndian argument
// indicates whether a big-endian or little-endian value should be
// read. If false or undefined, a big-endian value is read.
//
// These methods raise an exception if they would read
// beyond the end of the view.
byte getInt8(unsigned long byteOffset);
octet getUint8(unsigned long byteOffset);
short getInt16(unsigned long byteOffset,
optional boolean littleEndian);
unsigned short getUint16(unsigned long byteOffset,
optional boolean littleEndian);
long getInt32(unsigned long byteOffset,
optional boolean littleEndian);
unsigned long getUint32(unsigned long byteOffset,
optional boolean littleEndian);
float getFloat32(unsigned long byteOffset,
optional boolean littleEndian);
double getFloat64(unsigned long byteOffset,
optional boolean littleEndian);
// Stores a value of the given type at the specified byte offset
// from the start of the view. There is no alignment constraint;
// multi-byte values may be stored at any offset.
//
// For multi-byte values, the optional littleEndian argument
// indicates whether the value should be stored in big-endian or
// little-endian byte order. If false or undefined, the value is
// stored in big-endian byte order.
//
// These methods raise an exception if they would write
// beyond the end of the view.
void setInt8(unsigned long byteOffset,
byte value);
void setUint8(unsigned long byteOffset,
octet value);
void setInt16(unsigned long byteOffset,
short value,
optional boolean littleEndian);
void setUint16(unsigned long byteOffset,
unsigned short value,
optional boolean littleEndian);
void setInt32(unsigned long byteOffset,
long value,
optional boolean littleEndian);
void setUint32(unsigned long byteOffset,
unsigned long value,
optional boolean littleEndian);
void setFloat32(unsigned long byteOffset,
float value,
optional boolean littleEndian);
void setFloat64(unsigned long byteOffset,
double value,
optional boolean littleEndian);
};
DataView implements ArrayBufferView;

The following constructors, properties, and methods are available on a DataView:

Create a new DataView object using the passed
ArrayBuffer for its storage. Optional byteOffset and
byteLength can be used to limit the section of the buffer
referenced. The byteOffset indicates the offset in bytes
from the start of the ArrayBuffer, and the byteLength is
the number of bytes from the offset that
this DataView will reference. If both byteOffset
and byteLength are omitted, the DataView spans the
entire ArrayBuffer range. If the byteLength is omitted,
the DataView extends from the given byteOffset
until the end of the ArrayBuffer.

If the given byteOffset and byteLength references an
area beyond the end of the ArrayBuffer an exception is
raised.

Stores a value of the given type at the specified byte offset
from the start of the view. There is no alignment constraint;
multi-byte values may be stored at any offset.

For multi-byte values, the optional littleEndian argument
indicates whether the value should be stored in big-endian or
little-endian byte order. If false or undefined, the value is
stored in big-endian byte order.

These methods raise an exception if they would write
beyond the end of the view.

In order to enable repeated transfer of large amounts of data between Web
Workers [WEBWORKERS], ArrayBuffer implements
the Transferable
interface [HTML]. This section defines the behavior of ArrayBuffers and
views under
the structured
cloning[HTML] and transfer algorithms.

When a user agent is asked to clone an ArrayBufferView object old, it must run the following steps, which return a new object. These steps must be run atomically.

Let buffer be the result of invoking the internal structured cloning algorithm
recursively on the buffer property of old.

Let new view be a newly constructed ArrayBufferView subclass of the
same type as old, referring to buffer, and with the
same byteOffset, byteLength, and any subclass-specific properties
as old.

Return new view. It is the clone.

The above sections define the following behavior:

If an ArrayBuffer is transferred during a postMessage call, then
any ArrayBufferView instances which refer to that ArrayBuffer and
which are cloned during that call refer to the newly allocated ArrayBuffer
object. After the postMessage call, the ArrayBufferView instances
referring to the old ArrayBuffer can no longer be used to reference the
buffer's data. Further attempts to clone them or the old ArrayBuffer will
cause a DATA_CLONE_ERR exception to be thrown.

The behavior of cloning, rather than transferring, an ArrayBuffer and
any ArrayBufferView instances which refer to it is the same as for any other
object type.

Only the ArrayBuffer type is transferable. It is not possible to transfer
certain ArrayBufferView instances, and clone others, if they refer to the same
underlying ArrayBuffer.

Note that this code uses subarray() to create a new Float32Array
that references the same data as the original, so that it can always index the sub-array using 0..7.

Interleaved array types

Some APIs, in particular WebGL [WEBGL], can benefit from being able to
use a single contiguous buffer, with interleaved data types.
For example, a point might have coordinate data (3 Float32
values) followed by color data (4 Uint8 values).

For 4 points and their associated colors, this can be set up in the following way:

However, typed arrays don't have a way to explicitly encode the desired per-point structure, so some manual arithmetic must be done to correctly index into the right values. Note that the colors Uint8Array view is created with an explicit offset (which is given in bytes), so that the [0] element points at the 13th byte in the underlying buffer.

In this example, each set of packed data is 16 bytes in size (3 4-byte floats followed by 4 bytes). 16 / Float32Array.BYTES_PER_ELEMENT is 4, so from any given Float32 element, to skip to the same Float32 element in the next point, 4 must be added to the index. Similarly, to skip from any given Uint8 element to the same in the next point, 16 must be added:

In the above example, note that for keeping the data consistent, i must be one of 0, 1, or 2; and j must be one of 0, 1, 2, or 3. Any higher values will result in data segments that are reserved for 32-bit floats or for 8-bit integers being overwritten with incorrect data.

Slicing a large array into multiple regions

Another usage similar to the above is allocating one large buffer, and then using different regions of it for different purposes:

var buffer = new ArrayBuffer(1024);

Carve out 128 floats, 128*4 = 512 bytes in size:

var floats = new Float32Array(buffer, 0, 128);

Then 128 shorts, 128*2 = 256 bytes in size, immediately
following the floats. Note that the 512 byte offset
argument is equal to floats.byteOffset + floats.byteLength.

var shorts = new Uint16Array(buffer, 512, 128);

Finally, 256 unsigned bytes. We can write the byte offset
in the form suggested above to simplify the chaining.
We also let this array extend until the end of the ArrayBuffer
by not explicitly specifying a length.