@@ -128,6 +128,9 @@ module Streamly.Internal.Data.Stream.StreamD
128 , foldlMx'
128 , foldlMx'
129 , runFold
129 , runFold
131 -- ** Specialized Folds
134 -- ** Specialized Folds
133 , tapOffsetEvery
136 , tapOffsetEvery
@@ -320,7 +323,7 @@ where
321import Control.Concurrent (killThread, myThreadId, takeMVar, threadDelay)
324import Control.Concurrent (killThread, myThreadId, takeMVar, threadDelay)
322import Control.Exception
325import Control.Exception
323− (Exception, SomeException, AsyncException, fromException, mask_)
326+ (assert, Exception, SomeException, AsyncException, fromException, mask_)
324import Control.Monad (void, when, forever)
327import Control.Monad (void, when, forever)
325import Control.Monad.Catch (MonadCatch, throwM)
328import Control.Monad.Catch (MonadCatch, throwM)
326import Control.Monad.IO.Class (MonadIO(..))
329import Control.Monad.IO.Class (MonadIO(..))
@@ -343,13 +346,14 @@ import Prelude
343 takeWhile, drop, dropWhile, all, any, maximum, minimum, elem,
346 takeWhile, drop, dropWhile, all, any, maximum, minimum, elem,
344 notElem, null, head, tail, zipWith, lookup, foldr1, sequence,
347 notElem, null, head, tail, zipWith, lookup, foldr1, sequence,
345 (!!), scanl, scanl1, concatMap, replicate, enumFromTo, concat,
348 (!!), scanl, scanl1, concatMap, replicate, enumFromTo, concat,
346− reverse, iterate)
349+ reverse, iterate, splitAt)
348import qualified Control.Monad.Catch as MC
351import qualified Control.Monad.Catch as MC
349import qualified Control.Monad.Reader as Reader
352import qualified Control.Monad.Reader as Reader
350import qualified Control.Monad.State.Strict as State
353import qualified Control.Monad.State.Strict as State
351import qualified Prelude
354import qualified Prelude
356+import Fusion.Plugin.Types (Fuse(..))
353import Streamly.Internal.Mutable.Prim.Var
357import Streamly.Internal.Mutable.Prim.Var
354 (Prim, Var, readVar, newVar, modifyVar')
358 (Prim, Var, readVar, newVar, modifyVar')
355import Streamly.Internal.Data.Time.Units
359import Streamly.Internal.Data.Time.Units
@@ -358,6 +362,7 @@ import Streamly.Internal.Data.Time.Units
358import Streamly.Internal.Data.Atomics (atomicModifyIORefCAS_)
362import Streamly.Internal.Data.Atomics (atomicModifyIORefCAS_)
359import Streamly.Internal.Memory.Array.Types (Array(..))
363import Streamly.Internal.Memory.Array.Types (Array(..))
360import Streamly.Internal.Data.Fold.Types (Fold(..))
364import Streamly.Internal.Data.Fold.Types (Fold(..))
365+import Streamly.Internal.Data.Parse.Types (Parse(..))
361import Streamly.Internal.Data.Pipe.Types (Pipe(..), PipeState(..))
366import Streamly.Internal.Data.Pipe.Types (Pipe(..), PipeState(..))
362import Streamly.Internal.Data.Time.Clock (Clock(Monotonic), getTime)
367import Streamly.Internal.Data.Time.Clock (Clock(Monotonic), getTime)
363import Streamly.Internal.Data.Time.Units
368import Streamly.Internal.Data.Time.Units
@@ -374,6 +379,7 @@ import qualified Streamly.Internal.Memory.Array.Types as A
374import qualified Streamly.Internal.Data.Fold as FL
379import qualified Streamly.Internal.Data.Fold as FL
375import qualified Streamly.Memory.Ring as RB
380import qualified Streamly.Memory.Ring as RB
376import qualified Streamly.Internal.Data.Stream.StreamK as K
381import qualified Streamly.Internal.Data.Stream.StreamK as K
382+import qualified Streamly.Internal.Data.Parse.Types as PR
378------------------------------------------------------------------------------
384------------------------------------------------------------------------------
379-- Construction
385-- Construction
@@ -942,6 +948,184 @@ foldlS fstep begin (Stream step state) = Stream step' (Left (state, begin))
942 Skip s -> Skip (Right (Stream stp s))
948 Skip s -> Skip (Right (Stream stp s))
943 Stop -> Stop
949 Stop -> Stop
951+------------------------------------------------------------------------------
953+------------------------------------------------------------------------------
955+-- Inlined definition. Without the inline "serially/parser/take" benchmark
956+-- degrades and parseChunks does not fuse. Even using "inline" at the callsite
958+{-# INLINE splitAt #-}
959+splitAt :: Int -> [a] -> ([a],[a])
962+ | otherwise = splitAt' n ls
964+ splitAt' :: Int -> [a] -> ([a], [a])
965+ splitAt' _ [] = ([], [])
966+ splitAt' 1 (x:xs) = ([x], xs)
967+ splitAt' m (x:xs) = (x:xs', xs'')
969+ (xs', xs'') = splitAt' (m - 1) xs
971+-- | Run a 'Parse' over a stream.
972+{-# INLINE_NORMAL parselMx' #-}
975+ => (s -> a -> m (PR.Step s))
977+ -> (s -> m (Either String (Int, b)))
979+ -> m (Either String b)
980+parselMx' pstep initial extract (Stream step state) = do
981+ initial >>= go SPEC state []
985+ -- XXX currently we are using a dumb list based approach for backtracking
986+ -- buffer. This can be replaced by a sliding/ring buffer using Data.Array.
987+ -- That will allow us more efficient random back and forth movement.
989+ go !_ st buf !pst = do
990+ r <- step defState st
995+ -- PR.Keep 0 pst1 -> go SPEC s [] pst1
996+ PR.Keep n pst1 -> do
997+ assert (n <= length (x:buf)) (return ())
998+ go SPEC s (Prelude.take n (x:buf)) pst1
999+ PR.Back n pst1 -> do
1000+ assert (n <= length (x:buf)) (return ())
1001+ let (src0, buf1) = splitAt n (x:buf)
1002+ src = Prelude.reverse src0
1003+ gobuf SPEC s buf1 src pst1
1004+ PR.Hold pst1 -> go SPEC s (x:buf) pst1
1005+ PR.Halt pst1 -> fmap (fmap snd) (extract pst1)
1006+ Skip s -> go SPEC s buf pst
1007+ Stop -> fmap (fmap snd) (extract pst)
1009+ gobuf !_ s buf [] !pst = go SPEC s buf pst
1010+ gobuf !_ s buf (x:xs) !pst = do
1011+ pRes <- pstep pst x
1013+ -- PR.Keep 0 pst1 -> go SPEC s [] pst1
1014+ PR.Keep n pst1 -> do
1015+ assert (n <= length (x:buf)) (return ())
1016+ gobuf SPEC s (Prelude.take n (x:buf)) xs pst1
1017+ PR.Back n pst1 -> do
1018+ assert (n <= length (x:buf)) (return ())
1019+ let (src0, buf1) = splitAt n (x:buf)
1020+ src = Prelude.reverse src0 ++ xs
1021+ gobuf SPEC s buf1 src pst1
1022+ PR.Hold pst1 -> gobuf SPEC s (x:buf) xs pst1
1023+ PR.Halt pst1 -> fmap (fmap snd) (extract pst1)
1025+------------------------------------------------------------------------------
1027+------------------------------------------------------------------------------
1029+{-# ANN type ParseChunksState Fuse #-}
1030+data ParseChunksState x inpBuf st pst =
1031+ ParseChunksInit inpBuf st
1032+ | ParseChunksInitLeftOver inpBuf
1033+ | ParseChunksStream st inpBuf pst
1034+ | ParseChunksBuf inpBuf st inpBuf pst
1035+ | ParseChunksYield x (ParseChunksState x inpBuf st pst)
1037+{-# INLINE_NORMAL parseChunks #-}
1043+parseChunks (Parse pstep initial extract) (Stream step state) =
1044+ Stream stepOuter (ParseChunksInit [] state)
1048+ {-# INLINE_LATE stepOuter #-}
1049+ -- Buffer is empty, go to stream processing loop
1050+ stepOuter _ (ParseChunksInit [] st) = do
1051+ initial >>= return . Skip . ParseChunksStream st []
1053+ -- Buffer is not empty, go to buffered processing loop
1054+ stepOuter _ (ParseChunksInit src st) = do
1055+ initial >>= return . Skip . ParseChunksBuf src st []
1057+ -- XXX we just discard any leftover input at the end
1058+ stepOuter _ (ParseChunksInitLeftOver _) = return Stop
1060+ -- Buffer is empty process elements from the stream
1061+ stepOuter gst (ParseChunksStream st buf pst) = do
1062+ r <- step (adaptState gst) st
1065+ pRes <- pstep pst x
1067+ -- PR.Keep 0 pst1 -> go SPEC s [] pst1
1068+ PR.Keep n pst1 -> do
1069+ assert (n <= length (x:buf)) (return ())
1070+ let buf1 = Prelude.take n (x:buf)
1071+ return $ Skip $ ParseChunksStream s buf1 pst1
1072+ PR.Back n pst1 -> do
1073+ assert (n <= length (x:buf)) (return ())
1074+ let (src0, buf1) = splitAt n (x:buf)
1075+ src = Prelude.reverse src0
1076+ return $ Skip $ ParseChunksBuf src s buf1 pst1
1078+ return $ Skip $ ParseChunksStream s (x:buf) pst1
1080+ res <- extract pst1
1082+ Left err -> error err
1083+ -- XXX (0,a) common case
1085+ assert (n <= length (x:buf)) (return ())
1086+ let src = Prelude.reverse (Prelude.take n (x:buf))
1087+ return $ Skip $ ParseChunksYield a (ParseChunksInit src s)
1088+ Skip s -> return $ Skip $ ParseChunksStream s buf pst
1092+ Left err -> error err
1094+ let src = Prelude.reverse (Prelude.take n (buf))
1095+ return $ Skip $ ParseChunksYield a (ParseChunksInitLeftOver src)
1097+ -- go back to stream processing mode
1098+ stepOuter _ (ParseChunksBuf [] s buf pst) =
1099+ return $ Skip $ ParseChunksStream s buf pst
1101+ -- buffered processing loop
1102+ stepOuter _ (ParseChunksBuf (x:xs) s buf pst) = do
1103+ pRes <- pstep pst x
1105+ -- PR.Keep 0 pst1 ->
1106+ PR.Keep n pst1 -> do
1107+ assert (n <= length (x:buf)) (return ())
1108+ let buf1 = Prelude.take n (x:buf)
1109+ return $ Skip $ ParseChunksBuf xs s buf1 pst1
1110+ PR.Back n pst1 -> do
1111+ assert (n <= length (x:buf)) (return ())
1112+ let (src0, buf1) = splitAt n (x:buf)
1113+ src = Prelude.reverse src0 ++ xs
1114+ return $ Skip $ ParseChunksBuf src s buf1 pst1
1116+ return $ Skip $ ParseChunksBuf xs s (x:buf) pst1
1118+ res <- extract pst1
1120+ Left err -> error err
1121+ -- XXX have (0,a) case
1123+ assert (n <= length (x:buf)) (return ())
1124+ let src = Prelude.reverse (Prelude.take n (x:buf)) ++ xs
1125+ return $ Skip $ ParseChunksYield a (ParseChunksInit src s)
1127+ stepOuter _ (ParseChunksYield a next) = return $ Yield a next
945------------------------------------------------------------------------------
1129------------------------------------------------------------------------------
946-- Specialized Folds
1130-- Specialized Folds
947------------------------------------------------------------------------------
1131------------------------------------------------------------------------------