// Milestone 1, as far as it currently reaches. // // `Error`, `decoded_len` and `decode_nibble` are base16ct 1.0.0 verbatim. // `decode_into` is NOT: base16ct writes that loop as // for (src, dst) in src.chunks_exact(2).zip(dst.iter_mut()) // and `chunks_exact`/`zip`/`iter_mut` are not implemented yet, so the loop is // rewritten with indexing. The arithmetic under test -- the constant-time // nibble decode, its i16 wrapping and arithmetic shift, and the `err` accumulator // -- is unchanged, and that is the part the other transpiler could not represent. #[derive(Clone, Copy, Eq, PartialEq, Debug)] pub enum Error { InvalidEncoding, InvalidLength, } pub type Result = core::result::Result; pub fn decoded_len(bytes: &[u8]) -> Result { if bytes.len() & 1 == 0 { Ok(bytes.len() / 2) } else { Err(Error::InvalidLength) } } fn decode_nibble(src: u8) -> u16 { let byte = src as i16; let mut ret: i16 = -1; ret += (((0x2fi16 - byte) & (byte - 0x3a)) >> 8) & (byte - 47); ret += (((0x40i16 - byte) & (byte - 0x47)) >> 8) & (byte - 54); ret += (((0x60i16 - byte) & (byte - 0x67)) >> 8) & (byte - 86); ret as u16 } fn decode_into(src: &[u8], dst: &mut [u8]) -> Result { let n: usize = decoded_len(src)?; if dst.len() < n { return Err(Error::InvalidLength); } let mut err: u16 = 0; let mut i: usize = 0; while i < n { let byte = (decode_nibble(src[i * 2]) << 4) | decode_nibble(src[i * 2 + 1]); err |= byte >> 8; dst[i] = byte as u8; i += 1; } match err { 0 => Ok(n), _ => Err(Error::InvalidEncoding), } } fn show(hex: &[u8]) { let mut buf: Vec = vec![0u8; 16]; match decode_into(hex, &mut buf) { Ok(n) => { let mut i: usize = 0; while i < n { print!("{:02x}", buf[i]); i += 1; } println!(" ({} bytes)", n); } Err(e) => println!("error {:?}", e), } } fn main() { show(b"abcd1234"); show(b"ABCD1234"); show(b"abCD1234"); show(b"00ff7f80"); show(b"abc"); show(b"zzzz"); show(b""); let mut c: u8 = 0; while c < 128 { print!("{} ", decode_nibble(c)); c += 1; } println!(""); }