Comment convertir un objet ArrayBuffer en valeur String

Renato Mangini

Les objets ArrayBuffer sont utilisés pour transporter des données brutes et plusieurs nouvelles API s'en servent, y compris WebSockets, les Web Intents 2](https://www.html5rocks.com/en/tutorials/file/xhr2/) et WebWorkers. Cependant, comme ils ont récemment fait leur apparition dans le monde JavaScript, il arrive qu'ils soient mal interprétés ou utilisés à mauvais escient.

D'un point de vue sémantique, un ArrayBuffer est simplement un tableau d'octets visualisés via 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 d'un objet 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 de 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 String en octets. Vous avez probablement déjà vu ce "contrat" : il s'agit de l'encodage des caractères de la chaîne (et les "conditions du contrat" habituelles sont, par exemple, Unicode UTF-16 et iso8859-1). Ainsi, en supposant que vous et l'autre partie ayez convenu de 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 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.

Une question populaire à ce sujet sur StackOverflow a une réponse très accueillie avec une solution quelque peu compliquée à la conversion : créez un FileReader pour servir de convertisseur et insérez-y un Blob contenant la chaîne. Bien que cette méthode fonctionne, sa lisibilité est médiocre et je suppose qu'elle est lente. Étant donné que des soupçons infondés ont entraîné de nombreuses erreurs dans l'histoire de l'humanité, adoptons une approche plus scientifique. J'ai déjà testé les deux méthodes, et le résultat confirme mes suspicions. Vous pouvez voir la démonstration ici.

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