windows_strings/
decode.rs

1/// An internal helper for decoding an iterator of chars and displaying them
2pub struct Decode<F>(pub F);
3
4impl<F, R, E> core::fmt::Display for Decode<F>
5where
6    F: Clone + FnOnce() -> R,
7    R: IntoIterator<Item = core::result::Result<char, E>>,
8{
9    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
10        use core::fmt::Write;
11        let iter = self.0.clone();
12        for c in iter().into_iter() {
13            f.write_char(c.unwrap_or(core::char::REPLACEMENT_CHARACTER))?
14        }
15        Ok(())
16    }
17}
18
19/// Mirror of `std::char::decode_utf16` for utf-8.
20pub fn decode_utf8(
21    mut buffer: &[u8],
22) -> impl Iterator<Item = core::result::Result<char, core::str::Utf8Error>> + '_ {
23    let mut current = "".chars();
24    let mut previous_error = None;
25    core::iter::from_fn(move || {
26        loop {
27            match (current.next(), previous_error) {
28                (Some(c), _) => return Some(Ok(c)),
29                // Return the previous error
30                (None, Some(e)) => {
31                    previous_error = None;
32                    return Some(Err(e));
33                }
34                // We're completely done
35                (None, None) if buffer.is_empty() => return None,
36                (None, None) => {
37                    match core::str::from_utf8(buffer) {
38                        Ok(s) => {
39                            current = s.chars();
40                            buffer = &[];
41                        }
42                        Err(e) => {
43                            let (valid, rest) = buffer.split_at(e.valid_up_to());
44                            // Skip the invalid sequence and stop completely if we ended early
45                            let invalid_sequence_length = e.error_len()?;
46                            buffer = &rest[invalid_sequence_length..];
47
48                            // Set the current iterator to the valid section and indicate previous error
49                            // SAFETY: `valid` is known to be valid utf-8 from error
50                            current = unsafe { core::str::from_utf8_unchecked(valid) }.chars();
51                            previous_error = Some(e);
52                        }
53                    }
54                }
55            }
56        }
57    })
58}