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