raaz-0.0.1: Raaz/Core/Write.hs
-- | Module to write stuff to buffers. As opposed to similar functions
-- exposed in "Raaz.Core.Write.Unsafe", the writes exposed here are
-- safe as necessary range checks are done on the buffer before
-- writing stuff to it.
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE TypeSynonymInstances #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE MultiParamTypeClasses #-}
module Raaz.Core.Write
( Write, bytesToWrite, unsafeWrite
, write, writeStorable, writeVector, writeStorableVector
, writeBytes, writeByteString, skipWrite
) where
import Data.ByteString (ByteString)
import Data.String
import Data.ByteString.Internal (unsafeCreate)
import Data.Monoid
import qualified Data.Vector.Generic as G
import Data.Word (Word8)
import Foreign.Ptr (castPtr)
import Foreign.Storable
import Raaz.Core.MonoidalAction
import Raaz.Core.Types.Endian
import Raaz.Core.Types.Pointer
import Raaz.Core.Util.ByteString as BU
import Raaz.Core.Encode
-- | The monoid for write.
newtype WriteM = WriteM { unWriteM :: IO () }
instance Monoid WriteM where
mempty = WriteM $ return ()
{-# INLINE mempty #-}
mappend wa wb = WriteM $ unWriteM wa >> unWriteM wb
{-# INLINE mappend #-}
mconcat = WriteM . mapM_ unWriteM
{-# INLINE mconcat #-}
-- | A write action is nothing but an IO action that returns () on
-- input a pointer.
type WriteAction = Pointer -> WriteM
type BytesMonoid = Sum (BYTES Int)
instance LAction BytesMonoid WriteAction where
m <.> action = action . (m<.>)
{-# INLINE (<.>) #-}
instance Distributive BytesMonoid WriteAction
-- | A write is an action which when executed using `runWrite` writes
-- bytes to the input buffer. It is similar to the `WU.Write` type
-- exposed from the "Raaz.Write.Unsafe" module except that it keeps
-- track of the total bytes that would be written to the buffer if the
-- action is run. The `runWrite` action will raise an error if the
-- buffer it is provided with is of size smaller. `Write`s are monoid
-- and hence can be concatnated using the `<>` operator.
type Write = SemiR WriteAction BytesMonoid
-- | Create a write action.
makeWrite :: LengthUnit u => u -> (Pointer -> IO ()) -> Write
{-# INLINE makeWrite #-}
makeWrite sz action = SemiR (WriteM . action) $ Sum (inBytes sz)
-- | Returns the bytes that will be written when the write action is performed.
bytesToWrite :: Write -> BYTES Int
bytesToWrite = getSum . semiRMonoid
-- | Perform the write action without any checks.
unsafeWrite :: Write -> Pointer -> IO ()
unsafeWrite wr = unWriteM . semiRSpace wr
{-
-- | The function tries to write the given `Write` action on the
-- buffer and returns `True` if successful.
tryWriting :: Write -- ^ The write action.
-> CryptoBuffer -- ^ The buffer to which the bytes are to
-- be written.
-> IO Bool
tryWriting wr cbuf = withCryptoBuffer cbuf $ \ sz cptr ->
if sz < bytesToWrite wr then return False
else do unsafeWrite wr cptr; return True
-}
-- | The expression @`writeStorable` a@ gives a write action that
-- stores a value @a@ in machine endian. The type of the value @a@ has
-- to be an instance of `Storable`. This should be used when we want
-- to talk with C functions and not when talking to the outside world
-- (otherwise this could lead to endian confusion). To take care of
-- endianness use the `write` combinator.
writeStorable :: Storable a => a -> Write
writeStorable a = makeWrite (byteSize a) pokeIt
where pokeIt = flip poke a . castPtr
-- | The expression @`write` a@ gives a write action that stores a
-- value @a@. One needs the type of the value @a@ to be an instance of
-- `EndianStore`. Proper endian conversion is done irrespective of
-- what the machine endianness is. The man use of this write is to
-- serialize data for the consumption of the outside world.
write :: EndianStore a => a -> Write
write a = makeWrite (byteSize a) $ flip store a
-- | The vector version of `writeStorable`.
writeStorableVector :: (Storable a, G.Vector v a) => v a -> Write
{-# INLINE writeStorableVector #-}
writeStorableVector = G.foldl' foldFunc mempty
where foldFunc w a = w <> writeStorable a
-- | The vector version of `write`.
writeVector :: (EndianStore a, G.Vector v a) => v a -> Write
{-# INLINE writeVector #-}
writeVector = G.foldl' foldFunc mempty
where foldFunc w a = w <> write a
-- | The combinator @writeBytes n b@ writes @b@ as the next @n@
-- consecutive bytes.
writeBytes :: LengthUnit n => Word8 -> n -> Write
writeBytes w8 n = makeWrite n memsetIt
where memsetIt cptr = memset cptr w8 n
-- | Writes a strict bytestring.
writeByteString :: ByteString -> Write
writeByteString bs = makeWrite (BU.length bs) $ BU.unsafeCopyToPointer bs
-- | A write action that just skips over the given bytes.
skipWrite :: LengthUnit u => u -> Write
skipWrite = flip makeWrite $ const $ return ()
instance IsString Write where
fromString = writeByteString . fromString
instance Encodable Write where
{-# INLINE toByteString #-}
toByteString w = unsafeCreate n $ unsafeWrite w . castPtr
where BYTES n = bytesToWrite w
{-# INLINE unsafeFromByteString #-}
unsafeFromByteString = writeByteString
{-# INLINE fromByteString #-}
fromByteString = Just . writeByteString