krabka_verified/broker/
coordinator_partition.rs1use {super::FindCoordinatorAdmission, creusot_std::prelude::*};
2
3#[ensures(key_type@ == 0 ==> result == if acl_allowed {
11 FindCoordinatorAdmission::AllowGroup
12} else {
13 FindCoordinatorAdmission::DenyGroup
14})]
15#[ensures(key_type@ == 1 ==> result == if acl_allowed {
16 FindCoordinatorAdmission::AllowTransaction
17} else {
18 FindCoordinatorAdmission::DenyTransaction
19})]
20#[ensures(key_type@ == 2 ==> result == if api_version@ < 6 || !share_key_valid {
21 FindCoordinatorAdmission::InvalidRequest
22} else if acl_allowed {
23 FindCoordinatorAdmission::AllowShare
24} else {
25 FindCoordinatorAdmission::DenyCluster
26})]
27#[ensures(key_type@ < 0 || key_type@ > 2
28 ==> result == FindCoordinatorAdmission::InvalidRequest)]
29#[must_use]
30pub fn find_coordinator_admission(
31 api_version: i16,
32 key_type: i8,
33 acl_allowed: bool,
34 share_key_valid: bool,
35) -> FindCoordinatorAdmission {
36 match key_type {
37 0 if acl_allowed => FindCoordinatorAdmission::AllowGroup,
38 0 => FindCoordinatorAdmission::DenyGroup,
39 1 if acl_allowed => FindCoordinatorAdmission::AllowTransaction,
40 1 => FindCoordinatorAdmission::DenyTransaction,
41 2 if api_version < 6 || !share_key_valid => FindCoordinatorAdmission::InvalidRequest,
42 2 if acl_allowed => FindCoordinatorAdmission::AllowShare,
43 2 => FindCoordinatorAdmission::DenyCluster,
44 _ => FindCoordinatorAdmission::InvalidRequest,
45 }
46}
47
48#[ensures(result == (selected_partition_epoch@ == current_partition_epoch@
51 && !current_leader_alive
52 && selected_replicas@ == current_replicas@
53 && (exists<i: Int> 0 <= i && i < [email protected]()
54 && current_replicas@[i] == winner)))]
55#[must_use]
56pub fn unclean_recovery_commit_admission(
57 selected_partition_epoch: i32,
58 current_partition_epoch: i32,
59 selected_replicas: &[u64],
60 current_replicas: &[u64],
61 winner: u64,
62 current_leader_alive: bool,
63) -> bool {
64 if selected_partition_epoch != current_partition_epoch
65 || current_leader_alive
66 || selected_replicas.len() != current_replicas.len()
67 {
68 return false;
69 }
70
71 let mut winner_assigned = false;
72 let mut i = 0usize;
73 #[cfg_attr(creusot, invariant(i@ <= [email protected]()))]
74 #[cfg_attr(creusot, invariant([email protected]() == [email protected]()))]
75 #[cfg_attr(creusot, invariant(forall<k: Int> 0 <= k && k < i@
76 ==> selected_replicas@[k] == current_replicas@[k]))]
77 #[cfg_attr(creusot, invariant(winner_assigned == (exists<k: Int>
78 0 <= k && k < i@ && current_replicas@[k] == winner)))]
79 #[cfg_attr(creusot, variant([email protected]() - i@))]
80 while i < selected_replicas.len() {
81 let (Some(&selected), Some(¤t)) = (selected_replicas.get(i), current_replicas.get(i))
82 else {
83 return false;
84 };
85 if selected != current {
86 return false;
87 }
88 if current == winner {
89 winner_assigned = true;
90 }
91 i += 1;
92 }
93 winner_assigned
94}
95
96#[cfg(creusot)]
99#[cfg_attr(test, mutants::skip)]
100#[logic]
101#[requires(0 <= limit && limit <= units.len())]
102#[variant(limit)]
103pub fn java_string_hash_prefix_model(units: Seq<u16>, limit: Int) -> i32 {
104 pearlite! {
105 if limit <= 0 {
106 0i32
107 } else {
108 java_string_hash_prefix_model(units, limit - 1) * 31i32
109 + units[limit - 1] as i32
110 }
111 }
112}
113
114#[cfg(creusot)]
116#[cfg_attr(test, mutants::skip)]
117#[logic]
118fn java_string_abs_model(hash: i32) -> i32 {
119 pearlite! {
120 if hash == i32::MIN {
121 0i32
122 } else if hash < 0i32 {
123 -hash
124 } else {
125 hash
126 }
127 }
128}
129
130#[cfg(creusot)]
132#[cfg_attr(test, mutants::skip)]
133#[logic]
134#[requires(partition_count@ > 0)]
135fn java_string_hash_partition_model(units: Seq<u16>, partition_count: i32) -> Int {
136 pearlite! {
137 java_string_abs_model(java_string_hash_prefix_model(units, units.len()))@
138 % partition_count@
139 }
140}
141
142#[cfg_attr(
149 creusot,
150 ensures(partition_count@ > 0 ==>
151 exists<partition: i32> result == Some(partition)
152 && partition@ == java_string_hash_partition_model(units@, partition_count))
153)]
154#[ensures(result == None ==> partition_count@ <= 0)]
155#[ensures(partition_count@ <= 0 ==> result == None)]
156#[ensures(forall<partition: i32> result == Some(partition) ==>
157 0 <= partition@ && partition@ < partition_count@)]
158#[must_use]
159pub fn java_string_hash_partition(units: &[u16], partition_count: i32) -> Option<i32> {
160 if partition_count <= 0 {
161 return None;
162 }
163
164 let mut hash = 0_i32;
165 let mut index = 0_usize;
166 #[invariant(index@ <= [email protected]())]
167 #[cfg_attr(creusot, invariant(hash == java_string_hash_prefix_model(units@, index@)))]
168 #[variant([email protected]() - index@)]
169 while index < units.len() {
170 let Some(&unit) = units.get(index) else {
171 break;
172 };
173 hash = hash.wrapping_mul(31).wrapping_add(i32::from(unit));
174 index += 1;
175 }
176 let positive = match hash {
177 i32::MIN => 0,
178 negative if negative < 0 => -negative,
179 nonnegative => nonnegative,
180 };
181 Some(positive % partition_count)
182}