1#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
6pub enum FitVerdict {
7 RunsWell,
9 TightFit,
11 TooLarge,
13}
14
15#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
17pub struct FitAssessment {
18 pub verdict: FitVerdict,
20 pub required_bytes: i64,
22}
23
24impl FitVerdict {
25 pub fn as_str(&self) -> &'static str {
27 match self {
28 FitVerdict::RunsWell => "runs_well",
29 FitVerdict::TightFit => "tight_fit",
30 FitVerdict::TooLarge => "too_large",
31 }
32 }
33
34 const MEMORY_OVERHEAD_FACTOR: f64 = 1.25;
36 const RUNS_WELL_FRACTION: f64 = 0.75;
38 const TIGHT_FIT_FRACTION: f64 = 0.95;
40
41 pub fn assess(footprint_mb: Option<i64>, total_memory_bytes: u64) -> Option<FitAssessment> {
44 let footprint_mb = footprint_mb.filter(|&mb| mb > 0)?;
45 if total_memory_bytes == 0 {
46 return None;
47 }
48 let required_bytes =
49 (footprint_mb as f64 * (1i64 << 20) as f64 * Self::MEMORY_OVERHEAD_FACTOR) as i64;
50 let share = required_bytes as f64 / total_memory_bytes as f64;
51 let verdict = if share < Self::RUNS_WELL_FRACTION {
52 FitVerdict::RunsWell
53 } else if share < Self::TIGHT_FIT_FRACTION {
54 FitVerdict::TightFit
55 } else {
56 FitVerdict::TooLarge
57 };
58 Some(FitAssessment {
59 verdict,
60 required_bytes,
61 })
62 }
63}
64
65#[cfg(test)]
66mod tests {
67 use super::*;
68
69 const GIB: u64 = 1 << 30;
70
71 #[test]
72 fn an_unknown_or_empty_footprint_is_unassessable() {
73 assert!(FitVerdict::assess(None, 16 * GIB).is_none());
74 assert!(FitVerdict::assess(Some(0), 16 * GIB).is_none());
75 assert!(FitVerdict::assess(Some(-5), 16 * GIB).is_none());
76 assert!(FitVerdict::assess(Some(1000), 0).is_none());
77 }
78
79 #[test]
80 fn the_verdict_tracks_the_memory_share() {
81 let assessment = FitVerdict::assess(Some(1024), 16 * GIB).unwrap();
83 assert_eq!(assessment.verdict, FitVerdict::RunsWell);
84
85 let tight = FitVerdict::assess(Some(12 * 1024), 16 * GIB).unwrap();
87 assert_eq!(tight.verdict, FitVerdict::TightFit);
88
89 let too_large = FitVerdict::assess(Some(16 * 1024), 16 * GIB).unwrap();
91 assert_eq!(too_large.verdict, FitVerdict::TooLarge);
92 }
93
94 #[test]
95 fn each_verdict_has_a_stable_slug() {
96 assert_eq!(FitVerdict::RunsWell.as_str(), "runs_well");
97 assert_eq!(FitVerdict::TightFit.as_str(), "tight_fit");
98 assert_eq!(FitVerdict::TooLarge.as_str(), "too_large");
99 }
100
101 #[test]
102 fn required_bytes_includes_the_overhead_factor() {
103 let assessment = FitVerdict::assess(Some(1024), 64 * GIB).unwrap();
104 assert_eq!(assessment.required_bytes, 1280 * (1 << 20));
106 }
107}