1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
use core::cmp::Ordering;
use ext::*;
use mem_eq::MemEq;

/// Trait for values whose bytes can be compared directly.
pub trait MemOrd: MemEq {
    /// Returns an ordering between the memory of `self` and `other`.
    #[must_use]
    fn mem_cmp(&self, other: &Self) -> Ordering;
}

#[inline(always)]
fn _mem_cmp<T>(this: &T, other: &T) -> Ordering {
    match unsafe { _memcmp(this, other, 1) } {
        x if x < 0 => Ordering::Less,
        x if x > 0 => Ordering::Greater,
        _ => Ordering::Equal,
    }
}

impl<T> MemOrd for T {
    #[inline]
    #[cfg(feature = "specialization")]
    default fn mem_cmp(&self, other: &Self) -> Ordering { _mem_cmp(self, other) }

    #[cfg(not(feature = "specialization"))]
    fn mem_cmp(&self, other: &Self) -> Ordering { _mem_cmp(self, other) }
}

macro_rules! impl_specialized {
    ($($t:ty)+) => {
        $(#[cfg(feature = "specialization")]
        impl MemOrd for $t {
            #[inline]
            fn mem_cmp(&self, other: &Self) -> Ordering { self.cmp(other) }
        })+
    }
}

macro_rules! impl_specialized_dep {
    ($dep:ty => $($t:ty),+) => {
        $(#[cfg(feature = "specialization")]
        impl MemOrd for $t {
            #[inline]
            fn mem_cmp(&self, other: &Self) -> Ordering {
                use core::mem;
                unsafe {
                    let x: $dep = mem::transmute(*self);
                    let y: $dep = mem::transmute(*other);
                    x.mem_cmp(&y)
                }
            }
        })+
    }
}

impl_specialized!(u8 u16 u32 u64 usize);
impl_specialized_dep!(u8    => i8);
impl_specialized_dep!(u16   => i16);
impl_specialized_dep!(u32   => i32);
impl_specialized_dep!(u64   => i64);
impl_specialized_dep!(usize => isize);

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn compare_bytes() {
        let x = [0u8, 0, 0, 0];
        let y = [0u8, 0, 0, 4];
        assert_eq!(x.cmp(&y), x.mem_cmp(&y));
    }

    #[test]
    fn compare_signed() {
        macro_rules! compare {
            ($x:expr, $y:expr, $($t:ty),+) => {
                $({
                    let x: $t = $x;
                    let y: $t = $y;
                    assert_eq!(x.mem_cmp(&y), _mem_cmp(&x, &y));
                })+
            }
        }
        compare!(-1, 1, i8, i16, i32, i64, isize);
    }
}