TypedArray в TypeScript
В этой статье объясняется TypedArray в TypeScript.
Мы объясним TypedArray в TypeScript, включая практические примеры.
YouTube Video
TypedArray в TypeScript
TypedArray — это механизм для эффективной работы с бинарными данными. Он особенно полезен для низкоуровневых бинарных операций, таких как работа с большими изображениями, сетевыми байтовыми потоками и числовыми массивами для WebGL.
Как создать ArrayBuffer
ArrayBuffer представляет собой область памяти фиксированной длины в байтах. Сначала создайте буфер и проверьте его размер и длину в байтах.
1// Create an ArrayBuffer of 16 bytes
2const buffer: ArrayBuffer = new ArrayBuffer(16);
3console.log("buffer.byteLength:", buffer.byteLength); // 16
- Этот код создает пустую область памяти размером 16 байт. Сам
ArrayBufferне имеет функций для чтения/записи, поэтому доступ к нему осуществляется черезTypedArrayилиDataView.
Типы TypedArray
Существует много типов TypedArray, например, следующие. Они различаются в зависимости от типа данных и размера, с которыми работают.
| TypedArray | Тип данных | Размер в битах |
|---|---|---|
Int8Array |
8-битное целое число | 8 бит |
Uint8Array |
Беззнаковое 8-битное целое число | 8 бит |
Uint8ClampedArray |
Ограниченное беззнаковое 8-битное целое число | 8 бит |
Int16Array |
16-битное целое число | 16 бит |
Uint16Array |
Беззнаковое 16-битное целое число | 16 бит |
Int32Array |
32-битное целое число | 32 бит |
Uint32Array |
Беззнаковое 32-битное целое число | 32 бит |
Float32Array |
32-битное число с плавающей запятой | 32 бита |
Float64Array |
64-битное число с плавающей запятой | 64 бита |
Базовые типизированные массивы (Uint8Array, Int16Array, Float32Array и др.)
TypedArray создает 'типизированное представление' поверх ArrayBuffer. Ниже приведены примеры создания и использования типичных 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);- Создавая несколько представлений одного и того же
ArrayBuffer, вы можете читать и записывать одну и ту же память с разными типами и детализацией.lengthпредставления — количество элементов, аbyteLength— количество байт.
Запись и чтение (операции на уровне байтов)
Когда вы записываете значение в TypedArray, соответствующие байты в памяти обновляются. Вы можете увидеть изменения, если прочтете тот же буфер с помощью другого представления.
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
- В этом примере мы записываем последовательность байтов, затем читаем ту же область как 32-битное целое число. Обратите внимание, что результат зависит от порядка байт (endianness) среды выполнения.
Порядок байт (endianness) и DataView
DataView полезен, если вы хотите контролировать вопросы, связанные с порядком байт в зависимости от среды. DataView позволяет указывать порядок байт при чтении и записи.
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— это удобный механизм для детального чтения и записи байтов. Он поддерживает различные типы данных, такие как знаковые и беззнаковые числа, а также числа с плавающей запятой, и позволяет явно указывать порядок байт, что очень полезно при реализации бинарных протоколов.
Различие между subarray и slice
subarray у TypedArray возвращает представление, использующее тот же буфер, а slice возвращает новую копию. Производительность и побочные эффекты зависят от выбранного метода.
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
- Если вы изменяете общий просмотр, исходный массив также изменится, что может привести к непреднамеренным побочным эффектам. Если вы хотите безопасно сохранить исходный массив, вы можете заранее создать его копию с помощью
slice().
Копирование буферов и преобразование типов (конвертация между TypedArray)
Мы объясним, как копировать данные между TypedArray и как вставлять части с помощью метода 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]
- Если типы элементов различаются, необходимо выполнять преобразование значений (например, округление или проверку диапазонов), а не просто копировать. Метод
setпозволяет быстро копировать междуTypedArrayс одинаковым типом элементов.
Практический пример: парсер бинарного протокола (простая реализация)
Здесь мы приводим пример простого парсера, который читает бинарные данные фиксированного формата, состоящие из 1 байта типа, 2 байт длины данных и следующей полезной нагрузки (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));- В этом примере заголовок считывается с помощью
DataView, а срез полезной нагрузки создается с помощьюUint8Array. Порядок байт и проверка длины буфера имеют важное значение.
Web API и TypedArray (пример: получение бинарных данных)
Это типичный пример обработки ArrayBuffer, полученного из сетевого запроса с использованием 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, полученный с помощьюResponse.arrayBuffer(), вTypedArray. Это используется для изображений, аудио или собственных бинарных протоколов.
Советы по производительности и распространённые ошибки
Вот несколько советов по производительности и распространённых ошибок, которые полезно знать при работе с TypedArray:.
-
Избегайте ненужного копирования Для эффективной обработки больших данных вы можете сократить ненужное копирование, создавая частичные представления с помощью
subarrayили разделяя один и тот жеArrayBufferмежду несколькими представлениями. -
Будьте внимательны с порядком байт В сетевых взаимодействиях или файловых форматах порядок байт (byte order) часто явно указывается. Использование
DataViewпозволяет явно задавать порядок байт при чтении и записи, предотвращая неправильную интерпретацию данных. -
Учитывайте диапазоны значений каждого типа Например,
Uint8может представлять только значения от 0 до 255. Если вы вводите отрицательное значение, может произойти усечение или переполнение значения, поэтому следует определить правила преобразования при необходимости. -
Учитывайте нагрузку на сборку мусора Частое создание крупных объектов
ArrayBufferувеличивает нагрузку на управление памятью. В ситуациях, критичных к производительности, стоит рассмотреть возможность проектирования кода для максимального повторного использования существующих буферов.
Резюме
TypedArray — это механизм для быстрой и эффективной работы с бинарными данными. Комбинируя ArrayBuffer, который выделяет область байт фиксированной длины, с TypedArray или DataView, которые читают и записывают данные с использованием определённых типов, вы можете выполнять гибкие бинарные операции. В зависимости от вашего случая использования вы можете выбрать между использованием subarray/slice или DataView, а также проектировать свою реализацию с учетом порядка байтов и копирования.
Вы можете следовать этой статье, используя Visual Studio Code на нашем YouTube-канале. Пожалуйста, также посмотрите наш YouTube-канал.