tstr/
utils.rs

1//! Utility functions
2
3/// A const equivalent of `&str` equality comparison.
4///
5/// # Example
6///
7/// ```rust
8/// use tstr::utils::str_eq;
9///
10/// const FOO: &str = "foo";
11/// const BAR: &str = "fooooo";
12/// const BAZ: &str = "bar";
13///
14///
15/// const FOO_EQ_FOO: bool = str_eq(FOO, FOO);
16/// assert!( FOO_EQ_FOO );
17///
18/// const FOO_EQ_BAR: bool = str_eq(FOO, BAR);
19/// assert!( !FOO_EQ_BAR );
20///
21/// const FOO_EQ_BAZ: bool = str_eq(FOO, BAZ);
22/// assert!( !FOO_EQ_BAZ );
23///
24/// ```
25///
26#[cfg(feature = "rust_1_46")]
27#[cfg_attr(feature = "docsrs", doc(cfg(feature = "rust_1_46")))]
28#[inline]
29pub const fn str_eq(left: &str, right: &str) -> bool {
30    u8_slice_eq(left.as_bytes(), right.as_bytes())
31}
32
33/// A const equivalent of `&[u8]` equality comparison.
34///
35/// # Example
36///
37/// ```rust
38/// use tstr::utils::u8_slice_eq;
39///
40/// const FOO: &[u8] = &[10, 20];
41/// const BAR: &[u8] = &[10, 20, 30, 40];
42/// const BAZ: &[u8] = &[3, 5, 8, 13];
43///
44/// const FOO_EQ_FOO: bool = u8_slice_eq(FOO, FOO);
45/// assert!( FOO_EQ_FOO );
46///
47/// const FOO_EQ_BAR: bool = u8_slice_eq(FOO, BAR);
48/// assert!( !FOO_EQ_BAR );
49///
50/// const FOO_EQ_BAZ: bool = u8_slice_eq(FOO, BAZ);
51/// assert!( !FOO_EQ_BAZ );
52///
53///
54/// ```
55///
56#[cfg(feature = "rust_1_46")]
57#[cfg_attr(feature = "docsrs", doc(cfg(feature = "rust_1_46")))]
58#[inline]
59pub const fn u8_slice_eq(left: &[u8], right: &[u8]) -> bool {
60    if left.len() != right.len() {
61        return false;
62    }
63
64    let mut i = 0;
65    while i != left.len() {
66        if left[i] != right[i] {
67            return false;
68        }
69        i += 1;
70    }
71
72    true
73}
74
75#[cfg(feature = "rust_1_46")]
76pub use slice_cmp::{str_cmp, u8_slice_cmp};
77
78#[cfg(feature = "rust_1_46")]
79mod slice_cmp {
80    use core::cmp::Ordering;
81
82    const LESS: u8 = 0;
83    const GREATER: u8 = 1;
84    const EQUAL: u8 = 2;
85
86    macro_rules! ret_if_ne {
87        ($left:expr, $right:expr) => {{
88            let l = $left;
89            let r = $right;
90            if l != r {
91                return (l > r) as u8;
92            }
93        }};
94    }
95
96    const fn to_ordering(n: u8) -> Ordering {
97        match n {
98            LESS => Ordering::Less,
99            GREATER => Ordering::Greater,
100            _ => Ordering::Equal,
101        }
102    }
103
104    /// A const equivalent of `str::cmp`.
105    ///
106    /// # Example
107    ///
108    /// ```rust
109    /// use tstr::utils::str_cmp;
110    ///
111    /// use std::cmp::Ordering;
112    ///
113    /// const FOO: &str = "foo";
114    /// const BAR: &str = "fooooo";
115    /// const BAZ: &str = "bar";
116    ///
117    ///
118    /// const FOO_CMP_FOO: Ordering = str_cmp(FOO, FOO);
119    /// assert_eq!(FOO_CMP_FOO, Ordering::Equal);
120    ///
121    /// const FOO_CMP_BAR: Ordering = str_cmp(FOO, BAR);
122    /// assert_eq!(FOO_CMP_BAR, Ordering::Less);
123    ///
124    /// const FOO_CMP_BAZ: Ordering = str_cmp(FOO, BAZ);
125    /// assert_eq!(FOO_CMP_BAZ, Ordering::Greater);
126    ///
127    /// ```
128    ///
129    #[cfg_attr(feature = "docsrs", doc(cfg(feature = "rust_1_46")))]
130    #[inline]
131    pub const fn str_cmp(left: &str, right: &str) -> Ordering {
132        const fn str_cmp_inner(left: &[u8], right: &[u8]) -> u8 {
133            let left_len = left.len();
134            let right_len = right.len();
135            let (min_len, on_ne) = if left_len < right_len {
136                (left_len, LESS)
137            } else {
138                (right_len, GREATER)
139            };
140
141            let mut i = 0;
142            while i < min_len {
143                ret_if_ne! {left[i], right[i]}
144                i += 1;
145            }
146
147            if left_len == right_len {
148                EQUAL
149            } else {
150                on_ne
151            }
152        }
153
154        to_ordering(str_cmp_inner(left.as_bytes(), right.as_bytes()))
155    }
156
157    /// A const equivalent of `<[u8]>::cmp`.
158    ///
159    /// # Example
160    ///
161    /// ```rust
162    /// use tstr::utils::u8_slice_cmp;
163    ///
164    /// use std::cmp::Ordering;
165    ///
166    /// const FOO: &[u8] = &[10, 20];
167    /// const BAR: &[u8] = &[10, 20, 30, 40];
168    /// const BAZ: &[u8] = &[3, 5];
169    ///
170    /// const FOO_CMP_FOO: Ordering = u8_slice_cmp(FOO, FOO);
171    /// assert_eq!(FOO_CMP_FOO, Ordering::Equal);
172    ///
173    /// const FOO_CMP_BAR: Ordering = u8_slice_cmp(FOO, BAR);
174    /// assert_eq!(FOO_CMP_BAR, Ordering::Less);
175    ///
176    /// const FOO_CMP_BAZ: Ordering = u8_slice_cmp(FOO, BAZ);
177    /// assert_eq!(FOO_CMP_BAZ, Ordering::Greater);
178    ///
179    /// ```
180    ///
181    #[cfg_attr(feature = "docsrs", doc(cfg(feature = "rust_1_46")))]
182    #[inline]
183    pub const fn u8_slice_cmp(left: &[u8], right: &[u8]) -> Ordering {
184        const fn u8_slice_cmp_inner(left: &[u8], right: &[u8]) -> u8 {
185            let left_len = left.len();
186
187            ret_if_ne! {left_len, right.len()}
188
189            let mut i = 0;
190            while i < left_len {
191                ret_if_ne! {left[i], right[i]}
192                i += 1;
193            }
194
195            EQUAL
196        }
197
198        to_ordering(u8_slice_cmp_inner(left, right))
199    }
200}
201
202#[cfg(test)]
203mod tests {
204    use super::*;
205
206    #[test]
207    #[cfg(feature = "rust_1_46")]
208    fn slice_eq_test() {
209        assert!(u8_slice_eq(&[], &[]));
210        assert!(!u8_slice_eq(&[], &[0]));
211        assert!(!u8_slice_eq(&[0], &[]));
212        assert!(u8_slice_eq(&[0], &[0]));
213        assert!(!u8_slice_eq(&[0], &[1]));
214        assert!(!u8_slice_eq(&[1], &[0]));
215        assert!(!u8_slice_eq(&[0], &[0, 1]));
216        assert!(!u8_slice_eq(&[0, 1], &[0]));
217        assert!(u8_slice_eq(&[0, 1], &[0, 1]));
218        assert!(!u8_slice_eq(&[0, 1], &[0, 2]));
219    }
220
221    #[test]
222    #[cfg(feature = "rust_1_46")]
223    fn str_eq_test() {
224        assert!(str_eq("", ""));
225        assert!(!str_eq("", "0"));
226        assert!(!str_eq("0", ""));
227        assert!(str_eq("0", "0"));
228        assert!(!str_eq("0", "1"));
229        assert!(!str_eq("1", "0"));
230        assert!(!str_eq("0", "0, 1"));
231        assert!(!str_eq("0, 1", "0"));
232        assert!(!str_eq("0, 1", "1"));
233        assert!(str_eq("0, 1", "0, 1"));
234        assert!(!str_eq("0, 1", "0, 2"));
235    }
236
237    #[test]
238    #[cfg(feature = "rust_1_46")]
239    fn slice_cmp_test() {
240        use core::cmp::{
241            Ord,
242            Ordering::{Equal, Greater, Less},
243        };
244
245        macro_rules! assert_s_cmp {
246            ($left:expr, $right:expr, $expected:expr) => {
247                assert_eq!(u8_slice_cmp($left, $right), $expected);
248                assert_eq!(<[u8]>::cmp($left, $right), $expected);
249
250                assert_eq!(u8_slice_cmp($right, $left), $expected.reverse());
251                assert_eq!(<[u8]>::cmp($right, $left), $expected.reverse());
252            };
253        }
254
255        assert_s_cmp!(&[], &[], Equal);
256        assert_s_cmp!(&[], &[0], Less);
257        assert_s_cmp!(&[0], &[], Greater);
258        assert_s_cmp!(&[0], &[0], Equal);
259        assert_s_cmp!(&[0], &[1], Less);
260        assert_s_cmp!(&[0], &[0, 1], Less);
261        assert_s_cmp!(&[0, 1], &[0, 1], Equal);
262        assert_s_cmp!(&[0, 1], &[0, 2], Less);
263    }
264
265    #[test]
266    #[cfg(feature = "rust_1_46")]
267    fn str_cmp_test() {
268        use core::cmp::{
269            Ord,
270            Ordering::{Equal, Greater, Less},
271        };
272
273        macro_rules! assert_s_cmp {
274            ($left:expr, $right:expr, $expected:expr) => {
275                assert_eq!(str_cmp($left, $right), $expected, "A");
276                assert_eq!($left.cmp($right), $expected, "B");
277
278                assert_eq!(str_cmp($left, $left), Equal);
279                assert_eq!(str_cmp($right, $right), Equal);
280
281                assert_eq!(str_cmp($right, $left), $expected.reverse(), "cmp");
282                assert_eq!($right.cmp($left), $expected.reverse(), "cmp");
283            };
284        }
285
286        assert_s_cmp!("0", "", Greater);
287        assert_s_cmp!("0", "1", Less);
288        assert_s_cmp!("0", "01", Less);
289        assert_s_cmp!("1", "01", Greater);
290        assert_s_cmp!("099999", "12", Less);
291        assert_s_cmp!("111111", "12", Less);
292        assert_s_cmp!("120", "12", Greater);
293        assert_s_cmp!("199999", "12", Greater);
294        assert_s_cmp!("299999", "12", Greater);
295        assert_s_cmp!("01", "02", Less);
296    }
297}