Skip to main content

icu_collections/char16trie/
trie.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::string::String;
7use yoke::Yokeable;
8use zerofrom::ZeroFrom;
9use zerovec::{ZeroSlice, ZeroVec};
10
11// Match-node lead unit values, after masking off intermediate-value bits:
12
13// 00..0f: Branch node. If node!=0 then the length is node+1, otherwise
14// the length is one more than the next byte.
15
16// For a branch sub-node with at most this many entries, we drop down
17// to a linear search.
18const MAX_BRANCH_LINEAR_SUB_NODE_LENGTH: usize = 5;
19
20// 0030..003f: Linear-match node, match 1..16 units and continue reading the next node.
21const MIN_LINEAR_MATCH: u16 = 0x30;
22const MAX_LINEAR_MATCH_LENGTH: u16 = 0x10;
23
24// Match-node lead unit bits 14..6 for the optional intermediate value.
25// If these bits are 0, then there is no intermediate value.
26// Otherwise, see the *NodeValue* constants below.
27const MIN_VALUE_LEAD: u16 = MIN_LINEAR_MATCH + MAX_LINEAR_MATCH_LENGTH; // 0x40
28const NODE_TYPE_MASK: u16 = MIN_VALUE_LEAD - 1; // 0x003f
29
30// A final-value node has bit 15 set.
31const VALUE_IS_FINAL: u16 = 0x8000;
32
33// Compact value: After testing bit 0, shift right by 15 and then use the following thresholds.
34const MAX_ONE_UNIT_VALUE: u16 = 0x3fff;
35
36const MIN_TWO_UNIT_VALUE_LEAD: u16 = MAX_ONE_UNIT_VALUE + 1; // 0x4000
37
38const MAX_ONE_UNIT_NODE_VALUE: u16 = 0xff;
39
40const MIN_TWO_UNIT_NODE_VALUE_LEAD: u16 = MIN_VALUE_LEAD + ((MAX_ONE_UNIT_NODE_VALUE + 1) << 6); // 0x4040
41
42const THREE_UNIT_NODE_VALUE_LEAD: u16 = 0x7fc0;
43
44const THREE_UNIT_VALUE_LEAD: u16 = 0x7fff;
45
46// Compact delta integers.
47const MAX_ONE_UNIT_DELTA: u16 = 0xfbff;
48const MIN_TWO_UNIT_DELTA_LEAD: u16 = MAX_ONE_UNIT_DELTA + 1; // 0xfc00
49const THREE_UNIT_DELTA_LEAD: u16 = 0xffff;
50
51fn skip_value(pos: usize, lead: u16) -> usize {
52    if lead < MIN_TWO_UNIT_VALUE_LEAD {
53        pos
54    } else if lead < THREE_UNIT_VALUE_LEAD {
55        pos + 1
56    } else {
57        pos + 2
58    }
59}
60
61fn skip_node_value(pos: usize, lead: u16) -> usize {
62    if lead < MIN_TWO_UNIT_NODE_VALUE_LEAD {
63        pos
64    } else if lead < THREE_UNIT_NODE_VALUE_LEAD {
65        pos + 1
66    } else {
67        pos + 2
68    }
69}
70
71/// This struct represents a de-serialized `Char16Trie` that was exported from
72/// ICU binary data.
73///
74/// Light-weight, non-const reader class for a `CharsTrie`. Traverses a
75/// char-serialized data structure with minimal state, for mapping 16-bit-unit
76/// sequences to non-negative integer values.
77///
78/// For more information:
79/// - [ICU4C UCharsTrie](https://unicode-org.github.io/icu-docs/apidoc/released/icu4c/classicu_1_1UCharsTrie.html)
80/// - [ICU4J CharsTrie](https://unicode-org.github.io/icu-docs/apidoc/released/icu4j/com/ibm/icu/util/CharsTrie.html) API.
81#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
82#[cfg_attr(feature = "databake", derive(databake::Bake))]
83#[cfg_attr(feature = "databake", databake(path = icu_collections::char16trie))]
84#[derive(#[automatically_derived]#[allow(clippy::exhaustive_structs)]
impl<'data> ::core::clone::Clone for Char16Trie<'data> {
    #[inline]
    fn clone(&self) -> Self {
        Char16Trie { data: ::core::clone::Clone::clone(&self.data) }
    }
}Clone, #[automatically_derived]#[allow(clippy::exhaustive_structs)]
impl<'data> ::core::fmt::Debug for Char16Trie<'data> {
    #[inline]
    fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
        ::core::fmt::Formatter::debug_struct_field1_finish(f,
            "Char16Trie data", &&self.data)
    }
}Debug, #[automatically_derived]#[allow(clippy::exhaustive_structs)]
impl<'data> ::core::marker::StructuralPartialEq for Char16Trie<'data> { }
#[automatically_derived]#[allow(clippy::exhaustive_structs)]
impl<'data> ::core::cmp::PartialEq for Char16Trie<'data> {
    #[inline]
    fn eq(&self, other: &Self) -> bool { self.data == other.data }
}PartialEq, #[automatically_derived]#[allow(clippy::exhaustive_structs)]
impl<'data> ::core::cmp::Eq for Char16Trie<'data> {
    #[inline]
    #[doc(hidden)]
    #[coverage(off)]
    fn assert_fields_are_eq(&self) {
        let _: ::core::cmp::AssertParamIsEq<ZeroVec<'data, u16>>;
    }
}Eq, impl<'zf, 'zf_inner> zerofrom::ZeroFrom<'zf, Char16Trie<'zf_inner>> for
    Char16Trie<'zf> where  {
    fn zero_from(this: &'zf Char16Trie<'zf_inner>) -> Self {
        match *this {
            Char16Trie { data: ref __binding_0 } => {
                Char16Trie {
                    data: <ZeroVec<'zf, u16> as
                            zerofrom::ZeroFrom<'zf,
                            ZeroVec<'zf_inner, u16>>>::zero_from(__binding_0),
                }
            }
        }
    }
}ZeroFrom, unsafe impl<'yoke> yoke::Yokeable<'yoke> for Char16Trie<'static> where  {
    type Output = Char16Trie<'yoke>;
    #[inline]
    fn transform(&'yoke self) -> &'yoke Self::Output {
        if false { let _: *const &'yoke Self::Output = &raw const self; }
        self
    }
    #[inline]
    fn transform_owned(self) -> Self::Output { self }
    #[inline]
    unsafe fn make(from: Self::Output) -> Self {
        ::core::mem::transmute::<Self::Output, Self>(from)
    }
    #[inline]
    fn transform_mut<F>(&'yoke mut self, f: F) where F: 'static +
        for<'_yoke> FnOnce(&'_yoke mut Self::Output) {
        let y = unsafe { &mut *(self as *mut Self as *mut Self::Output) };
        f(y)
    }
}Yokeable)]
85#[allow(clippy::exhaustive_structs)] // effectively exhaustive, struct-constructible for baking
86pub struct Char16Trie<'data> {
87    /// An array of u16 containing the trie data.
88    #[cfg_attr(feature = "serde", serde(borrow, alias = "trie_data"))]
89    #[doc(hidden)] // #2417
90    pub data: ZeroVec<'data, u16>,
91}
92
93impl<'data> Char16Trie<'data> {
94    /// Returns a new [`Char16Trie`] with ownership of the provided data.
95    #[inline]
96    pub fn new(data: ZeroVec<'data, u16>) -> Self {
97        Self { data }
98    }
99
100    /// Returns a new [`Char16TrieCursor`] backed by borrowed data from the `trie` data.
101    #[inline]
102    pub fn cursor(&self) -> Char16TrieCursor<'_> {
103        Char16TrieCursor::new(&self.data)
104    }
105
106    /// Returns a new [`Char16TrieIterator`] backed by borrowed data from the `trie` data
107    #[deprecated(since = "2.4.0", note = "use `Char16Trie::cursor`")]
108    #[allow(deprecated)]
109    #[inline]
110    pub fn iter(&self) -> Char16TrieIterator<'_> {
111        Char16TrieIterator::new(&self.data)
112    }
113
114    /// Returns an iterator over the key/value pairs in this trie.
115    ///
116    /// This is called `iter2` because of the deprecated `iter` API, which
117    /// is conceptually a lookup cursor, not an iterator.
118    ///
119    /// ✨ *Enabled with the `alloc` Cargo feature.*
120    ///
121    /// # Examples
122    ///
123    /// ```
124    /// use icu::collections::char16trie::Char16Trie;
125    /// use zerovec::ZeroVec;
126    ///
127    /// // A Char16Trie containing the ASCII characters 'a' and 'ab'.
128    /// let trie_data = [48, 97, 176, 98, 32868];
129    /// let trie = Char16Trie::new(ZeroVec::from_slice_or_alloc(&trie_data));
130    ///
131    /// let mut it = trie.iter2();
132    /// assert_eq!(it.next(), Some(("a".into(), 1)));
133    /// assert_eq!(it.next(), Some(("ab".into(), 100)));
134    /// assert_eq!(it.next(), None);
135    /// ```
136    #[cfg(feature = "alloc")]
137    pub fn iter2(&self) -> impl Iterator<Item = (String, i32)> + '_ {
138        let cursor = self.cursor();
139        let mut root_value = cursor.value();
140        let mut stack = alloc::vec![(cursor, 0)];
141        let mut prefix = String::new();
142        let mut lead_surrogate = None;
143        core::iter::from_fn(move || {
144            if let Some(v) = root_value.take() {
145                return Some((String::new(), v));
146            }
147            while let Some((cursor, i)) = stack.last_mut() {
148                let mut next_cursor = cursor.clone();
149                if let Some(c) = next_cursor.probe(*i) {
150                    *i += 1;
151
152                    if let Some(lead) = lead_surrogate.take() {
153                        prefix.push(
154                            char::decode_utf16([lead, c])
155                                .next()
156                                .and_then(Result::ok)
157                                .unwrap_or(char::REPLACEMENT_CHARACTER),
158                        );
159                    } else if matches!(c, 0xD800..0xDC00) {
160                        lead_surrogate = Some(c);
161                    } else {
162                        prefix
163                            .push(char::from_u32(c as u32).unwrap_or(char::REPLACEMENT_CHARACTER));
164                    }
165
166                    let v = next_cursor.value();
167                    stack.push((next_cursor, 0));
168                    if let Some(v) = v {
169                        return Some((prefix.clone(), v));
170                    }
171                } else {
172                    stack.pop();
173                    if lead_surrogate.take().is_none()
174                        && let Some(ch @ '\u{10000}'..) = prefix.pop()
175                    {
176                        lead_surrogate = Some(u16_lead(ch as u32));
177                    }
178                }
179            }
180            None
181        })
182    }
183}
184
185/// A cursor into a [`Char16Trie`], useful for stepwise lookup.
186#[derive(#[automatically_derived]impl<'a> ::core::clone::Clone for Char16TrieCursor<'a>
    {
    #[inline]
    fn clone(&self) -> Self {
        Char16TrieCursor {
            trie: ::core::clone::Clone::clone(&self.trie),
            pos: ::core::clone::Clone::clone(&self.pos),
            remaining_match_length: ::core::clone::Clone::clone(&self.remaining_match_length),
        }
    }
}Clone, #[automatically_derived]impl<'a> ::core::fmt::Debug for Char16TrieCursor<'a> {
    #[inline]
    fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
        ::core::fmt::Formatter::debug_struct_field3_finish(f,
            "Char16TrieCursor trie pos remaining_match_length", &self.trie,
            &self.pos, &&self.remaining_match_length)
    }
}Debug)]
187pub struct Char16TrieCursor<'a> {
188    /// A reference to the [`Char16Trie`] data to iterate over.
189    trie: &'a ZeroSlice<u16>,
190    /// Index of next trie unit to read, or `None` if there are no more matches.
191    pos: Option<usize>,
192    /// Remaining length of a linear-match node, minus 1, or `None` if not in
193    /// such a node.
194    remaining_match_length: Option<u8>,
195}
196
197/// This struct represents an iterator over a [`Char16Trie`].
198#[deprecated(since = "2.4.0", note = "use `Char16TrieCursor`")]
199#[derive(#[automatically_derived]impl<'a> ::core::clone::Clone for
    Char16TrieIterator<'a> {
    #[inline]
    fn clone(&self) -> Self {
        Char16TrieIterator(::core::clone::Clone::clone(&self.0))
    }
}Clone, #[automatically_derived]impl<'a> ::core::fmt::Debug for Char16TrieIterator<'a>
    {
    #[inline]
    fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
        ::core::fmt::Formatter::debug_struct_field1_finish(f,
            "Char16TrieIterator", &&self.0)
    }
}Debug)]
200pub struct Char16TrieIterator<'a>(Char16TrieCursor<'a>);
201
202/// An enum representing the return value from a lookup in [`Char16Trie`].
203#[deprecated(since = "2.4.0", note = "use `Char16TrieCursor`")]
204#[derive(#[automatically_derived]#[doc(hidden)]#[allow(clippy::exhaustive_enums)]
unsafe impl ::core::clone::TrivialClone for TrieResult { }
#[automatically_derived]#[allow(clippy::exhaustive_enums)]
impl ::core::clone::Clone for TrieResult {
    #[inline]
    fn clone(&self) -> Self {
        let _: ::core::clone::AssertParamIsClone<i32>;
        *self
    }
}Clone, #[automatically_derived]#[allow(clippy::exhaustive_enums)]
impl ::core::marker::Copy for TrieResult { }Copy, #[automatically_derived]#[allow(clippy::exhaustive_enums)]
impl ::core::fmt::Debug for TrieResult {
    #[inline]
    fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
        match self {
            Self::NoMatch => ::core::fmt::Formatter::write_str(f, "NoMatch"),
            Self::NoValue => ::core::fmt::Formatter::write_str(f, "NoValue"),
            Self::FinalValue(__self_0) =>
                ::core::fmt::Formatter::debug_struct_field1_finish(f,
                    "FinalValue", &__self_0),
            Self::Intermediate(__self_0) =>
                ::core::fmt::Formatter::debug_struct_field1_finish(f,
                    "Intermediate", &__self_0),
        }
    }
}Debug, #[automatically_derived]#[allow(clippy::exhaustive_enums)]
impl ::core::marker::StructuralPartialEq for TrieResult { }
#[automatically_derived]#[allow(clippy::exhaustive_enums)]
impl ::core::cmp::PartialEq for TrieResult {
    #[inline]
    fn eq(&self, other: &Self) -> bool {
        ::core::intrinsics::discriminant_value(self) ==
                ::core::intrinsics::discriminant_value(other) &&
            match (self, other) {
                (Self::FinalValue(__self_0), Self::FinalValue(__arg1_0)) =>
                    __self_0 == __arg1_0,
                (Self::Intermediate(__self_0), Self::Intermediate(__arg1_0))
                    => __self_0 == __arg1_0,
                _ => true,
            }
    }
}PartialEq)]
205#[allow(clippy::exhaustive_enums)]
206pub enum TrieResult {
207    /// The input unit(s) did not continue a matching string.
208    /// Once `next()` returns `TrieResult::NoMatch`, all further calls to `next()`
209    /// will also return `TrieResult::NoMatch`.
210    NoMatch,
211    /// The input unit(s) matched a string but there is no value for the string
212    /// so far.  (It is a prefix of a longer string.)
213    NoValue,
214    /// The input unit(s) continued a matching string and there is a value for
215    /// the string so far. No further input byte/unit can continue a matching
216    /// string.
217    FinalValue(i32),
218    /// The input unit(s) continued a matching string and there is a value for
219    /// the string so far.  Another input byte/unit can continue a matching
220    /// string.
221    Intermediate(i32),
222}
223
224// Get the lead surrogate (0xd800..0xdbff) for a
225// supplementary code point (0x10000..0x10ffff).
226// @param supplementary 32-bit code point (U+10000..U+10ffff)
227// @return lead surrogate (U+d800..U+dbff) for supplementary
228fn u16_lead(supplementary: u32) -> u16 {
229    (((supplementary) >> 10) + 0xd7c0) as u16
230}
231
232// Get the trail surrogate (0xdc00..0xdfff) for a
233// supplementary code point (0x10000..0x10ffff).
234// @param supplementary 32-bit code point (U+10000..U+10ffff)
235// @return trail surrogate (U+dc00..U+dfff) for supplementary
236fn u16_tail(supplementary: u32) -> u16 {
237    (((supplementary) & 0x3ff) | 0xdc00) as u16
238}
239
240impl<'a> Char16TrieCursor<'a> {
241    /// Returns a new [`Char16TrieCursor`] backed by borrowed data for the `trie` array.
242    #[inline]
243    pub fn new(trie: &'a ZeroSlice<u16>) -> Self {
244        Self {
245            trie,
246            pos: Some(0),
247            remaining_match_length: None,
248        }
249    }
250
251    /// Steps the cursor one `char` into the trie.
252    ///
253    /// # Examples
254    ///
255    /// ```
256    /// use icu::collections::char16trie::Char16Trie;
257    /// use zerovec::ZeroVec;
258    ///
259    /// // A Char16Trie containing the ASCII characters 'a' and 'ab'.
260    /// let trie_data = [48, 97, 176, 98, 32868];
261    /// let trie = Char16Trie::new(ZeroVec::from_slice_or_alloc(&trie_data));
262    ///
263    /// let mut cursor = trie.cursor();
264    /// cursor.step('a');
265    /// assert_eq!(cursor.value(), Some(1));
266    /// assert!(!cursor.is_empty());
267    /// cursor.step('b');
268    /// assert_eq!(cursor.value(), Some(100));
269    /// assert!(cursor.is_empty());
270    /// cursor.step('c');
271    /// assert_eq!(cursor.value(), None);
272    /// assert!(cursor.is_empty());
273    /// ```
274    #[inline]
275    pub fn step(&mut self, c: char) {
276        self.step32(c as u32);
277    }
278
279    /// Steps the cursor one code point (up to `0x10ffff`) into the trie.
280    ///
281    /// # Examples
282    ///
283    /// ```
284    /// use icu::collections::char16trie::Char16Trie;
285    /// use zerovec::ZeroVec;
286    ///
287    /// // A Char16Trie containing the ASCII characters 'a' and 'ab'.
288    /// let trie_data = [48, 97, 176, 98, 32868];
289    /// let trie = Char16Trie::new(ZeroVec::from_slice_or_alloc(&trie_data));
290    ///
291    /// let mut cursor = trie.cursor();
292    /// cursor.step32('a' as u32);
293    /// assert_eq!(cursor.value(), Some(1));
294    /// assert!(!cursor.is_empty());
295    /// cursor.step32('b' as u32);
296    /// assert_eq!(cursor.value(), Some(100));
297    /// assert!(cursor.is_empty());
298    /// cursor.step32('c' as u32);
299    /// assert_eq!(cursor.value(), None);
300    /// assert!(cursor.is_empty());
301    /// ```
302    #[inline]
303    pub fn step32(&mut self, c: u32) {
304        let c = if let Ok(c) = u16::try_from(c) {
305            c
306        } else {
307            self.step16(u16_lead(c));
308            u16_tail(c)
309        };
310        self.step16(c);
311    }
312
313    /// Steps the cursor one 16-bit code unit into the trie.
314    ///
315    /// # Examples
316    ///
317    /// ```
318    /// use icu::collections::char16trie::Char16Trie;
319    /// use zerovec::ZeroVec;
320    ///
321    /// // A Char16Trie containing the ASCII characters 'a' and 'ab'.
322    /// let trie_data = [48, 97, 176, 98, 32868];
323    /// let trie = Char16Trie::new(ZeroVec::from_slice_or_alloc(&trie_data));
324    ///
325    /// let mut cursor = trie.cursor();
326    /// cursor.step16('a' as u16);
327    /// assert_eq!(cursor.value(), Some(1));
328    /// assert!(!cursor.is_empty());
329    /// cursor.step16('b' as u16);
330    /// assert_eq!(cursor.value(), Some(100));
331    /// assert!(cursor.is_empty());
332    /// cursor.step16('c' as u16);
333    /// assert_eq!(cursor.value(), None);
334    /// assert!(cursor.is_empty());
335    /// ```
336    #[inline]
337    pub fn step16(&mut self, c: u16) {
338        let Some(pos) = self.pos else {
339            return;
340        };
341        self.pos = if let Some(length) = self.remaining_match_length {
342            // Remaining part of a linear-match node
343            if Some(c) == self.trie.get(pos) {
344                self.remaining_match_length = length.checked_sub(1);
345                Some(pos + 1)
346            } else {
347                None
348            }
349        } else {
350            self.next_impl(pos, c)
351        };
352    }
353
354    #[cfg(feature = "alloc")]
355    fn probe(&mut self, mut index: usize) -> Option<u16> {
356        let mut pos = self.pos?;
357        if let Some(length) = self.remaining_match_length {
358            if index > 0 {
359                return None;
360            }
361            let unit = self.trie.get(pos)?;
362            self.remaining_match_length = length.checked_sub(1);
363            self.pos = Some(pos + 1);
364            return Some(unit);
365        }
366        let mut node = self.trie.get(pos)?;
367        pos += 1;
368        if node >= MIN_VALUE_LEAD {
369            if (node & VALUE_IS_FINAL) != 0 {
370                return None;
371            }
372            // Skip intermediate value.
373            pos = skip_node_value(pos, node);
374            node &= NODE_TYPE_MASK;
375        }
376        if node >= MIN_LINEAR_MATCH {
377            if index > 0 {
378                return None;
379            }
380            let length = (node - MIN_LINEAR_MATCH) as u8;
381            let unit = self.trie.get(pos)?;
382            self.remaining_match_length = length.checked_sub(1);
383            self.pos = Some(pos + 1);
384            return Some(unit);
385        }
386        let mut length = node as usize;
387        if length == 0 {
388            length = self.trie.get(pos)? as usize;
389            pos += 1;
390        }
391        length += 1;
392        if index >= length {
393            return None;
394        }
395
396        // The length of the branch is the number of units to select from.
397        // The data structure encodes a binary search.
398        while length > MAX_BRANCH_LINEAR_SUB_NODE_LENGTH {
399            let half = length >> 1;
400            if index < half {
401                length = half;
402                pos = self.jump_by_delta(pos + 1)?;
403            } else {
404                index -= half;
405                length -= half;
406                pos = self.skip_delta(pos + 1)?;
407            }
408        }
409        // Drop down to linear search for the last few units.
410        while index > 0 {
411            index -= 1;
412            length -= 1;
413            pos = self.skip_value(pos + 1)?;
414        }
415        let unit = self.trie.get(pos)?;
416        pos += 1;
417        if length > 1 {
418            let node = self.trie.get(pos)?;
419            if node & VALUE_IS_FINAL == 0 {
420                // Use the non-final value as the jump delta.
421                pos += 1;
422
423                if node < MIN_TWO_UNIT_VALUE_LEAD {
424                    pos += node as usize;
425                } else if node < THREE_UNIT_VALUE_LEAD {
426                    pos += (((node - MIN_TWO_UNIT_VALUE_LEAD) as u32) << 16) as usize
427                        | self.trie.get(pos)? as usize;
428                    pos += 1;
429                } else {
430                    pos +=
431                        ((self.trie.get(pos)? as usize) << 16) | self.trie.get(pos + 1)? as usize;
432                    pos += 2;
433                }
434            }
435        }
436        self.pos = Some(pos);
437        Some(unit)
438    }
439
440    /// Returns the value at the current position.
441    #[inline]
442    pub fn value(&self) -> Option<i32> {
443        if self.remaining_match_length.is_some() {
444            return None;
445        }
446        let pos = self.pos?;
447        let lead_unit = self.trie.get(pos)?;
448        if lead_unit < MIN_VALUE_LEAD {
449            None
450        } else if lead_unit & VALUE_IS_FINAL != 0 {
451            let v = self.read_value(pos + 1, lead_unit & 0x7fff);
452            if true {
    if !v.is_some() {
        ::core::panicking::panic("assertion failed: v.is_some()")
    };
};debug_assert!(v.is_some());
453            Some(v.unwrap_or(0))
454        } else {
455            let v = self.read_node_value(pos + 1, lead_unit);
456            if true {
    if !v.is_some() {
        ::core::panicking::panic("assertion failed: v.is_some()")
    };
};debug_assert!(v.is_some());
457            Some(v.unwrap_or(0))
458        }
459    }
460
461    /// Checks whether the cursor points to an empty trie (i.e., no further
462    /// units can be matched from the current position).
463    ///
464    /// Use this to determine when to stop iterating.
465    #[inline]
466    pub fn is_empty(&self) -> bool {
467        let Some(pos) = self.pos else {
468            return true;
469        };
470        self.remaining_match_length.is_none()
471            && self.trie.get(pos).is_none_or(|u| u & VALUE_IS_FINAL != 0)
472    }
473
474    fn branch_next(&self, mut pos: usize, mut length: usize, in_unit: u16) -> Option<usize> {
475        if length == 0 {
476            length = self.trie.get(pos)? as usize;
477            pos += 1;
478        }
479        length += 1;
480
481        // The length of the branch is the number of units to select from.
482        // The data structure encodes a binary search.
483        while length > MAX_BRANCH_LINEAR_SUB_NODE_LENGTH {
484            if in_unit < self.trie.get(pos)? {
485                length >>= 1;
486                pos = self.jump_by_delta(pos + 1)?;
487            } else {
488                length = length - (length >> 1);
489                pos = self.skip_delta(pos + 1)?;
490            }
491        }
492        // Drop down to linear search for the last few bytes.
493        // length>=2 because the loop body above sees length>kMaxBranchLinearSubNodeLength>=3
494        // and divides length by 2.
495        loop {
496            if in_unit == self.trie.get(pos)? {
497                pos += 1;
498                let node = self.trie.get(pos)?;
499                if node & VALUE_IS_FINAL != 0 {
500                    return Some(pos);
501                }
502                // Use the non-final value as the jump delta.
503                pos += 1;
504
505                if node < MIN_TWO_UNIT_VALUE_LEAD {
506                    pos += node as usize;
507                } else if node < THREE_UNIT_VALUE_LEAD {
508                    pos += (((node - MIN_TWO_UNIT_VALUE_LEAD) as u32) << 16) as usize
509                        | self.trie.get(pos)? as usize;
510                    pos += 1;
511                } else {
512                    pos +=
513                        ((self.trie.get(pos)? as usize) << 16) | self.trie.get(pos + 1)? as usize;
514                    pos += 2;
515                }
516                return Some(pos);
517            }
518            length -= 1;
519            pos = self.skip_value(pos + 1)?;
520            if length <= 1 {
521                break;
522            }
523        }
524
525        (in_unit == self.trie.get(pos)?).then_some(pos + 1)
526    }
527
528    fn next_impl(&mut self, pos: usize, in_unit: u16) -> Option<usize> {
529        let mut node = self.trie.get(pos)?;
530        let mut pos = pos + 1;
531        loop {
532            if node < MIN_LINEAR_MATCH {
533                return self.branch_next(pos, node as usize, in_unit);
534            } else if node < MIN_VALUE_LEAD {
535                // Match the first of length+1 units.
536                let length = (node - MIN_LINEAR_MATCH) as u8;
537                if in_unit == self.trie.get(pos)? {
538                    self.remaining_match_length = length.checked_sub(1);
539                    return Some(pos + 1);
540                }
541                // No match
542                break;
543            } else if (node & VALUE_IS_FINAL) != 0 {
544                // No further matching units.
545                break;
546            } else {
547                // Skip intermediate value.
548                pos = skip_node_value(pos, node);
549                node &= NODE_TYPE_MASK;
550            }
551        }
552        None
553    }
554
555    #[inline(always)] // 1 call site and we want the Option to go away
556    fn jump_by_delta(&self, pos: usize) -> Option<usize> {
557        let delta = self.trie.get(pos)?;
558        let v = if delta < MIN_TWO_UNIT_DELTA_LEAD {
559            // nothing to do
560            pos + 1 + delta as usize
561        } else if delta == THREE_UNIT_DELTA_LEAD {
562            let delta =
563                ((self.trie.get(pos + 1)? as usize) << 16) | (self.trie.get(pos + 2)? as usize);
564            pos + delta + 3
565        } else {
566            let delta = (((delta - MIN_TWO_UNIT_DELTA_LEAD) as usize) << 16)
567                | (self.trie.get(pos + 1)? as usize);
568            pos + delta + 2
569        };
570        Some(v)
571    }
572
573    #[inline(always)] // 1 call site and we want the Option to go away
574    fn skip_value(&self, pos: usize) -> Option<usize> {
575        let lead_unit = self.trie.get(pos)?;
576        Some(skip_value(pos + 1, lead_unit & 0x7fff))
577    }
578
579    #[inline(always)] // 1 call site and we want the Option to go away
580    fn skip_delta(&self, pos: usize) -> Option<usize> {
581        let delta = self.trie.get(pos)?;
582        let v = if delta < MIN_TWO_UNIT_DELTA_LEAD {
583            pos + 1
584        } else if delta == THREE_UNIT_DELTA_LEAD {
585            pos + 3
586        } else {
587            pos + 2
588        };
589        Some(v)
590    }
591
592    #[inline(always)] // 1 call site and we want the Option to go away
593    fn read_value(&self, pos: usize, lead_unit: u16) -> Option<i32> {
594        let v = if lead_unit < MIN_TWO_UNIT_VALUE_LEAD {
595            lead_unit.into()
596        } else if lead_unit < THREE_UNIT_VALUE_LEAD {
597            (((lead_unit - MIN_TWO_UNIT_VALUE_LEAD) as i32) << 16) | self.trie.get(pos)? as i32
598        } else {
599            ((self.trie.get(pos)? as i32) << 16) | self.trie.get(pos + 1)? as i32
600        };
601        Some(v)
602    }
603
604    #[inline(always)] // 1 call site and we want the Option to go away
605    fn read_node_value(&self, pos: usize, lead_unit: u16) -> Option<i32> {
606        let v = if lead_unit < (MIN_TWO_UNIT_NODE_VALUE_LEAD) {
607            ((lead_unit >> 6) - 1).into()
608        } else if lead_unit < THREE_UNIT_NODE_VALUE_LEAD {
609            ((((lead_unit & 0x7fc0) - MIN_TWO_UNIT_NODE_VALUE_LEAD) as i32) << 10)
610                | self.trie.get(pos)? as i32
611        } else {
612            ((self.trie.get(pos)? as i32) << 16) | self.trie.get(pos + 1)? as i32
613        };
614        Some(v)
615    }
616}
617
618impl core::fmt::Write for Char16TrieCursor<'_> {
619    fn write_str(&mut self, s: &str) -> core::fmt::Result {
620        for c in s.encode_utf16() {
621            self.step16(c);
622        }
623        Ok(())
624    }
625
626    fn write_char(&mut self, c: char) -> core::fmt::Result {
627        self.step(c);
628        Ok(())
629    }
630}
631
632#[allow(deprecated)]
633impl<'a> Char16TrieIterator<'a> {
634    /// Returns a new [`Char16TrieIterator`] backed by borrowed data for the `trie` array
635    #[inline]
636    pub fn new(trie: &'a ZeroSlice<u16>) -> Self {
637        Self(Char16TrieCursor::new(trie))
638    }
639
640    /// Traverses the trie from the current state for this input char.
641    #[inline]
642    pub fn next(&mut self, c: char) -> TrieResult {
643        self.next32(c as u32)
644    }
645
646    /// Traverses the trie from the current state for this input char.
647    #[inline]
648    pub fn next32(&mut self, c: u32) -> TrieResult {
649        if c <= 0xffff {
650            self.next16(c as u16)
651        } else {
652            self.0.step16(u16_lead(c));
653            self.next16(u16_tail(c))
654        }
655    }
656
657    /// Traverses the trie from the current state for this input char.
658    #[inline]
659    pub fn next16(&mut self, c: u16) -> TrieResult {
660        self.0.step16(c);
661        match (self.0.value(), self.0.is_empty()) {
662            (Some(v), true) => TrieResult::FinalValue(v),
663            (Some(v), false) => TrieResult::Intermediate(v),
664            (None, true) => TrieResult::NoMatch,
665            (None, false) => TrieResult::NoValue,
666        }
667    }
668}