1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
#![no_std]

use core::cmp;
use byteorder::{ ByteOrder, LittleEndian };
use gimli_permutation::{ S, gimli };


pub const RATE: usize = 16;


#[derive(Clone)]
pub struct GimliHash {
    state: [u32; S],
    pos: usize
}

impl Default for GimliHash {
    fn default() -> Self {
        GimliHash { state: [0; S], pos: 0 }
    }
}

impl GimliHash {
    #[inline]
    pub fn update(&mut self, buf: &[u8]) {
        self.absorb(buf);
    }

    #[inline]
    pub fn finalize(self, buf: &mut [u8]) {
        self.xof().squeeze(buf);
    }

    #[inline]
    pub fn xof(mut self) -> XofReader {
        self.pad();
        XofReader { state: self.state, pos: 0 }
    }

    fn absorb(&mut self, buf: &[u8]) {
        let GimliHash { state, pos } = self;

        let mut start = 0;
        let mut len = buf.len();

        while len > 0 {
            let take = cmp::min(RATE - *pos, len);

            with(state, |state| {
                for (dst, &src) in state[*pos..][..take].iter_mut()
                    .zip(&buf[start..][..take])
                {
                    *dst ^= src;
                }
                *pos += take;
                start += take;
                len -= take;
            });

            if *pos == RATE {
                gimli(state);
                *pos = 0;
            }
        }
    }

    fn pad(&mut self) {
        let &mut GimliHash { ref mut state, pos } = self;

        with(state, |state| {
            state[pos] ^= 0x1f;
            state[RATE - 1] ^= 0x80;
        });
        gimli(state);
    }
}


pub struct XofReader {
    state: [u32; S],
    pos: usize
}

impl XofReader {
    pub fn squeeze(&mut self, buf: &mut [u8]) {
        let XofReader { state, pos } = self;

        let take = cmp::min(RATE - *pos, buf.len());
        let (prefix, buf) = buf.split_at_mut(take);

        if !prefix.is_empty() {
            with(state, |state| {
                prefix.copy_from_slice(&state[*pos..][..take]);
                *pos += take;
            });

            if *pos == RATE {
                gimli(state);
                *pos = 0;
            }
        }

        for chunk in buf.chunks_mut(RATE) {
            let take = chunk.len();
            with(state, |state| {
                chunk.copy_from_slice(&state[*pos..][..take]);
            });

            if *pos == RATE {
                gimli(state);
            } else {
                *pos += take;
            }
        }
    }
}

fn with<F>(state: &mut [u32; S], f: F)
    where F: FnOnce(&mut [u8; S * 4])
{
    #[inline]
    fn transmute(arr: &mut [u32; S]) -> &mut [u8; S * 4] {
        unsafe { &mut *(arr as *mut [u32; S] as *mut [u8; S * 4]) }
    }

    LittleEndian::from_slice_u32(state);
    f(transmute(state));
    LittleEndian::from_slice_u32(state);
}