Javascript 类型数组和字节顺序
我正在使用 WebGL 渲染二进制编码的网格文件。二进制文件以大端格式写入(我可以通过在十六进制编辑器中打开文件或使用 fiddler 查看网络流量来验证这一点)。当我尝试使用 Float32Array 或 Int32Array 读取二进制响应时,二进制文件被解释为小端字节序,并且我的值是错误的:
// 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);
DataView 函数(例如“getInt32”)默认读取大端值。
(注意:我已经使用 Google Chrome 15 和 Firefox 8 进行了测试,它们的行为方式相同)
I'm using WebGL to render a binary encoded mesh file. The binary file is written out in big-endian format (I can verify this by opening the file in a hex editor, or viewing the network traffic using fiddler). When I try to read the binary response using a Float32Array or Int32Array, the binary is interpreted as little-endian and my values are wrong:
// Interpret first 32bits in buffer as an int
var wrongValue = new Int32Array(binaryArrayBuffer)[0];
I can't find any references to the default endianness of typed arrays in http://www.khronos.org/registry/typedarray/specs/latest/ so I'm wondering what's the deal? Should I assume that all binary data should be little-endian when reading using typed arrays?
To get around the problem I can use a DataView object (discussed in the previous link) and call:
// Interpret first 32bits in buffer as an int
var correctValue = new DataView(binaryArrayBuffer).getInt32(0);
The DataView functions such as "getInt32" read big-endian values by default.
(Note: I've tested using Google Chrome 15 and Firefox 8 and they both behave the same way)
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(7)
当前的行为由底层硬件的字节顺序决定。由于几乎所有台式计算机都是 x86,这意味着小尾数法。大多数 ARM 操作系统使用小端模式(ARM 处理器是双端模式,因此可以在任一模式下运行)。
这有点令人悲伤的原因是,这意味着几乎没有人会测试他们的代码是否在大端硬件上运行,从而损害了它的运行,而且整个 Web 平台是围绕跨实现和平台统一运行的代码设计的,这打破了。
The current behaviour, is determined by the endianness of the underlying hardware. As almost all desktop computers are x86, this means little-endian. Most ARM OSes use little-endian mode (ARM processors are bi-endian and thus can operate in either).
The reason why this is somewhat sad is the fact that it means almost nobody will test whether their code works on big-endian hardware, hurting what does, and the fact that the entire web platform was designed around code working uniformly across implementations and platforms, which this breaks.
仅供参考,您可以使用以下 javascript 函数来确定机器的字节序,然后您可以将适当格式的文件传递给客户端(您可以在服务器上存储文件的两个版本,大端和小端):
在您的情况下您可能必须以小尾数法重新创建文件,或者运行整个数据结构以使其成为小尾数法。使用上述方法的变形,您可以动态交换字节顺序(并不真正推荐,只有当整个结构是相同的紧密包装类型时才有意义,实际上您可以创建一个根据需要交换字节的存根函数):
FYI you can use the following javascript function to determine the endianness of the machine, after which you can pass an appropriately formatted file to the client (you can store two versions of the file on server, big endian and little endian):
In your case you will probably have to either recreate the file in little endian, or run through the entire data structure to make it little endian. Using a twist of the above method you can swap endianness on the fly (not really recommended and only makes sense if the entire structure is the same tightly packed types, in reality you can create a stub function that swaps bytes as needed):
摘自这里 http://www.khronos.org/registry/typedarray/specs/latest / (当该规范完全实现时)您可以使用:
但是,如果您无法使用这些方法,因为它们没有实现,您可以随时检查文件的 magic 值(几乎每种格式都有一个 magic 值) )在标题上查看是否需要根据您的字节序反转它。
此外,您可以在服务器上保存特定字节序的文件,并根据检测到的主机字节序相应地使用它们。
Taken from here http://www.khronos.org/registry/typedarray/specs/latest/ (when that spec is fully implemented) you can use:
However, if you can't use those method because they aren't implemented, you can always check the file's magic value (almost every format has a magic value) on the header to see if you need to invert it according to your endiannes.
Also, you can save endiannes-specific files on your server and use them accordingly to the detected host endiannes.
其他答案对我来说似乎有点过时,所以这里是最新规范的链接:
http://www.khronos.org/registry/typedarray/specs/latest/#2.1
特别是:
因此,如果您想以 Big Endian(网络字节顺序)读/写数据,请参阅:
http://www.khronos.org/registry/typedarray/specs/latest/ #数据查看
The other answers seem a bit outdated to me, so here's a link to the latest spec:
http://www.khronos.org/registry/typedarray/specs/latest/#2.1
In particular:
So if you want to read/write data in Big Endian (Network Byte Order), see:
http://www.khronos.org/registry/typedarray/specs/latest/#DATAVIEW
检查字节顺序的快速方法
工作原理:
view[0] = 1
设置数组保存 32 位值 1;!
运算符应用于&
操作的结果,将其转换为布尔值,同时还反转它,以便对于大尾数法返回 true。一个不错的调整是将其变成 IIFE,这样你就可以仅运行一次检查,然后缓存它,然后您的应用程序可以根据需要多次检查它:
Quick way to check endianness
How it works:
view[0] = 1
sets the array to hold 32-bit value 1;!
operator to the result of the&
operation, while also inverting it so that it returns true for big endian.One nice tweak is to turn it into an IIFE, that way you can run the check only once and then cache it, then your application can check it as many times as it needs:
Little-endian 将 [0] 设置为 1,将 [1] 设置为 0;相反,大字节序将 [0] 设置为 0,将 [1] 设置为 1。
这也可以在不调用函数的情况下完成,因为字节序在运行时是恒定的:
Little-endian sets [0] to 1 and [1] to 0; conversely, big endian sets [0] to 0 and [1] to 1.
This can also be done without a function call, as endianness is constant at runtime:
另一种检查字节顺序的快速方法:
只需在此处添加我的 2Cents,但是,我发现下面的首选方法很有用;特别是当静态存储在 Singleton 中并跨类可用时:
如果每个 8Bit 的存储顺序与输入的十六进制不同,则它使用的是小端字节序。然后将结果存储起来,以供进一步参考。结果之所以准确,是因为根据 ECMA 脚本规范,数据存储在缓冲区中,与本地存储在设备上的方式相同。
事实上它只调用一次,然后存储它,这是非常有用的;特别是超过一百万次迭代,所有迭代都需要知道使用哪种字节序,包括最明显的一个,渲染。
为了说明这一点:
它与已经发布的 isBigEndian 版本类似,只是反过来了;这符合 EndianNess 的精神。
Yet Another QUICK WAY to CHECK Endianness:
Just adding my 2Cents here, but, my preferred method below is something I’ve found useful; especially when stored statically in a Singleton and made available across classes:
If each 8Bits is not stored in the same order as the hex was input, then it’s using little endian. The result is then stored, and is useful for further reference. The reason the result is accurate, is because data is stored in the buffer, the same way as it is stored Natively on the device, according to the ECMA script spec.
The fact it calls only once, and then stores it, is very useful; especially with a million+ Itterations, all needing to know which endianness to use, including the most obvious one, rendering.
To illustrate this:
It’s similar to the isBigEndian version already posted, just done a the other way round; which is in the spirit of EndianNess.