use crate::compat;
use crate::encoding::binary::{self, ByteOrder};
use crate::hash;
use std::io::Write;
use super::md5block;
pub const SIZE: usize = 16;
pub const BLOCK_SIZE: usize = 64;
pub(super) type State = [u32; 4];
const INIT_STATE: State = [0x67452301, 0xEFCDAB89, 0x98BADCFE, 0x10325476];
#[derive(Copy, Clone)]
pub struct Digest {
s: State,
x: [u8; BLOCK_SIZE],
nx: usize,
len: u64,
}
impl Default for Digest {
fn default() -> Self {
Self::new()
}
}
impl Digest {
pub fn new() -> Self {
Self {
s: INIT_STATE,
x: [0; BLOCK_SIZE],
nx: 0,
len: 0,
}
}
fn checksum(&mut self) -> [u8; SIZE] {
let mut tmp = [0_u8; 1 + 63 + 8];
tmp[0] = 0x80;
let pad = ((55_u64.wrapping_sub(self.len)) % 64) as usize; binary::LITTLE_ENDIAN.put_uint64(&mut tmp[1 + pad..], self.len << 3); _ = self.write(&tmp[..(1 + pad + 8)]);
if self.nx != 0 {
panic!("self.nx != 0");
}
let mut digest = [0_u8; SIZE];
binary::LITTLE_ENDIAN.put_uint32(&mut digest[0..], self.s[0]);
binary::LITTLE_ENDIAN.put_uint32(&mut digest[4..], self.s[1]);
binary::LITTLE_ENDIAN.put_uint32(&mut digest[8..], self.s[2]);
binary::LITTLE_ENDIAN.put_uint32(&mut digest[12..], self.s[3]);
digest
}
}
impl hash::Hash for Digest {
fn reset(&mut self) {
self.s = INIT_STATE;
self.nx = 0;
self.len = 0;
}
fn size(&self) -> usize {
SIZE
}
fn block_size(&self) -> usize {
BLOCK_SIZE
}
fn sum(&self, b: &[u8]) -> Vec<u8> {
let mut d0 = *self;
let hash = d0.checksum();
let mut res = b.to_vec();
res.extend_from_slice(&hash);
res
}
}
impl std::io::Write for Digest {
fn write(&mut self, buf: &[u8]) -> std::io::Result<usize> {
let nn = buf.len();
let mut buf = buf;
self.len += nn as u64;
if self.nx > 0 {
let n = compat::copy(&mut self.x[self.nx..], buf);
self.nx += n;
if self.nx == BLOCK_SIZE {
md5block::block_generic(&mut self.s, &self.x);
self.nx = 0
}
buf = &buf[n..];
}
if buf.len() >= BLOCK_SIZE {
let n = buf.len() & !(BLOCK_SIZE - 1);
md5block::block_generic(&mut self.s, &buf[..n]);
buf = &buf[n..];
}
if !buf.is_empty() {
self.nx = compat::copy(&mut self.x, buf);
}
Ok(nn)
}
fn flush(&mut self) -> std::io::Result<()> {
Ok(())
}
}
pub fn sum(data: &[u8]) -> [u8; SIZE] {
let mut d = Digest::new();
_ = d.write(data);
d.checksum()
}