#[cfg(feature = "alloc")]
use alloc::vec::Vec;
use crate::trace::TrainingEvent;
use crate::training::TrainingOutcome;
pub const MAX_WARNINGS: usize = 5;
#[non_exhaustive]
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum TrainingWarning {
UndefinedLtpRequest {
lane: u8,
value: u8,
count: u32,
in_use: bool,
},
}
#[non_exhaustive]
#[derive(Debug, Clone, PartialEq, Eq)]
#[cfg_attr(not(feature = "alloc"), derive(Copy))]
pub struct Trained {
pub outcome: TrainingOutcome,
#[cfg(feature = "alloc")]
pub warnings: Vec<TrainingWarning>,
#[cfg(not(feature = "alloc"))]
pub warnings: [Option<TrainingWarning>; MAX_WARNINGS],
#[cfg(not(feature = "alloc"))]
pub num_warnings: usize,
}
impl Trained {
pub(crate) fn new(outcome: TrainingOutcome) -> Self {
Self {
outcome,
#[cfg(feature = "alloc")]
warnings: Vec::new(),
#[cfg(not(feature = "alloc"))]
warnings: [None; MAX_WARNINGS],
#[cfg(not(feature = "alloc"))]
num_warnings: 0,
}
}
pub fn iter_warnings(&self) -> impl Iterator<Item = &TrainingWarning> {
#[cfg(feature = "alloc")]
let warnings = self.warnings.iter();
#[cfg(not(feature = "alloc"))]
let warnings = self.warnings[..self.num_warnings].iter().flatten();
warnings
}
fn warnings_mut(&mut self) -> impl Iterator<Item = &mut TrainingWarning> {
#[cfg(feature = "alloc")]
let warnings = self.warnings.iter_mut();
#[cfg(not(feature = "alloc"))]
let warnings = self.warnings[..self.num_warnings].iter_mut().flatten();
warnings
}
fn push(&mut self, warning: TrainingWarning) {
#[cfg(feature = "alloc")]
self.warnings.push(warning);
#[cfg(not(feature = "alloc"))]
if let Some(slot) = self.warnings.get_mut(self.num_warnings) {
*slot = Some(warning);
self.num_warnings += 1;
}
}
pub(crate) fn observe(&mut self, event: &TrainingEvent) {
if let TrainingEvent::UndefinedLtpRequest {
lane,
value,
in_use,
} = *event
{
let earlier = self.warnings_mut().find_map(|warning| match warning {
TrainingWarning::UndefinedLtpRequest {
lane: l,
value,
count,
in_use: u,
} if *l == lane && *u == in_use => Some((value, count)),
_ => None,
});
match earlier {
Some((last, count)) => {
*last = value;
*count = count.saturating_add(1);
}
None => self.push(TrainingWarning::UndefinedLtpRequest {
lane,
value,
count: 1,
in_use,
}),
}
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::training::FallbackReason;
fn undefined(lane: u8, value: u8, in_use: bool) -> TrainingEvent {
TrainingEvent::UndefinedLtpRequest {
lane,
value,
in_use,
}
}
#[test]
fn the_repeat_count_saturates() {
let mut trained = Trained::new(TrainingOutcome::FallbackRequired {
reason: FallbackReason::TrainingTimeout,
});
trained.observe(&undefined(0, 0x9, true));
#[cfg(feature = "alloc")]
let warning = &mut trained.warnings[0];
#[cfg(not(feature = "alloc"))]
let warning = trained.warnings[0].as_mut().unwrap();
let TrainingWarning::UndefinedLtpRequest { count, .. } = warning;
*count = u32::MAX;
trained.observe(&undefined(0, 0x9, true));
assert!(matches!(
trained.iter_warnings().next(),
Some(TrainingWarning::UndefinedLtpRequest {
count: u32::MAX,
..
})
));
}
#[test]
fn repeats_on_a_lane_are_merged_with_the_last_value() {
let mut trained = Trained::new(TrainingOutcome::FallbackRequired {
reason: FallbackReason::TrainingTimeout,
});
trained.observe(&undefined(1, 0x9, true));
trained.observe(&TrainingEvent::ExitedToTmds);
trained.observe(&undefined(2, 0xA, true));
trained.observe(&undefined(1, 0xC, true));
trained.observe(&undefined(3, 0xB, false));
trained.observe(&undefined(3, 0xB, true));
let warnings: [TrainingWarning; 4] = [
TrainingWarning::UndefinedLtpRequest {
lane: 1,
value: 0xC,
count: 2,
in_use: true,
},
TrainingWarning::UndefinedLtpRequest {
lane: 2,
value: 0xA,
count: 1,
in_use: true,
},
TrainingWarning::UndefinedLtpRequest {
lane: 3,
value: 0xB,
count: 1,
in_use: false,
},
TrainingWarning::UndefinedLtpRequest {
lane: 3,
value: 0xB,
count: 1,
in_use: true,
},
];
assert!(trained.iter_warnings().eq(warnings.iter()));
}
#[test]
fn every_distinct_warning_fits() {
let mut trained = Trained::new(TrainingOutcome::FallbackRequired {
reason: FallbackReason::TrainingTimeout,
});
for (lane, in_use) in [(0, true), (1, true), (2, true), (3, true), (3, false)] {
for _ in 0..100 {
trained.observe(&undefined(lane, 0x9, in_use));
}
}
assert_eq!(trained.iter_warnings().count(), 5);
const { assert!(5 <= MAX_WARNINGS) };
}
}