我正在使用WebGL渲染二进制编码的网格文件。二进制文件以big-endian格式写出(我可以通过在十六进制编辑器中打开文件或使用fiddler查看网络流量来验证这一点)。当我尝试使用Float32Array或Int32Array读取二进制响应时,二进制文件被解释为little-endian并且我的值是错误的:
// Interpret first 32bits in buffer as an int
var wrongValue = new Int32Array(binaryArrayBuffer)[0];
我找不到http://www.khronos.org/registry/typedarray/specs/latest/中对类型化数组的默认字节序的任何引用,所以我想知道这是什么交易?在使用类型化数组进行读取时,我是否应该假设所有二进制数据都应该是小端?
为了解决这个问题,我可以使用DataView对象(在前面的链接中讨论过)并调用:
// Interpret first 32bits in buffer as an int
var correctValue = new DataView(binaryArrayBuffer).getInt32(0);
默认情况下,“getInt32”等DataView函数会读取big-endian值。
(注意:我已经使用Google Chrome 15和Firefox 8进行了测试,它们的行为方式相同)
答案 0 :(得分:59)
当前的行为,有点可悲的是,字节序是底层硬件的字节序。由于几乎所有台式电脑都是x86,这意味着小端。大多数ARM操作系统使用小端模式(ARM处理器是双端的,因此可以在两者中运行)。
这有点令人难过的原因在于,这意味着几乎没有人会测试他们的代码是否适用于大端硬件,损害了什么,以及整个Web平台是围绕代码在整个实现中统一工作而设计的和平台,这打破了。
答案 1 :(得分:26)
从这里采取http://www.khronos.org/registry/typedarray/specs/latest/(当该规范完全实施时),您可以使用:
new DataView(binaryArrayBuffer).getInt32(0, true) // For little endian
new DataView(binaryArrayBuffer).getInt32(0, false) // For big endian
但是,如果由于未实现这些方法而无法使用这些方法,则可以始终在标题上检查文件的魔术值(几乎每种格式都具有魔术值),以查看是否需要根据你的endiannes。
此外,您可以在服务器上保存特定于endiannes的文件,并相应地将它们用于检测到的主机端。
答案 2 :(得分:26)
仅供参考,您可以使用以下javascript函数来确定机器的字节顺序,之后您可以将格式正确的文件传递给客户端(您可以在服务器,big endian和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");
}
在您的情况下,您可能必须以小端重新创建文件,或者运行整个数据结构以使其成为小端。使用上述方法的扭曲,你可以动态交换字节顺序(不是真的推荐,只有整个结构是相同的紧密打包类型才有意义,实际上你可以创建一个根据需要交换字节的存根函数):
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;
}
}
}
答案 3 :(得分:15)
其他答案对我来说似乎有些过时,所以这里有最新规范的链接:
http://www.khronos.org/registry/typedarray/specs/latest/#2.1
特别是:
类型化数组视图类型与主机的字节顺序一起使用。
DataView类型对具有指定字节序(big-endian或little-endian)的数据进行操作。
因此,如果您想以Big Endian(网络字节顺序)读取/写入数据,请参阅: 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.
答案 4 :(得分:5)
/** @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]);
}
工作原理:
view[0] = 1
将数组设置为保留32位值1; !
运算符应用于&
操作的结果,将其转换为布尔值,同时还对其进行反转,以使它对于大字节序返回true。