type the provided not arraybufferview javascript endianness webgl typed-arrays arraybuffer

the - arraybuffer to string javascript



Javascript mecanografiado matrices y endianness (5)

Estoy usando WebGL para renderizar un archivo de malla codificado en binario. El archivo binario está escrito en formato big-endian (puedo verificar esto abriendo el archivo en un editor hexadecimal, o viendo el tráfico de la red usando Fiddler). Cuando trato de leer la respuesta binaria con un Float32Array o Int32Array, el binario se interpreta como little-endian y mis valores son incorrectos:

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

No puedo encontrar ninguna referencia al endianness predeterminado de los arreglos escritos en http://www.khronos.org/registry/typedarray/specs/latest/ así que me pregunto cuál es el problema. ¿Debo suponer que todos los datos binarios deberían ser little-endian al leer con matrices escritas?

Para solucionar el problema, puedo usar un objeto DataView (descrito en el enlace anterior) y llamar:

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

Las funciones de DataView como "getInt32" leen valores de big endian por defecto.

(Nota: He probado con Google Chrome 15 y Firefox 8 y ambos se comportan de la misma manera)


Manera rápida de comprobar el endianness

/** @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]); }

Cómo funciona:

  • se crea una matriz de 4 bytes;
  • una vista de 32 bits envuelve esa matriz;
  • view[0] = 1 establece la matriz para mantener el valor 1 de 32 bits;
  • Ahora viene la parte importante: si el sistema es big endian, ese es el que está siendo retenido por el byte más a la derecha (poco viene en último lugar); si es little endian, es el byte más a la izquierda que lo almacena (poco viene primero). Por lo tanto, hacer un AND a nivel de bits con el byte más a la izquierda devuelve false si la máquina es big endian;
  • la función finalmente lo convierte en un booleano aplicando el ! Operador al resultado de la operación & , mientras que también lo invierte para que devuelva verdadero para Big Endian.

El comportamiento actual, algo triste, es que la endianness es la del hardware subyacente. Como casi todas las computadoras de escritorio son x86, esto significa little-endian. La mayoría de los sistemas operativos ARM utilizan el modo little-endian (los procesadores ARM son bi-endian y, por lo tanto, pueden operar en cualquiera de ellos).

La razón por la que esto es algo triste es el hecho de que significa que casi nadie probará si su código funciona en hardware big-endian, perjudicando lo que hace, y el hecho de que toda la plataforma web fue diseñada para que el código funcione de manera uniforme en todas las implementaciones y plataformas. que esto rompe.


Las otras respuestas me parecen un poco anticuadas, así que aquí hay un enlace a la última especificación:

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

En particular:

Los tipos de vista de matriz escritos operan con la endianidad de la computadora host.

El tipo DataView opera sobre datos con un endianness específico (big-endian o little-endian).

Entonces, si desea leer / escribir datos en Big Endian (Orden de bytes de red), consulte: 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.


Para su información, puede utilizar la siguiente función de javascript para determinar la endianness de la máquina, después de lo cual puede pasar un archivo con el formato adecuado al cliente (puede almacenar dos versiones del archivo en el servidor, big endian y 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"); }

En su caso, probablemente tendrá que volver a crear el archivo en little endian, o ejecutar toda la estructura de datos para hacerlo poco endian. Con un giro del método anterior, puede intercambiar endianness sobre la marcha (no es realmente recomendable y solo tiene sentido si toda la estructura es del mismo tipo, pero en realidad puede crear una función de código auxiliar que intercambie los bytes según sea necesario):

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


Tomado de aquí http://www.khronos.org/registry/typedarray/specs/latest/ (cuando esa especificación está completamente implementada) puede usar:

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

Sin embargo, si no puede usar esos métodos porque no están implementados, siempre puede verificar el valor mágico del archivo (casi todos los formatos tienen un valor mágico) en el encabezado para ver si necesita invertirlo de acuerdo con sus endiannes.

Además, puede guardar archivos específicos de endiannes en su servidor y usarlos de acuerdo con las endiannes de host detectadas.