2121'use strict' ;
2222
2323const {
24+ ArrayBufferIsView,
25+ ArrayBufferPrototypeGetByteLength,
2426 ArrayFrom,
2527 ArrayIsArray,
2628 ArrayPrototypeIndexOf,
2729 ArrayPrototypeJoin,
2830 ArrayPrototypePush,
2931 ArrayPrototypeSlice,
32+ DataViewPrototypeGetBuffer,
33+ DataViewPrototypeGetByteLength,
34+ DataViewPrototypeGetByteOffset,
3035 Error,
3136 FunctionPrototypeCall,
3237 MapPrototypeDelete,
@@ -38,6 +43,7 @@ const {
3843 ObjectIs,
3944 ObjectKeys,
4045 ObjectPrototypeIsPrototypeOf,
46+ ObjectPrototypeToString,
4147 ReflectApply,
4248 ReflectHas,
4349 ReflectOwnKeys,
@@ -50,6 +56,8 @@ const {
5056 StringPrototypeSlice,
5157 StringPrototypeSplit,
5258 SymbolIterator,
59+ TypedArrayPrototypeGetLength,
60+ Uint8Array,
5361} = primordials ;
5462
5563const {
@@ -65,6 +73,8 @@ const AssertionError = require('internal/assert/assertion_error');
6573const { inspect } = require ( 'internal/util/inspect' ) ;
6674const { Buffer } = require ( 'buffer' ) ;
6775const {
76+ isArrayBuffer,
77+ isDataView,
6878 isKeyObject,
6979 isPromise,
7080 isRegExp,
@@ -73,6 +83,8 @@ const {
7383 isDate,
7484 isWeakSet,
7585 isWeakMap,
86+ isSharedArrayBuffer,
87+ isAnyArrayBuffer,
7688} = require ( 'internal/util/types' ) ;
7789const { isError, deprecate, emitExperimentalWarning } = require ( 'internal/util' ) ;
7890const { innerOk } = require ( 'internal/assert/utils' ) ;
@@ -369,9 +381,157 @@ function isSpecial(obj) {
369381}
370382
371383const typesToCallDeepStrictEqualWith = [
372- isKeyObject , isWeakSet , isWeakMap , Buffer . isBuffer ,
384+ isKeyObject , isWeakSet , isWeakMap , Buffer . isBuffer , isSharedArrayBuffer ,
373385] ;
374386
387+ function compareMaps ( actual , expected , comparedObjects ) {
388+ if ( actual . size !== expected . size ) {
389+ return false ;
390+ }
391+ const safeIterator = FunctionPrototypeCall ( SafeMap . prototype [ SymbolIterator ] , actual ) ;
392+
393+ comparedObjects ??= new SafeWeakSet ( ) ;
394+
395+ for ( const { 0 : key , 1 : val } of safeIterator ) {
396+ if ( ! MapPrototypeHas ( expected , key ) ) {
397+ return false ;
398+ }
399+ if ( ! compareBranch ( val , MapPrototypeGet ( expected , key ) , comparedObjects ) ) {
400+ return false ;
401+ }
402+ }
403+ return true ;
404+ }
405+
406+ function compareArrayBuffers ( actual , expected ) {
407+ let actualView , expectedView , expectedViewLength ;
408+
409+ if ( ! ArrayBufferIsView ( actual ) ) {
410+ let actualViewLength ;
411+
412+ if ( isArrayBuffer ( actual ) && isArrayBuffer ( expected ) ) {
413+ actualViewLength = ArrayBufferPrototypeGetByteLength ( actual ) ;
414+ expectedViewLength = ArrayBufferPrototypeGetByteLength ( expected ) ;
415+ } else if ( isSharedArrayBuffer ( actual ) && isSharedArrayBuffer ( expected ) ) {
416+ actualViewLength = actual . byteLength ;
417+ expectedViewLength = expected . byteLength ;
418+ } else {
419+ // Cannot compare ArrayBuffers with SharedArrayBuffers
420+ return false ;
421+ }
422+
423+ if ( expectedViewLength > actualViewLength ) {
424+ return false ;
425+ }
426+ actualView = new Uint8Array ( actual ) ;
427+ expectedView = new Uint8Array ( expected ) ;
428+
429+ } else if ( isDataView ( actual ) ) {
430+ if ( ! isDataView ( expected ) ) {
431+ return false ;
432+ }
433+ const actualByteLength = DataViewPrototypeGetByteLength ( actual ) ;
434+ expectedViewLength = DataViewPrototypeGetByteLength ( expected ) ;
435+ if ( expectedViewLength > actualByteLength ) {
436+ return false ;
437+ }
438+
439+ actualView = new Uint8Array (
440+ DataViewPrototypeGetBuffer ( actual ) ,
441+ DataViewPrototypeGetByteOffset ( actual ) ,
442+ actualByteLength ,
443+ ) ;
444+ expectedView = new Uint8Array (
445+ DataViewPrototypeGetBuffer ( expected ) ,
446+ DataViewPrototypeGetByteOffset ( expected ) ,
447+ expectedViewLength ,
448+ ) ;
449+ } else {
450+ if ( ObjectPrototypeToString ( actual ) !== ObjectPrototypeToString ( expected ) ) {
451+ return false ;
452+ }
453+ actualView = actual ;
454+ expectedView = expected ;
455+ expectedViewLength = TypedArrayPrototypeGetLength ( expected ) ;
456+
457+ if ( expectedViewLength > TypedArrayPrototypeGetLength ( actual ) ) {
458+ return false ;
459+ }
460+ }
461+
462+ for ( let i = 0 ; i < expectedViewLength ; i ++ ) {
463+ if ( actualView [ i ] !== expectedView [ i ] ) {
464+ return false ;
465+ }
466+ }
467+
468+ return true ;
469+ }
470+
471+ function compareSets ( actual , expected , comparedObjects ) {
472+ if ( expected . size > actual . size ) {
473+ return false ; // `expected` can't be a subset if it has more elements
474+ }
475+
476+ if ( isDeepEqual === undefined ) lazyLoadComparison ( ) ;
477+
478+ const actualArray = ArrayFrom ( FunctionPrototypeCall ( SafeSet . prototype [ SymbolIterator ] , actual ) ) ;
479+ const expectedIterator = FunctionPrototypeCall ( SafeSet . prototype [ SymbolIterator ] , expected ) ;
480+ const usedIndices = new SafeSet ( ) ;
481+
482+ expectedIteration: for ( const expectedItem of expectedIterator ) {
483+ for ( let actualIdx = 0 ; actualIdx < actualArray . length ; actualIdx ++ ) {
484+ if ( ! usedIndices . has ( actualIdx ) && isDeepStrictEqual ( actualArray [ actualIdx ] , expectedItem ) ) {
485+ usedIndices . add ( actualIdx ) ;
486+ continue expectedIteration;
487+ }
488+ }
489+ return false ;
490+ }
491+
492+ return true ;
493+ }
494+
495+ function compareArrays ( actual , expected , comparedObjects ) {
496+ if ( expected . length > actual . length ) {
497+ return false ;
498+ }
499+
500+ if ( isDeepEqual === undefined ) lazyLoadComparison ( ) ;
501+
502+ // Create a map to count occurrences of each element in the expected array
503+ const expectedCounts = new SafeMap ( ) ;
504+ for ( const expectedItem of expected ) {
505+ let found = false ;
506+ for ( const { 0 : key , 1 : count } of expectedCounts ) {
507+ if ( isDeepStrictEqual ( key , expectedItem ) ) {
508+ MapPrototypeSet ( expectedCounts , key , count + 1 ) ;
509+ found = true ;
510+ break ;
511+ }
512+ }
513+ if ( ! found ) {
514+ MapPrototypeSet ( expectedCounts , expectedItem , 1 ) ;
515+ }
516+ }
517+
518+ // Create a map to count occurrences of relevant elements in the actual array
519+ for ( const actualItem of actual ) {
520+ for ( const { 0 : key , 1 : count } of expectedCounts ) {
521+ if ( isDeepStrictEqual ( key , actualItem ) ) {
522+ if ( count === 1 ) {
523+ MapPrototypeDelete ( expectedCounts , key ) ;
524+ } else {
525+ MapPrototypeSet ( expectedCounts , key , count - 1 ) ;
526+ }
527+ break ;
528+ }
529+ }
530+ }
531+
532+ return ! expectedCounts . size ;
533+ }
534+
375535/**
376536 * Compares two objects or values recursively to check if they are equal.
377537 * @param {any } actual - The actual value to compare.
@@ -388,22 +548,16 @@ function compareBranch(
388548) {
389549 // Check for Map object equality
390550 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 ( ) ;
551+ return compareMaps ( actual , expected , comparedObjects ) ;
552+ }
397553
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 ;
554+ if (
555+ ArrayBufferIsView ( actual ) ||
556+ isAnyArrayBuffer ( actual ) ||
557+ ArrayBufferIsView ( expected ) ||
558+ isAnyArrayBuffer ( expected )
559+ ) {
560+ return compareArrayBuffers ( actual , expected ) ;
407561 }
408562
409563 for ( const type of typesToCallDeepStrictEqualWith ) {
@@ -415,68 +569,12 @@ function compareBranch(
415569
416570 // Check for Set object equality
417571 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 ;
572+ return compareSets ( actual , expected , comparedObjects ) ;
439573 }
440574
441575 // Check if expected array is a subset of actual array
442576 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 ;
577+ return compareArrays ( actual , expected , comparedObjects ) ;
480578 }
481579
482580 // Comparison done when at least one of the values is not an object
0 commit comments