TypedArray in TypeScript

TypedArray in TypeScript

Dieser Artikel erklärt TypedArray in TypeScript.

Wir erklären TypedArray in TypeScript, einschließlich praktischer Beispiele.

YouTube Video

TypedArray in TypeScript

TypedArray ist ein Mechanismus zur effizienten Verarbeitung von Binärdaten. Es ist besonders nützlich für Low-Level-Binäroperationen wie große Bilddaten, Netzwerk-Bytestreams und numerische Arrays für WebGL.

So erstellt man einen ArrayBuffer

ArrayBuffer repräsentiert einen Bereich von Bytes mit fester Länge. Erstellen Sie zunächst einen Buffer und überprüfen Sie dessen Größe und Byte-Länge.

1// Create an ArrayBuffer of 16 bytes
2const buffer: ArrayBuffer = new ArrayBuffer(16);
3console.log("buffer.byteLength:", buffer.byteLength); // 16
  • Dieser Code erstellt einen leeren Bereich mit 16 Bytes. ArrayBuffer selbst verfügt nicht über Lese-/Schreibfunktionen; der Zugriff erfolgt daher über TypedArray oder DataView.

Typen von TypedArray

Es gibt viele Arten von TypedArray, beispielsweise die folgenden. Diese unterscheiden sich je nach Datentyp und der Größe, die sie verarbeiten können.

TypedArray Datentyp Bitgröße
Int8Array 8-Bit-Ganzzahl 8 Bits
Uint8Array Unsigned 8-Bit-Ganzzahl 8 Bits
Uint8ClampedArray Abgegrenzte unsigned 8-Bit-Ganzzahl 8 Bits
Int16Array 16-Bit-Ganzzahl 16 Bits
Uint16Array Unsigned 16-Bit-Ganzzahl 16 Bits
Int32Array 32-Bit-Ganzzahl 32 Bits
Uint32Array Unsigned 32-Bit-Ganzzahl 32 Bits
Float32Array 32-Bit-Gleitkommazahl 32 Bit
Float64Array 64-Bit-Gleitkommazahl 64 Bit

Grundlegende TypedArrays (Uint8Array, Int16Array, Float32Array usw.)

TypedArray erzeugt eine 'typisierte Ansicht' auf einem ArrayBuffer. Nachfolgend finden Sie Beispiele zum Erstellen und Verwenden typischer TypedArrays.

 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);
  • Durch die Erstellung mehrerer Ansichten auf demselben ArrayBuffer können Sie denselben Speicher in unterschiedlichen Typen oder Granularitäten lesen und schreiben. length der Ansicht ist die Anzahl der Elemente, byteLength die Anzahl der Bytes.

Schreiben und Lesen (Operationen auf Byte-Ebene)

Beim Schreiben eines Wertes in ein TypedArray werden die entsprechenden Bytes im Speicher aktualisiert. Sie können die Änderungen sehen, wenn Sie denselben Buffer mit einer anderen Ansicht einlesen.

 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
  • In diesem Beispiel schreiben wir eine Bytesequenz und lesen denselben Bereich anschließend als 32-Bit-Integer ein. Beachten Sie, dass die Ausgabe von der Endianess der Ausführungsumgebung abhängt.

Endianness (Byte-Reihenfolge) und DataView

DataView ist praktisch, wenn Sie umgebungsabhängige Endianness-Probleme kontrollieren möchten. DataView ermöglicht es Ihnen, die Endianness beim Lesen und Schreiben anzugeben.

 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 ist ein praktischer Mechanismus, um Bytes im Detail zu lesen und zu schreiben. Es unterstützt verschiedene Typen wie signierte/unsignierte Ganzzahlen und Gleitkommazahlen. Außerdem können Sie explizit die Endianness (Byte-Reihenfolge) angeben, was beim Implementieren von Binärprotokollen sehr hilfreich ist.

Unterschied zwischen subarray und slice

subarray eines TypedArray gibt eine Ansicht zurück, die den Original-Buffer teilt, während slice eine neue Kopie liefert. Performance und Seiteneffekte unterscheiden sich je nach verwendeter Methode.

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
  • Wenn Sie die geteilte Ansicht ändern, wird auch das ursprüngliche Array geändert, was unbeabsichtigte Nebenwirkungen verursachen kann. Wenn Sie das ursprüngliche Array sicher erhalten möchten, können Sie im Voraus eine Kopie mit slice() erstellen.

Kopieren von Buffern und Typumwandlung (Konvertierung zwischen TypedArrays)

Wir erklären, wie Sie Daten zwischen TypedArrays kopieren und Teile mit der set-Methode einfügen.

 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]
  • Unterscheiden sich die Elementtypen, müssen Sie Werte umwandeln, z. B. durch Runden oder Bereichsprüfung, statt sie einfach zu kopieren. set ermöglicht ein schnelles Kopieren zwischen TypedArrays mit dem gleichen Elementtyp.

Praktisches Beispiel: Ein Parser für ein Binärprotokoll (einfache Implementierung)

Hier zeigen wir ein einfaches Parser-Beispiel, das Binärdaten mit festem Format liest: Einem 1-Byte-Typ, einer 2-Byte-Datenlänge und anschließendem Payload.

 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));
  • In diesem Beispiel wird der Header mit DataView ausgelesen und der Payload-Ausschnitt mit Uint8Array erstellt. Endianness und Prüfung der Buffer-Länge sind wichtig.

Web-API und TypedArray (Beispiel: Binärdaten abrufen)

Dies ist ein typisches Beispiel für die Behandlung eines von einer Netzwerk-Anfrage erhaltenen ArrayBuffer mittels 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");
  • Sie können den mit Response.arrayBuffer() erhaltenen ArrayBuffer direkt an ein TypedArray übergeben. Es wird für Bilder, Audio oder benutzerdefinierte Binärprotokolle verwendet.

Performance-Tipps und häufige Fallstricke

Hier sind einige 'Performance-Tipps' und 'häufige Fallstricke', die beim Einsatz von TypedArrays hilfreich sind:.

  • Vermeiden Sie unnötiges Kopieren Um große Daten effizient zu verarbeiten, können Sie unnötiges Kopieren vermeiden, indem Sie Teilansichten mit subarray erstellen oder denselben ArrayBuffer für mehrere Views nutzen.

  • Seien Sie vorsichtig bei der Endianness Bei Netzwerkkommunikation oder Dateiformaten ist die Reihenfolge der Daten (Byte-Order) oft vorgegeben. Mit DataView können Sie die Endianness beim Lesen und Schreiben explizit angeben und so Fehlinterpretationen vermeiden.

  • Beachten Sie die Wertebereiche der einzelnen Typen Zum Beispiel kann Uint8 nur Werte von 0 bis 255 darstellen. Wenn Sie einen negativen Wert eingeben, kann es zu einer Abschneidung oder einem Überlauf kommen, daher sollten Sie bei Bedarf Umwandlungsregeln definieren.

  • Berücksichtigen Sie die Belastung für die Speicherbereinigung (Garbage Collection) Das häufige Neuerstellen großer ArrayBuffer erhöht die Belastung des Speichermanagements. In performancekritischen Situationen sollten Sie den Code so gestalten, dass vorhandene Puffer möglichst wiederverwendet werden.

Zusammenfassung

TypedArray ist ein Mechanismus, um Binärdaten schnell und effizient zu verarbeiten. Durch die Kombination von ArrayBuffer, der einen Bereich mit fester Byte-Länge reserviert, und TypedArray bzw. DataView, die Inhalte in bestimmten Typen lesen und schreiben, können Sie flexible Binäroperationen durchführen. Je nach Anwendungsfall können Sie zwischen der Verwendung von subarray/slice oder DataView wählen und Ihre Implementierung unter Berücksichtigung von Endianess und Kopien gestalten.

Sie können den obigen Artikel mit Visual Studio Code auf unserem YouTube-Kanal verfolgen. Bitte schauen Sie sich auch den YouTube-Kanal an.

YouTube Video