#[derive(Debug)]
pub enum Consumption {
Consumed(usize),
Stop,
}
#[derive(Debug)]
pub enum Validation {
Ready,
Unready,
Discard,
Stop,
}
#[derive(Debug)]
pub enum Process {
Continue,
Stop,
}
pub trait IncConsumer {
fn consume(&mut self, output: &mut [u8]) -> Consumption;
fn validate(&mut self, output: u8) -> Validation;
fn process(&mut self, input: &[u8]) -> Process;
fn run(&mut self, buf: &mut [u8]) {
let mut begin = 0;
let mut shift = 0;
loop {
for idx in shift..begin {
buf[idx - shift] = buf[idx];
}
begin -= shift;
shift = 0;
match self.consume(&mut buf[begin..]) {
Consumption::Consumed(amount) => {
for ch in buf[begin..(begin + amount)].iter() {
begin += 1;
match self.validate(*ch) {
Validation::Discard => shift = begin,
Validation::Ready => {
match self.process(&buf[shift..begin]) {
Process::Continue => {}
Process::Stop => return,
}
shift = begin;
}
Validation::Stop => return,
Validation::Unready => {}
}
}
}
Consumption::Stop => return,
}
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn simple() {
#[derive(Default)]
struct Consumer {
pub consumption_count: usize,
pub validation_count: usize,
pub process_count: usize,
}
impl IncConsumer for Consumer {
fn consume(&mut self, _: &mut [u8]) -> Consumption {
self.consumption_count += 1;
Consumption::Stop
}
fn validate(&mut self, _: u8) -> Validation {
self.validation_count += 1;
Validation::Ready
}
fn process(&mut self, _: &[u8]) -> Process {
self.process_count += 1;
Process::Continue
}
}
let mut consumed = Consumer::default();
let buffer = &mut [0u8; 0];
consumed.run(buffer);
assert_eq!(1, consumed.consumption_count);
assert_eq!(0, consumed.validation_count);
assert_eq!(0, consumed.process_count);
}
#[test]
fn stop_at_validation() {
#[derive(Default)]
struct Consumer {
pub consumption_count: usize,
pub validation_count: usize,
pub process_count: usize,
}
impl IncConsumer for Consumer {
fn consume(&mut self, _: &mut [u8]) -> Consumption {
self.consumption_count += 1;
Consumption::Consumed(1)
}
fn validate(&mut self, _: u8) -> Validation {
self.validation_count += 1;
Validation::Stop
}
fn process(&mut self, _: &[u8]) -> Process {
self.process_count += 1;
Process::Continue
}
}
let mut consumed = Consumer::default();
let buffer = &mut [0u8; 1];
consumed.run(buffer);
assert_eq!(1, consumed.consumption_count);
assert_eq!(1, consumed.validation_count);
assert_eq!(0, consumed.process_count);
}
#[quickcheck_macros::quickcheck]
fn consuming_bytes_within_bounds_never_fails(bytes: u16) {
#[derive(Default)]
struct Consumer {
bytes: usize,
seen: usize,
}
impl IncConsumer for Consumer {
fn consume(&mut self, _: &mut [u8]) -> Consumption {
if self.bytes == 0 {
Consumption::Stop
} else {
Consumption::Consumed(self.bytes)
}
}
fn validate(&mut self, _: u8) -> Validation {
self.seen += 1;
if self.seen == self.bytes {
Validation::Ready
} else {
Validation::Unready
}
}
fn process(&mut self, input: &[u8]) -> Process {
assert_eq!(self.bytes, input.len());
Process::Stop
}
}
let mut consumed = Consumer {
bytes: bytes as usize,
seen: 0,
};
let mut buffer = vec![0u8; bytes as usize];
consumed.run(&mut buffer);
}
}