{-# OPTIONS_HADDOCK hide #-}
{-# LANGUAGE BangPatterns #-}
{-# LANGUAGE MagicHash #-}
{-# LANGUAGE PatternSynonyms #-}
{-# LANGUAGE UnboxedTuples #-}

-- |
-- Module: Crypto.Hash.SHA256.Arm
-- Copyright: (c) 2024 Jared Tobin
-- License: MIT
-- Maintainer: Jared Tobin <jared@ppad.tech>
--
-- ARM crypto extension support for SHA-256.

module Crypto.Hash.SHA256.Arm (
    sha256_arm_available
  , hash
  , hmac
  , _hmac_rr
  , _hmac_rsb
  ) where

import qualified Data.ByteString as BS
import qualified Data.ByteString.Internal as BI
import qualified Data.ByteString.Unsafe as BU
import Data.Word (Word8, Word32, Word64)
import Foreign.Marshal.Alloc (allocaBytes)
import Foreign.Ptr (Ptr)
import qualified GHC.Exts as Exts
import qualified GHC.IO (IO(..))
import qualified GHC.Ptr
import Crypto.Hash.SHA256.Internal hiding (update)
import System.IO.Unsafe (unsafeDupablePerformIO)

-- ffi ------------------------------------------------------------------------

foreign import ccall unsafe "sha256_block_arm"
  c_sha256_block :: Ptr Word32 -> Ptr Word32 -> IO ()

foreign import ccall unsafe "sha256_arm_available"
  c_sha256_arm_available :: IO Int

-- utilities ------------------------------------------------------------------

fi :: (Integral a, Num b) => a -> b
fi :: forall a b. (Integral a, Num b) => a -> b
fi = a -> b
forall a b. (Integral a, Num b) => a -> b
fromIntegral
{-# INLINE fi #-}


peek_registers
  :: Ptr Word32
  -> Registers
peek_registers :: Ptr Word32 -> Registers
peek_registers (GHC.Ptr.Ptr Addr#
addr) = Word32#
-> Word32#
-> Word32#
-> Word32#
-> Word32#
-> Word32#
-> Word32#
-> Word32#
-> Registers
R
  (Addr# -> Int# -> Word32#
Exts.indexWord32OffAddr# Addr#
addr Int#
0#)
  (Addr# -> Int# -> Word32#
Exts.indexWord32OffAddr# Addr#
addr Int#
1#)
  (Addr# -> Int# -> Word32#
Exts.indexWord32OffAddr# Addr#
addr Int#
2#)
  (Addr# -> Int# -> Word32#
Exts.indexWord32OffAddr# Addr#
addr Int#
3#)
  (Addr# -> Int# -> Word32#
Exts.indexWord32OffAddr# Addr#
addr Int#
4#)
  (Addr# -> Int# -> Word32#
Exts.indexWord32OffAddr# Addr#
addr Int#
5#)
  (Addr# -> Int# -> Word32#
Exts.indexWord32OffAddr# Addr#
addr Int#
6#)
  (Addr# -> Int# -> Word32#
Exts.indexWord32OffAddr# Addr#
addr Int#
7#)
{-# INLINE peek_registers #-}

poke_block :: Ptr Word32 -> Block -> IO ()
poke_block :: Ptr Word32 -> Block -> IO ()
poke_block
    (GHC.Ptr.Ptr Addr#
addr)
    (B Word32#
w00 Word32#
w01 Word32#
w02 Word32#
w03 Word32#
w04 Word32#
w05 Word32#
w06 Word32#
w07 Word32#
w08 Word32#
w09 Word32#
w10 Word32#
w11 Word32#
w12 Word32#
w13 Word32#
w14 Word32#
w15)
  = (State# RealWorld -> (# State# RealWorld, () #)) -> IO ()
forall a. (State# RealWorld -> (# State# RealWorld, a #)) -> IO a
GHC.IO.IO ((State# RealWorld -> (# State# RealWorld, () #)) -> IO ())
-> (State# RealWorld -> (# State# RealWorld, () #)) -> IO ()
forall a b. (a -> b) -> a -> b
$ \State# RealWorld
s00 ->
      case Addr# -> Int# -> Word32# -> State# RealWorld -> State# RealWorld
forall d. Addr# -> Int# -> Word32# -> State# d -> State# d
Exts.writeWord32OffAddr# Addr#
addr Int#
00# Word32#
w00 State# RealWorld
s00 of { State# RealWorld
s01 ->
      case Addr# -> Int# -> Word32# -> State# RealWorld -> State# RealWorld
forall d. Addr# -> Int# -> Word32# -> State# d -> State# d
Exts.writeWord32OffAddr# Addr#
addr Int#
01# Word32#
w01 State# RealWorld
s01 of { State# RealWorld
s02 ->
      case Addr# -> Int# -> Word32# -> State# RealWorld -> State# RealWorld
forall d. Addr# -> Int# -> Word32# -> State# d -> State# d
Exts.writeWord32OffAddr# Addr#
addr Int#
02# Word32#
w02 State# RealWorld
s02 of { State# RealWorld
s03 ->
      case Addr# -> Int# -> Word32# -> State# RealWorld -> State# RealWorld
forall d. Addr# -> Int# -> Word32# -> State# d -> State# d
Exts.writeWord32OffAddr# Addr#
addr Int#
03# Word32#
w03 State# RealWorld
s03 of { State# RealWorld
s04 ->
      case Addr# -> Int# -> Word32# -> State# RealWorld -> State# RealWorld
forall d. Addr# -> Int# -> Word32# -> State# d -> State# d
Exts.writeWord32OffAddr# Addr#
addr Int#
04# Word32#
w04 State# RealWorld
s04 of { State# RealWorld
s05 ->
      case Addr# -> Int# -> Word32# -> State# RealWorld -> State# RealWorld
forall d. Addr# -> Int# -> Word32# -> State# d -> State# d
Exts.writeWord32OffAddr# Addr#
addr Int#
05# Word32#
w05 State# RealWorld
s05 of { State# RealWorld
s06 ->
      case Addr# -> Int# -> Word32# -> State# RealWorld -> State# RealWorld
forall d. Addr# -> Int# -> Word32# -> State# d -> State# d
Exts.writeWord32OffAddr# Addr#
addr Int#
06# Word32#
w06 State# RealWorld
s06 of { State# RealWorld
s07 ->
      case Addr# -> Int# -> Word32# -> State# RealWorld -> State# RealWorld
forall d. Addr# -> Int# -> Word32# -> State# d -> State# d
Exts.writeWord32OffAddr# Addr#
addr Int#
07# Word32#
w07 State# RealWorld
s07 of { State# RealWorld
s08 ->
      case Addr# -> Int# -> Word32# -> State# RealWorld -> State# RealWorld
forall d. Addr# -> Int# -> Word32# -> State# d -> State# d
Exts.writeWord32OffAddr# Addr#
addr Int#
08# Word32#
w08 State# RealWorld
s08 of { State# RealWorld
s09 ->
      case Addr# -> Int# -> Word32# -> State# RealWorld -> State# RealWorld
forall d. Addr# -> Int# -> Word32# -> State# d -> State# d
Exts.writeWord32OffAddr# Addr#
addr Int#
09# Word32#
w09 State# RealWorld
s09 of { State# RealWorld
s10 ->
      case Addr# -> Int# -> Word32# -> State# RealWorld -> State# RealWorld
forall d. Addr# -> Int# -> Word32# -> State# d -> State# d
Exts.writeWord32OffAddr# Addr#
addr Int#
10# Word32#
w10 State# RealWorld
s10 of { State# RealWorld
s11 ->
      case Addr# -> Int# -> Word32# -> State# RealWorld -> State# RealWorld
forall d. Addr# -> Int# -> Word32# -> State# d -> State# d
Exts.writeWord32OffAddr# Addr#
addr Int#
11# Word32#
w11 State# RealWorld
s11 of { State# RealWorld
s12 ->
      case Addr# -> Int# -> Word32# -> State# RealWorld -> State# RealWorld
forall d. Addr# -> Int# -> Word32# -> State# d -> State# d
Exts.writeWord32OffAddr# Addr#
addr Int#
12# Word32#
w12 State# RealWorld
s12 of { State# RealWorld
s13 ->
      case Addr# -> Int# -> Word32# -> State# RealWorld -> State# RealWorld
forall d. Addr# -> Int# -> Word32# -> State# d -> State# d
Exts.writeWord32OffAddr# Addr#
addr Int#
13# Word32#
w13 State# RealWorld
s13 of { State# RealWorld
s14 ->
      case Addr# -> Int# -> Word32# -> State# RealWorld -> State# RealWorld
forall d. Addr# -> Int# -> Word32# -> State# d -> State# d
Exts.writeWord32OffAddr# Addr#
addr Int#
14# Word32#
w14 State# RealWorld
s14 of { State# RealWorld
s15 ->
      case Addr# -> Int# -> Word32# -> State# RealWorld -> State# RealWorld
forall d. Addr# -> Int# -> Word32# -> State# d -> State# d
Exts.writeWord32OffAddr# Addr#
addr Int#
15# Word32#
w15 State# RealWorld
s15 of { State# RealWorld
s16 ->
      (# State# RealWorld
s16, () #) }}}}}}}}}}}}}}}}
{-# INLINE poke_block #-}

-- update ---------------------------------------------------------------------

update :: Ptr Word32 -> Ptr Word32 -> Block -> IO ()
update :: Ptr Word32 -> Ptr Word32 -> Block -> IO ()
update Ptr Word32
rp Ptr Word32
bp Block
block = do
  Ptr Word32 -> Block -> IO ()
poke_block Ptr Word32
bp Block
block
  Ptr Word32 -> Ptr Word32 -> IO ()
c_sha256_block Ptr Word32
rp Ptr Word32
bp
{-# INLINE update #-}

-- api -----------------------------------------------------------------------

-- | Are ARM +sha2 extensions available?
sha256_arm_available :: Bool
sha256_arm_available :: Bool
sha256_arm_available = IO Int -> Int
forall a. IO a -> a
unsafeDupablePerformIO IO Int
c_sha256_arm_available Int -> Int -> Bool
forall a. Eq a => a -> a -> Bool
/= Int
0
{-# NOINLINE sha256_arm_available #-}

hash
  :: BS.ByteString
  -> BS.ByteString
hash :: ByteString -> ByteString
hash ByteString
m = IO ByteString -> ByteString
forall a. IO a -> a
unsafeDupablePerformIO (IO ByteString -> ByteString) -> IO ByteString -> ByteString
forall a b. (a -> b) -> a -> b
$
  Int -> (Ptr Word32 -> IO ByteString) -> IO ByteString
forall a b. Int -> (Ptr a -> IO b) -> IO b
allocaBytes Int
32 ((Ptr Word32 -> IO ByteString) -> IO ByteString)
-> (Ptr Word32 -> IO ByteString) -> IO ByteString
forall a b. (a -> b) -> a -> b
$ \Ptr Word32
rp ->
  Int -> (Ptr Word32 -> IO ByteString) -> IO ByteString
forall a b. Int -> (Ptr a -> IO b) -> IO b
allocaBytes Int
64 ((Ptr Word32 -> IO ByteString) -> IO ByteString)
-> (Ptr Word32 -> IO ByteString) -> IO ByteString
forall a b. (a -> b) -> a -> b
$ \Ptr Word32
bp -> do
    Ptr Word32 -> Registers -> IO ()
poke_registers Ptr Word32
rp (() -> Registers
iv ())
    Ptr Word32 -> Ptr Word32 -> Word64 -> ByteString -> IO ()
_hash Ptr Word32
rp Ptr Word32
bp Word64
0 ByteString
m
    let !rs :: Registers
rs = Ptr Word32 -> Registers
peek_registers Ptr Word32
rp
    ByteString -> IO ByteString
forall a. a -> IO a
forall (f :: * -> *) a. Applicative f => a -> f a
pure (Registers -> ByteString
cat Registers
rs)

_hash
  :: Ptr Word32    -- ^ register state
  -> Ptr Word32    -- ^ block state
  -> Word64        -- ^ extra prefix length, for padding calculation
  -> BS.ByteString -- ^ input
  -> IO ()
_hash :: Ptr Word32 -> Ptr Word32 -> Word64 -> ByteString -> IO ()
_hash Ptr Word32
rp Ptr Word32
bp Word64
el m :: ByteString
m@(BI.PS ForeignPtr Word8
_ Int
_ Int
l) = do
  Ptr Word32 -> Ptr Word32 -> ByteString -> IO ()
hash_blocks Ptr Word32
rp Ptr Word32
bp ByteString
m
  let !fin :: ByteString
fin@(BI.PS ForeignPtr Word8
_ Int
_ Int
ll) = Int -> ByteString -> ByteString
BU.unsafeDrop (Int
l Int -> Int -> Int
forall a. Num a => a -> a -> a
- Int
l Int -> Int -> Int
forall a. Integral a => a -> a -> a
`rem` Int
64) ByteString
m
      !total :: Word64
total = Word64
el Word64 -> Word64 -> Word64
forall a. Num a => a -> a -> a
+ Int -> Word64
forall a b. (Integral a, Num b) => a -> b
fi Int
l
  if   Int
ll Int -> Int -> Bool
forall a. Ord a => a -> a -> Bool
< Int
56
  then do
    let !ult :: Block
ult = ByteString -> Word64 -> Block
parse_pad1 ByteString
fin Word64
total
    Ptr Word32 -> Ptr Word32 -> Block -> IO ()
update Ptr Word32
rp Ptr Word32
bp Block
ult
  else do
    let !(# Block
pen, Block
ult #) = ByteString -> Word64 -> (# Block, Block #)
parse_pad2 ByteString
fin Word64
total
    Ptr Word32 -> Ptr Word32 -> Block -> IO ()
update Ptr Word32
rp Ptr Word32
bp Block
pen
    Ptr Word32 -> Ptr Word32 -> Block -> IO ()
update Ptr Word32
rp Ptr Word32
bp Block
ult
{-# INLINABLE _hash #-}

hash_blocks
  :: Ptr Word32    -- ^ register state
  -> Ptr Word32    -- ^ block state
  -> BS.ByteString -- ^ input
  -> IO ()
hash_blocks :: Ptr Word32 -> Ptr Word32 -> ByteString -> IO ()
hash_blocks Ptr Word32
rp Ptr Word32
bp m :: ByteString
m@(BI.PS ForeignPtr Word8
_ Int
_ Int
l) = Int -> IO ()
loop Int
0 where
  loop :: Int -> IO ()
loop !Int
j
    | Int
j Int -> Int -> Int
forall a. Num a => a -> a -> a
+ Int
64 Int -> Int -> Bool
forall a. Ord a => a -> a -> Bool
> Int
l = () -> IO ()
forall a. a -> IO a
forall (f :: * -> *) a. Applicative f => a -> f a
pure ()
    | Bool
otherwise  = do
        let !block :: Block
block = ByteString -> Int -> Block
parse ByteString
m Int
j
        Ptr Word32 -> Ptr Word32 -> Block -> IO ()
update Ptr Word32
rp Ptr Word32
bp Block
block
        Int -> IO ()
loop (Int
j Int -> Int -> Int
forall a. Num a => a -> a -> a
+ Int
64)
{-# INLINE hash_blocks #-}

-- hmac ------------------------------------------------------------------------

hmac :: BS.ByteString -> BS.ByteString -> BS.ByteString
hmac :: ByteString -> ByteString -> ByteString
hmac ByteString
k ByteString
m = IO ByteString -> ByteString
forall a. IO a -> a
unsafeDupablePerformIO (IO ByteString -> ByteString) -> IO ByteString -> ByteString
forall a b. (a -> b) -> a -> b
$
  Int -> (Ptr Word32 -> IO ByteString) -> IO ByteString
forall a b. Int -> (Ptr a -> IO b) -> IO b
allocaBytes Int
32 ((Ptr Word32 -> IO ByteString) -> IO ByteString)
-> (Ptr Word32 -> IO ByteString) -> IO ByteString
forall a b. (a -> b) -> a -> b
$ \Ptr Word32
rp ->
  Int -> (Ptr Word32 -> IO ByteString) -> IO ByteString
forall a b. Int -> (Ptr a -> IO b) -> IO b
allocaBytes Int
64 ((Ptr Word32 -> IO ByteString) -> IO ByteString)
-> (Ptr Word32 -> IO ByteString) -> IO ByteString
forall a b. (a -> b) -> a -> b
$ \Ptr Word32
bp -> do
    Ptr Word32 -> Ptr Word32 -> Block -> ByteString -> IO ()
_hmac Ptr Word32
rp Ptr Word32
bp (ByteString -> Block
prep_key ByteString
k) ByteString
m
    ByteString -> IO ByteString
forall a. a -> IO a
forall (f :: * -> *) a. Applicative f => a -> f a
pure (Registers -> ByteString
cat (Ptr Word32 -> Registers
peek_registers Ptr Word32
rp))

prep_key :: BS.ByteString -> Block
prep_key :: ByteString -> Block
prep_key k :: ByteString
k@(BI.PS ForeignPtr Word8
_ Int
_ Int
l)
    | Int
l Int -> Int -> Bool
forall a. Ord a => a -> a -> Bool
> Int
64    = ByteString -> Block
parse_key (ByteString -> ByteString
hash ByteString
k)
    | Bool
otherwise = ByteString -> Block
parse_key ByteString
k
{-# INLINABLE prep_key #-}

-- assume padded key as block.
_hmac
  :: Ptr Word32    -- ^ register state
  -> Ptr Word32    -- ^ block state
  -> Block         -- ^ padded key
  -> BS.ByteString -- ^ message
  -> IO ()
_hmac :: Ptr Word32 -> Ptr Word32 -> Block -> ByteString -> IO ()
_hmac Ptr Word32
rp Ptr Word32
bp Block
k ByteString
m = do
  Ptr Word32 -> Registers -> IO ()
poke_registers Ptr Word32
rp (() -> Registers
iv ())
  Ptr Word32 -> Ptr Word32 -> Block -> IO ()
update Ptr Word32
rp Ptr Word32
bp (Block -> Word32# -> Block
xor Block
k (Word# -> Word32#
Exts.wordToWord32# Word#
0x36363636##))
  Ptr Word32 -> Ptr Word32 -> Word64 -> ByteString -> IO ()
_hash Ptr Word32
rp Ptr Word32
bp Word64
64 ByteString
m
  let !block :: Block
block = Registers -> Block
pad_registers_with_length (Ptr Word32 -> Registers
peek_registers Ptr Word32
rp)
  Ptr Word32 -> Registers -> IO ()
poke_registers Ptr Word32
rp (() -> Registers
iv ())
  Ptr Word32 -> Ptr Word32 -> Block -> IO ()
update Ptr Word32
rp Ptr Word32
bp (Block -> Word32# -> Block
xor Block
k (Word# -> Word32#
Exts.wordToWord32# Word#
0x5C5C5C5C##))
  Ptr Word32 -> Ptr Word32 -> Block -> IO ()
update Ptr Word32
rp Ptr Word32
bp Block
block
{-# NOINLINE _hmac #-}

_hmac_rr
  :: Ptr Word32 -- ^ register state
  -> Ptr Word32 -- ^ block state
  -> Registers  -- ^ key
  -> Registers  -- ^ message
  -> IO ()
_hmac_rr :: Ptr Word32 -> Ptr Word32 -> Registers -> Registers -> IO ()
_hmac_rr Ptr Word32
rp Ptr Word32
bp Registers
k Registers
m =  do
  let !key :: Block
key   = Registers -> Block
pad_registers Registers
k
      !block :: Block
block = Registers -> Block
pad_registers_with_length Registers
m
  Ptr Word32 -> Ptr Word32 -> Block -> Block -> IO ()
_hmac_bb Ptr Word32
rp Ptr Word32
bp Block
key Block
block
{-# INLINABLE _hmac_rr #-}

_hmac_bb
  :: Ptr Word32  -- ^ register state
  -> Ptr Word32  -- ^ block state
  -> Block       -- ^ padded key
  -> Block       -- ^ padded message
  -> IO ()
_hmac_bb :: Ptr Word32 -> Ptr Word32 -> Block -> Block -> IO ()
_hmac_bb Ptr Word32
rp Ptr Word32
bp Block
k Block
m = do
  Ptr Word32 -> Registers -> IO ()
poke_registers Ptr Word32
rp (() -> Registers
iv ())
  Ptr Word32 -> Ptr Word32 -> Block -> IO ()
update Ptr Word32
rp Ptr Word32
bp (Block -> Word32# -> Block
xor Block
k (Word# -> Word32#
Exts.wordToWord32# Word#
0x36363636##))
  Ptr Word32 -> Ptr Word32 -> Block -> IO ()
update Ptr Word32
rp Ptr Word32
bp Block
m
  let !inner :: Block
inner = Registers -> Block
pad_registers_with_length (Ptr Word32 -> Registers
peek_registers Ptr Word32
rp)
  Ptr Word32 -> Registers -> IO ()
poke_registers Ptr Word32
rp (() -> Registers
iv ())
  Ptr Word32 -> Ptr Word32 -> Block -> IO ()
update Ptr Word32
rp Ptr Word32
bp (Block -> Word32# -> Block
xor Block
k (Word# -> Word32#
Exts.wordToWord32# Word#
0x5C5C5C5C##))
  Ptr Word32 -> Ptr Word32 -> Block -> IO ()
update Ptr Word32
rp Ptr Word32
bp Block
inner
{-# INLINABLE _hmac_bb #-}

-- | HMAC(key, v || sep || data) using ARM crypto extensions.
-- Writes result to destination pointer.
_hmac_rsb
  :: Ptr Word32    -- ^ destination (8 Word32s)
  -> Ptr Word32    -- ^ scratch block buffer (16 Word32s)
  -> Registers     -- ^ key
  -> Registers     -- ^ v
  -> Word8         -- ^ separator byte
  -> BS.ByteString -- ^ data
  -> IO ()
_hmac_rsb :: Ptr Word32
-> Ptr Word32
-> Registers
-> Registers
-> Word8
-> ByteString
-> IO ()
_hmac_rsb Ptr Word32
rp Ptr Word32
bp Registers
k Registers
v Word8
sep ByteString
dat = do
  Ptr Word32 -> Registers -> IO ()
poke_registers Ptr Word32
rp (() -> Registers
iv ())
  let !key :: Block
key = Registers -> Block
pad_registers Registers
k
  Ptr Word32 -> Ptr Word32 -> Block -> IO ()
update Ptr Word32
rp Ptr Word32
bp (Block -> Word32# -> Block
xor Block
key (Word# -> Word32#
Exts.wordToWord32# Word#
0x36363636##))
  Ptr Word32
-> Ptr Word32
-> Word64
-> Registers
-> Word8
-> ByteString
-> IO ()
_hash_vsb Ptr Word32
rp Ptr Word32
bp Word64
64 Registers
v Word8
sep ByteString
dat
  let !inner :: Block
inner = Registers -> Block
pad_registers_with_length (Ptr Word32 -> Registers
peek_registers Ptr Word32
rp)
  Ptr Word32 -> Registers -> IO ()
poke_registers Ptr Word32
rp (() -> Registers
iv ())
  Ptr Word32 -> Ptr Word32 -> Block -> IO ()
update Ptr Word32
rp Ptr Word32
bp (Block -> Word32# -> Block
xor Block
key (Word# -> Word32#
Exts.wordToWord32# Word#
0x5C5C5C5C##))
  Ptr Word32 -> Ptr Word32 -> Block -> IO ()
update Ptr Word32
rp Ptr Word32
bp Block
inner
{-# INLINABLE _hmac_rsb #-}

-- | Hash (v || sep || dat) with ARM crypto extensions.
-- Assumes register state already initialized at rp.
_hash_vsb
  :: Ptr Word32    -- ^ register state
  -> Ptr Word32    -- ^ block buffer
  -> Word64        -- ^ extra prefix length
  -> Registers     -- ^ v
  -> Word8         -- ^ sep
  -> BS.ByteString -- ^ dat
  -> IO ()
_hash_vsb :: Ptr Word32
-> Ptr Word32
-> Word64
-> Registers
-> Word8
-> ByteString
-> IO ()
_hash_vsb Ptr Word32
rp Ptr Word32
bp Word64
el Registers
v Word8
sep dat :: ByteString
dat@(BI.PS ForeignPtr Word8
_ Int
_ Int
l)
  | Int
l Int -> Int -> Bool
forall a. Ord a => a -> a -> Bool
>= Int
31 = do
      -- first block is complete: v || sep || dat[0:31]
      let !b0 :: Block
b0 = Registers -> Word8 -> ByteString -> Block
parse_vsb Registers
v Word8
sep ByteString
dat
      Ptr Word32 -> Ptr Word32 -> Block -> IO ()
update Ptr Word32
rp Ptr Word32
bp Block
b0
      -- hash remaining complete blocks from dat[31:]
      let !rest :: ByteString
rest    = Int -> ByteString -> ByteString
BU.unsafeDrop Int
31 ByteString
dat
          !restLen :: Int
restLen = Int
l Int -> Int -> Int
forall a. Num a => a -> a -> a
- Int
31
      Ptr Word32 -> Ptr Word32 -> ByteString -> IO ()
hash_blocks Ptr Word32
rp Ptr Word32
bp ByteString
rest
      -- handle final padding
      let !finLen :: Int
finLen = Int
restLen Int -> Int -> Int
forall a. Integral a => a -> a -> a
`rem` Int
64
          !fin :: ByteString
fin    = Int -> ByteString -> ByteString
BU.unsafeDrop (Int
restLen Int -> Int -> Int
forall a. Num a => a -> a -> a
- Int
finLen) ByteString
rest
          !total :: Word64
total  = Word64
el Word64 -> Word64 -> Word64
forall a. Num a => a -> a -> a
+ Word64
33 Word64 -> Word64 -> Word64
forall a. Num a => a -> a -> a
+ Int -> Word64
forall a b. (Integral a, Num b) => a -> b
fi Int
l
      if   Int
finLen Int -> Int -> Bool
forall a. Ord a => a -> a -> Bool
< Int
56
      then Ptr Word32 -> Ptr Word32 -> Block -> IO ()
update Ptr Word32
rp Ptr Word32
bp (ByteString -> Word64 -> Block
parse_pad1 ByteString
fin Word64
total)
      else do
        let !(# Block
pen, Block
ult #) = ByteString -> Word64 -> (# Block, Block #)
parse_pad2 ByteString
fin Word64
total
        Ptr Word32 -> Ptr Word32 -> Block -> IO ()
update Ptr Word32
rp Ptr Word32
bp Block
pen
        Ptr Word32 -> Ptr Word32 -> Block -> IO ()
update Ptr Word32
rp Ptr Word32
bp Block
ult
  | Bool
otherwise = do
      -- message < 64 bytes total, straight to padding
      let !total :: Word64
total = Word64
el Word64 -> Word64 -> Word64
forall a. Num a => a -> a -> a
+ Word64
33 Word64 -> Word64 -> Word64
forall a. Num a => a -> a -> a
+ Int -> Word64
forall a b. (Integral a, Num b) => a -> b
fi Int
l
      if   Int
33 Int -> Int -> Int
forall a. Num a => a -> a -> a
+ Int
l Int -> Int -> Bool
forall a. Ord a => a -> a -> Bool
< Int
56
      then Ptr Word32 -> Ptr Word32 -> Block -> IO ()
update Ptr Word32
rp Ptr Word32
bp (Registers -> Word8 -> ByteString -> Word64 -> Block
parse_pad1_vsb Registers
v Word8
sep ByteString
dat Word64
total)
      else do
        let !(# Block
pen, Block
ult #) = Registers -> Word8 -> ByteString -> Word64 -> (# Block, Block #)
parse_pad2_vsb Registers
v Word8
sep ByteString
dat Word64
total
        Ptr Word32 -> Ptr Word32 -> Block -> IO ()
update Ptr Word32
rp Ptr Word32
bp Block
pen
        Ptr Word32 -> Ptr Word32 -> Block -> IO ()
update Ptr Word32
rp Ptr Word32
bp Block
ult
{-# INLINABLE _hash_vsb #-}