1#[derive(Debug, Clone, Copy, PartialEq, Eq)]
14pub struct QueueStats {
15 pub depth: u64,
16 pub capacity: u64,
17}
18
19#[derive(Debug, Clone, Copy, PartialEq, Eq)]
22pub struct ItemMeta {
23 pub cost: u64,
24}
25
26#[derive(Debug, Clone, Copy, PartialEq, Eq)]
27pub enum ShedReason {
28 CapacityExhausted,
29}
30
31#[derive(Debug, Clone, Copy, PartialEq, Eq)]
34pub enum Admission {
35 Admit,
36 Shed { reason: ShedReason },
37}
38
39pub fn admit(stats: &QueueStats, item: &ItemMeta) -> Admission {
44 match stats.depth.checked_add(item.cost) {
45 Some(total) if total <= stats.capacity => Admission::Admit,
46 _ => Admission::Shed {
47 reason: ShedReason::CapacityExhausted,
48 },
49 }
50}
51
52#[cfg(kani)]
53mod verification {
54 use super::*;
60
61 #[kani::proof]
62 fn admit_is_total_and_monotone_in_depth() {
63 let capacity: u64 = kani::any();
64 let deep: u64 = kani::any();
65 let shallow: u64 = kani::any();
66 kani::assume(shallow <= deep);
67 let item = ItemMeta { cost: kani::any() };
68 let deep_verdict = admit(
69 &QueueStats {
70 depth: deep,
71 capacity,
72 },
73 &item,
74 );
75 let shallow_verdict = admit(
76 &QueueStats {
77 depth: shallow,
78 capacity,
79 },
80 &item,
81 );
82 if deep_verdict == Admission::Admit {
83 assert_eq!(shallow_verdict, Admission::Admit);
84 }
85 }
86
87 #[kani::proof]
92 fn an_overflowing_sum_never_admits() {
93 let stats = QueueStats {
94 depth: kani::any(),
95 capacity: kani::any(),
96 };
97 let item = ItemMeta { cost: kani::any() };
98 kani::assume(stats.depth.checked_add(item.cost).is_none());
99 assert_eq!(
100 admit(&stats, &item),
101 Admission::Shed {
102 reason: ShedReason::CapacityExhausted
103 }
104 );
105 }
106}
107
108#[cfg(test)]
109#[allow(
110 clippy::unwrap_used,
111 clippy::expect_used,
112 clippy::panic,
113 clippy::indexing_slicing,
114 reason = "test code"
115)]
116mod tests {
117 use super::*;
118 use proptest::prelude::*;
119
120 #[test]
121 fn admits_up_to_capacity_and_sheds_past_it() {
122 let stats = QueueStats {
123 depth: 9,
124 capacity: 10,
125 };
126 assert_eq!(admit(&stats, &ItemMeta { cost: 1 }), Admission::Admit);
127 assert_eq!(
128 admit(&stats, &ItemMeta { cost: 2 }),
129 Admission::Shed {
130 reason: ShedReason::CapacityExhausted
131 }
132 );
133 }
134
135 #[test]
136 fn overflow_sheds_instead_of_wrapping() {
137 let stats = QueueStats {
138 depth: u64::MAX,
139 capacity: u64::MAX,
140 };
141 assert_eq!(
142 admit(&stats, &ItemMeta { cost: 1 }),
143 Admission::Shed {
144 reason: ShedReason::CapacityExhausted
145 }
146 );
147 assert_eq!(admit(&stats, &ItemMeta { cost: 0 }), Admission::Admit);
149 }
150
151 proptest! {
152 #[test]
154 fn shallower_is_never_harder(capacity in any::<u64>(), deep in any::<u64>(),
155 delta in any::<u64>(), cost in any::<u64>()) {
156 let shallow = deep.saturating_sub(delta);
157 let item = ItemMeta { cost };
158 let deep_verdict = admit(&QueueStats { depth: deep, capacity }, &item);
159 let shallow_verdict = admit(&QueueStats { depth: shallow, capacity }, &item);
160 if deep_verdict == Admission::Admit {
161 prop_assert_eq!(shallow_verdict, Admission::Admit);
162 }
163 }
164
165 #[test]
167 fn an_overflowing_sum_sheds(capacity in any::<u64>(), depth in any::<u64>(),
168 cost in any::<u64>()) {
169 if depth.checked_add(cost).is_none() {
170 prop_assert_eq!(
171 admit(&QueueStats { depth, capacity }, &ItemMeta { cost }),
172 Admission::Shed { reason: ShedReason::CapacityExhausted }
173 );
174 }
175 }
176 }
177}