const HIGH: u64 = 0x8080_8080_8080_8080;
#[inline(always)]
pub(super) fn word(buf: &[u8], at: usize) -> [u8; 8] {
let mut word = [0; 8];
word.copy_from_slice(&buf[at..at + 8]);
word
}
#[inline]
pub(super) fn dec8(word: [u8; 8]) -> Option<u32> {
let v = u64::from_le_bytes(word);
let over = v.wrapping_add(0x4646_4646_4646_4646);
let digits = v.wrapping_sub(0x3030_3030_3030_3030);
if (over | digits) & HIGH != 0 {
return None;
}
const MASK: u64 = 0x0000_00ff_0000_00ff;
const MUL1: u64 = 0x000f_4240_0000_0064;
const MUL2: u64 = 0x0000_2710_0000_0001;
let v = (digits * 10) + (digits >> 8);
let v1 = (v & MASK).wrapping_mul(MUL1);
let v2 = ((v >> 16) & MASK).wrapping_mul(MUL2);
Some((v1.wrapping_add(v2) >> 32) as u32)
}
#[inline]
pub(super) fn hex8(word: [u8; 8]) -> Option<u32> {
const fn at_least(v: u64, n: u64) -> u64 {
const ONES: u64 = 0x0101_0101_0101_0101;
(v | HIGH).wrapping_sub(n * ONES) & HIGH
}
let v = u64::from_be_bytes(word);
let folded = v | 0x2020_2020_2020_2020;
let ascii = !v & HIGH;
let at_least_zero = at_least(v, 0x30);
let past_nine = at_least(folded, 0x3a);
let letter = at_least(folded, 0x61) & !at_least(folded, 0x67);
if ascii & at_least_zero & (!past_nine | letter) & HIGH != HIGH {
return None;
}
let nibbles = (v & 0x0f0f_0f0f_0f0f_0f0f) + ((v & 0x4040_4040_4040_4040) >> 6) * 9;
let v = (nibbles | (nibbles >> 4)) & 0x00ff_00ff_00ff_00ff;
let v = (v | (v >> 8)) & 0x0000_ffff_0000_ffff;
Some((v | (v >> 16)) as u32)
}
#[cfg(test)]
mod tests {
use super::{dec8, hex8, word};
fn reference(word: [u8; 8], radix: u32) -> Option<u32> {
let mut value = 0u32;
for b in word {
value = value * radix + (b as char).to_digit(radix)?;
}
Some(value)
}
#[test]
fn every_byte_in_every_position() {
for at in 0..8 {
for b in 0..=u8::MAX {
let mut w = *b"12345678";
w[at] = b;
assert_eq!(dec8(w), reference(w, 10), "decimal {w:?}");
assert_eq!(hex8(w), reference(w, 16), "hexadecimal {w:?}");
let mut w = *b"abcdefAB";
w[at] = b;
assert_eq!(hex8(w), reference(w, 16), "hexadecimal {w:?}");
}
}
}
#[test]
fn words_of_edge_bytes() {
const EDGES: &[u8] = &[
0x00, 0x0f, 0x19, 0x2f, b'0', b'5', b'9', 0x3a, 0x40, b'A', b'F', b'G', 0x5f, 0x60,
b'a', b'f', b'g', 0x7f, 0x80, 0xb5, 0xf0, 0xff,
];
for a in EDGES {
for b in EDGES {
for i in 0..8 {
for j in 0..8 {
let mut w = *b"0123abcd";
w[i] = *a;
w[j] = *b;
assert_eq!(dec8(w), reference(w, 10), "decimal {w:?}");
assert_eq!(hex8(w), reference(w, 16), "hexadecimal {w:?}");
}
}
}
}
}
#[test]
fn extremes() {
assert_eq!(dec8(*b"00000000"), Some(0));
assert_eq!(dec8(*b"99999999"), Some(99_999_999));
assert_eq!(dec8(*b"12345678"), Some(12_345_678));
assert_eq!(dec8(*b"00000001"), Some(1));
assert_eq!(dec8(*b"10000000"), Some(10_000_000));
assert_eq!(hex8(*b"00000000"), Some(0));
assert_eq!(hex8(*b"ffffffff"), Some(u32::MAX));
assert_eq!(hex8(*b"FFFFFFFF"), Some(u32::MAX));
assert_eq!(hex8(*b"0123abcd"), Some(0x0123_abcd));
assert_eq!(hex8(*b"0123ABCD"), Some(0x0123_abcd));
assert_eq!(hex8(*b"DeadBeef"), Some(0xdead_beef));
assert_eq!(hex8(*b"00000001"), Some(1));
assert_eq!(hex8(*b"10000000"), Some(0x1000_0000));
}
#[test]
fn reading_a_word() {
let buf = b"xx12345678yy";
assert_eq!(word(buf, 2), *b"12345678");
assert_eq!(dec8(word(buf, 2)), Some(12_345_678));
assert_eq!(dec8(word(buf, 0)), None);
}
}