raaz-0.0.2: Raaz/Core/ByteSource.hs
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE DefaultSignatures #-}
-- | Module define byte sources.
module Raaz.Core.ByteSource
( -- * Byte sources.
-- $bytesource$
InfiniteSource(..)
, ByteSource(..), PureByteSource
, FillResult(..)
, fill, slurp, processChunks
, withFillResult
) where
import Control.Applicative
import Control.Monad (liftM)
import Control.Monad.IO.Class
import qualified Data.ByteString as B
import qualified Data.ByteString.Lazy as L
import Data.Monoid
import Prelude hiding(length)
import System.IO (Handle)
import Raaz.Core.MonoidalAction
import Raaz.Core.Types (BYTES, Pointer, LengthUnit (..))
import Raaz.Core.Util.ByteString( unsafeCopyToPointer
, unsafeNCopyToPointer
, length
)
import Raaz.Core.Types.Pointer (hFillBuf)
-- $bytesource$
--
-- Cryptographic input come from various sources; they can come from
-- network sockets or might be just a string in the Haskell. To give a
-- uniform interfaces for all such inputs, we define the abstract
-- concept of a /byte source/. Essentially a byte source is one from
-- which we can fill a buffer with bytes. Depending on the nature of
-- the source we have two classes: `ByteSource` which captures bounded
-- sources and `InfiniteSource` that captures never ending source of
-- bytes.
--
-- Among instances of `ByteSource`, some like for example
-- `B.ByteString` are /pure/ in the sense filling a buffer with bytes
-- from such a source has no other side-effects. This is in contrast
-- to a source like a sockets. The type class `PureByteSource`
-- captures such byte sources.
--
-- | This type captures the result of a fill operation.
data FillResult a = Remaining a -- ^ the buffer is filled completely
| Exhausted (BYTES Int) -- ^ source exhausted with so much
-- bytes read.
instance Functor FillResult where
fmap f (Remaining a ) = Remaining $ f a
fmap _ (Exhausted sz) = Exhausted sz
-- | Combinator to handle a fill result.
withFillResult :: (a -> b) -- ^ stuff to do when filled
-> (BYTES Int -> b) -- ^ stuff to do when exhausted
-> FillResult a -- ^ the fill result to process
-> b
withFillResult continueWith _ (Remaining a) = continueWith a
withFillResult _ endBy (Exhausted sz) = endBy sz
------------------------ Byte sources ----------------------------------
-- | Abstract byte sources. A bytesource is something that you can use
-- to fill a buffer.
class ByteSource src where
-- | Fills a buffer from the source.
fillBytes :: BYTES Int -- ^ Buffer size
-> src -- ^ The source to fill.
-> Pointer -- ^ Buffer pointer
-> IO (FillResult src)
default fillBytes :: InfiniteSource src => BYTES Int -> src -> Pointer -> IO (FillResult src)
fillBytes sz src pointer = Remaining <$> slurp sz src pointer
-- | Never ending stream of bytes. The reads to the stream might get
-- delayed but it will always return the number of bytes that were
-- asked for.
class InfiniteSource src where
slurpBytes :: BYTES Int -- ^ bytes to read,
-> src -- ^ the source to fill from,
-> Pointer -- ^ the buffer source to fill.
-> IO src
-- | A version of fillBytes that takes type safe lengths as input.
fill :: ( LengthUnit len
, ByteSource src
)
=> len
-> src
-> Pointer
-> IO (FillResult src)
fill = fillBytes . inBytes
{-# INLINE fill #-}
-- | A version of slurp that takes type safe lengths as input.
slurp :: ( LengthUnit len
, InfiniteSource src
)
=> len
-> src
-> Pointer
-> IO src
slurp = slurpBytes . inBytes
-- | Process data from a source in chunks of a particular size.
processChunks :: ( MonadIO m, LengthUnit chunkSize, ByteSource src)
=> m a -- action on a complete chunk,
-> (BYTES Int -> m b) -- action on the last partial chunk,
-> src -- the source
-> chunkSize -- size of the chunksize
-> Pointer -- buffer to fill the chunk in
-> m b
processChunks mid end source csz ptr = go source
where fillChunk src = liftIO $ fill csz src ptr
step src = mid >> go src
go src = fillChunk src >>= withFillResult step end
-- | A byte source src is pure if filling from it does not have any
-- other side effect on the state of the byte source. Formally, two
-- different fills form the same source should fill the buffer with
-- the same bytes. This additional constraint on the source helps to
-- /purify/ certain crypto computations like computing the hash or mac
-- of the source. Usualy sources like `B.ByteString` etc are pure byte
-- sources. A file handle is a byte source that is /not/ a pure
-- source.
class ByteSource src => PureByteSource src where
----------------------- Instances of byte source -----------------------
instance ByteSource Handle where
{-# INLINE fillBytes #-}
fillBytes sz hand cptr = do
count <- hFillBuf hand cptr sz
return
(if count < sz then Exhausted count
else Remaining hand)
instance ByteSource B.ByteString where
{-# INLINE fillBytes #-}
fillBytes sz bs cptr | l < sz = do unsafeCopyToPointer bs cptr
return $ Exhausted l
| otherwise = do unsafeNCopyToPointer sz bs cptr
return $ Remaining rest
where l = length bs
rest = B.drop (fromIntegral sz) bs
instance ByteSource L.ByteString where
{-# INLINE fillBytes #-}
fillBytes sz bs = liftM (fmap L.fromChunks)
. fillBytes sz (L.toChunks bs)
instance ByteSource src => ByteSource (Maybe src) where
{-# INLINE fillBytes #-}
fillBytes sz ma cptr = maybe exhausted fillIt ma
where exhausted = return $ Exhausted 0
fillIt a = fmap Just <$> fillBytes sz a cptr
instance ByteSource src => ByteSource [src] where
fillBytes _ [] _ = return $ Exhausted 0
fillBytes sz (x:xs) cptr = do
result <- fillBytes sz x cptr
case result of
Exhausted rbytes -> let nptr = Sum rbytes <.> cptr
in fillBytes (sz - rbytes) xs nptr
Remaining nx -> return $ Remaining $ nx:xs
--------------------- Instances of pure byte source --------------------
instance PureByteSource B.ByteString where
instance PureByteSource L.ByteString where
instance PureByteSource src => PureByteSource [src]
instance PureByteSource src => PureByteSource (Maybe src)