~james/span

feat(encoding): prove canonical decoding and instruction injectivity

commit
a3a9eb2664b887f7300df432ae13005985ec1c82 browse tree
parent
a68ad347f48eb214044370b3ca67d33d9b3c9a6d
author
james <j@cpl0.zip>
signature
Verified ~james signed with a key registered by ~james
date
2026-10-31 10:00 UTC

2 files changed, +27 −0

src/Encoding.thy modified

@@ -53,6 +53,23 @@ theorem decode_encode:
5353 by (cases op)
5454 (auto simp: byte_join nat_less_iff div_less_iff_less_mult split: bool.splits if_splits)
5555
56+theorem decode_canonical:
57+ "decode bs = Some (op, rest) \<Longrightarrow> encodable op \<and> bs = encode op @ rest"
58+ by (cases bs)
59+ (auto simp: nat_less_iff div_less_iff_less_mult mod_less div_eq_0_iff
60+ nat_add_distrib nat_mult_distrib
61+ split: if_splits list.splits)
62+
63+corollary decode_progress:
64+ "decode bs = Some (op, rest) \<Longrightarrow> length rest < length bs"
65+ using decode_canonical[of bs op rest]
66+ by (cases op) (auto split: bool.splits)
67+
68+theorem encode_injective:
69+ "encodable a \<Longrightarrow> encodable b \<Longrightarrow> encode a = encode b \<longleftrightarrow> a = b"
70+ using decode_encode[of a "[]"] decode_encode[of b "[]"]
71+ by (metis append_Nil option.inject prod.inject)
72+
5673 fun lower :: "nat instruction list \<Rightarrow> nat set \<Rightarrow> nat \<Rightarrow> nat instruction \<Rightarrow> operation" where
5774 "lower p W i Nop = Idle"
5875 | "lower p W i Inc = Raise"

tests/Examples.thy modified

@@ -59,6 +59,16 @@ lemma truncated_rejected: "decode [18,1] = None"
5959 lemma unknown_rejected: "decode [255] = None"
6060 by eval
6161
62+lemma short_payload_rejected: "decode [16,256] = None"
63+ by eval
64+
65+lemma long_payload_rejected: "decode [19,0,256] = None"
66+ by eval
67+
68+lemma decoder_preserves_tail:
69+ "decode [19,255,255,3,255] = Some (Branch True True 32767, [3,255])"
70+ by eval
71+
6272 lemma undefined_rejected:
6373 "compile ''jmp missing'' = Error ''undefined label missing''"
6474 by eval