Skip to main content

vest_lib/primitives/
leb128.rs

1//! Unsigned little-endian base-128 (ULEB128) encoding.
2use crate::core::exec::input::InputBuf;
3use crate::core::exec::parser::*;
4use crate::{
5    combinators::{mapped::spec::*, recursive::*, Alt, FixWith, Mapped, Pair, Refined, U8},
6    core::{proof::*, spec::*},
7};
8use vstd::arithmetic::div_mod::*;
9use vstd::arithmetic::mul::*;
10use vstd::prelude::*;
11
12verus! {
13
14pub type ULeb128Fmt<const MINIMAL: bool, const RECLIMIT: usize> = FixWith<
15    RECLIMIT,
16    ULeb128RecBody<MINIMAL>,
17    (),
18>;
19
20pub open spec fn uleb128_fmt<const MINIMAL: bool, const N: usize>() -> ULeb128Fmt<MINIMAL, N> {
21    FixWith(ULeb128RecBody::<MINIMAL>, ())
22}
23
24pub struct ULeb128RecBody<const MINIMAL: bool>;
25
26impl<const MINIMAL: bool> SpecRecBody for ULeb128RecBody<MINIMAL> {
27    type Param = ();
28
29    type T = nat;
30
31    type Body = Alt<
32        TerminalByteNat,
33        Mapped<
34            Pair<ContinuationByte, Refined<BundledSpecs<nat>, PredFnSpec<nat>>>,
35            FnSpecMapper<(u8, nat), nat>,
36        >,
37    >;
38
39    /// 𝚞𝑁	::=	𝑛:𝚋𝚢𝚝𝚎          		⇒		𝑛		if 𝑛 < 2^7
40    ///
41    /// |	𝑛:𝚋𝚢𝚝𝚎  𝑚:𝚞(𝑁−7)		⇒		2^7 * 𝑚 + (𝑛 − 2^7)		  if 𝑛 >= 2^7
42    open spec fn spec_body(
43        &self,
44        _param: (),
45        rec: ParamRecSpecs<Self::Param, Self::T>,
46    ) -> Self::Body {
47        Alt(
48            terminal_byte_nat(),
49            Mapped {
50                // No trailing zeros (e.g., 0x80 0x00) allowed if MINIMAL
51                inner: Pair(continuation_byte(), Refined(rec(()), |v: nat| MINIMAL ==> v > 0)),
52                // map: (lsb, rest) -> lsb | (rest << 7)
53                // map_rev: o -> (lsb = o & 0x7F, rest = o >> 7)
54                mapper: (
55                    |pair: (u8, nat)| 128 * pair.1 + pair.0 as nat,
56                    |o: nat| ((o % 128) as u8, o / 128),
57                ),
58            },
59        )
60    }
61}
62
63pub type TerminalByteNat = Mapped<TerminalByte, TermByteFromToNat>;
64
65pub type TerminalByte = Refined<U8, PredFnSpec<u8>>;
66
67pub type ContinuationByte = Mapped<Refined<U8, PredFnSpec<u8>>, LowBitsMask>;
68
69pub const CONTINUATION_BIT: u8 = 0x80;
70
71/// Check that high bit is not set, and map to the corresponding nat value.
72pub open spec fn terminal_byte_nat() -> TerminalByteNat {
73    Mapped { inner: terminal_byte(), mapper: TermByteFromToNat }
74}
75
76/// Check that high bit is not set.
77pub open spec fn terminal_byte() -> TerminalByte {
78    Refined(U8, |b: u8| b < CONTINUATION_BIT)
79}
80
81/// Check that high bit is set, and map to the corresponding low 7 bits.
82pub open spec fn continuation_byte() -> ContinuationByte {
83    Mapped { inner: Refined(U8, |b: u8| b >= CONTINUATION_BIT), mapper: LowBitsMask }
84}
85
86pub struct TermByteFromToNat;
87
88impl SpecMapper for TermByteFromToNat {
89    type In = u8;
90
91    type Out = nat;
92
93    open spec fn wf_in(&self, i: Self::In) -> bool {
94        i < CONTINUATION_BIT
95    }
96
97    open spec fn wf_out(&self, o: Self::Out) -> bool {
98        o < CONTINUATION_BIT
99    }
100
101    open spec fn spec_map(&self, i: Self::In) -> Self::Out {
102        i as nat
103    }
104
105    open spec fn spec_map_rev(&self, o: Self::Out) -> Self::In {
106        o as u8
107    }
108}
109
110pub struct LowBitsMask;
111
112impl SpecMapper for LowBitsMask {
113    type In = u8;
114
115    type Out = u8;
116
117    open spec fn wf_in(&self, i: Self::In) -> bool {
118        i >= CONTINUATION_BIT
119    }
120
121    open spec fn wf_out(&self, o: Self::Out) -> bool {
122        o < CONTINUATION_BIT
123    }
124
125    open spec fn spec_map(&self, i: Self::In) -> Self::Out {
126        // Mask off the high bit to get the low 7 bits as a nat value
127        // i & 0x7Fu8
128        (i - CONTINUATION_BIT) as u8
129    }
130
131    open spec fn spec_map_rev(&self, o: Self::Out) -> Self::In {
132        // Set the high bit to get the continuation wire format
133        // o | LEB128_CONT_BIT
134        (o + CONTINUATION_BIT) as u8
135    }
136}
137
138impl LossyMapper for TermByteFromToNat {
139    proof fn lemma_sound_mapper(&self, o: Self::Out) {
140    }
141
142    proof fn lemma_mapper_wf_out_in(&self, o: Self::Out) {
143    }
144}
145
146impl LosslessMapper for TermByteFromToNat {
147    proof fn lemma_lossless_mapper(&self, i: Self::In) {
148    }
149
150    proof fn lemma_mapper_wf_in_out(&self, i: Self::In) {
151    }
152}
153
154impl LossyMapper for LowBitsMask {
155    proof fn lemma_sound_mapper(&self, o: Self::Out) {
156    }
157
158    proof fn lemma_mapper_wf_out_in(&self, o: Self::Out) {
159    }
160}
161
162impl LosslessMapper for LowBitsMask {
163    proof fn lemma_lossless_mapper(&self, i: Self::In) {
164    }
165
166    proof fn lemma_mapper_wf_in_out(&self, i: Self::In) {
167    }
168}
169
170pub struct ULeb128<const MINIMAL: bool, const N: usize>;
171
172mod leb128_derived_specs {
173    use super::*;
174
175    impl<const MINIMAL: bool, const N: usize> SpecParser for ULeb128<MINIMAL, N> {
176        type PVal = nat;
177
178        open spec fn spec_parse(&self, ibuf: Seq<u8>) -> Option<(int, Self::PVal)> {
179            uleb128_fmt::<MINIMAL, N>().spec_parse(ibuf)
180        }
181    }
182
183    impl<const MINIMAL: bool, const N: usize> Consistency for ULeb128<MINIMAL, N> {
184        type Val = nat;
185
186        open spec fn consistent(&self, v: Self::Val) -> bool {
187            uleb128_fmt::<MINIMAL, N>().consistent(v)
188        }
189    }
190
191    impl<const MINIMAL: bool, const N: usize> SpecSerializerDps for ULeb128<MINIMAL, N> {
192        type SValue = nat;
193
194        open spec fn spec_serialize_dps(&self, v: Self::SValue, obuf: Seq<u8>) -> Seq<u8> {
195            uleb128_fmt::<MINIMAL, N>().spec_serialize_dps(v, obuf)
196        }
197    }
198
199    impl<const MINIMAL: bool, const N: usize> SpecSerializer for ULeb128<MINIMAL, N> {
200        type SVal = nat;
201
202        open spec fn spec_serialize(&self, v: Self::SVal) -> Seq<u8> {
203            uleb128_fmt::<MINIMAL, N>().spec_serialize(v)
204        }
205    }
206
207    impl<const MINIMAL: bool, const N: usize> SpecByteLen for ULeb128<MINIMAL, N> {
208        type T = nat;
209
210        open spec fn byte_len(&self, v: Self::T) -> nat {
211            uleb128_fmt::<MINIMAL, N>().byte_len(v)
212        }
213    }
214
215}
216
217mod leb128_derived_proofs {
218    use super::*;
219
220    impl<const MINIMAL: bool> SafeParserRecBody for ULeb128RecBody<MINIMAL> {
221        proof fn lemma_body_safe_inv_preservation(
222            &self,
223            _param: (),
224            rec: ParamRecSpecs<Self::Param, Self::T>,
225        ) {
226        }
227    }
228
229    impl SoundParserRecBody for ULeb128RecBody<true> {
230        proof fn lemma_body_sound_inv_preservation(
231            &self,
232            _param: (),
233            rec: ParamRecSpecs<Self::Param, Self::T>,
234        ) {
235        }
236    }
237
238    impl<const MINIMAL: bool> NoLookAheadRecBody for ULeb128RecBody<MINIMAL> {
239        proof fn lemma_body_no_lookahead_inv_preservation(
240            &self,
241            _param: (),
242            rec: ParamRecSpecs<Self::Param, Self::T>,
243        ) {
244            reveal(disjoint_domains);
245        }
246    }
247
248    impl<const MINIMAL: bool> ProductiveRecBody for ULeb128RecBody<MINIMAL> {
249        proof fn lemma_body_productive_inv_preservation(
250            &self,
251            param: Self::Param,
252            rec: ParamRecSpecs<Self::Param, Self::T>,
253        ) {
254        }
255    }
256
257    impl NonMalleableRecBody for ULeb128RecBody<true> {
258        proof fn lemma_body_nonmal_inv_preservation(
259            &self,
260            _param: (),
261            rec: ParamRecSpecs<Self::Param, Self::T>,
262        ) {
263        }
264    }
265
266    impl<const MINIMAL: bool> GoodSerializerRecBody for ULeb128RecBody<MINIMAL> {
267        proof fn lemma_s_body_serialize_inv_preservation(
268            &self,
269            _param: (),
270            rec: ParamRecSpecs<Self::Param, Self::T>,
271        ) {
272        }
273    }
274
275    impl<const MINIMAL: bool> NonTailFmtRecBody for ULeb128RecBody<MINIMAL> {
276        proof fn lemma_s_body_dps_serialize_dps_inv_preservation(
277            &self,
278            _param: (),
279            rec: ParamRecSpecs<Self::Param, Self::T>,
280        ) {
281        }
282    }
283
284    impl<const MINIMAL: bool> SPRoundTripDpsRecBody for ULeb128RecBody<MINIMAL> {
285        proof fn lemma_body_sp_roundtrip_dps_inv_preservation(
286            &self,
287            _param: (),
288            rec: ParamRecSpecs<Self::Param, Self::T>,
289        ) {
290            reveal(disjoint_domains);
291        }
292    }
293
294    impl<const MINIMAL: bool> EquivSerializersGeneralRecBody for ULeb128RecBody<MINIMAL> {
295        proof fn lemma_s_body_equiv_general_inv_preservation(
296            &self,
297            _param: (),
298            rec: ParamRecSpecs<Self::Param, Self::T>,
299        ) {
300        }
301    }
302
303    impl<const MINIMAL: bool, const N: usize> SafeParser for ULeb128<MINIMAL, N> {
304        proof fn lemma_parse_safe(&self, ibuf: Seq<u8>) {
305            uleb128_fmt::<MINIMAL, N>().lemma_parse_safe(ibuf);
306        }
307    }
308
309    impl<const MINIMAL: bool, const N: usize> Productive for ULeb128<MINIMAL, N> {
310        proof fn lemma_productive(&self, s: Seq<u8>) {
311            uleb128_fmt::<MINIMAL, N>().lemma_productive(s);
312        }
313    }
314
315    impl<const N: usize> SoundParser for ULeb128<true, N> {
316        proof fn lemma_parse_sound_consumption(&self, ibuf: Seq<u8>) {
317            uleb128_fmt::<true, N>().lemma_parse_sound_consumption(ibuf);
318        }
319
320        proof fn lemma_parse_sound_value(&self, ibuf: Seq<u8>) {
321            uleb128_fmt::<true, N>().lemma_parse_sound_value(ibuf);
322        }
323    }
324
325    impl<const MINIMAL: bool, const N: usize> NonTailFmt for ULeb128<MINIMAL, N> {
326        proof fn lemma_serialize_dps_prepend(&self, v: Self::SValue, obuf: Seq<u8>) {
327            uleb128_fmt::<MINIMAL, N>().lemma_serialize_dps_prepend(v, obuf);
328        }
329
330        proof fn lemma_serialize_dps_len(&self, v: Self::SValue, obuf: Seq<u8>) {
331            uleb128_fmt::<MINIMAL, N>().lemma_serialize_dps_len(v, obuf);
332        }
333    }
334
335    impl<const MINIMAL: bool, const N: usize> GoodSerializer for ULeb128<MINIMAL, N> {
336        proof fn lemma_serialize_len(&self, v: Self::SVal) {
337            uleb128_fmt::<MINIMAL, N>().lemma_serialize_len(v);
338        }
339    }
340
341    impl<const MINIMAL: bool, const N: usize> SPRoundTripDps for ULeb128<MINIMAL, N> {
342        proof fn theorem_serialize_dps_parse_roundtrip(&self, v: Self::T, obuf: Seq<u8>) {
343            uleb128_fmt::<MINIMAL, N>().theorem_serialize_dps_parse_roundtrip(v, obuf);
344        }
345    }
346
347    impl<const MINIMAL: bool, const N: usize> NoLookAhead for ULeb128<MINIMAL, N> {
348        proof fn lemma_no_lookahead(&self, i1: Seq<u8>, i2: Seq<u8>) {
349            uleb128_fmt::<MINIMAL, N>().lemma_no_lookahead(i1, i2);
350        }
351    }
352
353    impl<const N: usize> NonMalleable for ULeb128<true, N> {
354        proof fn lemma_parse_non_malleable(&self, buf1: Seq<u8>, buf2: Seq<u8>) {
355            uleb128_fmt::<true, N>().lemma_parse_non_malleable(buf1, buf2);
356        }
357    }
358
359    impl<const MINIMAL: bool, const N: usize> EquivSerializersGeneral for ULeb128<MINIMAL, N> {
360        proof fn lemma_serialize_equiv(&self, v: Self::SVal, obuf: Seq<u8>) {
361            uleb128_fmt::<MINIMAL, N>().lemma_serialize_equiv(v, obuf);
362        }
363    }
364
365    impl<const MINIMAL: bool, const N: usize> EquivSerializers for ULeb128<MINIMAL, N> {
366        proof fn lemma_serialize_equiv_on_empty(&self, v: Self::SVal) {
367            uleb128_fmt::<MINIMAL, N>().lemma_serialize_equiv_on_empty(v);
368        }
369    }
370
371}
372
373// impl<const MINIMAL: bool> Parser<&[u8]> for ULeb128<MINIMAL, 5> {
374//     typ
375// }
376} // verus!