web-dev-qa-db-fra.com

Tableaux typés Javascript et endianness

J'utilise WebGL pour rendre un fichier de maillage codé binaire. Le fichier binaire est écrit au format big-endian (je peux le vérifier en ouvrant le fichier dans un éditeur hexadécimal ou en visualisant le trafic réseau à l'aide de fiddler). Lorsque j'essaie de lire la réponse binaire à l'aide d'un Float32Array ou Int32Array, le binaire est interprété comme little-endian et mes valeurs sont fausses:

// Interpret first 32bits in buffer as an int
var wrongValue = new Int32Array(binaryArrayBuffer)[0];

Je ne trouve aucune référence à l'endianité par défaut des tableaux typés dans http://www.khronos.org/registry/typedarray/specs/latest/ donc je me demande quel est le problème? Dois-je supposer que toutes les données binaires doivent être en petit-boutien lors de la lecture à l'aide de tableaux typés?

Pour contourner le problème, je peux utiliser un objet DataView (décrit dans le lien précédent) et appeler:

// Interpret first 32bits in buffer as an int
var correctValue = new DataView(binaryArrayBuffer).getInt32(0);

Les fonctions DataView telles que "getInt32" lisent les valeurs big-endian par défaut.

(Remarque: j'ai testé en utilisant Google Chrome 15 et Firefox 8 et ils se comportent tous les deux de la même manière)

55
Bob

Malheureusement, le comportement actuel est que l'endianisme est celui du matériel sous-jacent. Comme presque tous les ordinateurs de bureau sont x86, cela signifie petit-boutien. La plupart des ARM OS utilisent le mode little-endian (les processeurs ARM sont bi-endian et peuvent donc fonctionner dans les deux cas).

La raison pour laquelle cela est quelque peu triste est le fait que cela signifie que presque personne ne testera si son code fonctionne sur du matériel big-endian, ce qui fait mal, et le fait que la plate-forme Web entière a été conçue autour d'un code fonctionnant uniformément sur toutes les implémentations et plates-formes, que cela casse.

61
gsnedders

Pour info, vous pouvez utiliser la fonction javascript suivante pour déterminer l'endianité de la machine, après quoi vous pouvez passer un fichier correctement formaté au client (vous pouvez stocker deux versions du fichier sur le serveur, big endian et little endian):

function checkEndian() {
    var arrayBuffer = new ArrayBuffer(2);
    var uint8Array = new Uint8Array(arrayBuffer);
    var uint16array = new Uint16Array(arrayBuffer);
    uint8Array[0] = 0xAA; // set first byte
    uint8Array[1] = 0xBB; // set second byte
    if(uint16array[0] === 0xBBAA) return "little endian";
    if(uint16array[0] === 0xAABB) return "big endian";
    else throw new Error("Something crazy just happened");
}

Dans votre cas, vous devrez probablement recréer le fichier en petit endian, ou parcourir toute la structure de données pour le rendre peu endian. En utilisant une torsion de la méthode ci-dessus, vous pouvez échanger l'endianité à la volée (ce n'est pas vraiment recommandé et n'a de sens que si la structure entière est du même type étroitement compressé, en réalité, vous pouvez créer une fonction de stub qui permute les octets selon les besoins):

function swapBytes(buf, size) {
    var bytes = new Uint8Array(buf);
    var len = bytes.length;
    var holder;

    if (size == 'Word') {
        // 16 bit
        for (var i = 0; i<len; i+=2) {
            holder = bytes[i];
            bytes[i] = bytes[i+1];
            bytes[i+1] = holder;
        }
    } else if (size == 'DWORD') {
        // 32 bit
        for (var i = 0; i<len; i+=4) {
            holder = bytes[i];
            bytes[i] = bytes[i+3];
            bytes[i+3] = holder;
            holder = bytes[i+1];
            bytes[i+1] = bytes[i+2];
            bytes[i+2] = holder;
        }
    }
}
28
Ryan

Tiré d'ici http://www.khronos.org/registry/typedarray/specs/latest/ (lorsque cette spécification est entièrement implémentée), vous pouvez utiliser:

new DataView(binaryArrayBuffer).getInt32(0, true) // For little endian
new DataView(binaryArrayBuffer).getInt32(0, false) // For big endian

Cependant, si vous ne pouvez pas utiliser ces méthodes parce qu'elles ne sont pas implémentées, vous pouvez toujours vérifier la valeur magique du fichier (presque tous les formats ont une valeur magique) sur l'en-tête pour voir si vous devez l'inverser selon vos endiannes.

De plus, vous pouvez enregistrer des fichiers spécifiques aux endiannes sur votre serveur et les utiliser en fonction des endiannes hôtes détectés.

27
Chiguireitor

Les autres réponses me semblent un peu dépassées, voici donc un lien vers les dernières spécifications:

http://www.khronos.org/registry/typedarray/specs/latest/#2.1

En particulier:

Les types de vue de tableau typé fonctionnent avec l'endianité de l'ordinateur hôte.

Le type DataView fonctionne sur des données avec une endianité spécifiée (big-endian ou little-endian).

Donc, si vous souhaitez lire/écrire des données en Big Endian (Network Byte Order), voir: http://www.khronos.org/registry/typedarray/specs/latest/#DATAVIEW

// For multi-byte values, the optional littleEndian argument
// indicates whether a big-endian or little-endian value should be
// read. If false or undefined, a big-endian value is read.
15
user1338062

Un moyen rapide de vérifier l'endianité

/** @returns {Boolean} true if system is big endian */
function isBigEndian() {
    const array = new Uint8Array(4);
    const view = new Uint32Array(array.buffer);
    return !((view[0] = 1) & array[0]);
}

Comment ça fonctionne:

  • un tableau de 4 octets est créé;
  • une vue 32 bits enveloppe ce tableau;
  • view[0] = 1 définit le tableau pour contenir la valeur 32 bits 1;
  • vient maintenant la partie importante: si le système est big endian, que 1 est maintenu par l'octet le plus à droite (peu vient en dernier); s'il est peu endian, c'est l'octet le plus à gauche qui le stocke (peu vient en premier). Donc, faire un ET au niveau du bit avec l'octet le plus à gauche renvoie false si la machine est un gros endian;
  • la fonction le convertit finalement en booléen en appliquant le ! opérateur au résultat de & opération, tout en l'inversant pour qu'elle renvoie true pour big endian.
5
Lucio Paiva