1use std::collections::HashMap;
22
23use serde::Serialize;
24
25mod api;
26pub use self::api::ResourceAPIHandler;
27
28mod grant;
29pub use self::grant::MemoryGrant;
30
31mod limiter;
32pub use self::limiter::MemoryLimiter;
33
34mod registry;
35pub use self::registry::{ComponentRegistry, ComponentRegistryHandle, MemoryBoundsBuilder};
36
37mod verifier;
38pub use self::verifier::{BoundsVerifier, VerifiedBounds, VerifierError};
39
40#[cfg(test)]
41pub(crate) mod test_util;
42
43pub trait MemoryBounds {
52 fn specify_bounds(&self, builder: &mut MemoryBoundsBuilder);
54}
55
56impl<T> MemoryBounds for &T
57where
58 T: MemoryBounds,
59{
60 fn specify_bounds(&self, builder: &mut MemoryBoundsBuilder) {
61 T::specify_bounds(self, builder);
62 }
63}
64
65impl<T> MemoryBounds for Box<T>
66where
67 T: MemoryBounds + ?Sized,
68{
69 fn specify_bounds(&self, builder: &mut MemoryBoundsBuilder) {
70 T::specify_bounds(self, builder);
71 }
72}
73
74#[derive(Clone, Debug, Serialize)]
76#[serde(tag = "type")]
77pub enum UsageExpr {
78 Config {
80 name: String,
82 value: usize,
84 },
85
86 StructSize {
88 name: String,
90 value: usize,
92 },
93
94 Constant {
96 name: String,
98 value: usize,
100 },
101
102 Product {
104 values: Vec<UsageExpr>,
106 },
107
108 Sum {
110 values: Vec<UsageExpr>,
112 },
113}
114
115impl UsageExpr {
116 pub fn config(s: impl Into<String>, value: usize) -> Self {
118 Self::Config { name: s.into(), value }
119 }
120
121 pub fn constant(s: impl Into<String>, value: usize) -> Self {
123 Self::Constant { name: s.into(), value }
124 }
125
126 pub fn struct_size<T>(s: impl Into<String>) -> Self {
128 Self::StructSize {
129 name: s.into(),
130 value: std::mem::size_of::<T>(),
131 }
132 }
133
134 pub fn product(_s: impl Into<String>, lhs: UsageExpr, rhs: UsageExpr) -> Self {
136 Self::Product { values: vec![lhs, rhs] }
137 }
138
139 pub fn sum(_s: impl Into<String>, lhs: UsageExpr, rhs: UsageExpr) -> Self {
141 Self::Sum { values: vec![lhs, rhs] }
142 }
143
144 fn evaluate(&self) -> usize {
145 match self {
146 Self::Config { value, .. } | Self::StructSize { value, .. } | Self::Constant { value, .. } => *value,
147 Self::Product { values } => values.iter().map(UsageExpr::evaluate).product(),
148 Self::Sum { values } => values.iter().map(UsageExpr::evaluate).sum(),
149 }
150 }
151}
152
153#[derive(Clone, Debug, Default)]
155pub struct ComponentBounds {
156 self_minimum_required_bytes: Vec<UsageExpr>,
157 self_firm_limit_bytes: Vec<UsageExpr>,
158 subcomponents: HashMap<String, ComponentBounds>,
159}
160
161impl ComponentBounds {
162 pub fn total_minimum_required_bytes(&self) -> usize {
164 self.self_minimum_required_bytes
165 .iter()
166 .map(UsageExpr::evaluate)
167 .sum::<usize>()
168 + self
169 .subcomponents
170 .values()
171 .map(|cb| cb.total_minimum_required_bytes())
172 .sum::<usize>()
173 }
174
175 pub fn total_firm_limit_bytes(&self) -> usize {
179 self.self_minimum_required_bytes
180 .iter()
181 .map(UsageExpr::evaluate)
182 .sum::<usize>()
183 + self
184 .self_firm_limit_bytes
185 .iter()
186 .map(UsageExpr::evaluate)
187 .sum::<usize>()
188 + self
189 .subcomponents
190 .values()
191 .map(|cb| cb.total_firm_limit_bytes())
192 .sum::<usize>()
193 }
194
195 pub fn subcomponents(&self) -> impl IntoIterator<Item = (&String, &ComponentBounds)> {
199 self.subcomponents.iter()
200 }
201
202 pub fn to_exprs(&self) -> Vec<serde_json::Value> {
204 let path = vec!["root".to_string()];
205 let mut stack = vec![(path, self)];
206 let mut output = Vec::new();
207
208 while let Some((path, cb)) = stack.pop() {
209 for expr in &cb.self_minimum_required_bytes {
210 output.push(serde_json::json!({
211 "name": format!("{}.min", path.join(".")),
212 "expr": expr,
213 }));
214 }
215 for expr in &cb.self_firm_limit_bytes {
216 output.push(serde_json::json!({
217 "name": format!("{}.firm", path.join(".")),
218 "expr": expr,
219 }));
220 }
221
222 for (name, subcomponent) in cb.subcomponents() {
223 let mut path = path.clone();
224 path.push(name.clone());
225 stack.push((path, subcomponent));
226 }
227 }
228
229 output
230 }
231}
232
233#[cfg(test)]
234mod tests {
235 use super::UsageExpr;
236
237 #[test]
238 fn leaf_expressions_evaluate_to_their_value() {
239 assert_eq!(UsageExpr::config("cfg", 7).evaluate(), 7);
240 assert_eq!(UsageExpr::constant("const", 11).evaluate(), 11);
241 assert_eq!(
242 UsageExpr::struct_size::<u64>("u64").evaluate(),
243 std::mem::size_of::<u64>()
244 );
245 }
246
247 #[test]
248 fn product_evaluates_to_the_product_of_its_subexpressions() {
249 let expr = UsageExpr::product(
250 "area",
251 UsageExpr::constant("width", 4),
252 UsageExpr::constant("height", 8),
253 );
254 assert_eq!(expr.evaluate(), 32);
255 }
256
257 #[test]
258 fn sum_evaluates_to_the_sum_of_its_subexpressions() {
259 let expr = UsageExpr::sum("total", UsageExpr::constant("a", 4), UsageExpr::constant("b", 8));
260 assert_eq!(expr.evaluate(), 12);
261 }
262
263 #[test]
264 fn products_and_sums_compose_recursively() {
265 let expr = UsageExpr::product(
267 "scaled",
268 UsageExpr::sum("base", UsageExpr::constant("a", 2), UsageExpr::constant("b", 3)),
269 UsageExpr::constant("factor", 4),
270 );
271 assert_eq!(expr.evaluate(), 20);
272 }
273}