@@ -23,6 +23,8 @@ import qualified Data.ByteString as B
23import System.IO.Unsafe (unsafeInterleaveIO)
23import System.IO.Unsafe (unsafeInterleaveIO)
24import System.IO (withBinaryFile, IOMode (ReadMode), Handle, hIsEOF)
24import System.IO (withBinaryFile, IOMode (ReadMode), Handle, hIsEOF)
25import Data.ByteString.Lazy.Internal (defaultChunkSize)
25import Data.ByteString.Lazy.Internal (defaultChunkSize)
26+import Control.Concurrent (forkIO)
27+import Control.Concurrent.MVar
27-- | Performs a specified conversion on each 'B.ByteString' output by an
29-- | Performs a specified conversion on each 'B.ByteString' output by an
28-- enumerator.
30-- enumerator.
@@ -32,14 +34,17 @@ mapE f e iter = e iter' where
33-- | This uses 'unsafeInterleaveIO' to lazily read from an enumerator. All
35-- | This uses 'unsafeInterleaveIO' to lazily read from an enumerator. All
34-- normal lazy I/O warnings apply.
36-- normal lazy I/O warnings apply.
35−toLBS :: Enumerator (Maybe B.ByteString) -> IO L.ByteString
37+toLBS :: Enumerator () -> IO L.ByteString
36−toLBS e = L.fromChunks `fmap` helper where
40+ L.fromChunks `fmap` helper source
37− helper = unsafeInterleaveIO $ do
42+ helper source = unsafeInterleaveIO $ do
38− x <- toSource e
43+ x <- source
40 Nothing -> return []
45 Nothing -> return []
41 Just x' -> do
46 Just x' -> do
42− xs <- helper
47+ xs <- helper source
43 return $ x' : xs
48 return $ x' : xs
45-- | This function safely converts a lazy bytestring into an enumerator.
50-- | This function safely converts a lazy bytestring into an enumerator.
@@ -61,8 +66,29 @@ fromLBS' lbs' iter a0 = lbs' >>= \lbs -> fromLBS lbs iter a0
61-- | A source is a more standard way of accessing data from an 'Enumerator'.
66-- | A source is a more standard way of accessing data from an 'Enumerator'.
62-- Each time you call it, it returns the next chunk of data if available, or
67-- Each time you call it, it returns the next chunk of data if available, or
63-- 'Nothing' if the data has been completely consumed.
68-- 'Nothing' if the data has been completely consumed.
64−toSource :: Enumerator (Maybe B.ByteString) -> IO (Maybe B.ByteString)
69+toSource :: Enumerator () -> IO (IO (Maybe B.ByteString))
65−toSource e = fmap (either id id) $ e (const $ return . Left . Just) Nothing
70+toSource e = do
71+ buffer <- newEmptyMVar
72+ forkIO $ e (helper buffer) () >> putMVar buffer Nothing
73+ return $ source buffer
75+ helper :: MVar (Maybe B.ByteString)
79+ helper buffer _ bs = do
80+ putMVar buffer $ Just bs
82+ source :: MVar (Maybe B.ByteString) -> IO (Maybe B.ByteString)
87+ -- By putting Nothing back in, the source can be called
88+ -- again without causing a deadlock.
91+ Just bs -> return $ Just bs
67-- | Read a chunk of data from the given 'Handle' at a time. We use
93-- | Read a chunk of data from the given 'Handle' at a time. We use
68-- 'defaultChunkSize' from the bytestring package to determine the largest
94-- 'defaultChunkSize' from the bytestring package to determine the largest