21

有没有办法测试两个 JavaScript ArrayBuffers 是否相等?我想为消息撰写方法编写测试。我发现的唯一方法是将 ArrayBuffer 转换为字符串,然后进行比较。我错过了什么?

以下代码给出了错误,即使我认为它应该是真的:

(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');
    }


}());

如果我尝试比较 view 和 view2 它又是错误的。

4

6 回答 6

21

您不能使用==或直接在 JavaScript 中比较两个对象===
这些运算符只会检查引用的相等性(即,如果表达式引用相同的对象)。

但是,您可以使用DataViewArrayView对象来检索对象特定部分的值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

于 2014-02-04T13:39:12.227 回答
7

在一般的 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);
}

并通过以下方式调用:

equal(buf1, buf2)

以下是1、2、4 字节对齐内存 的性能测试。

在此处输入图像描述 在此处输入图像描述

备择方案:

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

在 Node.JS 中,您可能会获得更高的性能,Buffer因为它将具有本机代码:Buffer.from(buf1, 0).equals(Buffer.from(buf2, 0))

于 2018-09-05T09:07:53.277 回答
3

在今天的 V8 中,DataView现在应该“可用于性能关键的实际应用程序” — https://v8.dev/blog/dataview

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

// 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]);
}
于 2018-10-30T14:20:10.527 回答
3

要测试两个 TypedArray 之间的相等性,请考虑使用every方法,一旦发现不一致就会退出:

const a = Uint8Array.from([0,1,2,3]);
const b = Uint8Array.from([0,1,2,3]);
const c = Uint8Array.from([0,1,2,3,4]);
const areEqual = (first, second) =>
    first.length === second.length && first.every((value, index) => value === second[index]);

console.log(areEqual(a, b));
console.log(areEqual(a, c));

toString()即使在发现差异之后,这也比在剩余数组上迭代的替代方案(如比较)便宜。

于 2020-03-23T16:56:58.110 回答
0

您始终可以将数组转换为字符串并进行比较。例如

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");
}
于 2017-07-25T11:36:16.843 回答
0

我编写了这些函数来比较最正常的数据类型。它适用于 ArrayBuffer、TypedArray、DataView、Node.js 缓冲区和任何带有字节数据 (0-255) 的普通数组。

// It will not copy any underlying buffers, instead it will create a view into them.
function dataToUint8Array(data) {
  let uint8array
  if (data instanceof ArrayBuffer || Array.isArray(data)) {
    uint8array = new Uint8Array(data)
  } else if (data instanceof Buffer) { // Node.js Buffer
    uint8array = new Uint8Array(data.buffer, data.byteOffset, data.length)
  } else if (ArrayBuffer.isView(data)) { // DataView, TypedArray or Node.js Buffer
    uint8array = new Uint8Array(data.buffer, data.byteOffset, data.byteLength)
  } else {
    throw Error('Data is not an ArrayBuffer, TypedArray, DataView or a Node.js Buffer.')
  }
  return uint8array
}

function compareData(a, b) {
  a = dataToUint8Array(a); b = dataToUint8Array(b)
  if (a.byteLength != b.byteLength) return false
  return a.every((val, i) => val == b[i])
}
于 2021-03-04T08:55:18.757 回答