Skip to main content

vest_lib/primitives/
base128.rs

1//! Big-endian base-128 integer encoding and in-place conversion helpers.
2use super::leb128::*;
3use crate::combinators::disjoint::disjointness_lemmas;
4use crate::core::exec::output::*;
5use crate::core::exec::parser::*;
6use crate::{
7    combinators::mapped::spec::*,
8    combinators::*,
9    core::{exec::*, proof::*, spec::*},
10};
11#[cfg(feature = "alloc")]
12use alloc::{vec, vec::Vec};
13use input::InputBuf;
14use vstd::arithmetic::power::*;
15use vstd::calc;
16use vstd::prelude::*;
17
18verus! {
19
20/// Unsigned big-endian base-128 decoding.
21pub open spec fn nat_from_base128(bytes: Seq<u8>) -> nat
22    decreases bytes.len(),
23{
24    if bytes.len() == 0 {
25        0
26    } else {
27        nat_from_base128(bytes.drop_last()) * 128 + (bytes.last() % 128) as nat
28    }
29}
30
31/// Unsigned big-endian base-128 encoding.
32pub open spec fn nat_to_base128(n: nat) -> Seq<u8>
33    decreases n,
34{
35    if n < 128 {
36        seq![n as u8]
37    } else {
38        nat_to_base128((n / 128) as nat).push((n % 128) as u8)
39    }
40}
41
42pub proof fn lemma_from_base128_push(bytes: Seq<u8>, b: u8)
43    ensures
44        nat_from_base128(bytes.push(b)) == nat_from_base128(bytes) * 128 + (b % 128) as nat,
45{
46    assert(bytes.push(b).drop_last() == bytes);
47}
48
49pub proof fn lemma_pow128_succ(exp: nat)
50    ensures
51        pow(128, exp + 1) == pow(128, exp) * 128,
52{
53    lemma_pow_adds(128, exp, 1);
54    lemma_pow1(128);
55}
56
57pub proof fn lemma_from_base128_upper_bound(bytes: Seq<u8>)
58    ensures
59        nat_from_base128(bytes) < pow(128, bytes.len()),
60    decreases bytes.len(),
61{
62    if bytes.len() == 0 {
63        lemma_pow0(128);
64    } else {
65        let prefix = bytes.drop_last();
66        lemma_from_base128_upper_bound(prefix);
67        lemma_pow128_succ(prefix.len());
68    }
69}
70
71pub proof fn lemma_nat_from_base128_bounds(bytes: Seq<u8>)
72    ensures
73        bytes.len() <= 4 ==> nat_from_base128(bytes) <= u32::MAX,
74        bytes.len() <= 9 ==> nat_from_base128(bytes) <= u64::MAX,
75{
76    lemma_from_base128_upper_bound(bytes);
77    reveal_with_fuel(pow, 10);
78}
79
80pub proof fn lemma_to_base128_props(n: nat)
81    ensures
82        nat_to_base128(n).len() > 0,
83        n > 0 ==> nat_to_base128(n)[0] != 0,
84        n > 0 ==> pow(128, (nat_to_base128(n).len() - 1) as nat) <= n,
85        forall|i: int| 0 <= i < nat_to_base128(n).len() ==> #[trigger] nat_to_base128(n)[i] < 128,
86    decreases n,
87{
88    if n < 128 {
89        lemma_pow0(128);
90    } else {
91        let q = (n / 128) as nat;
92        lemma_to_base128_props(q);
93        lemma_pow128_succ((nat_to_base128(q).len() - 1) as nat);
94        assert(pow(128, (nat_to_base128(q).len() - 1) as nat) * 128 <= q * 128) by (nonlinear_arith)
95            requires
96                pow(128, (nat_to_base128(q).len() - 1) as nat) <= q,
97        ;
98    }
99}
100
101pub proof fn lemma_to_base128_len_bound(n: nat, max_len: nat)
102    requires
103        0 < max_len,
104        n < pow(128, max_len),
105    ensures
106        nat_to_base128(n).len() <= max_len,
107{
108    if n == 0 {
109    } else {
110        lemma_to_base128_props(n);
111        lemma_pow_strictly_increases_converse(128, (nat_to_base128(n).len() - 1) as nat, max_len);
112    }
113}
114
115pub proof fn lemma_to_base128_len_bounds()
116    ensures
117        forall|n: u32| #[trigger] nat_to_base128(n as nat).len() <= 5,
118        forall|n: u64| #[trigger] nat_to_base128(n as nat).len() <= 10,
119{
120    reveal_with_fuel(pow, 11);
121    assert forall|n: u32| #[trigger] nat_to_base128(n as nat).len() <= 5 by {
122        lemma_to_base128_len_bound(n as nat, 5);
123    }
124    assert forall|n: u64| #[trigger] nat_to_base128(n as nat).len() <= 10 by {
125        lemma_to_base128_len_bound(n as nat, 10);
126    }
127}
128
129pub proof fn lemma_to_from_base128_roundtrip(n: nat)
130    ensures
131        nat_from_base128(nat_to_base128(n)) == n,
132    decreases n,
133{
134    if n < 128 {
135        reveal_with_fuel(nat_from_base128, 2);
136    } else {
137        let q = (n / 128) as nat;
138        let r = (n % 128) as nat;
139        lemma_to_from_base128_roundtrip(q);
140        lemma_from_base128_push(nat_to_base128(q), r as u8);
141    }
142}
143
144pub proof fn lemma_from_to_base128_roundtrip(bytes: Seq<u8>)
145    requires
146        bytes.len() > 0,
147        bytes.len() > 1 ==> bytes[0] != 0,
148        forall|i: int| 0 <= i < bytes.len() ==> bytes[i] < 128,
149    ensures
150        nat_to_base128(nat_from_base128(bytes)) == bytes,
151    decreases bytes.len(),
152{
153    if bytes.len() == 1 {
154        reveal_with_fuel(nat_from_base128, 2);
155        assert(bytes == seq![bytes[0]]);
156    } else {
157        let prefix = bytes.drop_last();
158        lemma_from_to_base128_roundtrip(prefix);
159    }
160}
161
162pub const CONTINUATION_MASK: u8 = 0b1000_0000;
163
164pub const PAYLOAD_MASK: u8 = 0b0111_1111;
165
166// ceil(log_128(2^32)) = 5
167// pub const BASE128_MAX_BYTES: usize = 4;
168// pub type UInt = u32;
169// ceil(log_128(2^64)) = 10
170pub const BASE128_MAX_BYTES: usize = 9;
171
172pub type UInt = u64;
173
174pub type Base128Fmt__<const MINIMAL: bool> = Mapped<
175    Refined<
176        Repeat<Refined<U8, PredFnSpec<u8>>, Refined<U8, PredFnSpec<u8>>>,
177        PredFnSpec<(Seq<u8>, u8)>,
178    >,
179    FnSpecMapper<(Seq<u8>, u8), UInt>,
180>;
181
182pub open spec fn base128_fmt<const MINIMAL: bool>() -> Base128Fmt__<MINIMAL> {
183    Mapped {
184        inner: Refined(
185            Repeat(
186                Refined(U8, |b: u8| b & CONTINUATION_MASK != 0),
187                Refined(U8, |b: u8| b & CONTINUATION_MASK == 0),
188            ),
189            |pair: (Seq<u8>, u8)|
190                {
191                    // 1. No overflow: the number of bytes must be <= BASE128_MAX_BYTES
192                    // 2. No leading zeros if MINIMAL is true
193                    let (cont_bytes, term_byte) = pair;
194                    &&& cont_bytes.len() <= BASE128_MAX_BYTES - 1
195                    &&& MINIMAL ==> (cont_bytes.len() > 0 ==> cont_bytes[0] & PAYLOAD_MASK != 0)
196                },
197        ),
198        mapper: (
199            |pair: (Seq<u8>, u8)|
200                {
201                    let (cont_bytes, term_byte) = pair;
202                    let bytes = cont_bytes.push(term_byte);
203                    nat_from_base128(bytes) as UInt
204                },
205            |n: UInt|
206                {
207                    let bytes = nat_to_base128(n as nat);
208                    let cont_bytes = bytes.drop_last().map_values(|b: u8| b | CONTINUATION_MASK);
209                    let term_byte = bytes.last();
210                    (cont_bytes, term_byte)
211                },
212        ),
213    }
214}
215
216proof fn lemma_nat_from_base128_modulo(bytes: Seq<u8>)
217    ensures
218        nat_from_base128(bytes) == nat_from_base128(bytes.map_values(|b: u8| (b % 128) as u8)),
219    decreases bytes.len(),
220{
221    if bytes.len() == 0 {
222    } else {
223        let prefix = bytes.drop_last();
224        lemma_nat_from_base128_modulo(prefix);
225        assert(bytes.map_values(|b: u8| (b % 128) as u8).drop_last() == prefix.map_values(
226            |b: u8| (b % 128) as u8,
227        ));
228    }
229}
230
231broadcast proof fn lemma_mask_modulo(b: u8)
232    by (bit_vector)
233    ensures
234        #[trigger] (b & PAYLOAD_MASK) == (b % 128) as u8,
235{
236}
237
238pub proof fn lemma_base128_fmt_sound_nonmal_inv()
239    ensures
240        base128_fmt::<true>().sound_inv(),
241        base128_fmt::<true>().nonmal_inv(),
242{
243    reveal(<Star<_> as Consistency>::consistent);
244    let fmt = base128_fmt::<true>();
245    assert forall|pair| fmt.inner.consistent(pair) implies (fmt.mapper.1)((fmt.mapper.0)(pair))
246        == pair by {
247        let (cont_bytes, term_byte) = pair;
248        let bytes = cont_bytes.push(term_byte);
249        assert(bytes.len() <= BASE128_MAX_BYTES);
250        lemma_nat_from_base128_bounds(bytes);
251
252        broadcast use lemma_mask_modulo;
253
254        let payload_bytes = bytes.map_values(|b: u8| (b % 128) as u8);
255        let n = nat_from_base128(bytes);
256        lemma_from_to_base128_roundtrip(payload_bytes);  // ==> nat_to_base128(nat_from_base128(payload_bytes)) == payload_bytes
257        lemma_nat_from_base128_modulo(bytes);  // ==> nat_from_base128(bytes) == nat_from_base128(payload_bytes)
258        // need to show: map_rev(nat_from_base128(bytes) as UInt) == (cont_bytes, term_byte)
259
260        let encoded_bytes = nat_to_base128(n);
261        assert(encoded_bytes == payload_bytes);
262
263        assert(term_byte & PAYLOAD_MASK == term_byte) by (bit_vector)
264            requires
265                term_byte & CONTINUATION_MASK == 0,
266        ;
267        assert(encoded_bytes.last() == term_byte);
268        assert forall|i: int|
269            0 <= i < cont_bytes.len() implies encoded_bytes.drop_last().map_values(
270            |b: u8| b | CONTINUATION_MASK,
271        )[i] == cont_bytes[i] by {
272            let b_orig = cont_bytes[i];
273            assert(b_orig & CONTINUATION_MASK != 0);
274            assert(((b_orig & PAYLOAD_MASK) | 128) == b_orig) by (bit_vector)
275                requires
276                    b_orig & CONTINUATION_MASK != 0,
277            ;
278        }
279        assert(encoded_bytes.drop_last().map_values(|b: u8| b | CONTINUATION_MASK) =~= cont_bytes);
280    }
281}
282
283pub proof fn lemma_base128_fmt_unambiguous<const MINIMAL: bool>()
284    ensures
285        base128_fmt::<MINIMAL>().unambiguous(),
286{
287    broadcast use disjointness_lemmas;
288
289    let fmt = base128_fmt::<MINIMAL>();
290    // the following holds true even without `fmt.consistent(o) implies`
291    assert forall|o: UInt| #[trigger] (fmt.mapper.0)((fmt.mapper.1)(o)) == o by {
292        let bytes = nat_to_base128(o as nat);
293        let cont_bytes = bytes.drop_last().map_values(|b: u8| b | CONTINUATION_MASK);
294        let term_byte = bytes.last();
295        let bytes2 = cont_bytes.push(term_byte);
296
297        lemma_to_from_base128_roundtrip(o as nat);  // ==> nat_from_base128(bytes) == o
298        // need to show: nat_from_base128(bytes) == nat_from_base128(bytes2)
299        lemma_nat_from_base128_modulo(bytes);
300        lemma_nat_from_base128_modulo(bytes2);
301        let m1 = bytes2.map_values(|b: u8| (b % 128) as u8);
302        let m2 = bytes.map_values(|b: u8| (b % 128) as u8);
303        // need to show: m1 == m2
304        assert(forall|b: u8| ((b | 128) % 128) as u8 == b % 128) by (bit_vector);
305        assert(m1 == m2);
306
307    }
308}
309
310proof fn lemma_uint_shr7_is_div128(v: u64)
311    by (bit_vector)
312    ensures
313        (v >> 7usize) as nat == v as nat / 128,
314{
315}
316
317proof fn lemma_uint_low7_is_mod128(v: u64)
318    by (bit_vector)
319    ensures
320        (v & PAYLOAD_MASK as u64) as nat == v as nat % 128,
321{
322}
323
324proof fn lemma_uint64_shl7_or_is_base128(v: u64, b: u8)
325    by (bit_vector)
326    ensures
327        (((v << 7usize) | (b & 0x7fu8) as u64) as nat) == (v as nat * 128 + (b % 128) as nat) % (
328        0x1_0000_0000_0000_0000nat),
329{
330}
331
332pub(crate) proof fn lemma_base128_fmt_consistent<const MINIMAL: bool>(v: UInt)
333    requires
334        nat_to_base128(v as nat).len() <= BASE128_MAX_BYTES,
335    ensures
336        base128_fmt::<MINIMAL>().consistent(v),
337{
338    reveal(<Star<_> as Consistency>::consistent);
339    lemma_to_base128_props(v as nat);
340
341    assert(forall|byte: u8| #![auto] (byte | CONTINUATION_MASK) & CONTINUATION_MASK != 0)
342        by (bit_vector);
343    assert(forall|byte: u8| #![auto] byte < CONTINUATION_MASK ==> byte & CONTINUATION_MASK == 0)
344        by (bit_vector);
345    assert(forall|byte: u8|
346        #![auto]
347        byte < CONTINUATION_MASK ==> (byte | CONTINUATION_MASK) & PAYLOAD_MASK == byte)
348        by (bit_vector);
349}
350
351pub proof fn lemma_base128_fmt_byte_len<const MINIMAL: bool>(v: UInt)
352    ensures
353        base128_fmt::<MINIMAL>().byte_len(v) == nat_to_base128(v as nat).len(),
354{
355    let bytes = nat_to_base128(v as nat);
356    let cont_bytes = bytes.drop_last().map_values(|b: u8| b | CONTINUATION_MASK);
357    lemma_star_byte_len_seq_u8(cont_bytes);
358}
359
360/// A consistent base-128 value fits the implementation's fixed-size stack buffer.
361pub(crate) proof fn lemma_base128_fmt_consistent_byte_len_bound<const MINIMAL: bool>(v: UInt)
362    requires
363        Base128Fmt::<MINIMAL>.consistent(v),
364    ensures
365        Base128Fmt::<MINIMAL>.byte_len(v) <= BASE128_MAX_BYTES,
366{
367    lemma_base128_fmt_byte_len::<MINIMAL>(v);
368}
369
370proof fn lemma_star_serialize_seq_u8(vs: Seq<u8>)
371    ensures
372        Star(Refined(U8, |b: u8| b & CONTINUATION_MASK != 0)).spec_serialize(vs) == vs,
373    decreases vs.len(),
374{
375    reveal(<Star<_> as SpecSerializer>::spec_serialize);
376    if vs.len() > 0 {
377        let prefix = vs.drop_last();
378        lemma_star_serialize_seq_u8(prefix);
379    }
380}
381
382proof fn lemma_star_byte_len_seq_u8(vs: Seq<u8>)
383    ensures
384        Star(Refined(U8, |b: u8| b & CONTINUATION_MASK != 0)).byte_len(vs) == vs.len(),
385    decreases vs.len(),
386{
387    reveal(<Star<_> as SpecByteLen>::byte_len);
388    if vs.len() == 0 {
389    } else {
390        let prefix = vs.drop_last();
391        lemma_star_byte_len_seq_u8(prefix);
392    }
393}
394
395proof fn lemma_star_parse_rec_from_scan(ibuf: Seq<u8>, n: int)
396    requires
397        0 < n <= ibuf.len(),
398        ibuf[n - 1] & CONTINUATION_MASK == 0,
399        forall|i: int| #![auto] 0 <= i < n - 1 ==> ibuf[i] & CONTINUATION_MASK != 0,
400    ensures
401        Star(Refined(U8, |b: u8| b & CONTINUATION_MASK != 0)).parse_rec(ibuf) == (
402            n - 1,
403            ibuf.take(n - 1),
404        ),
405    decreases n,
406{
407    if n > 1 {
408        lemma_star_parse_rec_from_scan(ibuf.skip(1), (n - 1) as int);
409    }
410}
411
412#[verifier::loop_isolation(false)]
413fn scan_base128_bytes<'a, const MINIMAL: bool>(ibuf: &&'a [u8]) -> (out: PResult<&'a [u8]>)
414    ensures
415        out matches Ok((n, bytes)) ==> {
416            let scanned = bytes.deep_view();
417            &&& 0 < n <= BASE128_MAX_BYTES
418            &&& n <= ibuf@.len()
419            &&& scanned == ibuf@.take(n as int)
420            &&& base128_fmt::<MINIMAL>().inner.spec_parse(ibuf@) == Some(
421                (n as int, (scanned.drop_last(), scanned.last())),
422            )
423        },
424        out is Err ==> base128_fmt::<MINIMAL>().inner.spec_parse(ibuf@) is None,
425{
426    let ghost fmt = base128_fmt::<MINIMAL>();
427    let ghost star = Star(Refined(U8, |b: u8| b & CONTINUATION_MASK != 0));
428
429    let mut i = 0usize;
430    while i < ibuf.len()
431        invariant
432            i <= BASE128_MAX_BYTES,
433            i <= ibuf@.len(),
434            forall|j: int| #![auto] 0 <= j < i as int ==> ibuf@[j] & CONTINUATION_MASK != 0,
435        decreases ibuf@.len() - i,
436    {
437        reveal(<Star<_> as SpecParser>::spec_parse);
438
439        if i == BASE128_MAX_BYTES {
440            proof {
441                if fmt.inner.spec_parse(ibuf@) is Some {
442                    fmt.inner.lemma_parse_safe(ibuf@);
443                    let (n, (cont_bytes, term_byte)) = fmt.inner.spec_parse(ibuf@)->0;
444                    star.lemma_parse_sound_consumption(ibuf@);
445                    lemma_star_byte_len_seq_u8(cont_bytes);
446                    assert(term_byte == ibuf@[n - 1]);
447                    assert(term_byte & CONTINUATION_MASK == 0);
448                    assert(term_byte & CONTINUATION_MASK != 0);
449                }
450            }
451            return Err(ParseError::overflow());
452        }
453        let b = ibuf[i];
454        i += 1;
455        if b & CONTINUATION_MASK == 0 {
456            let bytes = ibuf.take(i);
457            if MINIMAL && i > 1 && (bytes[0] & PAYLOAD_MASK == 0) {
458                return Err(ParseError::non_canonical());
459            }
460            proof {
461                let scanned = bytes.deep_view();
462                let (cont_bytes, term_byte) = (scanned.drop_last(), scanned.last());
463                lemma_star_parse_rec_from_scan(ibuf@, i as int);
464                assert(star.parse_rec(ibuf@) == (i - 1, cont_bytes));
465                assert(fmt.inner.spec_parse(ibuf@) == Some((i as int, (cont_bytes, term_byte))));
466            }
467            return Ok((i, bytes));
468        }
469    }
470    proof {
471        if fmt.spec_parse(ibuf@) is Some {
472            fmt.inner.lemma_parse_safe(ibuf@);
473            let (n, (cont_bytes, term_byte)) = fmt.inner.spec_parse(ibuf@)->0;
474            star.lemma_parse_sound_consumption(ibuf@);
475            lemma_star_byte_len_seq_u8(cont_bytes);
476            assert(term_byte == ibuf@[n - 1]);
477            assert(term_byte & CONTINUATION_MASK == 0);
478            assert(term_byte & CONTINUATION_MASK != 0);
479        }
480    }
481    Err(ParseError::unexpected_eof())
482}
483
484pub fn uint_from_base128(bytes: &[u8]) -> (result: UInt)
485    requires
486        bytes.len() <= BASE128_MAX_BYTES,
487    ensures
488        result as nat == nat_from_base128(bytes.deep_view()),
489{
490    let n = bytes.len();
491    let mut acc: UInt = 0;
492    for i in 0..n
493        invariant
494            n == bytes.len(),
495            n <= BASE128_MAX_BYTES,
496            acc == nat_from_base128(bytes@.take(i as int)),
497    {
498        let b = bytes[i];
499        proof {
500            let prefix = bytes@.take(i as int);
501            let current = prefix.push(b);
502            assert(bytes@.take(i as int + 1) == current);
503            assert(current.drop_last() == prefix);
504            lemma_nat_from_base128_bounds(current);
505            lemma_uint64_shl7_or_is_base128(acc, b);
506        }
507        acc = (acc << 7usize) | ((b & PAYLOAD_MASK) as UInt);
508    }
509    assert(bytes@.take(n as int) == bytes.deep_view());
510    acc
511}
512
513#[cfg(feature = "alloc")]
514pub fn uint_to_base128(v: UInt) -> (buf: Vec<u8>)
515    ensures
516        buf@ == nat_to_base128(v as nat),
517    decreases v,
518{
519    if v < 128 {
520        vec![v as u8]
521    } else {
522        proof {
523            lemma_uint_shr7_is_div128(v);
524            lemma_uint_low7_is_mod128(v);
525        }
526        let mut buf = uint_to_base128(v >> 7);
527        buf.push((v & PAYLOAD_MASK as u64) as u8);
528        buf
529    }
530}
531
532/// Writes the minimal big-endian base-128 encoding of `v` into an exactly-sized slice.
533#[verifier::loop_isolation(false)]
534pub fn uint_to_base128_in_place(v: UInt, obuf: &mut [u8])
535    requires
536        old(obuf)@.len() == nat_to_base128(v as nat).len(),
537    ensures
538        final(obuf)@ == nat_to_base128(v as nat),
539{
540    let len = obuf.len();
541
542    let ghost target = nat_to_base128(v as nat);
543    proof {
544        lemma_to_from_base128_roundtrip(v as nat);
545        lemma_to_base128_props(v as nat);
546        assert(target.take(len as int) == target);
547    }
548    let mut pos = len;
549    let mut current = v;
550
551    // Write the base-128 digits from least significant to most significant, filling the
552    // big-endian output slice from right to left.
553    while pos > 0
554        invariant
555            len == obuf.len(),
556            pos <= len,
557            current as nat == nat_from_base128(target.take(pos as int)),
558            obuf@.skip(pos as int) == target.skip(pos as int),
559        decreases pos,
560    {
561        let ghost old_buf = obuf@;
562        let ghost old_current = current;
563
564        pos -= 1;
565        let byte = (current & PAYLOAD_MASK as UInt) as u8;
566        obuf[pos] = byte;
567        current = current >> 7;
568        proof {
569            lemma_uint_shr7_is_div128(old_current);
570            lemma_uint_low7_is_mod128(old_current);
571            assert(target[pos as int] < 128);
572            assert(target.take(pos as int + 1).drop_last() == target.take(pos as int));
573            assert(obuf@.skip(pos as int) == seq![byte] + old_buf.skip(pos as int + 1));
574        }
575    }
576}
577
578pub fn uint_to_base128_len(v: UInt) -> (len: usize)
579    ensures
580        len == nat_to_base128(v as nat).len(),
581{
582    let mut cur = v;
583    let mut len: usize = 1;
584    while cur >= 128
585        invariant
586            len + nat_to_base128(cur as nat).len() == nat_to_base128(v as nat).len() + 1,
587        decreases cur,
588    {
589        proof {
590            lemma_uint_shr7_is_div128(cur);
591            lemma_to_base128_len_bounds();
592        }
593        cur >>= 7;
594        len += 1;
595    }
596    len
597}
598
599#[derive(Clone, Copy)]
600pub struct Base128Fmt<const MINIMAL: bool = true>;
601
602mod derived_specs {
603    use super::*;
604
605    impl<const MINIMAL: bool> SpecParser for Base128Fmt<MINIMAL> {
606        type PVal = UInt;
607
608        open(crate) spec fn spec_parse(&self, ibuf: Seq<u8>) -> Option<(int, Self::PVal)> {
609            base128_fmt::<MINIMAL>().spec_parse(ibuf)
610        }
611    }
612
613    impl<const MINIMAL: bool> Consistency for Base128Fmt<MINIMAL> {
614        type Val = UInt;
615
616        open(crate) spec fn consistent(&self, v: Self::Val) -> bool {
617            base128_fmt::<MINIMAL>().consistent(v)
618        }
619    }
620
621    impl<const MINIMAL: bool> SpecSerializerDps for Base128Fmt<MINIMAL> {
622        type SValue = UInt;
623
624        open(crate) spec fn spec_serialize_dps(&self, v: Self::SValue, obuf: Seq<u8>) -> Seq<u8> {
625            base128_fmt::<MINIMAL>().spec_serialize_dps(v, obuf)
626        }
627    }
628
629    impl<const MINIMAL: bool> SpecSerializer for Base128Fmt<MINIMAL> {
630        type SVal = UInt;
631
632        open(crate) spec fn spec_serialize(&self, v: Self::SVal) -> Seq<u8> {
633            base128_fmt::<MINIMAL>().spec_serialize(v)
634        }
635    }
636
637    impl<const MINIMAL: bool> SpecByteLen for Base128Fmt<MINIMAL> {
638        type T = UInt;
639
640        open(crate) spec fn byte_len(&self, v: Self::T) -> nat {
641            base128_fmt::<MINIMAL>().byte_len(v)
642        }
643    }
644
645}
646
647mod derived_proofs {
648    use super::*;
649
650    impl<const MINIMAL: bool> SafeParser for Base128Fmt<MINIMAL> {
651        proof fn lemma_parse_safe(&self, ibuf: Seq<u8>) {
652            base128_fmt::<MINIMAL>().lemma_parse_safe(ibuf);
653        }
654    }
655
656    impl<const MINIMAL: bool> Productive for Base128Fmt<MINIMAL> {
657        proof fn lemma_productive(&self, s: Seq<u8>) {
658            base128_fmt::<MINIMAL>().lemma_productive(s);
659        }
660    }
661
662    impl SoundParser for Base128Fmt<true> {
663        proof fn lemma_parse_sound_consumption(&self, ibuf: Seq<u8>) {
664            lemma_base128_fmt_sound_nonmal_inv();
665            base128_fmt::<true>().lemma_parse_sound_consumption(ibuf);
666        }
667
668        proof fn lemma_parse_sound_value(&self, ibuf: Seq<u8>) {
669            lemma_base128_fmt_sound_nonmal_inv();
670            base128_fmt::<true>().lemma_parse_sound_value(ibuf);
671        }
672    }
673
674    impl<const MINIMAL: bool> NonTailFmt for Base128Fmt<MINIMAL> {
675        proof fn lemma_serialize_dps_prepend(&self, v: Self::SValue, obuf: Seq<u8>) {
676            base128_fmt::<MINIMAL>().lemma_serialize_dps_prepend(v, obuf);
677        }
678
679        proof fn lemma_serialize_dps_len(&self, v: Self::SValue, obuf: Seq<u8>) {
680            base128_fmt::<MINIMAL>().lemma_serialize_dps_len(v, obuf);
681        }
682    }
683
684    impl<const MINIMAL: bool> GoodSerializer for Base128Fmt<MINIMAL> {
685        proof fn lemma_serialize_len(&self, v: Self::SVal) {
686            base128_fmt::<MINIMAL>().lemma_serialize_len(v);
687        }
688    }
689
690    impl<const MINIMAL: bool> SPRoundTripDps for Base128Fmt<MINIMAL> {
691        proof fn theorem_serialize_dps_parse_roundtrip(&self, v: Self::T, obuf: Seq<u8>) {
692            lemma_base128_fmt_unambiguous::<MINIMAL>();
693            base128_fmt::<MINIMAL>().theorem_serialize_dps_parse_roundtrip(v, obuf);
694        }
695    }
696
697    impl<const MINIMAL: bool> NoLookAhead for Base128Fmt<MINIMAL> {
698        proof fn lemma_no_lookahead(&self, i1: Seq<u8>, i2: Seq<u8>) {
699            broadcast use disjointness_lemmas;
700
701            base128_fmt::<MINIMAL>().lemma_no_lookahead(i1, i2);
702        }
703    }
704
705    impl NonMalleable for Base128Fmt<true> {
706        proof fn lemma_parse_non_malleable(&self, buf1: Seq<u8>, buf2: Seq<u8>) {
707            lemma_base128_fmt_sound_nonmal_inv();
708            base128_fmt::<true>().lemma_parse_non_malleable(buf1, buf2);
709        }
710    }
711
712    impl<const MINIMAL: bool> EquivSerializersGeneral for Base128Fmt<MINIMAL> {
713        proof fn lemma_serialize_equiv(&self, v: Self::SVal, obuf: Seq<u8>) {
714            base128_fmt::<MINIMAL>().lemma_serialize_equiv(v, obuf);
715        }
716    }
717
718    impl<const MINIMAL: bool> EquivSerializers for Base128Fmt<MINIMAL> {
719        proof fn lemma_serialize_equiv_on_empty(&self, v: Self::SVal) {
720            base128_fmt::<MINIMAL>().lemma_serialize_equiv_on_empty(v);
721        }
722    }
723
724}
725
726impl<const MINIMAL: bool> Parser<&[u8]> for Base128Fmt<MINIMAL> {
727    type PT = UInt;
728
729    fn parse(&self, ibuf: &&[u8]) -> PResult<Self::PT> {
730        let (n, bytes) = scan_base128_bytes::<MINIMAL>(ibuf)?;
731        proof {
732            let (_, (cont_bytes, term_byte)) = base128_fmt::<MINIMAL>().inner.spec_parse(ibuf@)->0;
733            assert(cont_bytes.push(term_byte) == bytes.deep_view());
734        }
735        let value = uint_from_base128(bytes);
736        Ok((n, value))
737    }
738}
739
740impl<Output: OutputBuf, const MINIMAL: bool> Serializer<Output, UInt> for Base128Fmt<MINIMAL> {
741    #[verifier::loop_isolation(false)]
742    fn serialize_into(&self, v: &UInt, obuf: &mut Output) {
743        broadcast use crate::core::exec::output::outbuf_lemmas;
744
745        let num_bytes = uint_to_base128_len(*v);
746        let mut bytes = [0u8;BASE128_MAX_BYTES + 1];
747        let (encoded, _) = bytes.split_at_mut(num_bytes);
748        uint_to_base128_in_place(*v, encoded);
749
750        proof {
751            assert(bytes@.take(num_bytes as int) == nat_to_base128(*v as nat));
752            lemma_base128_fmt_byte_len::<MINIMAL>(*v);
753        }
754        let ghost cont_bytes = bytes@.take(num_bytes as int).drop_last().map_values(
755            |b: u8| b | CONTINUATION_MASK,
756        );
757        proof {
758            old(obuf).lemma_same_destination_reflexive();
759        }
760        for i in 0..num_bytes - 1
761            invariant
762                cont_bytes.len() == num_bytes - 1,
763                obuf@ == old(obuf)@ + cont_bytes.take(i as int),
764                forall|n| old(obuf).fits(i as nat + n) <==> #[trigger] obuf.fits(n),
765                old(obuf).same_destination(obuf),
766                forall|j: int|
767                    #![auto]
768                    0 <= j < cont_bytes.len() ==> cont_bytes[j] == bytes@.take(
769                        num_bytes as int,
770                    ).drop_last()[j] | CONTINUATION_MASK,
771        {
772            broadcast use crate::core::exec::output::outbuf_lemmas;
773
774            let b = bytes[i];
775            obuf.write_byte((b | CONTINUATION_MASK) as u8);
776        }
777        obuf.write_byte(bytes[num_bytes - 1]);
778        proof {
779            lemma_star_serialize_seq_u8(cont_bytes);
780        }
781    }
782}
783
784impl<const MINIMAL: bool> ByteLen<UInt> for Base128Fmt<MINIMAL> {
785    fn length(&self, v: &UInt) -> (len: usize) {
786        let len = uint_to_base128_len(*v);
787        proof {
788            lemma_base128_fmt_byte_len::<MINIMAL>(*v);
789        }
790        len
791    }
792}
793
794impl<const MINIMAL: bool> Prepare<UInt> for Base128Fmt<MINIMAL> {
795    fn prepare(&self, v: &UInt) -> (checked: Result<usize, PreSerializeError>) {
796        let len = uint_to_base128_len(*v);
797        if len <= BASE128_MAX_BYTES {
798            proof {
799                lemma_base128_fmt_byte_len::<MINIMAL>(*v);
800                lemma_base128_fmt_consistent::<MINIMAL>(*v);
801            }
802            Ok(len)
803        } else {
804            Err(PreSerializeError::length_too_large())
805        }
806    }
807}
808
809} // verus!
810#[cfg(all(test, feature = "alloc"))]
811mod tests {
812    use super::*;
813    use crate::core::exec::serializer::PreSerializeErrorKind;
814    use crate::core::exec::{ByteLen, ParseErrorKind, Parser, Prepare, SerializerExt};
815
816    #[test]
817    fn base128_minimal_roundtrip_boundaries() {
818        let fmt = Base128Fmt::<true>;
819
820        let cases: &[(u64, &[u8])] = &[
821            (0, &[0x00]),
822            (1, &[0x01]),
823            (127, &[0x7f]),
824            (128, &[0x81, 0x00]),
825            (16383, &[0xff, 0x7f]),
826        ];
827
828        for &(value, expected) in cases {
829            let mut out = vec![0; fmt.prepare(&value).unwrap()];
830            fmt.serialize(&value, &mut out);
831            assert_eq!(out, expected);
832
833            let mut stack_out = [0u8; 2];
834            fmt.serialize(&value, &mut stack_out[..expected.len()]);
835            assert_eq!(&stack_out[..expected.len()], expected);
836
837            let parsed = fmt.parse(&&out[..]);
838            assert_eq!(parsed, Ok((expected.len(), value)));
839
840            let prepared = fmt.prepare(&value);
841            assert_eq!(prepared, Ok(expected.len()));
842            assert_eq!(fmt.length(&value), expected.len());
843        }
844    }
845
846    #[test]
847    fn uint_to_base128_in_place_matches_vec_encoding() {
848        for value in [0, 1, 0x7f, 0x80, 0x3fff, 0x4000, u64::MAX] {
849            let expected = uint_to_base128(value);
850            let len = uint_to_base128_len(value);
851            let mut actual = [0u8; BASE128_MAX_BYTES + 1];
852
853            uint_to_base128_in_place(value, &mut actual[..len]);
854
855            assert_eq!(&actual[..len], expected.as_slice());
856        }
857    }
858
859    #[test]
860    fn base128_minimal_rejects_non_canonical_zero() {
861        let input = [0x80, 0x00];
862
863        let err = Base128Fmt::<true>.parse(&&input[..]).unwrap_err();
864        assert_eq!(err.kind, ParseErrorKind::NonCanonical);
865
866        let parsed = Base128Fmt::<false>.parse(&&input[..]);
867        assert_eq!(parsed, Ok((2, 0)));
868    }
869
870    #[test]
871    fn base128_distinguishes_unexpected_eof_from_overflow() {
872        let eof = [0x80];
873        let eof_err = Base128Fmt::<true>.parse(&&eof[..]).unwrap_err();
874        assert_eq!(eof_err.kind, ParseErrorKind::UnexpectedEof);
875
876        let overflow = [0x80; BASE128_MAX_BYTES + 1];
877        let overflow_err = Base128Fmt::<true>.parse(&&overflow[..]).unwrap_err();
878        assert_eq!(overflow_err.kind, ParseErrorKind::Overflow);
879    }
880
881    #[test]
882    fn base128_prepare_rejects_values_needing_ten_bytes() {
883        let fmt = Base128Fmt::<true>;
884        let max_supported = (1u64 << 63) - 1;
885        let too_large = 1u64 << 63;
886
887        let mut out = vec![0; fmt.prepare(&max_supported).unwrap()];
888        fmt.serialize(&max_supported, &mut out);
889        assert_eq!(out.len(), BASE128_MAX_BYTES);
890        assert_eq!(fmt.prepare(&max_supported), Ok(BASE128_MAX_BYTES));
891        assert_eq!(fmt.length(&max_supported), BASE128_MAX_BYTES);
892
893        let len = fmt.length(&too_large);
894        assert_eq!(len, BASE128_MAX_BYTES + 1);
895
896        let err = fmt.prepare(&too_large).unwrap_err();
897        assert_eq!(err.kind, PreSerializeErrorKind::LengthTooLarge);
898    }
899}