Где использовать массив ArrayBuffer против типизированного массива в JavaScript?

Я перемещаюсь из Node.js в среду браузера, и я все еще запутался в массивах ArrayBuffer и типизированных массивах (таких как Uint8Array).

Я смущен тем, где использовать типизированные массивы, и где использовать ArrayBuffer напрямую. Не сложно преобразовать один в другой и наоборот, но использовать, когда?

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

Или: следует ли мне возвращать ArrayBuffer из моих функций (например, для внешнего API) или типизированных массивов?

Обратите внимание, что я могу google как точно добавлять элементы и т.д. к этим типизированным массивам; то, что мне не хватает, - это краткое общее руководство, что использовать где. Особенно при перемещении из буфера node.

Ответ 1

Концепция

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

ArrayBuffer byte array in memory
Массив массива ArrayBuffer в памяти - каждый индекс равен одному байту. ArrayBuffer выровнен в памяти.

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

Визуальные зависимости

Различные виды используются в зависимости от того, что вам нужно. Если вам нужно только прочитать значения байтов, т.е. подписанные значения между -128 и 127 -или-unsigned значениями между 0-255, вы должны использовать Int8Array или Uint8Array. Обратите внимание, что их имена немного "вводят в заблуждение", поскольку они представляют собой представления, а не массивы, и ссылаются только на базовый ArrayBuffer.

Аналогично, у вас есть представления для Int8Array, Uint8Array, Uint8ClampedArray, Int16Array, Uint16Array, Int32Array, Uint3Array, Float32Array и Float64Array.

За исключением * int8Arrays, другие приходят с некоторым требованием к размеру ArrayBuffer. Например, представление Uint32Array должно располагаться поверх ArrayBuffer, которое делится на четыре, иначе оно выдает ошибку. * int 16 просмотров потребует двухбайтную границу.

Это обычно не проблема, потому что вы можете указать количество индексов с помощью конструктора представлений напрямую, и соответствующий ArrayBuffer будет создан автоматически для выполнения этих требований.

И поскольку ArrayBuffer является байтовым массивом, представление * int16 считывает из него два байта - или один индекс = два байта, * int32 четыре или один индекс = четыре байта и т.д.

Основное различие между Uint8Array и Uint8ClampedArray заключается в том, что значения вне диапазона подчиняются модулю (например, 256 становится 0) с обычными массивами. В зажатом массиве значения, как предполагается, зажимаются (256 становится 255).

*int16 view
Int16/Uint16 - каждый индекс представляет два байта и выравнивается по памяти.

*int32 view
Int32/Uint32 и Float32 - каждый индекс представляет четыре байта и выравнивается по памяти.

Float64 view
Float64 view - каждый индекс представляет собой восемь байтов и выравнивается по памяти.

DataView для гибкости

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

Например, индекс * int32 всегда указывает на ячейку памяти, которая делится на четыре. DataView, с другой стороны, может прочитать Uint32, скажем, в позиции 5, и позаботится обо всех необходимых шагах внутри (смещение битов, маскирование и т.д.), Но ценой незначительных накладных расходов.

Еще одно отличие состоит в том, что DataView не использует индексы, а абсолютные позиции байтов для данных, которые он представляет, и имеет свои собственные методы для чтения или записи различных ширины из/в любую позицию.

DataView
DataView - может считывать данные из любой позиции и любой ширины.

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

В настоящее время не существует 64-битных представлений для целых чисел, но представляется для ES8.

SharedArrayBuffers

Также полезно упомянуть новый SharedArrayBuffers, который можно использовать для веб-работников.

Вы могли (и все еще можете) использовать переносимые объекты в прошлом в некоторых браузерах, но SharedArrayBuffers более эффективны в том смысле, что память остается то же, передается только информация об этом. SharedArrayBuffers не могут отсоединиться, поскольку ArrayBuffers могут.

Цели и области использования

Типизированные массивы хороши для хранения определенных числовых значений и быстры. Растровые изображения - типичный кандидат для типизированных массивов (например, холст 2D/WebGL).

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

DataViews идеально подходят для разбора или создания двоичных файлов и форматов файлов.

Типизированные массивы - отличный способ упаковать двоичные данные для отправки через сеть, на сервер или через веб-сокеты и такие вещи, как каналы данных для WebRTC.

Если вы имеете дело с аудио, видео, холстом или записью носителя, часто нет возможности использовать типизированные массивы.

Ключами для использования типизированных массивов являются производительность и память. Они чаще всего используются в специальных сценариях, но нет ничего неправильного в их использовании в обычных случаях, когда вам нужно хранить только числовые значения (или строки utf-8, векторы шифрования и т.д.). Они быстрые и имеют небольшой объем памяти.

Меры предосторожности

Существует несколько предосторожностей:

Байт порядка

В отношении байтового порядка должны быть приняты некоторые меры предосторожности. Типизированные массивы всегда отражают архитектуру процессора, в которой они работают, т.е. мало-северный или big-endian. Большинство потребительских систем малоподобны, но при использовании массивов * int16 и * int32 вы должны уделять особое внимание порядку байтов. DataView также может помочь с этой частью, но не всегда является хорошим выбором, если производительность важна.

Байт-порядок также важен при приеме данных с сервера. Они обычно всегда в формате big-endian (заказ сети AKA). Для разбора форматов файлов будет применяться то же самое.

Кодирование числа с плавающей запятой

Float32/Float64 будет читать и записывать числа, закодированные в формате IEEE-754. Это также необходимо знать, если для одного и того же буфера используются несколько представлений.

Поддержка кросс-браузера

В настоящее время большинство браузеров поддерживают типизированные массивы. Если вам приходится иметь дело со старыми браузерами, вы должны вернуться к IE9 или более старым мобильным браузерам, чтобы не использовать их.

Safari не особенно оптимизирована в отношении их производительности, но есть и другие преимущества. Версия 5.1 не поддерживает Float64.

У мобильных устройств есть свои аппаратные ограничения, но в целом: типизированные массивы безопасны в использовании. Для особых случаев существует polyfill.