vyre 0.4.0

GPU compute intermediate representation with a standard operation library
Documentation
//! ASCII byte predicates and case-insensitive byte search helpers.

/// Lower an ASCII uppercase byte without touching non-uppercase bytes.
#[must_use]
pub fn lower_ascii(byte: u8) -> u8 {
    if byte.is_ascii_uppercase() {
        byte + 32
    } else {
        byte
    }
}

/// Return true for ASCII letters or digits.
#[must_use]
pub fn is_alnum(byte: u8) -> bool {
    byte.is_ascii_alphanumeric()
}

/// Return true for URI bytes accepted by the detectors.
#[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'%'
    )
}

/// Return true when an optional neighbor byte is a token boundary.
#[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'-'),
    }
}

/// Return true for base64 alphabet bytes.
#[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'=')
}

/// Return true for base64url alphabet bytes.
#[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'_')
}

/// Return true for bytes allowed in an email local part.
#[must_use]
pub fn is_email_local(byte: u8) -> bool {
    is_alnum(byte) || matches!(byte, b'.' | b'_' | b'%' | b'+' | b'-')
}

/// Return true for bytes allowed in an email domain.
#[must_use]
pub fn is_email_domain(byte: u8) -> bool {
    is_alnum(byte) || matches!(byte, b'.' | b'-')
}

/// Locate an exact byte sequence.
#[must_use]
pub fn find_bytes(input: &[u8], needle: &[u8]) -> Option<usize> {
    input
        .windows(needle.len())
        .position(|window| window == needle)
}

/// Locate a case-insensitive ASCII byte sequence.
#[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))
    })
}

/// Return true when `needle` occurs case-insensitively.
#[must_use]
pub fn contains_ci(input: &[u8], needle: &[u8]) -> bool {
    find_ci(input, needle).is_some()
}

/// Return true when `needle` starts at `index`, case-insensitively.
#[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))
}