1use core::{iter::FusedIterator, marker::PhantomData};
7
8use crate::{
9 encoding::Encoding,
10 props::{
11 CB_CONTROL, CB_CR, CB_EXTEND, CB_EXTEND_INCB_LINKER, CB_L, CB_LF, CB_LV, CB_LVT, CB_MASK,
12 CB_OTHER_INCB_CONSONANT, CB_PREPEND, CB_RI, CB_SPACING_MARK, CB_T, CB_V, CB_ZWJ, EPIC_BIT,
13 INCB_EXTEND_BIT, WIDTH_EMOJI_TEXT, WIDTH_SHIFT, is_emoji_modifier_base, props,
14 },
15 simd::plain_prefix,
16 unit::Unit,
17 utf8::Utf8,
18};
19
20pub struct Grapheme<'a, E: Encoding> {
22 pub units: &'a [E::Unit],
24 pub width: usize,
26}
27
28impl<E: Encoding> Clone for Grapheme<'_, E> {
29 #[inline(always)]
30 fn clone(&self) -> Self {
31 *self
32 }
33}
34
35impl<E: Encoding> Copy for Grapheme<'_, E> {}
36
37impl<E: Encoding> Grapheme<'_, E> {
38 #[inline]
47 pub fn is_control(&self) -> bool {
48 let mut units = self.units;
49 if units.is_empty() {
50 return false;
51 }
52 let cp = E::decode(&mut units);
53 matches!(cp, 0x00..=0x1f | 0x7f | 0x80..=0x9f)
54 }
55}
56
57pub struct Graphemes<'a, E: Encoding> {
63 rest: &'a [E::Unit],
64 _encoding: PhantomData<E>,
65}
66
67impl<E: Encoding> Clone for Graphemes<'_, E> {
68 #[inline(always)]
69 fn clone(&self) -> Self {
70 Self { rest: self.rest, _encoding: PhantomData }
71 }
72}
73
74impl<'a, E: Encoding> Iterator for Graphemes<'a, E> {
75 type Item = Grapheme<'a, E>;
76
77 #[inline]
78 fn next(&mut self) -> Option<Self::Item> {
79 if self.rest.is_empty() {
80 return None;
81 }
82 let scan = next_cluster::<E>(self.rest);
83 let (units, rest) = self.rest.split_at(scan.units);
84 self.rest = rest;
85 Some(Grapheme { units, width: scan.width })
86 }
87
88 #[inline]
89 fn size_hint(&self) -> (usize, Option<usize>) {
90 let len = cluster_count::<E>(self.rest);
91 (len, Some(len))
92 }
93
94 #[inline]
95 fn count(self) -> usize {
96 cluster_count::<E>(self.rest)
97 }
98
99 #[inline]
100 fn last(mut self) -> Option<Grapheme<'a, E>> {
101 self.next_back()
102 }
103}
104
105impl<'a, E: Encoding> DoubleEndedIterator for Graphemes<'a, E> {
106 #[inline]
107 fn next_back(&mut self) -> Option<Grapheme<'a, E>> {
108 if self.rest.is_empty() {
109 return None;
110 }
111 let scan = prev_cluster::<E>(self.rest);
112 let (rest, units) = self.rest.split_at(self.rest.len() - scan.units);
113 self.rest = rest;
114 Some(Grapheme { units, width: scan.width })
115 }
116}
117
118impl<E: Encoding> ExactSizeIterator for Graphemes<'_, E> {
119 #[inline]
120 fn len(&self) -> usize {
121 cluster_count::<E>(self.rest)
122 }
123}
124
125impl<E: Encoding> FusedIterator for Graphemes<'_, E> {}
126
127pub struct GraphemeIndices<'a, E: Encoding> {
135 inner: Graphemes<'a, E>,
136 offset: usize,
137}
138
139impl<E: Encoding> Clone for GraphemeIndices<'_, E> {
140 #[inline(always)]
141 fn clone(&self) -> Self {
142 Self { inner: self.inner.clone(), offset: self.offset }
143 }
144}
145
146impl<'a, E: Encoding> Iterator for GraphemeIndices<'a, E> {
147 type Item = (usize, Grapheme<'a, E>);
148
149 #[inline]
150 fn next(&mut self) -> Option<Self::Item> {
151 let grapheme = self.inner.next()?;
152 let offset = self.offset;
153 self.offset += grapheme.units.len();
154 Some((offset, grapheme))
155 }
156
157 #[inline]
158 fn size_hint(&self) -> (usize, Option<usize>) {
159 self.inner.size_hint()
160 }
161
162 #[inline]
163 fn count(self) -> usize {
164 self.inner.count()
165 }
166
167 #[inline]
168 fn last(mut self) -> Option<Self::Item> {
169 self.next_back()
170 }
171}
172
173impl<E: Encoding> DoubleEndedIterator for GraphemeIndices<'_, E> {
174 #[inline]
175 fn next_back(&mut self) -> Option<Self::Item> {
176 let grapheme = self.inner.next_back()?;
177 Some((self.offset + self.inner.rest.len(), grapheme))
178 }
179}
180
181impl<E: Encoding> ExactSizeIterator for GraphemeIndices<'_, E> {
182 #[inline]
183 fn len(&self) -> usize {
184 self.inner.len()
185 }
186}
187
188impl<E: Encoding> FusedIterator for GraphemeIndices<'_, E> {}
189
190#[inline(always)]
196pub const fn grapheme_indices<E: Encoding>(input: &[E::Unit]) -> GraphemeIndices<'_, E> {
197 GraphemeIndices { inner: graphemes(input), offset: 0 }
198}
199
200#[inline(always)]
203pub const fn graphemes<E: Encoding>(input: &[E::Unit]) -> Graphemes<'_, E> {
204 Graphemes { rest: input, _encoding: PhantomData }
205}
206
207#[derive(Clone)]
211pub struct StrGraphemes<'a> {
212 inner: Graphemes<'a, Utf8>,
213}
214
215impl<'a> Iterator for StrGraphemes<'a> {
216 type Item = &'a str;
217
218 #[inline]
219 fn next(&mut self) -> Option<&'a str> {
220 self
222 .inner
223 .next()
224 .map(|g| unsafe { core::str::from_utf8_unchecked(g.units) })
225 }
226
227 #[inline]
228 fn size_hint(&self) -> (usize, Option<usize>) {
229 self.inner.size_hint()
230 }
231
232 #[inline]
233 fn count(self) -> usize {
234 self.inner.count()
235 }
236
237 #[inline]
238 fn last(mut self) -> Option<&'a str> {
239 self.next_back()
240 }
241}
242
243impl<'a> DoubleEndedIterator for StrGraphemes<'a> {
244 #[inline]
245 fn next_back(&mut self) -> Option<&'a str> {
246 self
248 .inner
249 .next_back()
250 .map(|g| unsafe { core::str::from_utf8_unchecked(g.units) })
251 }
252}
253
254impl ExactSizeIterator for StrGraphemes<'_> {
255 #[inline]
256 fn len(&self) -> usize {
257 self.inner.len()
258 }
259}
260
261impl FusedIterator for StrGraphemes<'_> {}
262
263#[derive(Clone)]
266pub struct StrGraphemeIndices<'a> {
267 inner: GraphemeIndices<'a, Utf8>,
268}
269
270#[inline(always)]
271const fn indexed_str(item: (usize, Grapheme<'_, Utf8>)) -> (usize, &str) {
272 let (offset, grapheme) = item;
273 (offset, unsafe { core::str::from_utf8_unchecked(grapheme.units) })
275}
276
277impl<'a> Iterator for StrGraphemeIndices<'a> {
278 type Item = (usize, &'a str);
279
280 #[inline]
281 fn next(&mut self) -> Option<Self::Item> {
282 self.inner.next().map(indexed_str)
283 }
284
285 #[inline]
286 fn size_hint(&self) -> (usize, Option<usize>) {
287 self.inner.size_hint()
288 }
289
290 #[inline]
291 fn count(self) -> usize {
292 self.inner.count()
293 }
294
295 #[inline]
296 fn last(mut self) -> Option<Self::Item> {
297 self.next_back()
298 }
299}
300
301impl DoubleEndedIterator for StrGraphemeIndices<'_> {
302 #[inline]
303 fn next_back(&mut self) -> Option<Self::Item> {
304 self.inner.next_back().map(indexed_str)
305 }
306}
307
308impl ExactSizeIterator for StrGraphemeIndices<'_> {
309 #[inline]
310 fn len(&self) -> usize {
311 self.inner.len()
312 }
313}
314
315impl FusedIterator for StrGraphemeIndices<'_> {}
316
317#[inline]
320pub const fn graphemes_str(input: &str) -> StrGraphemes<'_> {
321 StrGraphemes { inner: graphemes::<Utf8>(input.as_bytes()) }
322}
323
324#[inline]
330pub const fn grapheme_indices_str(input: &str) -> StrGraphemeIndices<'_> {
331 StrGraphemeIndices { inner: grapheme_indices::<Utf8>(input.as_bytes()) }
332}
333
334pub struct ClusterScan {
336 pub units: usize,
337 pub width: usize,
338}
339
340#[inline(always)]
342pub const fn width_value(p: u8) -> usize {
343 let width = (p >> WIDTH_SHIFT) & 3;
344 if width == WIDTH_EMOJI_TEXT {
345 1
346 } else {
347 width as usize
348 }
349}
350
351pub struct ClusterState {
355 width: usize,
356 prev: u8,
357 prev_cp: u32,
358 epic: u8,
359 incb: u8,
360 ri_odd: bool,
361 after_zwj: bool,
362 promotable: bool,
363 promote: bool,
364}
365
366impl ClusterState {
367 #[inline(always)]
369 pub fn start(cp0: u32, p0: u8) -> Self {
370 let c0 = p0 & CB_MASK;
371 Self {
372 width: width_value(p0),
373 prev: c0,
374 prev_cp: cp0,
375 epic: u8::from(p0 & EPIC_BIT != 0),
376 incb: u8::from(c0 == CB_OTHER_INCB_CONSONANT),
377 ri_odd: c0 == CB_RI,
378 after_zwj: false,
379 promotable: (p0 >> WIDTH_SHIFT) & 3 == WIDTH_EMOJI_TEXT,
380 promote: false,
381 }
382 }
383
384 #[inline(always)]
387 pub const fn joins_plain(&self) -> bool {
388 self.prev == CB_PREPEND
389 }
390
391 #[inline(always)]
394 pub fn try_join(&mut self, cp: u32, p: u8) -> bool {
395 let c = p & CB_MASK;
396
397 if matches!(self.prev, CB_CR | CB_LF | CB_CONTROL) {
398 if self.prev != CB_CR || c != CB_LF {
400 return false;
401 }
402 } else {
403 let join = match c {
404 CB_CR | CB_LF | CB_CONTROL => false,
405 CB_EXTEND | CB_EXTEND_INCB_LINKER | CB_ZWJ => true,
406 CB_SPACING_MARK => true,
407 _ if self.prev == CB_PREPEND => true,
408 CB_L => self.prev == CB_L,
409 CB_V => matches!(self.prev, CB_L | CB_LV | CB_V),
410 CB_T => matches!(self.prev, CB_LV | CB_V | CB_LVT | CB_T),
411 CB_LV | CB_LVT => self.prev == CB_L,
412 CB_RI => self.prev == CB_RI && self.ri_odd,
413 CB_OTHER_INCB_CONSONANT => self.incb == 2,
414 _ => self.prev == CB_ZWJ && self.epic == 2 && p & EPIC_BIT != 0,
415 };
416 if !join {
417 return false;
418 }
419 }
420
421 if c == CB_EXTEND || c == CB_EXTEND_INCB_LINKER {
422 if self.epic != 1 {
423 self.epic = 0;
424 }
425 } else if c == CB_ZWJ {
426 self.epic = if self.epic == 1 { 2 } else { 0 };
427 } else if p & EPIC_BIT != 0 {
428 self.epic = 1;
429 } else {
430 self.epic = 0;
431 }
432
433 if c == CB_OTHER_INCB_CONSONANT {
434 self.incb = 1;
435 } else if c == CB_EXTEND_INCB_LINKER {
436 self.incb = if self.incb != 0 { 2 } else { 0 };
437 } else if p & INCB_EXTEND_BIT == 0 {
438 self.incb = 0;
439 }
440
441 self.ri_odd = c == CB_RI && !self.ri_odd;
442
443 if cp == 0xfe0f || cp == 0x20e3 {
444 self.promote = true;
445 }
446 if c == CB_ZWJ {
447 self.after_zwj = true;
448 } else if !self.after_zwj {
449 let modifier = (c == CB_EXTEND || c == CB_EXTEND_INCB_LINKER)
455 && width_value(p) == 2
456 && is_emoji_modifier_base(self.prev_cp);
457 if !modifier {
458 self.width += width_value(p);
459 }
460 }
461 self.prev_cp = cp;
462 self.prev = c;
463 true
464 }
465
466 #[inline(always)]
468 pub fn finish(&self) -> usize {
469 if self.promote && self.promotable {
470 self.width.max(2)
471 } else {
472 self.width
473 }
474 }
475}
476
477#[inline]
479pub fn next_cluster<E: Encoding>(input: &[E::Unit]) -> ClusterScan {
480 if !E::FOREIGN {
481 let u0 = input[0].to_u32();
482 if u0 < 0x80 {
483 if u0 == 0x0d {
484 if input.len() > 1 && input[1].to_u32() == 0x0a {
485 return ClusterScan { units: 2, width: 0 };
486 }
487 return ClusterScan { units: 1, width: 0 };
488 }
489 if input.len() == 1 || input[1].to_u32() < 0x80 {
490 let width = usize::from((0x20..=0x7e).contains(&u0));
491 return ClusterScan { units: 1, width };
492 }
493 }
494 }
495
496 let mut rest = input;
497 let cp0 = E::decode(&mut rest);
498 let mut state = ClusterState::start(cp0, props(cp0));
499
500 while !rest.is_empty() {
501 let mut peek = rest;
502 let cp = E::decode(&mut peek);
503 if !state.try_join(cp, props(cp)) {
504 break;
505 }
506 rest = peek;
507 }
508
509 ClusterScan { units: input.len() - rest.len(), width: state.finish() }
510}
511
512#[inline(always)]
518const fn may_join(a: u8, b: u8) -> bool {
519 let ca = a & CB_MASK;
520 let cb = b & CB_MASK;
521 if matches!(ca, CB_CR | CB_LF | CB_CONTROL) {
522 return ca == CB_CR && cb == CB_LF;
524 }
525 match cb {
526 CB_CR | CB_LF | CB_CONTROL => false,
527 CB_EXTEND | CB_EXTEND_INCB_LINKER | CB_ZWJ | CB_SPACING_MARK => true,
528 _ if ca == CB_PREPEND => true,
529 CB_L => ca == CB_L,
530 CB_V => matches!(ca, CB_L | CB_LV | CB_V),
531 CB_T => matches!(ca, CB_LV | CB_V | CB_LVT | CB_T),
532 CB_LV | CB_LVT => ca == CB_L,
533 CB_RI => ca == CB_RI,
534 CB_OTHER_INCB_CONSONANT => {
537 ca == CB_EXTEND_INCB_LINKER || (a & INCB_EXTEND_BIT != 0 && ca != CB_OTHER_INCB_CONSONANT)
538 },
539 _ => ca == CB_ZWJ && b & EPIC_BIT != 0,
540 }
541}
542
543#[inline]
551pub fn prev_cluster<E: Encoding>(input: &[E::Unit]) -> ClusterScan {
552 if !E::FOREIGN {
553 let last = input[input.len() - 1].to_u32();
554 if last < 0x80 {
555 let prev = if input.len() > 1 {
556 input[input.len() - 2].to_u32()
557 } else {
558 0x80
559 };
560 if last == 0x0a && prev == 0x0d {
561 return ClusterScan { units: 2, width: 0 };
562 }
563 if input.len() == 1 || prev < 0x80 {
566 let width = usize::from((0x20..=0x7e).contains(&last));
567 return ClusterScan { units: 1, width };
568 }
569 }
570 }
571
572 let mut back = input;
573 let mut after = props(E::decode_back(&mut back));
574 while !back.is_empty() {
575 let mut peek = back;
576 let p = props(E::decode_back(&mut peek));
577 if !may_join(p, after) {
578 break;
579 }
580 back = peek;
581 after = p;
582 }
583
584 let mut at = back.len();
586 loop {
587 let scan = next_cluster::<E>(&input[at..]);
588 if at + scan.units == input.len() {
589 return scan;
590 }
591 at += scan.units;
592 }
593}
594
595pub fn cluster_count<E: Encoding>(input: &[E::Unit]) -> usize {
598 let mut rest = input;
599 let mut count = 0;
600 while !rest.is_empty() {
601 if !E::FOREIGN {
602 let run = plain_prefix(rest);
603 if run == rest.len() {
604 return count + run;
605 }
606 if run > 1 {
609 count += run - 1;
610 rest = &rest[run - 1..];
611 }
612 }
613 let scan = next_cluster::<E>(rest);
614 count += 1;
615 rest = &rest[scan.units..];
616 }
617 count
618}