fn try_parse_str_chars()

in sgx_demangle/src/v0.rs [301:375]


    fn try_parse_str_chars(&self) -> Option<impl Iterator<Item = char> + 's> {
        if self.nibbles.len() % 2 != 0 {
            return None;
        }

        // FIXME(eddyb) use `array_chunks` instead, when that becomes stable.
        let mut bytes = self
            .nibbles
            .as_bytes()
            .chunks_exact(2)
            .map(|slice| match slice {
                [a, b] => [a, b],
                _ => unreachable!(),
            })
            .map(|[&hi, &lo]| {
                let half = |nibble: u8| (nibble as char).to_digit(16).unwrap() as u8;
                (half(hi) << 4) | half(lo)
            });

        let chars = iter::from_fn(move || {
            // As long as there are any bytes left, there's at least one more
            // UTF-8-encoded `char` to decode (or the possibility of error).
            bytes.next().map(|first_byte| -> Result<char, ()> {
                // FIXME(eddyb) this `enum` and `fn` should be somewhere in `core`.
                enum Utf8FirstByteError {
                    ContinuationByte,
                    TooLong,
                }
                fn utf8_len_from_first_byte(byte: u8) -> Result<usize, Utf8FirstByteError> {
                    match byte {
                        0x00..=0x7f => Ok(1),
                        0x80..=0xbf => Err(Utf8FirstByteError::ContinuationByte),
                        0xc0..=0xdf => Ok(2),
                        0xe0..=0xef => Ok(3),
                        0xf0..=0xf7 => Ok(4),
                        0xf8..=0xff => Err(Utf8FirstByteError::TooLong),
                    }
                }

                // Collect the appropriate amount of bytes (up to 4), according
                // to the UTF-8 length implied by the first byte.
                let utf8_len = utf8_len_from_first_byte(first_byte).map_err(|_| ())?;
                let utf8 = &mut [first_byte, 0, 0, 0][..utf8_len];
                for i in 1..utf8_len {
                    utf8[i] = bytes.next().ok_or(())?;
                }

                // Fully validate the UTF-8 sequence.
                let s = str::from_utf8(utf8).map_err(|_| ())?;

                // Since we included exactly one UTF-8 sequence, and validation
                // succeeded, `str::chars` should return exactly one `char`.
                let mut chars = s.chars();
                match (chars.next(), chars.next()) {
                    (Some(c), None) => Ok(c),
                    _ => unreachable!(
                        "str::from_utf8({:?}) = {:?} was expected to have 1 char, \
                         but {} chars were found",
                        utf8,
                        s,
                        s.chars().count()
                    ),
                }
            })
        });

        // HACK(eddyb) doing a separate validation iteration like this might be
        // wasteful, but it's easier to avoid starting to print a string literal
        // in the first place, than to abort it mid-string.
        if chars.clone().any(|r| r.is_err()) {
            None
        } else {
            Some(chars.map(Result::unwrap))
        }
    }