ArrayBuffer in TypeScript
Dieser Artikel erklärt ArrayBuffer in TypeScript.
Wir erklären ArrayBuffer in TypeScript Schritt für Schritt, von den Grundlagen bis zu praxisnahen Techniken.
YouTube Video
ArrayBuffer in TypeScript
ArrayBuffer ist ein integriertes Objekt, das einen „rohen Speicherbereich“ für Binärdaten darstellt. Es handelt sich um einen Rohpuffer fester Länge, auf dem TypedArray oder DataView zum Lesen und Schreiben aufsetzen.
Grundlagen und Eigenschaften von ArrayBuffer
ArrayBuffer ist eine Byte-Sequenz mit fester Länge. Die Größe wird beim Erstellen in Bytes angegeben und kann danach nicht mehr geändert werden.
1// Create a new ArrayBuffer of 16 bytes.
2const buf = new ArrayBuffer(16);
3console.log(buf.byteLength); // 16
- Dieser Code erstellt einen leeren Puffer von 16 Bytes. Die Größe kann mit
byteLengthüberprüft werden.
TypedArray und DataView — Sichten zum Manipulieren von Puffern
ArrayBuffer besitzt keine Funktion zum Lesen oder Schreiben von Daten. Deshalb werden tatsächliche Operationen über TypedArray oder DataView durchgeführt, wobei beim Zugriff auf Daten Typen wie Ganzzahlen oder Fließkommazahlen sowie die Byte-Reihenfolge (Endianness) angegeben werden.
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 ]
Uint8Arrayist eine byteorientierte Array-Ansicht und kann wie ein normales Array verwendet werden. Wenn Sie mehrere Views auf denselbenArrayBufferlegen, teilen sie sich denselben Speicher zum Lesen und Schreiben.
DataView: Lesen und Schreiben an beliebigen Grenzen und Endianness
DataView ist nützlich für feinkörniges Lesen und Schreiben nach Byte und erlaubt das Festlegen von Little- oder 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"
DataViewermöglicht das Lesen und Schreiben von Werten durch die Angabe von Speicher-Offsets und eignet sich daher für die Implementierung von Protokollen, die die Handhabung der Netzwerk-Byte-Reihenfolge erfordern.
Umwandlung zwischen Strings und ArrayBuffer (TextEncoder / TextDecoder)
Um Text in Binärdaten und umgekehrt zu konvertieren, verwenden Sie TextEncoder und 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.encodegibt einUint8Arrayzurück. Wenn Sie einenArrayBufferbenötigen, sollten Sie auf dessen.buffer-Eigenschaft zugreifen. Mit derslice-Methode können Sie die benötigten Daten extrahieren, indem Sie den Offset und die Länge angeben.
Schneiden und Kopieren von ArrayBuffer
Die slice-Methode gibt einen neuen ArrayBuffer zurück. ArrayBuffer kann nicht in der Größe geändert werden; falls eine Größenänderung erforderlich ist, erstellen Sie einen neuen Buffer und kopieren Sie die Daten hinein.
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
- Da die
slice-Methode einen neuenArrayBufferzurückgibt, können Sie, wenn Sie Effizienz oder das Teilen von Referenzen bevorzugen, diesubarray-Methode vonTypedArrayverwenden, um auf denselben Buffer zuzugreifen.
Unterschied zwischen TypedArray.subarray und dem Kopieren
TypedArray.subarray gibt eine neue Sicht auf denselben ArrayBuffer zurück.
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]
- Geteilte Sichten können die Kosten für das Kopieren vermeiden, aber da sie sich auf denselben Buffer beziehen, müssen Sie auf Nebenwirkungen achten.
Puffer verketten (Zusammenführen mehrerer ArrayBuffer)
Da ArrayBuffer eine feste Länge hat, erstellen Sie zum Zusammenführen mehrerer Puffer einen neuen ArrayBuffer und kopieren die Daten.
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]
- Wenn Sie häufig große Datenmengen zusammenfügen, ist es effizienter, wie in diesem Code zuvor die Gesamtgröße zu berechnen und nur einmal einen neuen Buffer zuzuweisen.
Übergeben eines ArrayBuffer an einen Worker (Transferable)
Im Browser können Sie mit postMessage ein ArrayBuffer als "transferierbares" Objekt übertragen. Das Eigentum kann ohne Kopiervorgang übertragen werden, wodurch die Kosten für das Kopieren vermieden werden.
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
- Indem Sie die zu übertragenden Objekte in einem Array als zweites Argument von
postMessageangeben, können Sie das Eigentum an transferierbaren Objekten wieArrayBufferohne Kopieren übertragen. - Nach der Übertragung wird der Buffer auf der ursprünglichen Seite "abgetrennt" und kann nicht mehr darauf zugegriffen werden. Die Verwendung von
SharedArrayBufferermöglicht den gleichzeitigen Zugriff von mehreren Threads, aber die Nutzung ist an Sicherheitsauflagen und Umgebungsbeschränkungen gebunden.
Handhabung in Node.js (Umwandlung mit Buffer)
In Node.js kann zwischen dem Node.js-Binärtyp Buffer und ArrayBuffer konvertiert werden. Das ist nützlich, wenn man sowohl Browser als auch Node.js mit TypeScript unterstützen möchte.
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)in Node.js erstellt in der Regel eine Kopie, jedoch ist in manchen Fällen gemeinsame Referenzierung möglich – achten Sie daher auf Offsets.
Leistungsaspekte und Best Practices
Zur Optimierung der Leistung und um ArrayBuffer effizient zu nutzen, sollten mehrere wichtige Punkte beachtet werden. Im Folgenden werden praktische Best Practices aufgelistet und erklärt.
-
Reduzieren Sie die Anzahl der Kopien Verwenden Sie bei der Verarbeitung großer Binärdaten
subarray(geteilte Sicht) oder Transferables, um Kopien zu minimieren. -
Große Puffer auf einmal anlegen Das wiederholte Zuweisen kleiner Puffer erhöht den Overhead. Wenn möglich, weisen Sie einmalig einen großen Puffer zu und verwenden Sie bei Bedarf Teile davon.
-
Endianness explizit angeben Bei Mehrbytewerten verwenden Sie
DataViewund geben die Endianness explizit an. Big-Endian ist oft Standard bei Netzwerkprotokollen.
Typische Anwendungsbeispiele
ArrayBuffer wird sowohl in Browsern als auch in Node.js häufig verwendet.
- Parsen und Erstellen von Binärprotokollen (Verarbeitung der Header-Informationen mit
DataView) - Medienverarbeitung wie Bild- und Audiodaten (
fetch(...).then(res => res.arrayBuffer())) - Gemeinsamer Speicher für WebAssembly (Wasm-Speicher basiert auf
ArrayBuffer) - Auslagerung rechenintensiver Verarbeitung an Worker (Übermittlung des
ArrayBufferals transferierbares Objekt)
Der folgende Code zeigt ein Beispiel für das Abrufen und Analysieren von Binärdaten.
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}- Dieser Code ruft Binärdaten von einer beliebigen URL im Browser ab und zeigt die ersten Bytes an. Der Code lädt Daten über die
fetch-API alsArrayBufferund untersucht die ersten 16 Bytes mithilfe einesUint8Array.
Zusammenfassung
ArrayBuffer ist eine rohe Speicherrepräsentation und ermöglicht effiziente Binäroperationen mittels TypedArray und DataView. Durch die Vermeidung unnötiger Kopien und das explizite Festlegen der Endianness können Sie eine sichere und leistungsstarke Binärverarbeitung erreichen.
Sie können den obigen Artikel mit Visual Studio Code auf unserem YouTube-Kanal verfolgen. Bitte schauen Sie sich auch den YouTube-Kanal an.