sorug 0.6.1

Ultra-high-performance, zero-copy, WHATWG-compliant URL parser
Documentation
//! Compile-time Unicode range tables for IDNA `CheckBidi` / `CheckJoiners` / UTS #46.
//!
//! Tables are generated by `build.rs` from vendored UCD under `data/ucd/` plus
//! `data/idna_overlay.txt` into `$OUT_DIR/idna_tables.rs` as sorted, merged
//! `&[(u32, u32)]` slices.
//! Membership is O(log N) via binary search with **zero heap allocation**.

include!(concat!(env!("OUT_DIR"), "/idna_tables.rs"));

/// Returns `true` if `c` lies in any inclusive `(lo, hi)` range of `table`.
///
/// `table` must be sorted by `lo` and free of overlaps (as emitted by `build.rs`).
/// Uses [`slice::partition_point`] — branch-friendly, cache-linear over the
/// probed cache lines, no allocation.
#[inline]
pub(crate) fn in_range_table(c: char, table: &[(u32, u32)]) -> bool {
    let cp = c as u32;
    // First index where `lo > cp`. Candidate is the preceding range.
    let i = table.partition_point(|&(lo, _)| lo <= cp);
    if i == 0 {
        return false;
    }
    let (_, hi) = table[i - 1];
    cp <= hi
}

#[inline]
pub(crate) fn is_bidi_ignored_mark(c: char) -> bool {
    in_range_table(c, BIDI_IGNORED)
}

#[inline]
pub(crate) fn is_joining_letter_cp(c: char) -> bool {
    in_range_table(c, JOINING_LETTERS)
}

#[inline]
pub(crate) fn is_rtl_non_letter(c: char) -> bool {
    in_range_table(c, RTL_NON_LETTERS)
}

#[inline]
pub(crate) fn is_rtl_alphabetic_block(c: char) -> bool {
    in_range_table(c, RTL_ALPHABETIC)
}

#[inline]
pub(crate) fn is_bidi_number_cp(c: char) -> bool {
    in_range_table(c, BIDI_NUMBERS)
}

#[inline]
pub(crate) fn is_legacy_arabic_cp(c: char) -> bool {
    in_range_table(c, LEGACY_ARABIC)
}

#[inline]
pub(crate) fn is_disallowed_cp(c: char) -> bool {
    in_range_table(c, DISALLOWED)
}

#[inline]
pub(crate) fn is_ltr_letter_block(c: char) -> bool {
    in_range_table(c, LTR_LETTERS)
}

#[inline]
pub(crate) fn is_arabic_ext_b_letter_cp(c: char) -> bool {
    in_range_table(c, ARABIC_EXT_B_LETTERS)
}

#[inline]
pub(crate) fn is_arabic_ext_b_compatible_cp(c: char) -> bool {
    in_range_table(c, ARABIC_EXT_B_COMPATIBLE)
}

#[inline]
pub(crate) fn is_arabic_ext_b_era_mark_cp(c: char) -> bool {
    in_range_table(c, ARABIC_EXT_B_ERA_MARKS)
}

#[inline]
pub(crate) fn is_arabic_ext_b_era_ok_cp(c: char) -> bool {
    in_range_table(c, ARABIC_EXT_B_ERA_OK)
}

#[inline]
pub(crate) fn maps_nfkc_via_space_cp(c: char) -> bool {
    in_range_table(c, NFKC_VIA_SPACE)
}

#[inline]
pub(crate) fn is_uts46_ignored_cp(c: char) -> bool {
    in_range_table(c, UTS46_IGNORED)
}

#[inline]
pub(crate) fn uts46_needs_map_cp(c: char) -> bool {
    in_range_table(c, UTS46_NEEDS_MAP)
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn partition_point_membership() {
        let table: &[(u32, u32)] = &[(0x0041, 0x005A), (0x0061, 0x007A), (0x00C0, 0x00FF)];
        assert!(in_range_table('A', table));
        assert!(in_range_table('Z', table));
        assert!(!in_range_table('[', table));
        assert!(in_range_table('a', table));
        assert!(in_range_table('ü', table));
        assert!(!in_range_table('Ѐ', table));
        assert!(!in_range_table('\0', table));
    }

    #[test]
    fn generated_bidi_ignored_spot_checks() {
        assert!(is_bidi_ignored_mark('\u{0613}'));
        assert!(is_bidi_ignored_mark('\u{0300}'));
        assert!(is_bidi_ignored_mark('\u{FB1E}'));
        assert!(!is_bidi_ignored_mark('\u{06DE}'));
        assert!(!is_bidi_ignored_mark('\u{05BE}'));
        assert!(is_bidi_ignored_mark('\u{112DF}'));
    }

    #[test]
    fn generated_rtl_and_joining_spot_checks() {
        assert!(is_rtl_non_letter('\u{05BE}'));
        assert!(is_rtl_non_letter('\u{0888}'));
        assert!(is_rtl_alphabetic_block('\u{05D0}'));
        assert!(is_rtl_alphabetic_block('\u{1E900}'));
        assert!(is_joining_letter_cp('\u{0627}'));
        assert!(is_joining_letter_cp('\u{0710}'));
        assert!(is_bidi_number_cp('\u{0660}'));
        assert!(is_legacy_arabic_cp('\u{0627}'));
    }

    #[test]
    fn generated_disallowed_and_map_spot_checks() {
        assert!(is_disallowed_cp('\u{2028}'));
        assert!(is_disallowed_cp('\u{0890}'));
        assert!(is_disallowed_cp('\u{FE52}'));
        assert!(!is_disallowed_cp('a'));
        assert!(is_uts46_ignored_cp('\u{00AD}'));
        assert!(is_uts46_ignored_cp('\u{1BCA0}'));
        assert!(uts46_needs_map_cp('\u{00BC}'));
        assert!(maps_nfkc_via_space_cp('\u{00A8}'));
        assert!(is_arabic_ext_b_letter_cp('\u{088F}'));
        assert!(!is_arabic_ext_b_letter_cp('\u{0888}'));
        assert!(is_ltr_letter_block('ü'));
    }

    #[test]
    fn tables_are_sorted_and_disjoint() {
        for (name, table) in [
            ("BIDI_IGNORED", BIDI_IGNORED),
            ("JOINING_LETTERS", JOINING_LETTERS),
            ("RTL_NON_LETTERS", RTL_NON_LETTERS),
            ("RTL_ALPHABETIC", RTL_ALPHABETIC),
            ("BIDI_NUMBERS", BIDI_NUMBERS),
            ("LEGACY_ARABIC", LEGACY_ARABIC),
            ("DISALLOWED", DISALLOWED),
            ("LTR_LETTERS", LTR_LETTERS),
            ("ARABIC_EXT_B_LETTERS", ARABIC_EXT_B_LETTERS),
            ("ARABIC_EXT_B_COMPATIBLE", ARABIC_EXT_B_COMPATIBLE),
            ("ARABIC_EXT_B_ERA_MARKS", ARABIC_EXT_B_ERA_MARKS),
            ("ARABIC_EXT_B_ERA_OK", ARABIC_EXT_B_ERA_OK),
            ("NFKC_VIA_SPACE", NFKC_VIA_SPACE),
            ("UTS46_IGNORED", UTS46_IGNORED),
            ("UTS46_NEEDS_MAP", UTS46_NEEDS_MAP),
        ] {
            assert!(!table.is_empty(), "{name} empty");
            for window in table.windows(2) {
                let (a_lo, a_hi) = window[0];
                let (b_lo, _) = window[1];
                assert!(a_lo <= a_hi, "{name}: inverted {a_lo:04X}..={a_hi:04X}");
                assert!(
                    a_hi.saturating_add(1) < b_lo,
                    "{name}: overlap/adjacent {a_lo:04X}..={a_hi:04X} vs {b_lo:04X}"
                );
            }
        }
    }
}