ArrayBuffer no TypeScript
Este artigo explica o ArrayBuffer no TypeScript.
Vamos explicar o ArrayBuffer no TypeScript passo a passo, cobrindo desde os conceitos básicos até técnicas práticas.
YouTube Video
ArrayBuffer no TypeScript
ArrayBuffer é um objeto embutido que representa uma “área de memória bruta” para dados binários. Ele representa um buffer bruto de comprimento fixo, sobre o qual TypedArray ou DataView são aplicados para leitura e escrita.
Conceitos Básicos e Características do ArrayBuffer
ArrayBuffer é uma sequência de bytes de comprimento fixo. Você especifica o tamanho em bytes ao criá-lo, e seu comprimento não pode ser alterado depois.
1// Create a new ArrayBuffer of 16 bytes.
2const buf = new ArrayBuffer(16);
3console.log(buf.byteLength); // 16
- Este código cria um buffer vazio de 16 bytes. Você pode verificar o tamanho usando
byteLength.
TypedArray e DataView — Visões para manipulação de buffers
ArrayBuffer não tem a capacidade de ler ou escrever dados. Portanto, as operações reais são realizadas por meio de TypedArray ou DataView, especificando tipos como inteiros ou números de ponto flutuante e a endianness ao acessar os dados.
1// Create a buffer and a Uint8Array view over it. Then write bytes and read them.
2const buffer = new ArrayBuffer(8);
3const u8 = new Uint8Array(buffer);
4
5u8[0] = 0x41; // 'A'
6u8[1] = 0x42; // 'B'
7console.log(u8); // Uint8Array(8) [ 65, 66, 0, 0, 0, 0, 0, 0 ]
Uint8Arrayé uma visão de array byte a byte, e pode ser acessado como um array normal. Se você colocar várias visualizações sobre o mesmoArrayBuffer, elas compartilham a mesma memória para leitura e escrita.
DataView: Leitura e Escrita em Limites Arbitrários e Endianness
DataView é útil para leitura e escrita detalhada byte a byte e permite especificar little ou big endian.
1// Using DataView to write/read multi-byte values with endianness control.
2const buf2 = new ArrayBuffer(8);
3const view = new DataView(buf2);
4
5// Write a 32-bit integer (little-endian)
6view.setInt32(0, 0x12345678, true);
7
8// Read it back as little-endian and big-endian
9const little = view.getInt32(0, true);
10const big = view.getInt32(0, false);
11console.log(little.toString(16)); // "12345678"
12console.log(big.toString(16)); // "78563412"
DataViewpermite ler e escrever valores especificando offsets na memória, o que o torna adequado para implementar protocolos que exigem manipulação de ordem de bytes de rede.
Conversão entre Strings e ArrayBuffer (TextEncoder / TextDecoder)
Para converter texto em binário e vice-versa, utilize TextEncoder e TextDecoder.
1// Convert string -> ArrayBuffer and back using TextEncoder/TextDecoder.
2const encoder = new TextEncoder();
3const decoder = new TextDecoder();
4
5// Unicode escape sequences
6const str = "\u3053\u3093\u306B\u3061\u306F";
7const encoded = encoder.encode(str); // Uint8Array
8console.log(encoded); // Uint8Array([...])
9
10// If you need an ArrayBuffer specifically:
11const ab = encoded.buffer.slice(encoded.byteOffset, encoded.byteOffset + encoded.byteLength);
12console.log(ab.byteLength); // bytes length of encoded text
13
14// Decode back
15const decoded = decoder.decode(encoded);
16console.log(decoded);TextEncoder.encoderetorna umUint8Array, portanto, se precisar de umArrayBuffer, deve consultar sua propriedade.buffer. Com o métodoslice, você pode extrair os dados necessários especificando o offset e o comprimento.
Fatiando e Copiando ArrayBuffer
O método slice retorna um novo ArrayBuffer. ArrayBuffer não pode ser redimensionado; se for necessário redimensionar, crie um novo buffer e copie os dados para ele.
1// Slice an ArrayBuffer and copy to a new sized buffer.
2const original = new Uint8Array([1,2,3,4,5]).buffer;
3const part = original.slice(1, 4); // bytes 1..3
4console.log(new Uint8Array(part)); // Uint8Array [ 2, 3, 4 ]
5
6// Resize: create a new buffer and copy existing content
7const larger = new ArrayBuffer(10);
8const target = new Uint8Array(larger);
9target.set(new Uint8Array(original), 0);
10console.log(target); // first bytes filled with original data
- Como o método
sliceretorna um novoArrayBuffer, se você prioriza eficiência ou compartilhamento de referências, pode utilizar o métodosubarraydoTypedArraypara referenciar o mesmo buffer.
Diferença entre TypedArray.subarray e Cópia
O subarray do TypedArray retorna uma nova visão que se refere ao mesmo ArrayBuffer.
1// subarray shares the same underlying buffer; modifying one affects the other.
2const arr = new Uint8Array([10,20,30,40]);
3const viewSub = arr.subarray(1,3); // shares memory
4viewSub[0] = 99;
5console.log(arr); // Uint8Array [10, 99, 30, 40]
- Visões compartilhadas podem evitar o custo de cópias, mas como referenciam o mesmo buffer, é necessário ter cuidado com efeitos colaterais.
Concatenando Buffers (Combinando Múltiplos ArrayBuffers)
Como o ArrayBuffer tem comprimento fixo, para combinar vários buffers, crie um novo ArrayBuffer e copie os dados.
1// Concatenate multiple ArrayBuffers
2function concatBuffers(buffers: ArrayBuffer[]): ArrayBuffer {
3 const total = buffers.reduce((sum, b) => sum + b.byteLength, 0);
4 const result = new Uint8Array(total);
5 let offset = 0;
6 for (const b of buffers) {
7 const u8 = new Uint8Array(b);
8 result.set(u8, offset);
9 offset += u8.length;
10 }
11 return result.buffer;
12}
13
14const a = new Uint8Array([1,2]).buffer;
15const b = new Uint8Array([3,4,5]).buffer;
16const c = concatBuffers([a, b]);
17console.log(new Uint8Array(c)); // [1,2,3,4,5]
- Se você frequentemente concatena grandes quantidades de dados, pré-calcular o tamanho total e alocar um novo buffer apenas uma vez, como neste código, é mais eficiente.
Passando ArrayBuffer para um Worker (Transferível)
No postMessage do navegador, você pode transferir um ArrayBuffer como um objeto "transferível". A propriedade pode ser transferida sem cópia, o que evita o custo da duplicação.
1// Example: posting an ArrayBuffer to a Worker as a transferable object (browser)
2const worker = new Worker('worker.js');
3const bufferToSend = new Uint8Array([1,2,3,4]).buffer;
4
5// Transfer ownership to the worker (main thread no longer owns it)
6worker.postMessage(bufferToSend, [bufferToSend]);
7
8// After transfer, bufferToSend.byteLength === 0 in many browsers (detached)
9console.log(bufferToSend.byteLength); // may be 0
- Ao especificar os objetos a serem transferidos em um array como segundo argumento do
postMessage, é possível transferir a propriedade, sem cópia, de objetos transferíveis, comoArrayBuffer. - Após a transferência, o buffer torna-se "desanexado" no lado original e não pode mais ser acessado. O uso de
SharedArrayBufferpermite acesso simultâneo de múltiplas threads, mas seu uso exige requisitos de segurança e restrições de ambiente.
Tratamento no Node.js (Interconversão com Buffer)
No Node.js, você pode converter entre o tipo binário do Node.js, Buffer, e ArrayBuffer. Isso é útil ao desenvolver para navegadores e Node.js com TypeScript.
1// Convert ArrayBuffer <-> Node.js Buffer
2// In Node.js environment:
3const ab = new Uint8Array([10,20,30]).buffer;
4const nodeBuffer = Buffer.from(ab); // ArrayBuffer -> Buffer
5console.log(nodeBuffer); // <Buffer 0a 14 1e>
6
7const backToAb = nodeBuffer.buffer.slice(
8 nodeBuffer.byteOffset,
9 nodeBuffer.byteOffset + nodeBuffer.byteLength
10);
11console.log(new Uint8Array(backToAb)); // Uint8Array [10,20,30]
Buffer.from(arrayBuffer)no Node.js normalmente cria uma cópia, mas há casos em que o compartilhamento de referência é possível, então fique atento aos offsets.
Considerações de Desempenho e Melhores Práticas
Para otimizar o desempenho e manipular ArrayBuffer de forma eficiente, há vários pontos importantes a considerar. A seguir, listamos e explicamos melhores práticas práticas.
-
Reduza o Número de Cópias Ao manipular binários grandes, use
subarray(visualização compartilhada) ou objetos transferíveis para reduzir cópias. -
Aloque Grandes Buffers de Uma Só Vez Alocar pequenos buffers repetidamente aumenta a sobrecarga. Se possível, aloque um buffer grande de uma vez e utilize partes dele conforme necessário.
-
Especifique o Endianness Explicitamente Ao manipular valores multibyte, use
DataViewe especifique explicitamente o endianness. Big-endian é geralmente o padrão para protocolos de rede.
Exemplos Comuns de Uso
ArrayBuffer é amplamente utilizado tanto em navegadores quanto no Node.js.
- Análise e construção de protocolos binários (processando informações de cabeçalho com
DataView) - Processamento de mídia como dados de imagens e áudio (
fetch(...).then(res => res.arrayBuffer())) - Memória compartilhada para WebAssembly (a memória Wasm opera baseada em
ArrayBuffer) - Descarregando processamento pesado para Workers (passando
ArrayBuffercomo transferível)
O código a seguir é um exemplo de obtenção e análise de dados binários.
1// Example: fetch binary data in browser and inspect first bytes
2async function fetchAndInspect(url: string) {
3 const resp = await fetch(url);
4 const ab = await resp.arrayBuffer();
5 const u8 = new Uint8Array(ab, 0, Math.min(16, ab.byteLength));
6 console.log('first bytes:', u8);
7}- Este código busca dados binários de qualquer URL no navegador e exibe os primeiros bytes. O código carrega dados buscados via a API
fetchcomo umArrayBuffere inspeciona os primeiros 16 bytes com umUint8Array.
Resumo
ArrayBuffer é uma representação de memória bruta, possibilitando operações binárias eficientes via TypedArray e DataView. Ao projetar para evitar cópias desnecessárias e especificar explicitamente o endianness, é possível obter um processamento binário seguro e de alto desempenho.
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.