{-# LANGUAGE FlexibleContexts #-}
module Raaz.Core.ByteSource
(
ByteSource(..), PureByteSource
, FillResult(..)
, fill, processChunks
, withFillResult
) where
import Control.Applicative
import Control.Monad.IO.Class
import qualified Data.ByteString as B
import qualified Data.ByteString.Lazy as L
import Prelude hiding(length)
import System.IO (Handle, hIsEOF)
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)
data FillResult a = Remaining a
| Exhausted (BYTES Int)
deriving (Show, Eq)
instance Functor FillResult where
fmap f (Remaining a ) = Remaining $ f a
fmap _ (Exhausted sz) = Exhausted sz
withFillResult :: (a -> b)
-> (BYTES Int -> b)
-> FillResult a
-> b
withFillResult continueWith _ (Remaining a) = continueWith a
withFillResult _ endBy (Exhausted sz) = endBy sz
class ByteSource src where
fillBytes :: BYTES Int
-> src
-> Pointer
-> IO (FillResult src)
fill :: ( LengthUnit len
, ByteSource src
)
=> len
-> src
-> Pointer
-> IO (FillResult src)
fill = fillBytes . inBytes
{-# INLINE fill #-}
processChunks :: ( MonadIO m, LengthUnit chunkSize, ByteSource src)
=> m a
-> (BYTES Int -> m b)
-> src
-> chunkSize
-> Pointer
-> 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
class ByteSource src => PureByteSource src where
instance ByteSource Handle where
{-# INLINE fillBytes #-}
fillBytes sz hand cptr = do
count <- hFillBuf hand cptr sz
eof <- hIsEOF hand
if eof then return $ Exhausted count
else return $ 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 = fmap (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
Remaining nx -> return $ Remaining $ nx:xs
Exhausted bytesX -> let nptr = bytesX <.> cptr
whenXSExhausted bytesXS = return $ Exhausted $ bytesX + bytesXS
whenXSRemains = return . Remaining
in fillBytes (sz - bytesX) xs nptr
>>= withFillResult whenXSRemains whenXSExhausted
instance PureByteSource B.ByteString where
instance PureByteSource L.ByteString where
instance PureByteSource src => PureByteSource [src]
instance PureByteSource src => PureByteSource (Maybe src)