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))
}
}