1#![no_std]
18
19#[cfg(feature = "icu_collections")]
27mod cptrie;
28#[cfg(feature = "icu_collections")]
29mod cptrie_indices;
30mod indices;
31mod report;
32
33#[cfg(feature = "icu_collections")]
34pub use crate::cptrie::Utf16CharsWithTrie;
35#[cfg(feature = "icu_collections")]
36pub use crate::cptrie::Utf16CharsWithTrieEx;
37#[cfg(feature = "icu_collections")]
38pub use crate::cptrie_indices::Utf16CharIndicesWithTrie;
39pub use crate::indices::Utf16CharIndices;
40pub use crate::report::ErrorReportingUtf16Chars;
41pub use crate::report::Utf16CharsError;
42use core::iter::FusedIterator;
43
44#[inline(always)]
45pub(crate) fn in_inclusive_range16(i: u16, start: u16, end: u16) -> bool {
46 i.wrapping_sub(start) <= (end - start)
47}
48
49#[derive(Debug, Clone)]
52pub struct Utf16Chars<'a> {
53 remaining: &'a [u16],
54}
55
56impl<'a> Utf16Chars<'a> {
57 #[inline(always)]
58 pub fn new(code_units: &'a [u16]) -> Self {
60 Utf16Chars::<'a> {
61 remaining: code_units,
62 }
63 }
64
65 #[inline(always)]
68 pub fn as_slice(&self) -> &'a [u16] {
69 self.remaining
70 }
71
72 #[inline(never)]
73 fn surrogate_next(&mut self, surrogate_base: u16, first: u16) -> char {
74 if surrogate_base <= (0xDBFF - 0xD800) {
75 if let Some((&low, tail_tail)) = self.remaining.split_first() {
76 if in_inclusive_range16(low, 0xDC00, 0xDFFF) {
77 self.remaining = tail_tail;
78 return unsafe {
79 char::from_u32_unchecked(
80 (u32::from(first) << 10) + u32::from(low)
81 - (((0xD800u32 << 10) - 0x10000u32) + 0xDC00u32),
82 )
83 };
84 }
85 }
86 }
87 '\u{FFFD}'
88 }
89
90 #[inline(never)]
91 fn surrogate_next_back(&mut self, last: u16) -> char {
92 if in_inclusive_range16(last, 0xDC00, 0xDFFF) {
93 if let Some((&high, head_head)) = self.remaining.split_last() {
94 if in_inclusive_range16(high, 0xD800, 0xDBFF) {
95 self.remaining = head_head;
96 return unsafe {
97 char::from_u32_unchecked(
98 (u32::from(high) << 10) + u32::from(last)
99 - (((0xD800u32 << 10) - 0x10000u32) + 0xDC00u32),
100 )
101 };
102 }
103 }
104 }
105 '\u{FFFD}'
106 }
107}
108
109impl<'a> Iterator for Utf16Chars<'a> {
110 type Item = char;
111
112 #[inline(always)]
113 fn next(&mut self) -> Option<char> {
114 let (&first, tail) = self.remaining.split_first()?;
120 self.remaining = tail;
121 let surrogate_base = first.wrapping_sub(0xD800);
122 if surrogate_base > (0xDFFF - 0xD800) {
123 return Some(unsafe { char::from_u32_unchecked(u32::from(first)) });
124 }
125 Some(self.surrogate_next(surrogate_base, first))
126 }
127}
128
129impl<'a> DoubleEndedIterator for Utf16Chars<'a> {
130 #[inline(always)]
131 fn next_back(&mut self) -> Option<char> {
132 let (&last, head) = self.remaining.split_last()?;
133 self.remaining = head;
134 if !in_inclusive_range16(last, 0xD800, 0xDFFF) {
135 return Some(unsafe { char::from_u32_unchecked(u32::from(last)) });
136 }
137 Some(self.surrogate_next_back(last))
138 }
139}
140
141impl FusedIterator for Utf16Chars<'_> {}
142
143pub trait Utf16CharsEx {
146 fn chars(&self) -> Utf16Chars<'_>;
149
150 fn char_indices(&self) -> Utf16CharIndices<'_>;
153}
154
155impl Utf16CharsEx for [u16] {
156 #[inline]
159 fn chars(&self) -> Utf16Chars<'_> {
160 Utf16Chars::new(self)
161 }
162 #[inline]
165 fn char_indices(&self) -> Utf16CharIndices<'_> {
166 Utf16CharIndices::new(self)
167 }
168}
169
170#[cfg(test)]
171mod tests {
172 use crate::Utf16CharsEx;
173
174 #[test]
175 fn test_boundaries() {
176 assert!([0xD7FFu16]
177 .as_slice()
178 .chars()
179 .eq(core::iter::once('\u{D7FF}')));
180 assert!([0xE000u16]
181 .as_slice()
182 .chars()
183 .eq(core::iter::once('\u{E000}')));
184 assert!([0xD800u16]
185 .as_slice()
186 .chars()
187 .eq(core::iter::once('\u{FFFD}')));
188 assert!([0xDFFFu16]
189 .as_slice()
190 .chars()
191 .eq(core::iter::once('\u{FFFD}')));
192 }
193
194 #[test]
195 fn test_unpaired() {
196 assert!([0xD800u16, 0x0061u16]
197 .as_slice()
198 .chars()
199 .eq([0xFFFDu16, 0x0061u16].as_slice().chars()));
200 assert!([0xDFFFu16, 0x0061u16]
201 .as_slice()
202 .chars()
203 .eq([0xFFFDu16, 0x0061u16].as_slice().chars()));
204 }
205
206 #[test]
207 fn test_unpaired_rev() {
208 assert!([0xD800u16, 0x0061u16]
209 .as_slice()
210 .chars()
211 .rev()
212 .eq([0xFFFDu16, 0x0061u16].as_slice().chars().rev()));
213 assert!([0xDFFFu16, 0x0061u16]
214 .as_slice()
215 .chars()
216 .rev()
217 .eq([0xFFFDu16, 0x0061u16].as_slice().chars().rev()));
218 }
219
220 #[test]
221 fn test_paired() {
222 assert!([0xD83Eu16, 0xDD73u16]
223 .as_slice()
224 .chars()
225 .eq(core::iter::once('🥳')));
226 }
227
228 #[test]
229 fn test_paired_rev() {
230 assert!([0xD83Eu16, 0xDD73u16]
231 .as_slice()
232 .chars()
233 .rev()
234 .eq(core::iter::once('🥳')));
235 }
236
237 #[test]
238 fn test_as_slice() {
239 let mut iter = [0x0061u16, 0x0062u16].as_slice().chars();
240 let at_start = iter.as_slice();
241 assert_eq!(iter.next(), Some('a'));
242 let in_middle = iter.as_slice();
243 assert_eq!(iter.next(), Some('b'));
244 let at_end = iter.as_slice();
245 assert_eq!(at_start.len(), 2);
246 assert_eq!(in_middle.len(), 1);
247 assert_eq!(at_end.len(), 0);
248 assert_eq!(at_start[0], 0x0061u16);
249 assert_eq!(at_start[1], 0x0062u16);
250 assert_eq!(in_middle[0], 0x0062u16);
251 }
252}