Skip to content

Commit 925822a

Browse files
committed
Rollup merge of rust-lang#32699 - bluss:slice-memcmp, r=alexcrichton
Specialize equality for [T] and comparison for [u8] to use memcmp when possible Specialize equality for [T] and comparison for [u8] to use memcmp when possible Where T is a type that can be compared for equality bytewise, we can use memcmp. We can also use memcmp for PartialOrd, Ord for [u8]. Use specialization to call memcmp in PartialEq for slices for certain element types. This PR does not change the user visible API since the implementation uses an intermediate trait. See commit messages for more information. The memcmp signature was changed from `*const i8` to `*const u8` which is in line with how the memcmp function is defined in C (taking const void * arguments, interpreting the values as unsigned bytes for purposes of the comparison).
2 parents a243f41 + a6c27be commit 925822a

File tree

4 files changed

+179
-44
lines changed

4 files changed

+179
-44
lines changed

src/libcollectionstest/slice.rs

+36-6
Original file line numberDiff line numberDiff line change
@@ -574,18 +574,48 @@ fn test_slice_2() {
574574
assert_eq!(v[1], 3);
575575
}
576576

577+
macro_rules! assert_order {
578+
(Greater, $a:expr, $b:expr) => {
579+
assert_eq!($a.cmp($b), Greater);
580+
assert!($a > $b);
581+
};
582+
(Less, $a:expr, $b:expr) => {
583+
assert_eq!($a.cmp($b), Less);
584+
assert!($a < $b);
585+
};
586+
(Equal, $a:expr, $b:expr) => {
587+
assert_eq!($a.cmp($b), Equal);
588+
assert_eq!($a, $b);
589+
}
590+
}
591+
592+
#[test]
593+
fn test_total_ord_u8() {
594+
let c = &[1u8, 2, 3];
595+
assert_order!(Greater, &[1u8, 2, 3, 4][..], &c[..]);
596+
let c = &[1u8, 2, 3, 4];
597+
assert_order!(Less, &[1u8, 2, 3][..], &c[..]);
598+
let c = &[1u8, 2, 3, 6];
599+
assert_order!(Equal, &[1u8, 2, 3, 6][..], &c[..]);
600+
let c = &[1u8, 2, 3, 4, 5, 6];
601+
assert_order!(Less, &[1u8, 2, 3, 4, 5, 5, 5, 5][..], &c[..]);
602+
let c = &[1u8, 2, 3, 4];
603+
assert_order!(Greater, &[2u8, 2][..], &c[..]);
604+
}
605+
606+
577607
#[test]
578-
fn test_total_ord() {
608+
fn test_total_ord_i32() {
579609
let c = &[1, 2, 3];
580-
[1, 2, 3, 4][..].cmp(c) == Greater;
610+
assert_order!(Greater, &[1, 2, 3, 4][..], &c[..]);
581611
let c = &[1, 2, 3, 4];
582-
[1, 2, 3][..].cmp(c) == Less;
612+
assert_order!(Less, &[1, 2, 3][..], &c[..]);
583613
let c = &[1, 2, 3, 6];
584-
[1, 2, 3, 4][..].cmp(c) == Equal;
614+
assert_order!(Equal, &[1, 2, 3, 6][..], &c[..]);
585615
let c = &[1, 2, 3, 4, 5, 6];
586-
[1, 2, 3, 4, 5, 5, 5, 5][..].cmp(c) == Less;
616+
assert_order!(Less, &[1, 2, 3, 4, 5, 5, 5, 5][..], &c[..]);
587617
let c = &[1, 2, 3, 4];
588-
[2, 2][..].cmp(c) == Greater;
618+
assert_order!(Greater, &[2, 2][..], &c[..]);
589619
}
590620

591621
#[test]

src/libcore/lib.rs

+1
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,7 @@
7575
#![feature(unwind_attributes)]
7676
#![feature(repr_simd, platform_intrinsics)]
7777
#![feature(rustc_attrs)]
78+
#![feature(specialization)]
7879
#![feature(staged_api)]
7980
#![feature(unboxed_closures)]
8081
#![feature(question_mark)]

src/libcore/slice.rs

+139-16
Original file line numberDiff line numberDiff line change
@@ -1630,12 +1630,60 @@ pub unsafe fn from_raw_parts_mut<'a, T>(p: *mut T, len: usize) -> &'a mut [T] {
16301630
}
16311631

16321632
//
1633-
// Boilerplate traits
1633+
// Comparison traits
16341634
//
16351635

1636+
extern {
1637+
/// Call implementation provided memcmp
1638+
///
1639+
/// Interprets the data as u8.
1640+
///
1641+
/// Return 0 for equal, < 0 for less than and > 0 for greater
1642+
/// than.
1643+
// FIXME(#32610): Return type should be c_int
1644+
fn memcmp(s1: *const u8, s2: *const u8, n: usize) -> i32;
1645+
}
1646+
16361647
#[stable(feature = "rust1", since = "1.0.0")]
16371648
impl<A, B> PartialEq<[B]> for [A] where A: PartialEq<B> {
16381649
fn eq(&self, other: &[B]) -> bool {
1650+
SlicePartialEq::equal(self, other)
1651+
}
1652+
1653+
fn ne(&self, other: &[B]) -> bool {
1654+
SlicePartialEq::not_equal(self, other)
1655+
}
1656+
}
1657+
1658+
#[stable(feature = "rust1", since = "1.0.0")]
1659+
impl<T: Eq> Eq for [T] {}
1660+
1661+
#[stable(feature = "rust1", since = "1.0.0")]
1662+
impl<T: Ord> Ord for [T] {
1663+
fn cmp(&self, other: &[T]) -> Ordering {
1664+
SliceOrd::compare(self, other)
1665+
}
1666+
}
1667+
1668+
#[stable(feature = "rust1", since = "1.0.0")]
1669+
impl<T: PartialOrd> PartialOrd for [T] {
1670+
fn partial_cmp(&self, other: &[T]) -> Option<Ordering> {
1671+
SlicePartialOrd::partial_compare(self, other)
1672+
}
1673+
}
1674+
1675+
#[doc(hidden)]
1676+
// intermediate trait for specialization of slice's PartialEq
1677+
trait SlicePartialEq<B> {
1678+
fn equal(&self, other: &[B]) -> bool;
1679+
fn not_equal(&self, other: &[B]) -> bool;
1680+
}
1681+
1682+
// Generic slice equality
1683+
impl<A, B> SlicePartialEq<B> for [A]
1684+
where A: PartialEq<B>
1685+
{
1686+
default fn equal(&self, other: &[B]) -> bool {
16391687
if self.len() != other.len() {
16401688
return false;
16411689
}
@@ -1648,7 +1696,8 @@ impl<A, B> PartialEq<[B]> for [A] where A: PartialEq<B> {
16481696

16491697
true
16501698
}
1651-
fn ne(&self, other: &[B]) -> bool {
1699+
1700+
default fn not_equal(&self, other: &[B]) -> bool {
16521701
if self.len() != other.len() {
16531702
return true;
16541703
}
@@ -1663,12 +1712,36 @@ impl<A, B> PartialEq<[B]> for [A] where A: PartialEq<B> {
16631712
}
16641713
}
16651714

1666-
#[stable(feature = "rust1", since = "1.0.0")]
1667-
impl<T: Eq> Eq for [T] {}
1715+
// Use memcmp for bytewise equality when the types allow
1716+
impl<A> SlicePartialEq<A> for [A]
1717+
where A: PartialEq<A> + BytewiseEquality
1718+
{
1719+
fn equal(&self, other: &[A]) -> bool {
1720+
if self.len() != other.len() {
1721+
return false;
1722+
}
1723+
unsafe {
1724+
let size = mem::size_of_val(self);
1725+
memcmp(self.as_ptr() as *const u8,
1726+
other.as_ptr() as *const u8, size) == 0
1727+
}
1728+
}
16681729

1669-
#[stable(feature = "rust1", since = "1.0.0")]
1670-
impl<T: Ord> Ord for [T] {
1671-
fn cmp(&self, other: &[T]) -> Ordering {
1730+
fn not_equal(&self, other: &[A]) -> bool {
1731+
!self.equal(other)
1732+
}
1733+
}
1734+
1735+
#[doc(hidden)]
1736+
// intermediate trait for specialization of slice's PartialOrd
1737+
trait SlicePartialOrd<B> {
1738+
fn partial_compare(&self, other: &[B]) -> Option<Ordering>;
1739+
}
1740+
1741+
impl<A> SlicePartialOrd<A> for [A]
1742+
where A: PartialOrd
1743+
{
1744+
default fn partial_compare(&self, other: &[A]) -> Option<Ordering> {
16721745
let l = cmp::min(self.len(), other.len());
16731746

16741747
// Slice to the loop iteration range to enable bound check
@@ -1677,19 +1750,33 @@ impl<T: Ord> Ord for [T] {
16771750
let rhs = &other[..l];
16781751

16791752
for i in 0..l {
1680-
match lhs[i].cmp(&rhs[i]) {
1681-
Ordering::Equal => (),
1753+
match lhs[i].partial_cmp(&rhs[i]) {
1754+
Some(Ordering::Equal) => (),
16821755
non_eq => return non_eq,
16831756
}
16841757
}
16851758

1686-
self.len().cmp(&other.len())
1759+
self.len().partial_cmp(&other.len())
16871760
}
16881761
}
16891762

1690-
#[stable(feature = "rust1", since = "1.0.0")]
1691-
impl<T: PartialOrd> PartialOrd for [T] {
1692-
fn partial_cmp(&self, other: &[T]) -> Option<Ordering> {
1763+
impl SlicePartialOrd<u8> for [u8] {
1764+
#[inline]
1765+
fn partial_compare(&self, other: &[u8]) -> Option<Ordering> {
1766+
Some(SliceOrd::compare(self, other))
1767+
}
1768+
}
1769+
1770+
#[doc(hidden)]
1771+
// intermediate trait for specialization of slice's Ord
1772+
trait SliceOrd<B> {
1773+
fn compare(&self, other: &[B]) -> Ordering;
1774+
}
1775+
1776+
impl<A> SliceOrd<A> for [A]
1777+
where A: Ord
1778+
{
1779+
default fn compare(&self, other: &[A]) -> Ordering {
16931780
let l = cmp::min(self.len(), other.len());
16941781

16951782
// Slice to the loop iteration range to enable bound check
@@ -1698,12 +1785,48 @@ impl<T: PartialOrd> PartialOrd for [T] {
16981785
let rhs = &other[..l];
16991786

17001787
for i in 0..l {
1701-
match lhs[i].partial_cmp(&rhs[i]) {
1702-
Some(Ordering::Equal) => (),
1788+
match lhs[i].cmp(&rhs[i]) {
1789+
Ordering::Equal => (),
17031790
non_eq => return non_eq,
17041791
}
17051792
}
17061793

1707-
self.len().partial_cmp(&other.len())
1794+
self.len().cmp(&other.len())
17081795
}
17091796
}
1797+
1798+
// memcmp compares a sequence of unsigned bytes lexicographically.
1799+
// this matches the order we want for [u8], but no others (not even [i8]).
1800+
impl SliceOrd<u8> for [u8] {
1801+
#[inline]
1802+
fn compare(&self, other: &[u8]) -> Ordering {
1803+
let order = unsafe {
1804+
memcmp(self.as_ptr(), other.as_ptr(),
1805+
cmp::min(self.len(), other.len()))
1806+
};
1807+
if order == 0 {
1808+
self.len().cmp(&other.len())
1809+
} else if order < 0 {
1810+
Less
1811+
} else {
1812+
Greater
1813+
}
1814+
}
1815+
}
1816+
1817+
#[doc(hidden)]
1818+
/// Trait implemented for types that can be compared for equality using
1819+
/// their bytewise representation
1820+
trait BytewiseEquality { }
1821+
1822+
macro_rules! impl_marker_for {
1823+
($traitname:ident, $($ty:ty)*) => {
1824+
$(
1825+
impl $traitname for $ty { }
1826+
)*
1827+
}
1828+
}
1829+
1830+
impl_marker_for!(BytewiseEquality,
1831+
u8 i8 u16 i16 u32 i32 u64 i64 usize isize char bool);
1832+

src/libcore/str/mod.rs

+3-22
Original file line numberDiff line numberDiff line change
@@ -1150,16 +1150,7 @@ Section: Comparing strings
11501150
#[lang = "str_eq"]
11511151
#[inline]
11521152
fn eq_slice(a: &str, b: &str) -> bool {
1153-
a.len() == b.len() && unsafe { cmp_slice(a, b, a.len()) == 0 }
1154-
}
1155-
1156-
/// Bytewise slice comparison.
1157-
/// NOTE: This uses the system's memcmp, which is currently dramatically
1158-
/// faster than comparing each byte in a loop.
1159-
#[inline]
1160-
unsafe fn cmp_slice(a: &str, b: &str, len: usize) -> i32 {
1161-
extern { fn memcmp(s1: *const i8, s2: *const i8, n: usize) -> i32; }
1162-
memcmp(a.as_ptr() as *const i8, b.as_ptr() as *const i8, len)
1153+
a.as_bytes() == b.as_bytes()
11631154
}
11641155

11651156
/*
@@ -1328,8 +1319,7 @@ Section: Trait implementations
13281319
*/
13291320

13301321
mod traits {
1331-
use cmp::{self, Ordering, Ord, PartialEq, PartialOrd, Eq};
1332-
use cmp::Ordering::{Less, Greater};
1322+
use cmp::{Ord, Ordering, PartialEq, PartialOrd, Eq};
13331323
use iter::Iterator;
13341324
use option::Option;
13351325
use option::Option::Some;
@@ -1340,16 +1330,7 @@ mod traits {
13401330
impl Ord for str {
13411331
#[inline]
13421332
fn cmp(&self, other: &str) -> Ordering {
1343-
let cmp = unsafe {
1344-
super::cmp_slice(self, other, cmp::min(self.len(), other.len()))
1345-
};
1346-
if cmp == 0 {
1347-
self.len().cmp(&other.len())
1348-
} else if cmp < 0 {
1349-
Less
1350-
} else {
1351-
Greater
1352-
}
1333+
self.as_bytes().cmp(other.as_bytes())
13531334
}
13541335
}
13551336

0 commit comments

Comments
 (0)