unicode_normalize_aligned/
decompose.rs1use core::iter::FusedIterator;
4
5use alloc::vec::Vec;
6
7use crate::buffer::{RunBuffer, INLINE};
8use crate::props::{self, hangul, Props};
9
10#[derive(Clone, Copy, Debug)]
11pub(crate) enum Kind {
12 Canonical,
13 Compatible,
14}
15
16#[derive(Clone, Debug)]
22pub struct Decompositions<I> {
23 kind: Kind,
24 iter: I,
25 buffer: RunBuffer<(u8, char, isize)>,
29 ready: usize,
30 pos: usize,
31 done: bool,
32}
33
34impl<I: Iterator<Item = char>> Decompositions<I> {
35 pub(crate) fn new(iter: I, kind: Kind) -> Self {
36 Decompositions {
37 kind,
38 iter,
39 buffer: RunBuffer::new(),
40 ready: 0,
41 pos: 0,
42 done: false,
43 }
44 }
45
46 fn sort_pending(&mut self) {
51 let start = self.ready;
52 let run = match self.buffer.as_mut_slice().get_mut(start..) {
53 Some(run) => run,
54 None => return,
55 };
56 if run.len() <= INLINE {
57 for i in 1..run.len() {
58 let mut j = i;
59 while j > 0 && run[j - 1].0 > run[j].0 {
60 run.swap(j - 1, j);
61 j -= 1;
62 }
63 }
64 } else {
65 counting_sort(run);
66 }
67 self.ready = self.buffer.len();
68 }
69
70 #[inline]
76 fn push_item(&mut self, item: (u8, char, isize)) {
77 if !self.buffer.try_push(item) {
78 self.push_item_slow(item);
79 }
80 }
81
82 #[cold]
86 fn push_item_slow(&mut self, item: (u8, char, isize)) {
87 if self.pos > 0 {
88 self.buffer.compact_front(self.pos);
89 self.ready -= self.pos;
90 self.pos = 0;
91 }
92 if !self.buffer.try_push(item) {
93 self.buffer.push(item);
94 }
95 }
96
97 #[inline]
98 fn push(&mut self, ccc: u8, c: char, tag: isize) {
99 if ccc == 0 {
100 if self.ready != self.buffer.len() {
101 self.sort_pending();
102 }
103 if self.pos == self.buffer.len() {
104 self.buffer.clear();
105 self.pos = 0;
106 }
107 self.push_item((0, c, tag));
108 self.ready = self.buffer.len();
109 } else {
110 self.push_item((ccc, c, tag));
111 }
112 }
113
114 #[inline]
117 pub(crate) fn next_full(&mut self) -> Option<(u8, char, isize)> {
118 loop {
119 if self.pos < self.ready {
120 let &item = self.buffer.get(self.pos)?;
121 self.pos += 1;
122 return Some(item);
123 }
124 if self.done {
125 return None;
126 }
127 match self.iter.next() {
128 Some(c) => self.push_char(c),
129 None => {
130 self.done = true;
131 self.sort_pending();
132 }
133 }
134 }
135 }
136
137 fn push_sequence(&mut self, seq: &[char]) {
138 for (i, &d) in seq.iter().enumerate() {
139 let tag = if i == 0 { 0 } else { 1 };
140 self.push(Props::of(d).ccc(), d, tag);
141 }
142 }
143
144 fn push_char(&mut self, c: char) {
145 if (c as u32) < 0x80 {
146 self.push(0, c, 0);
147 return;
148 }
149 let p = Props::of(c);
150 let mapping = match self.kind {
151 Kind::Canonical if p.nfd_no() => Some(Kind::Canonical),
152 Kind::Compatible if p.nfkd_no() => Some(self.kind),
153 _ => None,
154 };
155 let kind = match mapping {
156 Some(kind) => kind,
157 None => {
158 self.push(p.ccc(), c, 0);
159 return;
160 }
161 };
162 if let Some((l, v, t)) = hangul::decompose(c) {
163 self.push(0, l, 0);
164 self.push(0, v, 1);
165 if let Some(t) = t {
166 self.push(0, t, 1);
167 }
168 return;
169 }
170 let seq = match kind {
171 Kind::Compatible if p.has_compat() => props::compat_distinct(c),
172 _ => props::canonical(c),
173 };
174 match seq {
175 Some(seq) => self.push_sequence(seq),
176 None => self.push(p.ccc(), c, 0),
177 }
178 }
179}
180
181#[cold]
186fn counting_sort(run: &mut [(u8, char, isize)]) {
187 let mut next = [0usize; 256];
188 for item in run.iter() {
189 next[item.0 as usize] += 1;
190 }
191 let mut total = 0;
192 for slot in next.iter_mut() {
193 let count = *slot;
194 *slot = total;
195 total += count;
196 }
197 let mut scratch = Vec::new();
198 scratch.resize(run.len(), (0, '\0', 0));
199 for &item in run.iter() {
200 let slot = &mut next[item.0 as usize];
201 scratch[*slot] = item;
202 *slot += 1;
203 }
204 run.copy_from_slice(&scratch);
205}
206
207impl<I: Iterator<Item = char>> Iterator for Decompositions<I> {
208 type Item = (char, isize);
209
210 #[inline]
211 fn next(&mut self) -> Option<(char, isize)> {
212 self.next_full().map(|(_, c, tag)| (c, tag))
213 }
214
215 fn size_hint(&self) -> (usize, Option<usize>) {
216 let (lower, _) = self.iter.size_hint();
217 (lower.saturating_add(self.ready - self.pos), None)
220 }
221}
222
223impl<I: Iterator<Item = char>> FusedIterator for Decompositions<I> {}