libmhash 0.2.1

A file hashing library that can do multiple hashes for multile files at the same time.
Documentation
#![macro_use]

macro_rules! add {
    ("wrapping", $left:expr, $right:expr, $count_type:ty) => {
        $left = $left.wrapping_add($right.wrapping_mul(8));
    };
    ("checked", $left:expr, $right:expr, $count_type:ty) => {
        $left = $right
            .checked_mul(8)
            .and_then(|i| i.checked_add($left))
            .ok_or(Error::DataLengthOverflowed(<$count_type>::MAX as u128))?;
    };
}

macro_rules! from_bytes {
    ("le", $word_type:ty, $data:expr) => {
        <$word_type>::from_le_bytes($data)
    };
    ("be", $word_type:ty, $data:expr) => {
        <$word_type>::from_be_bytes($data)
    };
}

macro_rules! to_bytes {
    ("le", $data:expr) => {
        $data.to_le_bytes()
    };
    ("be", $data:expr) => {
        $data.to_be_bytes()
    };
}

#[macro_export]
macro_rules! transmute_update {
    ( $self:expr, $data:expr, $block_size:expr, $word_type:ty, $count_type:ty, $add_mode:tt, $endian:tt ) => {
        use $crate::Error;

        if $self.is_done {
            return Err(Error::UpdatingAfterFinished);
        }

        if $data.len() % $block_size != 0 {
            return Err(Error::DataLengthMismatched($data.len(), $block_size));
        }

        add!(
            $add_mode,
            $self.count,
            $data.len() as $count_type,
            $count_type
        );

        use $crate::paranoid_hash::hash_helper::slice_as_chunks;

        let block_chunks: &[[u8; $block_size]] = slice_as_chunks($data);

        for block_chunk in block_chunks {
            let value_chunks: &[[u8; size_of::<$word_type>()]] = slice_as_chunks(block_chunk);

            let mut temp_block = [0; $block_size / size_of::<$word_type>()];
            for (value, value_chunk) in temp_block.iter_mut().zip(value_chunks.iter()) {
                *value = from_bytes!($endian, $word_type, *value_chunk);
            }

            $self.update_block(&temp_block);
        }

        return Ok(());
    };
}

#[macro_export]
macro_rules! transmute_update_last {
    ( $self:expr, $data:expr, $block_size:expr, $word_type:ty, $count_type:ty, $add_mode:tt, $endian:tt ) => {
        const PADDING: u8 = 0b1000_0000;
        const PADDING_LENGTH: usize = 1;

        use $crate::Error;

        if $self.is_done {
            return Err(Error::UpdatingAfterFinished);
        }

        if $data.len() > $block_size {
            return Err(Error::DataTooLarge($data.len(), $block_size));
        }

        add!(
            $add_mode,
            $self.count,
            $data.len() as $count_type,
            $count_type
        );

        let mut final_block = [0u8; $block_size * 2];
        final_block[0..$data.len()].clone_from_slice($data);
        final_block[$data.len()] = PADDING;

        let count = to_bytes!($endian, $self.count);

        let final_block_slice = if $data.len() + count.len() + PADDING_LENGTH <= $block_size {
            final_block[$block_size - count.len()..$block_size].clone_from_slice(&count);
            &final_block[0..$block_size]
        } else {
            let length = final_block.len();
            final_block[length - count.len()..].clone_from_slice(&count);
            &final_block[..]
        };

        $self.update(final_block_slice)?;

        let digest_chunks: &mut [[u8; std::mem::size_of::<$word_type>()]] =
            $crate::paranoid_hash::hash_helper::slice_as_chunks_mut(&mut $self.digest);
        for (d, s) in digest_chunks.iter_mut().zip($self.state.iter()) {
            d.clone_from_slice(&to_bytes!($endian, s));
        }

        $self.is_done = true;

        return Ok(());
    };
}