Skip to main content

icu_collections/codepointtrie/
iter.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::iter::FusedIterator;
6use core::marker::PhantomData;
7
8use crate::codepointtrie::AbstractCodePointTrie;
9use crate::codepointtrie::TrieValue;
10
11/// Provides a trie accessor for types (likely iterators)
12/// that are holding a reference to a type that implements
13/// `AbstractCodePointTrie`.
14pub trait WithTrie<'trie, T, V>
15where
16    V: TrieValue,
17    T: AbstractCodePointTrie<'trie, V>,
18{
19    /// Get a reference to the trie.
20    fn trie(&self) -> &'trie T;
21}
22
23/// Iterator over `str` by `char` and `TrieValue`.
24#[derive(Debug)]
25pub struct CharsWithTrie<'slice, 'trie, T, V>
26where
27    V: TrieValue,
28    T: AbstractCodePointTrie<'trie, V>,
29{
30    delegate: core::slice::Iter<'slice, u8>,
31    trie: &'trie T,
32    phantom: PhantomData<V>,
33}
34
35impl<'slice, 'trie, T, V> CharsWithTrie<'slice, 'trie, T, V>
36where
37    V: TrieValue,
38    T: AbstractCodePointTrie<'trie, V>,
39{
40    /// Construct a new `CharsWithTrie`.
41    #[inline]
42    pub fn new(s: &'slice str, trie: &'trie T) -> Self {
43        Self {
44            delegate: s.as_bytes().iter(),
45            trie,
46            phantom: PhantomData,
47        }
48    }
49
50    /// Obtains the remainder of the iterator as a string slice.
51    #[inline]
52    pub fn as_str(&self) -> &'slice str {
53        // SAFETY: OK, because `delegate` came from `str` and is always
54        // advanced in a way that leaves the iterator at an UTF-8 sequence
55        // boundary.
56        unsafe { core::str::from_utf8_unchecked(self.delegate.as_slice()) }
57    }
58}
59
60impl<'slice, 'trie, T, V> Clone for CharsWithTrie<'slice, 'trie, T, V>
61where
62    V: TrieValue,
63    T: AbstractCodePointTrie<'trie, V>,
64{
65    #[inline]
66    fn clone(&self) -> Self {
67        Self {
68            delegate: self.delegate.clone(),
69            trie: self.trie,
70            phantom: PhantomData,
71        }
72    }
73}
74
75impl<'slice, 'trie, T, V> WithTrie<'trie, T, V> for CharsWithTrie<'slice, 'trie, T, V>
76where
77    V: TrieValue,
78    T: AbstractCodePointTrie<'trie, V>,
79{
80    #[inline]
81    fn trie(&self) -> &'trie T {
82        self.trie
83    }
84}
85
86impl<'slice, 'trie, T, V> Iterator for CharsWithTrie<'slice, 'trie, T, V>
87where
88    V: TrieValue,
89    T: AbstractCodePointTrie<'trie, V>,
90{
91    type Item = (char, V);
92
93    #[inline]
94    fn next(&mut self) -> Option<Self::Item> {
95        let lead = *self.delegate.next()?;
96        if lead < 0x80 {
97            // SAFETY: We checked the invariant of `ascii` immediately
98            // above.
99            return Some((char::from(lead), unsafe { self.trie.ascii(lead) }));
100        }
101        // SAFETY: Since `delegate` came from `str` and we always advance by a full UTF-8 sequence, we may assume that we
102        // have a valid lead byte. Not need to check for other cases.
103        if lead < 0xE0 {
104            // Two-byte sequence.
105            // SAFETY, since `delegate` came from `str` and we always advance by a full UTF-8 sequence, we may assume the
106            // presence of a trail byte.
107            let trail = *unsafe { self.delegate.next().unwrap_unchecked() };
108            let high_five = u32::from(lead & 0b11_111);
109            let low_six = u32::from(trail & 0b111_111);
110            // SAFETY: By construction, `high_five` and `low_six` conform
111            // to the invariant of `utf8_two_byte`.
112            let v = unsafe { self.trie.utf8_two_byte(high_five, low_six) };
113            // SAFETY: Since `delegate` came from `str` and we always advance by a full UTF-8 sequence, `lead` must be a
114            // valid (not overlong) two-byte lead and `trail` must be a valid
115            // trail. Therefore, the following shift and OR stays in the
116            // scalar value range.
117            let c = unsafe { char::from_u32_unchecked((high_five << 6) | low_six) };
118            return Some((c, v));
119        }
120        if lead < 0xF0 {
121            // Three-byte sequence.
122            // SAFETY, since `delegate` came from `str` and we always advance by a full UTF-8 sequence, we may assume the
123            // presence of two trail bytes.
124            let second = *unsafe { self.delegate.next().unwrap_unchecked() };
125            let third = *unsafe { self.delegate.next().unwrap_unchecked() };
126            let high_ten = (u32::from(lead & 0b1111) << 6) | u32::from(second & 0b111_111);
127            let low_six = u32::from(third & 0b111_111);
128            // SAFETY: By construction, `high_ten` and `low_six` conform
129            // to the invariant of `utf8_three_byte`.
130            let v = unsafe { self.trie.utf8_three_byte(high_ten, low_six) };
131            // SAFETY: Since `delegate` came from `str` and we always advance by a full UTF-8 sequence, `lead` must be a
132            // valid (not overlong) three-byte lead and `second` and `third`
133            // must be valid trails. Therefore, the following shift and OR
134            // stays in the scalar value range.
135            let c = unsafe { char::from_u32_unchecked((high_ten << 6) | low_six) };
136            return Some((c, v));
137        }
138        // Four-byte sequence
139        // SAFETY, since `delegate` came from `str` and we always advance by a full UTF-8 sequence, we may assume the
140        // presence of three trail bytes.
141        let second = *unsafe { self.delegate.next().unwrap_unchecked() };
142        let third = *unsafe { self.delegate.next().unwrap_unchecked() };
143        let fourth = *unsafe { self.delegate.next().unwrap_unchecked() };
144        // SAFETY: Since `delegate` came from `str` and we always advance by a full UTF-8 sequence, `lead` must be a
145        // valid (not overlong or out-of-range) four-byte lead and `second`,
146        // `third`, and `fourth` must be valid trails. Therefore, the
147        // following shift and OR stays in the scalar value range.
148        let c = unsafe {
149            char::from_u32_unchecked(
150                (u32::from(lead & 0b111) << 18)
151                    | (u32::from(second & 0b111_111) << 12)
152                    | (u32::from(third & 0b111_111) << 6)
153                    | u32::from(fourth & 0b111_111),
154            )
155        };
156        Some((c, self.trie.supplementary(c as u32)))
157    }
158
159    #[inline]
160    fn count(self) -> usize {
161        self.as_str().chars().count()
162    }
163
164    #[inline]
165    fn size_hint(&self) -> (usize, Option<usize>) {
166        self.as_str().chars().size_hint()
167    }
168
169    #[inline]
170    fn last(mut self) -> Option<Self::Item> {
171        self.next_back()
172    }
173
174    // TODO: Delegate advance_by to `Chars` once stabilized.
175}
176
177impl<'slice, 'trie, T, V> DoubleEndedIterator for CharsWithTrie<'slice, 'trie, T, V>
178where
179    V: TrieValue,
180    T: AbstractCodePointTrie<'trie, V>,
181{
182    #[inline]
183    fn next_back(&mut self) -> Option<Self::Item> {
184        let last = *self.delegate.next_back()?;
185        if last < 0x80 {
186            // SAFETY: We checked the invariant of `ascii` immediately
187            // above.
188            return Some((char::from(last), unsafe { self.trie.ascii(last) }));
189        }
190        // SAFETY Since `delegate` came from `str` and we always advance by a full UTF-8 sequence,
191        // `last` must be a valid trail byte and it is preceded either by a lead byte for a
192        // two-byte sequence or by another trail byte.
193        let second_last = *unsafe { self.delegate.next_back().unwrap_unchecked() };
194        if second_last >= 0b1100_0000 {
195            // Two-byte sequence.
196            let high_five = u32::from(second_last & 0b11_111);
197            let low_six = u32::from(last & 0b111_111);
198            // SAFETY: By construction, `high_five` and `low_six` conform
199            // to the invariant of `utf8_two_byte`.
200            let v = unsafe { self.trie.utf8_two_byte(high_five, low_six) };
201            // SAFETY: Since `delegate` came from `str` and we always advance by a full UTF-8 sequence, `second_last` must be a
202            // valid (not overlong) two-byte lead and `last` must be a valid
203            // trail. Therefore, the following shift and OR stays in the
204            // scalar value range.
205            let c = unsafe { char::from_u32_unchecked((high_five << 6) | low_six) };
206            return Some((c, v));
207        }
208        // SAFETY Since `delegate` came from `str` and we always advance by a full UTF-8 sequence,
209        // `second_last` must be a valid trail byte and it is preceded either by a lead byte for a
210        // three-byte sequence or by another trail byte.
211        let third_last = *unsafe { self.delegate.next_back().unwrap_unchecked() };
212        if third_last >= 0b1100_0000 {
213            // Three-byte sequence
214            let high_ten =
215                (u32::from(third_last & 0b1111) << 6) | u32::from(second_last & 0b111_111);
216            let low_six = u32::from(last & 0b111_111);
217            // SAFETY: By construction, `high_ten` and `low_six` conform
218            // to the invariant of `utf8_three_byte`.
219            let v = unsafe { self.trie.utf8_three_byte(high_ten, low_six) };
220            // SAFETY: Since `delegate` came from `str` and we always advance by a full UTF-8 sequence, `third_last` must be a
221            // valid (not overlong) three-byte lead and `second_last` and `last`
222            // must be valid trails. Therefore, the following shift and OR
223            // stays in the scalar value range.
224            let c = unsafe { char::from_u32_unchecked((high_ten << 6) | low_six) };
225            return Some((c, v));
226        }
227        // Four-byte sequence
228        // SAFETY, since `delegate` came from `str` and we always advance by a full UTF-8 sequence, we may assume the
229        // presence of a lead byte.
230        let lead = *unsafe { self.delegate.next_back().unwrap_unchecked() };
231        // SAFETY: Since `delegate` came from `str` and we always advance by a full UTF-8 sequence, `lead` must be a
232        // valid (not overlong or out-of-range) four-byte lead and `third_last`,
233        // `second_last`, and `last` must be valid trails. Therefore, the
234        // following shift and OR stays in the scalar value range.
235        let c = unsafe {
236            char::from_u32_unchecked(
237                (u32::from(lead & 0b111) << 18)
238                    | (u32::from(third_last & 0b111_111) << 12)
239                    | (u32::from(second_last & 0b111_111) << 6)
240                    | u32::from(last & 0b111_111),
241            )
242        };
243        Some((c, self.trie.supplementary(c as u32)))
244    }
245}
246
247impl<'slice, 'trie, T, V> FusedIterator for CharsWithTrie<'slice, 'trie, T, V>
248where
249    V: TrieValue,
250    T: AbstractCodePointTrie<'trie, V>,
251{
252}
253// --
254
255/// Iterator over `str` by `char` and `TrieValue`.
256#[derive(Debug)]
257pub struct CharIndicesWithTrie<'slice, 'trie, T, V>
258where
259    V: TrieValue,
260    T: AbstractCodePointTrie<'trie, V>,
261{
262    offset: usize,
263    delegate: CharsWithTrie<'slice, 'trie, T, V>,
264}
265
266impl<'slice, 'trie, T, V> CharIndicesWithTrie<'slice, 'trie, T, V>
267where
268    V: TrieValue,
269    T: AbstractCodePointTrie<'trie, V>,
270{
271    /// Construct a new `CharIndicesWithTrie`.
272    #[inline]
273    pub fn new(s: &'slice str, trie: &'trie T) -> Self {
274        Self {
275            offset: 0,
276            delegate: CharsWithTrie::new(s, trie),
277        }
278    }
279
280    /// Obtains the remainder of the iterator as a string slice.
281    #[inline]
282    pub fn as_str(&self) -> &'slice str {
283        self.delegate.as_str()
284    }
285}
286
287impl<'slice, 'trie, T, V> Clone for CharIndicesWithTrie<'slice, 'trie, T, V>
288where
289    V: TrieValue,
290    T: AbstractCodePointTrie<'trie, V>,
291{
292    #[inline]
293    fn clone(&self) -> Self {
294        Self {
295            offset: self.offset,
296            delegate: self.delegate.clone(),
297        }
298    }
299}
300
301impl<'slice, 'trie, T, V> WithTrie<'trie, T, V> for CharIndicesWithTrie<'slice, 'trie, T, V>
302where
303    V: TrieValue,
304    T: AbstractCodePointTrie<'trie, V>,
305{
306    #[inline]
307    fn trie(&self) -> &'trie T {
308        self.delegate.trie()
309    }
310}
311
312impl<'slice, 'trie, T, V> Iterator for CharIndicesWithTrie<'slice, 'trie, T, V>
313where
314    V: TrieValue,
315    T: AbstractCodePointTrie<'trie, V>,
316{
317    type Item = (usize, char, V);
318
319    #[inline]
320    fn next(&mut self) -> Option<Self::Item> {
321        let old_len = self.as_str().len();
322        let (c, v) = self.delegate.next()?;
323        let old_offset = self.offset;
324        self.offset += old_len - self.as_str().len();
325        Some((old_offset, c, v))
326    }
327
328    #[inline]
329    fn count(self) -> usize {
330        self.as_str().chars().count()
331    }
332
333    #[inline]
334    fn size_hint(&self) -> (usize, Option<usize>) {
335        self.as_str().chars().size_hint()
336    }
337
338    #[inline]
339    fn last(mut self) -> Option<Self::Item> {
340        self.next_back()
341    }
342
343    // TODO: Delegate advance_by to `Chars` once stabilized.
344}
345
346impl<'slice, 'trie, T, V> DoubleEndedIterator for CharIndicesWithTrie<'slice, 'trie, T, V>
347where
348    V: TrieValue,
349    T: AbstractCodePointTrie<'trie, V>,
350{
351    #[inline]
352    fn next_back(&mut self) -> Option<Self::Item> {
353        let (c, v) = self.delegate.next_back()?;
354        Some((self.offset + self.as_str().len(), c, v))
355    }
356}
357
358impl<'slice, 'trie, T, V> FusedIterator for CharIndicesWithTrie<'slice, 'trie, T, V>
359where
360    V: TrieValue,
361    T: AbstractCodePointTrie<'trie, V>,
362{
363}
364
365// --
366
367/// Adds convenience methods to `str`.
368pub trait CharsWithTrieEx<'slice, 'trie, T, V>
369where
370    V: TrieValue,
371    T: AbstractCodePointTrie<'trie, V>,
372{
373    /// Method for easily creating `CharsWithTrie` on `str` analogously to `chars()`.
374    fn chars_with_trie(&'slice self, trie: &'trie T) -> CharsWithTrie<'slice, 'trie, T, V>;
375
376    /// Method for easily creating `CharIndicesWithTrie` on `str` analogously to `char_indices()`.
377    fn char_indices_with_trie(
378        &'slice self,
379        trie: &'trie T,
380    ) -> CharIndicesWithTrie<'slice, 'trie, T, V>;
381}
382
383impl<'slice, 'trie, T, V> CharsWithTrieEx<'slice, 'trie, T, V> for str
384where
385    V: TrieValue,
386    T: AbstractCodePointTrie<'trie, V>,
387{
388    /// Method for easily creating `CharsWithTrie` on `str` analogously to `chars()`.
389    #[inline]
390    fn chars_with_trie(&'slice self, trie: &'trie T) -> CharsWithTrie<'slice, 'trie, T, V> {
391        CharsWithTrie::new(self, trie)
392    }
393
394    /// Method for easily creating `CharIndicesWithTrie` on `str` analogously to `char_indices()`.
395    #[inline]
396    fn char_indices_with_trie(
397        &'slice self,
398        trie: &'trie T,
399    ) -> CharIndicesWithTrie<'slice, 'trie, T, V> {
400        CharIndicesWithTrie::new(self, trie)
401    }
402}
403
404// --
405
406/// Iterator over `str` by `char` and `TrieValue` but
407/// the trie value for ASCII is `V::default()` instead of
408/// reading from the trie. (`V::default()` can be optimized
409/// on at compile time while reading the trie's default value
410/// is a run-time operation.)
411#[derive(Debug)]
412pub struct CharsWithTrieDefaultForAscii<'slice, 'trie, T, V>
413where
414    V: TrieValue + Default,
415    T: AbstractCodePointTrie<'trie, V>,
416{
417    delegate: core::slice::Iter<'slice, u8>,
418    trie: &'trie T,
419    phantom: PhantomData<V>,
420}
421
422impl<'slice, 'trie, T, V> CharsWithTrieDefaultForAscii<'slice, 'trie, T, V>
423where
424    V: TrieValue + Default,
425    T: AbstractCodePointTrie<'trie, V>,
426{
427    /// Construct a new `CharsWithTrieDefaultForAscii`.
428    #[inline]
429    pub fn new(s: &'slice str, trie: &'trie T) -> Self {
430        Self {
431            delegate: s.as_bytes().iter(),
432            trie,
433            phantom: PhantomData,
434        }
435    }
436
437    /// Obtains the remainder of the iterator as a string slice.
438    #[inline]
439    pub fn as_str(&self) -> &'slice str {
440        // SAFETY: OK, because `delegate` came from `str` and is always
441        // advanced in a way that leaves the iterator at an UTF-8 sequence
442        // boundary.
443        unsafe { core::str::from_utf8_unchecked(self.delegate.as_slice()) }
444    }
445}
446
447impl<'slice, 'trie, T, V> Clone for CharsWithTrieDefaultForAscii<'slice, 'trie, T, V>
448where
449    V: TrieValue + Default,
450    T: AbstractCodePointTrie<'trie, V>,
451{
452    #[inline]
453    fn clone(&self) -> Self {
454        Self {
455            delegate: self.delegate.clone(),
456            trie: self.trie,
457            phantom: PhantomData,
458        }
459    }
460}
461
462impl<'slice, 'trie, T, V> WithTrie<'trie, T, V>
463    for CharsWithTrieDefaultForAscii<'slice, 'trie, T, V>
464where
465    V: TrieValue + Default,
466    T: AbstractCodePointTrie<'trie, V>,
467{
468    #[inline]
469    fn trie(&self) -> &'trie T {
470        self.trie
471    }
472}
473
474impl<'slice, 'trie, T, V> Iterator for CharsWithTrieDefaultForAscii<'slice, 'trie, T, V>
475where
476    V: TrieValue + Default,
477    T: AbstractCodePointTrie<'trie, V>,
478{
479    type Item = (char, V);
480
481    #[inline]
482    fn next(&mut self) -> Option<Self::Item> {
483        let lead = *self.delegate.next()?;
484        if lead < 0x80 {
485            // SAFETY: We checked the invariant of `ascii` immediately
486            // above.
487            return Some((char::from(lead), V::default()));
488        }
489        // SAFETY: Since `delegate` came from `str` and we always advance by a full UTF-8 sequence, we may assume that we
490        // have a valid lead byte. Not need to check for other cases.
491        if lead < 0xE0 {
492            // Two-byte sequence.
493            // SAFETY, since `delegate` came from `str` and we always advance by a full UTF-8 sequence, we may assume the
494            // presence of a trail byte.
495            let trail = *unsafe { self.delegate.next().unwrap_unchecked() };
496            let high_five = u32::from(lead & 0b11_111);
497            let low_six = u32::from(trail & 0b111_111);
498            // SAFETY: By construction, `high_five` and `low_six` conform
499            // to the invariant of `utf8_two_byte`.
500            let v = unsafe { self.trie.utf8_two_byte(high_five, low_six) };
501            // SAFETY: Since `delegate` came from `str` and we always advance by a full UTF-8 sequence, `lead` must be a
502            // valid (not overlong) two-byte lead and `trail` must be a valid
503            // trail. Therefore, the following shift and OR stays in the
504            // scalar value range.
505            let c = unsafe { char::from_u32_unchecked((high_five << 6) | low_six) };
506            return Some((c, v));
507        }
508        if lead < 0xF0 {
509            // Three-byte sequence.
510            // SAFETY, since `delegate` came from `str` and we always advance by a full UTF-8 sequence, we may assume the
511            // presence of two trail bytes.
512            let second = *unsafe { self.delegate.next().unwrap_unchecked() };
513            let third = *unsafe { self.delegate.next().unwrap_unchecked() };
514            let high_ten = (u32::from(lead & 0b1111) << 6) | u32::from(second & 0b111_111);
515            let low_six = u32::from(third & 0b111_111);
516            // SAFETY: By construction, `high_ten` and `low_six` conform
517            // to the invariant of `utf8_three_byte`.
518            let v = unsafe { self.trie.utf8_three_byte(high_ten, low_six) };
519            // SAFETY: Since `delegate` came from `str` and we always advance by a full UTF-8 sequence, `lead` must be a
520            // valid (not overlong) three-byte lead and `second` and `third`
521            // must be valid trails. Therefore, the following shift and OR
522            // stays in the scalar value range.
523            let c = unsafe { char::from_u32_unchecked((high_ten << 6) | low_six) };
524            return Some((c, v));
525        }
526        // Four-byte sequence
527        // SAFETY, since `delegate` came from `str` and we always advance by a full UTF-8 sequence, we may assume the
528        // presence of three trail bytes.
529        let second = *unsafe { self.delegate.next().unwrap_unchecked() };
530        let third = *unsafe { self.delegate.next().unwrap_unchecked() };
531        let fourth = *unsafe { self.delegate.next().unwrap_unchecked() };
532        // SAFETY: Since `delegate` came from `str` and we always advance by a full UTF-8 sequence, `lead` must be a
533        // valid (not overlong or out-of-range) four-byte lead and `second`,
534        // `third`, and `fourth` must be valid trails. Therefore, the
535        // following shift and OR stays in the scalar value range.
536        let c = unsafe {
537            char::from_u32_unchecked(
538                (u32::from(lead & 0b111) << 18)
539                    | (u32::from(second & 0b111_111) << 12)
540                    | (u32::from(third & 0b111_111) << 6)
541                    | u32::from(fourth & 0b111_111),
542            )
543        };
544        Some((c, self.trie.supplementary(c as u32)))
545    }
546
547    #[inline]
548    fn count(self) -> usize {
549        self.as_str().chars().count()
550    }
551
552    #[inline]
553    fn size_hint(&self) -> (usize, Option<usize>) {
554        self.as_str().chars().size_hint()
555    }
556
557    #[inline]
558    fn last(mut self) -> Option<Self::Item> {
559        self.next_back()
560    }
561
562    // TODO: Delegate advance_by to `Chars` once stabilized.
563}
564
565impl<'slice, 'trie, T, V> DoubleEndedIterator for CharsWithTrieDefaultForAscii<'slice, 'trie, T, V>
566where
567    V: TrieValue + Default,
568    T: AbstractCodePointTrie<'trie, V>,
569{
570    #[inline]
571    fn next_back(&mut self) -> Option<Self::Item> {
572        let last = *self.delegate.next_back()?;
573        if last < 0x80 {
574            // SAFETY: We checked the invariant of `ascii` immediately
575            // above.
576            return Some((char::from(last), V::default()));
577        }
578        // SAFETY Since `delegate` came from `str` and we always advance by a full UTF-8 sequence,
579        // `last` must be a valid trail byte and it is preceded either by a lead byte for a
580        // two-byte sequence or by another trail byte.
581        let second_last = *unsafe { self.delegate.next_back().unwrap_unchecked() };
582        if second_last >= 0b1100_0000 {
583            // Two-byte sequence.
584            let high_five = u32::from(second_last & 0b11_111);
585            let low_six = u32::from(last & 0b111_111);
586            // SAFETY: By construction, `high_five` and `low_six` conform
587            // to the invariant of `utf8_two_byte`.
588            let v = unsafe { self.trie.utf8_two_byte(high_five, low_six) };
589            // SAFETY: Since `delegate` came from `str` and we always advance by a full UTF-8 sequence, `second_last` must be a
590            // valid (not overlong) two-byte lead and `last` must be a valid
591            // trail. Therefore, the following shift and OR stays in the
592            // scalar value range.
593            let c = unsafe { char::from_u32_unchecked((high_five << 6) | low_six) };
594            return Some((c, v));
595        }
596        // SAFETY Since `delegate` came from `str` and we always advance by a full UTF-8 sequence,
597        // `second_last` must be a valid trail byte and it is preceded either by a lead byte for a
598        // three-byte sequence or by another trail byte.
599        let third_last = *unsafe { self.delegate.next_back().unwrap_unchecked() };
600        if third_last >= 0b1100_0000 {
601            // Three-byte sequence
602            let high_ten =
603                (u32::from(third_last & 0b1111) << 6) | u32::from(second_last & 0b111_111);
604            let low_six = u32::from(last & 0b111_111);
605            // SAFETY: By construction, `high_ten` and `low_six` conform
606            // to the invariant of `utf8_three_byte`.
607            let v = unsafe { self.trie.utf8_three_byte(high_ten, low_six) };
608            // SAFETY: Since `delegate` came from `str` and we always advance by a full UTF-8 sequence, `third_last` must be a
609            // valid (not overlong) three-byte lead and `second_last` and `last`
610            // must be valid trails. Therefore, the following shift and OR
611            // stays in the scalar value range.
612            let c = unsafe { char::from_u32_unchecked((high_ten << 6) | low_six) };
613            return Some((c, v));
614        }
615        // Four-byte sequence
616        // SAFETY, since `delegate` came from `str` and we always advance by a full UTF-8 sequence, we may assume the
617        // presence of a lead byte.
618        let lead = *unsafe { self.delegate.next_back().unwrap_unchecked() };
619        // SAFETY: Since `delegate` came from `str` and we always advance by a full UTF-8 sequence, `lead` must be a
620        // valid (not overlong or out-of-range) four-byte lead and `third_last`,
621        // `second_last`, and `last` must be valid trails. Therefore, the
622        // following shift and OR stays in the scalar value range.
623        let c = unsafe {
624            char::from_u32_unchecked(
625                (u32::from(lead & 0b111) << 18)
626                    | (u32::from(third_last & 0b111_111) << 12)
627                    | (u32::from(second_last & 0b111_111) << 6)
628                    | u32::from(last & 0b111_111),
629            )
630        };
631        Some((c, self.trie.supplementary(c as u32)))
632    }
633}
634
635impl<'slice, 'trie, T, V> FusedIterator for CharsWithTrieDefaultForAscii<'slice, 'trie, T, V>
636where
637    V: TrieValue + Default,
638    T: AbstractCodePointTrie<'trie, V>,
639{
640}
641// --
642
643/// Iterator over `str` by `char` and `TrieValue`.
644#[derive(Debug)]
645pub struct CharIndicesWithTrieDefaultForAscii<'slice, 'trie, T, V>
646where
647    V: TrieValue + Default,
648    T: AbstractCodePointTrie<'trie, V>,
649{
650    offset: usize,
651    delegate: CharsWithTrieDefaultForAscii<'slice, 'trie, T, V>,
652}
653
654impl<'slice, 'trie, T, V> CharIndicesWithTrieDefaultForAscii<'slice, 'trie, T, V>
655where
656    V: TrieValue + Default,
657    T: AbstractCodePointTrie<'trie, V>,
658{
659    /// Construct a new `CharIndicesWithTrieDefaultForAscii`.
660    #[inline]
661    pub fn new(s: &'slice str, trie: &'trie T) -> Self {
662        Self {
663            offset: 0,
664            delegate: CharsWithTrieDefaultForAscii::new(s, trie),
665        }
666    }
667
668    /// Obtains the remainder of the iterator as a string slice.
669    #[inline]
670    pub fn as_str(&self) -> &'slice str {
671        self.delegate.as_str()
672    }
673}
674
675impl<'slice, 'trie, T, V> Clone for CharIndicesWithTrieDefaultForAscii<'slice, 'trie, T, V>
676where
677    V: TrieValue + Default,
678    T: AbstractCodePointTrie<'trie, V>,
679{
680    #[inline]
681    fn clone(&self) -> Self {
682        Self {
683            offset: self.offset,
684            delegate: self.delegate.clone(),
685        }
686    }
687}
688
689impl<'slice, 'trie, T, V> WithTrie<'trie, T, V>
690    for CharIndicesWithTrieDefaultForAscii<'slice, 'trie, T, V>
691where
692    V: TrieValue + Default,
693    T: AbstractCodePointTrie<'trie, V>,
694{
695    #[inline]
696    fn trie(&self) -> &'trie T {
697        self.delegate.trie()
698    }
699}
700
701impl<'slice, 'trie, T, V> Iterator for CharIndicesWithTrieDefaultForAscii<'slice, 'trie, T, V>
702where
703    V: TrieValue + Default,
704    T: AbstractCodePointTrie<'trie, V>,
705{
706    type Item = (usize, char, V);
707
708    #[inline]
709    fn next(&mut self) -> Option<Self::Item> {
710        let old_len = self.as_str().len();
711        let (c, v) = self.delegate.next()?;
712        let old_offset = self.offset;
713        self.offset += old_len - self.as_str().len();
714        Some((old_offset, c, v))
715    }
716
717    #[inline]
718    fn count(self) -> usize {
719        self.as_str().chars().count()
720    }
721
722    #[inline]
723    fn size_hint(&self) -> (usize, Option<usize>) {
724        self.as_str().chars().size_hint()
725    }
726
727    #[inline]
728    fn last(mut self) -> Option<Self::Item> {
729        self.next_back()
730    }
731
732    // TODO: Delegate advance_by to `Chars` once stabilized.
733}
734
735impl<'slice, 'trie, T, V> DoubleEndedIterator
736    for CharIndicesWithTrieDefaultForAscii<'slice, 'trie, T, V>
737where
738    V: TrieValue + Default,
739    T: AbstractCodePointTrie<'trie, V>,
740{
741    #[inline]
742    fn next_back(&mut self) -> Option<Self::Item> {
743        let (c, v) = self.delegate.next_back()?;
744        Some((self.offset + self.as_str().len(), c, v))
745    }
746}
747
748impl<'slice, 'trie, T, V> FusedIterator for CharIndicesWithTrieDefaultForAscii<'slice, 'trie, T, V>
749where
750    V: TrieValue + Default,
751    T: AbstractCodePointTrie<'trie, V>,
752{
753}
754
755// --
756
757/// Adds convenience methods to `str`.
758pub trait CharsWithTrieDefaultForAsciiEx<'slice, 'trie, T, V>
759where
760    V: TrieValue + Default,
761    T: AbstractCodePointTrie<'trie, V>,
762{
763    /// Method for easily creating `CharsWithTrie` on `str` analogously to `chars()`.
764    fn chars_with_trie_default_for_ascii(
765        &'slice self,
766        trie: &'trie T,
767    ) -> CharsWithTrieDefaultForAscii<'slice, 'trie, T, V>;
768
769    /// Method for easily creating `CharIndicesWithTrie` on `str` analogously to `char_indices()`.
770    fn char_indices_with_trie_default_for_ascii(
771        &'slice self,
772        trie: &'trie T,
773    ) -> CharIndicesWithTrieDefaultForAscii<'slice, 'trie, T, V>;
774}
775
776impl<'slice, 'trie, T, V> CharsWithTrieDefaultForAsciiEx<'slice, 'trie, T, V> for str
777where
778    V: TrieValue + Default,
779    T: AbstractCodePointTrie<'trie, V>,
780{
781    /// Method for easily creating `CharsWithTrie` on `str` analogously to `chars()`.
782    #[inline]
783    fn chars_with_trie_default_for_ascii(
784        &'slice self,
785        trie: &'trie T,
786    ) -> CharsWithTrieDefaultForAscii<'slice, 'trie, T, V> {
787        CharsWithTrieDefaultForAscii::new(self, trie)
788    }
789
790    /// Method for easily creating `CharIndicesWithTrie` on `str` analogously to `char_indices()`.
791    #[inline]
792    fn char_indices_with_trie_default_for_ascii(
793        &'slice self,
794        trie: &'trie T,
795    ) -> CharIndicesWithTrieDefaultForAscii<'slice, 'trie, T, V> {
796        CharIndicesWithTrieDefaultForAscii::new(self, trie)
797    }
798}
799
800// --
801
802/// Iterator over Latin1 `[u8]` by `char` and `TrieValue`.
803#[derive(Debug)]
804pub struct Latin1CharsWithTrie<'slice, 'trie, T, V>
805where
806    V: TrieValue,
807    T: AbstractCodePointTrie<'trie, V>,
808{
809    delegate: core::slice::Iter<'slice, u8>,
810    trie: &'trie T,
811    phantom: PhantomData<V>,
812}
813
814impl<'slice, 'trie, T, V> Latin1CharsWithTrie<'slice, 'trie, T, V>
815where
816    V: TrieValue,
817    T: AbstractCodePointTrie<'trie, V>,
818{
819    /// Construct a new `Latin1CharsWithTrie`.
820    #[inline]
821    pub fn new(s: &'slice [u8], trie: &'trie T) -> Self {
822        Self {
823            delegate: s.iter(),
824            trie,
825            phantom: PhantomData,
826        }
827    }
828
829    /// Obtains the remainder of the iterator as a slice.
830    #[inline]
831    pub fn as_slice(&self) -> &'slice [u8] {
832        self.delegate.as_slice()
833    }
834}
835
836impl<'slice, 'trie, T, V> Clone for Latin1CharsWithTrie<'slice, 'trie, T, V>
837where
838    V: TrieValue,
839    T: AbstractCodePointTrie<'trie, V>,
840{
841    #[inline]
842    fn clone(&self) -> Self {
843        Self {
844            delegate: self.delegate.clone(),
845            trie: self.trie,
846            phantom: PhantomData,
847        }
848    }
849}
850
851impl<'slice, 'trie, T, V> WithTrie<'trie, T, V> for Latin1CharsWithTrie<'slice, 'trie, T, V>
852where
853    V: TrieValue,
854    T: AbstractCodePointTrie<'trie, V>,
855{
856    #[inline]
857    fn trie(&self) -> &'trie T {
858        self.trie
859    }
860}
861
862impl<'slice, 'trie, T, V> Iterator for Latin1CharsWithTrie<'slice, 'trie, T, V>
863where
864    V: TrieValue,
865    T: AbstractCodePointTrie<'trie, V>,
866{
867    type Item = (char, V);
868
869    #[inline]
870    fn next(&mut self) -> Option<Self::Item> {
871        let b = *self.delegate.next()?;
872        Some((char::from(b), self.trie.latin1(b)))
873    }
874
875    #[inline]
876    fn count(self) -> usize {
877        self.delegate.count()
878    }
879
880    #[inline]
881    fn size_hint(&self) -> (usize, Option<usize>) {
882        self.delegate.size_hint()
883    }
884
885    #[inline]
886    fn last(mut self) -> Option<Self::Item> {
887        self.next_back()
888    }
889
890    // TODO: Delegate advance_by to `delegate` once stabilized.
891}
892
893impl<'slice, 'trie, T, V> DoubleEndedIterator for Latin1CharsWithTrie<'slice, 'trie, T, V>
894where
895    V: TrieValue,
896    T: AbstractCodePointTrie<'trie, V>,
897{
898    #[inline]
899    fn next_back(&mut self) -> Option<Self::Item> {
900        let b = *self.delegate.next_back()?;
901        Some((char::from(b), self.trie.latin1(b)))
902    }
903}
904
905impl<'slice, 'trie, T, V> FusedIterator for Latin1CharsWithTrie<'slice, 'trie, T, V>
906where
907    V: TrieValue,
908    T: AbstractCodePointTrie<'trie, V>,
909{
910}
911
912// --
913
914/// Iterator over `str` by `char` and `TrieValue`.
915#[derive(Debug)]
916pub struct Latin1CharIndicesWithTrie<'slice, 'trie, T, V>
917where
918    V: TrieValue,
919    T: AbstractCodePointTrie<'trie, V>,
920{
921    offset: usize,
922    delegate: core::slice::Iter<'slice, u8>,
923    trie: &'trie T,
924    phantom: PhantomData<V>,
925}
926
927impl<'slice, 'trie, T, V> Latin1CharIndicesWithTrie<'slice, 'trie, T, V>
928where
929    V: TrieValue,
930    T: AbstractCodePointTrie<'trie, V>,
931{
932    /// Construct a new `Latin1CharIndicesWithTrie`.
933    #[inline]
934    pub fn new(s: &'slice [u8], trie: &'trie T) -> Self {
935        Self {
936            offset: 0,
937            delegate: s.iter(),
938            trie,
939            phantom: PhantomData,
940        }
941    }
942
943    /// Obtains the remainder of the iterator as a slice.
944    #[inline]
945    pub fn as_slice(&self) -> &'slice [u8] {
946        self.delegate.as_slice()
947    }
948}
949
950impl<'slice, 'trie, T, V> Clone for Latin1CharIndicesWithTrie<'slice, 'trie, T, V>
951where
952    V: TrieValue,
953    T: AbstractCodePointTrie<'trie, V>,
954{
955    #[inline]
956    fn clone(&self) -> Self {
957        Self {
958            offset: self.offset,
959            delegate: self.delegate.clone(),
960            trie: self.trie,
961            phantom: PhantomData,
962        }
963    }
964}
965
966impl<'slice, 'trie, T, V> WithTrie<'trie, T, V> for Latin1CharIndicesWithTrie<'slice, 'trie, T, V>
967where
968    V: TrieValue,
969    T: AbstractCodePointTrie<'trie, V>,
970{
971    #[inline]
972    fn trie(&self) -> &'trie T {
973        self.trie
974    }
975}
976
977impl<'slice, 'trie, T, V> Iterator for Latin1CharIndicesWithTrie<'slice, 'trie, T, V>
978where
979    V: TrieValue,
980    T: AbstractCodePointTrie<'trie, V>,
981{
982    type Item = (usize, char, V);
983
984    #[inline]
985    fn next(&mut self) -> Option<Self::Item> {
986        let b = *self.delegate.next()?;
987        let old_offset = self.offset;
988        self.offset += 1;
989        Some((old_offset, char::from(b), self.trie.latin1(b)))
990    }
991
992    #[inline]
993    fn count(self) -> usize {
994        self.delegate.count()
995    }
996
997    #[inline]
998    fn size_hint(&self) -> (usize, Option<usize>) {
999        self.delegate.size_hint()
1000    }
1001
1002    #[inline]
1003    fn last(mut self) -> Option<Self::Item> {
1004        self.next_back()
1005    }
1006
1007    // TODO: Delegate advance_by to `delegate` once stabilized.
1008}
1009
1010impl<'slice, 'trie, T, V> DoubleEndedIterator for Latin1CharIndicesWithTrie<'slice, 'trie, T, V>
1011where
1012    V: TrieValue,
1013    T: AbstractCodePointTrie<'trie, V>,
1014{
1015    #[inline]
1016    fn next_back(&mut self) -> Option<Self::Item> {
1017        let b = *self.delegate.next_back()?;
1018        Some((
1019            self.offset + self.as_slice().len(),
1020            char::from(b),
1021            self.trie.latin1(b),
1022        ))
1023    }
1024}
1025
1026impl<'slice, 'trie, T, V> FusedIterator for Latin1CharIndicesWithTrie<'slice, 'trie, T, V>
1027where
1028    V: TrieValue,
1029    T: AbstractCodePointTrie<'trie, V>,
1030{
1031}
1032
1033// --
1034
1035/// Adds convenience methods to `[u8]`.
1036pub trait Latin1CharsWithTrieEx<'slice, 'trie, T, V>
1037where
1038    V: TrieValue,
1039    T: AbstractCodePointTrie<'trie, V>,
1040{
1041    /// Method for easily creating `Latin1CharsWithTrie` on `[u8]` analogously to `chars()` on `str`.
1042    /// (The name is prefixed with `latin1_` to avoid ambiguity with interpreting [u8] as UTF-8.)
1043    fn latin1_chars_with_trie(
1044        &'slice self,
1045        trie: &'trie T,
1046    ) -> Latin1CharsWithTrie<'slice, 'trie, T, V>;
1047
1048    /// Method for easily creating `Latin1CharIndicesWithTrie` on `str` analogously to `char_indices()` on `str`.
1049    /// (The name is prefixed with `latin1_` to avoid ambiguity with interpreting [u8] as UTF-8.)
1050    fn latin1_char_indices_with_trie(
1051        &'slice self,
1052        trie: &'trie T,
1053    ) -> Latin1CharIndicesWithTrie<'slice, 'trie, T, V>;
1054}
1055
1056impl<'slice, 'trie, T, V> Latin1CharsWithTrieEx<'slice, 'trie, T, V> for [u8]
1057where
1058    V: TrieValue,
1059    T: AbstractCodePointTrie<'trie, V>,
1060{
1061    /// Method for easily creating `Latin1CharsWithTrie` on `[u8]` analogously to `chars()` on `str`.
1062    /// (The name is prefixed with `latin1_` to avoid ambiguity with interpreting [u8] as UTF-8.)
1063    #[inline]
1064    fn latin1_chars_with_trie(
1065        &'slice self,
1066        trie: &'trie T,
1067    ) -> Latin1CharsWithTrie<'slice, 'trie, T, V> {
1068        Latin1CharsWithTrie::new(self, trie)
1069    }
1070
1071    /// Method for easily creating `Latin1CharIndicesWithTrie` on `str` analogously to `char_indices()` on `str`.
1072    /// (The name is prefixed with `latin1_` to avoid ambiguity with interpreting [u8] as UTF-8.)
1073    #[inline]
1074    fn latin1_char_indices_with_trie(
1075        &'slice self,
1076        trie: &'trie T,
1077    ) -> Latin1CharIndicesWithTrie<'slice, 'trie, T, V> {
1078        Latin1CharIndicesWithTrie::new(self, trie)
1079    }
1080}
1081
1082// --
1083
1084/// Wraps an `Iterator<Item = char>` with a reference to
1085/// an `AbstractCodePointTrie`.
1086#[derive(Debug)]
1087pub struct CharIterWithTrie<'trie, T, V, I>
1088where
1089    V: TrieValue,
1090    T: AbstractCodePointTrie<'trie, V>,
1091    I: Iterator<Item = char>,
1092{
1093    delegate: I,
1094    trie: &'trie T,
1095    phantom: PhantomData<V>,
1096}
1097
1098impl<'trie, T, V, I> CharIterWithTrie<'trie, T, V, I>
1099where
1100    V: TrieValue,
1101    T: AbstractCodePointTrie<'trie, V>,
1102    I: Iterator<Item = char>,
1103{
1104    /// Constructs a new `CharIterWithTrie`.
1105    #[inline]
1106    pub fn new(iter: I, trie: &'trie T) -> Self {
1107        Self {
1108            delegate: iter,
1109            trie,
1110            phantom: PhantomData,
1111        }
1112    }
1113}
1114
1115impl<'trie, T, V, I> WithTrie<'trie, T, V> for CharIterWithTrie<'trie, T, V, I>
1116where
1117    V: TrieValue,
1118    T: AbstractCodePointTrie<'trie, V>,
1119    I: Iterator<Item = char>,
1120{
1121    #[inline]
1122    fn trie(&self) -> &'trie T {
1123        self.trie
1124    }
1125}
1126
1127impl<'trie, T, V, I> Iterator for CharIterWithTrie<'trie, T, V, I>
1128where
1129    V: TrieValue,
1130    T: AbstractCodePointTrie<'trie, V>,
1131    I: Iterator<Item = char>,
1132{
1133    type Item = (char, V);
1134
1135    #[inline]
1136    fn next(&mut self) -> Option<Self::Item> {
1137        let c = self.delegate.next()?;
1138        Some((c, self.trie.scalar(c)))
1139    }
1140
1141    #[inline]
1142    fn count(self) -> usize {
1143        self.delegate.count()
1144    }
1145
1146    #[inline]
1147    fn size_hint(&self) -> (usize, Option<usize>) {
1148        self.delegate.size_hint()
1149    }
1150
1151    // Looks like conditionally implementing `last()` is not allowed.
1152
1153    // TODO: Delegate advance_by to `delegate` once stabilized.
1154}
1155
1156impl<'trie, T, V, I> DoubleEndedIterator for CharIterWithTrie<'trie, T, V, I>
1157where
1158    V: TrieValue,
1159    T: AbstractCodePointTrie<'trie, V>,
1160    I: DoubleEndedIterator<Item = char>,
1161{
1162    #[inline]
1163    fn next_back(&mut self) -> Option<Self::Item> {
1164        let c = self.delegate.next_back()?;
1165        Some((c, self.trie.scalar(c)))
1166    }
1167}
1168
1169impl<'trie, T, V, I> FusedIterator for CharIterWithTrie<'trie, T, V, I>
1170where
1171    V: TrieValue,
1172    T: AbstractCodePointTrie<'trie, V>,
1173    I: FusedIterator<Item = char>,
1174{
1175}
1176
1177#[cfg(test)]
1178mod tests {
1179    use super::*;
1180
1181    #[test]
1182    fn test_forward() {
1183        let trie = crate::codepointtrie::planes::get_planes_trie();
1184        let s = "abäαあ🥳𧉧";
1185        let mut iter = s.chars_with_trie(&trie);
1186        assert_eq!(iter.next(), Some(('a', 0)));
1187        assert_eq!(iter.next(), Some(('b', 0)));
1188        assert_eq!(iter.next(), Some(('ä', 0)));
1189        assert_eq!(iter.next(), Some(('α', 0)));
1190        assert_eq!(iter.next(), Some(('あ', 0)));
1191        assert_eq!(iter.next(), Some(('🥳', 1)));
1192        assert_eq!(iter.next(), Some(('𧉧', 2)));
1193        assert_eq!(iter.next(), None);
1194    }
1195
1196    #[test]
1197    fn test_backwards() {
1198        let trie = crate::codepointtrie::planes::get_planes_trie();
1199        let s = "abäαあ🥳𧉧";
1200        let mut iter = s.chars_with_trie(&trie);
1201        assert_eq!(iter.next_back(), Some(('𧉧', 2)));
1202        assert_eq!(iter.next_back(), Some(('🥳', 1)));
1203        assert_eq!(iter.next_back(), Some(('あ', 0)));
1204        assert_eq!(iter.next_back(), Some(('α', 0)));
1205        assert_eq!(iter.next_back(), Some(('ä', 0)));
1206        assert_eq!(iter.next_back(), Some(('b', 0)));
1207        assert_eq!(iter.next_back(), Some(('a', 0)));
1208        assert_eq!(iter.next(), None);
1209    }
1210
1211    #[test]
1212    fn test_indices_forward() {
1213        let trie = crate::codepointtrie::planes::get_planes_trie();
1214        let s = "abäαあ🥳𧉧";
1215        let mut iter = s.char_indices_with_trie(&trie);
1216        assert_eq!(iter.next(), Some((0, 'a', 0)));
1217        assert_eq!(iter.next(), Some((1, 'b', 0)));
1218        assert_eq!(iter.next(), Some((2, 'ä', 0)));
1219        assert_eq!(iter.next(), Some((4, 'α', 0)));
1220        assert_eq!(iter.next(), Some((6, 'あ', 0)));
1221        assert_eq!(iter.next(), Some((9, '🥳', 1)));
1222        assert_eq!(iter.next(), Some((13, '𧉧', 2)));
1223        assert_eq!(iter.next(), None);
1224    }
1225
1226    #[test]
1227    fn test_indices_backwards() {
1228        let trie = crate::codepointtrie::planes::get_planes_trie();
1229        let s = "abäαあ🥳𧉧";
1230        let mut iter = s.char_indices_with_trie(&trie);
1231        assert_eq!(iter.next_back(), Some((13, '𧉧', 2)));
1232        assert_eq!(iter.next_back(), Some((9, '🥳', 1)));
1233        assert_eq!(iter.next_back(), Some((6, 'あ', 0)));
1234        assert_eq!(iter.next_back(), Some((4, 'α', 0)));
1235        assert_eq!(iter.next_back(), Some((2, 'ä', 0)));
1236        assert_eq!(iter.next_back(), Some((1, 'b', 0)));
1237        assert_eq!(iter.next_back(), Some((0, 'a', 0)));
1238        assert_eq!(iter.next(), None);
1239    }
1240}