#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum FitVerdict {
RunsWell,
TightFit,
TooLarge,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct FitAssessment {
pub verdict: FitVerdict,
pub required_bytes: i64,
}
impl FitVerdict {
pub fn as_str(&self) -> &'static str {
match self {
FitVerdict::RunsWell => "runs_well",
FitVerdict::TightFit => "tight_fit",
FitVerdict::TooLarge => "too_large",
}
}
const MEMORY_OVERHEAD_FACTOR: f64 = 1.25;
const RUNS_WELL_FRACTION: f64 = 0.75;
const TIGHT_FIT_FRACTION: f64 = 0.95;
pub fn assess(footprint_bytes: Option<i64>, total_memory_bytes: u64) -> Option<FitAssessment> {
let footprint_bytes = footprint_bytes.filter(|&bytes| bytes > 0)?;
if total_memory_bytes == 0 {
return None;
}
let required_bytes = (footprint_bytes as f64 * Self::MEMORY_OVERHEAD_FACTOR) as i64;
let share = required_bytes as f64 / total_memory_bytes as f64;
let verdict = if share < Self::RUNS_WELL_FRACTION {
FitVerdict::RunsWell
} else if share < Self::TIGHT_FIT_FRACTION {
FitVerdict::TightFit
} else {
FitVerdict::TooLarge
};
Some(FitAssessment {
verdict,
required_bytes,
})
}
}
#[cfg(test)]
mod tests {
use super::*;
const GIB: i64 = 1 << 30;
const MEMORY: u64 = 16 * (1 << 30);
#[test]
fn an_unknown_or_empty_footprint_is_unassessable() {
assert!(FitVerdict::assess(None, MEMORY).is_none());
assert!(FitVerdict::assess(Some(0), MEMORY).is_none());
assert!(FitVerdict::assess(Some(-5), MEMORY).is_none());
assert!(FitVerdict::assess(Some(GIB), 0).is_none());
}
#[test]
fn the_verdict_tracks_the_memory_share() {
let assessment = FitVerdict::assess(Some(GIB), MEMORY).unwrap();
assert_eq!(assessment.verdict, FitVerdict::RunsWell);
let tight = FitVerdict::assess(Some(12 * GIB), MEMORY).unwrap();
assert_eq!(tight.verdict, FitVerdict::TightFit);
let too_large = FitVerdict::assess(Some(16 * GIB), MEMORY).unwrap();
assert_eq!(too_large.verdict, FitVerdict::TooLarge);
}
#[test]
fn each_verdict_has_a_stable_slug() {
assert_eq!(FitVerdict::RunsWell.as_str(), "runs_well");
assert_eq!(FitVerdict::TightFit.as_str(), "tight_fit");
assert_eq!(FitVerdict::TooLarge.as_str(), "too_large");
}
#[test]
fn required_bytes_includes_the_overhead_factor() {
let assessment = FitVerdict::assess(Some(GIB), 64 * (1 << 30)).unwrap();
assert_eq!(assessment.required_bytes, 1280 * (1 << 20));
}
}