Как преобразовать ArrayBuffer в строку и обратно

ArrayBuffers используются для транспортировки необработанных данных, и на них полагаются несколько новых API, включая WebSockets , Web Intents 2](https://www.html5rocks.com/en/tutorials/file/xhr2/) и WebWorkers . Однако, поскольку они недавно появились в мире JavaScript, иногда их неправильно интерпретируют или используют.

Семантически ArrayBuffer — это просто массив байтов, просматриваемый через определенную маску. Эта маска, экземпляр ArrayBufferView , определяет, как байты выравниваются, чтобы соответствовать ожидаемой структуре содержимого. Например, если вы знаете, что байты в ArrayBuffer представляют собой массив 16-битных целых чисел без знака, вы просто обертываете ArrayBuffer в представление Uint16Array и можете манипулировать его элементами, используя синтаксис скобок, как если бы Uint16Array был целочисленным массивом:

// suppose buf contains the bytes [0x02, 0x01, 0x03, 0x07]
// notice the multibyte values respect the hardware endianess, which is little-endian in x86
var bufView = new Uint16Array(buf);
if (bufView[0]===258) {   // 258 === 0x0102
    console.log("ok");
}
bufView[0] = 255;    // buf now contains the bytes [0xFF, 0x00, 0x03, 0x07]
bufView[0] = 0xff05; // buf now contains the bytes [0x05, 0xFF, 0x03, 0x07]
bufView[1] = 0x0210; // buf now contains the bytes [0x05, 0xFF, 0x10, 0x02]

Один из распространенных практических вопросов, касающихся ArrayBuffer, — как преобразовать String в ArrayBuffer и наоборот. Поскольку ArrayBuffer фактически представляет собой массив байтов, это преобразование требует, чтобы оба конца договорились о том, как представлять символы в строке в виде байтов. Вы, вероятно, уже видели это «соглашение» раньше: это кодировка символов String (а обычными «условиями соглашения» являются, например, Unicode UTF-16 и iso8859-1). Таким образом, предположим, что вы и другая сторона договорились о кодировке UTF-16, код преобразования может быть примерно таким:

function ab2str(buf) {
    return String.fromCharCode.apply(null, new Uint16Array(buf));
}
function str2ab(str) {
    var buf = new ArrayBuffer(str.length*2); // 2 bytes for each char
    var bufView = new Uint16Array(buf);
    for (var i=0, strLen=str.length; i < strLen; i++) {
    bufView[i] = str.charCodeAt(i);
    }
    return buf;
}

Обратите внимание на использование Uint16Array . Это представление ArrayBuffer, которое выравнивает байты ArrayBuffers как 16-битные элементы. Он не обрабатывает саму кодировку символов, которая обрабатывается как Unicode с помощью String.fromCharCode и str.charCodeAt .

На популярный вопрос StackOverflow по этому поводу есть ответ, получивший большое количество голосов, с несколько запутанным решением преобразования: создать FileReader , который будет действовать как преобразователь, и передать в него Blob , содержащий строку. Хотя этот метод работает, он плохо читаем и, подозреваю, медленный. Поскольку необоснованные подозрения привели к множеству ошибок в истории человечества, давайте применим здесь более научный подход. Я выполнил оба метода с помощью jsperf , и результат подтверждает мои подозрения, и вы можете посмотреть демо-версию здесь .

В Chrome 20 использование прямого кода манипуляции ArrayBuffer в этой статье почти в 27 раз быстрее, чем использование метода FileReader / Blob .