TypedArray w TypeScript

TypedArray w TypeScript

Ten artykuł wyjaśnia TypedArray w TypeScript.

Wyjaśnimy TypedArray w TypeScript, w tym praktyczne przykłady.

YouTube Video

TypedArray w TypeScript

TypedArray to mechanizm do wydajnego przetwarzania danych binarnych. Jest szczególnie przydatny do niskopoziomowych operacji binarnych, takich jak duże dane obrazów, strumienie bajtów sieciowych oraz tablice liczbowe dla WebGL.

Jak utworzyć ArrayBuffer

ArrayBuffer reprezentuje obszar bajtów o stałej długości. Najpierw utwórz bufor i sprawdź jego rozmiar oraz długość w bajtach.

1// Create an ArrayBuffer of 16 bytes
2const buffer: ArrayBuffer = new ArrayBuffer(16);
3console.log("buffer.byteLength:", buffer.byteLength); // 16
  • Ten kod tworzy pusty obszar o długości 16 bajtów. Sam ArrayBuffer nie posiada funkcji odczytu/zapisu, więc dostęp do niego uzyskujesz przez TypedArray albo DataView.

Typy TypedArray

Istnieje wiele typów TypedArray, takich jak poniższe. Różnią się w zależności od typu danych i ich rozmiaru, które obsługują.

TypedArray Typ danych Rozmiar bitowy
Int8Array Liczba całkowita 8-bitowa 8 bitów
Uint8Array Nieznakowana liczba całkowita 8-bitowa 8 bitów
Uint8ClampedArray Ściśnięta nieznakowana liczba całkowita 8-bitowa 8 bitów
Int16Array Liczba całkowita 16-bitowa 16 bitów
Uint16Array Nieznakowana liczba całkowita 16-bitowa 16 bitów
Int32Array Liczba całkowita 32-bitowa 32 bitów
Uint32Array Nieznakowana liczba całkowita 32-bitowa 32 bitów
Float32Array 32-bitowa liczba zmiennoprzecinkowa 32 bity
Float64Array 64-bitowa liczba zmiennoprzecinkowa 64 bity

Podstawowe TypedArray (Uint8Array, Int16Array, Float32Array itd.)

TypedArray tworzy 'widok typowany' na istniejącym ArrayBuffer. Poniżej znajdują się przykłady tworzenia i używania kilku typowych TypedArray.

 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);
  • Tworząc wiele widoków na tym samym ArrayBuffer, możesz czytać i zapisywać tę samą pamięć w różnych typach lub z różną szczegółowością. length widoku to liczba elementów, a byteLength to liczba bajtów.

Zapis i odczyt (operacje na poziomie bajtów)

Gdy zapisujesz wartość do TypedArray, odpowiednie bajty w pamięci są aktualizowane. Zmiany można zobaczyć, gdy ten sam bufor przeczytasz za pomocą innego widoku.

 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
  • W tym przykładzie zapisujemy sekwencję bajtów i odczytujemy ten sam obszar jako 32-bitową liczbę całkowitą. Zwróć uwagę, że wynik zależy od porządku bajtów (endianness) środowiska uruchomieniowego.

Porządek bajtów (endianness) i DataView

DataView jest przydatny, gdy chcesz kontrolować kwestie porządku bajtów zależnego od środowiska. DataView pozwala określać porządek bajtów przy odczycie i zapisie.

 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 to wygodny mechanizm do szczegółowego odczytu i zapisu bajtów. Obsługuje różne typy, takie jak liczby całkowite ze znakiem lub bez znaku oraz liczby zmiennoprzecinkowe, pozwalając jawnie określić porządek bajtów (endianness), co jest bardzo przydatne przy implementowaniu protokołów binarnych.

Różnica między subarray i slice

subarray w TypedArray zwraca widok dzielący oryginalny bufor, natomiast slice zwraca nową kopię. Wydajność i skutki uboczne różnią się w zależności od użytej metody.

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
  • Jeśli zmodyfikujesz współdzielony widok, oryginalna tablica również się zmieni, co może powodować niezamierzone skutki uboczne. Jeśli chcesz bezpiecznie zachować oryginalną tablicę, możesz wcześniej utworzyć jej kopię za pomocą slice().

Kopiowanie buforów i konwersja typów (konwersja między TypedArray)

Wyjaśnimy, jak kopiować dane między TypedArray i jak wklejać fragmenty za pomocą metody 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]
  • Jeśli typy elementów się różnią, trzeba przekonwertować wartości, np. zaokrąglając je lub sprawdzając zakres, zamiast po prostu kopiować. set umożliwia szybkie kopiowanie między TypedArray tego samego typu elementów.

Praktyczny przykład: parser protokołu binarnego (prosta implementacja)

Tutaj przedstawiamy prosty przykład parsera, który odczytuje dane binarne o ustalonym formacie: 1 bajt typu, 2 bajty długości danych i następujący po nich 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));
  • W tym przykładzie nagłówek jest odczytywany za pomocą DataView, a fragment danych (payload) tworzony przy użyciu Uint8Array. Porządek bajtów (endianness) i kontrola długości bufora są istotne.

Web API i TypedArray (Przykład: pobieranie danych binarnych)

To typowy przykład obsługi ArrayBuffer uzyskanego z żądania sieciowego za pomocą 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");
  • ArrayBuffer uzyskany za pomocą Response.arrayBuffer() możesz bezpośrednio przekazać do TypedArray. Jest to używane do obrazów, audio lub niestandardowych protokołów binarnych.

Wskazówki dotyczące wydajności i częste pułapki

Oto kilka 'wskazówek dotyczących wydajności' i 'częstych pułapek', które warto znać przy używaniu TypedArray:.

  • Unikaj niepotrzebnego kopiowania Aby wydajnie przetwarzać duże dane, możesz ograniczyć niepotrzebne kopiowanie, tworząc częściowe widoki za pomocą subarray lub współdzieląc ten sam ArrayBuffer pomiędzy różne widoki.

  • Uważaj na porządek bajtów (endianness) W komunikacji sieciowej czy formatach plików często określony jest porządek danych (porządek bajtów). Użycie DataView pozwala jawnie określić porządek bajtów przy odczycie i zapisie, co zapobiega błędnej interpretacji danych.

  • Zwracaj uwagę na zakresy wartości dla każdego typu Na przykład, Uint8 może reprezentować tylko wartości od 0 do 255. Jeśli wprowadzisz wartość ujemną, może dojść do obcięcia lub nadpisania wartości, dlatego powinieneś zdefiniować odpowiednie zasady konwersji.

  • Zwróć uwagę na wpływ na garbage collection (GC) Częste ponowne tworzenie dużych ArrayBuffer zwiększa obciążenie zarządzania pamięcią. W sytuacjach krytycznych pod względem wydajności warto rozważyć projektowanie kodu w taki sposób, aby jak najczęściej wykorzystywać istniejące bufory.

Podsumowanie

TypedArray to mechanizm do szybkiego i wydajnego przetwarzania danych binarnych. Łącząc ArrayBuffer, który rezerwuje obszar bajtów o stałej długości, z TypedArray lub DataView, które odczytują i zapisują zawartość w określonych typach, możesz wykonywać elastyczne operacje binarne. W zależności od przypadku użycia możesz wybrać pomiędzy subarray/slice a DataView oraz zaprojektować implementację, zwracając uwagę na kolejność bajtów (endianness) i kopiowanie.

Możesz śledzić ten artykuł, korzystając z Visual Studio Code na naszym kanale YouTube. Proszę również sprawdzić nasz kanał YouTube.

YouTube Video