#[must_use]
pub fn lower_ascii(byte: u8) -> u8 {
if byte.is_ascii_uppercase() {
byte + 32
} else {
byte
}
}
#[must_use]
pub fn is_alnum(byte: u8) -> bool {
byte.is_ascii_alphanumeric()
}
#[must_use]
pub fn is_uri_char(byte: u8) -> bool {
matches!(
byte,
b'a'..=b'z' | b'A'..=b'Z' | b'0'..=b'9' | b'.' | b'-' | b'_' | b'~'
| b':' | b'/' | b'?' | b'#' | b'[' | b']' | b'@' | b'!' | b'$' | b'&'
| b'\'' | b'(' | b')' | b'*' | b'+' | b',' | b';' | b'=' | b'%'
)
}
#[must_use]
pub fn is_boundary(byte: Option<u8>) -> bool {
match byte {
None => true,
Some(value) => !matches!(value, b'a'..=b'z' | b'A'..=b'Z' | b'0'..=b'9' | b'_' | b'-'),
}
}
#[must_use]
pub fn is_base64_char(byte: &u8) -> bool {
matches!(*byte, b'a'..=b'z' | b'A'..=b'Z' | b'0'..=b'9' | b'+' | b'/' | b'=')
}
#[must_use]
pub fn is_base64url_char(byte: u8) -> bool {
matches!(byte, b'a'..=b'z' | b'A'..=b'Z' | b'0'..=b'9' | b'-' | b'_')
}
#[must_use]
pub fn is_email_local(byte: u8) -> bool {
is_alnum(byte) || matches!(byte, b'.' | b'_' | b'%' | b'+' | b'-')
}
#[must_use]
pub fn is_email_domain(byte: u8) -> bool {
is_alnum(byte) || matches!(byte, b'.' | b'-')
}
#[must_use]
pub fn find_bytes(input: &[u8], needle: &[u8]) -> Option<usize> {
input
.windows(needle.len())
.position(|window| window == needle)
}
#[must_use]
pub fn find_ci(input: &[u8], needle: &[u8]) -> Option<usize> {
if needle.is_empty() {
return Some(0);
}
input.windows(needle.len()).position(|window| {
window
.iter()
.zip(needle.iter())
.all(|(&a, &b)| lower_ascii(a) == lower_ascii(b))
})
}
#[must_use]
pub fn contains_ci(input: &[u8], needle: &[u8]) -> bool {
find_ci(input, needle).is_some()
}
#[must_use]
pub fn starts_ci(input: &[u8], index: usize, needle: &[u8]) -> bool {
input
.get(index..index + needle.len())
.is_some_and(|s| contains_ci(s, needle))
}