21
21
'use strict' ;
22
22
23
23
const {
24
+ ArrayBuffer,
25
+ ArrayBufferIsView,
24
26
ArrayFrom,
25
27
ArrayIsArray,
26
28
ArrayPrototypeIndexOf,
@@ -38,6 +40,7 @@ const {
38
40
ObjectIs,
39
41
ObjectKeys,
40
42
ObjectPrototypeIsPrototypeOf,
43
+ ObjectPrototypeToString,
41
44
ReflectApply,
42
45
ReflectHas,
43
46
ReflectOwnKeys,
@@ -50,6 +53,7 @@ const {
50
53
StringPrototypeSlice,
51
54
StringPrototypeSplit,
52
55
SymbolIterator,
56
+ Uint8Array,
53
57
} = primordials ;
54
58
55
59
const {
@@ -73,6 +77,7 @@ const {
73
77
isDate,
74
78
isWeakSet,
75
79
isWeakMap,
80
+ isSharedArrayBuffer,
76
81
} = require ( 'internal/util/types' ) ;
77
82
const { isError, deprecate, emitExperimentalWarning } = require ( 'internal/util' ) ;
78
83
const { innerOk } = require ( 'internal/assert/utils' ) ;
@@ -369,9 +374,114 @@ function isSpecial(obj) {
369
374
}
370
375
371
376
const typesToCallDeepStrictEqualWith = [
372
- isKeyObject , isWeakSet , isWeakMap , Buffer . isBuffer ,
377
+ isKeyObject , isWeakSet , isWeakMap , Buffer . isBuffer , isSharedArrayBuffer ,
373
378
] ;
374
379
380
+ function compareMaps ( actual , expected , comparedObjects ) {
381
+ if ( actual . size !== expected . size ) {
382
+ return false ;
383
+ }
384
+ const safeIterator = FunctionPrototypeCall ( SafeMap . prototype [ SymbolIterator ] , actual ) ;
385
+
386
+ comparedObjects ??= new SafeWeakSet ( ) ;
387
+
388
+ for ( const { 0 : key , 1 : val } of safeIterator ) {
389
+ if ( ! MapPrototypeHas ( expected , key ) ) {
390
+ return false ;
391
+ }
392
+ if ( ! compareBranch ( val , MapPrototypeGet ( expected , key ) , comparedObjects ) ) {
393
+ return false ;
394
+ }
395
+ }
396
+ return true ;
397
+ }
398
+
399
+ function compareArrayBuffers ( actual , expected ) {
400
+ const actualView = ArrayBufferIsView ( actual ) ? actual : new Uint8Array ( actual ) ;
401
+ const expectedView = ArrayBufferIsView ( expected ) ? expected : new Uint8Array ( expected ) ;
402
+
403
+ if ( ObjectPrototypeToString ( actualView ) !== ObjectPrototypeToString ( expectedView ) ) {
404
+ return false ;
405
+ }
406
+
407
+ // Compare the lengths of the views (not just byte length, but actual element count)
408
+ if ( expectedView . length > actualView . length ) {
409
+ return false ;
410
+ }
411
+
412
+ for ( let i = 0 ; i < expectedView . length ; i ++ ) {
413
+ if ( actualView [ i ] !== expectedView [ i ] ) {
414
+ return false ;
415
+ }
416
+ }
417
+
418
+ return true ;
419
+ }
420
+
421
+ function compareSets ( actual , expected , comparedObjects ) {
422
+ if ( expected . size > actual . size ) {
423
+ return false ; // `expected` can't be a subset if it has more elements
424
+ }
425
+
426
+ if ( isDeepEqual === undefined ) lazyLoadComparison ( ) ;
427
+
428
+ const actualArray = ArrayFrom ( FunctionPrototypeCall ( SafeSet . prototype [ SymbolIterator ] , actual ) ) ;
429
+ const expectedIterator = FunctionPrototypeCall ( SafeSet . prototype [ SymbolIterator ] , expected ) ;
430
+ const usedIndices = new SafeSet ( ) ;
431
+
432
+ expectedIteration: for ( const expectedItem of expectedIterator ) {
433
+ for ( let actualIdx = 0 ; actualIdx < actualArray . length ; actualIdx ++ ) {
434
+ if ( ! usedIndices . has ( actualIdx ) && isDeepStrictEqual ( actualArray [ actualIdx ] , expectedItem ) ) {
435
+ usedIndices . add ( actualIdx ) ;
436
+ continue expectedIteration;
437
+ }
438
+ }
439
+ return false ;
440
+ }
441
+
442
+ return true ;
443
+ }
444
+
445
+ function compareArrays ( actual , expected , comparedObjects ) {
446
+ if ( expected . length > actual . length ) {
447
+ return false ;
448
+ }
449
+
450
+ if ( isDeepEqual === undefined ) lazyLoadComparison ( ) ;
451
+
452
+ // Create a map to count occurrences of each element in the expected array
453
+ const expectedCounts = new SafeMap ( ) ;
454
+ for ( const expectedItem of expected ) {
455
+ let found = false ;
456
+ for ( const { 0 : key , 1 : count } of expectedCounts ) {
457
+ if ( isDeepStrictEqual ( key , expectedItem ) ) {
458
+ MapPrototypeSet ( expectedCounts , key , count + 1 ) ;
459
+ found = true ;
460
+ break ;
461
+ }
462
+ }
463
+ if ( ! found ) {
464
+ MapPrototypeSet ( expectedCounts , expectedItem , 1 ) ;
465
+ }
466
+ }
467
+
468
+ // Create a map to count occurrences of relevant elements in the actual array
469
+ for ( const actualItem of actual ) {
470
+ for ( const { 0 : key , 1 : count } of expectedCounts ) {
471
+ if ( isDeepStrictEqual ( key , actualItem ) ) {
472
+ if ( count === 1 ) {
473
+ MapPrototypeDelete ( expectedCounts , key ) ;
474
+ } else {
475
+ MapPrototypeSet ( expectedCounts , key , count - 1 ) ;
476
+ }
477
+ break ;
478
+ }
479
+ }
480
+ }
481
+
482
+ return ! expectedCounts . size ;
483
+ }
484
+
375
485
/**
376
486
* Compares two objects or values recursively to check if they are equal.
377
487
* @param {any } actual - The actual value to compare.
@@ -388,22 +498,14 @@ function compareBranch(
388
498
) {
389
499
// Check for Map object equality
390
500
if ( isMap ( actual ) && isMap ( expected ) ) {
391
- if ( actual . size !== expected . size ) {
392
- return false ;
393
- }
394
- const safeIterator = FunctionPrototypeCall ( SafeMap . prototype [ SymbolIterator ] , actual ) ;
395
-
396
- comparedObjects ??= new SafeWeakSet ( ) ;
501
+ return compareMaps ( actual , expected , comparedObjects ) ;
502
+ }
397
503
398
- for ( const { 0 : key , 1 : val } of safeIterator ) {
399
- if ( ! MapPrototypeHas ( expected , key ) ) {
400
- return false ;
401
- }
402
- if ( ! compareBranch ( val , MapPrototypeGet ( expected , key ) , comparedObjects ) ) {
403
- return false ;
404
- }
405
- }
406
- return true ;
504
+ if (
505
+ ( ArrayBufferIsView ( actual ) && ArrayBufferIsView ( expected ) ) ||
506
+ ( actual instanceof ArrayBuffer && expected instanceof ArrayBuffer )
507
+ ) {
508
+ return compareArrayBuffers ( actual , expected ) ;
407
509
}
408
510
409
511
for ( const type of typesToCallDeepStrictEqualWith ) {
@@ -415,68 +517,12 @@ function compareBranch(
415
517
416
518
// Check for Set object equality
417
519
if ( isSet ( actual ) && isSet ( expected ) ) {
418
- if ( expected . size > actual . size ) {
419
- return false ; // `expected` can't be a subset if it has more elements
420
- }
421
-
422
- if ( isDeepEqual === undefined ) lazyLoadComparison ( ) ;
423
-
424
- const actualArray = ArrayFrom ( FunctionPrototypeCall ( SafeSet . prototype [ SymbolIterator ] , actual ) ) ;
425
- const expectedIterator = FunctionPrototypeCall ( SafeSet . prototype [ SymbolIterator ] , expected ) ;
426
- const usedIndices = new SafeSet ( ) ;
427
-
428
- expectedIteration: for ( const expectedItem of expectedIterator ) {
429
- for ( let actualIdx = 0 ; actualIdx < actualArray . length ; actualIdx ++ ) {
430
- if ( ! usedIndices . has ( actualIdx ) && isDeepStrictEqual ( actualArray [ actualIdx ] , expectedItem ) ) {
431
- usedIndices . add ( actualIdx ) ;
432
- continue expectedIteration;
433
- }
434
- }
435
- return false ;
436
- }
437
-
438
- return true ;
520
+ return compareSets ( actual , expected , comparedObjects ) ;
439
521
}
440
522
441
523
// Check if expected array is a subset of actual array
442
524
if ( ArrayIsArray ( actual ) && ArrayIsArray ( expected ) ) {
443
- if ( expected . length > actual . length ) {
444
- return false ;
445
- }
446
-
447
- if ( isDeepEqual === undefined ) lazyLoadComparison ( ) ;
448
-
449
- // Create a map to count occurrences of each element in the expected array
450
- const expectedCounts = new SafeMap ( ) ;
451
- for ( const expectedItem of expected ) {
452
- let found = false ;
453
- for ( const { 0 : key , 1 : count } of expectedCounts ) {
454
- if ( isDeepStrictEqual ( key , expectedItem ) ) {
455
- MapPrototypeSet ( expectedCounts , key , count + 1 ) ;
456
- found = true ;
457
- break ;
458
- }
459
- }
460
- if ( ! found ) {
461
- MapPrototypeSet ( expectedCounts , expectedItem , 1 ) ;
462
- }
463
- }
464
-
465
- // Create a map to count occurrences of relevant elements in the actual array
466
- for ( const actualItem of actual ) {
467
- for ( const { 0 : key , 1 : count } of expectedCounts ) {
468
- if ( isDeepStrictEqual ( key , actualItem ) ) {
469
- if ( count === 1 ) {
470
- MapPrototypeDelete ( expectedCounts , key ) ;
471
- } else {
472
- MapPrototypeSet ( expectedCounts , key , count - 1 ) ;
473
- }
474
- break ;
475
- }
476
- }
477
- }
478
-
479
- return ! expectedCounts . size ;
525
+ return compareArrays ( actual , expected , comparedObjects ) ;
480
526
}
481
527
482
528
// Comparison done when at least one of the values is not an object
0 commit comments