2
0

More than 1 year has passed since last update.

【AI Programmerは失敗】2個のDataViewの中身が同じかを判定する関数

Posted at

はじめに

JavaScriptで2個のDataViewの中身が同じかを判定したくなりました。
ここで、「中身が同じ」とは「データの内容が同じ」を意味し、
参照しているArrayBufferやその中の範囲は違っても「同じ」になりうることとします。

うまく動かないコード

AI Programmer に、JavaScriptの

2個のDataViewの中身が同じかを判定する関数

を頼んだところ、以下のコードが出力されました。

function compareDataViews(view1, view2) {
  if (view1.byteLength !== view2.byteLength) {
    return false;
  }
  const view1Array = new Uint8Array(view1.buffer);
  const view2Array = new Uint8Array(view2.buffer);
  for (let i = 0; i < view1Array.length; i++) {
    if (view1Array[i] !== view2Array[i]) {
      return false;
    }
  }
  return true;
}

残念ながら、このコードは正しくありません。
例えば、以下の入力を与えてみます。

const data1 = new Uint8Array([0x12, 0x34, 0x56, 0x78]).buffer;
const data2 = new Uint8Array([0x56, 0x78]).buffer;

const view1 = new DataView(data1, 0, 2);
const view2 = new DataView(data1, 2, 2);
const view3 = new DataView(data2);

console.log("view1 : " + view1.getUint16(0).toString(16));
console.log("view2 : " + view2.getUint16(0).toString(16));
console.log("view3 : " + view3.getUint16(0).toString(16));
console.log("view1 == view2 : " + compareDataViews(view1, view2));
console.log("view2 == view3 : " + compareDataViews(view2, view3));
console.log("view1 == view3 : " + compareDataViews(view1, view3));

iUMUUW - Online JavaScript Interpreter & Debugging Tool - Ideone.com

以下の実行結果が得られました。

view1 : 1234
view2 : 5678
view3 : 5678
view1 == view2 : true
view2 == view3 : false
view1 == view3 : false

view1view2は中身が異なるにもかかわらず、中身が同じだと判定されています。
また、view2view3は中身が同じであるにもかかわらず、中身が異なると判定されています。

何がいけなかったのか

DataView - JavaScript | MDN

DataViewは、以下のプロパティを持ちます。

  • buffer : 参照するArrayBuffer
  • byteOffset : ArrayBufferの中で参照を開始する位置
  • byteLength : ArrayBufferの中で参照する長さ

今回の例では、各DataViewは以下のようにArrayBufferを参照しています。

dataview-20221004.drawio.png

一方、生成された compareDataViews 関数は byteOffset を無視して buffer を参照しています。
すると、view2view1 と同様に data1 の先頭から参照していると間違えてしまいます。

dataview-wrong-20221004.drawio.png

そのため、間違った判定結果が得られてしまいます。

どうすればよかったか

Uint8Array に変換する際に、buffer だけでなく byteOffsetbyteLength も参照します。
ついでに、関数名も改善しておきます。

function isDataViewsEquals(view1, view2) {
  if (view1.byteLength !== view2.byteLength) {
    return false;
  }
  const view1Array = new Uint8Array(view1.buffer, view1.byteOffset, view1.byteLength);
  const view2Array = new Uint8Array(view2.buffer, view2.byteOffset, view2.byteLength);
  for (let i = 0; i < view1Array.length; i++) {
    if (view1Array[i] !== view2Array[i]) {
      return false;
    }
  }
  return true;
}

const data1 = new Uint8Array([0x12, 0x34, 0x56, 0x78]).buffer;
const data2 = new Uint8Array([0x56, 0x78]).buffer;

const view1 = new DataView(data1, 0, 2);
const view2 = new DataView(data1, 2, 2);
const view3 = new DataView(data2);

console.log("view1 : " + view1.getUint16(0).toString(16));
console.log("view2 : " + view2.getUint16(0).toString(16));
console.log("view3 : " + view3.getUint16(0).toString(16));
console.log("view1 == view2 : " + isDataViewsEquals(view1, view2));
console.log("view2 == view3 : " + isDataViewsEquals(view2, view3));
console.log("view1 == view3 : " + isDataViewsEquals(view1, view3));

AECB0S - Online JavaScript Interpreter & Debugging Tool - Ideone.com

以下の実行結果が得られました。

view1 : 1234
view2 : 5678
view3 : 5678
view1 == view2 : false
view2 == view3 : true
view1 == view3 : false

正しく判定できていそうであることがわかります。

2
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
0