はじめに
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
正しく判定できていそうであることがわかります。