Cómo convertir ArrayBuffer en una string y entre ella

Renato Mangini

Los ArrayBuffers se usan para transportar datos sin procesar y varias API nuevas los usan, como WebSockets, Web Intents 2](https://www.html5rocks.com/en/tutorials/file/xhr2/) y WebWorkers. Sin embargo, debido a que llegaron recientemente al mundo de JavaScript, a veces se malinterpretan o se usan de forma inadecuada.

Semánticamente, un ArrayBuffer es simplemente un array de bytes vistos a través de una máscara específica. Esta máscara, una instancia de ArrayBufferView, define cómo se alinean los bytes para que coincidan con la estructura esperada del contenido. Por ejemplo, si sabes que los bytes de un ArrayBuffer representan un array de números enteros sin firma de 16 bits, solo debes unir el ArrayBuffer a una vista Uint16Array y manipular sus elementos con la sintaxis de corchetes como si la Uint16Array fuera un array de números enteros:

// 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]

Una pregunta práctica común sobre ArrayBuffer es cómo convertir un String en ArrayBuffer, y viceversa. Dado que un ArrayBuffer es, de hecho, un array de bytes, esta conversión requiere que ambos extremos acuerden cómo representar los caracteres de la string como bytes. Es probable que ya hayas visto este “acuerdo” antes: es la codificación de caracteres de la string (y los “términos del acuerdo” habituales son, por ejemplo, Unicode UTF-16 e iso8859-1). Por lo tanto, si tú y la otra parte acordaron la codificación UTF-16, el código de conversión podría ser similar al siguiente:

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;
}

Observa el uso de Uint16Array. Esta es una vista de ArrayBuffer que alinea los bytes de ArrayBuffers como elementos de 16 bits. No controla la codificación de caracteres en sí, que String.fromCharCode y str.charCodeAt la manejan como Unicode.

Una pregunta sobre esto popular de Stack Overflow tiene una respuesta muy votada con una solución en cierta medida para la conversión: crea un FileReader para que actúe como conversor y envíale un Blob que contenga la cadena. Aunque este método funciona, tiene mala legibilidad y sospecho que es lento. Dado que las sospechas infundadas generaron muchos errores en la historia de la humanidad, adoptemos un enfoque más científico. Ya jsperfé los dos métodos, y el resultado confirma mi sospecha. Puedes consultar la demostración aquí.

En Chrome 20, es casi 27 veces más rápido usar el código de manipulación ArrayBuffer directo en este artículo que usar el método FileReader/Blob.