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
use wolf_crypto_sys::{
    Blake2b as wc_Blake2b, wc_InitBlake2b, wc_Blake2bUpdate, wc_Blake2bFinal
};
use core::mem::MaybeUninit;
use core::ptr::addr_of_mut;
use crate::opaque_res::Res;
use crate::{const_lte, const_can_cast_u32, can_cast_u32, gte, const_gte};

#[repr(transparent)]
pub struct Blake2b<const C: usize> {
    inner: wc_Blake2b
}

impl<const C: usize> Blake2b<C> {
    pub fn new() -> Result<Self, ()> {
        if !const_lte::<C, 64>() { return Err(()); }
        let mut res = Res::new();

        unsafe {
            let mut inner = MaybeUninit::<wc_Blake2b>::uninit();
            res.ensure_0(wc_InitBlake2b(inner.as_mut_ptr(), C as u32));
            res.unit_err_with(|| Self { inner: inner.assume_init() })
        }
    }

    #[inline]
    pub unsafe fn update_unchecked(&mut self, data: &[u8]) -> Res {
        let mut res = Res::new();

        res.ensure_0(wc_Blake2bUpdate(
            addr_of_mut!(self.inner),
            data.as_ptr(),
            data.len() as u32
        ));

        res
    }

    #[inline]
    pub fn try_update(&mut self, data: &[u8]) -> Res {
        if !can_cast_u32(data.len()) { return Res::ERR }
        unsafe { self.update_unchecked(data) }
    }

    #[inline]
    pub fn update_sized<const OC: usize>(&mut self, data: &[u8; OC]) -> Res {
        if !const_can_cast_u32::<{ OC }>() { return Res::ERR }
        unsafe { self.update_unchecked(data) }
    }

    #[cfg(feature = "panic-api")]
    #[track_caller]
    pub fn update(&mut self, data: &[u8]) {
        self.try_update(data).unit_err(()).expect("Failed to update hash in `Blake2b`")
    }

    #[inline]
    pub unsafe fn finalize_unchecked(mut self, output: &mut [u8]) -> Res {
        let mut res = Res::new();

        res.ensure_0(wc_Blake2bFinal(
            addr_of_mut!(self.inner),
            output.as_mut_ptr(),
            C as u32
        ));

        res
    }

    #[inline]
    pub fn finalize_into(self, output: &mut [u8]) -> Res {
        if !gte::<C>(output.len()) { return Res::ERR }
        unsafe { self.finalize_unchecked(output) }
    }

    #[inline]
    pub fn finalize_into_sized<const OC: usize>(self, output: &mut [u8; OC]) -> Res {
        if !const_gte::<OC, C>() { return Res::ERR }
        unsafe { self.finalize_unchecked(output) }
    }

    #[inline]
    pub fn finalize_into_exact(self, output: &mut [u8; C]) -> Res {
        unsafe { self.finalize_unchecked(output) }
    }

    #[inline]
    pub fn try_finalize(self) -> Result<[u8; C], ()> {
        let mut buf = [0u8; C];
        self.finalize_into_exact(&mut buf).unit_err(buf)
    }

    #[cfg(feature = "panic-api")]
    #[track_caller]
    pub fn finalize(self) -> [u8; C] {
        self.try_finalize().expect("Failed to finalize in `Blake2b`")
    }
}