Skip to main content

krabka_verified/
offset_allocator.rs

1//! Pure offset-reservation kernel for diskless WAL sequencers.
2
3use creusot_std::prelude::*;
4
5/// Admit reservations only after the current leader's epoch marker commits.
6///
7/// That marker follows every inherited log entry, so crossing it makes the
8/// applied metadata image an exact durable base for a new reservation.
9#[ensures(result == (high_watermark@ > epoch_start_offset@))]
10#[must_use]
11pub fn wal_reservation_epoch_ready(high_watermark: i64, epoch_start_offset: i64) -> bool {
12    high_watermark > epoch_start_offset
13}
14
15/// Extend an exact pending-reservation chain by one range.
16///
17/// `pending_base` must equal the current frontier. This rejects gaps,
18/// overlaps, zero-sized reservations, negative coordinates, and overflow.
19#[ensures(match result {
20    Some(next) => current@ >= 0
21        && pending_base@ == current@
22        && pending_count@ > 0
23        && next@ == current@ + pending_count@
24        && next@ <= i64::MAX@,
25    None => current@ < 0
26        || pending_base@ != current@
27        || pending_count@ <= 0
28        || current@ + pending_count@ > i64::MAX@,
29})]
30#[must_use]
31pub fn wal_reservation_frontier(
32    current: i64,
33    pending_base: i64,
34    pending_count: i64,
35) -> Option<i64> {
36    if current < 0 || pending_base != current || pending_count <= 0 {
37        return None;
38    }
39    current.checked_add(pending_count)
40}
41
42/// Admit a controller reservation response bound to the exact request and
43/// leader epoch.
44///
45/// `-1` means the caller cannot observe the controller epoch; the response must
46/// still carry a nonnegative epoch committed by the leader.
47#[ensures(match result {
48    Some(admitted_base) => request_matches
49        && response_epoch@ >= 0
50        && (request_epoch@ == -1
51            || (request_epoch@ >= 0
52                && observed_epoch@ == request_epoch@
53                && response_epoch@ == request_epoch@))
54        && base@ >= 0
55        && count@ > 0
56        && admitted_base@ == base@
57        && base@ + count@ <= i64::MAX@,
58    None => !request_matches
59        || response_epoch@ < 0
60        || (request_epoch@ != -1
61            && (request_epoch@ < 0
62                || observed_epoch@ != request_epoch@
63                || response_epoch@ != request_epoch@))
64        || base@ < 0
65        || count@ <= 0
66        || base@ + count@ > i64::MAX@,
67})]
68#[must_use]
69pub fn wal_reservation_response(
70    request_matches: bool,
71    request_epoch: i64,
72    observed_epoch: i64,
73    response_epoch: i64,
74    base: i64,
75    count: i64,
76) -> Option<i64> {
77    if !request_matches || response_epoch < 0 {
78        return None;
79    }
80    if request_epoch != -1
81        && (request_epoch < 0 || observed_epoch != request_epoch || response_epoch != request_epoch)
82    {
83        return None;
84    }
85    if base < 0 || count <= 0 {
86        return None;
87    }
88    base.checked_add(count).map(|_| base)
89}
90
91/// Reserve `count` offsets from `next`, returning `(base, next_after)`.
92///
93/// Admits exactly a nonnegative frontier, a positive count, and a range whose
94/// end fits `i64`; the reservation then starts at `next` and the frontier
95/// moves to `next + count`. Anything else is `None`.
96#[ensures((result != None) == (next@ >= 0 && count@ > 0 && next@ + count@ <= i64::MAX@))]
97#[ensures(forall<base: i64, next_after: i64> result == Some((base, next_after))
98    ==> base@ == next@ && next_after@ == next@ + count@)]
99#[must_use]
100pub const fn reserve_offsets(next: i64, count: i64) -> Option<(i64, i64)> {
101    if next < 0 || count <= 0 {
102        return None;
103    }
104    match next.checked_add(count) {
105        Some(next_after) => Some((next, next_after)),
106        None => None,
107    }
108}
109
110#[cfg(test)]
111mod tests {
112    use {super::*, assert2::assert};
113
114    #[test]
115    fn reserve_offsets_admits_exactly_representable_ranges() {
116        // (next, count, reservation)
117        let cases = [
118            (0, 1, Some((0, 1))),
119            (11, 3, Some((11, 14))),
120            (i64::MAX - 3, 3, Some((i64::MAX - 3, i64::MAX))),
121            (i64::MAX - 1, 3, None),
122            (11, 0, None),
123            (11, -1, None),
124            (-1, 3, None),
125        ];
126        for (next, count, reservation) in cases {
127            assert!(reserve_offsets(next, count) == reservation);
128        }
129    }
130
131    #[test]
132    fn pending_frontier_is_exact_contiguous_and_bounded() {
133        assert!(wal_reservation_frontier(0, 0, 1) == Some(1));
134        assert!(wal_reservation_frontier(10, 10, 3) == Some(13));
135        assert!(wal_reservation_frontier(10, 9, 3).is_none());
136        assert!(wal_reservation_frontier(10, 11, 3).is_none());
137        assert!(wal_reservation_frontier(10, 10, 0).is_none());
138        assert!(wal_reservation_frontier(-1, -1, 1).is_none());
139        assert!(wal_reservation_frontier(i64::MAX, i64::MAX, 1).is_none());
140    }
141
142    #[test]
143    fn leader_epoch_must_be_committed_before_reservation() {
144        assert!(!wal_reservation_epoch_ready(7, 7));
145        assert!(wal_reservation_epoch_ready(8, 7));
146    }
147
148    #[yare::parameterized(
149        first_offset = { true, 0, 0, 0, 0, 1, Some(0) },
150        current_epoch = { true, 7, 7, 7, 11, 3, Some(11) },
151        unknown_epoch_sentinel = { true, -1, -1, 7, 11, 3, Some(11) },
152        not_leader = { false, 7, 7, 7, 11, 3, None },
153        claimed_identity_mismatch = { true, 7, 8, 7, 11, 3, None },
154        local_epoch_mismatch = { true, 7, 7, 6, 11, 3, None },
155        invalid_identity = { true, -2, -2, 7, 11, 3, None },
156        unknown_epoch_with_no_local_epoch = { true, -1, -1, -1, 11, 3, None },
157        negative_next = { true, 7, 7, 7, -1, 3, None },
158        empty_count = { true, 7, 7, 7, 11, 0, None },
159        range_overflow = { true, 7, 7, 7, i64::MAX, 1, None },
160    )]
161    fn response_admission_binds_identity_epoch_and_exact_range(
162        is_leader: bool,
163        authenticated: i64,
164        claimed: i64,
165        local: i64,
166        next: i64,
167        count: i64,
168        expected: Option<i64>,
169    ) {
170        assert2::assert!(
171            wal_reservation_response(is_leader, authenticated, claimed, local, next, count)
172                == expected
173        );
174    }
175}