Skip to main content

dynomite/util/
dyn_string.rs

1//! Byte-slice helpers that complement [`bytes::Bytes`] and [`String`].
2//!
3//! Message payloads use [`bytes::Bytes`] for shared ownership and
4//! [`String`] for textual data. This module collects a small handful
5//! of helpers callers reach for: a length-prefixed byte-ordering
6//! comparison and char-finding wrappers that operate on slices.
7
8/// Compare two byte slices: shorter slices sort first, otherwise sort
9/// lexicographically.
10///
11/// # Examples
12///
13/// ```
14/// use std::cmp::Ordering;
15/// use dynomite::util::dyn_string::string_compare;
16///
17/// assert_eq!(string_compare(b"abc", b"abc"), Ordering::Equal);
18/// assert_eq!(string_compare(b"ab", b"abc"), Ordering::Less);
19/// assert_eq!(string_compare(b"abd", b"abc"), Ordering::Greater);
20/// ```
21pub fn string_compare(a: &[u8], b: &[u8]) -> std::cmp::Ordering {
22    if a.len() == b.len() {
23        a.cmp(b)
24    } else {
25        a.len().cmp(&b.len())
26    }
27}
28
29/// Return the byte index of the first occurrence of `needle` in
30/// `haystack`, or [`None`] if absent.
31///
32/// # Examples
33///
34/// ```
35/// use dynomite::util::dyn_string::strchr;
36/// assert_eq!(strchr(b"abcde", b'c'), Some(2));
37/// assert_eq!(strchr(b"abcde", b'z'), None);
38/// ```
39pub fn strchr(haystack: &[u8], needle: u8) -> Option<usize> {
40    haystack.iter().position(|&b| b == needle)
41}
42
43/// Return the byte index of the last occurrence of `needle` in
44/// `haystack`, or [`None`] if absent.
45///
46/// # Examples
47///
48/// ```
49/// use dynomite::util::dyn_string::strrchr;
50/// assert_eq!(strrchr(b"abcabc", b'b'), Some(4));
51/// assert_eq!(strrchr(b"abcabc", b'z'), None);
52/// ```
53pub fn strrchr(haystack: &[u8], needle: u8) -> Option<usize> {
54    haystack.iter().rposition(|&b| b == needle)
55}
56
57/// Case-insensitive ASCII slice equality. Slices of different lengths
58/// are unequal.
59///
60/// # Examples
61///
62/// ```
63/// use dynomite::util::dyn_string::eq_ignore_ascii_case;
64/// assert!(eq_ignore_ascii_case(b"GET", b"get"));
65/// assert!(!eq_ignore_ascii_case(b"GET", b"GETS"));
66/// ```
67pub fn eq_ignore_ascii_case(a: &[u8], b: &[u8]) -> bool {
68    a.eq_ignore_ascii_case(b)
69}
70
71#[cfg(test)]
72mod tests {
73    use std::cmp::Ordering;
74
75    use super::*;
76
77    #[test]
78    fn shorter_sorts_first() {
79        assert_eq!(string_compare(b"a", b"ab"), Ordering::Less);
80        assert_eq!(string_compare(b"abc", b"ab"), Ordering::Greater);
81    }
82
83    #[test]
84    fn equal_length_uses_lex_order() {
85        assert_eq!(string_compare(b"abc", b"abd"), Ordering::Less);
86        assert_eq!(string_compare(b"abc", b"abc"), Ordering::Equal);
87    }
88
89    #[test]
90    fn strchr_and_strrchr() {
91        assert_eq!(strchr(b"hello", b'l'), Some(2));
92        assert_eq!(strrchr(b"hello", b'l'), Some(3));
93        assert_eq!(strchr(b"", b'x'), None);
94        assert_eq!(strrchr(b"", b'x'), None);
95    }
96}