    module Foundation.Hashing.Hasher
        ( Hasher(..)
        ) where

    import           Basement.Compat.Base
    import           Basement.IntegralConv
    import           Foundation.Array (UArray)
    import qualified Basement.UArray as A
    import           Data.Bits

    -- | Incremental Hashing state. Represent an hashing algorithm
    --
    -- the base primitive of this class is `hashMix8`, append
    -- mix a Word8 in the state
    --
    -- The class allow to define faster mixing function that works on
    -- bigger Word size and any unboxed array of any PrimType elements
    class Hasher st where
        {-# MINIMAL hashNew, hashNewParam, hashMix8, hashEnd #-}

        -- | Associate type when finalizing the state with 'hashEnd'
        type HashResult st

        -- | Associate type when initializing the state (e.g. a Key or seed)
        type HashInitParam st

        -- | Create a new Hashing context
        hashNew :: st

        -- | Create a new Hashing context
        hashNewParam :: HashInitParam st -> st

        -- | Finalize the state and returns the hash result
        hashEnd :: st -> HashResult st

        -- | Mix a Word8 (Byte) into the state and return the new state
        hashMix8  :: Word8  -> st -> st

        -- | Mix a Word16 into the state and return the new state
        hashMix16 :: Word16 -> st -> st
        hashMix16 Word16
    w st
    st = Word8 -> st -> st
    forall st. Hasher st => Word8 -> st -> st
    hashMix8 Word8
    w2 (st -> st) -> st -> st
    forall a b. (a -> b) -> a -> b
    $ Word8 -> st -> st
    forall st. Hasher st => Word8 -> st -> st
    hashMix8 Word8
    w1 st
    st
          where
            !w1 :: Word8
    w1 = Word16 -> Word8
    forall a b. IntegralDownsize a b => a -> b
    integralDownsize (Word16
    w Word16 -> Int -> Word16
    forall a. Bits a => a -> Int -> a
    `unsafeShiftR` Int
    8)
            !w2 :: Word8
    w2 = Word16 -> Word8
    forall a b. IntegralDownsize a b => a -> b
    integralDownsize Word16
    w

        -- | Mix a Word32 into the state and return the new state
        hashMix32 :: Word32 -> st -> st
        hashMix32 Word32
    w st
    st = Word8 -> st -> st
    forall st. Hasher st => Word8 -> st -> st
    hashMix8 Word8
    w4 (st -> st) -> st -> st
    forall a b. (a -> b) -> a -> b
    $ Word8 -> st -> st
    forall st. Hasher st => Word8 -> st -> st
    hashMix8 Word8
    w3 (st -> st) -> st -> st
    forall a b. (a -> b) -> a -> b
    $ Word8 -> st -> st
    forall st. Hasher st => Word8 -> st -> st
    hashMix8 Word8
    w2 (st -> st) -> st -> st
    forall a b. (a -> b) -> a -> b
    $ Word8 -> st -> st
    forall st. Hasher st => Word8 -> st -> st
    hashMix8 Word8
    w1 st
    st
          where
            !w1 :: Word8
    w1 = Word32 -> Word8
    forall a b. IntegralDownsize a b => a -> b
    integralDownsize (Word32
    w Word32 -> Int -> Word32
    forall a. Bits a => a -> Int -> a
    `unsafeShiftR` Int
    24)
            !w2 :: Word8
    w2 = Word32 -> Word8
    forall a b. IntegralDownsize a b => a -> b
    integralDownsize (Word32
    w Word32 -> Int -> Word32
    forall a. Bits a => a -> Int -> a
    `unsafeShiftR` Int
    16)
            !w3 :: Word8
    w3 = Word32 -> Word8
    forall a b. IntegralDownsize a b => a -> b
    integralDownsize (Word32
    w Word32 -> Int -> Word32
    forall a. Bits a => a -> Int -> a
    `unsafeShiftR` Int
    8)
            !w4 :: Word8
    w4 = Word32 -> Word8
    forall a b. IntegralDownsize a b => a -> b
    integralDownsize Word32
    w

        -- | Mix a Word64 into the state and return the new state
        hashMix64 :: Word64 -> st -> st
        hashMix64 Word64
    w st
    st = Word32 -> st -> st
    forall st. Hasher st => Word32 -> st -> st
    hashMix32 Word32
    w2 (st -> st) -> st -> st
    forall a b. (a -> b) -> a -> b
    $ Word32 -> st -> st
    forall st. Hasher st => Word32 -> st -> st
    hashMix32 Word32
    w1 st
    st
          where
            !w1 :: Word32
    w1 = Word64 -> Word32
    forall a b. IntegralDownsize a b => a -> b
    integralDownsize (Word64
    w Word64 -> Int -> Word64
    forall a. Bits a => a -> Int -> a
    `unsafeShiftR` Int
    32)
            !w2 :: Word32
    w2 = Word64 -> Word32
    forall a b. IntegralDownsize a b => a -> b
    integralDownsize Word64
    w

        -- | Mix an arbitrary sized unboxed array and return the new state
        hashMixBytes :: A.PrimType e => UArray e -> st -> st
        hashMixBytes UArray e
    ba st
    st = (st -> Word8 -> st) -> st -> UArray Word8 -> st
    forall ty a. PrimType ty => (a -> ty -> a) -> a -> UArray ty -> a
    A.foldl' ((Word8 -> st -> st) -> st -> Word8 -> st
    forall a b c. (a -> b -> c) -> b -> a -> c
    flip Word8 -> st -> st
    forall st. Hasher st => Word8 -> st -> st
    hashMix8) st
    st (UArray e -> UArray Word8
    forall a b. (PrimType a, PrimType b) => UArray a -> UArray b
    A.unsafeRecast UArray e
    ba)