2121'use strict' ;
2222
2323const {
24+ ArrayBuffer,
25+ ArrayBufferIsView,
2426 ArrayFrom,
2527 ArrayIsArray,
2628 ArrayPrototypeIndexOf,
@@ -38,6 +40,7 @@ const {
3840 ObjectIs,
3941 ObjectKeys,
4042 ObjectPrototypeIsPrototypeOf,
43+ ObjectPrototypeToString,
4144 ReflectApply,
4245 ReflectHas,
4346 ReflectOwnKeys,
@@ -50,6 +53,7 @@ const {
5053 StringPrototypeSlice,
5154 StringPrototypeSplit,
5255 SymbolIterator,
56+ Uint8Array,
5357} = primordials ;
5458
5559const {
@@ -73,6 +77,7 @@ const {
7377 isDate,
7478 isWeakSet,
7579 isWeakMap,
80+ isSharedArrayBuffer,
7681} = require ( 'internal/util/types' ) ;
7782const { isError, deprecate, emitExperimentalWarning } = require ( 'internal/util' ) ;
7883const { innerOk } = require ( 'internal/assert/utils' ) ;
@@ -369,9 +374,114 @@ function isSpecial(obj) {
369374}
370375
371376const typesToCallDeepStrictEqualWith = [
372- isKeyObject , isWeakSet , isWeakMap , Buffer . isBuffer ,
377+ isKeyObject , isWeakSet , isWeakMap , Buffer . isBuffer , isSharedArrayBuffer ,
373378] ;
374379
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+
375485/**
376486 * Compares two objects or values recursively to check if they are equal.
377487 * @param {any } actual - The actual value to compare.
@@ -388,22 +498,14 @@ function compareBranch(
388498) {
389499 // Check for Map object equality
390500 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+ }
397503
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 ) ;
407509 }
408510
409511 for ( const type of typesToCallDeepStrictEqualWith ) {
@@ -415,68 +517,12 @@ function compareBranch(
415517
416518 // Check for Set object equality
417519 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 ) ;
439521 }
440522
441523 // Check if expected array is a subset of actual array
442524 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 ) ;
480526 }
481527
482528 // Comparison done when at least one of the values is not an object
0 commit comments