ArrayBuffer i TypeScript

ArrayBuffer i TypeScript

Denne artikkelen forklarer ArrayBuffer i TypeScript.

Vi forklarer ArrayBuffer i TypeScript trinn for trinn, fra grunnleggende til praktiske teknikker.

YouTube Video

ArrayBuffer i TypeScript

ArrayBuffer er et innebygd objekt som representerer et «rått minneområde» for binærdata. Den representerer en råbuffer med fast lengde, hvor TypedArray eller DataView legges over for lesing og skriving.

Grunnleggende konsepter og egenskaper for ArrayBuffer

ArrayBuffer er en byte-sekvens med fast lengde. Du angir størrelsen i byte når du oppretter den, og lengden kan ikke endres senere.

1// Create a new ArrayBuffer of 16 bytes.
2const buf = new ArrayBuffer(16);
3console.log(buf.byteLength); // 16
  • Denne koden oppretter en tom buffer på 16 byte. Du kan sjekke størrelsen ved å bruke byteLength.

TypedArray og DataView — visninger for å manipulere buffere

ArrayBuffer har ikke mulighet til å lese eller skrive data. Derfor utføres faktiske operasjoner gjennom TypedArray eller DataView, hvor man spesifiserer typer som heltall eller flyttall og endianness ved tilgang til data.

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 er en byte-vis arrayvisning, og kan nås som et vanlig array. Hvis du legger flere visninger på samme ArrayBuffer, deler de det samme minnet for lesing og skriving.

DataView: Lesing og skriving ved vilkårlige grenser og endianness

DataView er nyttig for detaljert lesing og skriving per byte, og du kan spesifisere liten eller stor 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"
  • DataView lar deg lese og skrive verdier ved å spesifisere offset i minnet, noe som gjør det egnet for implementering av protokoller som krever håndtering av nettverks-byterekkefølge.

Konvertering mellom strenger og ArrayBuffer (TextEncoder / TextDecoder)

For å konvertere tekst til binær og omvendt, bruk TextEncoder og 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.encode returnerer en Uint8Array, så dersom du trenger en ArrayBuffer, bør du referere til dens .buffer-egenskap. Med slice-metoden kan du trekke ut nødvendig data ved å spesifisere offset og lengde.

Slicing og kopiering av ArrayBuffer

slice-metoden returnerer en ny ArrayBuffer. ArrayBuffer kan ikke endres i størrelse; hvis det er nødvendig å endre størrelse, lag en ny buffer og kopier dataene inn i den.

 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
  • Siden slice-metoden returnerer en ny ArrayBuffer, kan du, hvis du prioriterer effektivitet eller deling av referanser, bruke subarray-metoden til TypedArray for å referere til samme buffer.

Forskjellen mellom TypedArray.subarray og kopiering

TypedArray sitt subarray returnerer en ny visning som refererer til den samme 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]
  • Delte visninger kan unngå kostnaden ved kopiering, men siden de refererer til samme buffer, må du være forsiktig med sideeffekter.

Kjeding av buffere (kombinere flere ArrayBuffer-objekter)

Siden ArrayBuffer har en fast lengde, må du lage en ny ArrayBuffer og kopiere dataene for å kombinere flere buffere.

 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]
  • Hvis du ofte setter sammen store mengder data, er det mer effektivt å forhåndsberegne total størrelse og allokere en ny buffer kun én gang, slik som i denne koden.

Sende ArrayBuffer til en Worker (Transferable)

I nettleserens postMessage kan du overføre en ArrayBuffer som et "overførbart" objekt. Eierskapet kan overføres uten kopiering, noe som unngår kostnaden ved kopiering.

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
  • Ved å spesifisere objektene som skal overføres i et array som det andre argumentet til postMessage, kan du overføre eierskap, uten kopiering, av overførbare objekter som ArrayBuffer.
  • Etter overføringen blir bufferen "frakoblet" på den opprinnelige siden og kan ikke nås. Bruk av SharedArrayBuffer gir mulighet for samtidig tilgang fra flere tråder, men bruken innebærer sikkerhetskrav og miljøbegrensninger.

Håndtering i Node.js (konvertering med Buffer)

I Node.js kan du konvertere mellom Node.js sin binærtype Buffer og ArrayBuffer. Dette er nyttig når du vil støtte både nettlesere og Node.js med 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) i Node.js lager vanligvis en kopi, men det finnes tilfeller hvor referansedeling er mulig, så vær oppmerksom på offset.

Ytelseshensyn og beste praksis

For å optimalisere ytelse og bruke ArrayBuffer effektivt, er det flere viktige punkter å være klar over. Nedenfor lister og forklarer vi praktiske beste praksiser.

  • Reduser antall kopier Når du håndterer store binærfiler, bruk subarray (delt visning) eller overførbare objekter for å redusere antall kopier.

  • Alloker store buffere på en gang Å gjentatte ganger allokere små buffere øker overhead. Hvis mulig, allokér en stor buffer på én gang og bruk deler av den ved behov.

  • Spesifiser endianness eksplisitt Når du håndterer flerbytes-verdier, bruk DataView og angi endianness eksplisitt. Big-endian er ofte standard for nettverksprotokoller.

Vanlige brukstilfeller

ArrayBuffer brukes mye både i nettlesere og i Node.js.

  1. Parsing og bygging av binære protokoller (behandling av header-informasjon med DataView)
  2. Mediebehandling som bilde- og lyddata (fetch(...).then(res => res.arrayBuffer()))
  3. Delt minne for WebAssembly (Wasm-minne opererer basert på ArrayBuffer)
  4. Avlasting av tung prosessering til Workers (overføre ArrayBuffer som transferable)

Koden under er et eksempel på innhenting og analyse av binærdata.

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}
  • Denne koden henter binærdata fra en hvilken som helst URL i nettleseren og viser de første byte. Koden laster inn data hentet via fetch-API-et som en ArrayBuffer og inspiserer de første 16 byte med en Uint8Array.

Sammendrag

ArrayBuffer er en representasjon av råminne, som gir effektive binæroperasjoner via TypedArray og DataView. Ved å unngå unødvendig kopiering og eksplisitt angi endianness, kan du oppnå sikker og høytytende binærprosessering.

Du kan følge med på artikkelen ovenfor ved å bruke Visual Studio Code på vår YouTube-kanal. Vennligst sjekk ut YouTube-kanalen.

YouTube Video