unicode_normalize_aligned/
recompose.rs1use core::iter::FusedIterator;
4
5use crate::buffer::RunBuffer;
6use crate::decompose::Decompositions;
7use crate::props::{self, hangul, Props};
8
9#[derive(Clone, Copy, Debug)]
10enum State {
11 Composing,
12 Purging(usize),
13 Finished(usize),
14}
15
16#[derive(Clone, Debug)]
22pub struct Recompositions<I> {
23 inner: Decompositions<I>,
24 state: State,
25 buffer: RunBuffer<(char, isize)>,
27 composee: Option<(char, isize)>,
28 composee_first: Option<bool>,
32 last_ccc: Option<u8>,
33}
34
35impl<I: Iterator<Item = char>> Recompositions<I> {
36 pub(crate) fn new(inner: Decompositions<I>) -> Self {
37 Recompositions {
38 inner,
39 state: State::Composing,
40 buffer: RunBuffer::new(),
41 composee: None,
42 composee_first: None,
43 last_ccc: None,
44 }
45 }
46
47 #[inline]
48 fn set_composee(&mut self, c: char, tag: isize) {
49 self.composee = Some((c, tag));
50 self.composee_first = None;
51 }
52
53 #[inline]
56 fn try_compose(&mut self, starter: (char, isize), c: char, tag: isize) -> bool {
57 if !Props::of(c).composes_second() {
58 return false;
59 }
60 let first = *self
61 .composee_first
62 .get_or_insert_with(|| Props::of(starter.0).composes_first());
63 if !first {
64 return false;
65 }
66 let composed = hangul::compose(starter.0, c).or_else(|| props::compose_pair(starter.0, c));
67 match composed {
68 Some(r) => {
69 self.set_composee(r, starter.1 + tag - 1);
70 true
71 }
72 None => false,
73 }
74 }
75
76 fn block(&mut self, c: char, tag: isize, ccc: u8) {
77 self.buffer.push((c, tag));
78 self.last_ccc = Some(ccc);
79 }
80
81 fn compose_step(&mut self) -> Option<(char, isize)> {
82 while let Some((ccc, c, tag)) = self.inner.next_full() {
83 let starter = match self.composee {
84 Some(k) => k,
85 None => {
86 if ccc != 0 {
87 return Some((c, tag));
88 }
89 self.set_composee(c, tag);
90 continue;
91 }
92 };
93 match self.last_ccc {
94 None => {
95 if self.try_compose(starter, c, tag) {
96 continue;
97 }
98 if ccc == 0 {
99 self.set_composee(c, tag);
100 return Some(starter);
101 }
102 self.block(c, tag, ccc);
103 }
104 Some(last) => {
105 if last >= ccc {
106 if ccc == 0 {
107 self.set_composee(c, tag);
108 self.last_ccc = None;
109 self.state = State::Purging(0);
110 return Some(starter);
111 }
112 self.block(c, tag, ccc);
113 continue;
114 }
115 if self.try_compose(starter, c, tag) {
116 continue;
117 }
118 self.block(c, tag, ccc);
119 }
120 }
121 }
122 self.state = State::Finished(0);
123 self.composee.take()
124 }
125}
126
127impl<I: Iterator<Item = char>> Iterator for Recompositions<I> {
128 type Item = (char, isize);
129
130 #[inline]
131 fn next(&mut self) -> Option<(char, isize)> {
132 loop {
133 match self.state {
134 State::Composing => {
135 if let Some(item) = self.compose_step() {
136 return Some(item);
137 }
138 }
139 State::Purging(i) => {
140 if let Some(&item) = self.buffer.get(i) {
141 self.state = State::Purging(i + 1);
142 return Some(item);
143 }
144 self.buffer.clear();
145 self.state = State::Composing;
146 }
147 State::Finished(i) => {
148 if let Some(&item) = self.buffer.get(i) {
149 self.state = State::Finished(i + 1);
150 return Some(item);
151 }
152 self.buffer.clear();
153 return None;
154 }
155 }
156 }
157 }
158
159 fn size_hint(&self) -> (usize, Option<usize>) {
160 (0, None)
163 }
164}
165
166impl<I: Iterator<Item = char>> FusedIterator for Recompositions<I> {}