Hoe ArrayBuffer van en naar String te converteren

ArrayBuffers worden gebruikt om onbewerkte gegevens te transporteren en verschillende nieuwe API's zijn hiervan afhankelijk, waaronder WebSockets , Web Intents 2](https://www.html5rocks.com/en/tutorials/file/xhr2/) en WebWorkers . Omdat ze echter onlangs in de JavaScript-wereld zijn beland, worden ze soms verkeerd geïnterpreteerd of misbruikt.

Semantisch gezien is een ArrayBuffer eenvoudigweg een reeks bytes die door een specifiek masker worden bekeken. Dit masker, een exemplaar van ArrayBufferView , definieert hoe bytes worden uitgelijnd om overeen te komen met de verwachte structuur van de inhoud. Als u bijvoorbeeld weet dat de bytes in een ArrayBuffer een array van 16-bits gehele getallen zonder teken vertegenwoordigen, plaatst u de ArrayBuffer gewoon in een Uint16Array weergave en kunt u de elementen ervan manipuleren met behulp van de syntaxis van haakjes alsof de Uint16Array een array met gehele getallen is:

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

Een veel voorkomende praktische vraag over ArrayBuffer is hoe je een String naar een ArrayBuffer converteert en omgekeerd. Omdat een ArrayBuffer in feite een byte-array is, vereist deze conversie dat beide uiteinden het eens zijn over hoe de tekens in de String als bytes moeten worden weergegeven. Je hebt deze "overeenkomst" waarschijnlijk al eerder gezien: het is de tekencodering van de String (en de gebruikelijke "overeenkomsttermen" zijn bijvoorbeeld Unicode UTF-16 en iso8859-1). Stel dat u en de andere partij het eens zijn over de UTF-16-codering, dan zou de conversiecode er ongeveer zo uit kunnen zien:

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

Let op het gebruik van Uint16Array . Dit is een ArrayBuffer-weergave waarin bytes van de ArrayBuffers worden uitgelijnd als 16-bits elementen. Het verwerkt de tekencodering zelf niet, die wordt afgehandeld als Unicode door String.fromCharCode en str.charCodeAt .

Een populaire StackOverflow- vraag hierover heeft een veelgeprezen antwoord met een enigszins ingewikkelde oplossing voor de conversie: maak een FileReader die als converter fungeert en voer er een Blob in die de String bevat. Hoewel deze methode werkt, is deze slecht leesbaar en vermoed ik dat deze traag is. Omdat ongegronde vermoedens tot veel fouten in de geschiedenis van de mensheid hebben geleid, laten we hier een meer wetenschappelijke benadering hanteren. Ik heb de twee methoden gejsperfd ​​en het resultaat bevestigt mijn vermoeden en je kunt de demo hier bekijken .

In Chrome 20 is het bijna 27 keer sneller om de directe ArrayBuffer manipulatiecode voor dit artikel te gebruiken dan om de FileReader / Blob -methode te gebruiken.