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,
32- MapPrototypeDelete,
3337 MapPrototypeGet,
38+ MapPrototypeGetSize,
3439 MapPrototypeHas,
35- MapPrototypeSet,
3640 NumberIsNaN,
3741 ObjectAssign,
3842 ObjectIs,
3943 ObjectKeys,
4044 ObjectPrototypeIsPrototypeOf,
45+ ObjectPrototypeToString,
4146 ReflectApply,
4247 ReflectHas,
4348 ReflectOwnKeys,
4449 RegExpPrototypeExec,
50+ SafeArrayIterator,
4551 SafeMap,
4652 SafeSet,
4753 SafeWeakSet,
54+ SetPrototypeGetSize,
4855 String,
4956 StringPrototypeIndexOf,
5057 StringPrototypeSlice,
5158 StringPrototypeSplit,
5259 SymbolIterator,
60+ TypedArrayPrototypeGetLength,
61+ Uint8Array,
5362} = primordials ;
5463
5564const {
@@ -65,6 +74,8 @@ const AssertionError = require('internal/assert/assertion_error');
6574const { inspect } = require ( 'internal/util/inspect' ) ;
6675const { Buffer } = require ( 'buffer' ) ;
6776const {
77+ isArrayBuffer,
78+ isDataView,
6879 isKeyObject,
6980 isPromise,
7081 isRegExp,
@@ -73,6 +84,8 @@ const {
7384 isDate,
7485 isWeakSet,
7586 isWeakMap,
87+ isSharedArrayBuffer,
88+ isAnyArrayBuffer,
7689} = require ( 'internal/util/types' ) ;
7790const { isError, deprecate, emitExperimentalWarning } = require ( 'internal/util' ) ;
7891const { innerOk } = require ( 'internal/assert/utils' ) ;
@@ -369,9 +382,161 @@ function isSpecial(obj) {
369382}
370383
371384const typesToCallDeepStrictEqualWith = [
372- isKeyObject , isWeakSet , isWeakMap , Buffer . isBuffer ,
385+ isKeyObject , isWeakSet , isWeakMap , Buffer . isBuffer , isSharedArrayBuffer ,
373386] ;
374387
388+ function compareMaps ( actual , expected , comparedObjects ) {
389+ if ( MapPrototypeGetSize ( actual ) !== MapPrototypeGetSize ( expected ) ) {
390+ return false ;
391+ }
392+ const safeIterator = FunctionPrototypeCall ( SafeMap . prototype [ SymbolIterator ] , actual ) ;
393+
394+ comparedObjects ??= new SafeWeakSet ( ) ;
395+
396+ for ( const { 0 : key , 1 : val } of safeIterator ) {
397+ if ( ! MapPrototypeHas ( expected , key ) ) {
398+ return false ;
399+ }
400+ if ( ! compareBranch ( val , MapPrototypeGet ( expected , key ) , comparedObjects ) ) {
401+ return false ;
402+ }
403+ }
404+ return true ;
405+ }
406+
407+ function partiallyCompareArrayBuffersOrViews ( actual , expected ) {
408+ let actualView , expectedView , expectedViewLength ;
409+
410+ if ( ! ArrayBufferIsView ( actual ) ) {
411+ let actualViewLength ;
412+
413+ if ( isArrayBuffer ( actual ) && isArrayBuffer ( expected ) ) {
414+ actualViewLength = ArrayBufferPrototypeGetByteLength ( actual ) ;
415+ expectedViewLength = ArrayBufferPrototypeGetByteLength ( expected ) ;
416+ } else if ( isSharedArrayBuffer ( actual ) && isSharedArrayBuffer ( expected ) ) {
417+ actualViewLength = actual . byteLength ;
418+ expectedViewLength = expected . byteLength ;
419+ } else {
420+ // Cannot compare ArrayBuffers with SharedArrayBuffers
421+ return false ;
422+ }
423+
424+ if ( expectedViewLength > actualViewLength ) {
425+ return false ;
426+ }
427+ actualView = new Uint8Array ( actual ) ;
428+ expectedView = new Uint8Array ( expected ) ;
429+
430+ } else if ( isDataView ( actual ) ) {
431+ if ( ! isDataView ( expected ) ) {
432+ return false ;
433+ }
434+ const actualByteLength = DataViewPrototypeGetByteLength ( actual ) ;
435+ expectedViewLength = DataViewPrototypeGetByteLength ( expected ) ;
436+ if ( expectedViewLength > actualByteLength ) {
437+ return false ;
438+ }
439+
440+ actualView = new Uint8Array (
441+ DataViewPrototypeGetBuffer ( actual ) ,
442+ DataViewPrototypeGetByteOffset ( actual ) ,
443+ actualByteLength ,
444+ ) ;
445+ expectedView = new Uint8Array (
446+ DataViewPrototypeGetBuffer ( expected ) ,
447+ DataViewPrototypeGetByteOffset ( expected ) ,
448+ expectedViewLength ,
449+ ) ;
450+ } else {
451+ if ( ObjectPrototypeToString ( actual ) !== ObjectPrototypeToString ( expected ) ) {
452+ return false ;
453+ }
454+ actualView = actual ;
455+ expectedView = expected ;
456+ expectedViewLength = TypedArrayPrototypeGetLength ( expected ) ;
457+
458+ if ( expectedViewLength > TypedArrayPrototypeGetLength ( actual ) ) {
459+ return false ;
460+ }
461+ }
462+
463+ for ( let i = 0 ; i < expectedViewLength ; i ++ ) {
464+ if ( actualView [ i ] !== expectedView [ i ] ) {
465+ return false ;
466+ }
467+ }
468+
469+ return true ;
470+ }
471+
472+ function partiallyCompareSets ( actual , expected , comparedObjects ) {
473+ if ( SetPrototypeGetSize ( expected ) > SetPrototypeGetSize ( actual ) ) {
474+ return false ; // `expected` can't be a subset if it has more elements
475+ }
476+
477+ if ( isDeepEqual === undefined ) lazyLoadComparison ( ) ;
478+
479+ const actualArray = ArrayFrom ( FunctionPrototypeCall ( SafeSet . prototype [ SymbolIterator ] , actual ) ) ;
480+ const expectedIterator = FunctionPrototypeCall ( SafeSet . prototype [ SymbolIterator ] , expected ) ;
481+ const usedIndices = new SafeSet ( ) ;
482+
483+ expectedIteration: for ( const expectedItem of expectedIterator ) {
484+ for ( let actualIdx = 0 ; actualIdx < actualArray . length ; actualIdx ++ ) {
485+ if ( ! usedIndices . has ( actualIdx ) && isDeepStrictEqual ( actualArray [ actualIdx ] , expectedItem ) ) {
486+ usedIndices . add ( actualIdx ) ;
487+ continue expectedIteration;
488+ }
489+ }
490+ return false ;
491+ }
492+
493+ return true ;
494+ }
495+
496+ function partiallyCompareArrays ( actual , expected , comparedObjects ) {
497+ if ( expected . length > actual . length ) {
498+ return false ;
499+ }
500+
501+ if ( isDeepEqual === undefined ) lazyLoadComparison ( ) ;
502+
503+ // Create a map to count occurrences of each element in the expected array
504+ const expectedCounts = new SafeMap ( ) ;
505+ for ( const expectedItem of expected ) {
506+ let found = false ;
507+ for ( const { 0 : key , 1 : count } of expectedCounts ) {
508+ if ( isDeepStrictEqual ( key , expectedItem ) ) {
509+ expectedCounts . set ( key , count + 1 ) ;
510+ found = true ;
511+ break ;
512+ }
513+ }
514+ if ( ! found ) {
515+ expectedCounts . set ( expectedItem , 1 ) ;
516+ }
517+ }
518+
519+ const safeActual = new SafeArrayIterator ( actual ) ;
520+
521+ // Create a map to count occurrences of relevant elements in the actual array
522+ for ( const actualItem of safeActual ) {
523+ for ( const { 0 : key , 1 : count } of expectedCounts ) {
524+ if ( isDeepStrictEqual ( key , actualItem ) ) {
525+ if ( count === 1 ) {
526+ expectedCounts . delete ( key ) ;
527+ } else {
528+ expectedCounts . set ( key , count - 1 ) ;
529+ }
530+ break ;
531+ }
532+ }
533+ }
534+
535+ const { size } = expectedCounts ;
536+ expectedCounts . clear ( ) ;
537+ return size === 0 ;
538+ }
539+
375540/**
376541 * Compares two objects or values recursively to check if they are equal.
377542 * @param {any } actual - The actual value to compare.
@@ -388,22 +553,16 @@ function compareBranch(
388553) {
389554 // Check for Map object equality
390555 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 ( ) ;
556+ return compareMaps ( actual , expected , comparedObjects ) ;
557+ }
397558
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 ;
559+ if (
560+ ArrayBufferIsView ( actual ) ||
561+ isAnyArrayBuffer ( actual ) ||
562+ ArrayBufferIsView ( expected ) ||
563+ isAnyArrayBuffer ( expected )
564+ ) {
565+ return partiallyCompareArrayBuffersOrViews ( actual , expected ) ;
407566 }
408567
409568 for ( const type of typesToCallDeepStrictEqualWith ) {
@@ -415,68 +574,12 @@ function compareBranch(
415574
416575 // Check for Set object equality
417576 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 ;
577+ return partiallyCompareSets ( actual , expected , comparedObjects ) ;
439578 }
440579
441580 // Check if expected array is a subset of actual array
442581 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 ;
582+ return partiallyCompareArrays ( actual , expected , comparedObjects ) ;
480583 }
481584
482585 // Comparison done when at least one of the values is not an object
0 commit comments