Come convertire Arraybuffer in e da String

Renato Mangini

Gli ArrayBuffer vengono utilizzati per trasportare i dati non elaborati e diverse nuove API si basano su questi oggetti, tra cui WebSockets, Web Intents 2](https://www.html5rocks.com/en/tutorials/file/xhr2/) e WebWorkers. Tuttavia, poiché sono state introdotte di recente nel mondo di JavaScript, a volte vengono interpretate o utilizzate in modo errato.

Dal punto di vista semantico, un ArrayBuffer è semplicemente un array di byte visualizzato tramite una maschera specifica. Questa maschera, un'istanza di ArrayBufferView, definisce in che modo i byte vengono allineati in modo da corrispondere alla struttura prevista dei contenuti. Ad esempio, se sai che i byte in un ArrayBuffer rappresentano un array di interi non firmati a 16 bit, basta avvolgere l'ArrayBuffer in una vista Uint16Array e puoi manipolarne gli elementi utilizzando la sintassi delle parentesi graffe come se Uint16Array fosse un array di interi:

// 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 domanda pratica comune su ArrayBuffer è come convertire un String in un ArrayBuffer e viceversa. Poiché un ArrayBuffer è in realtà un array di byte, questa conversione richiede che entrambe le parti concordino su come rappresentare i caratteri nella stringa come byte. Probabilmente hai già visto questo "contratto": si tratta della codifica dei caratteri della stringa (e i "termini del contratto" usuali sono, ad esempio, Unicode UTF-16 e iso8859-1). Pertanto, supponendo che tu e l'altra parte abbiate concordato la codifica UTF-16, il codice di conversione potrebbe essere simile al seguente:

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

Nota l'utilizzo di Uint16Array. Si tratta di una visualizzazione ArrayBuffer che allinea i byte degli ArrayBuffer come elementi a 16 bit. Non gestisce la codifica dei caratteri, che viene gestita come Unicode da String.fromCharCode e str.charCodeAt.

Una popolare domanda su questo argomento su Stack Overflow ha una risposta molto votata con una soluzione un po' complicata per la conversione: creare un FileReader da utilizzare come convertitore e inviarvi un Blob contenente la stringa. Sebbene questo metodo funzioni, ha una scarsa leggibilità e immagino sia lento. Poiché sospetti infondati hanno causato molti errori nella storia dell'umanità, adottiamo un approccio più scientifico. Ho eseguito jsperf sui due metodi e il risultato conferma i miei sospetti. Dai un'occhiata alla demo qui.

In Chrome 20, è quasi 27 volte più veloce utilizzare il codice di manipolazione diretta di ArrayBuffer riportato in questo articolo rispetto al metodo FileReader/Blob.