hex_str 0.3.0

A library that helps handle hexadecimal strings
Documentation
pub fn parse(a: u8, b: u8) -> Option<u8> {
    #[rustfmt::skip]
    static HEX_LSB: [i16; 256] = [
        -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1, -1, -1, -1, -1, -1, -1,
        -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1, -1, -1, -1, -1, -1, -1,
        -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1, -1, -1, -1, -1, -1, -1,
         0,   1,   2,   3,   4,   5,   6,   7,   8,   9, -1, -1, -1, -1, -1, -1,
        -1,  10,  11,  12,  13,  14,  15,  -1,  -1,  -1, -1, -1, -1, -1, -1, -1,
        -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1, -1, -1, -1, -1, -1, -1,
        -1,  10,  11,  12,  13,  14,  15,  -1,  -1,  -1, -1, -1, -1, -1, -1, -1,
        -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1, -1, -1, -1, -1, -1, -1,
        -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1, -1, -1, -1, -1, -1, -1,
        -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1, -1, -1, -1, -1, -1, -1,
        -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1, -1, -1, -1, -1, -1, -1,
        -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1, -1, -1, -1, -1, -1, -1,
        -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1, -1, -1, -1, -1, -1, -1,
        -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1, -1, -1, -1, -1, -1, -1,
        -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1, -1, -1, -1, -1, -1, -1,
        -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1, -1, -1, -1, -1, -1, -1,
    ];

    #[rustfmt::skip]
    static HEX_MSB: [i16; 256] = [
        -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1, -1, -1, -1, -1, -1, -1,
        -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1, -1, -1, -1, -1, -1, -1,
        -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1, -1, -1, -1, -1, -1, -1,
         0,  16,  32,  48,  64,  80,  96, 112, 128, 144, -1, -1, -1, -1, -1, -1,
        -1, 160, 176, 192, 208, 224, 240,  -1,  -1,  -1, -1, -1, -1, -1, -1, -1,
        -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1, -1, -1, -1, -1, -1, -1,
        -1, 160, 176, 192, 208, 224, 240,  -1,  -1,  -1, -1, -1, -1, -1, -1, -1,
        -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1, -1, -1, -1, -1, -1, -1,
        -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1, -1, -1, -1, -1, -1, -1,
        -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1, -1, -1, -1, -1, -1, -1,
        -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1, -1, -1, -1, -1, -1, -1,
        -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1, -1, -1, -1, -1, -1, -1,
        -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1, -1, -1, -1, -1, -1, -1,
        -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1, -1, -1, -1, -1, -1, -1,
        -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1, -1, -1, -1, -1, -1, -1,
        -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1, -1, -1, -1, -1, -1, -1,
    ];

    parse_(a, b, &HEX_LSB, &HEX_MSB)
}

pub fn parse_lower(a: u8, b: u8) -> Option<u8> {
    #[rustfmt::skip]
    static HEX_LSB_LOWER: [i16; 256] = [
        -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1, -1, -1, -1, -1, -1, -1,
        -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1, -1, -1, -1, -1, -1, -1,
        -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1, -1, -1, -1, -1, -1, -1,
         0,   1,   2,   3,   4,   5,   6,   7,   8,   9, -1, -1, -1, -1, -1, -1,
        -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1, -1, -1, -1, -1, -1, -1,
        -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1, -1, -1, -1, -1, -1, -1,
        -1,  10,  11,  12,  13,  14,  15,  -1,  -1,  -1, -1, -1, -1, -1, -1, -1,
        -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1, -1, -1, -1, -1, -1, -1,
        -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1, -1, -1, -1, -1, -1, -1,
        -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1, -1, -1, -1, -1, -1, -1,
        -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1, -1, -1, -1, -1, -1, -1,
        -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1, -1, -1, -1, -1, -1, -1,
        -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1, -1, -1, -1, -1, -1, -1,
        -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1, -1, -1, -1, -1, -1, -1,
        -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1, -1, -1, -1, -1, -1, -1,
        -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1, -1, -1, -1, -1, -1, -1,
    ];

    #[rustfmt::skip]
    static HEX_MSB_LOWER: [i16; 256] = [
        -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1, -1, -1, -1, -1, -1, -1,
        -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1, -1, -1, -1, -1, -1, -1,
        -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1, -1, -1, -1, -1, -1, -1,
         0,  16,  32,  48,  64,  80,  96, 112, 128, 144, -1, -1, -1, -1, -1, -1,
        -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1, -1, -1, -1, -1, -1, -1,
        -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1, -1, -1, -1, -1, -1, -1,
        -1, 160, 176, 192, 208, 224, 240,  -1,  -1,  -1, -1, -1, -1, -1, -1, -1,
        -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1, -1, -1, -1, -1, -1, -1,
        -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1, -1, -1, -1, -1, -1, -1,
        -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1, -1, -1, -1, -1, -1, -1,
        -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1, -1, -1, -1, -1, -1, -1,
        -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1, -1, -1, -1, -1, -1, -1,
        -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1, -1, -1, -1, -1, -1, -1,
        -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1, -1, -1, -1, -1, -1, -1,
        -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1, -1, -1, -1, -1, -1, -1,
        -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1, -1, -1, -1, -1, -1, -1,
    ];

    parse_(a, b, &HEX_LSB_LOWER, &HEX_MSB_LOWER)
}

pub fn parse_upper(a: u8, b: u8) -> Option<u8> {
    #[rustfmt::skip]
    static HEX_LSB_UPPER: [i16; 256] = [
        -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1, -1, -1, -1, -1, -1, -1,
        -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1, -1, -1, -1, -1, -1, -1,
        -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1, -1, -1, -1, -1, -1, -1,
         0,   1,   2,   3,   4,   5,   6,   7,   8,   9, -1, -1, -1, -1, -1, -1,
        -1,  10,  11,  12,  13,  14,  15,  -1,  -1,  -1, -1, -1, -1, -1, -1, -1,
        -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1, -1, -1, -1, -1, -1, -1,
        -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1, -1, -1, -1, -1, -1, -1,
        -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1, -1, -1, -1, -1, -1, -1,
        -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1, -1, -1, -1, -1, -1, -1,
        -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1, -1, -1, -1, -1, -1, -1,
        -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1, -1, -1, -1, -1, -1, -1,
        -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1, -1, -1, -1, -1, -1, -1,
        -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1, -1, -1, -1, -1, -1, -1,
        -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1, -1, -1, -1, -1, -1, -1,
        -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1, -1, -1, -1, -1, -1, -1,
        -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1, -1, -1, -1, -1, -1, -1,
    ];

    #[rustfmt::skip]
    static HEX_MSB_UPPER: [i16; 256] = [
        -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1, -1, -1, -1, -1, -1, -1,
        -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1, -1, -1, -1, -1, -1, -1,
        -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1, -1, -1, -1, -1, -1, -1,
         0,  16,  32,  48,  64,  80,  96, 112, 128, 144, -1, -1, -1, -1, -1, -1,
        -1, 160, 176, 192, 208, 224, 240,  -1,  -1,  -1, -1, -1, -1, -1, -1, -1,
        -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1, -1, -1, -1, -1, -1, -1,
        -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1, -1, -1, -1, -1, -1, -1,
        -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1, -1, -1, -1, -1, -1, -1,
        -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1, -1, -1, -1, -1, -1, -1,
        -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1, -1, -1, -1, -1, -1, -1,
        -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1, -1, -1, -1, -1, -1, -1,
        -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1, -1, -1, -1, -1, -1, -1,
        -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1, -1, -1, -1, -1, -1, -1,
        -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1, -1, -1, -1, -1, -1, -1,
        -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1, -1, -1, -1, -1, -1, -1,
        -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1,  -1, -1, -1, -1, -1, -1, -1,
    ];

    parse_(a, b, &HEX_LSB_UPPER, &HEX_MSB_UPPER)
}

/// Safety: The values in lut's must be in range of `i16::MIN..256`
#[allow(clippy::inline_always, clippy::similar_names)]
#[inline(always)]
fn parse_(a: u8, b: u8, lut_lsb: &[i16; 256], lut_msb: &[i16; 256]) -> Option<u8> {
    let v = lut_msb[a as usize] | lut_lsb[b as usize];
    if v < 0 {
        None
    } else {
        #[allow(clippy::cast_possible_truncation)]
        #[allow(clippy::cast_sign_loss)]
        Some(v as u8)
    }
}

pub fn to_hex_lower(v: u8) -> [u8; 2] {
    let helper = |v: u8| -> u8 {
        match v {
            v @ 0..=9 => v + b'0',
            v @ 10..=15 => v - 10 + b'a',
            _ => unreachable!(),
        }
    };

    let a = (v & 0xf0) >> 4;
    let b = v & 0x0f;

    [helper(a), helper(b)]
}

pub fn to_hex_upper(v: u8) -> [u8; 2] {
    let helper = |v: u8| -> u8 {
        match v {
            v @ 0..=9 => v + b'0',
            v @ 10..=15 => v - 10 + b'A',
            _ => unreachable!(),
        }
    };

    let a = (v & 0xf0) >> 4;
    let b = v & 0x0f;

    [helper(a), helper(b)]
}

#[cfg(test)]
mod tests {
    fn to_lower(v: u8) -> Option<u8> {
        match v {
            0..=9 => Some(b'0' + v),
            10..=15 => Some(b'a' + v - 10),
            _ => None,
        }
    }

    fn to_upper(v: u8) -> Option<u8> {
        match v {
            0..=9 => Some(b'0' + v),
            10..=15 => Some(b'A' + v - 10),
            _ => None,
        }
    }

    #[test]
    fn parse() {
        for i in 0..16 {
            for j in 0..16 {
                let lower_a = to_lower(i).unwrap();
                let lower_b = to_lower(j).unwrap();

                let v = super::parse(lower_a, lower_b).unwrap();
                assert_eq!(v, i * 16 + j);

                let upper_a = to_upper(i).unwrap();
                let upper_b = to_upper(j).unwrap();

                let v = super::parse(upper_a, upper_b).unwrap();
                assert_eq!(v, i * 16 + j);
            }
        }
    }

    #[test]
    fn parse_lower() {
        for i in 0..16 {
            for j in 0..16 {
                let lower_a = to_lower(i).unwrap();
                let lower_b = to_lower(j).unwrap();

                let v = super::parse_lower(lower_a, lower_b).unwrap();
                assert_eq!(v, i * 16 + j);

                let upper_a = to_upper(i).unwrap();
                let upper_b = to_upper(j).unwrap();

                let v = super::parse_lower(upper_a, upper_b);
                if i < 10 && j < 10 {
                    // if we're dealing with `0..=9` then it'll parse correctly
                    assert_eq!(v.unwrap(), i * 16 + j);
                } else {
                    assert!(v.is_none());
                }
            }
        }
    }

    #[test]
    fn parse_upper() {
        for i in 0..16 {
            for j in 0..16 {
                let lower_a = to_lower(i).unwrap();
                let lower_b = to_lower(j).unwrap();

                let v = super::parse_upper(lower_a, lower_b);
                if i < 10 && j < 10 {
                    // if we're dealing with `0..=9` then it'll parse correctly
                    assert_eq!(v.unwrap(), i * 16 + j);
                } else {
                    assert!(v.is_none());
                }

                let upper_a = to_upper(i).unwrap();
                let upper_b = to_upper(j).unwrap();

                let v = super::parse_upper(upper_a, upper_b).unwrap();
                assert_eq!(v, i * 16 + j);
            }
        }
    }
}