use crate::Utf8Error;
pub struct TryDecoder<R> {
bytes: R,
offset: usize,
}
impl<R> TryDecoder<R> {
pub fn new(source: R) -> TryDecoder<R> {
TryDecoder {
bytes: source,
offset: 0,
}
}
}
impl<R, E> Iterator for TryDecoder<R>
where
R: Iterator<Item = Result<u8, E>>,
E: From<Utf8Error>,
{
type Item = Result<char, E>;
fn next(&mut self) -> Option<Result<char, E>> {
Some(
try_decode_iter_char(self.offset, &mut self.bytes)
.transpose()?
.map(|(c, len)| {
self.offset += len as usize;
c
}),
)
}
}
pub fn try_decode_iter_char<E>(
offset: usize,
iter: &mut impl Iterator<Item = Result<u8, E>>,
) -> Result<Option<(char, u8)>, E>
where
E: From<Utf8Error>,
{
match try_decode_iter_codepoint(offset, iter)? {
Some((codepoint, len)) => match char::from_u32(codepoint) {
Some(c) => Ok(Some((c, len))),
None => Err(Utf8Error::new(offset, len as usize).into()),
},
None => Ok(None),
}
}
fn try_decode_iter_codepoint<E>(
offset: usize,
iter: &mut impl Iterator<Item = Result<u8, E>>,
) -> Result<Option<(u32, u8)>, E>
where
E: From<Utf8Error>,
{
match iter.next() {
Some(Ok(a)) => {
let a = a as u32;
if a & 0x80 == 0x00 {
Ok(Some((a, 1)))
} else if a & 0xE0 == 0xC0 {
let b = try_next_iter_byte(iter, offset, 1)?;
Ok(Some(((a & 0x1F) << 6 | b, 2)))
} else if a & 0xF0 == 0xE0 {
let b = try_next_iter_byte(iter, offset, 1)?;
let c = try_next_iter_byte(iter, offset, 2)?;
Ok(Some(((a & 0x0F) << 12 | b << 6 | c, 3)))
} else if a & 0xF8 == 0xF0 {
let b = try_next_iter_byte(iter, offset, 1)?;
let c = try_next_iter_byte(iter, offset, 2)?;
let d = try_next_iter_byte(iter, offset, 3)?;
Ok(Some(((a & 0x07) << 18 | b << 12 | c << 6 | d, 4)))
} else {
Err(Utf8Error::new(offset, 1).into())
}
}
Some(Err(e)) => Err(e),
None => Ok(None),
}
}
fn try_next_iter_byte<E>(
iter: &mut impl Iterator<Item = Result<u8, E>>,
offset: usize,
len: usize,
) -> Result<u32, E>
where
E: From<Utf8Error>,
{
match iter.next() {
Some(Ok(c)) => {
if c & 0xC0 == 0x80 {
Ok((c & 0x3F) as u32)
} else {
Err(Utf8Error::new(offset, len + 1).into())
}
}
Some(Err(e)) => Err(e),
None => Err(Utf8Error::new(offset, len).into()),
}
}