iteratee-0.8.9.0: src/Data/Iteratee/IO/Handle.hs
{-# LANGUAGE ScopedTypeVariables, FlexibleContexts #-}
-- |Random and Binary IO with generic Iteratees. These functions use Handles
-- for IO operations, and are provided for compatibility. When available,
-- the File Descriptor based functions are preferred as these wastefully
-- allocate memory rather than running in constant space.
module Data.Iteratee.IO.Handle(
-- * File enumerators
enumHandle
,enumHandleCatch
,enumHandleRandom
,enumFile
,enumFileRandom
-- * Iteratee drivers
,fileDriverHandle
,fileDriverRandomHandle
)
where
import Data.Iteratee.Base.ReadableChunk
import Data.Iteratee.Iteratee
import Data.Iteratee.Binary()
import Control.Applicative ((<$>))
import Control.Monad
import Control.Monad.Trans.Control
import Control.Exception.Lifted
import Control.Monad.IO.Class
import Foreign.Ptr
import Foreign.Storable
import Foreign.Marshal.Alloc
import System.IO
-- ------------------------------------------------------------------------
-- Binary Random IO enumerators
makeHandleCallback :: forall st m s el.
(MonadBaseControl IO m, MonadIO m, NullPoint s, ReadableChunk s el) =>
Ptr el
-> Int
-> Handle
-> Callback st m s
makeHandleCallback p bsize h st = do
n' <- (try $ liftIO $ hGetBuf h p bsize) :: m (Either SomeException Int)
case n' of
Left e -> return $ Left e
Right 0 -> return $ Right ((Finished, st), empty)
Right n -> liftIO $ (\s -> Right ((HasMore, st), s)) <$>
readFromPtr p (fromIntegral n)
-- |The (monadic) enumerator of a file Handle. This version enumerates
-- over the entire contents of a file, in order, unless stopped by
-- the iteratee. In particular, seeking is not supported.
-- Data is read into a buffer of the specified size.
enumHandle
:: forall s el m a.
(NullPoint s, ReadableChunk s el, MonadBaseControl IO m, MonadIO m)
=> Int -- ^Buffer size (number of elements per read)
-> Handle
-> Enumerator s m a
enumHandle bs h i =
let bufsize = bs * sizeOf (undefined :: el)
in bracket (liftIO $ mallocBytes bufsize)
(liftIO . free)
(\p -> enumFromCallback (makeHandleCallback p bufsize h) () i)
-- |An enumerator of a file handle that catches exceptions raised by
-- the Iteratee.
enumHandleCatch
:: forall e s el m a.(IException e,
NullPoint s,
ReadableChunk s el,
MonadBaseControl IO m,
MonadIO m)
=> Int -- ^Buffer size (number of elements per read)
-> Handle
-> (e -> m (Maybe EnumException))
-> Enumerator s m a
enumHandleCatch bs h handler i =
let bufsize = bs * sizeOf (undefined :: el)
in bracket (liftIO $ mallocBytes bufsize)
(liftIO . free)
(\p -> enumFromCallbackCatch (makeHandleCallback p bufsize h) handler () i)
-- |The enumerator of a Handle: a variation of enumHandle that
-- supports RandomIO (seek requests).
-- Data is read into a buffer of the specified size.
enumHandleRandom
:: forall s el m a. (NullPoint s
,ReadableChunk s el
,MonadBaseControl IO m
,MonadIO m)
=> Int -- ^ Buffer size (number of elements per read)
-> Handle
-> Enumerator s m a
enumHandleRandom bs h i = enumHandleCatch bs h handler i
where
handler (SeekException off) =
liftM (either
(Just . EnumException :: IOException -> Maybe EnumException)
(const Nothing))
. liftIO . try $ hSeek h AbsoluteSeek $ fromIntegral off
-- ----------------------------------------------
-- File Driver wrapper functions.
enumFile'
:: (NullPoint s, MonadBaseControl IO m, ReadableChunk s el, MonadIO m)
=> (Int -> Handle -> Enumerator s m a)
-> Int -- ^Buffer size
-> FilePath
-> Enumerator s m a
enumFile' enumf bufsize filepath iter = bracket
(liftIO $ openBinaryFile filepath ReadMode)
(liftIO . hClose)
(flip (enumf bufsize) iter)
enumFile
:: (NullPoint s, MonadBaseControl IO m, ReadableChunk s el, MonadIO m)
=> Int -- ^Buffer size
-> FilePath
-> Enumerator s m a
enumFile = enumFile' enumHandle
enumFileRandom
:: (NullPoint s, MonadBaseControl IO m, ReadableChunk s el, MonadIO m)
=> Int -- ^Buffer size
-> FilePath
-> Enumerator s m a
enumFileRandom = enumFile' enumHandleRandom
-- |Process a file using the given @Iteratee@. This function wraps
-- @enumHandle@ as a convenience.
fileDriverHandle
:: (NullPoint s, MonadBaseControl IO m, ReadableChunk s el, MonadIO m)
=> Int -- ^Buffer size (number of elements)
-> Iteratee s m a
-> FilePath
-> m a
fileDriverHandle bufsize iter filepath =
enumFile bufsize filepath iter >>= run
-- |A version of @fileDriverHandle@ that supports seeking.
fileDriverRandomHandle
:: (NullPoint s, MonadBaseControl IO m, ReadableChunk s el, MonadIO m)
=> Int -- ^ Buffer size (number of elements)
-> Iteratee s m a
-> FilePath
-> m a
fileDriverRandomHandle bufsize iter filepath =
enumFileRandom bufsize filepath iter >>= run