Skip to main content

icu_collections/codepointinvlist/
utils.rs

1// This file is part of ICU4X. For terms of use, please see the file
2// called LICENSE at the top level of the ICU4X source tree
3// (online at: https://github.com/unicode-org/icu4x/blob/main/LICENSE ).
4
5use core::{
6    char,
7    ops::{Bound::*, RangeBounds},
8};
9use potential_utf::PotentialCodePoint;
10use zerovec::ZeroVec;
11use zerovec::ule::AsULE;
12
13/// Returns whether the vector is sorted ascending non inclusive, of even length,
14/// and within the bounds of `0x0 -> 0x10FFFF + 1` inclusive.
15#[expect(clippy::indexing_slicing)] // windows
16#[expect(clippy::unwrap_used)] // by is_empty check
17pub fn is_valid_zv(inv_list_zv: &ZeroVec<'_, PotentialCodePoint>) -> bool {
18    inv_list_zv.is_empty()
19        || (inv_list_zv.len().is_multiple_of(2)
20            && inv_list_zv.as_ule_slice().windows(2).all(|chunk| {
21                <PotentialCodePoint as AsULE>::from_unaligned(chunk[0])
22                    < <PotentialCodePoint as AsULE>::from_unaligned(chunk[1])
23            })
24            && u32::from(inv_list_zv.last().unwrap()) <= char::MAX as u32 + 1)
25}
26
27/// Returns start (inclusive) and end (exclusive) bounds of [`RangeBounds`]
28pub fn deconstruct_range<T>(range: impl RangeBounds<T>) -> (u32, u32)
29where
30    T: Into<u32> + Copy,
31{
32    let from = match range.start_bound() {
33        Included(b) => (*b).into(),
34        Excluded(_) => ::core::panicking::panic("internal error: entered unreachable code")unreachable!(),
35        Unbounded => 0,
36    };
37    let till = match range.end_bound() {
38        Included(b) => (*b).into() + 1,
39        Excluded(b) => (*b).into(),
40        Unbounded => (char::MAX as u32) + 1,
41    };
42    (from, till)
43}
44
45#[cfg(test)]
46mod tests {
47    use super::{PotentialCodePoint, deconstruct_range, is_valid_zv};
48    use zerovec::ZeroVec;
49
50    fn make_zv(slice: &[u32]) -> ZeroVec<'_, PotentialCodePoint> {
51        slice
52            .iter()
53            .copied()
54            .map(PotentialCodePoint::from_u24)
55            .collect()
56    }
57    #[test]
58    fn test_is_valid_zv() {
59        let check = make_zv(&[0x2, 0x3, 0x4, 0x5]);
60        assert!(is_valid_zv(&check));
61    }
62
63    #[test]
64    fn test_is_valid_zv_empty() {
65        let check = make_zv(&[]);
66        assert!(is_valid_zv(&check));
67    }
68
69    #[test]
70    fn test_is_valid_zv_overlapping() {
71        let check = make_zv(&[0x2, 0x5, 0x4, 0x6]);
72        assert!(!is_valid_zv(&check));
73    }
74
75    #[test]
76    fn test_is_valid_zv_out_of_order() {
77        let check = make_zv(&[0x5, 0x4, 0x5, 0x6, 0x7]);
78        assert!(!is_valid_zv(&check));
79    }
80
81    #[test]
82    fn test_is_valid_zv_duplicate() {
83        let check = make_zv(&[0x1, 0x2, 0x3, 0x3, 0x5]);
84        assert!(!is_valid_zv(&check));
85    }
86
87    #[test]
88    fn test_is_valid_zv_odd() {
89        let check = make_zv(&[0x1, 0x2, 0x3, 0x4, 0x5]);
90        assert!(!is_valid_zv(&check));
91    }
92
93    #[test]
94    fn test_is_valid_zv_out_of_range() {
95        let check = make_zv(&[0x1, 0x2, 0x3, 0x4, (char::MAX as u32) + 1]);
96        assert!(!is_valid_zv(&check));
97    }
98
99    // deconstruct_range
100
101    #[test]
102    fn test_deconstruct_range() {
103        let expected = (0x41, 0x45);
104        let check = deconstruct_range('A'..'E'); // Range
105        assert_eq!(check, expected);
106        let check = deconstruct_range('A'..='D'); // Range Inclusive
107        assert_eq!(check, expected);
108        let check = deconstruct_range('A'..); // Range From
109        assert_eq!(check, (0x41, (char::MAX as u32) + 1));
110        let check = deconstruct_range(..'A'); // Range To
111        assert_eq!(check, (0x0, 0x41));
112        let check = deconstruct_range(..='A'); // Range To Inclusive
113        assert_eq!(check, (0x0, 0x42));
114        let check = deconstruct_range::<char>(..); // Range Full
115        assert_eq!(check, (0x0, (char::MAX as u32) + 1));
116    }
117}