はじめに
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
view1とview2は中身が異なるにもかかわらず、中身が同じだと判定されています。
また、view2とview3は中身が同じであるにもかかわらず、中身が異なると判定されています。
何がいけなかったのか
DataViewは、以下のプロパティを持ちます。
-
buffer: 参照するArrayBuffer -
byteOffset:ArrayBufferの中で参照を開始する位置 -
byteLength:ArrayBufferの中で参照する長さ
今回の例では、各DataViewは以下のようにArrayBufferを参照しています。
一方、生成された compareDataViews 関数は byteOffset を無視して buffer を参照しています。
すると、view2 も view1 と同様に data1 の先頭から参照していると間違えてしまいます。
そのため、間違った判定結果が得られてしまいます。
どうすればよかったか
Uint8Array に変換する際に、buffer だけでなく byteOffset や byteLength も参照します。
ついでに、関数名も改善しておきます。
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
正しく判定できていそうであることがわかります。

