Coverage Report

Created: 2025-11-24 06:43

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/unicode-normalization/src/recompose.rs
Line
Count
Source
1
// Copyright 2012-2015 The Rust Project Developers. See the COPYRIGHT
2
// file at the top-level directory of this distribution and at
3
// http://rust-lang.org/COPYRIGHT.
4
//
5
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8
// option. This file may not be copied, modified, or distributed
9
// except according to those terms.
10
11
use crate::decompose::Decompositions;
12
use core::{
13
    fmt::{self, Write},
14
    iter::FusedIterator,
15
};
16
use tinyvec::TinyVec;
17
18
#[derive(Clone)]
19
enum RecompositionState {
20
    Composing,
21
    Purging(usize),
22
    Finished(usize),
23
}
24
25
/// External iterator for a string recomposition's characters.
26
#[derive(Clone)]
27
pub struct Recompositions<I> {
28
    iter: Decompositions<I>,
29
    state: RecompositionState,
30
    buffer: TinyVec<[char; 4]>,
31
    composee: Option<char>,
32
    last_ccc: Option<u8>,
33
}
34
35
impl<I: Iterator<Item = char>> Recompositions<I> {
36
    /// Create a new recomposition iterator for canonical compositions (NFC)
37
    ///
38
    /// Note that this iterator can also be obtained by directly calling [`.nfc()`](crate::UnicodeNormalization::nfc)
39
    /// on the iterator.
40
    #[inline]
41
24.2k
    pub fn new_canonical(iter: I) -> Self {
42
24.2k
        Recompositions {
43
24.2k
            iter: Decompositions::new_canonical(iter),
44
24.2k
            state: self::RecompositionState::Composing,
45
24.2k
            buffer: TinyVec::new(),
46
24.2k
            composee: None,
47
24.2k
            last_ccc: None,
48
24.2k
        }
49
24.2k
    }
<unicode_normalization::recompose::Recompositions<unicode_normalization::stream_safe::StreamSafe<core::str::iter::Chars>>>::new_canonical
Line
Count
Source
41
11.6k
    pub fn new_canonical(iter: I) -> Self {
42
11.6k
        Recompositions {
43
11.6k
            iter: Decompositions::new_canonical(iter),
44
11.6k
            state: self::RecompositionState::Composing,
45
11.6k
            buffer: TinyVec::new(),
46
11.6k
            composee: None,
47
11.6k
            last_ccc: None,
48
11.6k
        }
49
11.6k
    }
<unicode_normalization::recompose::Recompositions<core::str::iter::Chars>>::new_canonical
Line
Count
Source
41
9.37k
    pub fn new_canonical(iter: I) -> Self {
42
9.37k
        Recompositions {
43
9.37k
            iter: Decompositions::new_canonical(iter),
44
9.37k
            state: self::RecompositionState::Composing,
45
9.37k
            buffer: TinyVec::new(),
46
9.37k
            composee: None,
47
9.37k
            last_ccc: None,
48
9.37k
        }
49
9.37k
    }
Unexecuted instantiation: <unicode_normalization::recompose::Recompositions<_>>::new_canonical
<unicode_normalization::recompose::Recompositions<streaming::Counter>>::new_canonical
Line
Count
Source
41
2.23k
    pub fn new_canonical(iter: I) -> Self {
42
2.23k
        Recompositions {
43
2.23k
            iter: Decompositions::new_canonical(iter),
44
2.23k
            state: self::RecompositionState::Composing,
45
2.23k
            buffer: TinyVec::new(),
46
2.23k
            composee: None,
47
2.23k
            last_ccc: None,
48
2.23k
        }
49
2.23k
    }
<unicode_normalization::recompose::Recompositions<core::str::iter::Chars>>::new_canonical
Line
Count
Source
41
913
    pub fn new_canonical(iter: I) -> Self {
42
913
        Recompositions {
43
913
            iter: Decompositions::new_canonical(iter),
44
913
            state: self::RecompositionState::Composing,
45
913
            buffer: TinyVec::new(),
46
913
            composee: None,
47
913
            last_ccc: None,
48
913
        }
49
913
    }
50
51
    /// Create a new recomposition iterator for compatability compositions (NFkC)
52
    ///
53
    /// Note that this iterator can also be obtained by directly calling [`.nfkc()`](crate::UnicodeNormalization::nfkc)
54
    /// on the iterator.
55
    #[inline]
56
10.5k
    pub fn new_compatible(iter: I) -> Self {
57
10.5k
        Recompositions {
58
10.5k
            iter: Decompositions::new_compatible(iter),
59
10.5k
            state: self::RecompositionState::Composing,
60
10.5k
            buffer: TinyVec::new(),
61
10.5k
            composee: None,
62
10.5k
            last_ccc: None,
63
10.5k
        }
64
10.5k
    }
<unicode_normalization::recompose::Recompositions<core::str::iter::Chars>>::new_compatible
Line
Count
Source
56
9.16k
    pub fn new_compatible(iter: I) -> Self {
57
9.16k
        Recompositions {
58
9.16k
            iter: Decompositions::new_compatible(iter),
59
9.16k
            state: self::RecompositionState::Composing,
60
9.16k
            buffer: TinyVec::new(),
61
9.16k
            composee: None,
62
9.16k
            last_ccc: None,
63
9.16k
        }
64
9.16k
    }
Unexecuted instantiation: <unicode_normalization::recompose::Recompositions<_>>::new_compatible
<unicode_normalization::recompose::Recompositions<core::str::iter::Chars>>::new_compatible
Line
Count
Source
56
1.39k
    pub fn new_compatible(iter: I) -> Self {
57
1.39k
        Recompositions {
58
1.39k
            iter: Decompositions::new_compatible(iter),
59
1.39k
            state: self::RecompositionState::Composing,
60
1.39k
            buffer: TinyVec::new(),
61
1.39k
            composee: None,
62
1.39k
            last_ccc: None,
63
1.39k
        }
64
1.39k
    }
65
}
66
67
impl<I: Iterator<Item = char>> Iterator for Recompositions<I> {
68
    type Item = char;
69
70
    #[inline]
71
304M
    fn next(&mut self) -> Option<char> {
72
        use self::RecompositionState::*;
73
74
        loop {
75
306M
            match self.state {
76
                Composing => {
77
314M
                    for ch in self.iter.by_ref() {
78
314M
                        let ch_class = super::char::canonical_combining_class(ch);
79
314M
                        let k = match self.composee {
80
                            None => {
81
4.17M
                                if ch_class != 0 {
82
4.13M
                                    return Some(ch);
83
32.5k
                                }
84
32.5k
                                self.composee = Some(ch);
85
32.5k
                                continue;
86
                            }
87
310M
                            Some(k) => k,
88
                        };
89
310M
                        match self.last_ccc {
90
148M
                            None => match super::char::compose(k, ch) {
91
10.3M
                                Some(r) => {
92
10.3M
                                    self.composee = Some(r);
93
10.3M
                                    continue;
94
                                }
95
                                None => {
96
138M
                                    if ch_class == 0 {
97
136M
                                        self.composee = Some(ch);
98
136M
                                        return Some(k);
99
2.03M
                                    }
100
2.03M
                                    self.buffer.push(ch);
101
2.03M
                                    self.last_ccc = Some(ch_class);
102
                                }
103
                            },
104
162M
                            Some(l_class) => {
105
162M
                                if l_class >= ch_class {
106
                                    // `ch` is blocked from `composee`
107
161M
                                    if ch_class == 0 {
108
2.02M
                                        self.composee = Some(ch);
109
2.02M
                                        self.last_ccc = None;
110
2.02M
                                        self.state = Purging(0);
111
2.02M
                                        return Some(k);
112
159M
                                    }
113
159M
                                    self.buffer.push(ch);
114
159M
                                    self.last_ccc = Some(ch_class);
115
159M
                                    continue;
116
392k
                                }
117
392k
                                match super::char::compose(k, ch) {
118
27.7k
                                    Some(r) => {
119
27.7k
                                        self.composee = Some(r);
120
27.7k
                                        continue;
121
                                    }
122
364k
                                    None => {
123
364k
                                        self.buffer.push(ch);
124
364k
                                        self.last_ccc = Some(ch_class);
125
364k
                                    }
126
                                }
127
                            }
128
                        }
129
                    }
130
34.3k
                    self.state = Finished(0);
131
34.3k
                    if self.composee.is_some() {
132
32.1k
                        return self.composee.take();
133
2.23k
                    }
134
                }
135
123M
                Purging(next) => match self.buffer.get(next).cloned() {
136
2.02M
                    None => {
137
2.02M
                        self.buffer.clear();
138
2.02M
                        self.state = Composing;
139
2.02M
                    }
140
121M
                    s => {
141
121M
                        self.state = Purging(next + 1);
142
121M
                        return s;
143
                    }
144
                },
145
40.1M
                Finished(next) => match self.buffer.get(next).cloned() {
146
                    None => {
147
34.1k
                        self.buffer.clear();
148
34.1k
                        return self.composee.take();
149
                    }
150
40.1M
                    s => {
151
40.1M
                        self.state = Finished(next + 1);
152
40.1M
                        return s;
153
                    }
154
                },
155
            }
156
        }
157
304M
    }
<unicode_normalization::recompose::Recompositions<unicode_normalization::stream_safe::StreamSafe<core::str::iter::Chars>> as core::iter::traits::iterator::Iterator>::next
Line
Count
Source
71
88.4M
    fn next(&mut self) -> Option<char> {
72
        use self::RecompositionState::*;
73
74
        loop {
75
90.1M
            match self.state {
76
                Composing => {
77
92.3M
                    for ch in self.iter.by_ref() {
78
92.3M
                        let ch_class = super::char::canonical_combining_class(ch);
79
92.3M
                        let k = match self.composee {
80
                            None => {
81
37.2k
                                if ch_class != 0 {
82
26.0k
                                    return Some(ch);
83
11.1k
                                }
84
11.1k
                                self.composee = Some(ch);
85
11.1k
                                continue;
86
                            }
87
92.2M
                            Some(k) => k,
88
                        };
89
92.2M
                        match self.last_ccc {
90
40.7M
                            None => match super::char::compose(k, ch) {
91
3.91M
                                Some(r) => {
92
3.91M
                                    self.composee = Some(r);
93
3.91M
                                    continue;
94
                                }
95
                                None => {
96
36.8M
                                    if ch_class == 0 {
97
35.0M
                                        self.composee = Some(ch);
98
35.0M
                                        return Some(k);
99
1.74M
                                    }
100
1.74M
                                    self.buffer.push(ch);
101
1.74M
                                    self.last_ccc = Some(ch_class);
102
                                }
103
                            },
104
51.5M
                            Some(l_class) => {
105
51.5M
                                if l_class >= ch_class {
106
                                    // `ch` is blocked from `composee`
107
51.3M
                                    if ch_class == 0 {
108
1.73M
                                        self.composee = Some(ch);
109
1.73M
                                        self.last_ccc = None;
110
1.73M
                                        self.state = Purging(0);
111
1.73M
                                        return Some(k);
112
49.5M
                                    }
113
49.5M
                                    self.buffer.push(ch);
114
49.5M
                                    self.last_ccc = Some(ch_class);
115
49.5M
                                    continue;
116
203k
                                }
117
203k
                                match super::char::compose(k, ch) {
118
7.77k
                                    Some(r) => {
119
7.77k
                                        self.composee = Some(r);
120
7.77k
                                        continue;
121
                                    }
122
195k
                                    None => {
123
195k
                                        self.buffer.push(ch);
124
195k
                                        self.last_ccc = Some(ch_class);
125
195k
                                    }
126
                                }
127
                            }
128
                        }
129
                    }
130
11.5k
                    self.state = Finished(0);
131
11.5k
                    if self.composee.is_some() {
132
11.0k
                        return self.composee.take();
133
499
                    }
134
                }
135
53.2M
                Purging(next) => match self.buffer.get(next).cloned() {
136
1.73M
                    None => {
137
1.73M
                        self.buffer.clear();
138
1.73M
                        self.state = Composing;
139
1.73M
                    }
140
51.5M
                    s => {
141
51.5M
                        self.state = Purging(next + 1);
142
51.5M
                        return s;
143
                    }
144
                },
145
44.3k
                Finished(next) => match self.buffer.get(next).cloned() {
146
                    None => {
147
11.5k
                        self.buffer.clear();
148
11.5k
                        return self.composee.take();
149
                    }
150
32.8k
                    s => {
151
32.8k
                        self.state = Finished(next + 1);
152
32.8k
                        return s;
153
                    }
154
                },
155
            }
156
        }
157
88.4M
    }
<unicode_normalization::recompose::Recompositions<core::str::iter::Chars> as core::iter::traits::iterator::Iterator>::next
Line
Count
Source
71
148M
    fn next(&mut self) -> Option<char> {
72
        use self::RecompositionState::*;
73
74
        loop {
75
148M
            match self.state {
76
                Composing => {
77
154M
                    for ch in self.iter.by_ref() {
78
154M
                        let ch_class = super::char::canonical_combining_class(ch);
79
154M
                        let k = match self.composee {
80
                            None => {
81
162k
                                if ch_class != 0 {
82
145k
                                    return Some(ch);
83
17.3k
                                }
84
17.3k
                                self.composee = Some(ch);
85
17.3k
                                continue;
86
                            }
87
154M
                            Some(k) => k,
88
                        };
89
154M
                        match self.last_ccc {
90
85.9M
                            None => match super::char::compose(k, ch) {
91
6.40M
                                Some(r) => {
92
6.40M
                                    self.composee = Some(r);
93
6.40M
                                    continue;
94
                                }
95
                                None => {
96
79.5M
                                    if ch_class == 0 {
97
79.4M
                                        self.composee = Some(ch);
98
79.4M
                                        return Some(k);
99
35.0k
                                    }
100
35.0k
                                    self.buffer.push(ch);
101
35.0k
                                    self.last_ccc = Some(ch_class);
102
                                }
103
                            },
104
68.6M
                            Some(l_class) => {
105
68.6M
                                if l_class >= ch_class {
106
                                    // `ch` is blocked from `composee`
107
68.6M
                                    if ch_class == 0 {
108
29.5k
                                        self.composee = Some(ch);
109
29.5k
                                        self.last_ccc = None;
110
29.5k
                                        self.state = Purging(0);
111
29.5k
                                        return Some(k);
112
68.6M
                                    }
113
68.6M
                                    self.buffer.push(ch);
114
68.6M
                                    self.last_ccc = Some(ch_class);
115
68.6M
                                    continue;
116
54.4k
                                }
117
54.4k
                                match super::char::compose(k, ch) {
118
11.5k
                                    Some(r) => {
119
11.5k
                                        self.composee = Some(r);
120
11.5k
                                        continue;
121
                                    }
122
42.8k
                                    None => {
123
42.8k
                                        self.buffer.push(ch);
124
42.8k
                                        self.last_ccc = Some(ch_class);
125
42.8k
                                    }
126
                                }
127
                            }
128
                        }
129
                    }
130
18.2k
                    self.state = Finished(0);
131
18.2k
                    if self.composee.is_some() {
132
17.0k
                        return self.composee.take();
133
1.18k
                    }
134
                }
135
46.3M
                Purging(next) => match self.buffer.get(next).cloned() {
136
29.4k
                    None => {
137
29.4k
                        self.buffer.clear();
138
29.4k
                        self.state = Composing;
139
29.4k
                    }
140
46.2M
                    s => {
141
46.2M
                        self.state = Purging(next + 1);
142
46.2M
                        return s;
143
                    }
144
                },
145
22.4M
                Finished(next) => match self.buffer.get(next).cloned() {
146
                    None => {
147
18.1k
                        self.buffer.clear();
148
18.1k
                        return self.composee.take();
149
                    }
150
22.3M
                    s => {
151
22.3M
                        self.state = Finished(next + 1);
152
22.3M
                        return s;
153
                    }
154
                },
155
            }
156
        }
157
148M
    }
Unexecuted instantiation: <unicode_normalization::recompose::Recompositions<_> as core::iter::traits::iterator::Iterator>::next
<unicode_normalization::recompose::Recompositions<streaming::Counter> as core::iter::traits::iterator::Iterator>::next
Line
Count
Source
71
15.0M
    fn next(&mut self) -> Option<char> {
72
        use self::RecompositionState::*;
73
74
        loop {
75
15.3M
            match self.state {
76
                Composing => {
77
15.0M
                    for ch in self.iter.by_ref() {
78
15.0M
                        let ch_class = super::char::canonical_combining_class(ch);
79
15.0M
                        let k = match self.composee {
80
                            None => {
81
9.60k
                                if ch_class != 0 {
82
7.56k
                                    return Some(ch);
83
2.03k
                                }
84
2.03k
                                self.composee = Some(ch);
85
2.03k
                                continue;
86
                            }
87
15.0M
                            Some(k) => k,
88
                        };
89
15.0M
                        match self.last_ccc {
90
7.98M
                            None => match super::char::compose(k, ch) {
91
15.9k
                                Some(r) => {
92
15.9k
                                    self.composee = Some(r);
93
15.9k
                                    continue;
94
                                }
95
                                None => {
96
7.96M
                                    if ch_class == 0 {
97
7.71M
                                        self.composee = Some(ch);
98
7.71M
                                        return Some(k);
99
253k
                                    }
100
253k
                                    self.buffer.push(ch);
101
253k
                                    self.last_ccc = Some(ch_class);
102
                                }
103
                            },
104
7.08M
                            Some(l_class) => {
105
7.08M
                                if l_class >= ch_class {
106
                                    // `ch` is blocked from `composee`
107
6.96M
                                    if ch_class == 0 {
108
252k
                                        self.composee = Some(ch);
109
252k
                                        self.last_ccc = None;
110
252k
                                        self.state = Purging(0);
111
252k
                                        return Some(k);
112
6.71M
                                    }
113
6.71M
                                    self.buffer.push(ch);
114
6.71M
                                    self.last_ccc = Some(ch_class);
115
6.71M
                                    continue;
116
116k
                                }
117
116k
                                match super::char::compose(k, ch) {
118
6.74k
                                    Some(r) => {
119
6.74k
                                        self.composee = Some(r);
120
6.74k
                                        continue;
121
                                    }
122
109k
                                    None => {
123
109k
                                        self.buffer.push(ch);
124
109k
                                        self.last_ccc = Some(ch_class);
125
109k
                                    }
126
                                }
127
                            }
128
                        }
129
                    }
130
2.23k
                    self.state = Finished(0);
131
2.23k
                    if self.composee.is_some() {
132
2.03k
                        return self.composee.take();
133
197
                    }
134
                }
135
7.32M
                Purging(next) => match self.buffer.get(next).cloned() {
136
252k
                    None => {
137
252k
                        self.buffer.clear();
138
252k
                        self.state = Composing;
139
252k
                    }
140
7.07M
                    s => {
141
7.07M
                        self.state = Purging(next + 1);
142
7.07M
                        return s;
143
                    }
144
                },
145
9.74k
                Finished(next) => match self.buffer.get(next).cloned() {
146
                    None => {
147
2.23k
                        self.buffer.clear();
148
2.23k
                        return self.composee.take();
149
                    }
150
7.51k
                    s => {
151
7.51k
                        self.state = Finished(next + 1);
152
7.51k
                        return s;
153
                    }
154
                },
155
            }
156
        }
157
15.0M
    }
<unicode_normalization::recompose::Recompositions<core::str::iter::Chars> as core::iter::traits::iterator::Iterator>::next
Line
Count
Source
71
52.7M
    fn next(&mut self) -> Option<char> {
72
        use self::RecompositionState::*;
73
74
        loop {
75
52.7M
            match self.state {
76
                Composing => {
77
52.7M
                    for ch in self.iter.by_ref() {
78
52.7M
                        let ch_class = super::char::canonical_combining_class(ch);
79
52.7M
                        let k = match self.composee {
80
                            None => {
81
3.96M
                                if ch_class != 0 {
82
3.96M
                                    return Some(ch);
83
1.95k
                                }
84
1.95k
                                self.composee = Some(ch);
85
1.95k
                                continue;
86
                            }
87
48.7M
                            Some(k) => k,
88
                        };
89
48.7M
                        match self.last_ccc {
90
14.0M
                            None => match super::char::compose(k, ch) {
91
6.59k
                                Some(r) => {
92
6.59k
                                    self.composee = Some(r);
93
6.59k
                                    continue;
94
                                }
95
                                None => {
96
14.0M
                                    if ch_class == 0 {
97
14.0M
                                        self.composee = Some(ch);
98
14.0M
                                        return Some(k);
99
5.39k
                                    }
100
5.39k
                                    self.buffer.push(ch);
101
5.39k
                                    self.last_ccc = Some(ch_class);
102
                                }
103
                            },
104
34.6M
                            Some(l_class) => {
105
34.6M
                                if l_class >= ch_class {
106
                                    // `ch` is blocked from `composee`
107
34.6M
                                    if ch_class == 0 {
108
4.57k
                                        self.composee = Some(ch);
109
4.57k
                                        self.last_ccc = None;
110
4.57k
                                        self.state = Purging(0);
111
4.57k
                                        return Some(k);
112
34.6M
                                    }
113
34.6M
                                    self.buffer.push(ch);
114
34.6M
                                    self.last_ccc = Some(ch_class);
115
34.6M
                                    continue;
116
18.0k
                                }
117
18.0k
                                match super::char::compose(k, ch) {
118
1.63k
                                    Some(r) => {
119
1.63k
                                        self.composee = Some(r);
120
1.63k
                                        continue;
121
                                    }
122
16.4k
                                    None => {
123
16.4k
                                        self.buffer.push(ch);
124
16.4k
                                        self.last_ccc = Some(ch_class);
125
16.4k
                                    }
126
                                }
127
                            }
128
                        }
129
                    }
130
2.30k
                    self.state = Finished(0);
131
2.30k
                    if self.composee.is_some() {
132
1.95k
                        return self.composee.take();
133
352
                    }
134
                }
135
17.0M
                Purging(next) => match self.buffer.get(next).cloned() {
136
4.57k
                    None => {
137
4.57k
                        self.buffer.clear();
138
4.57k
                        self.state = Composing;
139
4.57k
                    }
140
17.0M
                    s => {
141
17.0M
                        self.state = Purging(next + 1);
142
17.0M
                        return s;
143
                    }
144
                },
145
17.6M
                Finished(next) => match self.buffer.get(next).cloned() {
146
                    None => {
147
2.30k
                        self.buffer.clear();
148
2.30k
                        return self.composee.take();
149
                    }
150
17.6M
                    s => {
151
17.6M
                        self.state = Finished(next + 1);
152
17.6M
                        return s;
153
                    }
154
                },
155
            }
156
        }
157
52.7M
    }
158
}
159
160
impl<I: Iterator<Item = char> + FusedIterator> FusedIterator for Recompositions<I> {}
161
162
impl<I: Iterator<Item = char> + Clone> fmt::Display for Recompositions<I> {
163
0
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
164
0
        for c in self.clone() {
165
0
            f.write_char(c)?;
166
        }
167
0
        Ok(())
168
0
    }
169
}