use std::{fmt, fs::File, io};
use crate::{event::InputEvent, write_raw};
const BATCH_WRITE_SIZE: usize = 12;
pub(crate) struct BatchWriter {
buffer: [InputEvent; BATCH_WRITE_SIZE],
bufpos: usize,
}
impl fmt::Debug for BatchWriter {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("BatchWriter")
.field("buffer", &&self.buffer[..self.bufpos])
.finish()
}
}
impl BatchWriter {
pub(crate) fn new() -> Self {
BatchWriter {
buffer: [InputEvent::zeroed(); BATCH_WRITE_SIZE],
bufpos: 0,
}
}
pub(crate) fn write(&mut self, events: &[InputEvent], file: &File) -> io::Result<()> {
self.write_to(events, |ev| write_raw(file, ev))
}
pub(crate) fn flush(&mut self, file: &File) -> io::Result<()> {
self.flush_to(|ev| write_raw(file, ev))
}
fn write_to<W>(&mut self, events: &[InputEvent], mut writer: W) -> io::Result<()>
where
W: FnMut(&[InputEvent]) -> io::Result<()>,
{
let remaining = self.buffer.len() - self.bufpos;
if events.len() > remaining {
self.flush_to(&mut writer)?;
}
if events.len() >= BATCH_WRITE_SIZE {
self.flush_to(&mut writer)?;
return writer(events);
}
self.buffer[self.bufpos..][..events.len()].copy_from_slice(events);
self.bufpos += events.len();
Ok(())
}
fn flush_to<W>(&mut self, mut writer: W) -> io::Result<()>
where
W: FnMut(&[InputEvent]) -> io::Result<()>,
{
let is_empty = self.bufpos == 0;
if !is_empty {
writer(&self.buffer[..self.bufpos])?;
self.bufpos = 0;
}
Ok(())
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn batch_writer() -> io::Result<()> {
let mut w = BatchWriter::new();
w.write_to(&[InputEvent::zeroed(); BATCH_WRITE_SIZE - 1], |_| {
unreachable!("shouldn't write them yet")
})?;
w.write_to(&[InputEvent::zeroed(); 1], |_| {
unreachable!("shouldn't write them yet")
})?;
let mut wrote = Vec::new();
w.write_to(&[InputEvent::zeroed()], |ev| {
wrote.push(ev.len());
Ok(())
})?;
assert_eq!(wrote, &[BATCH_WRITE_SIZE], "should have written events");
assert_eq!(w.bufpos, 1, "should have 1 event in the buffer");
let mut wrote = Vec::new();
w.write_to(&[InputEvent::zeroed(); BATCH_WRITE_SIZE + 1], |ev| {
wrote.push(ev.len());
Ok(())
})?;
assert_eq!(wrote, &[1, BATCH_WRITE_SIZE + 1]);
assert_eq!(w.bufpos, 0);
let mut wrote = Vec::new();
w.write_to(&[InputEvent::zeroed(); BATCH_WRITE_SIZE], |ev| {
wrote.push(ev.len());
Ok(())
})?;
assert_eq!(wrote, &[BATCH_WRITE_SIZE]);
assert_eq!(w.bufpos, 0);
w.write_to(&[InputEvent::zeroed(); 1], |_| {
unreachable!("shouldn't write them yet")
})?;
assert_eq!(w.bufpos, 1);
let mut wrote = Vec::new();
w.write_to(&[InputEvent::zeroed(); BATCH_WRITE_SIZE], |ev| {
wrote.push(ev.len());
Ok(())
})?;
assert_eq!(wrote, &[1, BATCH_WRITE_SIZE]);
assert_eq!(w.bufpos, 0);
w.flush_to(|_| {
unreachable!("should not flush anything");
})?;
Ok(())
}
}