Skip to content

Commit b4baf19

Browse files
committed
assert: Check typed array view type in deepEqual
Do not convert typed arrays to `Buffer` for deepEqual since their values may not be accurately represented by 8-bit ints. Instead perform binary comparison of underlying `ArrayBuffer`s, but only when the array types match. Never apply any kind of optimization for floating-point typed arrays since bit pattern equality is not the right kind of check for them. PR-URL: #5910 Reviewed-By: Benjamin Gruenbaum <[email protected]> Fixes: #5907
1 parent 1845c4f commit b4baf19

File tree

2 files changed

+27
-6
lines changed

2 files changed

+27
-6
lines changed

lib/assert.js

+13-4
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ const compare = process.binding('buffer').compare;
2929
const util = require('util');
3030
const Buffer = require('buffer').Buffer;
3131
const pSlice = Array.prototype.slice;
32+
const pToString = (obj) => Object.prototype.toString.call(obj);
3233

3334
// 1. The assert module provides functions that throw
3435
// AssertionError's when particular conditions are not met. The
@@ -170,10 +171,18 @@ function _deepEqual(actual, expected, strict) {
170171
(expected === null || typeof expected !== 'object')) {
171172
return strict ? actual === expected : actual == expected;
172173

173-
// If both values are instances of typed arrays, wrap them in
174-
// a Buffer each to increase performance
175-
} else if (ArrayBuffer.isView(actual) && ArrayBuffer.isView(expected)) {
176-
return compare(Buffer.from(actual), Buffer.from(expected)) === 0;
174+
// If both values are instances of typed arrays, wrap their underlying
175+
// ArrayBuffers in a Buffer each to increase performance
176+
// This optimization requires the arrays to have the same type as checked by
177+
// Object.prototype.toString (aka pToString). Never perform binary
178+
// comparisons for Float*Arrays, though, since e.g. +0 === -0 but their
179+
// bit patterns are not identical.
180+
} else if (ArrayBuffer.isView(actual) && ArrayBuffer.isView(expected) &&
181+
pToString(actual) === pToString(expected) &&
182+
!(actual instanceof Float32Array ||
183+
actual instanceof Float64Array)) {
184+
return compare(Buffer.from(actual.buffer),
185+
Buffer.from(expected.buffer)) === 0;
177186

178187
// 7.5 For all other Object pairs, including Array objects, equivalence is
179188
// determined by having the same number of owned properties (as verified

test/parallel/test-assert-typedarray-deepequal.js

+14-2
Original file line numberDiff line numberDiff line change
@@ -20,13 +20,25 @@ const equalArrayPairs = [
2020
[new Int16Array(1e5), new Int16Array(1e5)],
2121
[new Int32Array(1e5), new Int32Array(1e5)],
2222
[new Float32Array(1e5), new Float32Array(1e5)],
23-
[new Float64Array(1e5), new Float64Array(1e5)]
23+
[new Float64Array(1e5), new Float64Array(1e5)],
24+
[new Int16Array(256), new Uint16Array(256)],
25+
[new Int16Array([256]), new Uint16Array([256])],
26+
[new Float32Array([+0.0]), new Float32Array([-0.0])],
27+
[new Float64Array([+0.0]), new Float32Array([-0.0])],
28+
[new Float64Array([+0.0]), new Float64Array([-0.0])]
2429
];
2530

2631
const notEqualArrayPairs = [
2732
[new Uint8Array(2), new Uint8Array(3)],
2833
[new Uint8Array([1, 2, 3]), new Uint8Array([4, 5, 6])],
29-
[new Uint8ClampedArray([300, 2, 3]), new Uint8Array([300, 2, 3])]
34+
[new Uint8ClampedArray([300, 2, 3]), new Uint8Array([300, 2, 3])],
35+
[new Uint16Array([2]), new Uint16Array([3])],
36+
[new Uint16Array([0]), new Uint16Array([256])],
37+
[new Int16Array([0]), new Uint16Array([256])],
38+
[new Int16Array([-256]), new Uint16Array([0xff00])], // same bits
39+
[new Int32Array([-256]), new Uint32Array([0xffffff00])], // ditto
40+
[new Float32Array([0.1]), new Float32Array([0.0])],
41+
[new Float64Array([0.1]), new Float64Array([0.0])]
3042
];
3143

3244
equalArrayPairs.forEach((arrayPair) => {

0 commit comments

Comments
 (0)