module Streamly.External.ByteString.Lazy ( chunkReader , reader , toChunks , fromChunks , fromChunksIO -- Deprecated , read , readChunks ) where import Data.Word (Word8) import Streamly.Data.Array (Array) import System.IO.Unsafe (unsafeInterleaveIO) import Streamly.Data.Stream (Stream) -- Internal imports import Data.ByteString.Lazy.Internal (ByteString(..), chunk) import Streamly.Internal.Data.Stream.StreamD.Type (Step(..)) import Streamly.Internal.Data.Unfold.Type (Unfold(..)) import qualified Streamly.External.ByteString as Strict import qualified Streamly.Data.Array as Array import qualified Streamly.Data.Unfold as Unfold import qualified Streamly.Data.Stream as Stream import Prelude hiding (read) -- | Unfold a lazy ByteString to a stream of 'Array' 'Words'. {-# INLINE chunkReader #-} chunkReader :: Monad m => Unfold m ByteString (Array Word8) chunkReader = Unfold step seed where seed = return step (Chunk bs bl) = return $ Yield (Strict.toArray bs) bl step Empty = return Stop -- | Unfold a lazy ByteString to a stream of Word8 {-# INLINE reader #-} reader :: Monad m => Unfold m ByteString Word8 reader = Unfold.many Array.reader readChunks -- TODO: "toChunks" should be called "read" instead -- | Convert a lazy 'ByteString' to a serial stream of 'Array' 'Word8'. {-# INLINE toChunks #-} toChunks :: Monad m => ByteString -> Stream m (Array Word8) toChunks = Stream.unfold readChunks {- newtype LazyIO a = LazyIO { runLazy :: IO a } deriving (Functor, Applicative) liftToLazy :: IO a -> LazyIO a liftToLazy = LazyIO instance Monad LazyIO where return = pure LazyIO a >>= f = LazyIO (unsafeInterleaveIO a >>= unsafeInterleaveIO . runLazy . f) -} -- | Convert a serial stream of 'Array' 'Word8' to a lazy 'ByteString'. -- -- IMPORTANT NOTE: This function is lazy only for lazy monads -- (e.g. Identity). For strict monads (e.g. /IO/) it consumes the entire input -- before generating the output. For /IO/ monad please use fromChunksIO -- instead. -- -- For strict monads like /IO/ you could create a newtype wrapper to make the -- monad bind operation lazy and lift the stream to that type using hoist, then -- you can use this function to generate the bytestring lazily. For example you -- can wrap the /IO/ type to make the bind lazy like this: -- -- @ -- newtype LazyIO a = LazyIO { runLazy :: IO a } deriving (Functor, Applicative) -- -- liftToLazy :: IO a -> LazyIO a -- liftToLazy = LazyIO -- -- instance Monad LazyIO where -- return = pure -- LazyIO a >>= f = LazyIO (unsafeInterleaveIO a >>= unsafeInterleaveIO . runLazy . f) -- @ -- -- /fromChunks/ can then be used as, -- @ -- {-# INLINE fromChunksIO #-} -- fromChunksIO :: Stream IO (Array Word8) -> IO ByteString -- fromChunksIO str = runLazy (fromChunks (Stream.hoist liftToLazy str)) -- @ {-# INLINE fromChunks #-} fromChunks :: Monad m => Stream m (Array Word8) -> m ByteString fromChunks = Stream.foldr chunk Empty . fmap Strict.fromArray -- | Convert a serial stream of 'Array' 'Word8' to a lazy 'ByteString' in the -- /IO/ monad. {-# INLINE fromChunksIO #-} fromChunksIO :: Stream IO (Array Word8) -> IO ByteString fromChunksIO = -- Although the /IO/ monad is strict in nature we emulate laziness using -- 'unsafeInterleaveIO'. Stream.foldrM (\x b -> chunk x <$> unsafeInterleaveIO b) (pure Empty) . fmap Strict.fromArray -------------------------------------------------------------------------------- -- Deprecated -------------------------------------------------------------------------------- {-# DEPRECATED readChunks "Please use chunkReader instead." #-} {-# INLINE readChunks #-} readChunks :: Monad m => Unfold m ByteString (Array Word8) readChunks = chunkReader {-# DEPRECATED read "Please use reader instead." #-} {-# INLINE read #-} read :: Monad m => Unfold m ByteString Word8 read = reader