use core::fmt;
use core::fmt::Write as _;
use crate::error::{Error, Result};
const HEX: &[u8; 16] = b"0123456789abcdef";
const HEX_DIGIT: [&str; 16] = [
"0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "a", "b", "c", "d", "e", "f",
];
#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct Digest<const N: usize> {
bytes: [u8; N],
}
impl<const N: usize> Default for Digest<N> {
fn default() -> Self {
Digest { bytes: [0u8; N] }
}
}
impl<const N: usize> Digest<N> {
pub const HEX_LEN: usize = 2 * N;
pub fn from_bytes(bytes: [u8; N]) -> Self {
Digest { bytes }
}
pub fn as_bytes(&self) -> &[u8; N] {
&self.bytes
}
pub fn to_hex_into(self, out: &mut [u8]) -> Result<()> {
if out.len() != Self::HEX_LEN {
return Err(Error::BadValue("hex buffer length"));
}
for (i, &byte) in self.bytes.iter().enumerate() {
out[2 * i] = HEX[usize::from(byte >> 4)];
out[2 * i + 1] = HEX[usize::from(byte & 0x0f)];
}
Ok(())
}
pub fn from_hex(hex: &[u8]) -> Result<Self> {
if hex.len() % 2 != 0 {
return Err(Error::BadValue("hex length is odd"));
}
if hex.len() < 2 * N {
return Err(Error::truncated("hex digest", 2 * N, hex.len()));
}
if hex.len() > 2 * N {
return Err(Error::BadValue("hex digest length"));
}
let mut bytes = [0u8; N];
for (dst, pair) in bytes.iter_mut().zip(hex.chunks_exact(2)) {
*dst = (nibble(pair[0])? << 4) | nibble(pair[1])?;
}
Ok(Digest { bytes })
}
pub fn from_slice(data: &[u8]) -> Result<Self> {
if data.len() < N {
return Err(Error::truncated("digest", N, data.len()));
}
if data.len() > N {
return Err(Error::BadValue("digest length"));
}
let mut bytes = [0u8; N];
bytes.copy_from_slice(data);
Ok(Digest { bytes })
}
pub fn from_stream<R: Read + ?Sized>(stream: &mut R) -> Result<Self> {
let mut bytes = [0u8; N];
let mut filled = 0usize;
while filled < N {
let n = stream.read_some(&mut bytes[filled..]);
if n == 0 {
return Err(Error::truncated("digest", N, filled));
}
filled += n;
}
Ok(Digest { bytes })
}
}
impl<const N: usize> fmt::Display for Digest<N> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let pad = f.width().map_or(0, |w| w.saturating_sub(2 * N));
let fill = f.fill();
let (before, after) = match f.align().unwrap_or(fmt::Alignment::Left) {
fmt::Alignment::Left => (0, pad),
fmt::Alignment::Right => (pad, 0),
fmt::Alignment::Center => (pad / 2, pad - pad / 2),
};
for _ in 0..before {
f.write_char(fill)?;
}
for &byte in self.bytes.iter() {
f.write_str(HEX_DIGIT[usize::from(byte >> 4)])?;
f.write_str(HEX_DIGIT[usize::from(byte & 0x0f)])?;
}
for _ in 0..after {
f.write_char(fill)?;
}
Ok(())
}
}
fn nibble(byte: u8) -> Result<u8> {
match byte {
b'0'..=b'9' => Ok(byte - b'0'),
b'a'..=b'f' => Ok(byte - b'a' + 10),
b'A'..=b'F' => Ok(byte - b'A' + 10),
_ => Err(Error::BadValue("hex digit")),
}
}
pub trait Read {
fn read_some(&mut self, buf: &mut [u8]) -> usize;
}
impl Read for &[u8] {
fn read_some(&mut self, buf: &mut [u8]) -> usize {
let n = self.len().min(buf.len());
buf[..n].copy_from_slice(&self[..n]);
*self = &self[n..];
n
}
}