use core::fmt;
#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct Nibble(u8);
impl Nibble {
pub fn zero() -> Self {
Nibble(0)
}
pub fn max() -> Self {
Nibble(15)
}
pub fn checked_add(self, val: u8) -> Option<Self> {
let new_nibble = self.0.checked_add(val)?;
if new_nibble >= 16 {
return None;
}
Some(Nibble(new_nibble))
}
pub fn from_ascii_hex_digit(digit: u8) -> Option<Self> {
if digit.is_ascii_digit() {
Some(Nibble(digit - b'0'))
} else if (b'a'..=b'f').contains(&digit) {
Some(Nibble(10 + digit - b'a'))
} else if (b'A'..=b'F').contains(&digit) {
Some(Nibble(10 + digit - b'A'))
} else {
None
}
}
}
impl TryFrom<u8> for Nibble {
type Error = NibbleFromU8Error;
fn try_from(val: u8) -> Result<Self, Self::Error> {
if val < 16 {
Ok(Nibble(val))
} else {
Err(NibbleFromU8Error::TooLarge)
}
}
}
impl From<Nibble> for u8 {
fn from(nibble: Nibble) -> u8 {
nibble.0
}
}
impl From<Nibble> for usize {
fn from(nibble: Nibble) -> usize {
usize::from(nibble.0)
}
}
impl fmt::LowerHex for Nibble {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
fmt::LowerHex::fmt(&self.0, f)
}
}
impl fmt::UpperHex for Nibble {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
fmt::UpperHex::fmt(&self.0, f)
}
}
impl fmt::Debug for Nibble {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{:x}", self.0)
}
}
#[derive(Debug, derive_more::Display, derive_more::Error)]
pub enum NibbleFromU8Error {
#[display("Value is too large")]
TooLarge,
}
pub fn all_nibbles() -> impl ExactSizeIterator<Item = Nibble> {
(0..16).map(Nibble)
}
pub fn nibbles_to_bytes_suffix_extend<I: Iterator<Item = Nibble>>(
nibbles: I,
) -> impl Iterator<Item = u8> {
struct Iter<I>(I);
impl<I: Iterator<Item = Nibble>> Iterator for Iter<I> {
type Item = u8;
fn next(&mut self) -> Option<u8> {
let n1 = self.0.next()?;
let n2 = self.0.next().unwrap_or(Nibble(0));
let byte = (n1.0 << 4) | n2.0;
Some(byte)
}
fn size_hint(&self) -> (usize, Option<usize>) {
let (min, max) = self.0.size_hint();
fn conv(n: usize) -> usize {
n.saturating_add(1) / 2
}
(conv(min), max.map(conv))
}
}
Iter(nibbles)
}
pub fn nibbles_to_bytes_prefix_extend<I: ExactSizeIterator<Item = Nibble>>(
nibbles: I,
) -> impl ExactSizeIterator<Item = u8> {
struct Iter<I>(I, bool);
impl<I: ExactSizeIterator<Item = Nibble>> Iterator for Iter<I> {
type Item = u8;
fn next(&mut self) -> Option<u8> {
let n1 = if self.1 {
self.1 = false;
Nibble(0)
} else {
self.0.next()?
};
let n2 = self.0.next()?;
let byte = (n1.0 << 4) | n2.0;
Some(byte)
}
fn size_hint(&self) -> (usize, Option<usize>) {
let inner_len = self.0.len();
let len = if self.1 {
debug_assert_eq!(inner_len % 2, 1);
(inner_len / 2) + 1
} else {
debug_assert_eq!(inner_len % 2, 0);
inner_len / 2
};
(len, Some(len))
}
}
impl<I: ExactSizeIterator<Item = Nibble>> ExactSizeIterator for Iter<I> {}
let has_prefix_nibble = (nibbles.len() % 2) != 0;
Iter(nibbles, has_prefix_nibble)
}
pub fn nibbles_to_bytes_truncate<I: Iterator<Item = Nibble>>(
nibbles: I,
) -> impl Iterator<Item = u8> {
struct Iter<I>(I);
impl<I: Iterator<Item = Nibble>> Iterator for Iter<I> {
type Item = u8;
fn next(&mut self) -> Option<u8> {
let n1 = self.0.next()?;
let n2 = self.0.next()?;
let byte = (n1.0 << 4) | n2.0;
Some(byte)
}
fn size_hint(&self) -> (usize, Option<usize>) {
let (min, max) = self.0.size_hint();
fn conv(n: usize) -> usize {
n / 2
}
(conv(min), max.map(conv))
}
}
Iter(nibbles)
}
pub fn bytes_to_nibbles<I>(bytes: I) -> BytesToNibbles<I> {
BytesToNibbles {
inner: bytes,
next: None,
}
}
#[derive(Debug, Copy, Clone)]
pub struct BytesToNibbles<I> {
inner: I,
next: Option<Nibble>,
}
impl<I: Iterator<Item = u8>> Iterator for BytesToNibbles<I> {
type Item = Nibble;
fn next(&mut self) -> Option<Nibble> {
if let Some(next) = self.next.take() {
return Some(next);
}
let byte = self.inner.next()?;
self.next = Some(Nibble(byte & 0xf));
Some(Nibble(byte >> 4))
}
fn size_hint(&self) -> (usize, Option<usize>) {
let (min, max) = self.inner.size_hint();
if self.next.is_some() {
(
min.saturating_mul(2).saturating_add(1),
max.and_then(|max| max.checked_mul(2))
.and_then(|max| max.checked_add(1)),
)
} else {
(
min.saturating_mul(2),
max.and_then(|max| max.checked_mul(2)),
)
}
}
}
impl<I: ExactSizeIterator<Item = u8>> ExactSizeIterator for BytesToNibbles<I> {}
#[cfg(test)]
mod tests {
use super::{Nibble, NibbleFromU8Error, bytes_to_nibbles};
#[test]
fn nibble_try_from() {
assert_eq!(u8::from(Nibble::try_from(0).unwrap()), 0);
assert_eq!(u8::from(Nibble::try_from(1).unwrap()), 1);
assert_eq!(u8::from(Nibble::try_from(15).unwrap()), 15);
assert!(matches!(
Nibble::try_from(16),
Err(NibbleFromU8Error::TooLarge)
));
assert!(matches!(
Nibble::try_from(255),
Err(NibbleFromU8Error::TooLarge)
));
}
#[test]
fn from_ascii_hex_digit_works() {
assert_eq!(u8::from(Nibble::from_ascii_hex_digit(b'0').unwrap()), 0);
assert_eq!(u8::from(Nibble::from_ascii_hex_digit(b'9').unwrap()), 9);
assert_eq!(u8::from(Nibble::from_ascii_hex_digit(b'a').unwrap()), 10);
assert_eq!(u8::from(Nibble::from_ascii_hex_digit(b'f').unwrap()), 15);
assert_eq!(u8::from(Nibble::from_ascii_hex_digit(b'A').unwrap()), 10);
assert_eq!(u8::from(Nibble::from_ascii_hex_digit(b'F').unwrap()), 15);
assert!(Nibble::from_ascii_hex_digit(b'j').is_none());
assert!(Nibble::from_ascii_hex_digit(b' ').is_none());
assert!(Nibble::from_ascii_hex_digit(0).is_none());
assert!(Nibble::from_ascii_hex_digit(255).is_none());
}
#[test]
fn bytes_to_nibbles_works() {
assert_eq!(
bytes_to_nibbles([].iter().cloned()).collect::<Vec<_>>(),
&[]
);
assert_eq!(
bytes_to_nibbles([1].iter().cloned()).collect::<Vec<_>>(),
&[Nibble::try_from(0).unwrap(), Nibble::try_from(1).unwrap()]
);
assert_eq!(
bytes_to_nibbles([200].iter().cloned()).collect::<Vec<_>>(),
&[
Nibble::try_from(0xc).unwrap(),
Nibble::try_from(0x8).unwrap()
]
);
assert_eq!(
bytes_to_nibbles([80, 200, 9].iter().cloned()).collect::<Vec<_>>(),
&[
Nibble::try_from(5).unwrap(),
Nibble::try_from(0).unwrap(),
Nibble::try_from(0xc).unwrap(),
Nibble::try_from(0x8).unwrap(),
Nibble::try_from(0).unwrap(),
Nibble::try_from(9).unwrap()
]
);
}
#[test]
fn bytes_to_nibbles_len() {
assert_eq!(bytes_to_nibbles([].iter().cloned()).len(), 0);
assert_eq!(bytes_to_nibbles([1].iter().cloned()).len(), 2);
assert_eq!(bytes_to_nibbles([200].iter().cloned()).len(), 2);
assert_eq!(
bytes_to_nibbles([1, 2, 3, 4, 5, 6].iter().cloned()).len(),
12
);
}
}