use {
crate::unicode,
std::{
io::{self, Read},
},
};
pub struct CharReader<R: Read> {
src: R,
buffer: Box<[u8]>,
pos: usize,
len: usize,
}
const DEFAULT_BUF_SIZE: usize = 5_000;
impl<R: Read> CharReader<R> {
pub fn new(src: R) -> Self {
let buf_size = DEFAULT_BUF_SIZE;
let buffer = vec![0; buf_size].into_boxed_slice();
Self {
src,
buffer,
pos: 0,
len: 0,
}
}
pub fn load_char(&mut self) -> io::Result<Option<(char, usize)>> {
if self.pos >= self.len {
self.len = self.src.read(&mut self.buffer)?;
if self.len == 0 {
return Ok(None);
}
self.pos = 0;
}
let b = self.buffer[self.pos];
let char_size = unicode::utf8_char_width(b);
if self.pos + char_size > self.len {
self.buffer.copy_within(self.pos..self.len, 0);
self.len = self.len - self.pos;
self.len += self.src.read(&mut self.buffer[self.len..])?;
if self.len < char_size {
return Ok(None);
}
self.pos = 0;
}
let code_point = unicode::read_code_point(&self.buffer, self.pos, char_size);
match std::char::from_u32(code_point) {
Some(c) => Ok(Some((c, char_size))),
None => Err(io::Error::new(io::ErrorKind::InvalidData, "Not UTF8"))
}
}
pub fn next_char(&mut self) -> io::Result<Option<char>> {
Ok(match self.load_char()? {
Some(cw) => {
self.pos += cw.1;
Some(cw.0)
}
None => None,
})
}
pub fn peek_char(&mut self) -> io::Result<Option<char>> {
self.load_char().map(|cw| cw.map(|cw| cw.0))
}
pub fn read_line(
&mut self,
line: &mut String, drop_after: usize, fail_after: usize, ) -> io::Result<bool> {
let mut chars_count = 0; loop {
match self.next_char() {
Err(e) => {
return Err(e);
}
Ok(None) => {
return if chars_count > 0 {
Ok(true)
} else {
Ok(false)
};
}
Ok(Some(c)) => {
if c == '\r' {
if let Ok(Some(('\n', 1))) = self.load_char() {
self.pos += 1;
}
return Ok(true);
} else if c == '\n' {
return Ok(true);
} else if chars_count >= fail_after {
return Err(io::Error::new(io::ErrorKind::Other, "Line too long"));
} else if chars_count >= drop_after {
} else {
line.push(c);
}
chars_count += 1;
}
}
}
}
pub fn next_line(
&mut self,
drop_after: usize, fail_after: usize, ) -> io::Result<Option<String>> {
let mut line = String::new();
match self.read_line(&mut line, drop_after, fail_after) {
Ok(true) => Ok(Some(line)),
Ok(false) => Ok(None),
Err(e) => Err(e),
}
}
}