on master help sign in
Add array streams folds
commit206e353659a7e3c80601e037ac01dd6673d0b489 authorHarendra Kumar <harendra@composewell.com> dateThu 15 Apr 2021, 19:11 (6 years ago) parentcd916f5
5 files changed+766−14
src/Streamly/Internal/Data/Array/Stream/Fold/Foreign.hs+293 src/Streamly/Internal/Data/Array/Stream/Foreign.hs+310−5 src/Streamly/Internal/Data/Parser/ParserD.hs+1 src/Streamly/Internal/Data/Parser/ParserD/Type.hs+161−9 streamly.cabal+1
src/Streamly/Internal/Data/Array/Stream/Fold/Foreign.hsnew file+293
@@ -0,0 +1,293 @@
1+-- |
2+-- Module : Streamly.Internal.Data.Array.Stream.Fold.Foreign
3+-- Copyright : (c) 2021 Composewell Technologies
4+-- License : BSD-3-Clause
5+-- Maintainer : streamly@composewell.com
6+-- Stability : experimental
7+-- Portability : GHC
8+--
9+-- Fold a stream of foreign arrays. @Fold m a b@ in this module works
10+-- on a stream of "Array a" and produces an output of type @b@.
11+--
12+-- Though @Fold m a b@ in this module works on a stream of @Array a@ it is
13+-- different from @Data.Fold m (Array a) b@. While the latter works on arrays
14+-- as a whole treating them as atomic elements, the folds in this module can
15+-- work on the stream of arrays as if it is an element stream with all the
16+-- arrays coalesced together. This module allows adapting the element stream
17+-- folds in Data.Fold to correctly work on an array stream as if it is an
18+-- element stream. For example:
19+--
20+-- >>> import qualified Streamly.Data.Fold as Fold
21+-- >>> import qualified Streamly.Internal.Data.Array.Stream.Foreign as ArrayStream
22+-- >>> import qualified Streamly.Internal.Data.Array.Stream.Fold.Foreign as ArrayFold
23+-- >>> import qualified Streamly.Internal.Data.Stream.IsStream as Stream (arraysOf)
24+-- >>> import qualified Streamly.Prelude as Stream
25+--
26+-- >>> ArrayStream.fold (ArrayFold.fromFold (Fold.take 7 Fold.toList)) $ Stream.arraysOf 5 $ Stream.fromList "hello world"
27+-- "hello w"
28+--
29+module Streamly.Internal.Data.Array.Stream.Fold.Foreign
30+ (
31+ Fold (..)
32+
33+ -- * Construction
34+ , fromFold
35+ , fromArrayFold
36+
37+ -- * Mapping
38+ , rmapM
39+
40+ -- * Applicative
41+ , yield
42+ , yieldM
43+ , serialWith
44+
45+ -- * Monad
46+ , concatMap
47+
48+ -- * Transformation
49+ , take
50+ )
51+where
52+
53+import Control.Applicative (liftA2)
54+import Control.Exception (assert)
55+import Control.Monad.Catch (MonadThrow)
56+import Control.Monad.IO.Class (MonadIO(..))
57+import Foreign.ForeignPtr (touchForeignPtr)
58+import Foreign.Ptr (minusPtr, plusPtr)
59+import Foreign.Storable (Storable(..))
60+import GHC.ForeignPtr (ForeignPtr(..))
61+import GHC.Ptr (Ptr(..))
62+import GHC.Types (SPEC(..))
63+import Streamly.Internal.Data.Array.Foreign.Type (Array(..))
64+import Streamly.Internal.Data.Parser.ParserD (Initial(..), Step(..))
65+import Streamly.Internal.Data.Tuple.Strict (Tuple'(..))
66+
67+import qualified Streamly.Internal.Data.Array.Foreign as Array
68+import qualified Streamly.Internal.Data.Fold as Fold
69+import qualified Streamly.Internal.Data.Parser.ParserD as ParserD
70+import qualified Streamly.Internal.Data.Parser.ParserD.Type as ParserD
71+
72+import Prelude hiding (concatMap, take)
73+
74+-- | Array stream fold.
75+--
76+-- An array stream fold is basically an array stream "Parser" that does not
77+-- fail. In case of array stream folds the count in 'Partial', 'Continue' and
78+-- 'Done' is a count of elements that includes the leftover element count in
79+-- the array that is currently being processed by the parser. If none of the
80+-- elements is consumed by the parser the count is at least the whole array
81+-- length. If the whole array is consumed by the parser then the count will be
82+-- 0.
83+--
84+-- /Pre-release/
85+--
86+newtype Fold m a b = Fold (ParserD.Parser m (Array a) b)
87+
88+-------------------------------------------------------------------------------
89+-- Constructing array stream folds from element folds and parsers
90+-------------------------------------------------------------------------------
91+
92+-- | Convert an element 'Fold' into an array stream fold.
93+--
94+-- /Pre-release/
95+{-# INLINE fromFold #-}
96+fromFold :: forall m a b. (MonadIO m, Storable a) =>
97+ Fold.Fold m a b -> Fold m a b
98+fromFold (Fold.Fold fstep finitial fextract) =
99+ Fold (ParserD.Parser step initial fextract)
100+
101+ where
102+
103+ initial = do
104+ res <- finitial
105+ return
106+ $ case res of
107+ Fold.Partial s1 -> IPartial s1
108+ Fold.Done b -> IDone b
109+
110+ step s (Array fp@(ForeignPtr start _) end) = do
111+ goArray SPEC (Ptr start) s
112+
113+ where
114+
115+ goArray !_ !cur !fs | cur >= end = do
116+ assert (cur == end) (return ())
117+ liftIO $ touchForeignPtr fp
118+ return $ Partial 0 fs
119+ goArray !_ !cur !fs = do
120+ x <- liftIO $ peek cur
121+ res <- fstep fs x
122+ let elemSize = sizeOf (undefined :: a)
123+ next = cur `plusPtr` elemSize
124+ case res of
125+ Fold.Done b ->
126+ return $ Done ((end `minusPtr` next) `div` elemSize) b
127+ Fold.Partial fs1 ->
128+ goArray SPEC next fs1
129+
130+-- | Adapt an array stream fold.
131+--
132+-- /Pre-release/
133+{-# INLINE fromArrayFold #-}
134+fromArrayFold :: forall m a b. (MonadIO m) =>
135+ Fold.Fold m (Array a) b -> Fold m a b
136+fromArrayFold f = Fold $ ParserD.fromFold f
137+
138+-------------------------------------------------------------------------------
139+-- Functor
140+-------------------------------------------------------------------------------
141+
142+-- | Maps a function over the result of fold.
143+--
144+-- /Pre-release/
145+instance Functor m => Functor (Fold m a) where
146+ {-# INLINE fmap #-}
147+ fmap f (Fold p) = Fold $ fmap f p
148+
149+-- | Map a monadic function on the output of a fold.
150+--
151+-- /Pre-release/
152+{-# INLINE rmapM #-}
153+rmapM :: Monad m => (b -> m c) -> Fold m a b -> Fold m a c
154+rmapM f (Fold p) = Fold $ ParserD.rmapM f p
155+
156+-------------------------------------------------------------------------------
157+-- Sequential applicative
158+-------------------------------------------------------------------------------
159+
160+-- | A fold that always yields a pure value without consuming any input.
161+--
162+-- /Pre-release/
163+--
164+{-# INLINE yield #-}
165+yield :: Monad m => b -> Fold m a b
166+yield = Fold . ParserD.yield
167+
168+-- | A fold that always yields the result of an effectful action without
169+-- consuming any input.
170+--
171+-- /Pre-release/
172+--
173+{-# INLINE yieldM #-}
174+yieldM :: Monad m => m b -> Fold m a b
175+yieldM = Fold . ParserD.yieldM
176+
177+-- | Applies two folds sequentially on the input stream and combines their
178+-- results using the supplied function.
179+--
180+-- /Pre-release/
181+{-# INLINE serial_ #-}
182+serial_ :: MonadThrow m => Fold m x a -> Fold m x b -> Fold m x b
183+serial_ (Fold p1) (Fold p2) = Fold $ ParserD.noErrorUnsafeSplit_ p1 p2
184+
185+-- | Applies two folds sequentially on the input stream and combines their
186+-- results using the supplied function.
187+--
188+-- /Pre-release/
189+{-# INLINE serialWith #-}
190+serialWith :: MonadThrow m
191+ => (a -> b -> c) -> Fold m x a -> Fold m x b -> Fold m x c
192+serialWith f (Fold p1) (Fold p2) =
193+ Fold $ ParserD.noErrorUnsafeSplitWith f p1 p2
194+
195+-- | 'Applicative' form of 'serialWith'.
196+-- > (<*>) = serialWith id
197+instance MonadThrow m => Applicative (Fold m a) where
198+ {-# INLINE pure #-}
199+ pure = yield
200+
201+ {-# INLINE (<*>) #-}
202+ (<*>) = serialWith id
203+
204+ {-# INLINE (*>) #-}
205+ (*>) = serial_
206+
207+#if MIN_VERSION_base(4,10,0)
208+ {-# INLINE liftA2 #-}
209+ liftA2 f x = (<*>) (fmap f x)
210+#endif
211+
212+-------------------------------------------------------------------------------
213+-- Monad
214+-------------------------------------------------------------------------------
215+
216+-- | Applies a fold on the input stream, generates the next fold from the
217+-- output of the previously applied fold and then applies that fold.
218+--
219+-- /Pre-release/
220+--
221+{-# INLINE concatMap #-}
222+concatMap :: MonadThrow m =>
223+ (b -> Fold m a c) -> Fold m a b -> Fold m a c
224+concatMap func (Fold p) =
225+ Fold $ ParserD.noErrorUnsafeConcatMap (\x -> let Fold y = func x in y) p
226+
227+-- | Monad instance applies folds sequentially. Next fold can depend on the
228+-- output of the previous fold. See 'concatMap'.
229+--
230+-- > (>>=) = flip concatMap
231+instance MonadThrow m => Monad (Fold m a) where
232+ {-# INLINE return #-}
233+ return = pure
234+
235+ {-# INLINE (>>=) #-}
236+ (>>=) = flip concatMap
237+
238+ {-# INLINE (>>) #-}
239+ (>>) = (*>)
240+
241+-------------------------------------------------------------------------------
242+-- Array to Array folds
243+-------------------------------------------------------------------------------
244+
245+{-# INLINE take #-}
246+take :: forall m a b. (Monad m, Storable a) => Int -> Fold m a b -> Fold m a b
247+take n (Fold (ParserD.Parser step1 initial1 extract1)) =
248+ Fold $ ParserD.Parser step initial extract
249+
250+ where
251+
252+ initial = do
253+ res <- initial1
254+ case res of
255+ IPartial s ->
256+ if n > 0
257+ then return $ IPartial $ Tuple' n s
258+ else IDone <$> extract1 s
259+ IDone b -> return $ IDone b
260+ IError err -> return $ IError err
261+
262+ step (Tuple' i r) arr = do
263+ let len = Array.length arr
264+ if len <= i
265+ then do
266+ res <- step1 r arr
267+ case res of
268+ Partial j s ->
269+ if (i + j - len) > 0
270+ then return $ Partial j (Tuple' (i + j - len) s)
271+ else Done j <$> extract1 s
272+ Continue j s ->
273+ if (i + j - len) > 0
274+ then return $ Continue j (Tuple' (i + j - len) s)
275+ else Done j <$> extract1 s
276+ Done j b -> return $ Done j b
277+ Error err -> return $ Error err
278+ else do
279+ let !(Array (ForeignPtr start contents) _) = arr
280+ sz = sizeOf (undefined :: a)
281+ end = Ptr start `plusPtr` (i * sz)
282+ arr1 = Array (ForeignPtr start contents) end
283+ remaining = len - i
284+ res <- step1 r arr1
285+ case res of
286+ Partial 0 s -> Done remaining <$> extract1 s
287+ Partial j s -> return $ Partial (remaining + j) (Tuple' j s)
288+ Continue 0 s -> Done remaining <$> extract1 s
289+ Continue j s -> return $ Continue (remaining + j) (Tuple' j s)
290+ Done j b -> return $ Done (remaining + j) b
291+ Error err -> return $ Error err
292+
293+ extract (Tuple' _ r) = extract1 r
src/Streamly/Internal/Data/Array/Stream/Foreign.hs+310−5
@@ -22,6 +22,10 @@ module Streamly.Internal.Data.Array.Stream.Foreign
2222 , unlines
2323
2424 -- * Elimination
25+ , fold
26+ -- , parse
27+ , parseD
28+ , foldMany
2529 , toArray
2630
2731 -- * Compaction
@@ -39,30 +43,40 @@ where
3943
4044#include "inline.hs"
4145
46+import Control.Exception (assert)
47+import Control.Monad.Catch (MonadThrow, throwM)
4248import Control.Monad.IO.Class (MonadIO(..))
4349import Data.Word (Word8)
4450import Foreign.ForeignPtr (withForeignPtr, touchForeignPtr)
4551import Foreign.ForeignPtr.Unsafe (unsafeForeignPtrToPtr)
4652import Foreign.Ptr (minusPtr, plusPtr, castPtr)
4753import Foreign.Storable (Storable(..))
54+import Fusion.Plugin.Types (Fuse(..))
55+import GHC.Exts (SpecConstrAnnotation(..))
4856import GHC.ForeignPtr (ForeignPtr(..))
4957import GHC.Ptr (Ptr(..))
50−import Prelude hiding (length, null, last, map, (!!), read, concat, unlines)
58+import GHC.Types (SPEC(..))
59+import Prelude hiding (null, last, (!!), read, concat, unlines)
5160
5261#if !defined(mingw32_HOST_OS)
5362import Streamly.Internal.FileSystem.FDIO (IOVec(..))
5463#endif
55−import Streamly.Internal.Data.Array.Foreign.Type (Array(..), length)
64+import Streamly.Internal.Data.Array.Foreign.Type (Array(..))
5665import Streamly.Internal.Data.Fold.Type (Fold(..))
66+import Streamly.Internal.Data.Parser (ParseError(..))
5767import Streamly.Internal.Data.Stream.Serial (SerialT)
5868import Streamly.Internal.Data.Stream.StreamK.Type (IsStream)
59−import Streamly.Internal.Data.SVar (adaptState)
69+import Streamly.Internal.Data.SVar (adaptState, defState)
6070
6171import qualified Streamly.Internal.Data.Array.Foreign as A
72+import qualified Streamly.Internal.Data.Array.Foreign as Array
6273import qualified Streamly.Internal.Data.Array.Foreign.Type as A
6374import qualified Streamly.Internal.Data.Array.Foreign.Mut.Type as MA
6475import qualified Streamly.Internal.Data.Array.Stream.Mut.Foreign as AS
76+import qualified Streamly.Internal.Data.Array.Stream.Fold.Foreign as ASF
6577import qualified Streamly.Internal.Data.Fold.Type as FL
78+import qualified Streamly.Internal.Data.Parser as PR
79+import qualified Streamly.Internal.Data.Parser.ParserD as PRD
6680import qualified Streamly.Internal.Data.Stream.IsStream as S
6781import qualified Streamly.Internal.Data.Stream.StreamD as D
6882
@@ -300,6 +314,56 @@ splitOnSuffix byte s =
300314-- Elimination
301315-------------------------------------------------------------------------------
302316
317+-- When we have to take an array partially, take the last part of the array.
318+{-# INLINE takeArrayListRev #-}
319+takeArrayListRev :: forall a. Storable a => Int -> [Array a] -> [Array a]
320+takeArrayListRev = go
321+
322+ where
323+
324+ go _ [] = []
325+ go n _ | n <= 0 = []
326+ go n (x:xs) =
327+ let len = Array.length x
328+ in if n > len
329+ then x : go (n - len) xs
330+ else if n == len
331+ then [x]
332+ else let !(Array (ForeignPtr _ contents) end) = x
333+ sz = sizeOf (undefined :: a)
334+ !(Ptr start) = end `plusPtr` negate (n * sz)
335+ in [Array (ForeignPtr start contents) end]
336+
337+-- When we have to take an array partially, take the last part of the array in
338+-- the first split.
339+{-# INLINE splitAtArrayListRev #-}
340+splitAtArrayListRev :: forall a. Storable a =>
341+ Int -> [Array a] -> ([Array a],[Array a])
342+splitAtArrayListRev n ls
343+ | n <= 0 = ([], ls)
344+ | otherwise = go n ls
345+ where
346+ go :: Int -> [Array a] -> ([Array a], [Array a])
347+ go _ [] = ([], [])
348+ go m (x:xs) =
349+ let len = Array.length x
350+ (xs', xs'') = go (m - len) xs
351+ in if m > len
352+ then (x:xs', xs'')
353+ else if m == len
354+ then ([x],xs)
355+ else let !(Array (ForeignPtr start contents) end) = x
356+ sz = sizeOf (undefined :: a)
357+ end1 = end `plusPtr` negate (m * sz)
358+ arr2 = Array (ForeignPtr start contents) end1
359+ !(Ptr addrEnd1) = end1
360+ arr1 = Array (ForeignPtr addrEnd1 contents) end
361+ in ([arr1], arr2:xs)
362+
363+-------------------------------------------------------------------------------
364+-- Fold to a single Array
365+-------------------------------------------------------------------------------
366+
303367-- XXX Both of these implementations of splicing seem to perform equally well.
304368-- We need to perform benchmarks over a range of sizes though.
305369
@@ -326,7 +390,7 @@ _spliceArrays :: (MonadIO m, Storable a)
326390 => SerialT m (Array a) -> m (Array a)
327391_spliceArrays s = do
328392 buffered <- S.foldr S.cons S.nil s
329− len <- S.sum (S.map length buffered)
393+ len <- S.sum (S.map Array.length buffered)
330394 arr <- liftIO $ MA.newArray len
331395 end <- S.foldlM' writeArr (return $ MA.aEnd arr) s
332396 return $ A.unsafeFreeze $ arr {MA.aEnd = end}
@@ -344,7 +408,7 @@ _spliceArraysBuffered :: (MonadIO m, Storable a)
344408 => SerialT m (Array a) -> m (Array a)
345409_spliceArraysBuffered s = do
346410 buffered <- S.foldr S.cons S.nil s
347− len <- S.sum (S.map length buffered)
411+ len <- S.sum (S.map Array.length buffered)
348412 A.unsafeFreeze <$> spliceArraysLenUnsafe len (S.map A.unsafeThaw s)
349413
350414{-# INLINE spliceArraysRealloced #-}
@@ -357,6 +421,8 @@ spliceArraysRealloced s = do
357421 arr <- S.foldlM' MA.spliceWithDoubling idst (S.map A.unsafeThaw s)
358422 liftIO $ A.unsafeFreeze <$> MA.shrinkToFit arr
359423
424+-- XXX This should just be "fold A.write"
425+--
360426-- | Given a stream of arrays, splice them all together to generate a single
361427-- array. The stream must be /finite/.
362428--
@@ -385,3 +451,242 @@ _toArraysOf :: (MonadIO m, Storable a)
385451 => Int -> Fold m a (SerialT Identity (Array a))
386452_toArraysOf n = FL.chunksOf n (A.writeNF n) FL.toStream
387453-}
454+
455+-------------------------------------------------------------------------------
456+-- Parsing
457+-------------------------------------------------------------------------------
458+
459+-- GHC parser does not accept {-# ANN type [] NoSpecConstr #-}, so we need
460+-- to make a newtype.
461+{-# ANN type List NoSpecConstr #-}
462+newtype List a = List {getList :: [a]}
463+
464+{-# INLINE_NORMAL parseD #-}
465+parseD ::
466+ forall m a b. (MonadIO m, MonadThrow m, Storable a)
467+ => PRD.Parser m (Array a) b
468+ -> D.Stream m (Array.Array a)
469+ -> m (b, D.Stream m (Array.Array a))
470+parseD (PRD.Parser pstep initial extract) stream@(D.Stream step state) = do
471+ res <- initial
472+ case res of
473+ PRD.IPartial s -> go SPEC state (List []) s
474+ PRD.IDone b -> return (b, stream)
475+ PRD.IError err -> throwM $ ParseError err
476+
477+ where
478+
479+ -- "backBuf" contains last few items in the stream that we may have to
480+ -- backtrack to.
481+ --
482+ -- XXX currently we are using a dumb list based approach for backtracking
483+ -- buffer. This can be replaced by a sliding/ring buffer using Data.Array.
484+ -- That will allow us more efficient random back and forth movement.
485+ {-# INLINE go #-}
486+ go !_ st backBuf !pst = do
487+ r <- step defState st
488+ case r of
489+ D.Yield x s -> gobuf SPEC [x] s backBuf pst
490+ D.Skip s -> go SPEC s backBuf pst
491+ D.Stop -> do
492+ b <- extract pst
493+ return (b, D.nil)
494+
495+ gobuf !_ [] s backBuf !pst = go SPEC s backBuf pst
496+ gobuf !_ (x:xs) s backBuf !pst = do
497+ pRes <- pstep pst x
498+ case pRes of
499+ PR.Partial 0 pst1 ->
500+ gobuf SPEC xs s (List []) pst1
501+ PR.Partial n pst1 -> do
502+ assert
503+ (n <= sum (map Array.length (x:getList backBuf)))
504+ (return ())
505+ let src0 = takeArrayListRev n (x:getList backBuf)
506+ src = Prelude.reverse src0 ++ xs
507+ gobuf SPEC src s (List []) pst1
508+ PR.Continue 0 pst1 ->
509+ gobuf SPEC xs s (List (x:getList backBuf)) pst1
510+ PR.Continue n pst1 -> do
511+ assert
512+ (n <= sum (map Array.length (x:getList backBuf)))
513+ (return ())
514+ let (src0, buf1) = splitAtArrayListRev n (x:getList backBuf)
515+ src = Prelude.reverse src0 ++ xs
516+ gobuf SPEC src s (List buf1) pst1
517+ PR.Done 0 b ->
518+ return (b, D.Stream step s)
519+ PR.Done n b -> do
520+ assert
521+ (n <= sum (map Array.length (x:getList backBuf)))
522+ (return ())
523+ let src0 = takeArrayListRev n (x:getList backBuf)
524+ src = Prelude.reverse src0 ++ xs
525+ return (b, D.append (D.fromList src) (D.Stream step s))
526+ PR.Error err -> throwM $ ParseError err
527+
528+{-
529+-- | Parse an array stream using the supplied 'Parser'. Returns the parse
530+-- result and the unconsumed stream. Throws 'ParseError' if the parse fails.
531+--
532+-- /Internal/
533+--
534+{-# INLINE parse #-}
535+parse ::
536+ (MonadIO m, MonadThrow m, Storable a)
537+ => PRD.Parser m a b
538+ -> SerialT m (A.Array a)
539+ -> m (b, SerialT m (A.Array a))
540+parse p s = fmap D.fromStreamD <$> parseD p (D.toStreamD s)
541+-}
542+
543+-- | Fold an array stream using the supplied array stream 'Fold'.
544+--
545+-- /Pre-release/
546+--
547+{-# INLINE fold #-}
548+fold :: (MonadIO m, MonadThrow m, Storable a) =>
549+ ASF.Fold m a b -> SerialT m (A.Array a) -> m b
550+fold (ASF.Fold p) s = fst <$> parseD p (D.toStreamD s)
551+
552+{-# ANN type ParseChunksState Fuse #-}
553+data ParseChunksState x inpBuf st pst =
554+ ParseChunksInit inpBuf st
555+ | ParseChunksInitLeftOver inpBuf
556+ | ParseChunksStream st inpBuf !pst
557+ | ParseChunksBuf inpBuf st inpBuf !pst
558+ | ParseChunksYield x (ParseChunksState x inpBuf st pst)
559+
560+{-# INLINE_NORMAL foldManyD #-}
561+foldManyD
562+ :: (MonadThrow m, Storable a)
563+ => ASF.Fold m a b
564+ -> D.Stream m (Array a)
565+ -> D.Stream m b
566+foldManyD (ASF.Fold (PRD.Parser pstep initial extract)) (D.Stream step state) =
567+ D.Stream stepOuter (ParseChunksInit [] state)
568+
569+ where
570+
571+ {-# INLINE_LATE stepOuter #-}
572+ -- Buffer is empty, get the first element from the stream, initialize the
573+ -- fold and then go to stream processing loop.
574+ stepOuter gst (ParseChunksInit [] st) = do
575+ r <- step (adaptState gst) st
576+ case r of
577+ D.Yield x s -> do
578+ res <- initial
579+ case res of
580+ PRD.IPartial ps ->
581+ return $ D.Skip $ ParseChunksBuf [x] s [] ps
582+ PRD.IDone pb ->
583+ let next = ParseChunksInit [x] s
584+ in return $ D.Skip $ ParseChunksYield pb next
585+ PRD.IError err -> throwM $ ParseError err
586+ D.Skip s -> return $ D.Skip $ ParseChunksInit [] s
587+ D.Stop -> return D.Stop
588+
589+ -- Buffer is not empty, go to buffered processing loop
590+ stepOuter _ (ParseChunksInit src st) = do
591+ res <- initial
592+ case res of
593+ PRD.IPartial ps ->
594+ return $ D.Skip $ ParseChunksBuf src st [] ps
595+ PRD.IDone pb ->
596+ let next = ParseChunksInit src st
597+ in return $ D.Skip $ ParseChunksYield pb next
598+ PRD.IError err -> throwM $ ParseError err
599+
600+ -- XXX we just discard any leftover input at the end
601+ stepOuter _ (ParseChunksInitLeftOver _) = return D.Stop
602+
603+ -- Buffer is empty, process elements from the stream
604+ stepOuter gst (ParseChunksStream st backBuf pst) = do
605+ r <- step (adaptState gst) st
606+ case r of
607+ D.Yield x s -> do
608+ pRes <- pstep pst x
609+ case pRes of
610+ PR.Partial 0 pst1 ->
611+ return $ D.Skip $ ParseChunksStream s [] pst1
612+ PR.Partial n pst1 -> do
613+ assert
614+ (n <= sum (map Array.length (x:backBuf)))
615+ (return ())
616+ let src0 = takeArrayListRev n (x:backBuf)
617+ src = Prelude.reverse src0
618+ return $ D.Skip $ ParseChunksBuf src s [] pst1
619+ PR.Continue 0 pst1 ->
620+ return $ D.Skip $ ParseChunksStream s (x:backBuf) pst1
621+ PR.Continue n pst1 -> do
622+ assert
623+ (n <= sum (map Array.length (x:backBuf)))
624+ (return ())
625+ let (src0, buf1) = splitAtArrayListRev n (x:backBuf)
626+ src = Prelude.reverse src0
627+ return $ D.Skip $ ParseChunksBuf src s buf1 pst1
628+ PR.Done 0 b -> do
629+ return $ D.Skip $
630+ ParseChunksYield b (ParseChunksInit [] s)
631+ PR.Done n b -> do
632+ assert
633+ (n <= sum (map Array.length (x:backBuf)))
634+ (return ())
635+ let src0 = takeArrayListRev n (x:backBuf)
636+ let src = Prelude.reverse src0
637+ return $ D.Skip $
638+ ParseChunksYield b (ParseChunksInit src s)
639+ PR.Error err -> throwM $ ParseError err
640+ D.Skip s -> return $ D.Skip $ ParseChunksStream s backBuf pst
641+ D.Stop -> do
642+ b <- extract pst
643+ let src = Prelude.reverse backBuf
644+ return $ D.Skip $
645+ ParseChunksYield b (ParseChunksInitLeftOver src)
646+
647+ -- go back to stream processing mode
648+ stepOuter _ (ParseChunksBuf [] s buf pst) =
649+ return $ D.Skip $ ParseChunksStream s buf pst
650+
651+ -- buffered processing loop
652+ stepOuter _ (ParseChunksBuf (x:xs) s backBuf pst) = do
653+ pRes <- pstep pst x
654+ case pRes of
655+ PR.Partial 0 pst1 ->
656+ return $ D.Skip $ ParseChunksBuf xs s [] pst1
657+ PR.Partial n pst1 -> do
658+ assert (n <= sum (map Array.length (x:backBuf))) (return ())
659+ let src0 = takeArrayListRev n (x:backBuf)
660+ src = Prelude.reverse src0 ++ xs
661+ return $ D.Skip $ ParseChunksBuf src s [] pst1
662+ PR.Continue 0 pst1 ->
663+ return $ D.Skip $ ParseChunksBuf xs s (x:backBuf) pst1
664+ PR.Continue n pst1 -> do
665+ assert (n <= sum (map Array.length (x:backBuf))) (return ())
666+ let (src0, buf1) = splitAtArrayListRev n (x:backBuf)
667+ src = Prelude.reverse src0 ++ xs
668+ return $ D.Skip $ ParseChunksBuf src s buf1 pst1
669+ PR.Done 0 b ->
670+ return $ D.Skip $ ParseChunksYield b (ParseChunksInit xs s)
671+ PR.Done n b -> do
672+ assert (n <= sum (map Array.length (x:backBuf))) (return ())
673+ let src0 = takeArrayListRev n (x:backBuf)
674+ src = Prelude.reverse src0 ++ xs
675+ return $ D.Skip $ ParseChunksYield b (ParseChunksInit src s)
676+ PR.Error err -> throwM $ ParseError err
677+
678+ stepOuter _ (ParseChunksYield a next) = return $ D.Yield a next
679+
680+-- | Apply an array stream 'Fold' repeatedly on an array stream and emit the
681+-- fold outputs in the output stream.
682+--
683+-- See "Streamly.Prelude.foldMany" for more details.
684+--
685+-- /Pre-release/
686+{-# INLINE foldMany #-}
687+foldMany
688+ :: (IsStream t, MonadThrow m, Storable a)
689+ => ASF.Fold m a b
690+ -> t m (Array a)
691+ -> t m b
692+foldMany p m = D.fromStreamD $ foldManyD p (D.toStreamD m)
src/Streamly/Internal/Data/Parser/ParserD.hs+1
@@ -16,6 +16,7 @@ module Streamly.Internal.Data.Parser.ParserD
1616 , ParseError (..)
1717 , Step (..)
1818 , Initial (..)
19+ , rmapM
1920
2021 -- First order parsers
2122 -- * Accumulators
src/Streamly/Internal/Data/Parser/ParserD/Type.hs+161−9
@@ -115,6 +115,7 @@ module Streamly.Internal.Data.Parser.ParserD.Type
115115 , Step (..)
116116 , Parser (..)
117117 , ParseError (..)
118+ , rmapM
118119
119120 , yield
120121 , yieldM
@@ -129,13 +130,15 @@ module Streamly.Internal.Data.Parser.ParserD.Type
129130 , alt
130131 , concatMap
131132
133+ , noErrorUnsafeSplit_
132134 , noErrorUnsafeSplitWith
135+ , noErrorUnsafeConcatMap
133136 )
134137where
135138
136139import Control.Applicative (Alternative(..), liftA2)
137140import Control.Exception (assert, Exception(..))
138−import Control.Monad (MonadPlus(..))
141+import Control.Monad (MonadPlus(..), (>=>))
139142import Control.Monad.Catch (MonadCatch, try, throwM, MonadThrow)
140143import Data.Bifunctor (Bifunctor(..))
141144import Fusion.Plugin.Types (Fuse(..))
@@ -264,6 +267,18 @@ instance Functor (Step s) where
264267 fmap f (Done n b) = Done n (f b)
265268 fmap _ (Error err) = Error err
266269
270+-- | Map a monadic function over the result @b@ in @Step s b@.
271+--
272+-- /Internal/
273+{-# INLINE mapMStep #-}
274+mapMStep :: Applicative m => (a -> m b) -> Step s a -> m (Step s b)
275+mapMStep f res =
276+ case res of
277+ Partial n s -> pure $ Partial n s
278+ Done n b -> Done n <$> f b
279+ Continue n s -> pure $ Continue n s
280+ Error err -> pure $ Error err
281+
267282-- | A parser is a fold that can fail and is represented as @Parser step
268283-- initial extract@. Before we drive a parser we call the @initial@ action to
269284-- retrieve the initial state of the fold. The parser driver invokes @step@
@@ -303,6 +318,27 @@ instance Functor m => Functor (Parser m a) where
303318 step s b = fmap2 f (step1 s b)
304319 fmap2 g = fmap (fmap g)
305320
321+------------------------------------------------------------------------------
322+-- Mapping on the output
323+------------------------------------------------------------------------------
324+
325+-- | Map a monadic function on the output of a parser.
326+--
327+-- /Pre-release/
328+{-# INLINE rmapM #-}
329+rmapM :: Monad m => (b -> m c) -> Parser m a b -> Parser m a c
330+rmapM f (Parser step initial extract) = Parser step1 initial1 (extract >=> f)
331+
332+ where
333+
334+ initial1 = do
335+ res <- initial
336+ case res of
337+ IPartial x -> return $ IPartial x
338+ IDone a -> IDone <$> f a
339+ IError err -> return $ IError err
340+ step1 s a = step s a >>= mapMStep f
341+
306342-- | See 'Streamly.Internal.Data.Parser.yield'.
307343--
308344-- /Pre-release/
@@ -454,7 +490,6 @@ noErrorUnsafeSplitWith func (Parser stepL initialL extractL)
454490 IError err -> error $ "noErrorUnsafeSplitWith: cannot use a "
455491 ++ "failing parser. Parser failed with: " ++ err
456492
457−
458493{-# ANN type SeqAState Fuse #-}
459494data SeqAState sl sr = SeqAL sl | SeqAR sr
460495
@@ -484,19 +519,75 @@ split_ (Parser stepL initialL extractL) (Parser stepR initialR extractR) =
484519
485520 -- Note: For the composed parse to terminate, the left parser has to be
486521 -- a terminating parser returning a Done at some point.
487− step (SeqAL st) a =
488− (\resL initR -> case resL of
522+ step (SeqAL st) a = do
523+ -- Important: Do not use Applicative here. Applicative somehow caused
524+ -- the right action to run many times, not sure why though.
525+ resL <- stepL st a
526+ case resL of
489527 -- Note: this leads to buffering even if we are not in an
490528 -- Alternative composition.
491− Partial n s -> Continue n (SeqAL s)
492− Continue n s -> Continue n (SeqAL s)
529+ Partial n s -> return $ Continue n (SeqAL s)
530+ Continue n s -> return $ Continue n (SeqAL s)
493531 Done n _ -> do
494− case initR of
532+ initR <- initialR
533+ return $ case initR of
495534 IPartial s -> Continue n (SeqAR s)
496535 IDone b -> Done n b
497536 IError err -> Error err
498− -- XXX should we sequence initialR monadically?
499− Error err -> Error err) <$> stepL st a <*> initialR
537+ Error err -> return $ Error err
538+
539+ step (SeqAR st) a =
540+ (\case
541+ Partial n s -> Partial n (SeqAR s)
542+ Continue n s -> Continue n (SeqAR s)
543+ Done n b -> Done n b
544+ Error err -> Error err) <$> stepR st a
545+
546+ extract (SeqAR sR) = extractR sR
547+ extract (SeqAL sL) = do
548+ _ <- extractL sL
549+ res <- initialR
550+ case res of
551+ IPartial sR -> extractR sR
552+ IDone rR -> return rR
553+ IError err -> throwM $ ParseError err
554+
555+{-# INLINE noErrorUnsafeSplit_ #-}
556+noErrorUnsafeSplit_ :: MonadThrow m => Parser m x a -> Parser m x b -> Parser m x b
557+noErrorUnsafeSplit_ (Parser stepL initialL extractL) (Parser stepR initialR extractR) =
558+ Parser step initial extract
559+
560+ where
561+
562+ initial = do
563+ resL <- initialL
564+ case resL of
565+ IPartial sl -> return $ IPartial $ SeqAL sl
566+ IDone _ -> do
567+ resR <- initialR
568+ return $ case resR of
569+ IPartial sr -> IPartial $ SeqAR sr
570+ IDone br -> IDone br
571+ IError err -> IError err
572+ IError err -> return $ IError err
573+
574+ -- Note: For the composed parse to terminate, the left parser has to be
575+ -- a terminating parser returning a Done at some point.
576+ step (SeqAL st) a = do
577+ -- Important: Please do not use Applicative here. Applicative somehow
578+ -- caused the next action to run many times in the "tar" parsing code,
579+ -- not sure why though.
580+ resL <- stepL st a
581+ case resL of
582+ Partial n s -> return $ Partial n (SeqAL s)
583+ Continue n s -> return $ Continue n (SeqAL s)
584+ Done n _ -> do
585+ initR <- initialR
586+ return $ case initR of
587+ IPartial s -> Partial n (SeqAR s)
588+ IDone b -> Done n b
589+ IError err -> Error err
590+ Error err -> return $ Error err
500591
501592 step (SeqAR st) a =
502593 (\case
@@ -923,6 +1014,64 @@ concatMap func (Parser stepL initialL extractL) = Parser step initial extract
9231014 extract (ConcatParseR _ s extractR) = extractR s
9241015 extract (ConcatParseL sL) = extractL sL >>= extractP . func
9251016
1017+{-# INLINE noErrorUnsafeConcatMap #-}
1018+noErrorUnsafeConcatMap :: MonadThrow m =>
1019+ (b -> Parser m a c) -> Parser m a b -> Parser m a c
1020+noErrorUnsafeConcatMap func (Parser stepL initialL extractL) =
1021+ Parser step initial extract
1022+
1023+ where
1024+
1025+ {-# INLINE initializeR #-}
1026+ initializeR (Parser stepR initialR extractR) = do
1027+ resR <- initialR
1028+ return $ case resR of
1029+ IPartial sr -> IPartial $ ConcatParseR stepR sr extractR
1030+ IDone br -> IDone br
1031+ IError err -> IError err
1032+
1033+ initial = do
1034+ res <- initialL
1035+ case res of
1036+ IPartial s -> return $ IPartial $ ConcatParseL s
1037+ IDone b -> initializeR (func b)
1038+ IError err -> return $ IError err
1039+
1040+ {-# INLINE initializeRL #-}
1041+ initializeRL n (Parser stepR initialR extractR) = do
1042+ resR <- initialR
1043+ return $ case resR of
1044+ IPartial sr -> Partial n $ ConcatParseR stepR sr extractR
1045+ IDone br -> Done n br
1046+ IError err -> Error err
1047+
1048+ step (ConcatParseL st) a = do
1049+ r <- stepL st a
1050+ case r of
1051+ Partial n s -> return $ Partial n (ConcatParseL s)
1052+ Continue n s -> return $ Continue n (ConcatParseL s)
1053+ Done n b -> initializeRL n (func b)
1054+ Error err -> return $ Error err
1055+
1056+ step (ConcatParseR stepR st extractR) a = do
1057+ r <- stepR st a
1058+ return $ case r of
1059+ Partial n s -> Partial n $ ConcatParseR stepR s extractR
1060+ Continue n s -> Continue n $ ConcatParseR stepR s extractR
1061+ Done n b -> Done n b
1062+ Error err -> Error err
1063+
1064+ {-# INLINE extractP #-}
1065+ extractP (Parser _ initialR extractR) = do
1066+ res <- initialR
1067+ case res of
1068+ IPartial s -> extractR s
1069+ IDone b -> return b
1070+ IError err -> throwM $ ParseError err
1071+
1072+ extract (ConcatParseR _ s extractR) = extractR s
1073+ extract (ConcatParseL sL) = extractL sL >>= extractP . func
1074+
9261075-- Note: The monad instance has quadratic performance complexity. It works fine
9271076-- for small number of compositions but for a scalable implementation we need a
9281077-- CPS version.
@@ -936,6 +1085,9 @@ instance MonadThrow m => Monad (Parser m a) where
9361085 {-# INLINE (>>=) #-}
9371086 (>>=) = flip concatMap
9381087
1088+ {-# INLINE (>>) #-}
1089+ (>>) = (*>)
1090+
9391091-- | See documentation of 'Streamly.Internal.Data.Parser.ParserK.Type.Parser'.
9401092--
9411093instance MonadCatch m => MonadPlus (Parser m a) where
streamly.cabal+1
@@ -481,6 +481,7 @@ library
481481 , Streamly.Internal.Data.SmallArray
482482 , Streamly.Internal.Data.Array.Stream.Mut.Foreign
483483 , Streamly.Internal.Data.Array.Stream.Foreign
484+ , Streamly.Internal.Data.Array.Stream.Fold.Foreign
484485
485486 -- Memory storage
486487 , Streamly.Internal.Ring.Foreign