TypedArray en TypeScript
Este artículo explica TypedArray en TypeScript.
Explicaremos TypedArray en TypeScript, incluyendo ejemplos prácticos.
YouTube Video
TypedArray en TypeScript
TypedArray es un mecanismo para manejar datos binarios de manera eficiente. Es especialmente útil para operaciones binarias de bajo nivel, como datos de imágenes grandes, flujos de bytes de red y arreglos numéricos para WebGL.
Cómo crear un ArrayBuffer
ArrayBuffer representa una región de bytes de longitud fija. Primero, crea un buffer y verifica su tamaño y longitud en bytes.
1// Create an ArrayBuffer of 16 bytes
2const buffer: ArrayBuffer = new ArrayBuffer(16);
3console.log("buffer.byteLength:", buffer.byteLength); // 16
- Este código crea una región vacía de 16 bytes.
ArrayBufferen sí no tiene funciones de lectura/escritura, por lo que accedes a él medianteTypedArrayoDataView.
Tipos de TypedArray
Existen muchos tipos de TypedArray, tales como los siguientes. Estos varían según el tipo de datos y el tamaño que manejan.
| TypedArray | Tipo de datos | Tamaño en bits |
|---|---|---|
Int8Array |
Entero de 8 bits | 8 bits |
Uint8Array |
Entero sin signo de 8 bits | 8 bits |
Uint8ClampedArray |
Entero sin signo de 8 bits limitado | 8 bits |
Int16Array |
Entero de 16 bits | 16 bits |
Uint16Array |
Entero sin signo de 16 bits | 16 bits |
Int32Array |
Entero de 32 bits | 32 bits |
Uint32Array |
Entero sin signo de 32 bits | 32 bits |
Float32Array |
Número de punto flotante de 32 bits | 32 bits |
Float64Array |
Número de punto flotante de 64 bits | 64 bits |
Los TypedArray básicos (Uint8Array, Int16Array, Float32Array, etc.)
TypedArray crea una 'vista tipada' sobre un ArrayBuffer. A continuación se muestran ejemplos de cómo crear y utilizar algunos TypedArray típicos.
1// Create a buffer and different typed views over it
2const buf = new ArrayBuffer(8); // 8 bytes
3
4// Create views
5const u8 = new Uint8Array(buf); // 8 x uint8
6const i16 = new Int16Array(buf); // 4 x int16 (since each int16 is 2 bytes)
7const f32 = new Float32Array(buf); // 2 x float32 (4 bytes each)
8
9console.log("Uint8 length:", u8.length);
10console.log("Int16 length:", i16.length);
11console.log("Float32 length:", f32.length);- Al crear múltiples vistas sobre el mismo
ArrayBuffer, puedes leer y escribir la misma memoria en diferentes tipos o granularidades.lengthde la vista es el número de elementos ybyteLengthes la cantidad de bytes.
Escritura y lectura (operaciones a nivel de byte)
Cuando escribes un valor en un TypedArray, los bytes correspondientes en memoria se actualizan. Puedes ver los cambios al leer el mismo buffer con una vista diferente.
1// Demonstrate writing via one view and reading via another
2const buffer2 = new ArrayBuffer(4);
3const u8view = new Uint8Array(buffer2);
4const u32view = new Uint32Array(buffer2);
5
6u8view[0] = 0x78;
7u8view[1] = 0x56;
8u8view[2] = 0x34;
9u8view[3] = 0x12;
10
11console.log("Uint8 bytes:", Array.from(u8view)); // [120, 86, 52, 18]
12console.log("Uint32 value (platform endianness):", u32view[0]); // value depends on endianness
- En este ejemplo, escribimos una secuencia de bytes y luego leemos la misma área como un entero de 32 bits. Ten en cuenta que la salida depende de la endianidad del entorno de ejecución.
Endianidad (orden de bytes) y DataView
DataView es útil si necesitas controlar problemas de endianidad dependientes del entorno. DataView te permite especificar la endianidad al leer y escribir.
1// Use DataView to read/write with explicit endianness control
2const b = new ArrayBuffer(4);
3const dv = new DataView(b);
4
5// write little-endian 32-bit integer
6dv.setUint32(0, 0x12345678, true);
7
8// read as little-endian and big-endian
9const little = dv.getUint32(0, true);
10const big = dv.getUint32(0, false);
11
12console.log("little-endian read:", little.toString(16)); // "12345678"
13console.log("big-endian read:", big.toString(16)); // different value
DataViewes un mecanismo conveniente para leer y escribir bytes en detalle. Soporta una variedad de tipos, como enteros con o sin signo y números de punto flotante, y permite especificar explícitamente la endianidad (orden de bytes), lo cual es muy útil al implementar protocolos binarios.
Diferencia entre subarray y slice
subarray de un TypedArray devuelve una vista que comparte el buffer original, mientras que slice devuelve una nueva copia. El rendimiento y los efectos secundarios varían según cuál utilices.
1const base = new Uint8Array([1, 2, 3, 4, 5]);
2
3const shared = base.subarray(1, 4); // shares underlying buffer
4const copied = base.slice(1, 4); // copies data
5
6shared[0] = 99;
7console.log("base after shared modification:", base); // shows change
8console.log("copied remains:", copied); // unaffected
- Si modifica la vista compartida, el arreglo original también cambiará, lo que puede causar efectos secundarios no deseados. Si desea preservar de manera segura el arreglo original, puede crear una copia por adelantado usando
slice().
Copia de buffers y conversión de tipos (conversión entre TypedArrays)
Explicaremos cómo copiar datos entre TypedArrays y cómo pegar partes usando el método set.
1// Copy and convert between typed arrays
2const src = new Float32Array([1.5, 2.5, 3.5]);
3const dst = new Uint16Array(src.length);
4
5// Convert by mapping (explicit conversion)
6for (let i = 0; i < src.length; i++) {
7 dst[i] = Math.round(src[i]); // simple conversion rule
8}
9console.log("converted dst:", Array.from(dst)); // [2, 2, 4]
10
11// Using set to copy bytes (requires compatible underlying buffer or same element type)
12const src2 = new Uint8Array([10, 20, 30]);
13const dst2 = new Uint8Array(6);
14dst2.set(src2, 2); // copy src2 into dst2 starting at index 2
15console.log("dst2 after set:", Array.from(dst2)); // [0,0,10,20,30,0]
- Si los tipos de elementos difieren, debes convertir los valores, por ejemplo, redondeando o verificando rangos, en lugar de simplemente copiarlos.
setpermite copiar rápidamente entreTypedArraydel mismo tipo de elemento.
Ejemplo práctico: Un analizador de protocolo binario (implementación simple)
Aquí presentamos un ejemplo simple de analizador que lee un dato binario de formato fijo, que consiste en un tipo de 1 byte, una longitud de datos de 2 bytes y un payload a continuación.
1// Simple binary message parser:
2// format: [type: uint8][length: uint16 BE][payload: length bytes]
3function parseMessages(buffer: ArrayBuffer) {
4 const dv = new DataView(buffer);
5 let offset = 0;
6 const messages: { type: number; payload: Uint8Array }[] = [];
7
8 while (offset + 3 <= dv.byteLength) {
9 const type = dv.getUint8(offset);
10 const length = dv.getUint16(offset + 1, false); // big-endian
11 offset += 3;
12 if (offset + length > dv.byteLength) break; // truncated
13 const payload = new Uint8Array(buffer, offset, length);
14 messages.push({ type, payload });
15 offset += length;
16 }
17
18 return messages;
19}
20
21// Example usage
22const buf = new ArrayBuffer(1 + 2 + 3 + 1 + 2 + 2); // two messages
23const dv = new DataView(buf);
24let off = 0;
25// first message: type=1, length=3, payload=[1,2,3]
26dv.setUint8(off, 1); dv.setUint16(off + 1, 3, false); off += 3;
27new Uint8Array(buf, off, 3).set([1, 2, 3]); off += 3;
28// second message: type=2, length=2, payload=[9,8]
29dv.setUint8(off, 2); dv.setUint16(off + 1, 2, false); off += 3;
30new Uint8Array(buf, off, 2).set([9, 8]);
31
32console.log(parseMessages(buf));- En este ejemplo, el encabezado se lee usando
DataView, y la porción de datos (payload) se crea conUint8Array. Es importante comprobar la endianidad y la longitud del buffer.
Web API y TypedArray (ejemplo: obtener datos binarios)
Este es un ejemplo típico de cómo manejar un ArrayBuffer obtenido de una solicitud de red usando un TypedArray.
1// Example of fetching binary and mapping to typed array
2async function fetchBinary(url: string) {
3 const res = await fetch(url);
4 const ab = await res.arrayBuffer();
5 const view = new Uint8Array(ab);
6 // process view...
7 console.log("received bytes:", view.length);
8 return view;
9}
10
11// Usage (call in async context)
12// fetchBinary("/path/to/file.bin");
- Puedes pasar directamente el
ArrayBufferobtenido conResponse.arrayBuffer()a unTypedArray. Se utiliza para imágenes, audio o protocolos binarios personalizados.
Consejos de rendimiento y errores comunes
Aquí tienes algunos 'consejos de rendimiento' y 'errores comunes' que es útil conocer al usar TypedArrays:.
-
Evita copias innecesarias Para procesar grandes cantidades de datos de manera eficiente, puedes reducir copias innecesarias creando vistas parciales con
subarrayo compartiendo el mismoArrayBufferentre varias vistas. -
Ten cuidado con la endianidad Para la comunicación en red o formatos de archivo, el orden de los datos (orden de bytes) suele estar especificado. Al usar
DataViewpuedes especificar la endianidad explícitamente al leer y escribir, evitando interpretaciones incorrectas. -
Ten en cuenta los rangos de valores de cada tipo Por ejemplo,
Uint8solo puede representar valores entre 0 y 255. Si ingresa un valor negativo, puede ocurrir truncamiento o desbordamiento de valores, por lo que debería definir reglas de conversión según sea necesario. -
Ten en cuenta la presión sobre el recolector de basura Recrear frecuentemente grandes
ArrayBuffers aumenta la carga de la gestión de memoria. En situaciones críticas para el rendimiento, puede considerar diseñar el código para reutilizar los buffers existentes tanto como sea posible.
Resumen
TypedArray es un mecanismo para manejar datos binarios de forma rápida y eficiente. Al combinar ArrayBuffer, que reserva un área de bytes de longitud fija, con TypedArray o DataView, que leen y escriben los contenidos usando tipos específicos, puedes realizar operaciones binarias flexibles. Dependiendo de su caso de uso, puede elegir entre usar subarray/slice o DataView, y diseñar su implementación prestando atención a la endianness y las copias.
Puedes seguir el artículo anterior utilizando Visual Studio Code en nuestro canal de YouTube. Por favor, también revisa nuestro canal de YouTube.