Javascript 如何测试ArrayBuffer、DataView和TypedArray中的相等性

Javascript 如何测试ArrayBuffer、DataView和TypedArray中的相等性,javascript,arraybuffer,typed-arrays,Javascript,Arraybuffer,Typed Arrays,有没有办法测试两个JavaScript数组缓冲区是否相等?我想为消息编写方法编写测试。我找到的唯一方法是将ArrayBuffer转换为字符串,然后进行比较。我错过什么了吗 以下代码给出的是false,即使我认为它应该是true: (function() { 'use strict'; /* Fill buffer with data of Verse header and user_auth * command */ var buf_pos = 0;

有没有办法测试两个JavaScript数组缓冲区是否相等?我想为消息编写方法编写测试。我找到的唯一方法是将ArrayBuffer转换为字符串,然后进行比较。我错过什么了吗

以下代码给出的是false,即使我认为它应该是true:

(function() {
    'use strict';

    /* Fill buffer with data of Verse header and user_auth
     * command */
    var buf_pos = 0;
    var name_len = 6
    var message_len = 4 + 1 + 1 + 1 + name_len + 1;

    var buf = new ArrayBuffer(message_len);
    var view = new DataView(buf);
    /* Verse header starts with version */
    view.setUint8(buf_pos, 1 << 4); /* First 4 bits are reserved for version of protocol */
    buf_pos += 2;
    /* The lenght of the message */
    view.setUint16(buf_pos, message_len);
    buf_pos += 2;

    buf_pos = 0;
    var buf2 = new ArrayBuffer(message_len);
    var view2 = new DataView(buf);
    /* Verse header starts with version */
    view2.setUint8(buf_pos, 1 << 4); /* First 4 bits are reserved for version of protocol */
    buf_pos += 2;
    /* The lenght of the message */
    view2.setUint16(buf_pos, message_len);
    buf_pos += 2;


    if(buf == buf2){
        console.log('true');
    }
    else{
        console.log('false');
    }


}());
(函数(){
"严格使用",;
/*用Verse头和user_auth的数据填充缓冲区
*命令*/
var buf_pos=0;
变量名称\u len=6
var消息长度=4+1+1+1+名称长度+1;
var buf=新阵列缓冲(消息);
var视图=新数据视图(buf);
/*诗首以版本开头*/

view.setUint8(buf_pos,1您不能使用
=
===
在JavaScript中直接比较两个对象
这些运算符只检查引用的相等性(即表达式是否引用同一对象)

但是,您可以使用
DataView
ArrayView
对象来检索
ArrayBuffer
对象的特定部分的值并进行检查

如果要检查标题,请执行以下操作:

if (  view1.getUint8 (0) == view2.getUint8 (0)
   && view1.getUint16(2) == view2.getUint16(2)) ...
或者,如果要检查缓冲区的全局性:

function equal (buf1, buf2)
{
    if (buf1.byteLength != buf2.byteLength) return false;
    var dv1 = new Int8Array(buf1);
    var dv2 = new Int8Array(buf2);
    for (var i = 0 ; i != buf1.byteLength ; i++)
    {
        if (dv1[i] != dv2[i]) return false;
    }
    return true;
}

如果您想基于
ArrayBuffer
实现一个复杂的数据结构,我建议您创建自己的类,否则每次您想在结构中移入移出火柴棒时,都必须求助于笨重的原始
DataView
/
ArrayView
实例。

您总是可以将数组转换为字符串并进行比较

let a = new Uint8Array([1, 2, 3, 4]);
let b = new Uint8Array([1, 2, 3, 4]);
if (a.toString() == b.toString()) {
    console.log("Yes");
} else {
    console.log("No");
}

在一般javascript中,您当前必须比较两个ArrayBuffer对象,方法是使用TypedArray包装每个对象,然后手动迭代每个元素并执行元素相等

如果底层缓冲区是2字节或4字节内存对齐的,那么可以通过使用Uint16或Uint32类型的数组进行比较来进行重大优化

/**
 * compare two binary arrays for equality
 * @param {(ArrayBuffer|ArrayBufferView)} a
 * @param {(ArrayBuffer|ArrayBufferView)} b 
 */
function equal(a, b) {
  if (a instanceof ArrayBuffer) a = new Uint8Array(a, 0);
  if (b instanceof ArrayBuffer) b = new Uint8Array(b, 0);
  if (a.byteLength != b.byteLength) return false;
  if (aligned32(a) && aligned32(b))
    return equal32(a, b);
  if (aligned16(a) && aligned16(b))
    return equal16(a, b);
  return equal8(a, b);
}

function equal8(a, b) {
  const ua = new Uint8Array(a.buffer, a.byteOffset, a.byteLength);
  const ub = new Uint8Array(b.buffer, b.byteOffset, b.byteLength);
  return compare(ua, ub);
}
function equal16(a, b) {
  const ua = new Uint16Array(a.buffer, a.byteOffset, a.byteLength / 2);
  const ub = new Uint16Array(b.buffer, b.byteOffset, b.byteLength / 2);
  return compare(ua, ub);
}
function equal32(a, b) {
  const ua = new Uint32Array(a.buffer, a.byteOffset, a.byteLength / 4);
  const ub = new Uint32Array(b.buffer, b.byteOffset, b.byteLength / 4);
  return compare(ua, ub);
}

function compare(a, b) {
  for (let i = a.length; -1 < i; i -= 1) {
    if ((a[i] !== b[i])) return false;
  }
  return true;
}

function aligned16(a) {
  return (a.byteOffset % 2 === 0) && (a.byteLength % 2 === 0);
}

function aligned32(a) {
  return (a.byteOffset % 4 === 0) && (a.byteLength % 4 === 0);
}
对于1、2、4字节对齐内存

备选方案:

使用WASM还可以获得更高的性能,但将数据传输到堆的成本可能会抵消比较的好处


在Node.JS中,您可以使用
Buffer
获得更高的性能,因为它将具有本机代码:
Buffer.from(buf1,0).equals(Buffer.from(buf2,0))
在今天的V8中,
DataView
现在应该“可用于性能关键的实际应用程序”-

下面的函数基于您已经实例化的对象测试相等性。如果您已经有
TypedArray
对象,您可以直接比较它们,而无需为它们创建其他
DataView
对象(欢迎有人测量这两个选项的性能)

//比较ArrayBuffers
函数数组缓冲区相等(a,b){
返回dataViewsAreEqual(新DataView(a)、新DataView(b));
}
//比较数据视图
函数数据视图相等(a、b){
如果(a.byteLength!==b.byteLength)返回false;
for(设i=0;ival==b[i]);
}

<代码> > p>为了测试两种类型的DARDARIX之间的相等性,请考虑使用该方法,一旦发现不一致,该方法就退出:

const a=Uint8Array.from([0,1,2,3]);
常数b=Uint8Array.from([0,1,2,3]);
常数c=Uint8Array.from([0,1,2,3,4]);
常数等于=(第一,第二)=>
first.length==second.length&&first.every((值,索引)=>value==second[index]);
console.log(areEqual(a,b));
console.log(areEqual(a,c));

这比其他方法(如
toString()
comparations)更便宜,即使在发现差异后也会在剩余数组上迭代。

我编写这些函数是为了比较最普通的数据类型。它与ArrayBuffer、TypedArray、DataView、Node.js Buffer和任何具有字节数据(0-255)的普通数组一起工作

//它不会复制任何底层缓冲区,而是在其中创建一个视图。
函数dataToUint8Array(数据){
让uint8array
if(ArrayBuffer | | Array.isArray的数据实例(数据)){
uint8array=新的uint8array(数据)
}else if(缓冲区的数据实例){//Node.js Buffer
uint8array=新的uint8array(data.buffer、data.byteOffset、data.length)
}else if(ArrayBuffer.isView(data)){//DataView,TypedArray或Node.js Buffer
uint8array=新的uint8array(data.buffer、data.byteOffset、data.bytellength)
}否则{
抛出错误('数据不是ArrayBuffer、TypedArray、DataView或Node.js缓冲区')
}
返回uint8array
}
功能比较数据(a、b){
a=dataToUint8Array(a);b=dataToUint8Array(b)
如果(a.byteLength!=b.byteLength)返回false
返回a.every((val,i)=>val==b[i])
}

谢谢。这正是我需要做的-实现一组用于通过websocket处理消息的类。我希望测试涵盖方法和状态机。猜测一下……如果我们将两个数组都转换为字符串(比如base 64),它会起作用吗然后比较它们。考虑到使用
DataView
可以获得更好的性能,我怀疑这会更有效。请参阅下面的答案。如果其中一个缓冲区两边都用0填充,这可能不一定有效。如果
缓冲区填充为0,则返回字符串的长度将不会反映actu所有字符串长度,有字符串比较将不起作用。例如,
Buffer.from([80])。toString()
是“P”。
Buffer.from([80,0])。toString()
也是“P”,但第二个的长度是2。即使字符串显然只有一个字母。修剪字符串似乎也不起作用。此代码有两个错误:1.如果传递的键入数组不是
Int8Array
Uint8Array
U的实例
equal(buf1, buf2)
// compare ArrayBuffers
function arrayBuffersAreEqual(a, b) {
  return dataViewsAreEqual(new DataView(a), new DataView(b));
}

// compare DataViews
function dataViewsAreEqual(a, b) {
  if (a.byteLength !== b.byteLength) return false;
  for (let i=0; i < a.byteLength; i++) {
    if (a.getUint8(i) !== b.getUint8(i)) return false;
  }
  return true;
}

// compare TypedArrays
function typedArraysAreEqual(a, b) {
  if (a.byteLength !== b.byteLength) return false;
  return a.every((val, i) => val === b[i]);
}