@@ -55,54 +55,57 @@ impl<A, B> SlicePartialEq<B> for [A]
55
55
where
56
56
A : PartialEq < B > ,
57
57
{
58
- #[ inline]
59
58
default fn equal ( & self , other : & [ B ] ) -> bool {
60
59
if self . len ( ) != other. len ( ) {
61
60
return false ;
62
61
}
63
62
64
- // at least 8 items for unrolling to make sense (4 peeled + 4+ unrolled)
65
- if self . len ( ) < 8 {
66
- return eq_small ( self , other) ;
63
+ if self . len ( ) == 0 {
64
+ return true ;
67
65
}
68
66
69
- eq_unroll ( self , other)
70
- }
71
- }
67
+ // ZSTs have no identity and slices don't guarantee which addresses-to-ZSTs they produce
68
+ // so we only need to compare them once to determine the behavior of the PartialEq impl
69
+ if const { mem:: size_of :: < A > ( ) == 0 && mem:: size_of :: < B > ( ) == 0 } {
70
+ // zero-length slices are always equal
71
+ // SAFETY: A and B are ZSTs so it's ok to conjure them out of thin air
72
+ return unsafe { mem:: zeroed :: < A > ( ) == mem:: zeroed :: < B > ( ) } ;
73
+ }
72
74
73
- #[ inline]
74
- fn eq_small < A , B > ( a : & [ A ] , b : & [ B ] ) -> bool
75
- where
76
- A : PartialEq < B > ,
77
- {
78
- a. iter ( ) . zip ( b) . all ( |( a, b) | a == b)
79
- }
75
+ const UNROLL : usize = 4 ;
76
+ let mut i = 0 ;
77
+ let mut is_eq = true ;
78
+
79
+ let a = self . as_ptr ( ) ;
80
+ let b = other. as_ptr ( ) ;
81
+ let len = self . len ( ) ;
82
+
83
+ // compare items 1 by 1 in case comparisons are expensive. at least one item, then
84
+ // until the remainder is a multiple of UNROLL
85
+ loop {
86
+ // SAFETY: slices are of the same length and loop conditions ensure indexes are in bounds
87
+ unsafe {
88
+ is_eq = is_eq & PartialEq :: eq ( & * a. add ( i) , & * b. add ( i) ) ;
89
+ i = i. unchecked_add ( 1 ) ;
90
+ }
80
91
81
- fn eq_unroll < A , B > ( a : & [ A ] , b : & [ B ] ) -> bool
82
- where
83
- A : PartialEq < B > ,
84
- {
85
- let ( mut chunks_a, residual_a) = a. as_chunks :: < 4 > ( ) ;
86
- let ( mut chunks_b, residual_b) = b. as_chunks :: < 4 > ( ) ;
87
- let peeled_a = chunks_a. take_first ( ) . unwrap ( ) ;
88
- let peeled_b = chunks_b. take_first ( ) . unwrap ( ) ;
89
-
90
- // peel the first chunk and do a short-circuiting comparison to bail early on mismatches
91
- // in case comparisons are expensive
92
- let mut result = eq_small ( peeled_a, peeled_b) ;
93
-
94
- // then check the residual, another chance to bail early
95
- result = result && eq_small ( residual_a, residual_b) ;
96
-
97
- // iter.all short-circuits which means the backend can't unroll the loop due to early exits.
98
- // So we unroll it manually.
99
- result = result
100
- && chunks_a
101
- . iter ( )
102
- . zip ( chunks_b)
103
- . all ( |( a, b) | ( a[ 0 ] == b[ 0 ] ) & ( a[ 1 ] == b[ 1 ] ) & ( a[ 2 ] == b[ 2 ] ) & ( a[ 3 ] == b[ 3 ] ) ) ;
104
-
105
- result
92
+ if !is_eq || i == len || ( len - i) % UNROLL == 0 {
93
+ break ;
94
+ }
95
+ }
96
+ while is_eq && i + UNROLL <= len {
97
+ // SAFETY: slices are of the same length and loop conditions ensure indexes are in bounds
98
+ unsafe {
99
+ is_eq = is_eq & PartialEq :: eq ( & * a. add ( i) , & * b. add ( i) ) ;
100
+ is_eq = is_eq & PartialEq :: eq ( & * a. add ( i + 1 ) , & * b. add ( i + 1 ) ) ;
101
+ is_eq = is_eq & PartialEq :: eq ( & * a. add ( i + 2 ) , & * b. add ( i + 2 ) ) ;
102
+ is_eq = is_eq & PartialEq :: eq ( & * a. add ( i + 3 ) , & * b. add ( i + 3 ) ) ;
103
+ i = i. unchecked_add ( UNROLL ) ;
104
+ }
105
+ }
106
+
107
+ is_eq
108
+ }
106
109
}
107
110
108
111
// When each element can be compared byte-wise, we can compare all the bytes
0 commit comments