TypedArray em TypeScript

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. ArrayBuffer em si não possui funções de leitura/escrita, portanto, você deve acessá-lo através de TypedArray ou DataView.

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. O length da visualização é o número de elementos e o byteLength é 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. set permite copiagem rápida entre TypedArrays do 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 com Uint8Array. 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 ArrayBuffer obtido com Response.arrayBuffer() para um TypedArray. É 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 subarray ou compartilhando o mesmo ArrayBuffer entre 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, Uint8 só 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 ArrayBuffers aumenta 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.

YouTube Video