use crate::char_traits::is_breakz;
use crate::input::{BorrowedInput, Input};
use arraydeque::ArrayDeque;
const BUFFER_LEN: usize = 16;
#[allow(clippy::module_name_repetitions)]
pub struct BufferedInput<T: Iterator<Item = char>> {
input: T,
buffer: ArrayDeque<char, BUFFER_LEN>,
}
impl<T: Iterator<Item = char>> BufferedInput<T> {
pub fn new(input: T) -> Self {
Self {
input,
buffer: ArrayDeque::default(),
}
}
}
impl<T: Iterator<Item = char>> Input for BufferedInput<T> {
#[inline]
fn lookahead(&mut self, count: usize) {
let target = count.min(BUFFER_LEN);
if self.buffer.len() >= target {
return;
}
for _ in 0..(target - self.buffer.len()) {
self.buffer
.push_back(self.input.next().unwrap_or('\0'))
.unwrap();
}
}
#[inline]
fn buflen(&self) -> usize {
self.buffer.len()
}
#[inline]
fn bufmaxlen(&self) -> usize {
BUFFER_LEN
}
#[inline]
fn raw_read_ch(&mut self) -> char {
self.input.next().unwrap_or('\0')
}
#[inline]
fn raw_read_non_breakz_ch(&mut self) -> Option<char> {
if let Some(c) = self.input.next() {
if is_breakz(c) {
self.buffer.push_back(c).unwrap();
None
} else {
Some(c)
}
} else {
None
}
}
#[inline]
fn skip(&mut self) {
self.buffer.pop_front();
}
#[inline]
fn skip_n(&mut self, count: usize) {
self.buffer.drain(0..count);
}
#[inline]
fn peek(&self) -> char {
self.buffer[0]
}
#[inline]
fn peek_nth(&self, n: usize) -> char {
self.buffer[n]
}
}
impl<T: Iterator<Item = char>> BorrowedInput<'static> for BufferedInput<T> {
#[inline]
fn slice_borrowed(&self, _start: usize, _end: usize) -> Option<&'static str> {
None
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn lookahead_larger_than_buffer_is_clamped() {
let mut input = BufferedInput::new("abc".chars());
input.lookahead(BUFFER_LEN + 8);
assert_eq!(input.buflen(), BUFFER_LEN);
assert_eq!(input.peek(), 'a');
assert_eq!(input.peek_nth(1), 'b');
assert_eq!(input.peek_nth(2), 'c');
assert_eq!(input.peek_nth(3), '\0');
}
}