content: {-# LANGUAGE DataKinds                  #-}
  
  -- | The portable C-implementation of Poly1305.
  module Poly1305.CPortable
         ( name, primName, description
         , Prim, Internals, BufferAlignment
         , BufferPtr
         , additionalBlocks
         , processBlocks
         , processLast
         , Key(..)
         ) where
  
  import Raaz.Core
  import Raaz.Core.Types.Internal
  import Raaz.Core.Transfer.Unsafe
  import Raaz.Primitive.Poly1305.Internal
  import Poly1305.Memory
  
  import Raaz.Verse.Poly1305.C.Portable
  
  name :: String
  name = "libverse-c"
  
  primName :: String
  primName = "poly1305"
  
  description :: String
  description = "Poly1305 Implementation in C exposed by libverse"
  
  type Prim                    = Poly1305
  type Internals               = Mem
  type BufferAlignment         = 32
  type BufferPtr               = AlignedBlockPtr BufferAlignment Prim
  
  additionalBlocks :: BlockCount Poly1305
  additionalBlocks = blocksOf 1 Proxy
  
  
  -- | Incrementally process poly1305 blocks.
  processBlocks :: BufferPtr
                -> BlockCount Poly1305
                -> Internals
                -> IO ()
  processBlocks buf blks = withAccumR $ runWithBlocks verse_poly1305_c_portable_incremental buf blks
  
  -- | Process a message that is exactly a multiple of the blocks.
  blocksMac :: BufferPtr
            -> BlockCount Poly1305
            -> Internals
            -> IO ()
  blocksMac buf blks = withAccumRS $ runWithBlocks verse_poly1305_c_portable_blockmac buf blks
  
  -- | Run an IO action with the pointers to the element, r and s cells.
  withAccumRS :: ( Ptr Element ->
                   Ptr (Tuple 2 Word64) ->
                   Ptr (Tuple 2 Word64) ->
                   a
                 )
              -> Internals
              -> a
  withAccumRS func mem = withAccumR func mem $ sKeyPtr mem
  
  withAccumR :: ( Ptr Element ->
                  Ptr (Tuple 2 Word64) ->
                  a
                )
             -> Internals
             -> a
  withAccumR func mem = func (accumPtr mem) $ rKeyPtr mem
  
  runWithBlocks :: ( Ptr a ->
                     Word64 ->
                     b
                   )
                -> BufferPtr
                -> BlockCount Poly1305
                -> b
  runWithBlocks func buf = unsafeWithPointerCast func buf . toEnum . fromEnum
  
  -- | Process a message that has its last block incomplete. The total
  -- blocks argument here is the greatest multiple of the block that is
  -- less that the message length.
  partialBlockMac :: BufferPtr
                  -> BlockCount Poly1305
                  -> Internals
                  -> IO ()
  partialBlockMac buf blks mem = do
    processBlocks buf blks mem
    withAccumRS (unsafeWithPointerCast partialMac buf) mem
    where partialMac bufPtr = verse_poly1305_c_portable_partialmac (bufPtr `movePtr` blks)
  
  -- | Process the last bytes.
  processLast :: BufferPtr
              -> BYTES Int
              -> Internals
              -> IO ()
  processLast buf nBytes mem
    | blksC == blksF = blocksMac buf blksC mem
    | otherwise      = do
        unsafeTransfer pad (forgetAlignment buf)
        partialBlockMac buf blksF mem
    where blksC = atLeast nBytes :: BlockCount Poly1305
          blksF = atMost  nBytes :: BlockCount Poly1305
          pad   = padding nBytes
  
  -- | Poly1305 padding. Call this padding function if and only if the
  -- message is not a multiple of the block length.
  padding :: BYTES Int    -- Data in buffer.
          -> WriteTo
  padding mLen = padWrite 0 boundary $ skip mLen `mappend` one
    where one         = writeStorable (1::Word8)
          boundary    = blocksOf 1 (Proxy :: Proxy Poly1305)
  