Vectores JavaScript tipados

Los vectores JavaScript tipados son objetos tipo Array y proveen un mecanismo para el acceso a datos binarios puros. Como sabes, los objetos del tipo Array crecen y decrecen dinámicamente y pueden tener cualquier valor JavaScript. Los motores JavaScript realizan optimizaciones y así estos vectores son veloces. Sin embargo, como las aplicaciones web se volvieron más y más poderosas, añadiendo funcionalidades tales como la manipulación de audio y video, acceso a datos puros usando WebSockets, etcétera, ha quedado claro que hay ocasiones en que sería beneficioso para el código JavaScript tener la posibilidad de, rápida y fácilmente, manipular datos binarios puros en vectores tipados.

Sin embargo, los vectores tipados no deben ser confundidos con vectores normales, al llamar Array.isArray() en un vector tipado retorna false. Además, no todos los metodos disponibles para vectores normales son mantenidos por los vectores tipados (p. ej. push y pop).

Buffers y vistas: arquitectura de vectores tipados

Para lograr una máxima flexibilidad y eficiencia, los vectores JavaScript tipados separan la impementación en buffers y vistas. Un buffer (implementedo por el objeto ArrayBuffer) es un objeto representando un segmento de datos; no tiene formato de referencia, y no ofrece mecanismo para acceso a su contenido. Para acceder a la memoria contenida en un buffer, necesitas usar una vista. Una vista provee un contexto — que es, un tipo de dato, despalzamiento de inicio, y numero de elementos — que vuelven los datos en un vector tipado real.

Typed arrays in an ArrayBuffer

ArrayBuffer

El ArrayBuffer es un tipo de datos que es usado para representar un buffer de datos binario de largo-fijado genérico. No puedes manipular directamente el contenido de un ArrayBuffer; en su lugar, creas una vista de vector tipado o una DataView que representa al buffer en un formato específico, y usas eso para leer y escribir los contenidos del buffer.

Vistas de vector tipado

Las vistas de vector tipado tienen nombres autodescriptivos y proveen vistas para todos los tipos numéricos usuales como Int8, Uint32, Float64, etcétera. Hay una vista de vector tipado especial, el Uint8ClampedArray. Restringe los valores entre 0 y 255. Esto es útil para procesamiento de datos de Canvas, por ejemplo.

Type Size in bytes Description Web IDL type Equivalent C type
Int8Array 1 8-bit two's complement signed integer byte int8_t
Uint8Array 1 8-bit unsigned integer octet uint8_t
Uint8ClampedArray 1 8-bit unsigned integer (clamped) octet uint8_t
Int16Array 2 16-bit two's complement signed integer short int16_t
Uint16Array 2 16-bit unsigned integer unsigned short uint16_t
Int32Array 4 32-bit two's complement signed integer long int32_t
Uint32Array 4 32-bit unsigned integer unsigned long uint32_t
Float32Array 4 32-bit IEEE floating point number unrestricted float float
Float64Array 8 64-bit IEEE floating point number unrestricted double double

DataView

La DataView es una interface de bajo nivel que provee una API getter/setter para leer y escribir datos en el buffer arbitrariamente. Esto es útil cuando hay que lidiar con diferentes tipos de datos, por ejemplo. Las vistas de vector tipado están en el orden de bytes nativo (ver Endianness) de tu plataforma. Con una DataView puedes controlar el orden de bytes. Es big-endian por defecto y puede ser cambiado a little-endian en los métodos getter/setter.

APIs de Web que usan vectores tipados

FileReader.prototype.readAsArrayBuffer()
El método FileReader.prototype.readAsArrayBuffer() comienza leyendo los contenidos del BlobFile especificado.
XMLHttpRequest.prototype.send()
El metodo send() de las instancias XMLHttpRequest ahora admite vectores tipados y objetos ArrayBuffer como argumento.
ImageData.data
Es un Uint8ClampedArray representando una matriz unidimensional conteniendo los datos en el orden RGBA, con valores enteros entre 0 y 255 inclusive.

Ejemplos

Usando vistas con buffers

Primero que todo, necesitaremos crear un buffer, aquí con un largo fijo de 16-bytes:

var buffer = new ArrayBuffer(16);

En este punto, tenemos un segmento de memoria cuyos bytes están todos preinicializados a 0. No hay mucho que podamos hacer con ello, sin embargo. Podemos confirmar que de hecho es de 16 bytes de largo, y de eso se trata:

if (buffer.byteLength === 16) {
  console.log("Sí, son 16 bytes.");
} else {
  console.log("Oh no, es el tamaño equivocado!");
} 

Antes que realmente podamos trabajar con este buffer, necesitamos crear una vista. Vamos a crear una vista que trate los datos en el buffer como un vector de enteros con signo de 32-bit:

var int32View = new Int32Array(buffer);

Ahora podemos acceder los campos en el vector como si fuera un vector normal:

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

Esto llena las 4 entradas en el vector (4 entradas a 4 bytes cada una hacen 16 bytes en total) con los valores 0, 2, 4, y 6.

Multiples vistas en los mismos datos

Las cosas comienzan a ponerse realmente interesante cuando consideras que puedes crear multiples vistas sobre los mismos datos. Por ejemplo, dado el código de arriba, podemos continuar como sigue:

var int16View = new Int16Array(buffer);

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

Aquí creamos una vista de entero de 16-bit que comparte el mismo buffer que la vista existente de 32-bit y mostramos todos los valores en el buffer como enteros de 16-bit. Ahora obtenemos la salida 0, 0, 2, 0, 4, 0, 6, 0.

Puedes ir un paso más lejos, sin embargo. Considera esto:

int16View[0] = 32;
console.log("Entrada 0 en el vector de 32-bit es ahora " + int32View[0]);

La salida de esto es "Entrada 0 en el vector de 32-bit es ahora 32". En otras palabras, los dos vectores son de hecho simplemente vistas sobre el mismo buffer de datos, tratándolo como formatos diferentes. Puedes hacer esto con cualquier tipo de vista.

Trabajando con estructuras de datos complejas

Combinando un solo buffer con múltiples vistas de diferentes tipos, comenzando a diferentes desplazamientes dentro del buffer, puedes interactuar con objetos de datos conteniendo múltiples tipos de datos. Esto te permite, por ejemplo, interactuar con estructuras de datos complejas de WebGL, archivos de datos, o estructuras C que necesitas usar mientras se usa js-ctypes.

Considera esta estructura C:

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

Puedes acceder un buffer conteniendo datos en este formato como sigue:

var buffer = new ArrayBuffer(24);

// ... leer los datos en el buffer ...

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

Entonces puedes acceder, por ejemplo, el monto adeudado con montoAdeudadoView[0].

Nota: La alineación de estructuras de datos en una estructura C es dependiente de la plataforma. Ten precauciones y consideraciones para estas diferencias de llenado.

Conversión a vectores normales

Después de procesar un vector tipado, algunas veces es conveniente convertirlo de vuelta a un vector normal para beneficiarse del prototype de Array. Esto puede hacerse usando Array.from, o usando el siguiente código donde Array.from no sea admitido.

var typedArray = new Uint8Array([1, 2, 3, 4]),
    normalArray = Array.prototype.slice.call(typedArray);
normalArray.length === 4;
normalArray.constructor === Array;

Especificaciones

Especificación Estado Comentario
Typed Array Specification Obsoleta Sustituido por ECMAScript 6.
ECMAScript 2015 (6th Edition, ECMA-262)
The definition of 'TypedArray Objects' in that specification.
Standard

Definición inicial en un estándar ECMA.

Compatibilidad de navegador

Chrome 7 incluye soporte para ArrayBuffer, Float32Array, Int16Array, y Uint8Array. Chrome 9 y Firefox 15 añaden soporte para objetos DataView. Internet Explorer 10 soporta todos los tipos excepto Uint8ClampedArray y ArrayBuffer.prototype.slice que están presentes comenzando en Internet Explorer 11.

Funcionalidad Chrome Firefox (Gecko) Internet Explorer Opera Safari
Soporte básico 7.0 4.0 (2) 10 11.6 5.1
Funcionalidad Android Chrome for Android Firefox Mobile (Gecko) IE Mobile Opera Mobile Safari Mobile
Soporte básico 4.0 (Yes) 4.0 (2) 10 11.6 4.2

Véase también

Etiquetas y colaboradores del documento

Etiquetas: 
 Colaboradores en esta página: LeoE
 Última actualización por: LeoE,