04. Typed Arrays

Типизированные массивы в ES6 используются для работы с бинарными данными, а так же для взаимодействия с нативными API, использующими бинарные данные. С развитием веба появилось все больше API, работающих с бинарными данными.

  • File API

  • XMLHttpRequest

  • Fetch API

  • Canvas

  • WebSockets

  • WebGL

  • Аудио/Видео

В Typed Array API используется две разновидности объектов:

  • Типизированные массивы (Uint8Array, Int16Array, Float32Array, etc.) интерпретируют содержимое ArrayBuffer как индексированную последовательность элементов заданного типа.

  • Объекты DataView позволяют получать доступ к содержимому ArrayBuffer как к элементам одного из числовых типов (Uint8, Int16, Float32, etc.) с определенной размерностью.

ArrayBuffers store the data, views (Typed Arrays and DataView) let you read and change it. In order to create a DataView, you need to provide its constructor with an ArrayBuffer. Typed Array constructors can optionally create an ArrayBuffer for you.

ArrayBuffer

Объект ArrayBuffer это стандартный набор бинарных данных с фиксированной длинной. Вы не можете манипулировать содержимым ArrayBuffer напрямую. Вместо этого, необходимо создать типизованное представление DataView, которое будет отображать буфер в определенном формате, и даст доступ на запись и чтение его содержимого.

// 16 bytes array buffer
const buffer = new ArrayBuffer(16);

// we can have multiple views on the top of buffer
const int32View = new Int32Array(buffer);
const int16View = new Int16Array(buffer);

Setting up Typed Array.

При создании типизированного массива вызывается его конструктор в котором указывается число байт:

// Typed array views work pretty much like normal arrays.
var f64a = new Float64Array(8);
f64a[0] = 10;
f64a[1] = 20;
f64a[2] = f64a[0] + f64a[1];

Статический метод ArrayBuffer.isView(obj) принимает на вход obj в проверяет, является ли он ArrayBuffer или его представлением.

Если необходимо создать Typed Array из набора значений, то стоит использовать функцию TypedArray.of(...items), играющей роль статического конструктора.

Float32Array.of(0.151, -8, 3.7);

Аналогично массивам, существует метод TypedArray<U>.from(source : Iterable<T>, mapfn? : T => U, thisArg?) конвертирующий данные из итерируемого источника в новый объект типизированного массива

const ui16 = Uint16Array.from(Uint8Array.of(0, 1, 2));

Typed Arrays and regular Arrays

Типизированные массивы хоть и берут многое от массивов, технически ими не являются.

Сходства:

  • У Typed Array есть поле length.

  • Доступ к элементам Typed Array осуществляется по индексу в квадратных скобках [...].

  • У них есть все методы, свойственные массивам.

  • Вызов Arrays.isArray() вернет false для типизированных массивов.

  • Типизированные массивы являются итерируемыми.

Различия:

  • Индекс элемента может быть отрицательным

  • Все элементы типизированных массивов имеют один тип.

  • Типизированные массивы являются непрерывными и не имеют дыр.

  • Имеют поле buffer, ссылающееся на оригинальный буфер, представлением которого является данный типизированный массив.

Byte Data

Типизированные массивы накладывают ограничения на значения своих элементов в диапазоне (в соответствии с типом). Для всех типизированных массивов, за исключением UInt8ClampedArray переполнение обрабатывается по обычным правилам:

  • При переполнении в большую сторону, значение переходит в минимальное для данного диапазона

  • При переполнении в меньшую сторону, значение переходит в большее для данного диапазона

const uint8 = new Uint8Array(1);
uint8[0] = 255;
uint8[0]; // highest value within range
255;
uint8[0] = 256;
uint8[0]; // overflow
0;

Для UInt8ClampedArray выходящие за диапазон сбрасываются к своим соответствующим верхним/нижним границам.

Для типов данных, чья размерность превышает один байт начинает играть роль порядок байт. Для типизированных массивов используется порядок байт той платформы на которой они были созданы и не может быть измен в дальнейшем.

DataView

Объект DataView это низкоуровневый интерфейс предоставляющий API для записи/чтения произвольных данных в буфер. Это полезно при работе с разнородными данными, например. В то время как типизованные представления всегда имеют порядок байт родной для вашей операционной системы, DataView позволяет контролировать порядок байт (byte-order). По умолчанию этоbig-endian, но через API можно установить little-endian.

Объекты DataView создаются при помощи следующего конструктора DataView(buffer, byteOffset=0, byteLength=buffer.byteLength-byteOffset) на основе существующего буфера.

Объекты DataView представляют read/write api для чтения данных из буфера. Два основных метода:

  • getElementType(byteOffset, littleEndian=false) - читает данные в виде одного из типов.

  • setElementType(byteOffset, value, littleEndian=false) - записывает данные в виде одного из типов.

    Где ElementType это один из Float32, Float64, Int8, Int16, Int32, Uint8, Uint16, Uint32

const dv = new DataView(buffer);
const vector_length = dv.getUint8(0);
const width = dv.getUint16(1); // 0+uint8 = 1 bytes offset
const height = dv.getUint16(3); // 0+uint8+uint16 = 3 bytes offset
const vectors = new Float32Array(width * height * vector_length);
for (const i = 0, off = 5; i < vectors.length; i++, off += 4) {
  vectors[i] = dv.getFloat32(off);
}

Для объектов DataView можно контролировать порядок байт, указывая его в методах доступа/чтения. По умолчанию используется BigEndian.

Объекты DataView могут использоваться для гетерогенных данных.

References

Last updated