krabka_verified/diskless/
trim_decision.rs1use {
2 super::{DisklessBatchStep, DisklessTrimDecision},
3 creusot_std::prelude::*,
4};
5
6#[ensures(match result {
11 Some(total) => same_object
12 && current_start@ + current_len@ == next_start@
13 && total@ == current_len@ + next_len@
14 && total@ <= max_bytes@,
15 None => !same_object
16 || current_start@ + current_len@ != next_start@
17 || current_len@ + next_len@ > max_bytes@,
18})]
19#[must_use]
20pub fn diskless_span_extension(
21 current_start: u64,
22 current_len: u64,
23 next_start: u64,
24 next_len: u64,
25 same_object: bool,
26 max_bytes: u64,
27) -> Option<u64> {
28 if !same_object || current_start.checked_add(current_len) != Some(next_start) {
29 return None;
30 }
31 let total = current_len.checked_add(next_len)?;
32 (total <= max_bytes).then_some(total)
33}
34
35#[cfg(creusot)]
37#[cfg_attr(test, mutants::skip)]
38#[logic]
39fn batch_step_valid(
40 selected_start: Option<usize>,
41 batch_start: usize,
42 encoded_len: usize,
43 base_offset: i64,
44 last_offset_delta: i32,
45) -> bool {
46 pearlite! {
47 encoded_len@ > 0
48 && last_offset_delta@ >= 0
49 && batch_start@ + encoded_len@ <= usize::MAX@
50 && base_offset@ + last_offset_delta@ <= i64::MAX@
51 && match selected_start {
52 Some(start) => start@ <= batch_start@,
53 None => true,
54 }
55 }
56}
57
58#[ensures((result == DisklessBatchStep::Invalid) == !batch_step_valid(
65 selected_start,
66 batch_start,
67 encoded_len,
68 base_offset,
69 last_offset_delta,
70))]
71#[ensures(match result {
72 DisklessBatchStep::Skip(next) => selected_start == None
73 && next@ == batch_start@ + encoded_len@
74 && base_offset@ + last_offset_delta@ < floor@,
75 DisklessBatchStep::Start(next) => selected_start == None
76 && next@ == batch_start@ + encoded_len@
77 && floor@ <= base_offset@ + last_offset_delta@,
78 DisklessBatchStep::Continue(next) => match selected_start {
79 Some(start) => start@ <= batch_start@
80 && next@ == batch_start@ + encoded_len@
81 && next@ - start@ <= max_bytes@,
82 None => false,
83 },
84 DisklessBatchStep::Stop => match selected_start {
85 Some(start) => start@ <= batch_start@
86 && batch_start@ + encoded_len@ - start@ > max_bytes@,
87 None => false,
88 },
89 DisklessBatchStep::Invalid => true,
91})]
92#[must_use]
93pub fn diskless_batch_step(
94 selected_start: Option<usize>,
95 batch_start: usize,
96 encoded_len: usize,
97 base_offset: i64,
98 last_offset_delta: i32,
99 floor: i64,
100 max_bytes: usize,
101) -> DisklessBatchStep {
102 if encoded_len == 0 || last_offset_delta < 0 {
103 return DisklessBatchStep::Invalid;
104 }
105 let Some(next) = batch_start.checked_add(encoded_len) else {
106 return DisklessBatchStep::Invalid;
107 };
108 let Some(last_offset) = base_offset.checked_add(i64::from(last_offset_delta)) else {
109 return DisklessBatchStep::Invalid;
110 };
111 if let Some(start) = selected_start {
112 if start > batch_start {
113 return DisklessBatchStep::Invalid;
114 }
115 if next - start > max_bytes {
116 DisklessBatchStep::Stop
117 } else {
118 DisklessBatchStep::Continue(next)
119 }
120 } else if last_offset < floor {
121 DisklessBatchStep::Skip(next)
122 } else {
123 DisklessBatchStep::Start(next)
124 }
125}
126
127#[cfg(creusot)]
129#[cfg_attr(test, mutants::skip)]
130#[logic(open)]
131pub fn effective_trim_lag(safety_lag: i64) -> Int {
132 pearlite! { if safety_lag@ < 0 { 0 } else { safety_lag@ } }
133}
134
135#[cfg(creusot)]
137#[cfg_attr(test, mutants::skip)]
138#[logic(open)]
139fn trim_target(frontier: i64, high_watermark: i64, safety_lag: i64) -> Int {
140 pearlite! {
141 let high_watermark_floor = high_watermark@ - effective_trim_lag(safety_lag);
142 if frontier@ < high_watermark_floor { frontier@ } else { high_watermark_floor }
143 }
144}
145
146#[ensures(result.should_trim == (
152 frontier@ >= 0
153 && high_watermark@ >= 0
154 && current_start@ >= 0
155 && effective_trim_lag(safety_lag) <= high_watermark@
156 && current_start@ < trim_target(frontier, high_watermark, safety_lag)
157))]
158#[ensures(result.target@ == if result.should_trim {
159 trim_target(frontier, high_watermark, safety_lag)
160} else {
161 current_start@
162})]
163#[ensures(result.target@ >= current_start@)]
164#[ensures(result.should_trim ==> result.target@ <= frontier@)]
165#[ensures(result.should_trim ==>
166 result.target@ + effective_trim_lag(safety_lag) <= high_watermark@)]
167#[must_use]
168pub fn diskless_trim_decision(
169 frontier: i64,
170 high_watermark: i64,
171 safety_lag: i64,
172 current_start: i64,
173) -> DisklessTrimDecision {
174 if frontier < 0 || high_watermark < 0 || current_start < 0 {
175 return DisklessTrimDecision {
176 should_trim: false,
177 target: current_start,
178 };
179 }
180
181 let safety_lag = safety_lag.max(0);
182 if safety_lag > high_watermark {
183 return DisklessTrimDecision {
184 should_trim: false,
185 target: current_start,
186 };
187 }
188
189 let target = frontier.min(high_watermark - safety_lag);
190 if target <= current_start {
191 DisklessTrimDecision {
192 should_trim: false,
193 target: current_start,
194 }
195 } else {
196 DisklessTrimDecision {
197 should_trim: true,
198 target,
199 }
200 }
201}