//@ args: run //@ cfg: feature=alloc // base16ct 1.0.0, transpiled as a multi-file crate. // // `error.rs`, `lower.rs`, `upper.rs`, `mixed.rs` and `display.rs` are the // crate's own files, byte-for-byte. // This file carries `lib.rs`'s core -- `decoded_len`, `encoded_len` and // `decode_inner` verbatim -- plus a driver, because the runner needs a `main`. // The `alloc`-gated items are on, via `--cfg feature=alloc`. mod display; mod error; mod lower; mod mixed; mod upper; // `lib.rs` re-exports these from `alloc` for its modules to `use crate::..`; // this crate root stands in for it. #[cfg(feature = "alloc")] pub use std::{string::String, vec::Vec}; pub use crate::display::HexDisplay; pub use crate::error::{Error, Result}; /// Compute decoded length of the given hex-encoded input. #[inline(always)] pub fn decoded_len(bytes: &[u8]) -> Result { if bytes.len() & 1 == 0 { Ok(bytes.len() / 2) } else { Err(Error::InvalidLength) } } /// Get the length of Base16 (hex) produced by encoding the given bytes. #[inline(always)] pub fn encoded_len(bytes: &[u8]) -> usize { bytes.len() * 2 } fn decode_inner<'a>( src: &[u8], dst: &'a mut [u8], decode_nibble: impl Fn(u8) -> u16, ) -> Result<&'a [u8]> { let dst = dst .get_mut(..decoded_len(src)?) .ok_or(Error::InvalidLength)?; let mut err: u16 = 0; for (src, dst) in src.chunks_exact(2).zip(dst.iter_mut()) { let byte = (decode_nibble(src[0]) << 4) | decode_nibble(src[1]); err |= byte >> 8; *dst = byte as u8; } match err { 0 => Ok(dst), _ => Err(Error::InvalidEncoding), } } fn show(tag: &str, r: Result<&[u8]>) { match r { Ok(v) => { print!("{} ok ", tag); for b in v.iter() { print!("{:02x}", b); } println!(" len={}", v.len()); } Err(e) => println!("{} err {:?} / {}", tag, e, e), } } fn main() { let mut buf = [0u8; 16]; show("lower", lower::decode(b"abcd1234", &mut buf)); show("mixed-l", mixed::decode(b"abcd1234", &mut buf)); show("mixed-u", mixed::decode(b"ABCD1234", &mut buf)); show("mixed-m", mixed::decode(b"abCD1234", &mut buf)); show("edge", mixed::decode(b"00ff7f80", &mut buf)); show("oddlen", mixed::decode(b"abc", &mut buf)); show("bad", mixed::decode(b"zzzz", &mut buf)); show("empty", mixed::decode(b"", &mut buf)); let mut small = [0u8; 2]; show("short-dst", mixed::decode(b"abcd1234", &mut small)); show("upper", upper::decode(b"ABCD1234", &mut buf)); show("upper-rej", upper::decode(b"abcd1234", &mut buf)); let mut enc = [0u8; 8]; show("encode", lower::encode(b"\xab\xcd\x12\x34", &mut enc)); let mut encu = [0u8; 8]; show("encode-up", upper::encode(b"\xab\xcd\x12\x34", &mut encu)); // `encode_str` is a closure over an `unsafe` block returning a borrowed // `&str` -- a view of the bytes just written, not a copy. let mut enc2 = [0u8; 8]; match lower::encode_str(b"\xab\xcd\x12\x34", &mut enc2) { Ok(s) => println!("encode_str ok {} len={}", s, s.len()), Err(e) => println!("encode_str err {:?}", e), } let mut tiny = [0u8; 2]; match lower::encode_str(b"\xab\xcd", &mut tiny) { Ok(s) => println!("tiny ok {}", s), Err(e) => println!("tiny err {:?} / {}", e, e), } println!("{} {}", decoded_len(b"abcd").unwrap(), encoded_len(b"\xab\xcd")); // The `alloc` half: `decode_vec` and `encode_string`. println!("{:?}", lower::decode_vec(b"abcd1234")); println!("{:?}", lower::decode_vec(b"abc")); println!("{:?}", mixed::decode_vec(b"ABcd1234")); println!("{}", lower::encode_string(b"\xab\xcd\x12\x34")); println!("{}", upper::encode_string(b"\xab\xcd\x12\x34")); println!("[{}]", lower::encode_string(b"")); // `HexDisplay` is a tuple struct holding a borrowed slice, and its // `UpperHex`/`LowerHex` impls write once per byte into the formatter. let raw = b"\xab\xcd\x12\x34"; println!("{}", HexDisplay(raw)); println!("{:X} {:x}", HexDisplay(raw), HexDisplay(raw)); println!("{}", HexDisplay(b"")); }