1#[cfg(feature = "alloc")]
3use alloc::{vec, vec::Vec};
4use vstd::arithmetic::{div_mod::*, power::*, power2::*};
5use vstd::bits::*;
6#[cfg(verus_only)]
7use vstd::std_specs::iter::IteratorSpec;
8use vstd::{calc, prelude::*};
9
10verus! {
11
12const USIZE_MODULUS_32: u64 = 0x100000000;
13
14const USIZE_MODULUS_64: u128 = 0x10000000000000000u128;
15
16pub open spec fn nat_from_be_bytes(bytes: Seq<u8>) -> nat
18 decreases bytes.len(),
19{
20 if bytes.len() == 0 {
21 0
22 } else {
23 nat_from_be_bytes(bytes.drop_last()) * 256 + bytes.last() as nat
24 }
25}
26
27pub open spec fn nat_to_be_bytes(n: nat) -> Seq<u8>
29 decreases n,
30{
31 if n < 256 {
32 seq![n as u8]
33 } else {
34 nat_to_be_bytes((n / 256) as nat).push((n % 256) as u8)
35 }
36}
37
38pub open spec fn size_of_usize() -> nat {
40 if usize::BITS == 32 {
41 4
42 } else {
43 8
44 }
45}
46
47proof fn lemma_usize_shr8_is_div256(v: usize)
48 ensures
49 (v >> 8usize) as nat == v as nat / 256,
50{
51 lemma_usize_shr_is_div(v, 8usize);
52 assert(pow2(8) == 256) by (compute_only);
53}
54
55proof fn lemma_usize_low8_is_mod256(v: usize)
56 ensures
57 (v & 0xffusize) as nat == v as nat % 256,
58{
59 lemma_usize_low_bits_mask_is_mod(v, 8);
60 assert(pow2(8) == 256) by (compute_only);
61}
62
63proof fn lemma_nat_from_be_bytes_fits_shr8(bytes: Seq<u8>)
64 requires
65 bytes.len() <= size_of_usize(),
66 ensures
67 usize::BITS == 32 ==> nat_from_be_bytes(bytes) < USIZE_MODULUS_32 as nat,
68 usize::BITS == 64 ==> nat_from_be_bytes(bytes) < USIZE_MODULUS_64 as nat,
69{
70 lemma_from_be_bytes_upper_bound(bytes);
71 assert(usize::BITS == 32 || usize::BITS == 64);
72 if usize::BITS == 32 {
73 assert(size_of_usize() == 4);
74 reveal_with_fuel(pow, 5);
75 } else {
76 assert(usize::BITS == 64);
77 assert(size_of_usize() == 8);
78 reveal_with_fuel(pow, 9);
79 }
80}
81
82proof fn lemma_usize32_shl8_or_is_base256(v: usize, b: u8)
83 by (bit_vector)
84 requires
85 usize::BITS == 32,
86 ensures
87 (((v << 8usize) | b as usize) as nat) == (v as nat * 256 + b as nat) % (
88 USIZE_MODULUS_32 as nat),
89{
90}
91
92proof fn lemma_usize64_shl8_or_is_base256(v: usize, b: u8)
93 by (bit_vector)
94 requires
95 usize::BITS == 64,
96 ensures
97 (((v << 8usize) | b as usize) as nat) == (v as nat * 256 + b as nat) % (
98 USIZE_MODULUS_64 as nat),
99{
100}
101
102pub proof fn lemma_nat_from_be_bytes_fits_usize(bytes: Seq<u8>)
103 requires
104 bytes.len() <= size_of_usize(),
105 ensures
106 nat_from_be_bytes(bytes) <= usize::MAX,
107{
108 lemma_from_be_bytes_upper_bound(bytes);
112 if usize::BITS == 32 {
113 reveal_with_fuel(pow, 5); } else {
115 reveal_with_fuel(pow, 9); }
117}
118
119pub proof fn lemma_from_be_bytes_push(bytes: Seq<u8>, b: u8)
120 ensures
121 nat_from_be_bytes(bytes.push(b)) == nat_from_be_bytes(bytes) * 256 + b as nat,
122{
123 assert(bytes.push(b).drop_last() == bytes);
124}
125
126pub proof fn lemma_from_be_bytes_singleton(b: u8)
127 ensures
128 nat_from_be_bytes(seq![b]) == b as nat,
129{
130 lemma_from_be_bytes_push(seq![], b);
131}
132
133pub proof fn lemma_pow256_succ(exp: nat)
134 ensures
135 pow(256, exp + 1) == pow(256, exp) * 256,
136{
137 lemma_pow_adds(256, exp, 1);
138 lemma_pow1(256);
139}
140
141pub broadcast proof fn lemma_from_be_bytes_upper_bound(bytes: Seq<u8>)
142 ensures
143 #[trigger] nat_from_be_bytes(bytes) < pow(256, bytes.len()),
144 decreases bytes.len(),
145{
146 if bytes.len() == 0 {
147 lemma_pow0(256);
148 } else {
149 let prefix = bytes.drop_last();
150 lemma_from_be_bytes_upper_bound(prefix);
151 lemma_pow256_succ(prefix.len());
152 }
153}
154
155pub proof fn lemma_from_be_bytes_lower_bound(bytes: Seq<u8>)
156 requires
157 bytes.len() > 0,
158 bytes[0] != 0,
159 ensures
160 pow(256, (bytes.len() - 1) as nat) <= nat_from_be_bytes(bytes),
161 decreases bytes.len(),
162{
163 if bytes.len() == 1 {
164 lemma_pow0(256);
165 } else {
166 let prefix = bytes.drop_last();
167 lemma_from_be_bytes_lower_bound(prefix);
168 lemma_pow256_succ((prefix.len() - 1) as nat);
169 }
170}
171
172pub proof fn lemma_to_be_bytes_props(n: nat)
173 ensures
174 nat_to_be_bytes(n).len() > 0,
175 n > 0 ==> nat_to_be_bytes(n)[0] != 0,
176 n > 0 ==> pow(256, (nat_to_be_bytes(n).len() - 1) as nat) <= n,
177 decreases n,
178{
179 if n < 256 {
180 lemma_pow0(256);
181 } else {
182 let q = (n / 256) as nat;
183 lemma_to_be_bytes_props(q);
184 lemma_pow256_succ((nat_to_be_bytes(q).len() - 1) as nat);
185 }
186}
187
188pub proof fn lemma_to_be_bytes_len_bound(n: nat, max_len: nat)
189 requires
190 0 < max_len,
191 n < pow(256, max_len),
192 ensures
193 nat_to_be_bytes(n).len() <= max_len,
194{
195 if n == 0 {
196 } else {
197 lemma_to_be_bytes_props(n);
198 lemma_pow_strictly_increases_converse(256, (nat_to_be_bytes(n).len() - 1) as nat, max_len);
199 }
200}
201
202pub proof fn lemma_usize_to_be_bytes_len_bound(n: usize)
203 ensures
204 usize::BITS == 32 ==> nat_to_be_bytes(n as nat).len() <= 4,
205 usize::BITS == 64 ==> nat_to_be_bytes(n as nat).len() <= 8,
206{
207 if usize::BITS == 32 {
208 reveal_with_fuel(pow, 5);
209 lemma_to_be_bytes_len_bound(n as nat, 4);
210 } else {
211 reveal_with_fuel(pow, 9);
212 lemma_to_be_bytes_len_bound(n as nat, 8);
213 }
214}
215
216pub proof fn lemma_to_from_be_bytes_roundtrip(n: nat)
217 ensures
218 nat_from_be_bytes(nat_to_be_bytes(n)) == n,
219 decreases n,
220{
221 if n < 256 {
222 lemma_from_be_bytes_singleton(n as u8);
223 } else {
224 let q = (n / 256) as nat;
225 let r = (n % 256) as nat;
226 lemma_to_from_be_bytes_roundtrip(q);
227 lemma_from_be_bytes_push(nat_to_be_bytes(q), r as u8);
228 }
229}
230
231pub proof fn lemma_from_to_be_bytes_roundtrip(bytes: Seq<u8>)
232 requires
233 bytes.len() > 0,
234 bytes.len() > 1 ==> bytes[0] != 0,
235 ensures
236 nat_to_be_bytes(nat_from_be_bytes(bytes)) == bytes,
237 decreases bytes.len(),
238{
239 if bytes.len() == 1 {
240 lemma_from_be_bytes_singleton(bytes[0]);
241 assert(bytes == seq![bytes[0]]);
242 } else {
243 let prefix = bytes.drop_last();
244 lemma_from_to_be_bytes_roundtrip(prefix);
245 }
246}
247
248pub proof fn lemma_from_be_bytes_prepend(bytes: Seq<u8>, b: u8)
249 ensures
250 nat_from_be_bytes(seq![b] + bytes) == b as nat * pow(256, bytes.len()) + nat_from_be_bytes(
251 bytes,
252 ),
253 decreases bytes.len(),
254{
255 if bytes.len() == 0 {
256 lemma_from_be_bytes_singleton(b);
257 lemma_pow0(256);
258 } else {
259 let prefix = bytes.drop_last();
260 let last = bytes.last();
261 lemma_from_be_bytes_prepend(prefix, b);
262 lemma_from_be_bytes_push(prefix, last);
263 lemma_from_be_bytes_push(seq![b] + prefix, last);
264 lemma_pow256_succ(prefix.len());
265 assert(seq![b] + bytes == (seq![b] + prefix).push(last));
266 assert((b as nat * pow(256, prefix.len()) + nat_from_be_bytes(prefix)) * 256 + last as nat
267 == b as nat * (pow(256, prefix.len()) * 256) + (nat_from_be_bytes(prefix) * 256
268 + last as nat)) by (nonlinear_arith);
269 }
270}
271
272#[verifier::loop_isolation(false)]
275pub fn usize_from_be_bytes_exec(bytes: &[u8]) -> (result: usize)
276 requires
277 bytes.len() <= size_of_usize(),
278 ensures
279 result as nat == nat_from_be_bytes(bytes.deep_view()),
280{
281 let mut acc: usize = 0;
282 for &b in iter: bytes.iter()
283 invariant
284 acc == nat_from_be_bytes(bytes@[0..iter.index()]),
285 {
286 proof {
287 let prefix = bytes@[0..iter.index() + 1];
288 assert(prefix.drop_last() == bytes@[0..iter.index()]);
289 lemma_nat_from_be_bytes_fits_shr8(prefix);
290 if usize::BITS == 32 {
291 lemma_usize32_shl8_or_is_base256(acc, b);
292 } else {
293 lemma_usize64_shl8_or_is_base256(acc, b);
294 }
295 }
296 acc = (acc << 8usize) | (b as usize);
297 }
298 assert(bytes@[0..bytes@.len()] == bytes.deep_view());
299 acc
300}
301
302pub fn u64_from_be_bytes(bytes: &[u8]) -> (r: u64)
303 requires
304 usize::BITS == 64,
305 bytes.len() <= 8,
306 ensures
307 r as nat == nat_from_be_bytes(bytes.deep_view()),
308{
309 usize_from_be_bytes_exec(bytes) as u64
310}
311
312#[cfg(feature = "alloc")]
318pub fn usize_to_be_bytes_exec(v: usize) -> (buf: Vec<u8>)
319 ensures
320 buf@ == nat_to_be_bytes(v as nat),
321 decreases v,
322{
323 if v < 256 {
324 vec![v as u8]
325 } else {
326 proof {
327 lemma_usize_shr8_is_div256(v);
328 lemma_usize_low8_is_mod256(v);
329 }
330 let mut buf = usize_to_be_bytes_exec(v >> 8);
331 buf.push((v & 0xff) as u8);
332 buf
333 }
334}
335
336#[verifier::loop_isolation(false)]
338pub fn usize_to_be_bytes_in_place(v: usize, obuf: &mut [u8])
339 requires
340 old(obuf)@.len() == nat_to_be_bytes(v as nat).len(),
341 ensures
342 final(obuf)@ == nat_to_be_bytes(v as nat),
343{
344 let ghost len = obuf@.len();
345
346 let ghost target = nat_to_be_bytes(v as nat);
347 proof {
348 lemma_to_from_be_bytes_roundtrip(v as nat); assert(target[0..len] == target);
350 }
351 let mut current = v;
352
353 let bytes = obuf.iter_mut();
357 let ghost slots = bytes.remaining();
358 let reversed = bytes.rev();
359 proof {
360 vstd::std_specs::iter::rev_postcondition(bytes, reversed);
361 }
362 for byte in iter: reversed
363 invariant
364 iter.seq().len() == len,
365 current as nat == nat_from_be_bytes(target[0..len - iter.index()]),
366 forall|j: int|
367 #![auto]
368 len as int - iter.index() <= j < len ==> *final(slots[j]) == target[j],
369 {
370 proof {
371 let pos = len as int - iter.index() - 1;
372 lemma_usize_shr8_is_div256(current);
373 lemma_usize_low8_is_mod256(current);
374 assert(target[0..pos + 1].drop_last() == target[0..pos]);
375 }
376 *byte = (current & 0xff) as u8;
377 current >>= 8;
378 }
379}
380
381#[cfg(feature = "alloc")]
382pub fn u64_to_be_bytes(v: u64) -> (buf: Vec<u8>)
383 requires
384 usize::BITS == 64,
385 ensures
386 buf@ == nat_to_be_bytes(v as nat),
387{
388 usize_to_be_bytes_exec(v as usize)
389}
390
391pub fn usize_to_be_bytes_len(v: usize) -> (len: usize)
394 ensures
395 len == nat_to_be_bytes(v as nat).len(),
396{
397 let mut cur = v;
398 let mut len: usize = 1;
399 while cur >= 256
400 invariant
401 len + nat_to_be_bytes(cur as nat).len() == nat_to_be_bytes(v as nat).len() + 1,
402 decreases cur,
403 {
404 proof {
405 lemma_usize_shr8_is_div256(cur);
406 lemma_usize_to_be_bytes_len_bound(v);
407 }
408 cur >>= 8;
409 len += 1;
410 }
411 len
412}
413
414pub fn u64_to_be_bytes_len(v: u64) -> (len: usize)
415 requires
416 usize::BITS == 64,
417 ensures
418 len == nat_to_be_bytes(v as nat).len(),
419{
420 usize_to_be_bytes_len(v as usize)
421}
422
423pub fn u64_to_be_bytes_first(v: u64) -> (first: u8)
424 requires
425 usize::BITS == 64,
426 ensures
427 first == nat_to_be_bytes(v as nat)[0],
428 decreases v,
429{
430 if v < 256 {
431 v as u8
432 } else {
433 proof {
434 lemma_usize_shr8_is_div256(v as usize);
435 }
436 u64_to_be_bytes_first(v >> 8)
437 }
438}
439
440} #[cfg(all(test, feature = "alloc"))]
442mod tests {
443 use super::{usize_to_be_bytes_exec, usize_to_be_bytes_in_place, usize_to_be_bytes_len};
444
445 #[test]
446 fn usize_to_be_bytes_in_place_matches_vec_encoding() {
447 for value in [0, 1, 0xff, 0x100, 0xffff, 0x10000, usize::MAX] {
448 let expected = usize_to_be_bytes_exec(value);
449 let len = usize_to_be_bytes_len(value);
450 let mut actual = [0u8; size_of::<usize>()];
451
452 usize_to_be_bytes_in_place(value, &mut actual[..len]);
453
454 assert_eq!(&actual[..len], expected.as_slice());
455 }
456 }
457}