zest4096 0.1.27

ChaCha based hash for faster, larger blocks
Documentation
use std::num::Wrapping;
use std::ops::{BitXor, BitXorAssign, Index, IndexMut};

#[derive(Clone, Copy, PartialEq, Debug)]
pub struct ZestBlock {
    pub data: [u64; 64],
}
impl ZestBlock {
    //Blank construction. See "From"
    #[inline(always)]
    pub fn new() -> ZestBlock {
        ZestBlock { data: [0_u64; 64] }
    }
    //Construction with least significant u64 set:
    #[inline(always)]
    pub fn from_u64(least_sig: u64) -> ZestBlock {
        let mut data = [0_u64; 64];
        data[63] = least_sig;
        ZestBlock { data }
    }
    //Increment Block as if it were a number, rightmost bytes least significant.
    #[inline(always)]
    pub fn increment_block(&mut self) {
        let mut carry: u64 = 1;
        for elem in self.data.iter_mut().rev() {
            *elem = (Wrapping(*elem) + Wrapping(carry)).0;
            carry = (*elem < carry) as u64;
        }
    }
    //Present the data as big endian (for consistency)
    //Set to consume self to prevent casual use, as this
    //function has overhead
    #[inline(always)]
    pub fn data_u8(self) -> [u8; 512] {
        let mut u8_data = [0_u8; 512];
        for (i, chunk) in u8_data.chunks_exact_mut(8).enumerate() {
            chunk.copy_from_slice(&self.data[i].to_be_bytes());
        }
        u8_data
    }
}

//XOR, bitwise.
impl BitXor<ZestBlock> for ZestBlock {
    type Output = ZestBlock;
    #[inline(always)]
    fn bitxor(self, rhs: ZestBlock) -> Self::Output {
        let mut xored = self;
        for (index, chunk) in xored.data.iter_mut().enumerate() {
            *chunk ^= rhs[index];
        }
        xored
    }
}
impl BitXorAssign<ZestBlock> for ZestBlock {
    #[inline(always)]
    fn bitxor_assign(&mut self, rhs: ZestBlock) {
        for (index, chunk) in self.data.iter_mut().enumerate() {
            *chunk ^= rhs[index];
        }
    }
}

//Indexing
impl Index<usize> for ZestBlock {
    type Output = u64;
    #[inline(always)]
    fn index(&self, index: usize) -> &Self::Output {
        &self.data[index]
    }
}
impl IndexMut<usize> for ZestBlock {
    #[inline(always)]
    fn index_mut(&mut self, index: usize) -> &mut Self::Output {
        &mut self.data[index]
    }
}

//Construction
impl From<[u64; 64]> for ZestBlock {
    #[inline(always)]
    fn from(data: [u64; 64]) -> Self {
        ZestBlock { data }
    }
}

//From big endian (for consistency)
//Use [u64; u64] if possible, due to overhead of conversion
impl From<[u8; 512]> for ZestBlock {
    #[inline(always)]
    fn from(data: [u8; 512]) -> Self {
        // Safety: All values of array are initialized.
        let mut u64_data = [0_u64; 64];
        for (i, chunk) in data.chunks_exact(8).enumerate() {
            u64_data[i] = u64::from_be_bytes(chunk.try_into().unwrap());
        }
        ZestBlock { data: u64_data }
    }
}

// Default
impl Default for ZestBlock {
    #[inline(always)]
    fn default() -> Self {
        ZestBlock::new()
    }
}

//Functions for messing with arrays of blocks:
#[inline(always)]
pub fn bytes_to_blocks(bytes_input: Vec<u8>) -> Vec<ZestBlock> {
    let mut blocks: Vec<ZestBlock> = vec![];
    for block in bytes_input.chunks_exact(512) {
        let block_arr: [u8; 512] = block.try_into().unwrap();
        blocks.push(block_arr.into());
    }
    blocks
}
#[inline(always)]
pub fn blocks_to_bytes(blocks_input: Vec<ZestBlock>) -> Vec<u8> {
    let mut blocks: Vec<u8> = vec![];
    for block in blocks_input {
        blocks.append(&mut block.data_u8().to_vec())
    }
    blocks
}