use std::fmt;
use thiserror::Error;
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
pub struct CommandScratchMetrics {
pub commands_recorded: u64,
pub peak_commands: usize,
pub capacity_stalls: u64,
pub host_allocation_events: u64,
}
#[derive(Clone, Copy, Debug, Error, PartialEq, Eq)]
#[error("command scratch capacity {capacity} is exhausted")]
pub struct ScratchFull {
pub capacity: usize,
}
pub struct CommandScratch<C: Copy> {
commands: Vec<C>,
limit: usize,
metrics: CommandScratchMetrics,
}
impl<C: Copy> CommandScratch<C> {
#[must_use]
pub fn new(capacity: usize) -> Self {
Self {
commands: Vec::with_capacity(capacity),
limit: capacity,
metrics: CommandScratchMetrics {
host_allocation_events: u64::from(capacity > 0),
..CommandScratchMetrics::default()
},
}
}
pub fn clear(&mut self) {
self.commands.clear();
}
pub fn push(&mut self, command: C) -> Result<(), ScratchFull> {
if self.commands.len() == self.limit {
self.metrics.capacity_stalls = self.metrics.capacity_stalls.saturating_add(1);
return Err(ScratchFull {
capacity: self.limit,
});
}
self.commands.push(command);
self.metrics.commands_recorded = self.metrics.commands_recorded.saturating_add(1);
self.metrics.peak_commands = self.metrics.peak_commands.max(self.commands.len());
Ok(())
}
#[must_use]
pub fn as_slice(&self) -> &[C] {
&self.commands
}
#[must_use]
pub fn as_mut_slice(&mut self) -> &mut [C] {
&mut self.commands
}
#[must_use]
pub const fn capacity(&self) -> usize {
self.limit
}
#[must_use]
pub const fn metrics(&self) -> CommandScratchMetrics {
self.metrics
}
}
impl<C: Copy + fmt::Debug> fmt::Debug for CommandScratch<C> {
fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
formatter
.debug_struct("CommandScratch")
.field("commands", &self.commands)
.field("limit", &self.limit)
.field("metrics", &self.metrics)
.finish()
}
}
#[cfg(test)]
#[path = "command_tests.rs"]
mod tests;