1 | //! Comparison traits for `[T]`. |
2 | |
3 | use super::{from_raw_parts, memchr}; |
4 | use crate::ascii; |
5 | use crate::cmp::{self, BytewiseEq, Ordering}; |
6 | use crate::intrinsics::compare_bytes; |
7 | use crate::num::NonZero; |
8 | use crate::ops::ControlFlow; |
9 | |
10 | #[stable (feature = "rust1" , since = "1.0.0" )] |
11 | impl<T, U> PartialEq<[U]> for [T] |
12 | where |
13 | T: PartialEq<U>, |
14 | { |
15 | fn eq(&self, other: &[U]) -> bool { |
16 | SlicePartialEq::equal(self, other) |
17 | } |
18 | |
19 | fn ne(&self, other: &[U]) -> bool { |
20 | SlicePartialEq::not_equal(self, other) |
21 | } |
22 | } |
23 | |
24 | #[stable (feature = "rust1" , since = "1.0.0" )] |
25 | impl<T: Eq> Eq for [T] {} |
26 | |
27 | /// Implements comparison of slices [lexicographically](Ord#lexicographical-comparison). |
28 | #[stable (feature = "rust1" , since = "1.0.0" )] |
29 | impl<T: Ord> Ord for [T] { |
30 | fn cmp(&self, other: &[T]) -> Ordering { |
31 | SliceOrd::compare(self, right:other) |
32 | } |
33 | } |
34 | |
35 | #[inline ] |
36 | fn as_underlying(x: ControlFlow<bool>) -> u8 { |
37 | // SAFETY: This will only compile if `bool` and `ControlFlow<bool>` have the same |
38 | // size (which isn't guaranteed but this is libcore). Because they have the same |
39 | // size, it's a niched implementation, which in one byte means there can't be |
40 | // any uninitialized memory. The callers then only check for `0` or `1` from this, |
41 | // which must necessarily match the `Break` variant, and we're fine no matter |
42 | // what ends up getting picked as the value representing `Continue(())`. |
43 | unsafe { crate::mem::transmute(src:x) } |
44 | } |
45 | |
46 | /// Implements comparison of slices [lexicographically](Ord#lexicographical-comparison). |
47 | #[stable (feature = "rust1" , since = "1.0.0" )] |
48 | impl<T: PartialOrd> PartialOrd for [T] { |
49 | #[inline ] |
50 | fn partial_cmp(&self, other: &[T]) -> Option<Ordering> { |
51 | SlicePartialOrd::partial_compare(self, other) |
52 | } |
53 | #[inline ] |
54 | fn lt(&self, other: &Self) -> bool { |
55 | // This is certainly not the obvious way to implement these methods. |
56 | // Unfortunately, using anything that looks at the discriminant means that |
57 | // LLVM sees a check for `2` (aka `ControlFlow<bool>::Continue(())`) and |
58 | // gets very distracted by that, ending up generating extraneous code. |
59 | // This should be changed to something simpler once either LLVM is smarter, |
60 | // see <https://github.com/llvm/llvm-project/issues/132678>, or we generate |
61 | // niche discriminant checks in a way that doesn't trigger it. |
62 | |
63 | as_underlying(self.__chaining_lt(other)) == 1 |
64 | } |
65 | #[inline ] |
66 | fn le(&self, other: &Self) -> bool { |
67 | as_underlying(self.__chaining_le(other)) != 0 |
68 | } |
69 | #[inline ] |
70 | fn gt(&self, other: &Self) -> bool { |
71 | as_underlying(self.__chaining_gt(other)) == 1 |
72 | } |
73 | #[inline ] |
74 | fn ge(&self, other: &Self) -> bool { |
75 | as_underlying(self.__chaining_ge(other)) != 0 |
76 | } |
77 | #[inline ] |
78 | fn __chaining_lt(&self, other: &Self) -> ControlFlow<bool> { |
79 | SliceChain::chaining_lt(self, other) |
80 | } |
81 | #[inline ] |
82 | fn __chaining_le(&self, other: &Self) -> ControlFlow<bool> { |
83 | SliceChain::chaining_le(self, other) |
84 | } |
85 | #[inline ] |
86 | fn __chaining_gt(&self, other: &Self) -> ControlFlow<bool> { |
87 | SliceChain::chaining_gt(self, other) |
88 | } |
89 | #[inline ] |
90 | fn __chaining_ge(&self, other: &Self) -> ControlFlow<bool> { |
91 | SliceChain::chaining_ge(self, other) |
92 | } |
93 | } |
94 | |
95 | #[doc (hidden)] |
96 | // intermediate trait for specialization of slice's PartialEq |
97 | trait SlicePartialEq<B> { |
98 | fn equal(&self, other: &[B]) -> bool; |
99 | |
100 | fn not_equal(&self, other: &[B]) -> bool { |
101 | !self.equal(other) |
102 | } |
103 | } |
104 | |
105 | // Generic slice equality |
106 | impl<A, B> SlicePartialEq<B> for [A] |
107 | where |
108 | A: PartialEq<B>, |
109 | { |
110 | default fn equal(&self, other: &[B]) -> bool { |
111 | if self.len() != other.len() { |
112 | return false; |
113 | } |
114 | |
115 | // Implemented as explicit indexing rather |
116 | // than zipped iterators for performance reasons. |
117 | // See PR https://github.com/rust-lang/rust/pull/116846 |
118 | for idx: usize in 0..self.len() { |
119 | // bound checks are optimized away |
120 | if self[idx] != other[idx] { |
121 | return false; |
122 | } |
123 | } |
124 | |
125 | true |
126 | } |
127 | } |
128 | |
129 | // When each element can be compared byte-wise, we can compare all the bytes |
130 | // from the whole size in one call to the intrinsics. |
131 | impl<A, B> SlicePartialEq<B> for [A] |
132 | where |
133 | A: BytewiseEq<B>, |
134 | { |
135 | fn equal(&self, other: &[B]) -> bool { |
136 | if self.len() != other.len() { |
137 | return false; |
138 | } |
139 | |
140 | // SAFETY: `self` and `other` are references and are thus guaranteed to be valid. |
141 | // The two slices have been checked to have the same size above. |
142 | unsafe { |
143 | let size: usize = size_of_val(self); |
144 | compare_bytes(self.as_ptr() as *const u8, right:other.as_ptr() as *const u8, bytes:size) == 0 |
145 | } |
146 | } |
147 | } |
148 | |
149 | #[doc (hidden)] |
150 | // intermediate trait for specialization of slice's PartialOrd |
151 | trait SlicePartialOrd: Sized { |
152 | fn partial_compare(left: &[Self], right: &[Self]) -> Option<Ordering>; |
153 | } |
154 | |
155 | #[doc (hidden)] |
156 | // intermediate trait for specialization of slice's PartialOrd chaining methods |
157 | trait SliceChain: Sized { |
158 | fn chaining_lt(left: &[Self], right: &[Self]) -> ControlFlow<bool>; |
159 | fn chaining_le(left: &[Self], right: &[Self]) -> ControlFlow<bool>; |
160 | fn chaining_gt(left: &[Self], right: &[Self]) -> ControlFlow<bool>; |
161 | fn chaining_ge(left: &[Self], right: &[Self]) -> ControlFlow<bool>; |
162 | } |
163 | |
164 | type AlwaysBreak<B> = ControlFlow<B, crate::convert::Infallible>; |
165 | |
166 | impl<A: PartialOrd> SlicePartialOrd for A { |
167 | default fn partial_compare(left: &[A], right: &[A]) -> Option<Ordering> { |
168 | let elem_chain: impl Fn(&A, &A) -> ControlFlow<…> = |a: &A, b: &A| match PartialOrd::partial_cmp(self:a, other:b) { |
169 | Some(Ordering::Equal) => ControlFlow::Continue(()), |
170 | non_eq: Option => ControlFlow::Break(non_eq), |
171 | }; |
172 | let len_chain: impl Fn(&usize, &usize) -> … = |a: &_, b: &_| ControlFlow::Break(usize::partial_cmp(self:a, other:b)); |
173 | let AlwaysBreak::Break(b: Option) = chaining_impl(left, right, elem_chain, len_chain); |
174 | b |
175 | } |
176 | } |
177 | |
178 | impl<A: PartialOrd> SliceChain for A { |
179 | default fn chaining_lt(left: &[Self], right: &[Self]) -> ControlFlow<bool> { |
180 | chaining_impl(left, right, elem_chain:PartialOrd::__chaining_lt, len_chain:usize::__chaining_lt) |
181 | } |
182 | default fn chaining_le(left: &[Self], right: &[Self]) -> ControlFlow<bool> { |
183 | chaining_impl(left, right, elem_chain:PartialOrd::__chaining_le, len_chain:usize::__chaining_le) |
184 | } |
185 | default fn chaining_gt(left: &[Self], right: &[Self]) -> ControlFlow<bool> { |
186 | chaining_impl(left, right, elem_chain:PartialOrd::__chaining_gt, len_chain:usize::__chaining_gt) |
187 | } |
188 | default fn chaining_ge(left: &[Self], right: &[Self]) -> ControlFlow<bool> { |
189 | chaining_impl(left, right, elem_chain:PartialOrd::__chaining_ge, len_chain:usize::__chaining_ge) |
190 | } |
191 | } |
192 | |
193 | #[inline ] |
194 | fn chaining_impl<'l, 'r, A: PartialOrd, B, C>( |
195 | left: &'l [A], |
196 | right: &'r [A], |
197 | elem_chain: impl Fn(&'l A, &'r A) -> ControlFlow<B>, |
198 | len_chain: impl for<'a> dynFnOnce(&'a usize, &'a usize) -> ControlFlow<B, C>, |
199 | ) -> ControlFlow<B, C> { |
200 | let l: usize = cmp::min(v1:left.len(), v2:right.len()); |
201 | |
202 | // Slice to the loop iteration range to enable bound check |
203 | // elimination in the compiler |
204 | let lhs: &[A] = &left[..l]; |
205 | let rhs: &[A] = &right[..l]; |
206 | |
207 | for i: usize in 0..l { |
208 | elem_chain(&lhs[i], &rhs[i])?; |
209 | } |
210 | |
211 | len_chain(&left.len(), &right.len()) |
212 | } |
213 | |
214 | // This is the impl that we would like to have. Unfortunately it's not sound. |
215 | // See `partial_ord_slice.rs`. |
216 | /* |
217 | impl<A> SlicePartialOrd for A |
218 | where |
219 | A: Ord, |
220 | { |
221 | default fn partial_compare(left: &[A], right: &[A]) -> Option<Ordering> { |
222 | Some(SliceOrd::compare(left, right)) |
223 | } |
224 | } |
225 | */ |
226 | |
227 | impl<A: AlwaysApplicableOrd> SlicePartialOrd for A { |
228 | fn partial_compare(left: &[A], right: &[A]) -> Option<Ordering> { |
229 | Some(SliceOrd::compare(left, right)) |
230 | } |
231 | } |
232 | |
233 | #[rustc_specialization_trait ] |
234 | trait AlwaysApplicableOrd: SliceOrd + Ord {} |
235 | |
236 | macro_rules! always_applicable_ord { |
237 | ($([$($p:tt)*] $t:ty,)*) => { |
238 | $(impl<$($p)*> AlwaysApplicableOrd for $t {})* |
239 | } |
240 | } |
241 | |
242 | always_applicable_ord! { |
243 | [] u8, [] u16, [] u32, [] u64, [] u128, [] usize, |
244 | [] i8, [] i16, [] i32, [] i64, [] i128, [] isize, |
245 | [] bool, [] char, |
246 | [T: ?Sized] *const T, [T: ?Sized] *mut T, |
247 | [T: AlwaysApplicableOrd] &T, |
248 | [T: AlwaysApplicableOrd] &mut T, |
249 | [T: AlwaysApplicableOrd] Option<T>, |
250 | } |
251 | |
252 | #[doc (hidden)] |
253 | // intermediate trait for specialization of slice's Ord |
254 | trait SliceOrd: Sized { |
255 | fn compare(left: &[Self], right: &[Self]) -> Ordering; |
256 | } |
257 | |
258 | impl<A: Ord> SliceOrd for A { |
259 | default fn compare(left: &[Self], right: &[Self]) -> Ordering { |
260 | let elem_chain: impl Fn(&A, &A) -> ControlFlow<…> = |a: &A, b: &A| match Ord::cmp(self:a, other:b) { |
261 | Ordering::Equal => ControlFlow::Continue(()), |
262 | non_eq: Ordering => ControlFlow::Break(non_eq), |
263 | }; |
264 | let len_chain: impl Fn(&usize, &usize) -> … = |a: &_, b: &_| ControlFlow::Break(usize::cmp(self:a, other:b)); |
265 | let AlwaysBreak::Break(b: Ordering) = chaining_impl(left, right, elem_chain, len_chain); |
266 | b |
267 | } |
268 | } |
269 | |
270 | /// Marks that a type should be treated as an unsigned byte for comparisons. |
271 | /// |
272 | /// # Safety |
273 | /// * The type must be readable as an `u8`, meaning it has to have the same |
274 | /// layout as `u8` and always be initialized. |
275 | /// * For every `x` and `y` of this type, `Ord(x, y)` must return the same |
276 | /// value as `Ord::cmp(transmute::<_, u8>(x), transmute::<_, u8>(y))`. |
277 | #[rustc_specialization_trait ] |
278 | unsafe trait UnsignedBytewiseOrd: Ord {} |
279 | |
280 | unsafe impl UnsignedBytewiseOrd for bool {} |
281 | unsafe impl UnsignedBytewiseOrd for u8 {} |
282 | unsafe impl UnsignedBytewiseOrd for NonZero<u8> {} |
283 | unsafe impl UnsignedBytewiseOrd for Option<NonZero<u8>> {} |
284 | unsafe impl UnsignedBytewiseOrd for ascii::Char {} |
285 | |
286 | // `compare_bytes` compares a sequence of unsigned bytes lexicographically, so |
287 | // use it if the requirements for `UnsignedBytewiseOrd` are fulfilled. |
288 | impl<A: Ord + UnsignedBytewiseOrd> SliceOrd for A { |
289 | #[inline ] |
290 | fn compare(left: &[Self], right: &[Self]) -> Ordering { |
291 | // Since the length of a slice is always less than or equal to |
292 | // isize::MAX, this never underflows. |
293 | let diff: isize = left.len() as isize - right.len() as isize; |
294 | // This comparison gets optimized away (on x86_64 and ARM) because the |
295 | // subtraction updates flags. |
296 | let len: usize = if left.len() < right.len() { left.len() } else { right.len() }; |
297 | let left: *const u8 = left.as_ptr().cast(); |
298 | let right: *const u8 = right.as_ptr().cast(); |
299 | // SAFETY: `left` and `right` are references and are thus guaranteed to |
300 | // be valid. `UnsignedBytewiseOrd` is only implemented for types that |
301 | // are valid u8s and can be compared the same way. We use the minimum |
302 | // of both lengths which guarantees that both regions are valid for |
303 | // reads in that interval. |
304 | let mut order: isize = unsafe { compare_bytes(left, right, bytes:len) as isize }; |
305 | if order == 0 { |
306 | order = diff; |
307 | } |
308 | order.cmp(&0) |
309 | } |
310 | } |
311 | |
312 | // Don't generate our own chaining loops for `memcmp`-able things either. |
313 | impl<A: PartialOrd + UnsignedBytewiseOrd> SliceChain for A { |
314 | #[inline ] |
315 | fn chaining_lt(left: &[Self], right: &[Self]) -> ControlFlow<bool> { |
316 | match SliceOrd::compare(left, right) { |
317 | Ordering::Equal => ControlFlow::Continue(()), |
318 | ne => ControlFlow::Break(ne.is_lt()), |
319 | } |
320 | } |
321 | #[inline ] |
322 | fn chaining_le(left: &[Self], right: &[Self]) -> ControlFlow<bool> { |
323 | match SliceOrd::compare(left, right) { |
324 | Ordering::Equal => ControlFlow::Continue(()), |
325 | ne => ControlFlow::Break(ne.is_le()), |
326 | } |
327 | } |
328 | #[inline ] |
329 | fn chaining_gt(left: &[Self], right: &[Self]) -> ControlFlow<bool> { |
330 | match SliceOrd::compare(left, right) { |
331 | Ordering::Equal => ControlFlow::Continue(()), |
332 | ne => ControlFlow::Break(ne.is_gt()), |
333 | } |
334 | } |
335 | #[inline ] |
336 | fn chaining_ge(left: &[Self], right: &[Self]) -> ControlFlow<bool> { |
337 | match SliceOrd::compare(left, right) { |
338 | Ordering::Equal => ControlFlow::Continue(()), |
339 | ne => ControlFlow::Break(ne.is_ge()), |
340 | } |
341 | } |
342 | } |
343 | |
344 | pub(super) trait SliceContains: Sized { |
345 | fn slice_contains(&self, x: &[Self]) -> bool; |
346 | } |
347 | |
348 | impl<T> SliceContains for T |
349 | where |
350 | T: PartialEq, |
351 | { |
352 | default fn slice_contains(&self, x: &[Self]) -> bool { |
353 | x.iter().any(|y: &T| *y == *self) |
354 | } |
355 | } |
356 | |
357 | impl SliceContains for u8 { |
358 | #[inline ] |
359 | fn slice_contains(&self, x: &[Self]) -> bool { |
360 | memchr::memchr(*self, text:x).is_some() |
361 | } |
362 | } |
363 | |
364 | impl SliceContains for i8 { |
365 | #[inline ] |
366 | fn slice_contains(&self, x: &[Self]) -> bool { |
367 | let byte: u8 = *self as u8; |
368 | // SAFETY: `i8` and `u8` have the same memory layout, thus casting `x.as_ptr()` |
369 | // as `*const u8` is safe. The `x.as_ptr()` comes from a reference and is thus guaranteed |
370 | // to be valid for reads for the length of the slice `x.len()`, which cannot be larger |
371 | // than `isize::MAX`. The returned slice is never mutated. |
372 | let bytes: &[u8] = unsafe { from_raw_parts(data:x.as_ptr() as *const u8, x.len()) }; |
373 | memchr::memchr(x:byte, text:bytes).is_some() |
374 | } |
375 | } |
376 | |
377 | macro_rules! impl_slice_contains { |
378 | ($($t:ty),*) => { |
379 | $( |
380 | impl SliceContains for $t { |
381 | #[inline] |
382 | fn slice_contains(&self, arr: &[$t]) -> bool { |
383 | // Make our LANE_COUNT 4x the normal lane count (aiming for 128 bit vectors). |
384 | // The compiler will nicely unroll it. |
385 | const LANE_COUNT: usize = 4 * (128 / (size_of::<$t>() * 8)); |
386 | // SIMD |
387 | let mut chunks = arr.chunks_exact(LANE_COUNT); |
388 | for chunk in &mut chunks { |
389 | if chunk.iter().fold(false, |acc, x| acc | (*x == *self)) { |
390 | return true; |
391 | } |
392 | } |
393 | // Scalar remainder |
394 | return chunks.remainder().iter().any(|x| *x == *self); |
395 | } |
396 | } |
397 | )* |
398 | }; |
399 | } |
400 | |
401 | impl_slice_contains!(u16, u32, u64, i16, i32, i64, f32, f64, usize, isize, char); |
402 | |