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::ule::AsULE;
11use zerovec::ZeroVec;
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#[allow(clippy::indexing_slicing)] // windows
16#[allow(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() % 2 == 0
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(_) => 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::{deconstruct_range, is_valid_zv, PotentialCodePoint};
48    use core::char;
49    use zerovec::ZeroVec;
50
51    fn make_zv(slice: &[u32]) -> ZeroVec<PotentialCodePoint> {
52        slice
53            .iter()
54            .copied()
55            .map(PotentialCodePoint::from_u24)
56            .collect()
57    }
58    #[test]
59    fn test_is_valid_zv() {
60        let check = make_zv(&[0x2, 0x3, 0x4, 0x5]);
61        assert!(is_valid_zv(&check));
62    }
63
64    #[test]
65    fn test_is_valid_zv_empty() {
66        let check = make_zv(&[]);
67        assert!(is_valid_zv(&check));
68    }
69
70    #[test]
71    fn test_is_valid_zv_overlapping() {
72        let check = make_zv(&[0x2, 0x5, 0x4, 0x6]);
73        assert!(!is_valid_zv(&check));
74    }
75
76    #[test]
77    fn test_is_valid_zv_out_of_order() {
78        let check = make_zv(&[0x5, 0x4, 0x5, 0x6, 0x7]);
79        assert!(!is_valid_zv(&check));
80    }
81
82    #[test]
83    fn test_is_valid_zv_duplicate() {
84        let check = make_zv(&[0x1, 0x2, 0x3, 0x3, 0x5]);
85        assert!(!is_valid_zv(&check));
86    }
87
88    #[test]
89    fn test_is_valid_zv_odd() {
90        let check = make_zv(&[0x1, 0x2, 0x3, 0x4, 0x5]);
91        assert!(!is_valid_zv(&check));
92    }
93
94    #[test]
95    fn test_is_valid_zv_out_of_range() {
96        let check = make_zv(&[0x1, 0x2, 0x3, 0x4, (char::MAX as u32) + 1]);
97        assert!(!is_valid_zv(&check));
98    }
99
100    // deconstruct_range
101
102    #[test]
103    fn test_deconstruct_range() {
104        let expected = (0x41, 0x45);
105        let check = deconstruct_range('A'..'E'); // Range
106        assert_eq!(check, expected);
107        let check = deconstruct_range('A'..='D'); // Range Inclusive
108        assert_eq!(check, expected);
109        let check = deconstruct_range('A'..); // Range From
110        assert_eq!(check, (0x41, (char::MAX as u32) + 1));
111        let check = deconstruct_range(..'A'); // Range To
112        assert_eq!(check, (0x0, 0x41));
113        let check = deconstruct_range(..='A'); // Range To Inclusive
114        assert_eq!(check, (0x0, 0x42));
115        let check = deconstruct_range::<char>(..); // Range Full
116        assert_eq!(check, (0x0, (char::MAX as u32) + 1));
117    }
118}