Endianness 安全复制二进制数据

Endianness safe copying binary data

我正在尝试使用 ArrayBuffer 操作二进制数据。如果原始 ArrayBuffer 看起来像这样:

var buffer = new ArrayBuffer(40);
var dv = new DataView(buffer);

var num = 0;
for(var i = 0; i < 40; i+=2) {
    dv.setUint16(i, num++);
}

那么ArrayBuffer有20个长度的Uint16Array。我想添加额外的二进制数据,而不是追加。假设我想像这样添加 5 个字节的 Uint8:

ArrayBuffer[0] = 10;
ArrayBuffer[1] = 20;
ArrayBuffer[2] = 30;
ArrayBuffer[3] = 40;
ArrayBuffer[4] = 50;
ArrayBuffer[5 ~ 25] = Original Data

据我所知,ArrayBuffer中没有prepending like方法,所以我自己做了。这无关紧要,但考虑字节顺序真的让我发疯。

前5个字节的数据是我生成的,所以我可以手动设置字节序,但原始数据来自外部,所以数据可能是小端和大端。

我正在为 Node.js 制作某种二进制数据模块,Web 浏览器都可以使用,基本用法是使用此模块从 Web 浏览器中添加额外的二进制数据,然后浏览器将其发送到服务器,然后服务器也从这个模块读取这个二进制数据,并拆分为前缀和原始。

但问题是,如果浏览器,我的意思是客户端使用小端,而服务器使用大端,原始数据可能无法正确读取,因为它们的字节序不同。

我像这样将原始数据附加到新的二进制数据。新的二进制数据在0~5字节有不同的5字节数据。所以我必须在 5 个字节偏移量之后写。

// write rest of data
var newBuffer = new ArrayBuffer(5 + origBuffer.byteLength);
var ndv = new DataView(newBuffer);
var dv = new DataView(origBuffer);    

for(var i = 0; i < origBuffer.byteLength; i++) {
    var offset = 5 + i;
    ndv.setUint8(offset, dv.getUint8(i));
}

在我的本地机器上测试没问题,因为服务器和客户端都使用相同的cpu,但在现实生活中,如果它们有不同的字节顺序,这个模块将无法正常工作。

是否有字节序安全的方法来使用 ArrayBuffer 和 DataView 复制二进制数据?或者我应该忘记字节序?任何建议将不胜感激。

由于您使用的不是带索引访问的 Uint16Array,而是 DataView 及其 setUint16 method,字节顺序始终默认为大端。

对于字节来说,这并不重要,因为它们没有字节序,但是如果你得到的数据比一个字节宽(16/32 位等),你必须提前知道字节序,因为没有办法检测原始字节顺序(您可以根据数据进行一些猜测,但是...)。

在大多数情况下,字节流采用所谓的网络顺序,基本上是 big-endian,DataView 默认为。

但是,我建议在向客户端发送任何数据之前实现一个测试功能。例如,使用 isLittleEndian() 函数从客户端调用服务器,触发服务器发回 16 位值,如 0xff00。然后测试接收字节的顺序 - 例如:

function isLittleEndian(callback) {  // callback as request will be async
  // get 0xff00 from server as 2 byte ArrayBuffer here somehow...
  var test = new Uint8Array(arraybufferFromServer);
  callback({isLittleEndian: !test[0]}); // will be 0x00ff as little-endian
}

然后可以将结果与使用小端标志的 DataView 一起用于各种方法。

请注意,这适用于数据流,不一定适用于服务器的 CPU architecture/endianess(小端服务器仍然(并且很可能)在 big-endian/network 中发送数据]顺序)。