memory_accounting/
verifier.rs1use bytesize::ByteSize;
2use snafu::Snafu;
3
4use crate::{ComponentBounds, MemoryGrant};
5
6#[derive(Debug, Eq, PartialEq, Snafu)]
8pub enum VerifierError {
9 #[snafu(display("invalid component bounds for {}: {}", component_name, reason))]
11 InvalidComponentBounds {
12 component_name: String,
14
15 reason: String,
17 },
18
19 #[snafu(display(
21 "minimum require memory ({}) exceeds available memory ({})",
22 ByteSize::b(*minimum_required_bytes as u64).display().si(),
23 ByteSize::b(*available_bytes as u64).display().si(),
24 ))]
25 InsufficientMinimumMemory {
26 available_bytes: usize,
28
29 minimum_required_bytes: usize,
31 },
32
33 #[snafu(display(
35 "firm limit ({}) exceeds available memory ({})",
36 ByteSize::b(*firm_limit_bytes as u64).display().si(),
37 ByteSize::b(*available_bytes as u64).display().si(),
38 ))]
39 FirmLimitExceedsAvailable {
40 available_bytes: usize,
42
43 firm_limit_bytes: usize,
45 },
46}
47
48pub struct VerifiedBounds {
54 grant: MemoryGrant,
55 component_bounds: ComponentBounds,
56}
57
58impl VerifiedBounds {
59 pub fn total_available_bytes(&self) -> usize {
61 self.grant.effective_limit_bytes()
62 }
63
64 pub fn total_minimum_required_bytes(&self) -> usize {
66 self.component_bounds.total_minimum_required_bytes()
67 }
68
69 pub fn total_firm_limit_bytes(&self) -> usize {
71 self.component_bounds.total_firm_limit_bytes()
72 }
73
74 pub fn bounds(&self) -> &ComponentBounds {
76 &self.component_bounds
77 }
78}
79
80pub struct BoundsVerifier {
82 grant: MemoryGrant,
83 component_bounds: ComponentBounds,
84}
85
86impl BoundsVerifier {
87 pub fn new(grant: MemoryGrant, component_bounds: ComponentBounds) -> Self {
89 Self {
90 grant,
91 component_bounds,
92 }
93 }
94
95 pub fn verify(self) -> Result<VerifiedBounds, VerifierError> {
107 let available_bytes = self.grant.effective_limit_bytes();
109 let total_minimum_required_bytes = self.component_bounds.total_minimum_required_bytes();
110 let total_firm_limit_bytes = self.component_bounds.total_firm_limit_bytes();
111
112 if available_bytes < total_minimum_required_bytes {
113 return Err(VerifierError::InsufficientMinimumMemory {
114 available_bytes,
115 minimum_required_bytes: total_minimum_required_bytes,
116 });
117 }
118
119 if available_bytes < total_firm_limit_bytes {
120 return Err(VerifierError::FirmLimitExceedsAvailable {
121 available_bytes,
122 firm_limit_bytes: total_firm_limit_bytes,
123 });
124 }
125
126 Ok(VerifiedBounds {
127 grant: self.grant,
128 component_bounds: self.component_bounds,
129 })
130 }
131}
132
133#[cfg(test)]
134mod tests {
135 use super::{BoundsVerifier, VerifiedBounds, VerifierError};
136 use crate::{
137 test_util::{get_component_bounds, BoundedComponent},
138 MemoryGrant,
139 };
140
141 fn get_grant(initial_limit_bytes: usize) -> MemoryGrant {
142 const SLOP_FACTOR: f64 = 0.25;
143
144 MemoryGrant::with_slop_factor(initial_limit_bytes, SLOP_FACTOR).expect("should never be invalid")
145 }
146
147 fn verify_component(
148 initial_limit_bytes: usize, component: &BoundedComponent,
149 ) -> (MemoryGrant, Result<VerifiedBounds, VerifierError>) {
150 let initial_grant = get_grant(initial_limit_bytes);
151 let bounds = get_component_bounds(component);
152
153 let verifier = BoundsVerifier::new(initial_grant, bounds);
154 (initial_grant, verifier.verify())
155 }
156
157 #[test]
158 fn verify() {
159 let minimum_required_bytes = 10;
160 let firm_limit_bytes = 20;
161
162 let bounded = BoundedComponent::new(Some(minimum_required_bytes), firm_limit_bytes);
164
165 let (grant, result) = verify_component(1, &bounded);
168 assert_eq!(
169 result.err(),
170 Some(VerifierError::InsufficientMinimumMemory {
171 available_bytes: grant.effective_limit_bytes(),
172 minimum_required_bytes,
173 })
174 );
175
176 let (grant, result) = verify_component(10, &bounded);
177 assert_eq!(
178 result.err(),
179 Some(VerifierError::InsufficientMinimumMemory {
180 available_bytes: grant.effective_limit_bytes(),
181 minimum_required_bytes,
182 })
183 );
184
185 let (grant, result) = verify_component(14, &bounded);
188 assert_eq!(
189 result.err(),
190 Some(VerifierError::FirmLimitExceedsAvailable {
191 available_bytes: grant.effective_limit_bytes(),
192 firm_limit_bytes: minimum_required_bytes + firm_limit_bytes,
193 })
194 );
195
196 let (grant, result) = verify_component(30, &bounded);
197 assert_eq!(
198 result.err(),
199 Some(VerifierError::FirmLimitExceedsAvailable {
200 available_bytes: grant.effective_limit_bytes(),
201 firm_limit_bytes: minimum_required_bytes + firm_limit_bytes,
202 })
203 );
204
205 let (_, result) = verify_component(40, &bounded);
207 assert!(result.is_ok());
208 }
209}