{-|

Some utility function for byte strings.

-}

{-# LANGUAGE FlexibleContexts #-}
module Raaz.Core.Util.ByteString
       ( length, replicate
       , fromByteStringStorable
       , create, createFrom
       , withByteString
       , unsafeCopyToPointer
       , unsafeNCopyToPointer
       ) where

import           Prelude            hiding (length, replicate)
import qualified Data.ByteString    as B
import           Data.ByteString    (ByteString)
import qualified Data.ByteString.Internal as BI
import           Data.Word
import           Foreign.ForeignPtr (withForeignPtr)
import           Foreign.Ptr        (castPtr, plusPtr)
import           Foreign.Storable   (peek, Storable)

import           System.IO.Unsafe   (unsafePerformIO)

import           Raaz.Core.Types.Pointer
import           Raaz.Core.Types.Copying

-- | A typesafe length for Bytestring
length :: ByteString -> BYTES Int
length = BYTES . B.length

-- | A type safe version of replicate
replicate :: LengthUnit l => l -> Word8 -> ByteString
replicate l = B.replicate sz
  where BYTES sz = inBytes l

-- | Copy the bytestring to the crypto buffer. This operation leads to
-- undefined behaviour if the crypto pointer points to an area smaller
-- than the size of the byte string.
unsafeCopyToPointer :: ByteString   -- ^ The source.
                    -> Pointer      -- ^ The destination.
                    -> IO ()
unsafeCopyToPointer bs cptr =  withForeignPtr fptr $
           \ p -> memcpy dptr (source $ p `plusPtr` offset) (BYTES n)
    where (fptr, offset,n) = BI.toForeignPtr bs
          dptr = destination $ castPtr cptr


-- | Similar to `unsafeCopyToPointer` but takes an additional input
-- @n@ which is the number of bytes (expressed in type safe length
-- units) to transfer. This operation leads to undefined behaviour if
-- either the bytestring is shorter than @n@ or the crypto pointer
-- points to an area smaller than @n@.
unsafeNCopyToPointer :: LengthUnit n
                       => n              -- ^ length of data to be copied
                       -> ByteString     -- ^ The source byte string
                       -> Pointer        -- ^ The buffer
                       -> IO ()
unsafeNCopyToPointer n bs cptr = withForeignPtr fptr $
           \ p -> memcpy dptr (source $ p `plusPtr` offset) n
    where (fptr, offset,_) = BI.toForeignPtr bs
          dptr             = destination $ castPtr cptr

-- | Works directly on the pointer associated with the
-- `ByteString`. This function should only read and not modify the
-- contents of the pointer.
withByteString :: ByteString -> (Pointer -> IO a) -> IO a
withByteString bs f = withForeignPtr fptr (f . flip plusPtr off . castPtr)
  where (fptr, off, _) = BI.toForeignPtr bs

-- | Get the value from the bytestring using `peek`.
fromByteStringStorable :: Storable k => ByteString -> k
fromByteStringStorable str = unsafePerformIO $ withByteString str (peek . castPtr)


-- | The action @create l act@ creates a length @l@ bytestring where
-- the contents are filled using the the @act@ to fill the buffer.
create :: LengthUnit l => l -> (Pointer -> IO ()) -> IO ByteString
create l act = myCreate (act . castPtr)
  where myCreate =  BI.create $ fromIntegral $ inBytes l

-- | The IO action @createFrom n cptr@ creates a bytestring by copying
-- @n@ bytes from the pointer @cptr@.
createFrom :: LengthUnit l => l -> Pointer -> IO ByteString
createFrom l cptr = create l filler
  where filler dptr = memcpy (destination $ castPtr dptr) (source cptr) l

----------------------  Hexadecimal encoding. -----------------------------------