TypedArray em TypeScript
Este artigo explica o TypedArray no TypeScript.
Vamos explicar o TypedArray em TypeScript, incluindo exemplos práticos.
YouTube Video
TypedArray em TypeScript
TypedArray é um mecanismo para manipulação eficiente de dados binários. É especialmente útil para operações binárias de baixo nível, como dados de imagem em grande escala, fluxos de bytes em redes e matrizes numéricas para WebGL.
Como criar um ArrayBuffer
ArrayBuffer representa uma região de bytes com comprimento fixo. Primeiro, crie um buffer e verifique seu tamanho e comprimento em bytes.
1// Create an ArrayBuffer of 16 bytes
2const buffer: ArrayBuffer = new ArrayBuffer(16);
3console.log("buffer.byteLength:", buffer.byteLength); // 16
- Este código cria uma região vazia de 16 bytes.
ArrayBufferem si não possui funções de leitura/escrita, portanto, você deve acessá-lo através deTypedArrayouDataView.
Tipos de TypedArray
Existem vários tipos de TypedArray, como os seguintes:. Eles variam dependendo do tipo de dado e tamanho que manipulam.
| TypedArray | Tipo de Dado | Tamanho em Bits |
|---|---|---|
Int8Array |
Inteiro de 8 bits | 8 bits |
Uint8Array |
Inteiro sem sinal de 8 bits | 8 bits |
Uint8ClampedArray |
Inteiro sem sinal de 8 bits com limite | 8 bits |
Int16Array |
Inteiro de 16 bits | 16 bits |
Uint16Array |
Inteiro sem sinal de 16 bits | 16 bits |
Int32Array |
Inteiro de 32 bits | 32 bits |
Uint32Array |
Inteiro sem sinal de 32 bits | 32 bits |
Float32Array |
Número de ponto flutuante de 32 bits | 32 bits |
Float64Array |
Número de ponto flutuante de 64 bits | 64 bits |
TypedArrays básicos (Uint8Array, Int16Array, Float32Array, etc.)
TypedArray cria uma 'visão tipada' sobre um ArrayBuffer. Abaixo estão exemplos de criação e utilização de alguns TypedArrays 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);- Criando múltiplas visualizações sobre o mesmo
ArrayBuffer, é possível ler e escrever a mesma memória em diferentes tipos ou granularidades. Olengthda visualização é o número de elementos e obyteLengthé o número de bytes.
Escrita e leitura (operações em nível de byte)
Quando você escreve um valor em um TypedArray, os bytes correspondentes na memória são atualizados. Você pode ver as alterações ao ler o mesmo buffer com uma visualização 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
- Neste exemplo, escrevemos uma sequência de bytes e, em seguida, lemos a mesma área como um inteiro de 32 bits. Observe que a saída depende da ordem dos bytes (endianness) do ambiente de execução.
Endianness (ordem dos bytes) e DataView
DataView é útil caso você queira controlar questões de endianness dependentes do ambiente. DataView permite especificar a ordem dos bytes ao ler e escrever.
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
DataViewé um mecanismo conveniente para leitura e escrita detalhada de bytes. Ele suporta vários tipos, como inteiros com ou sem sinal e números de ponto flutuante, e permite especificar explicitamente a ordem dos bytes (endianness), o que é muito útil na implementação de protocolos binários.
Diferença entre subarray e slice
O subarray de um TypedArray retorna uma visualização que compartilha o buffer original, enquanto slice retorna uma nova cópia. O desempenho e os efeitos colaterais variam de acordo com qual você utiliza.
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
- Se você modificar a visualização compartilhada, o array original também será alterado, o que pode causar efeitos colaterais indesejados. Se você quiser preservar com segurança o array original, pode criar uma cópia antecipadamente usando
slice().
Cópia de buffers e conversão de tipos (conversão entre TypedArrays)
Explicaremos como copiar dados entre TypedArrays e como colar partes usando o 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]
- Se os tipos de elementos forem diferentes, é necessário converter os valores, como arredondar ou verificar limites, em vez de apenas copiar.
setpermite copiagem rápida entreTypedArraysdo mesmo tipo de elemento.
Exemplo prático: Um interpretador de protocolo binário (implementação simples)
Aqui, apresentamos um exemplo simples de interpretador que lê um dado binário de formato fixo composto por um tipo de 1 byte, um comprimento de 2 bytes e um payload a seguir.
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));- Neste exemplo, o cabeçalho é lido usando
DataView, e o trecho do payload é criado comUint8Array. Verificar a endianness e o comprimento do buffer é importante.
Web API e TypedArray (Exemplo: obtendo dados binários)
Este é um exemplo típico de como manipular um ArrayBuffer obtido de uma requisição de rede usando 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");
- Você pode passar diretamente o
ArrayBufferobtido comResponse.arrayBuffer()para umTypedArray. É usado para imagens, áudio ou protocolos binários personalizados.
Dicas de desempenho e armadilhas comuns
Aqui estão algumas 'dicas de desempenho' e 'armadilhas comuns' úteis ao usar TypedArrays:.
-
Evite cópias desnecessárias Para processar grandes volumes de dados de forma eficiente, reduza cópias desnecessárias criando visualizações parciais com
subarrayou compartilhando o mesmoArrayBufferentre várias visualizações. -
Cuidado com a endianness Para comunicação em rede ou formatos de arquivo, a ordem dos dados (ordem dos bytes) geralmente é especificada. Usando
DataView, você pode especificar explicitamente a endianness ao ler e escrever, evitando interpretações incorretas. -
Atente para os intervalos de valores de cada tipo Por exemplo,
Uint8só pode representar valores de 0 a 255. Se você inserir um valor negativo, pode ocorrer truncamento ou rotação de valores, portanto, é recomendável definir regras de conversão conforme necessário. -
Considere a pressão sobre o garbage collection A recriação frequente de grandes
ArrayBuffersaumenta o ônus do gerenciamento de memória. Em situações críticas de desempenho, considere projetar o código para reutilizar buffers existentes sempre que possível.
Resumo
TypedArray é um mecanismo para manipulação rápida e eficiente de dados binários. Combinando ArrayBuffer, que reserva uma área de bytes de tamanho fixo, com TypedArray ou DataView, que lêem e escrevem o conteúdo usando tipos específicos, é possível realizar operações binárias flexíveis. Dependendo do seu caso de uso, você pode optar por usar subarray/slice ou DataView, e projetar sua implementação com atenção à endianness e à cópia de dados.
Você pode acompanhar o artigo acima usando o Visual Studio Code em nosso canal do YouTube. Por favor, confira também o canal do YouTube.