{-# LANGUAGE ScopedTypeVariables #-} -- |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 ,enumHandleRandom -- * Iteratee drivers ,fileDriverHandle ,fileDriverRandomHandle ) where import Data.Iteratee.Base.StreamChunk (ReadableChunk (..)) import Data.Iteratee.Base import Data.Iteratee.Binary() import Data.Int import Control.Exception.Extensible import Control.Monad import Control.Monad.Trans import Foreign.Ptr import Foreign.ForeignPtr import Foreign.Storable import System.IO -- ------------------------------------------------------------------------ -- Binary Random IO enumerators -- |The 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. enumHandle :: forall s el m a.(ReadableChunk s el, MonadIO m) => Handle -> EnumeratorGM s el m a enumHandle h i = liftIO (mallocForeignPtrBytes (fromIntegral buffer_size)) >>= loop i where buffer_size = 4096 - mod 4096 (sizeOf (undefined :: el)) loop iter fp = do s <- liftIO . withForeignPtr fp $ \p -> do n <- try $ hGetBuf h p buffer_size :: IO (Either SomeException Int) case n of Left _ -> return $ Left "IO error" Right 0 -> return $ Right Nothing Right n' -> liftM (Right . Just) $ readFromPtr p (fromIntegral n') checkres fp iter s checkres fp iter = either (flip enumErr iter) (maybe (return iter) (check fp <=< runIter iter . Chunk)) check _p (Done x _) = return . return $ x check p (Cont i' Nothing) = loop i' p check _p (Cont _ (Just e)) = return $ throwErr e -- |The enumerator of a Handle: a variation of enumHandle that -- supports RandomIO (seek requests) enumHandleRandom :: forall s el m a.(ReadableChunk s el, MonadIO m) => Handle -> EnumeratorGM s el m a enumHandleRandom h i = liftIO (mallocForeignPtrBytes (fromIntegral buffer_size)) >>= loop (0,0) i where buffer_size = 4096 - mod 4096 (sizeOf (undefined :: el)) -- the first argument of loop is (off,len), describing which part -- of the file is currently in the buffer 'fp' loop :: (FileOffset,Int) -> IterateeG s el m a -> ForeignPtr el -> m (IterateeG s el m a) -- strictify `off', else the `off + fromIntegral len' accumulates thunks loop (off,len) _iter _p | off `seq` len `seq` False = undefined loop (off,len) iter fp = do s <- liftIO . withForeignPtr fp $ \p -> do n <- try $ hGetBuf h p buffer_size :: IO (Either SomeException Int) case n of Left _errno -> return $ Left "IO error" Right 0 -> return $ Right Nothing Right n' -> liftM (Right . Just . (,) (off + fromIntegral len, fromIntegral n')) (readFromPtr p (fromIntegral n')) checkres fp iter s seekTo pos@(off, len) off' iter fp | off <= off' && off' < off + fromIntegral len = -- Seek within buffer do let local_off = fromIntegral $ off' - off s <- liftIO $ withForeignPtr fp $ \p -> readFromPtr (p `plusPtr` local_off) (len - local_off) igv <- runIter iter (Chunk s) check pos fp igv seekTo _pos off iter fp = do -- Seek outside buffer off' <- liftIO (try $ hSeek h AbsoluteSeek (fromIntegral off) :: IO (Either SomeException ())) case off' of Left _errno -> enumErr "IO error" iter Right _ -> loop (off,0) iter fp checkres fp iter = either (flip enumErr iter) (maybe (return iter) (uncurry $ runS fp iter)) runS fp iter o s = runIter iter (Chunk s) >>= check o fp check _ _ (Done x _) = return . return $ x check o fp (Cont i' Nothing) = loop o i' fp check o fp (Cont i' (Just (Seek off))) = seekTo o off i' fp check _ _ (Cont _ (Just e)) = return $ throwErr e -- ---------------------------------------------- -- File Driver wrapper functions. -- |Process a file using the given IterateeGM. This function wraps -- enumHandle as a convenience. fileDriverHandle :: (MonadIO m, ReadableChunk s el) => IterateeG s el m a -> FilePath -> m a fileDriverHandle iter filepath = do h <- liftIO $ openBinaryFile filepath ReadMode result <- enumHandle h iter >>= run liftIO $ hClose h return result -- |Process a file using the given IterateeGM. This function wraps -- enumHandleRandom as a convenience. fileDriverRandomHandle :: (MonadIO m, ReadableChunk s el) => IterateeG s el m a -> FilePath -> m a fileDriverRandomHandle iter filepath = do h <- liftIO $ openBinaryFile filepath ReadMode result <- enumHandleRandom h iter >>= run liftIO $ hClose h return result