use crate::command::EventSpec;
#[derive(Debug, Clone, Copy)]
pub struct RecordEntry {
pub tick_delta: u16,
pub spec: EventSpec,
}
impl Default for RecordEntry {
fn default() -> Self {
Self {
tick_delta: 0,
spec: EventSpec::Tick,
}
}
}
pub struct EventRecorder<const N: usize = 256> {
buf: [RecordEntry; N],
len: usize,
running: bool,
tick_counter: u32,
last_event_tick: u32,
}
impl<const N: usize> Default for EventRecorder<N> {
fn default() -> Self {
Self::new()
}
}
impl<const N: usize> EventRecorder<N> {
pub fn new() -> Self {
Self {
buf: [RecordEntry::default(); N],
len: 0,
running: false,
tick_counter: 0,
last_event_tick: 0,
}
}
pub fn start(&mut self) {
self.len = 0;
self.tick_counter = 0;
self.last_event_tick = 0;
self.running = true;
}
pub fn stop(&mut self) {
self.running = false;
}
pub fn is_running(&self) -> bool {
self.running
}
pub fn is_full(&self) -> bool {
self.len >= N
}
pub fn len(&self) -> usize {
self.len
}
pub fn is_empty(&self) -> bool {
self.len == 0
}
pub fn record(&mut self, spec: EventSpec) {
if !self.running || self.len >= N {
if self.len >= N {
self.running = false;
}
return;
}
let delta = self.tick_counter.saturating_sub(self.last_event_tick);
self.buf[self.len] = RecordEntry {
tick_delta: delta.min(u16::MAX as u32) as u16,
spec,
};
self.len += 1;
self.last_event_tick = self.tick_counter;
if self.len >= N {
self.running = false;
}
}
pub fn tick(&mut self) {
if self.running {
self.tick_counter = self.tick_counter.wrapping_add(1);
}
}
pub fn iter(&self) -> impl Iterator<Item = &RecordEntry> {
self.buf[..self.len].iter()
}
pub fn drain(&mut self) -> DrainIter<'_, N> {
self.running = false;
let len = self.len;
self.len = 0;
DrainIter {
buf: &self.buf,
pos: 0,
end: len,
}
}
}
pub struct DrainIter<'a, const N: usize> {
buf: &'a [RecordEntry; N],
pos: usize,
end: usize,
}
impl<const N: usize> Iterator for DrainIter<'_, N> {
type Item = RecordEntry;
fn next(&mut self) -> Option<Self::Item> {
if self.pos < self.end {
let entry = self.buf[self.pos];
self.pos += 1;
Some(entry)
} else {
None
}
}
fn size_hint(&self) -> (usize, Option<usize>) {
let remaining = self.end - self.pos;
(remaining, Some(remaining))
}
}
impl<const N: usize> ExactSizeIterator for DrainIter<'_, N> {}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn basic_record_and_drain() {
let mut rec = EventRecorder::<16>::new();
assert!(!rec.is_running());
assert_eq!(rec.len(), 0);
rec.start();
assert!(rec.is_running());
rec.record(EventSpec::PointerDown { x: 10, y: 20 });
assert_eq!(rec.len(), 1);
rec.tick();
rec.tick();
rec.tick();
rec.record(EventSpec::PointerMove { x: 15, y: 25 });
assert_eq!(rec.len(), 2);
rec.tick();
rec.tick();
rec.record(EventSpec::PointerUp { x: 20, y: 30 });
rec.stop();
assert!(!rec.is_running());
assert_eq!(rec.len(), 3);
let entries: Vec<_> = rec.drain().collect();
assert_eq!(entries.len(), 3);
assert_eq!(entries[0].tick_delta, 0);
assert_eq!(entries[0].spec, EventSpec::PointerDown { x: 10, y: 20 });
assert_eq!(entries[1].tick_delta, 3);
assert_eq!(entries[1].spec, EventSpec::PointerMove { x: 15, y: 25 });
assert_eq!(entries[2].tick_delta, 2);
assert_eq!(entries[2].spec, EventSpec::PointerUp { x: 20, y: 30 });
assert_eq!(rec.len(), 0);
}
#[test]
fn auto_stops_when_full() {
let mut rec = EventRecorder::<4>::new();
rec.start();
for i in 0..10 {
rec.record(EventSpec::PointerMove { x: i, y: 0 });
}
assert!(!rec.is_running());
assert!(rec.is_full());
assert_eq!(rec.len(), 4);
let entries: Vec<_> = rec.iter().collect();
assert_eq!(entries[0].spec, EventSpec::PointerMove { x: 0, y: 0 });
assert_eq!(entries[3].spec, EventSpec::PointerMove { x: 3, y: 0 });
}
#[test]
fn record_while_stopped_is_noop() {
let mut rec = EventRecorder::<16>::new();
rec.record(EventSpec::Tick);
assert_eq!(rec.len(), 0);
}
#[test]
fn start_clears_previous() {
let mut rec = EventRecorder::<16>::new();
rec.start();
rec.record(EventSpec::Tick);
rec.record(EventSpec::Tick);
assert_eq!(rec.len(), 2);
rec.start(); assert_eq!(rec.len(), 0);
assert!(rec.is_running());
}
#[test]
fn tick_delta_saturates() {
let mut rec = EventRecorder::<4>::new();
rec.start();
rec.record(EventSpec::Tick);
for _ in 0..70_000u32 {
rec.tick();
}
rec.record(EventSpec::Tick);
let entries: Vec<_> = rec.iter().collect();
assert_eq!(entries[1].tick_delta, u16::MAX);
}
}