Skip to main content

zerotrie/builder/
slice_indices.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
5#[cfg(feature = "alloc")]
6use alloc::vec::Vec;
7
8/// Const function to evaluate `a < b` lexicographically.
9const fn is_less_than(a: &[u8], b: &[u8]) -> bool {
10    let mut i = 0;
11    #[allow(clippy::indexing_slicing, reason = "bounds checked in while loop")]
12    while i < a.len() && i < b.len() {
13        if a[i] < b[i] {
14            return true;
15        }
16        if a[i] > b[i] {
17            return false;
18        }
19        i += 1;
20    }
21    a.len() < b.len()
22}
23
24/// Const function to evaluate `a[..prefix_len] == b[..prefix_len]`
25///
26/// # Panics
27///
28/// Panics if `prefix_len` is longer than either `a` or `b`.
29pub(crate) const fn prefix_eq_or_panic(a: &[u8], b: &[u8], prefix_len: usize) -> bool {
30    let mut i = 0;
31    #[allow(clippy::indexing_slicing, reason = "documented panic")]
32    while i < prefix_len {
33        if a[i] != b[i] {
34            return false;
35        }
36        i += 1;
37    }
38    true
39}
40
41/// An abstraction over a slice of key-value pairs that can have either `&[u8]`
42/// or `&str` keys. This is used in the builder to avoid unsound casts.
43#[derive(#[automatically_derived]
impl<'a> ::core::marker::Copy for ByteSliceWithIndices<'a> { }Copy, #[automatically_derived]
impl<'a> ::core::clone::Clone for ByteSliceWithIndices<'a> {
    #[inline]
    fn clone(&self) -> ByteSliceWithIndices<'a> {
        let _: ::core::clone::AssertParamIsClone<&'a [(&'a [u8], usize)]>;
        let _: ::core::clone::AssertParamIsClone<&'a [(&'a str, usize)]>;
        *self
    }
}Clone)]
44pub(crate) enum ByteSliceWithIndices<'a> {
45    Bytes(&'a [(&'a [u8], usize)]),
46    Str(&'a [(&'a str, usize)]),
47}
48
49impl<'a> ByteSliceWithIndices<'a> {
50    pub const fn from_byte_slice(s: &'a [(&'a [u8], usize)]) -> Self {
51        Self::Bytes(s)
52    }
53
54    pub const fn from_str_slice(s: &'a [(&'a str, usize)]) -> Self {
55        Self::Str(s)
56    }
57
58    pub const fn len(&self) -> usize {
59        match self {
60            Self::Bytes(s) => s.len(),
61            Self::Str(s) => s.len(),
62        }
63    }
64
65    /// Gets the bytes and index at `index`
66    ///
67    /// # Panics
68    ///
69    /// Panics when `index >= self.len()`
70    #[allow(clippy::indexing_slicing, reason = "documented")]
71    pub const fn get_or_panic(&self, index: usize) -> (&'a [u8], usize) {
72        match self {
73            Self::Bytes(s) => s[index],
74            Self::Str(s) => {
75                let (key, value) = s[index];
76                (key.as_bytes(), value)
77            }
78        }
79    }
80
81    pub const fn last(&self) -> Option<(&'a [u8], usize)> {
82        if self.len() == 0 {
83            None
84        } else {
85            Some(self.get_or_panic(self.len() - 1))
86        }
87    }
88
89    #[cfg(feature = "alloc")]
90    pub fn to_vec_u8(self) -> Vec<(&'a [u8], usize)> {
91        match self {
92            Self::Bytes(s) => s.to_vec(),
93            Self::Str(s) => s.iter().map(|(k, v)| (k.as_bytes(), *v)).collect(),
94        }
95    }
96
97    pub const fn is_sorted(self) -> bool {
98        let mut i = 0;
99        let mut prev: Option<&'a [u8]> = None;
100
101        while i < self.len() {
102            let (ascii_str, _) = self.get_or_panic(i);
103            match prev {
104                None => (),
105                Some(prev) => {
106                    if !is_less_than(prev, ascii_str) {
107                        return false;
108                    }
109                }
110            };
111            prev = Some(ascii_str);
112            i += 1;
113        }
114        true
115    }
116
117    #[cfg(feature = "alloc")]
118    pub fn is_all_ascii(&self) -> bool {
119        match self {
120            Self::Bytes(s) => s
121                .iter()
122                .all(|(slice, _)| slice.iter().all(|c| c.is_ascii())),
123            Self::Str(s) => s
124                .iter()
125                .all(|(slice, _)| slice.as_bytes().iter().all(|c| c.is_ascii())),
126        }
127    }
128}