module Foundation.Hashing.SipHash
( SipKey(..)
, SipHash(..)
, Sip1_3
, Sip2_4
) where
import Data.Bits
import Basement.Compat.Base
import Basement.Types.OffsetSize
import Basement.PrimType
import Basement.Cast (cast)
import Basement.IntegralConv
import Foundation.Hashing.Hasher
import qualified Basement.UArray as A
import Foundation.Array
import Foundation.Numerical
import Foundation.Bits
import qualified Prelude
import GHC.ST
import GHC.Prim
data SipKey = SipKey !Word64
!Word64
newtype SipHash = SipHash Word64
deriving (Show,Eq,Ord)
newtype Sip2_4 = Sip2_4 Sip
newtype Sip1_3 = Sip1_3 Sip
instance Hasher Sip2_4 where
type HashResult Sip2_4 = SipHash
type HashInitParam Sip2_4 = SipKey
hashNew = Sip2_4 $ newSipState (SipKey 0 0)
hashNewParam key = Sip2_4 $ newSipState key
hashEnd (Sip2_4 st) = finish 2 4 st
hashMix8 w (Sip2_4 st) = Sip2_4 $ mix8 2 w st
hashMix32 w (Sip2_4 st) = Sip2_4 $ mix32 2 w st
hashMix64 w (Sip2_4 st) = Sip2_4 $ mix64 2 w st
hashMixBytes ba (Sip2_4 st) = Sip2_4 $ mixBa 2 ba st
instance Hasher Sip1_3 where
type HashResult Sip1_3 = SipHash
type HashInitParam Sip1_3 = SipKey
hashNew = Sip1_3 $ newSipState (SipKey 0 0)
hashNewParam key = Sip1_3 $ newSipState key
hashEnd (Sip1_3 st) = finish 1 3 st
hashMix8 w (Sip1_3 st) = Sip1_3 $ mix8 1 w st
hashMix32 w (Sip1_3 st) = Sip1_3 $ mix32 1 w st
hashMix64 w (Sip1_3 st) = Sip1_3 $ mix64 1 w st
hashMixBytes ba (Sip1_3 st) = Sip1_3 $ mixBa 1 ba st
data Sip = Sip !InternalState !SipIncremental !(CountOf Word8)
data InternalState = InternalState !Word64
!Word64
!Word64
!Word64
data SipIncremental =
SipIncremental0
| SipIncremental1 !Word64
| SipIncremental2 !Word64
| SipIncremental3 !Word64
| SipIncremental4 !Word64
| SipIncremental5 !Word64
| SipIncremental6 !Word64
| SipIncremental7 !Word64
newSipState :: SipKey -> Sip
newSipState (SipKey k0 k1) = Sip st SipIncremental0 0
where
st = InternalState (k0 `xor` 0x736f6d6570736575)
(k1 `xor` 0x646f72616e646f6d)
(k0 `xor` 0x6c7967656e657261)
(k1 `xor` 0x7465646279746573)
mix8Prim :: Int -> Word8 -> InternalState -> SipIncremental -> (InternalState, SipIncremental)
mix8Prim !c !w !ist !incremental =
case incremental of
SipIncremental7 acc -> (process c ist ((acc `unsafeShiftL` 8) .|. Prelude.fromIntegral w), SipIncremental0)
SipIncremental6 acc -> doAcc SipIncremental7 acc
SipIncremental5 acc -> doAcc SipIncremental6 acc
SipIncremental4 acc -> doAcc SipIncremental5 acc
SipIncremental3 acc -> doAcc SipIncremental4 acc
SipIncremental2 acc -> doAcc SipIncremental3 acc
SipIncremental1 acc -> doAcc SipIncremental2 acc
SipIncremental0 -> (ist, SipIncremental1 $ Prelude.fromIntegral w)
where
doAcc constr acc =
(ist , constr ((acc .<<. 8) .|. Prelude.fromIntegral w))
mix8 :: Int -> Word8 -> Sip -> Sip
mix8 !c !w (Sip ist incremental len) =
case incremental of
SipIncremental7 acc -> Sip (process c ist ((acc .<<. 8) .|. Prelude.fromIntegral w))
SipIncremental0 (len+1)
SipIncremental6 acc -> doAcc SipIncremental7 acc
SipIncremental5 acc -> doAcc SipIncremental6 acc
SipIncremental4 acc -> doAcc SipIncremental5 acc
SipIncremental3 acc -> doAcc SipIncremental4 acc
SipIncremental2 acc -> doAcc SipIncremental3 acc
SipIncremental1 acc -> doAcc SipIncremental2 acc
SipIncremental0 -> Sip ist (SipIncremental1 $ Prelude.fromIntegral w) (len+1)
where
doAcc constr acc =
Sip ist (constr ((acc .<<. 8) .|. Prelude.fromIntegral w)) (len+1)
mix32 :: Int -> Word32 -> Sip -> Sip
mix32 !c !w (Sip ist incremental len) =
case incremental of
SipIncremental0 -> Sip ist (SipIncremental4 $ Prelude.fromIntegral w) (len+4)
SipIncremental1 acc -> consume acc 32 SipIncremental5
SipIncremental2 acc -> consume acc 32 SipIncremental6
SipIncremental3 acc -> consume acc 32 SipIncremental7
SipIncremental4 acc -> Sip (process c ist ((acc .<<. 32) .|. Prelude.fromIntegral w)) SipIncremental0 (len+4)
SipIncremental5 acc -> consumeProcess acc 24 8 SipIncremental1
SipIncremental6 acc -> consumeProcess acc 16 16 SipIncremental2
SipIncremental7 acc -> consumeProcess acc 8 24 SipIncremental3
where
consume acc n constr =
Sip ist (constr ((acc .<<. n) .&. Prelude.fromIntegral w)) (len+4)
consumeProcess acc n x constr =
Sip (process c ist ((acc .<<. n) .|. (Prelude.fromIntegral w .>>. x)))
(constr (Prelude.fromIntegral w .&. andMask64 n))
(len+4)
mix64 :: Int -> Word64 -> Sip -> Sip
mix64 !c !w (Sip ist incremental len) =
case incremental of
SipIncremental0 -> Sip (process c ist w) SipIncremental0 (len+8)
SipIncremental1 acc -> consume acc 56 8 SipIncremental1
SipIncremental2 acc -> consume acc 48 16 SipIncremental2
SipIncremental3 acc -> consume acc 40 24 SipIncremental3
SipIncremental4 acc -> consume acc 32 32 SipIncremental4
SipIncremental5 acc -> consume acc 24 40 SipIncremental5
SipIncremental6 acc -> consume acc 16 48 SipIncremental6
SipIncremental7 acc -> consume acc 8 56 SipIncremental7
where
consume acc n x constr =
Sip (process c ist ((acc .<<. n) .|. ((w .>>. x) .&. andMask64 n)))
(constr $ acc .&. andMask64 x)
(len+8)
finish :: Int -> Int -> Sip -> SipHash
finish !c !d (Sip ist incremental (CountOf len)) = finalize d $
case incremental of
SipIncremental0 -> process c ist lenMask
SipIncremental1 acc -> process c ist (lenMask .|. acc)
SipIncremental2 acc -> process c ist (lenMask .|. acc)
SipIncremental3 acc -> process c ist (lenMask .|. acc)
SipIncremental4 acc -> process c ist (lenMask .|. acc)
SipIncremental5 acc -> process c ist (lenMask .|. acc)
SipIncremental6 acc -> process c ist (lenMask .|. acc)
SipIncremental7 acc -> process c ist (lenMask .|. acc)
where
lenMask = (wlen .&. 0xff) .<<. 56
wlen = cast (integralUpsize len :: Int64) :: Word64
mixBa :: PrimType a => Int -> UArray a -> Sip -> Sip
mixBa !c !array (Sip initSt initIncr currentLen) =
A.unsafeDewrap goVec goAddr array8
where
totalLen = A.length array8
array8 = A.unsafeRecast array
goVec :: ByteArray# -> Offset Word8 -> Sip
goVec ba start = loop8 initSt initIncr start totalLen
where
loop8 !st !incr _ 0 = Sip st incr (currentLen + totalLen)
loop8 !st SipIncremental0 !ofs !l = case l 8 of
Nothing -> loop1 st SipIncremental0 ofs l
Just l8 ->
let v = to64 56 (primBaIndex ba ofs)
.|. to64 48 (primBaIndex ba (ofs + Offset 1))
.|. to64 40 (primBaIndex ba (ofs + Offset 2))
.|. to64 32 (primBaIndex ba (ofs + Offset 3))
.|. to64 24 (primBaIndex ba (ofs + Offset 4))
.|. to64 16 (primBaIndex ba (ofs + Offset 5))
.|. to64 8 (primBaIndex ba (ofs + Offset 6))
.|. to64 0 (primBaIndex ba (ofs + Offset 7))
in loop8 (process c st v) SipIncremental0 (start + Offset 8) l8
loop8 !st !incr !ofs !l = loop1 st incr ofs l
loop1 !st !incr !ofs !l = case l 1 of
Nothing -> Sip st incr (currentLen + totalLen)
Just l1 -> let (!st', !incr') = mix8Prim c (primBaIndex ba ofs) st incr
in loop1 st' incr' (ofs + Offset 1) l1
to64 :: Int -> Word8 -> Word64
to64 0 !v = Prelude.fromIntegral v
to64 !s !v = Prelude.fromIntegral v .<<. s
goAddr :: Ptr Word8 -> Offset Word8 -> ST s Sip
goAddr (Ptr ptr) start = return $ loop8 initSt initIncr start totalLen
where
loop8 !st !incr _ 0 = Sip st incr (currentLen + totalLen)
loop8 !st SipIncremental0 !ofs !l = case l 8 of
Nothing -> loop1 st SipIncremental0 ofs l
Just l8 ->
let v = to64 56 (primAddrIndex ptr ofs)
.|. to64 48 (primAddrIndex ptr (ofs + Offset 1))
.|. to64 40 (primAddrIndex ptr (ofs + Offset 2))
.|. to64 32 (primAddrIndex ptr (ofs + Offset 3))
.|. to64 24 (primAddrIndex ptr (ofs + Offset 4))
.|. to64 16 (primAddrIndex ptr (ofs + Offset 5))
.|. to64 8 (primAddrIndex ptr (ofs + Offset 6))
.|. to64 0 (primAddrIndex ptr (ofs + Offset 7))
in loop8 (process c st v) SipIncremental0 (start + Offset 8) l8
loop8 !st !incr !ofs !l = loop1 st incr ofs l
loop1 !st !incr !ofs !l = case l 1 of
Nothing -> Sip st incr (currentLen + totalLen)
Just l1 -> let (!st', !incr') = mix8Prim c (primAddrIndex ptr ofs) st incr
in loop1 st' incr' (ofs + Offset 1) l1
doRound :: InternalState -> InternalState
doRound (InternalState !v0 !v1 !v2 !v3) =
let !v0' = v0 + v1
!v2' = v2 + v3
!v1' = v1 `rotateL` 13
!v3' = v3 `rotateL` 16
!v1'' = v1' `xor` v0'
!v3'' = v3' `xor` v2'
!v0'' = v0' `rotateL` 32
!v2'' = v2' + v1''
!v0''' = v0'' + v3''
!v1''' = v1'' `rotateL` 17
!v3''' = v3'' `rotateL` 21
!v1'''' = v1''' `xor` v2''
!v3'''' = v3''' `xor` v0'''
!v2''' = v2'' `rotateL` 32
in InternalState v0''' v1'''' v2''' v3''''
process :: Int -> InternalState -> Word64 -> InternalState
process !c !istate !m = postInject $! runRoundsCompression $! preInject istate
where
preInject (InternalState v0 v1 v2 v3) = InternalState v0 v1 v2 (v3 `xor` m)
postInject (InternalState v0 v1 v2 v3) = InternalState (v0 `xor` m) v1 v2 v3
runRoundsCompression st
| c == 2 = doRound $! doRound st
| otherwise = loopRounds c st
finalize :: Int -> InternalState -> SipHash
finalize !d !istate = getDigest $! runRoundsDigest $! preInject istate
where
getDigest (InternalState v0 v1 v2 v3) = SipHash (v0 `xor` v1 `xor` v2 `xor` v3)
preInject (InternalState v0 v1 v2 v3) = InternalState v0 v1 (v2 `xor` 0xff) v3
runRoundsDigest st
| d == 4 = doRound $! doRound $! doRound $! doRound st
| otherwise = loopRounds d st
loopRounds :: Int -> InternalState -> InternalState
loopRounds 1 !v = doRound v
loopRounds n !v = loopRounds (n1) (doRound v)
andMask64 :: Int -> Word64
andMask64 64 = 0xffffffffffffffff
andMask64 56 = 0x00ffffffffffffff
andMask64 48 = 0x0000ffffffffffff
andMask64 40 = 0x000000ffffffffff
andMask64 32 = 0x00000000ffffffff
andMask64 24 = 0x0000000000ffffff
andMask64 16 = 0x000000000000ffff
andMask64 8 = 0x00000000000000ff
andMask64 n = (1 .<<. n) (1 :: Word64)