2-- Module : Streamly.Internal.Data.Array.Stream.Foreign
3-- Copyright : (c) 2019 Composewell Technologies
4-- License : BSD3-3-Clause
5-- Maintainer : streamly@composewell.com
6-- Stability : pre-release
9-- Combinators to efficiently manipulate streams of immutable arrays.
11module Streamly.Internal.Data.Array.Stream.Foreign
16 -- * Flattening to elements
51import Data.Bifunctor (second)
52import Control.Exception (assert)
53import Control.Monad.Catch (MonadThrow, throwM)
54import Control.Monad.IO.Class (MonadIO(..))
55#if __GLASGOW_HASKELL__ < 808
56import Data.Semigroup (Semigroup(..))
58import Data.Word (Word8)
59import Foreign.ForeignPtr (touchForeignPtr)
60import Foreign.Ptr (minusPtr, plusPtr, castPtr)
61import Foreign.Storable (Storable(..))
62import Fusion.Plugin.Types (Fuse(..))
63import GHC.Exts (SpecConstrAnnotation(..))
64import GHC.ForeignPtr (ForeignPtr(..))
65import GHC.Ptr (Ptr(..))
66import GHC.Types (SPEC(..))
67import Prelude hiding (null, last, (!!), read, concat, unlines)
69import Streamly.Internal.Data.Array.Foreign.Mut.Type
70 (arrayToFptrContents, fptrToArrayContents, touch)
71import Streamly.Internal.Data.Array.Foreign.Type (Array(..))
72import Streamly.Internal.Data.Fold.Type (Fold(..))
73import Streamly.Internal.Data.Parser (ParseError(..))
74import Streamly.Internal.Data.Stream.Serial (SerialT)
75import Streamly.Internal.Data.Stream.IsStream.Type
76 (IsStream, fromStreamD, toStreamD)
77import Streamly.Internal.Data.SVar (adaptState, defState)
78import Streamly.Internal.Data.Array.Foreign.Mut.Type
79 (memcpy, allocBytesToElemCount)
81import qualified Streamly.Internal.Data.Array.Foreign as A
82import qualified Streamly.Internal.Data.Array.Foreign as Array
83import qualified Streamly.Internal.Data.Array.Foreign.Type as A
84import qualified Streamly.Internal.Data.Array.Foreign.Mut.Type as MA
85import qualified Streamly.Internal.Data.Array.Stream.Mut.Foreign as AS
86import qualified Streamly.Internal.Data.Array.Stream.Fold.Foreign as ASF
87import qualified Streamly.Internal.Data.Fold.Type as FL
88import qualified Streamly.Internal.Data.Parser as PR
89import qualified Streamly.Internal.Data.Parser.ParserD as PRD
90import qualified Streamly.Internal.Data.Stream.IsStream as S
91import qualified Streamly.Internal.Data.Stream.StreamD as D
93-- XXX Since these are immutable arrays MonadIO constraint can be removed from
96-------------------------------------------------------------------------------
98-------------------------------------------------------------------------------
100-- | @arraysOf n stream@ groups the elements in the input stream into arrays of
103-- > arraysOf n = Stream.chunksOf n (Array.writeN n)
106{-# INLINE arraysOf #-}
107arraysOf :: (IsStream t, MonadIO m, Storable a)
108 => Int -> t m a -> t m (Array a)
109arraysOf n str = fromStreamD $ A.arraysOf n (toStreamD str)
111-------------------------------------------------------------------------------
113-------------------------------------------------------------------------------
115-- XXX efficiently compare two streams of arrays. Two streams can have chunks
116-- of different sizes, we can handle that in the stream comparison abstraction.
117-- This could be useful e.g. to fast compare whether two files differ.
119-- | Convert a stream of arrays into a stream of their elements.
121-- Same as the following:
123-- > concat = Stream.unfoldMany Array.read
127concat :: (IsStream t, Monad m, Storable a) => t m (Array a) -> t m a
128-- concat m = fromStreamD $ A.flattenArrays (toStreamD m)
129-- concat m = fromStreamD $ D.concatMap A.toStreamD (toStreamD m)
130concat m = fromStreamD $ D.unfoldMany A.read (toStreamD m)
132-- | Convert a stream of arrays into a stream of their elements reversing the
133-- contents of each array before flattening.
135-- > concatRev = Stream.unfoldMany Array.readRev
138{-# INLINE concatRev #-}
139concatRev :: (IsStream t, Monad m, Storable a) => t m (Array a) -> t m a
140-- concatRev m = fromStreamD $ A.flattenArraysRev (toStreamD m)
141concatRev m = fromStreamD $ D.unfoldMany A.readRev (toStreamD m)
143-------------------------------------------------------------------------------
144-- Intersperse and append
145-------------------------------------------------------------------------------
147-- | Flatten a stream of arrays after inserting the given element between
151{-# INLINE interpose #-}
152interpose :: (Monad m, IsStream t, Storable a) => a -> t m (Array a) -> t m a
153interpose x = S.interpose x A.read
155{-# INLINE intercalateSuffix #-}
156intercalateSuffix :: (Monad m, IsStream t, Storable a)
157 => Array a -> t m (Array a) -> t m a
158intercalateSuffix = S.intercalateSuffix A.read
160-- | Flatten a stream of arrays appending the given element after each
164{-# INLINE interposeSuffix #-}
165interposeSuffix :: (Monad m, IsStream t, Storable a)
166 => a -> t m (Array a) -> t m a
167-- interposeSuffix x = fromStreamD . A.unlines x . toStreamD
168interposeSuffix x = S.interposeSuffix x A.read
170data FlattenState s a =
172 | InnerLoop s !MA.ArrayContents !(Ptr a) !(Ptr a)
174-- XXX Remove monadIO constraint
175{-# INLINE_NORMAL unlines #-}
176unlines :: forall m a. (MonadIO m, Storable a)
177 => a -> D.Stream m (Array a) -> D.Stream m a
178unlines sep (D.Stream step state) = D.Stream step' (OuterLoop state)
180 {-# INLINE_LATE step' #-}
181 step' gst (OuterLoop st) = do
182 r <- step (adaptState gst) st
184 D.Yield Array{..} s ->
185 D.Skip (InnerLoop s arrContents arrStart aEnd)
186 D.Skip s -> D.Skip (OuterLoop s)
189 step' _ (InnerLoop st _ p end) | p == end =
190 return $ D.Yield sep $ OuterLoop st
192 step' _ (InnerLoop st contents p end) = do
197 return $ D.Yield x (InnerLoop st contents
198 (p `plusPtr` sizeOf (undefined :: a)) end)
200-------------------------------------------------------------------------------
202-------------------------------------------------------------------------------
204-- XXX These would not be needed once we implement compactLEFold, see
205-- module Streamly.Internal.Data.Array.Stream.Mut.Foreign
207{-# INLINE_NORMAL packArraysChunksOf #-}
208packArraysChunksOf :: (MonadIO m, Storable a)
209 => Int -> D.Stream m (Array a) -> D.Stream m (Array a)
210packArraysChunksOf n str =
211 D.map A.unsafeFreeze $ AS.packArraysChunksOf n $ D.map A.unsafeThaw str
213-- XXX instead of writing two different versions of this operation, we should
214-- write it as a pipe.
215{-# INLINE_NORMAL lpackArraysChunksOf #-}
216lpackArraysChunksOf :: (MonadIO m, Storable a)
217 => Int -> Fold m (Array a) () -> Fold m (Array a) ()
218lpackArraysChunksOf n fld =
219 FL.lmap A.unsafeThaw $ AS.lpackArraysChunksOf n (FL.lmap A.unsafeFreeze fld)
221-- | Coalesce adjacent arrays in incoming stream to form bigger arrays of a
222-- maximum specified size in bytes.
225{-# INLINE compact #-}
226compact :: (MonadIO m, Storable a)
227 => Int -> SerialT m (Array a) -> SerialT m (Array a)
228compact n xs = fromStreamD $ packArraysChunksOf n (toStreamD xs)
230-------------------------------------------------------------------------------
232-------------------------------------------------------------------------------
238 | Yielding arr (SplitState s arr)
241-- | Split a stream of arrays on a given separator byte, dropping the separator
242-- and coalescing all the arrays between two separators into a single array.
245{-# INLINE_NORMAL _splitOn #-}
249 -> D.Stream m (Array Word8)
250 -> D.Stream m (Array Word8)
251_splitOn byte (D.Stream step state) = D.Stream step' (Initial state)
255 {-# INLINE_LATE step' #-}
256 step' gst (Initial st) = do
260 (arr1, marr2) <- A.breakOn byte arr
261 return $ case marr2 of
262 Nothing -> D.Skip (Buffering s arr1)
263 Just arr2 -> D.Skip (Yielding arr1 (Splitting s arr2))
264 D.Skip s -> return $ D.Skip (Initial s)
265 D.Stop -> return D.Stop
267 step' gst (Buffering st buf) = do
271 (arr1, marr2) <- A.breakOn byte arr
272 buf' <- A.splice buf arr1
273 return $ case marr2 of
274 Nothing -> D.Skip (Buffering s buf')
275 Just x -> D.Skip (Yielding buf' (Splitting s x))
276 D.Skip s -> return $ D.Skip (Buffering s buf)
278 if A.byteLength buf == 0
280 else D.Skip (Yielding buf Finishing)
282 step' _ (Splitting st buf) = do
283 (arr1, marr2) <- A.breakOn byte buf
284 return $ case marr2 of
285 Nothing -> D.Skip $ Buffering st arr1
286 Just arr2 -> D.Skip $ Yielding arr1 (Splitting st arr2)
288 step' _ (Yielding arr next) = return $ D.Yield arr next
289 step' _ Finishing = return D.Stop
291-- XXX Remove MonadIO constraint.
292-- | Split a stream of arrays on a given separator byte, dropping the separator
293-- and coalescing all the arrays between two separators into a single array.
296{-# INLINE splitOn #-}
298 :: (IsStream t, MonadIO m)
303 fromStreamD $ D.splitInnerBy (A.breakOn byte) A.splice $ toStreamD s
305{-# INLINE splitOnSuffix #-}
307 :: (IsStream t, MonadIO m)
311-- splitOn byte s = fromStreamD $ A.splitOn byte $ toStreamD s
312splitOnSuffix byte s =
313 fromStreamD $ D.splitInnerBySuffix (A.breakOn byte) A.splice $ toStreamD s
315-------------------------------------------------------------------------------
316-- Elimination - Running folds
317-------------------------------------------------------------------------------
319-- XXX This should be written using CPS (as foldK) if we want it to scale wrt
320-- to the number of times it can be called on the same stream.
322{-# INLINE_NORMAL foldD #-}
323foldD :: forall m a b. (MonadIO m, Storable a) =>
324 Fold m a b -> D.Stream m (Array a) -> m (b, D.Stream m (Array a))
325foldD (Fold fstep initial extract) stream@(D.Stream step state) = do
328 FL.Partial fs -> go SPEC state fs
329 FL.Done fb -> return $! (fb, stream)
335 r <- step defState st
337 D.Yield (Array contents start (Ptr end)) s ->
338 let fp = ForeignPtr end (arrayToFptrContents contents)
339 in goArray SPEC s fp start fs
340 D.Skip s -> go SPEC s fs
345 goArray !_ s fp@(ForeignPtr end _) !cur !fs
346 | cur == Ptr end = do
347 liftIO $ touchForeignPtr fp
349 goArray !_ st fp@(ForeignPtr end contents) !cur !fs = do
350 x <- liftIO $ peek cur
352 let elemSize = sizeOf (undefined :: a)
353 next = cur `plusPtr` elemSize
356 let arr = Array (fptrToArrayContents contents) next (Ptr end)
357 return $! (b, D.cons arr (D.Stream step st))
358 FL.Partial fs1 -> goArray SPEC st fp next fs1
360-- | Fold an array stream using the supplied 'Fold'. Returns the fold result
361-- and the unconsumed stream.
365{-# INLINE_NORMAL fold #-}
367 (MonadIO m, Storable a)
369 -> SerialT m (A.Array a)
370 -> m (b, SerialT m (A.Array a))
371fold f s = fmap fromStreamD <$> foldD f (toStreamD s)
373-------------------------------------------------------------------------------
374-- Fold to a single Array
375-------------------------------------------------------------------------------
377-- When we have to take an array partially, take the last part of the array.
378{-# INLINE takeArrayListRev #-}
379takeArrayListRev :: forall a. Storable a => Int -> [Array a] -> [Array a]
387 let len = Array.length x
389 then x : go (n - len) xs
392 else let !(Array contents _ end) = x
393 sz = sizeOf (undefined :: a)
394 !start = end `plusPtr` negate (n * sz)
395 in [Array contents start end]
397-- When we have to take an array partially, take the last part of the array in
399{-# INLINE splitAtArrayListRev #-}
400splitAtArrayListRev :: forall a. Storable a =>
401 Int -> [Array a] -> ([Array a],[Array a])
402splitAtArrayListRev n ls
404 | otherwise = go n ls
406 go :: Int -> [Array a] -> ([Array a], [Array a])
409 let len = Array.length x
410 (xs', xs'') = go (m - len) xs
415 else let !(Array contents start end) = x
416 sz = sizeOf (undefined :: a)
417 end1 = end `plusPtr` negate (m * sz)
418 arr2 = Array contents start end1
419 arr1 = Array contents end1 end
422-------------------------------------------------------------------------------
423-- Fold to a single Array
424-------------------------------------------------------------------------------
426-- XXX Both of these implementations of splicing seem to perform equally well.
427-- We need to perform benchmarks over a range of sizes though.
429-- CAUTION! length must more than equal to lengths of all the arrays in the
431{-# INLINE spliceArraysLenUnsafe #-}
432spliceArraysLenUnsafe :: (MonadIO m, Storable a)
433 => Int -> SerialT m (MA.Array a) -> m (MA.Array a)
434spliceArraysLenUnsafe len buffered = do
435 arr <- liftIO $ MA.newArray len
436 end <- S.foldlM' writeArr (return $ MA.aEnd arr) buffered
437 return $ arr {MA.aEnd = end}
441 writeArr dst (MA.Array ac src ae _) =
443 let count = ae `minusPtr` src
444 memcpy (castPtr dst) (castPtr src) count
446 return $ dst `plusPtr` count
448{-# INLINE _spliceArrays #-}
449_spliceArrays :: (MonadIO m, Storable a)
450 => SerialT m (Array a) -> m (Array a)
452 buffered <- S.foldr S.cons S.nil s
453 len <- S.sum (S.map Array.length buffered)
454 arr <- liftIO $ MA.newArray len
455 end <- S.foldlM' writeArr (return $ MA.aEnd arr) s
456 return $ A.unsafeFreeze $ arr {MA.aEnd = end}
460 writeArr dst (Array ac src ae) =
462 let count = ae `minusPtr` src
463 memcpy (castPtr dst) (castPtr src) count
465 return $ dst `plusPtr` count
467{-# INLINE _spliceArraysBuffered #-}
468_spliceArraysBuffered :: (MonadIO m, Storable a)
469 => SerialT m (Array a) -> m (Array a)
470_spliceArraysBuffered s = do
471 buffered <- S.foldr S.cons S.nil s
472 len <- S.sum (S.map Array.length buffered)
473 A.unsafeFreeze <$> spliceArraysLenUnsafe len (S.map A.unsafeThaw s)
475{-# INLINE spliceArraysRealloced #-}
476spliceArraysRealloced :: forall m a. (MonadIO m, Storable a)
477 => SerialT m (Array a) -> m (Array a)
478spliceArraysRealloced s = do
479 let n = allocBytesToElemCount (undefined :: a) (4 * 1024)
480 idst = liftIO $ MA.newArray n
482 arr <- S.foldlM' MA.spliceExp idst (S.map A.unsafeThaw s)
483 liftIO $ A.unsafeFreeze <$> MA.rightSize arr
485-- XXX This should just be "fold A.write"
487-- | Given a stream of arrays, splice them all together to generate a single
488-- array. The stream must be /finite/.
491{-# INLINE toArray #-}
492toArray :: (MonadIO m, Storable a) => SerialT m (Array a) -> m (Array a)
493toArray = spliceArraysRealloced
494-- spliceArrays = _spliceArraysBuffered
496-- exponentially increasing sizes of the chunks upto the max limit.
497-- XXX this will be easier to implement with parsers/terminating folds
498-- With this we should be able to reduce the number of chunks/allocations.
499-- The reallocation/copy based toArray can also be implemented using this.
502{-# INLINE toArraysInRange #-}
503toArraysInRange :: (IsStream t, MonadIO m, Storable a)
504 => Int -> Int -> Fold m (Array a) b -> Fold m a b
505toArraysInRange low high (Fold step initial extract) =
509-- | Fold the input to a pure buffered stream (List) of arrays.
510{-# INLINE _toArraysOf #-}
511_toArraysOf :: (MonadIO m, Storable a)
512 => Int -> Fold m a (SerialT Identity (Array a))
513_toArraysOf n = FL.chunksOf n (A.writeNF n) FL.toStream
516-------------------------------------------------------------------------------
517-- Elimination - running element parsers
518-------------------------------------------------------------------------------
520-- GHC parser does not accept {-# ANN type [] NoSpecConstr #-}, so we need
522{-# ANN type List NoSpecConstr #-}
523newtype List a = List {getList :: [a]}
525-- This can be generalized to any type provided it can be unfolded to a stream
526-- and it can be combined using a semigroup operation.
528-- XXX This should be written using CPS (as parseK) if we want it to scale wrt
529-- to the number of times it can be called on the same stream.
530{-# INLINE_NORMAL parseD #-}
532 forall m a b. (MonadIO m, MonadThrow m, Storable a)
534 -> D.Stream m (Array.Array a)
535 -> m (b, D.Stream m (Array.Array a))
536parseD (PRD.Parser pstep initial extract) stream@(D.Stream step state) = do
539 PRD.IPartial s -> go SPEC state (List []) s
540 PRD.IDone b -> return (b, stream)
541 PRD.IError err -> throwM $ ParseError err
545 -- "backBuf" contains last few items in the stream that we may have to
548 -- XXX currently we are using a dumb list based approach for backtracking
549 -- buffer. This can be replaced by a sliding/ring buffer using Data.Array.
550 -- That will allow us more efficient random back and forth movement.
552 go !_ st backBuf !pst = do
553 r <- step defState st
555 D.Yield (Array contents start (Ptr end)) s ->
557 (ForeignPtr end (arrayToFptrContents contents)) start pst
558 D.Skip s -> go SPEC s backBuf pst
563 -- Use strictness on "cur" to keep it unboxed
564 gobuf !_ s backBuf fp@(ForeignPtr end _) !cur !pst
565 | cur == Ptr end = do
566 liftIO $ touchForeignPtr fp
567 go SPEC s backBuf pst
568 gobuf !_ s backBuf fp@(ForeignPtr end contents) !cur !pst = do
569 x <- liftIO $ peek cur
571 let elemSize = sizeOf (undefined :: a)
572 next = cur `plusPtr` elemSize
575 gobuf SPEC s (List []) fp next pst1
576 PR.Partial n pst1 -> do
577 assert (n <= Prelude.length (x:getList backBuf)) (return ())
578 let src0 = Prelude.take n (x:getList backBuf)
579 arr0 = A.fromListN n (Prelude.reverse src0)
580 arr1 = Array (fptrToArrayContents contents) next (Ptr end)
582 let !(Array cont1 start (Ptr end1)) = src
583 fp1 = ForeignPtr end1 (arrayToFptrContents cont1)
584 gobuf SPEC s (List []) fp1 start pst1
585 PR.Continue 0 pst1 ->
586 gobuf SPEC s (List (x:getList backBuf)) fp next pst1
587 PR.Continue n pst1 -> do
588 assert (n <= Prelude.length (x:getList backBuf)) (return ())
589 let (src0, buf1) = splitAt n (x:getList backBuf)
590 arr0 = A.fromListN n (Prelude.reverse src0)
591 arr1 = Array (fptrToArrayContents contents) next (Ptr end)
593 let !(Array cont1 start (Ptr end1)) = src
594 fp1 = ForeignPtr end1 (arrayToFptrContents cont1)
595 gobuf SPEC s (List buf1) fp1 start pst1
597 let arr = Array (fptrToArrayContents contents) next (Ptr end)
598 return (b, D.cons arr (D.Stream step s))
600 assert (n <= Prelude.length (x:getList backBuf)) (return ())
601 let src0 = Prelude.take n (x:getList backBuf)
602 -- XXX create the array in reverse instead
603 arr0 = A.fromListN n (Prelude.reverse src0)
604 arr1 = Array (fptrToArrayContents contents) next (Ptr end)
605 -- XXX Use StreamK to avoid adding arbitrary layers of
606 -- constructors every time.
607 str = D.cons arr0 (D.cons arr1 (D.Stream step s))
609 PR.Error err -> throwM $ ParseError err
611-- | Parse an array stream using the supplied 'Parser'. Returns the parse
612-- result and the unconsumed stream. Throws 'ParseError' if the parse fails.
616{-# INLINE_NORMAL parse #-}
618 (MonadIO m, MonadThrow m, Storable a)
620 -> SerialT m (A.Array a)
621 -> m (b, SerialT m (A.Array a))
622parse p s = fmap fromStreamD <$> parseD p (toStreamD s)
624-------------------------------------------------------------------------------
625-- Elimination - Running Array Folds and parsers
626-------------------------------------------------------------------------------
628{-# INLINE_NORMAL parseArrD #-}
630 forall m a b. (MonadIO m, MonadThrow m, Storable a)
631 => PRD.Parser m (Array a) b
632 -> D.Stream m (Array.Array a)
633 -> m (b, D.Stream m (Array.Array a))
634parseArrD (PRD.Parser pstep initial extract) stream@(D.Stream step state) = do
637 PRD.IPartial s -> go SPEC state (List []) s
638 PRD.IDone b -> return (b, stream)
639 PRD.IError err -> throwM $ ParseError err
643 -- "backBuf" contains last few items in the stream that we may have to
646 -- XXX currently we are using a dumb list based approach for backtracking
647 -- buffer. This can be replaced by a sliding/ring buffer using Data.Array.
648 -- That will allow us more efficient random back and forth movement.
650 go !_ st backBuf !pst = do
651 r <- step defState st
653 D.Yield x s -> gobuf SPEC [x] s backBuf pst
654 D.Skip s -> go SPEC s backBuf pst
659 gobuf !_ [] s backBuf !pst = go SPEC s backBuf pst
660 gobuf !_ (x:xs) s backBuf !pst = do
664 gobuf SPEC xs s (List []) pst1
665 PR.Partial n pst1 -> do
667 (n <= sum (map Array.length (x:getList backBuf)))
669 let src0 = takeArrayListRev n (x:getList backBuf)
670 src = Prelude.reverse src0 ++ xs
671 gobuf SPEC src s (List []) pst1
672 PR.Continue 0 pst1 ->
673 gobuf SPEC xs s (List (x:getList backBuf)) pst1
674 PR.Continue n pst1 -> do
676 (n <= sum (map Array.length (x:getList backBuf)))
678 let (src0, buf1) = splitAtArrayListRev n (x:getList backBuf)
679 src = Prelude.reverse src0 ++ xs
680 gobuf SPEC src s (List buf1) pst1
682 return (b, D.Stream step s)
685 (n <= sum (map Array.length (x:getList backBuf)))
687 let src0 = takeArrayListRev n (x:getList backBuf)
688 src = Prelude.reverse src0 ++ xs
689 return (b, D.append (D.fromList src) (D.Stream step s))
690 PR.Error err -> throwM $ ParseError err
693-- | Parse an array stream using the supplied 'Parser'. Returns the parse
694-- result and the unconsumed stream. Throws 'ParseError' if the parse fails.
698{-# INLINE parseArr #-}
700 (MonadIO m, MonadThrow m, Storable a)
702 -> SerialT m (A.Array a)
703 -> m (b, SerialT m (A.Array a))
704parseArr p s = fmap fromStreamD <$> parseD p (toStreamD s)
707-- | Fold an array stream using the supplied array stream 'Fold'.
711{-# INLINE foldArr #-}
712foldArr :: (MonadIO m, MonadThrow m, Storable a) =>
713 ASF.Fold m a b -> SerialT m (A.Array a) -> m b
714foldArr (ASF.Fold p) s = fst <$> parseArrD p (toStreamD s)
716-- | Like 'fold' but also returns the remaining stream.
720{-# INLINE foldArr_ #-}
721foldArr_ :: (MonadIO m, MonadThrow m, Storable a) =>
722 ASF.Fold m a b -> SerialT m (A.Array a) -> m (b, SerialT m (A.Array a))
723foldArr_ (ASF.Fold p) s = second fromStreamD <$> parseArrD p (toStreamD s)
725{-# ANN type ParseChunksState Fuse #-}
726data ParseChunksState x inpBuf st pst =
727 ParseChunksInit inpBuf st
728 | ParseChunksInitLeftOver inpBuf
729 | ParseChunksStream st inpBuf !pst
730 | ParseChunksBuf inpBuf st inpBuf !pst
731 | ParseChunksYield x (ParseChunksState x inpBuf st pst)
733{-# INLINE_NORMAL foldArrManyD #-}
735 :: (MonadThrow m, Storable a)
737 -> D.Stream m (Array a)
739foldArrManyD (ASF.Fold (PRD.Parser pstep initial extract)) (D.Stream step state) =
740 D.Stream stepOuter (ParseChunksInit [] state)
744 {-# INLINE_LATE stepOuter #-}
745 -- Buffer is empty, get the first element from the stream, initialize the
746 -- fold and then go to stream processing loop.
747 stepOuter gst (ParseChunksInit [] st) = do
748 r <- step (adaptState gst) st
754 return $ D.Skip $ ParseChunksBuf [x] s [] ps
756 let next = ParseChunksInit [x] s
757 in return $ D.Skip $ ParseChunksYield pb next
758 PRD.IError err -> throwM $ ParseError err
759 D.Skip s -> return $ D.Skip $ ParseChunksInit [] s
760 D.Stop -> return D.Stop
762 -- Buffer is not empty, go to buffered processing loop
763 stepOuter _ (ParseChunksInit src st) = do
767 return $ D.Skip $ ParseChunksBuf src st [] ps
769 let next = ParseChunksInit src st
770 in return $ D.Skip $ ParseChunksYield pb next
771 PRD.IError err -> throwM $ ParseError err
773 -- XXX we just discard any leftover input at the end
774 stepOuter _ (ParseChunksInitLeftOver _) = return D.Stop
776 -- Buffer is empty, process elements from the stream
777 stepOuter gst (ParseChunksStream st backBuf pst) = do
778 r <- step (adaptState gst) st
784 return $ D.Skip $ ParseChunksStream s [] pst1
785 PR.Partial n pst1 -> do
787 (n <= sum (map Array.length (x:backBuf)))
789 let src0 = takeArrayListRev n (x:backBuf)
790 src = Prelude.reverse src0
791 return $ D.Skip $ ParseChunksBuf src s [] pst1
792 PR.Continue 0 pst1 ->
793 return $ D.Skip $ ParseChunksStream s (x:backBuf) pst1
794 PR.Continue n pst1 -> do
796 (n <= sum (map Array.length (x:backBuf)))
798 let (src0, buf1) = splitAtArrayListRev n (x:backBuf)
799 src = Prelude.reverse src0
800 return $ D.Skip $ ParseChunksBuf src s buf1 pst1
803 ParseChunksYield b (ParseChunksInit [] s)
806 (n <= sum (map Array.length (x:backBuf)))
808 let src0 = takeArrayListRev n (x:backBuf)
809 let src = Prelude.reverse src0
811 ParseChunksYield b (ParseChunksInit src s)
812 PR.Error err -> throwM $ ParseError err
813 D.Skip s -> return $ D.Skip $ ParseChunksStream s backBuf pst
816 let src = Prelude.reverse backBuf
818 ParseChunksYield b (ParseChunksInitLeftOver src)
820 -- go back to stream processing mode
821 stepOuter _ (ParseChunksBuf [] s buf pst) =
822 return $ D.Skip $ ParseChunksStream s buf pst
824 -- buffered processing loop
825 stepOuter _ (ParseChunksBuf (x:xs) s backBuf pst) = do
829 return $ D.Skip $ ParseChunksBuf xs s [] pst1
830 PR.Partial n pst1 -> do
831 assert (n <= sum (map Array.length (x:backBuf))) (return ())
832 let src0 = takeArrayListRev n (x:backBuf)
833 src = Prelude.reverse src0 ++ xs
834 return $ D.Skip $ ParseChunksBuf src s [] pst1
835 PR.Continue 0 pst1 ->
836 return $ D.Skip $ ParseChunksBuf xs s (x:backBuf) pst1
837 PR.Continue n pst1 -> do
838 assert (n <= sum (map Array.length (x:backBuf))) (return ())
839 let (src0, buf1) = splitAtArrayListRev n (x:backBuf)
840 src = Prelude.reverse src0 ++ xs
841 return $ D.Skip $ ParseChunksBuf src s buf1 pst1
843 return $ D.Skip $ ParseChunksYield b (ParseChunksInit xs s)
845 assert (n <= sum (map Array.length (x:backBuf))) (return ())
846 let src0 = takeArrayListRev n (x:backBuf)
847 src = Prelude.reverse src0 ++ xs
848 return $ D.Skip $ ParseChunksYield b (ParseChunksInit src s)
849 PR.Error err -> throwM $ ParseError err
851 stepOuter _ (ParseChunksYield a next) = return $ D.Yield a next
853-- | Apply an array stream 'Fold' repeatedly on an array stream and emit the
854-- fold outputs in the output stream.
856-- See "Streamly.Prelude.foldMany" for more details.
859{-# INLINE foldArrMany #-}
861 :: (IsStream t, MonadThrow m, Storable a)
865foldArrMany p m = fromStreamD $ foldArrManyD p (toStreamD m)
keysesc to close
- j k
- move down, up
- enter
- open
- esc
- back, or up a level
- 1 2 3 4
- readme, files, commits, refs
- /
- filter
- y
- copy the clone command
- c
- compare the chosen commit with another (or shift-click both)
- ?
- show or hide this list