nandi/rustnimpublic Fork 0
8354895601e0a0fa3e1962b9ef728d00050ffb97
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.

Add enums, Option/Result, `?`, and the machinery base16ct needs around them b0ccd80 · on 8354895601e0a0fa3e1962b9ef728d00050ffb97 · nandithebull · 19h ago
022-base16ct-decode.rs · 89 lines · 2.3 KBRust Blame HistoryRaw
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
// 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<T> = core::result::Result<T, Error>;

pub fn decoded_len(bytes: &[u8]) -> Result<usize> {
    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<usize> {
    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<u8> = 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!("");
}