content: {-# LANGUAGE DataKinds                  #-}
  {-# LANGUAGE MultiParamTypeClasses      #-}
  {-# LANGUAGE FlexibleInstances          #-}
  -- | This module implements memory elements used for Poly1305
  -- implementations.
  module Poly1305.Memory
         ( Mem(..)
         , Element
         , elementToInteger
         , rKeyPtr
         , sKeyPtr
         , accumPtr
         ) where
  
  import qualified Data.Vector.Unboxed as V
  import           Foreign.Ptr                        ( castPtr )
  import           Raaz.Core
  import qualified Raaz.Core.Types.Internal        as TI
  import           Raaz.Primitive.Poly1305.Internal
  
  import           Raaz.Verse.Poly1305.C.Portable (verse_poly1305_c_portable_clamp)
  
  -- | An element in the finite field GF(2¹³⁰ - 5) requires 130 bits
  -- which is stored as three 64-bit word where the last word has only
  -- 2-bits.
  type Element = Tuple 3 Word64
  
  
  -- | Convert the element to an integer.
  elementToInteger :: Element -> Integer
  elementToInteger = V.foldr fld 0 . unsafeToVector
    where fld :: Word64 -> Integer -> Integer
          fld w i = toInteger w + i `shiftL` 32
  
  
  -- | The memory associated with Poly1305 stores the
  data Mem = Mem { accCell :: MemoryCell Element
                 , rCell   :: MemoryCell R
                 , sCell   :: MemoryCell S
                 }
  
  -- | Clearing the accumulator.
  clearAcc :: Mem -> IO ()
  clearAcc = initialise zero . accCell
    where zero :: Element
          zero = unsafeFromList [0,0,0]
  
  instance Memory Mem where
    memoryAlloc     = Mem <$> memoryAlloc <*> memoryAlloc <*> memoryAlloc
    unsafeToPointer = unsafeToPointer . accCell
  
  
  -- | Get the pointer to the array holding the key fragment r.
  rKeyPtr  :: Mem  -> Ptr (Tuple 2 Word64)
  rKeyPtr  = castPtr . unsafeGetCellPointer . rCell
  
  -- | Get the pointer to the array holding the key fragment s.
  sKeyPtr  :: Mem -> Ptr (Tuple 2 Word64)
  sKeyPtr  = castPtr . unsafeGetCellPointer . sCell
  
  -- | Get the pointer to the accumulator array.
  accumPtr :: Mem -> Ptr Element
  accumPtr = castPtr . unsafeGetCellPointer .  accCell
  
  -- |  The clamping function on pointer
  clampPtr :: Ptr (Tuple 2 Word64) -> IO ()
  clampPtr = flip verse_poly1305_c_portable_clamp 1
  
  -- | The clamping operation
  clamp :: Mem -> IO ()
  clamp =  clampPtr . rKeyPtr
  
  instance Initialisable Mem (Key Poly1305) where
    initialise (Key r s) mem = do clearAcc mem
                                  initialise r $ rCell mem
                                  initialise s $ sCell mem
                                  clamp mem
  
  instance Extractable Mem Poly1305 where
      extract = fmap toPoly1305 . extract . accCell
        where toPoly1305 = Poly1305 . TI.map littleEndian . project
              project :: Tuple 3 Word64 -> Tuple 2 Word64
              project = initial
  
  instance WriteAccessible Mem where
    writeAccess mem          = writeAccess (rCell mem) ++ writeAccess (sCell mem)
    afterWriteAdjustment mem = do
      clearAcc mem
      afterWriteAdjustment $ rCell mem
      afterWriteAdjustment $ sCell mem
      clamp mem
  