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