use std::{
collections::VecDeque,
convert::From,
default::Default,
io,
io::{
Bytes,
Read,
},
iter::Iterator,
ops::Range,
str,
};
pub struct ReadChars<R: Read> {
inner: Bytes<R>,
current: usize,
queue: VecDeque<io::Result<(char, Range<usize>)>>,
}
impl<R: Read> ReadChars<R> {
pub const DEFAULT_CAPACITY: usize = 16;
pub fn new(reader: R, capacity: usize) -> ReadChars<R> {
ReadChars {
inner: reader.bytes(),
current: usize::default(),
queue: VecDeque::with_capacity(capacity),
}
}
}
impl<R: Read> From<R> for ReadChars<R> {
fn from(value: R) -> Self {
ReadChars {
inner: value.bytes(),
current: usize::default(),
queue: VecDeque::with_capacity(ReadChars::<R>::DEFAULT_CAPACITY),
}
}
}
impl<R: Read> Iterator for ReadChars<R> {
type Item = io::Result<(char, Range<usize>)>;
fn next(&mut self) -> Option<Self::Item> {
if self.queue.is_empty() { 'outer: while self.queue.len() != self.queue.capacity() {
let mut buffer = [0x0u8; 4];
let chr_start = self.current;
buffer[0] = match self.inner.next() {
Some(Ok(c)) => c,
Some(Err(e)) => return Some(Err(e)),
None => return None,
};
self.current += 1;
let need_bytes = {
let start = buffer[0];
if start.is_ascii() { 0 } else
if start >> 5 == 0b110 { 1 } else
if start >> 4 == 0b1110 { 2 } else
if start >> 3 == 0b11110 { 3 } else
{
self.queue.push_front(Err(io::Error::from(io::ErrorKind::InvalidData)));
self.current += 1;
continue;
}
};
if need_bytes != 0 {
for idx in 1..=need_bytes {
buffer[idx] = match self.inner.next() {
Some(Ok(byte)) if byte >> 6 == 0b10 => {
self.current += 1;
byte
},
Some(Ok(_)) => {
self.queue.push_front(Err(io::Error::from(io::ErrorKind::InvalidData)));
self.current += 1;
continue 'outer;
},
Some(Err(e)) => {
self.queue.push_front(Err(e));
self.current += 1;
continue 'outer;
},
None => {
self.queue.push_front(Err(io::Error::from(io::ErrorKind::UnexpectedEof)));
self.current += 1;
continue 'outer;
}
};
}
}
let chr = match str::from_utf8(&buffer[0..=need_bytes]) {
Ok(chr) => chr.chars().next().unwrap(),
Err(e) => {
self.queue.push_front(Err(io::Error::new(io::ErrorKind::InvalidData, e)));
continue 'outer;
},
};
self.queue.push_front(Ok((chr, chr_start..self.current)));
}}
self.queue.pop_back()
}
}