nandi/rustnimpublic Fork 0
3d4a7d283b83d3a5305dfc6654c290bd4ee162ae
Commits
Clone
git clone https://git.rickub.com/nandi/rustnim.git
git clone ssh://git@rickub.com/nandi/rustnim.git

Host key fingerprint (ed25519): SHA256:iycHnxEyq0Q7uyVpB7JlznP0G7JrTPXLYRcAU5CSLhc — verify it before your first connect.

main.rs · 222 lines · 7.1 KBRust Blame HistoryRaw
Prove byte-identity for base16ct by enumerating whole input domains 99b8376 nandithebull 10h ago1//@ cfg: feature=alloc
2// Exhaustive differential driver for base16ct.
3//
4// This is the crate root for the proof harness. The five module files are the
5// crate's own sources, referenced in place rather than copied, so this cannot
6// drift from what `tests/cases/026-base16ct-crate/` transpiles. `lib.rs`'s
7// core is carried here verbatim, as it is there.
8//
9// The point of this file is to enumerate *entire input domains* rather than a
10// handful of examples. See PROOF.md for what that does and does not establish.
11
12#[path = "../tests/cases/026-base16ct-crate/display.rs"]
13mod display;
14#[path = "../tests/cases/026-base16ct-crate/error.rs"]
15mod error;
16#[path = "../tests/cases/026-base16ct-crate/lower.rs"]
17mod lower;
18#[path = "../tests/cases/026-base16ct-crate/mixed.rs"]
19mod mixed;
20#[path = "../tests/cases/026-base16ct-crate/upper.rs"]
21mod upper;
22
23#[cfg(feature = "alloc")]
24pub use std::{string::String, vec::Vec};
25
26pub use crate::display::HexDisplay;
27pub use crate::error::{Error, Result};
28
29/// Compute decoded length of the given hex-encoded input.
30#[inline(always)]
31pub fn decoded_len(bytes: &[u8]) -> Result<usize> {
32 if bytes.len() & 1 == 0 {
33 Ok(bytes.len() / 2)
34 } else {
35 Err(Error::InvalidLength)
36 }
37}
38
39/// Get the length of Base16 (hex) produced by encoding the given bytes.
40#[inline(always)]
41pub fn encoded_len(bytes: &[u8]) -> usize {
42 bytes.len() * 2
43}
44
45fn decode_inner<'a>(
46 src: &[u8],
47 dst: &'a mut [u8],
48 decode_nibble: impl Fn(u8) -> u16,
49) -> Result<&'a [u8]> {
50 let dst = dst
51 .get_mut(..decoded_len(src)?)
52 .ok_or(Error::InvalidLength)?;
53
54 let mut err: u16 = 0;
55 for (src, dst) in src.chunks_exact(2).zip(dst.iter_mut()) {
56 let byte = (decode_nibble(src[0]) << 4) | decode_nibble(src[1]);
57 err |= byte >> 8;
58 *dst = byte as u8;
59 }
60
61 match err {
62 0 => Ok(dst),
63 _ => Err(Error::InvalidEncoding),
64 }
65}
66
67// --------------------------------------------------------------- reporting
68
69/// One decode result, rendered so that any difference in status, length or
70/// content shows up as a difference in these bytes.
71fn report(tag: &str, r: Result<&[u8]>) {
72 match r {
73 Ok(v) => {
74 print!("{}=", tag);
75 for b in v.iter() {
76 print!("{:02x}", b);
77 }
78 print!("/{}", v.len());
79 }
80 Err(e) => print!("{}!{:?}", tag, e),
81 }
82}
83
84fn main() {
85 // -- Domain A: every two-byte input, all three decoders. 65,536 inputs,
86 // which is the complete domain of a single decode chunk and therefore
87 // pins the nibble function exactly.
88 println!("A two-byte decode, exhaustive");
89 let mut hi: u32 = 0;
90 while hi < 256 {
91 let mut lo: u32 = 0;
92 while lo < 256 {
93 let src: [u8; 2] = [hi as u8, lo as u8];
94 let mut b1 = [0u8; 4];
95 let mut b2 = [0u8; 4];
96 let mut b3 = [0u8; 4];
97 print!("{:02x}{:02x} ", hi, lo);
98 report("l", lower::decode(&src, &mut b1));
99 print!(" ");
100 report("u", upper::decode(&src, &mut b2));
101 print!(" ");
102 report("m", mixed::decode(&src, &mut b3));
103 println!("");
104 lo += 1;
105 }
106 hi += 1;
107 }
108
109 // -- Domain B: every two-byte input, both encoders. Encoding is defined
110 // per byte, so this is the complete domain of an encode step too.
111 println!("B two-byte encode, exhaustive");
112 let mut a: u32 = 0;
113 while a < 256 {
114 let mut b: u32 = 0;
115 while b < 256 {
116 let src: [u8; 2] = [a as u8, b as u8];
117 let mut e1 = [0u8; 4];
118 let mut e2 = [0u8; 4];
119 print!("{:02x}{:02x} ", a, b);
120 report("l", lower::encode(&src, &mut e1));
121 print!(" ");
122 report("u", upper::encode(&src, &mut e2));
123 println!("");
124 b += 1;
125 }
126 a += 1;
127 }
128
129 // -- Domain C: every single byte, encode_str and HexDisplay.
130 println!("C single-byte encode_str and HexDisplay, exhaustive");
131 let mut c: u32 = 0;
132 while c < 256 {
133 let src: [u8; 1] = [c as u8];
134 let mut e = [0u8; 2];
135 match lower::encode_str(&src, &mut e) {
136 Ok(s) => print!("{:02x} s={} ", c, s),
137 Err(er) => print!("{:02x} s!{:?} ", c, er),
138 }
139 println!("x={:x} X={:X} d={}", HexDisplay(&src), HexDisplay(&src), HexDisplay(&src));
140 c += 1;
141 }
142
143 // -- Domain D: lengths 0..=128, both length functions and the buffer-size
144 // boundary. Covers odd/even and the too-small-destination path.
145 println!("D lengths, exhaustive to 128");
146 let big = [b'a'; 128];
147 let mut n: usize = 0;
148 while n <= 128 {
149 let src = &big[..n];
150 match decoded_len(src) {
151 Ok(v) => print!("{} dl={} el={}", n, v, encoded_len(src)),
152 Err(e) => print!("{} dl!{:?} el={}", n, e, encoded_len(src)),
153 }
154 // Destination exactly one byte too small, to exercise the bound.
155 let mut tight = [0u8; 64];
156 let want = n / 2;
157 if want > 0 && want <= 64 {
158 let fit = &mut tight[..want - 1];
159 print!(" ");
160 report("t", mixed::decode(src, fit));
161 }
162 println!("");
163 n += 1;
164 }
165
166 // -- Domain E: pseudorandom inputs of varied length, from a generator
167 // defined here so both sides produce the identical sequence.
168 println!("E pseudorandom, 20000 cases");
169 let mut state: u32 = 2463534242;
170 let mut i: u32 = 0;
171 while i < 20000 {
172 // xorshift32, chosen because it is exactly representable in both
173 // languages: u32 wrapping shifts and xor, nothing else.
174 state ^= state << 13;
175 state ^= state >> 17;
176 state ^= state << 5;
177 let len: usize = (state % 49) as usize;
178
179 let mut buf = [0u8; 48];
180 let mut j: usize = 0;
181 while j < len {
182 state ^= state << 13;
183 state ^= state >> 17;
184 state ^= state << 5;
185 // Mostly hex characters, sometimes not, so both the accepting and
186 // the rejecting paths are exercised.
187 let pick = state % 20;
188 if pick < 16 {
189 let d = (state >> 8) % 16;
190 if d < 10 {
191 buf[j] = 48 + d as u8;
192 } else if (state >> 4) % 2 == 0 {
193 buf[j] = 87 + d as u8;
194 } else {
195 buf[j] = 55 + d as u8;
196 }
197 } else {
198 buf[j] = (state >> 16) as u8;
199 }
200 j += 1;
201 }
202 let src = &buf[..len];
203
204 let mut d1 = [0u8; 24];
205 let mut d2 = [0u8; 24];
206 let mut d3 = [0u8; 24];
207 print!("{} {} ", i, len);
208 report("l", lower::decode(src, &mut d1));
209 print!(" ");
210 report("u", upper::decode(src, &mut d2));
211 print!(" ");
212 report("m", mixed::decode(src, &mut d3));
213
214 // Round trip: encode the bytes, decode them back.
215 let mut enc = [0u8; 96];
216 report(" e", lower::encode(src, &mut enc));
217 println!("");
218 i += 1;
219 }
220
221 println!("done");
222}