Comment convertir un objet ArrayBuffer en valeur String

Renato Mangini

Les ArrayBuffers sont utilisés pour transporter des données brutes et plusieurs nouvelles API s'appuient dessus, y compris WebSockets, Web Intents 2](https://www.html5rocks.com/en/tutorials/file/xhr2/) et WebWorkers. Toutefois, étant donné qu'ils ont récemment fait leur entrée dans l'univers JavaScript, ils sont parfois mal interprétés ou mal utilisés.

Du point de vue sémantique, un ArrayBuffer est simplement un tableau d'octets visualisés à travers un masque spécifique. Ce masque, une instance de ArrayBufferView, définit la manière dont les octets sont alignés pour correspondre à la structure attendue du contenu. Par exemple, si vous savez que les octets dans un ArrayBuffer représentent un tableau d'entiers non signés de 16 bits, il vous suffit d'encapsuler le ArrayBuffer dans une vue Uint16Array et vous pouvez manipuler ses éléments à l'aide de la syntaxe entre crochets comme si Uint16Array était un tableau d'entiers:

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

Une question pratique courante concernant ArrayBuffer est de savoir comment convertir un String en ArrayBuffer et inversement. Étant donné qu'un ArrayBuffer est en fait un tableau d'octets, cette conversion nécessite que les deux extrémités conviennent de la manière de représenter les caractères de la chaîne en octets. Vous avez probablement déjà vu cet "accord" : il s'agit de l'encodage des caractères de la chaîne (et les "conditions d'accord" habituelles sont, par exemple, Unicode UTF-16 et iso8859-1). Ainsi, si vous et l'autre partie êtes d'accord sur l'encodage UTF-16, le code de conversion pourrait ressembler à ceci:

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

Notez l'utilisation de Uint16Array. Il s'agit d'une vue ArrayBuffer qui aligne les octets des ArrayBuffers en tant qu'éléments de 16 bits. Il ne gère pas l'encodage des caractères lui-même, qui est géré comme Unicode par String.fromCharCode et str.charCodeAt.

Dans StackOverflow, une question populaire à ce sujet a une réponse très votée avec une solution quelque peu compliquée pour la conversion : créez un FileReader pour agir en tant que convertisseur et transmettez-lui un Blob contenant la chaîne. Bien que cette méthode fonctionne, elle présente une mauvaise lisibilité et je suppose qu'elle est lente. Puisque des soupçons infondés ont commis de nombreuses erreurs dans l'histoire de l'humanité, prenons ici une approche plus scientifique. J'ai défini les deux méthodes, et le résultat confirme mes doutes. Vous pouvez regarder la démo ici.

Dans Chrome 20, il est presque 27 fois plus rapide d'utiliser le code de manipulation directe ArrayBuffer dans cet article que d'utiliser la méthode FileReader/Blob.