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