1use super::char::EscapeDebugExtArgs;
2use super::from_utf8_unchecked;
3use super::validations::utf8_char_width;
4use crate::fmt;
5use crate::fmt::{Formatter, Write};
6use crate::iter::FusedIterator;
7
8impl [u8] {
9 #[stable(feature = "utf8_chunks", since = "1.79.0")]
45 pub fn utf8_chunks(&self) -> Utf8Chunks<'_> {
46 Utf8Chunks { source: self }
47 }
48}
49
50#[stable(feature = "utf8_chunks", since = "1.79.0")]
71#[derive(#[automatically_derived]
#[stable(feature = "utf8_chunks", since = "1.79.0")]
impl<'a> crate::clone::Clone for Utf8Chunk<'a> {
#[inline]
fn clone(&self) -> Utf8Chunk<'a> {
Utf8Chunk {
valid: crate::clone::Clone::clone(&self.valid),
invalid: crate::clone::Clone::clone(&self.invalid),
}
}
}Clone, #[automatically_derived]
#[stable(feature = "utf8_chunks", since = "1.79.0")]
impl<'a> crate::fmt::Debug for Utf8Chunk<'a> {
#[inline]
fn fmt(&self, f: &mut crate::fmt::Formatter) -> crate::fmt::Result {
crate::fmt::Formatter::debug_struct_field2_finish(f, "Utf8Chunk",
"valid", &self.valid, "invalid", &&self.invalid)
}
}Debug, #[automatically_derived]
#[stable(feature = "utf8_chunks", since = "1.79.0")]
impl<'a> crate::marker::StructuralPartialEq for Utf8Chunk<'a> { }
#[automatically_derived]
#[stable(feature = "utf8_chunks", since = "1.79.0")]
impl<'a> crate::cmp::PartialEq for Utf8Chunk<'a> {
#[inline]
fn eq(&self, other: &Utf8Chunk<'a>) -> bool {
self.valid == other.valid && self.invalid == other.invalid
}
}PartialEq, #[automatically_derived]
#[stable(feature = "utf8_chunks", since = "1.79.0")]
impl<'a> crate::cmp::Eq for Utf8Chunk<'a> {
#[inline]
#[doc(hidden)]
#[coverage(off)]
fn assert_fields_are_eq(&self) {
let _: crate::cmp::AssertParamIsEq<&'a str>;
let _: crate::cmp::AssertParamIsEq<&'a [u8]>;
}
}Eq)]
72pub struct Utf8Chunk<'a> {
73 valid: &'a str,
74 invalid: &'a [u8],
75}
76
77impl<'a> Utf8Chunk<'a> {
78 #[must_use]
83 #[stable(feature = "utf8_chunks", since = "1.79.0")]
84 pub fn valid(&self) -> &'a str {
85 self.valid
86 }
87
88 #[must_use]
103 #[stable(feature = "utf8_chunks", since = "1.79.0")]
104 pub fn invalid(&self) -> &'a [u8] {
105 self.invalid
106 }
107}
108
109#[must_use]
110#[unstable(feature = "str_internals", issue = "none")]
111pub struct Debug<'a>(&'a [u8]);
112
113#[unstable(feature = "str_internals", issue = "none")]
114impl fmt::Debug for Debug<'_> {
115 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
116 f.write_char('"')?;
117
118 for chunk in self.0.utf8_chunks() {
119 {
122 let valid = chunk.valid();
123 let mut from = 0;
124 for (i, c) in valid.char_indices() {
125 let esc = c.escape_debug_ext(EscapeDebugExtArgs {
126 escape_single_quote: false,
127 escape_double_quote: true,
128 });
129 if esc.len() != 1 {
131 f.write_str(&valid[from..i])?;
132 for c in esc {
133 f.write_char(c)?;
134 }
135 from = i + c.len_utf8();
136 }
137 }
138 f.write_str(&valid[from..])?;
139 }
140
141 for &b in chunk.invalid() {
143 f.write_fmt(format_args!("\\x{0:02X}", b))write!(f, "\\x{:02X}", b)?;
144 }
145 }
146
147 f.write_char('"')
148 }
149}
150
151#[must_use = "iterators are lazy and do nothing unless consumed"]
183#[stable(feature = "utf8_chunks", since = "1.79.0")]
184#[derive(#[automatically_derived]
#[stable(feature = "utf8_chunks", since = "1.79.0")]
impl<'a> crate::clone::Clone for Utf8Chunks<'a> {
#[inline]
fn clone(&self) -> Utf8Chunks<'a> {
Utf8Chunks { source: crate::clone::Clone::clone(&self.source) }
}
}Clone)]
185pub struct Utf8Chunks<'a> {
186 source: &'a [u8],
187}
188
189impl<'a> Utf8Chunks<'a> {
190 #[doc(hidden)]
191 #[unstable(feature = "str_internals", issue = "none")]
192 pub fn debug(&self) -> Debug<'_> {
193 Debug(self.source)
194 }
195}
196
197#[stable(feature = "utf8_chunks", since = "1.79.0")]
198impl<'a> Iterator for Utf8Chunks<'a> {
199 type Item = Utf8Chunk<'a>;
200
201 fn next(&mut self) -> Option<Utf8Chunk<'a>> {
202 if self.source.is_empty() {
203 return None;
204 }
205
206 const TAG_CONT_U8: u8 = 128;
207 fn safe_get(xs: &[u8], i: usize) -> u8 {
208 *xs.get(i).unwrap_or(&0)
209 }
210
211 let mut i = 0;
212 let mut valid_up_to = 0;
213 while let Some(byte) = self.source.get(i).copied() {
214 i += 1;
215
216 if byte < 128 {
217 } else {
221 let w = utf8_char_width(byte);
222
223 match w {
224 2 => {
225 if safe_get(self.source, i) & 192 != TAG_CONT_U8 {
226 break;
227 }
228 i += 1;
229 }
230 3 => {
231 match (byte, safe_get(self.source, i)) {
232 (0xE0, 0xA0..=0xBF) => (),
233 (0xE1..=0xEC, 0x80..=0xBF) => (),
234 (0xED, 0x80..=0x9F) => (),
235 (0xEE..=0xEF, 0x80..=0xBF) => (),
236 _ => break,
237 }
238 i += 1;
239 if safe_get(self.source, i) & 192 != TAG_CONT_U8 {
240 break;
241 }
242 i += 1;
243 }
244 4 => {
245 match (byte, safe_get(self.source, i)) {
246 (0xF0, 0x90..=0xBF) => (),
247 (0xF1..=0xF3, 0x80..=0xBF) => (),
248 (0xF4, 0x80..=0x8F) => (),
249 _ => break,
250 }
251 i += 1;
252 if safe_get(self.source, i) & 192 != TAG_CONT_U8 {
253 break;
254 }
255 i += 1;
256 if safe_get(self.source, i) & 192 != TAG_CONT_U8 {
257 break;
258 }
259 i += 1;
260 }
261 _ => break,
262 }
263 }
264
265 valid_up_to = i;
266 }
267
268 let (inspected, remaining) = unsafe { self.source.split_at_unchecked(i) };
277 self.source = remaining;
278
279 let (valid, invalid) = unsafe { inspected.split_at_unchecked(valid_up_to) };
282
283 Some(Utf8Chunk {
284 valid: unsafe { from_utf8_unchecked(valid) },
286 invalid,
287 })
288 }
289}
290
291#[stable(feature = "utf8_chunks", since = "1.79.0")]
292impl FusedIterator for Utf8Chunks<'_> {}
293
294#[stable(feature = "utf8_chunks", since = "1.79.0")]
295impl fmt::Debug for Utf8Chunks<'_> {
296 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
297 f.debug_struct("Utf8Chunks").field("source", &self.debug()).finish()
298 }
299}