automation_structures/compositions/
allocation_snapshot.rs1use vstd::prelude::*;
21
22verus! {
23
24pub open spec fn cost_sum_to(entries: Seq<(u64, u64)>, n: int) -> int
26 decreases n,
27{
28 if n <= 0 || n > entries.len() {
29 0
30 } else {
31 entries[n - 1].1 as int + cost_sum_to(entries, n - 1)
32 }
33}
34
35proof fn cost_sum_push_prefix(entries: Seq<(u64, u64)>, entry: (u64, u64), n: int)
37 requires 0 <= n <= entries.len(),
38 ensures cost_sum_to(entries.push(entry), n) == cost_sum_to(entries, n),
39 decreases n,
40{
41 if n > 0 {
42 cost_sum_push_prefix(entries, entry, n - 1);
43 assert(entries.push(entry)[n - 1] == entries[n - 1]);
44 }
45}
46
47pub proof fn cost_sum_push(entries: Seq<(u64, u64)>, key: u64, cost: u64)
49 ensures
50 cost_sum_to(entries.push((key, cost)), entries.len() as int + 1)
51 == cost_sum_to(entries, entries.len() as int) + cost as int,
52{
53 cost_sum_push_prefix(entries, (key, cost), entries.len() as int);
54 assert(entries.push((key, cost))[entries.len() as int].1 == cost);
55}
56
57pub struct AllocationSnapshot {
60 pub num_nodes: u64,
62 pub registry: crate::primitives::resource_registry::ResourceRegistry<u64, u64>,
64 pub budget: crate::primitives::budget::Budget,
66}
67
68impl AllocationSnapshot {
69 pub open spec fn accepted_subset_nodes(&self) -> bool {
73 forall|i: int|
74 0 <= i < self.registry.entries.len()
75 ==> #[trigger] self.registry.entries@[i].0 < self.num_nodes
76 }
77
78 pub open spec fn accepted_distinct(&self) -> bool {
81 self.registry.unique_mapping()
82 }
83
84 pub open spec fn costs_valid(&self) -> bool {
86 forall|i: int| 0 <= i < self.registry.entries.len()
87 ==> #[trigger] self.registry.entries@[i].1 > 0
88 }
89
90 pub open spec fn type_invariant(&self) -> bool {
93 self.accepted_subset_nodes() && self.accepted_distinct() && self.costs_valid()
94 }
95
96 pub open spec fn budget_consistency(&self) -> bool {
98 &&& self.budget.safety_invariant()
99 &&& self.budget.reserved == 0
100 &&& self.budget.pending_eviction == 0
101 &&& self.budget.allocated as int
102 == cost_sum_to(self.registry.entries@, self.registry.entries.len() as int)
103 }
104
105 pub open spec fn contains(&self, n: u64) -> bool {
107 self.registry.contains_key(n)
108 }
109
110 pub fn new(capacity: u64, num_nodes: u64) -> (s: AllocationSnapshot)
115 ensures
116 s.num_nodes == num_nodes,
117 s.registry.entries@.len() == 0,
118 s.budget.capacity == capacity,
119 s.budget.allocated == 0,
120 s.type_invariant(),
121 s.budget_consistency(),
122 {
123 AllocationSnapshot {
124 num_nodes,
125 registry: crate::primitives::resource_registry::ResourceRegistry::new(),
126 budget: crate::primitives::budget::Budget::new(capacity),
127 }
128 }
129
130 pub fn contains_exec(&self, n: u64) -> (b: bool)
135 requires self.registry.unique_mapping(),
136 ensures
137 b == self.contains(n),
138 {
139 match self.registry.lookup(n) {
140 Some(_) => true,
141 None => false,
142 }
143 }
144
145 pub fn accept_node(&mut self, n: u64, node_cost: u64)
151 requires
152 old(self).type_invariant(),
153 old(self).budget_consistency(),
154 n < old(self).num_nodes, !old(self).contains(n), 1 <= node_cost, old(self).budget.used() + node_cost as int <= old(self).budget.capacity as int,
158 ensures
159 final(self).num_nodes == old(self).num_nodes,
160 final(self).registry.entries@
161 == old(self).registry.entries@.push((n, node_cost)),
162 final(self).budget.capacity == old(self).budget.capacity,
163 final(self).budget.allocated == old(self).budget.allocated + node_cost,
164 final(self).contains(n),
165 final(self).type_invariant(),
166 final(self).budget_consistency(),
167 {
168 let _accepted = self.budget.try_allocate(node_cost);
169 assert(_accepted);
170 let ghost prior_entries = self.registry.entries@;
171 self.registry.register(n, node_cost);
172 proof { cost_sum_push(prior_entries, n, node_cost); }
173 assert(self.contains(n)) by {
177 assert(self.registry.maps_to(n, node_cost));
178 }
179 }
180}
181
182pub fn capture(capacity: u64, num_nodes: u64, nodes: &[u64], costs: &[u64])
190 -> (s: AllocationSnapshot)
191 requires
192 nodes@.len() == costs@.len(),
193 ensures
194 s.budget.capacity == capacity,
195 s.num_nodes == num_nodes,
196 s.type_invariant(),
197 s.budget_consistency(),
198{
199 let mut s = AllocationSnapshot::new(capacity, num_nodes);
200 let n_reqs = nodes.len();
201 let mut i: usize = 0;
202 while i < n_reqs
203 invariant
204 i <= n_reqs,
205 n_reqs == nodes@.len(),
206 nodes@.len() == costs@.len(),
207 s.budget.capacity == capacity,
208 s.num_nodes == num_nodes,
209 s.type_invariant(),
210 s.budget_consistency(),
211 decreases n_reqs - i,
212 {
213 let n = nodes[i];
214 let c = costs[i];
215 let available = s.budget.available();
216 if n < num_nodes && 1 <= c && c <= available && !s.contains_exec(n) {
217 s.accept_node(n, c);
218 }
219 i = i + 1;
220 }
221 s
222}
223
224}