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
35 , runArrayParserDBreak
50#include "ArrayMacros.h"
53import Data.Bifunctor (second)
54import Control.Exception (assert)
55import Control.Monad.Catch (MonadThrow, throwM)
56import Control.Monad.IO.Class (MonadIO(..))
57#if __GLASGOW_HASKELL__ < 808
58import Data.Semigroup (Semigroup(..))
60import Data.Word (Word8)
61import Streamly.Internal.Data.Unboxed (Unboxed, peekWith, sizeOf)
62import Fusion.Plugin.Types (Fuse(..))
63import GHC.Exts (SpecConstrAnnotation(..))
64import GHC.Types (SPEC(..))
65import Prelude hiding (null, last, (!!), read, concat, unlines)
67import Streamly.Internal.Data.Array.Unboxed.Type (Array(..))
68import Streamly.Internal.Data.Array.Stream.Fold.Foreign (ArrayFold(..))
69import Streamly.Internal.Data.Fold.Type (Fold(..))
70import Streamly.Internal.Data.Parser (ParseError(..))
71import Streamly.Internal.Data.Stream (Stream)
72import Streamly.Internal.Data.Stream.IsStream.Type
73 (IsStream, fromStreamD, toStreamD)
74import Streamly.Internal.Data.SVar (adaptState, defState)
75import Streamly.Internal.Data.Array.Unboxed.Mut.Type
76 (allocBytesToElemCount)
77import Streamly.Internal.Data.Tuple.Strict (Tuple'(..))
79import qualified Streamly.Internal.Data.Array.Unboxed as A
80import qualified Streamly.Internal.Data.Array.Unboxed as Array
81-- import qualified Streamly.Internal.Data.Array.Stream.Fold.Foreign
83import qualified Streamly.Internal.Data.Array.Unboxed.Type as A
84import qualified Streamly.Internal.Data.Array.Unboxed.Mut.Type as MA
85import qualified Streamly.Internal.Data.Array.Stream.Mut.Foreign as AS
86import qualified Streamly.Internal.Data.Fold.Type as FL
87import qualified Streamly.Internal.Data.Parser as PR
88import qualified Streamly.Internal.Data.Parser.ParserD as PRD
89import qualified Streamly.Internal.Data.Stream.IsStream as S
90import qualified Streamly.Internal.Data.Stream.StreamD as D
91import qualified Streamly.Internal.Data.Stream.StreamK as K
92import qualified Streamly.Internal.Data.Stream.Type as Stream
94-- XXX Since these are immutable arrays MonadIO constraint can be removed from
97-------------------------------------------------------------------------------
99-------------------------------------------------------------------------------
101-- | @arraysOf n stream@ groups the elements in the input stream into arrays of
104-- > arraysOf n = Stream.chunksOf n (Array.writeN n)
107{-# INLINE arraysOf #-}
108arraysOf :: (IsStream t, MonadIO m, Unboxed a)
109 => Int -> t m a -> t m (Array a)
110arraysOf n str = fromStreamD $ A.arraysOf n (toStreamD str)
112-------------------------------------------------------------------------------
114-------------------------------------------------------------------------------
116-- XXX efficiently compare two streams of arrays. Two streams can have chunks
117-- of different sizes, we can handle that in the stream comparison abstraction.
118-- This could be useful e.g. to fast compare whether two files differ.
120-- | Convert a stream of arrays into a stream of their elements.
122-- Same as the following:
124-- > concat = Stream.unfoldMany Array.read
128concat :: (IsStream t, Monad m, Unboxed a) => t m (Array a) -> t m a
129-- concat m = fromStreamD $ A.flattenArrays (toStreamD m)
130-- concat m = fromStreamD $ D.concatMap A.toStreamD (toStreamD m)
131concat m = fromStreamD $ D.unfoldMany A.read (toStreamD m)
133-- | Convert a stream of arrays into a stream of their elements reversing the
134-- contents of each array before flattening.
136-- > concatRev = Stream.unfoldMany Array.readRev
139{-# INLINE concatRev #-}
140concatRev :: (IsStream t, Monad m, Unboxed a) => t m (Array a) -> t m a
141-- concatRev m = fromStreamD $ A.flattenArraysRev (toStreamD m)
142concatRev m = fromStreamD $ D.unfoldMany A.readRev (toStreamD m)
144-------------------------------------------------------------------------------
145-- Intersperse and append
146-------------------------------------------------------------------------------
148-- | Flatten a stream of arrays after inserting the given element between
152{-# INLINE interpose #-}
153interpose :: (Monad m, IsStream t, Unboxed a) => a -> t m (Array a) -> t m a
154interpose x = S.interpose x A.read
156{-# INLINE intercalateSuffix #-}
157intercalateSuffix :: (Monad m, IsStream t, Unboxed a)
158 => Array a -> t m (Array a) -> t m a
159intercalateSuffix = S.intercalateSuffix A.read
161-- | Flatten a stream of arrays appending the given element after each
165{-# INLINE interposeSuffix #-}
166interposeSuffix :: (Monad m, IsStream t, Unboxed a)
167 => a -> t m (Array a) -> t m a
168-- interposeSuffix x = fromStreamD . A.unlines x . toStreamD
169interposeSuffix x = S.interposeSuffix x A.read
171data FlattenState s a =
173 | InnerLoop s !(MA.ArrayContents a) !Int !Int
175-- XXX Remove monadIO constraint
176{-# INLINE_NORMAL unlines #-}
177unlines :: forall m a. (MonadIO m, Unboxed a)
178 => a -> D.Stream m (Array a) -> D.Stream m a
179unlines sep (D.Stream step state) = D.Stream step' (OuterLoop state)
181 {-# INLINE_LATE step' #-}
182 step' gst (OuterLoop st) = do
183 r <- step (adaptState gst) st
185 D.Yield Array{..} s ->
186 D.Skip (InnerLoop s arrContents arrStart arrEnd)
187 D.Skip s -> D.Skip (OuterLoop s)
190 step' _ (InnerLoop st _ p end) | p == end =
191 return $ D.Yield sep $ OuterLoop st
193 step' _ (InnerLoop st contents p end) = do
194 x <- liftIO $ peekWith contents p
195 return $ D.Yield x (InnerLoop st contents (INDEX_NEXT(p,a)) end)
197-------------------------------------------------------------------------------
199-------------------------------------------------------------------------------
201-- XXX These would not be needed once we implement compactLEFold, see
202-- module Streamly.Internal.Data.Array.Stream.Mut.Foreign
204{-# INLINE_NORMAL packArraysChunksOf #-}
205packArraysChunksOf :: (MonadIO m, Unboxed a)
206 => Int -> D.Stream m (Array a) -> D.Stream m (Array a)
207packArraysChunksOf n str =
208 D.map A.unsafeFreeze $ AS.packArraysChunksOf n $ D.map A.unsafeThaw str
210-- XXX instead of writing two different versions of this operation, we should
211-- write it as a pipe.
212{-# INLINE_NORMAL lpackArraysChunksOf #-}
213lpackArraysChunksOf :: (MonadIO m, Unboxed a)
214 => Int -> Fold m (Array a) () -> Fold m (Array a) ()
215lpackArraysChunksOf n fld =
216 FL.lmap A.unsafeThaw $ AS.lpackArraysChunksOf n (FL.lmap A.unsafeFreeze fld)
218-- | Coalesce adjacent arrays in incoming stream to form bigger arrays of a
219-- maximum specified size in bytes.
222{-# INLINE compact #-}
223compact :: (MonadIO m, Unboxed a)
224 => Int -> Stream m (Array a) -> Stream m (Array a)
225compact n xs = fromStreamD $ packArraysChunksOf n (toStreamD xs)
227-------------------------------------------------------------------------------
229-------------------------------------------------------------------------------
235 | Yielding arr (SplitState s arr)
238-- | Split a stream of arrays on a given separator byte, dropping the separator
239-- and coalescing all the arrays between two separators into a single array.
242{-# INLINE_NORMAL _splitOn #-}
246 -> D.Stream m (Array Word8)
247 -> D.Stream m (Array Word8)
248_splitOn byte (D.Stream step state) = D.Stream step' (Initial state)
252 {-# INLINE_LATE step' #-}
253 step' gst (Initial st) = do
257 (arr1, marr2) <- A.breakOn byte arr
258 return $ case marr2 of
259 Nothing -> D.Skip (Buffering s arr1)
260 Just arr2 -> D.Skip (Yielding arr1 (Splitting s arr2))
261 D.Skip s -> return $ D.Skip (Initial s)
262 D.Stop -> return D.Stop
264 step' gst (Buffering st buf) = do
268 (arr1, marr2) <- A.breakOn byte arr
269 buf' <- A.splice buf arr1
270 return $ case marr2 of
271 Nothing -> D.Skip (Buffering s buf')
272 Just x -> D.Skip (Yielding buf' (Splitting s x))
273 D.Skip s -> return $ D.Skip (Buffering s buf)
275 if A.byteLength buf == 0
277 else D.Skip (Yielding buf Finishing)
279 step' _ (Splitting st buf) = do
280 (arr1, marr2) <- A.breakOn byte buf
281 return $ case marr2 of
282 Nothing -> D.Skip $ Buffering st arr1
283 Just arr2 -> D.Skip $ Yielding arr1 (Splitting st arr2)
285 step' _ (Yielding arr next) = return $ D.Yield arr next
286 step' _ Finishing = return D.Stop
288-- XXX Remove MonadIO constraint.
289-- | Split a stream of arrays on a given separator byte, dropping the separator
290-- and coalescing all the arrays between two separators into a single array.
293{-# INLINE splitOn #-}
295 :: (IsStream t, MonadIO m)
300 fromStreamD $ D.splitInnerBy (A.breakOn byte) A.splice $ toStreamD s
302{-# INLINE splitOnSuffix #-}
304 :: (IsStream t, MonadIO m)
308-- splitOn byte s = fromStreamD $ A.splitOn byte $ toStreamD s
309splitOnSuffix byte s =
310 fromStreamD $ D.splitInnerBySuffix (A.breakOn byte) A.splice $ toStreamD s
312-------------------------------------------------------------------------------
313-- Elimination - Running folds
314-------------------------------------------------------------------------------
316-- XXX This should be written using CPS (as foldK) if we want it to scale wrt
317-- to the number of times it can be called on the same stream.
319{-# INLINE_NORMAL foldBreakD #-}
320foldBreakD :: forall m a b. (MonadIO m, Unboxed a) =>
321 Fold m a b -> D.Stream m (Array a) -> m (b, D.Stream m (Array a))
322foldBreakD (Fold fstep initial extract) stream@(D.Stream step state) = do
325 FL.Partial fs -> go SPEC state fs
326 FL.Done fb -> return $! (fb, stream)
332 r <- step defState st
334 D.Yield (Array contents start end) s ->
335 let fp = Tuple' end contents
336 in goArray SPEC s fp start fs
337 D.Skip s -> go SPEC s fs
342 goArray !_ s (Tuple' end _) !cur !fs
345 goArray !_ st fp@(Tuple' end contents) !cur !fs = do
346 x <- liftIO $ peekWith contents cur
348 let next = INDEX_NEXT(cur,a)
351 let arr = Array contents next end
352 return $! (b, D.cons arr (D.Stream step st))
353 FL.Partial fs1 -> goArray SPEC st fp next fs1
355{-# INLINE_NORMAL foldBreakK #-}
356foldBreakK :: forall m a b. (MonadIO m, Unboxed a) =>
357 Fold m a b -> K.Stream m (Array a) -> m (b, K.Stream m (Array a))
358foldBreakK (Fold fstep initial extract) stream = do
361 FL.Partial fs -> go fs stream
362 FL.Done fb -> return (fb, stream)
368 let stop = (, K.nil) <$> extract fs
369 single a = yieldk a K.nil
370 yieldk (Array contents start end) r =
371 let fp = Tuple' end contents
372 in goArray fs r fp start
373 in K.foldStream defState yieldk single stop st
375 goArray !fs st (Tuple' end _) !cur
378 goArray !fs st fp@(Tuple' end contents) !cur = do
379 x <- liftIO $ peekWith contents cur
381 let next = INDEX_NEXT(cur,a)
384 let arr = Array contents next end
385 return $! (b, K.cons arr st)
386 FL.Partial fs1 -> goArray fs1 st fp next
388-- | Fold an array stream using the supplied 'Fold'. Returns the fold result
389-- and the unconsumed stream.
391-- > foldBreak f = runArrayFoldBreak (ArrayFold.fromFold f)
395{-# INLINE_NORMAL foldBreak #-}
397 (MonadIO m, Unboxed a)
399 -> Stream m (A.Array a)
400 -> m (b, Stream m (A.Array a))
401-- foldBreak f s = fmap fromStreamD <$> foldBreakD f (toStreamD s)
403 fmap (fmap Stream.fromStreamK)
406-- If foldBreak performs better than runArrayFoldBreak we can use a rewrite
407-- rule to rewrite runArrayFoldBreak to fold.
408-- foldBreak f = runArrayFoldBreak (ArrayFold.fromFold f)
410-------------------------------------------------------------------------------
411-- Fold to a single Array
412-------------------------------------------------------------------------------
414-- When we have to take an array partially, take the last part of the array.
415{-# INLINE takeArrayListRev #-}
416takeArrayListRev :: forall a. Unboxed a => Int -> [Array a] -> [Array a]
424 let len = Array.length x
426 then x : go (n - len) xs
429 else let !(Array contents _ end) = x
430 !start = end - (n * SIZE_OF(a))
431 in [Array contents start end]
433-- When we have to take an array partially, take the last part of the array in
435{-# INLINE splitAtArrayListRev #-}
436splitAtArrayListRev ::
437 forall a. Unboxed a => Int -> [Array a] -> ([Array a],[Array a])
438splitAtArrayListRev n ls
440 | otherwise = go n ls
442 go :: Int -> [Array a] -> ([Array a], [Array a])
445 let len = Array.length x
446 (xs', xs'') = go (m - len) xs
451 else let !(Array contents start end) = x
452 end1 = end - (m * SIZE_OF(a))
453 arr2 = Array contents start end1
454 arr1 = Array contents end1 end
457-------------------------------------------------------------------------------
458-- Fold to a single Array
459-------------------------------------------------------------------------------
461-- XXX Both of these implementations of splicing seem to perform equally well.
462-- We need to perform benchmarks over a range of sizes though.
464-- CAUTION! length must more than equal to lengths of all the arrays in the
466{-# INLINE spliceArraysLenUnsafe #-}
467spliceArraysLenUnsafe :: (MonadIO m, Unboxed a)
468 => Int -> Stream m (MA.Array a) -> m (MA.Array a)
469spliceArraysLenUnsafe len buffered = do
470 arr <- liftIO $ MA.newArray len
471 S.foldlM' MA.spliceUnsafe (return arr) buffered
473{-# INLINE _spliceArrays #-}
474_spliceArrays :: (MonadIO m, Unboxed a)
475 => Stream m (Array a) -> m (Array a)
477 buffered <- S.foldr S.cons S.nil s
478 len <- S.sum (S.map Array.length buffered)
479 arr <- liftIO $ MA.newArray len
480 final <- S.foldlM' writeArr (return arr) s
481 return $ A.unsafeFreeze final
485 writeArr dst arr = MA.spliceUnsafe dst (A.unsafeThaw arr)
487{-# INLINE _spliceArraysBuffered #-}
488_spliceArraysBuffered :: (MonadIO m, Unboxed a)
489 => Stream m (Array a) -> m (Array a)
490_spliceArraysBuffered s = do
491 buffered <- S.foldr S.cons S.nil s
492 len <- S.sum (S.map Array.length buffered)
493 A.unsafeFreeze <$> spliceArraysLenUnsafe len (S.map A.unsafeThaw s)
495{-# INLINE spliceArraysRealloced #-}
496spliceArraysRealloced :: forall m a. (MonadIO m, Unboxed a)
497 => Stream m (Array a) -> m (Array a)
498spliceArraysRealloced s = do
499 let n = allocBytesToElemCount (undefined :: a) (4 * 1024)
500 idst = liftIO $ MA.newArray n
502 arr <- S.foldlM' MA.spliceExp idst (S.map A.unsafeThaw s)
503 liftIO $ A.unsafeFreeze <$> MA.rightSize arr
505-- XXX This should just be "fold A.write"
507-- | Given a stream of arrays, splice them all together to generate a single
508-- array. The stream must be /finite/.
511{-# INLINE toArray #-}
512toArray :: (MonadIO m, Unboxed a) => Stream m (Array a) -> m (Array a)
513toArray = spliceArraysRealloced
514-- spliceArrays = _spliceArraysBuffered
516-- exponentially increasing sizes of the chunks upto the max limit.
517-- XXX this will be easier to implement with parsers/terminating folds
518-- With this we should be able to reduce the number of chunks/allocations.
519-- The reallocation/copy based toArray can also be implemented using this.
522{-# INLINE toArraysInRange #-}
523toArraysInRange :: (IsStream t, MonadIO m, Unboxed a)
524 => Int -> Int -> Fold m (Array a) b -> Fold m a b
525toArraysInRange low high (Fold step initial extract) =
529-- | Fold the input to a pure buffered stream (List) of arrays.
530{-# INLINE _toArraysOf #-}
531_toArraysOf :: (MonadIO m, Unboxed a)
532 => Int -> Fold m a (Stream Identity (Array a))
533_toArraysOf n = FL.chunksOf n (A.writeNF n) FL.toStream
536-------------------------------------------------------------------------------
537-- Elimination - running element parsers
538-------------------------------------------------------------------------------
540-- GHC parser does not accept {-# ANN type [] NoSpecConstr #-}, so we need
542{-# ANN type List NoSpecConstr #-}
543newtype List a = List {getList :: [a]}
546-- This can be generalized to any type provided it can be unfolded to a stream
547-- and it can be combined using a semigroup operation.
549-- XXX This should be written using CPS (as parseK) if we want it to scale wrt
550-- to the number of times it can be called on the same stream.
551{-# INLINE_NORMAL parseBreakD #-}
553 forall m a b. (MonadIO m, MonadThrow m, Unboxed a)
555 -> D.Stream m (Array.Array a)
556 -> m (b, D.Stream m (Array.Array a))
558 (PRD.Parser pstep initial extract) stream@(D.Stream step state) = do
562 PRD.IPartial s -> go SPEC state (List []) s
563 PRD.IDone b -> return (b, stream)
564 PRD.IError err -> throwM $ ParseError err
568 -- "backBuf" contains last few items in the stream that we may have to
571 -- XXX currently we are using a dumb list based approach for backtracking
572 -- buffer. This can be replaced by a sliding/ring buffer using Data.Array.
573 -- That will allow us more efficient random back and forth movement.
574 go !_ st backBuf !pst = do
575 r <- step defState st
577 D.Yield (Array contents start end) s ->
579 (Tuple' end contents) start pst
580 D.Skip s -> go SPEC s backBuf pst
585 -- Use strictness on "cur" to keep it unboxed
586 gobuf !_ s backBuf (Tuple' end _) !cur !pst
588 go SPEC s backBuf pst
589 gobuf !_ s backBuf fp@(Tuple' end contents) !cur !pst = do
590 x <- liftIO $ peekWith contents cur
592 let next = INDEX_NEXT(cur,a)
595 gobuf SPEC s (List []) fp next pst1
596 PR.Partial n pst1 -> do
597 assert (n <= Prelude.length (x:getList backBuf)) (return ())
598 let src0 = Prelude.take n (x:getList backBuf)
599 arr0 = A.fromListN n (Prelude.reverse src0)
600 arr1 = Array contents next end
602 let !(Array cont1 start end1) = src
603 fp1 = Tuple' end1 cont1
604 gobuf SPEC s (List []) fp1 start pst1
605 PR.Continue 0 pst1 ->
606 gobuf SPEC s (List (x:getList backBuf)) fp next pst1
607 PR.Continue n pst1 -> do
608 assert (n <= Prelude.length (x:getList backBuf)) (return ())
609 let (src0, buf1) = splitAt n (x:getList backBuf)
610 arr0 = A.fromListN n (Prelude.reverse src0)
611 arr1 = Array contents next end
613 let !(Array cont1 start end1) = src
614 fp1 = Tuple' end1 cont1
615 gobuf SPEC s (List buf1) fp1 start pst1
617 let arr = Array contents next end
618 return (b, D.cons arr (D.Stream step s))
620 assert (n <= Prelude.length (x:getList backBuf)) (return ())
621 let src0 = Prelude.take n (x:getList backBuf)
622 -- XXX create the array in reverse instead
623 arr0 = A.fromListN n (Prelude.reverse src0)
624 arr1 = Array contents next end
625 -- XXX Use StreamK to avoid adding arbitrary layers of
626 -- constructors every time.
627 str = D.cons arr0 (D.cons arr1 (D.Stream step s))
629 PR.Error err -> throwM $ ParseError err
632{-# INLINE_NORMAL parseBreakK #-}
634 forall m a b. (MonadIO m, MonadThrow m, Unboxed a)
636 -> K.Stream m (Array.Array a)
637 -> m (b, K.Stream m (Array.Array a))
638parseBreakK (PRD.Parser pstep initial extract) stream = do
641 PRD.IPartial s -> go s stream []
642 PRD.IDone b -> return (b, stream)
643 PRD.IError err -> throwM $ ParseError err
647 -- "backBuf" contains last few items in the stream that we may have to
650 -- XXX currently we are using a dumb list based approach for backtracking
651 -- buffer. This can be replaced by a sliding/ring buffer using Data.Array.
652 -- That will allow us more efficient random back and forth movement.
653 go !pst st backBuf = do
654 let stop = goStop pst backBuf -- (, K.nil) <$> extract pst
655 single a = yieldk a K.nil
656 yieldk arr r = goArray pst backBuf r arr
657 in K.foldStream defState yieldk single stop st
659 -- Use strictness on "cur" to keep it unboxed
660 goArray !pst backBuf st (Array _ cur end) | cur == end = go pst st backBuf
661 goArray !pst backBuf st (Array contents cur end) = do
662 x <- liftIO $ peekWith contents cur
664 let next = INDEX_NEXT(cur,a)
667 goArray s [] st (Array contents next end)
669 assert (n <= Prelude.length (x:backBuf)) (return ())
670 let src0 = Prelude.take n (x:backBuf)
671 arr0 = A.fromListN n (Prelude.reverse src0)
672 arr1 = Array contents next end
676 goArray s (x:backBuf) st (Array contents next end)
677 PR.Continue n s -> do
678 assert (n <= Prelude.length (x:backBuf)) (return ())
679 let (src0, buf1) = splitAt n (x:backBuf)
680 arr0 = A.fromListN n (Prelude.reverse src0)
681 arr1 = Array contents next end
683 goArray s buf1 st src
685 let arr = Array contents next end
686 return (b, K.cons arr st)
688 assert (n <= Prelude.length (x:backBuf)) (return ())
689 let src0 = Prelude.take n (x:backBuf)
690 -- XXX Use fromListRevN once implemented
691 -- arr0 = A.fromListRevN n src0
692 arr0 = A.fromListN n (Prelude.reverse src0)
693 arr1 = Array contents next end
694 str = K.cons arr0 (K.cons arr1 st)
696 PR.Error err -> throwM $ ParseError err
698 -- This is a simplified goArray
699 goExtract !pst backBuf (Array _ cur end)
700 | cur == end = goStop pst backBuf
701 goExtract !pst backBuf (Array contents cur end) = do
702 x <- liftIO $ peekWith contents cur
704 let next = INDEX_NEXT(cur,a)
707 goExtract s [] (Array contents next end)
709 assert (n <= Prelude.length (x:backBuf)) (return ())
710 let src0 = Prelude.take n (x:backBuf)
711 arr0 = A.fromListN n (Prelude.reverse src0)
712 arr1 = Array contents next end
716 goExtract s backBuf (Array contents next end)
717 PR.Continue n s -> do
718 assert (n <= Prelude.length (x:backBuf)) (return ())
719 let (src0, buf1) = splitAt n (x:backBuf)
720 arr0 = A.fromListN n (Prelude.reverse src0)
721 arr1 = Array contents next end
725 let arr = Array contents next end
726 return (b, K.fromPure arr)
728 assert (n <= Prelude.length backBuf) (return ())
729 let src0 = Prelude.take n backBuf
730 -- XXX Use fromListRevN once implemented
731 -- arr0 = A.fromListRevN n src0
732 arr0 = A.fromListN n (Prelude.reverse src0)
733 arr1 = Array contents next end
734 str = K.cons arr0 (K.fromPure arr1)
736 PR.Error err -> throwM $ ParseError err
738 -- This is a simplified goExtract
739 {-# INLINE goStop #-}
740 goStop !pst backBuf = do
743 PR.Partial _ _ -> error "Bug: parseBreak: Partial in extract"
745 error "parseBreak: extract, Continue 0 creates infinite loop"
746 PR.Continue n s -> do
747 assert (n <= Prelude.length backBuf) (return ())
748 let (src0, buf1) = splitAt n backBuf
749 arr = A.fromListN n (Prelude.reverse src0)
754 assert (n <= Prelude.length backBuf) (return ())
755 let src0 = Prelude.take n backBuf
756 -- XXX Use fromListRevN once implemented
757 -- arr0 = A.fromListRevN n src0
758 arr0 = A.fromListN n (Prelude.reverse src0)
759 return (b, K.fromPure arr0)
760 PR.Error err -> throwM $ ParseError err
762-- | Parse an array stream using the supplied 'Parser'. Returns the parse
763-- result and the unconsumed stream. Throws 'ParseError' if the parse fails.
767{-# INLINE_NORMAL parseBreak #-}
769 (MonadIO m, MonadThrow m, Unboxed a)
771 -> Stream m (A.Array a)
772 -> m (b, Stream m (A.Array a))
775 fmap fromStreamD <$> parseBreakD (PRD.fromParserK p) (toStreamD s)
778 fmap (fmap Stream.fromStreamK)
779 . parseBreakK (PRD.fromParserK p)
782-------------------------------------------------------------------------------
783-- Elimination - Running Array Folds and parsers
784-------------------------------------------------------------------------------
786-- | Note that this is not the same as using a @Parser m (Array a) b@ with the
787-- regular "Streamly.Internal.Data.IsStream.parse" function. The regular parse
788-- would consume the input arrays as single unit. This parser parses in the way
789-- as described in the ArrayFold module. The input arrays are treated as @n@
790-- element units and can be consumed partially. The remaining elements are
791-- inserted in the source stream as an array.
793{-# INLINE_NORMAL runArrayParserDBreak #-}
794runArrayParserDBreak ::
795 forall m a b. (MonadIO m, MonadThrow m, Unboxed a)
796 => PRD.Parser m (Array a) b
797 -> D.Stream m (Array.Array a)
798 -> m (b, D.Stream m (Array.Array a))
800 (PRD.Parser pstep initial extract)
801 stream@(D.Stream step state) = do
805 PRD.IPartial s -> go SPEC state (List []) s
806 PRD.IDone b -> return (b, stream)
807 PRD.IError err -> throwM $ ParseError err
811 -- "backBuf" contains last few items in the stream that we may have to
814 -- XXX currently we are using a dumb list based approach for backtracking
815 -- buffer. This can be replaced by a sliding/ring buffer using Data.Array.
816 -- That will allow us more efficient random back and forth movement.
817 go _ st backBuf !pst = do
818 r <- step defState st
820 D.Yield x s -> gobuf SPEC [x] s backBuf pst
821 D.Skip s -> go SPEC s backBuf pst
822 D.Stop -> goStop backBuf pst
824 gobuf !_ [] s backBuf !pst = go SPEC s backBuf pst
825 gobuf !_ (x:xs) s backBuf !pst = do
829 gobuf SPEC xs s (List []) pst1
830 PR.Partial n pst1 -> do
832 (n <= sum (map Array.length (x:getList backBuf)))
834 let src0 = takeArrayListRev n (x:getList backBuf)
835 src = Prelude.reverse src0 ++ xs
836 gobuf SPEC src s (List []) pst1
837 PR.Continue 0 pst1 ->
838 gobuf SPEC xs s (List (x:getList backBuf)) pst1
839 PR.Continue n pst1 -> do
841 (n <= sum (map Array.length (x:getList backBuf)))
843 let (src0, buf1) = splitAtArrayListRev n (x:getList backBuf)
844 src = Prelude.reverse src0 ++ xs
845 gobuf SPEC src s (List buf1) pst1
847 return (b, D.Stream step s)
850 (n <= sum (map Array.length (x:getList backBuf)))
852 let src0 = takeArrayListRev n (x:getList backBuf)
853 src = Prelude.reverse src0 ++ xs
854 return (b, D.append (D.fromList src) (D.Stream step s))
855 PR.Error err -> throwM $ ParseError err
857 -- This is a simplified gobuf
858 goExtract _ [] backBuf !pst = goStop backBuf pst
859 goExtract _ (x:xs) backBuf !pst = do
863 goExtract SPEC xs (List []) pst1
864 PR.Partial n pst1 -> do
866 (n <= sum (map Array.length (x:getList backBuf)))
868 let src0 = takeArrayListRev n (x:getList backBuf)
869 src = Prelude.reverse src0 ++ xs
870 goExtract SPEC src (List []) pst1
871 PR.Continue 0 pst1 ->
872 goExtract SPEC xs (List (x:getList backBuf)) pst1
873 PR.Continue n pst1 -> do
875 (n <= sum (map Array.length (x:getList backBuf)))
877 let (src0, buf1) = splitAtArrayListRev n (x:getList backBuf)
878 src = Prelude.reverse src0 ++ xs
879 goExtract SPEC src (List buf1) pst1
884 (n <= sum (map Array.length (x:getList backBuf)))
886 let src0 = takeArrayListRev n (x:getList backBuf)
887 src = Prelude.reverse src0 ++ xs
888 return (b, D.fromList src)
889 PR.Error err -> throwM $ ParseError err
891 -- This is a simplified goExtract
892 {-# INLINE goStop #-}
893 goStop backBuf pst = do
896 PR.Partial _ _ -> error "Bug: runArrayParserDBreak: Partial in extract"
898 error "runArrayParserDBreak: extract, Continue 0 creates infinite loop"
899 PR.Continue n pst1 -> do
901 (n <= sum (map Array.length (getList backBuf)))
903 let (src0, buf1) = splitAtArrayListRev n (getList backBuf)
904 src = Prelude.reverse src0
905 goExtract SPEC src (List buf1) pst1
906 PR.Done 0 b -> return (b, D.nil)
909 (n <= sum (map Array.length (getList backBuf)))
911 let src0 = takeArrayListRev n (getList backBuf)
912 src = Prelude.reverse src0
913 return (b, D.fromList src)
914 PR.Error err -> throwM $ ParseError err
917-- | Parse an array stream using the supplied 'Parser'. Returns the parse
918-- result and the unconsumed stream. Throws 'ParseError' if the parse fails.
922{-# INLINE parseArr #-}
924 (MonadIO m, MonadThrow m, Unboxed a)
926 -> Stream m (A.Array a)
927 -> m (b, Stream m (A.Array a))
928parseArr p s = fmap fromStreamD <$> parseBreakD p (toStreamD s)
931-- | Fold an array stream using the supplied array stream 'Fold'.
935{-# INLINE runArrayFold #-}
936runArrayFold :: (MonadIO m, MonadThrow m, Unboxed a) =>
937 ArrayFold m a b -> Stream m (A.Array a) -> m b
938runArrayFold (ArrayFold p) s = fst <$> runArrayParserDBreak p (toStreamD s)
940-- | Like 'fold' but also returns the remaining stream.
944{-# INLINE runArrayFoldBreak #-}
945runArrayFoldBreak :: (MonadIO m, MonadThrow m, Unboxed a) =>
946 ArrayFold m a b -> Stream m (A.Array a) -> m (b, Stream m (A.Array a))
947runArrayFoldBreak (ArrayFold p) s =
948 second fromStreamD <$> runArrayParserDBreak p (toStreamD s)
950{-# ANN type ParseChunksState Fuse #-}
951data ParseChunksState x inpBuf st pst =
952 ParseChunksInit inpBuf st
953 | ParseChunksInitBuf inpBuf
954 | ParseChunksInitLeftOver inpBuf
955 | ParseChunksStream st inpBuf !pst
956 | ParseChunksStop inpBuf !pst
957 | ParseChunksBuf inpBuf st inpBuf !pst
958 | ParseChunksExtract inpBuf inpBuf !pst
959 | ParseChunksYield x (ParseChunksState x inpBuf st pst)
961{-# INLINE_NORMAL runArrayFoldManyD #-}
963 :: (MonadThrow m, Unboxed a)
965 -> D.Stream m (Array a)
968 (ArrayFold (PRD.Parser pstep initial extract)) (D.Stream step state) =
970 D.Stream stepOuter (ParseChunksInit [] state)
974 {-# INLINE_LATE stepOuter #-}
975 -- Buffer is empty, get the first element from the stream, initialize the
976 -- fold and then go to stream processing loop.
977 stepOuter gst (ParseChunksInit [] st) = do
978 r <- step (adaptState gst) st
984 return $ D.Skip $ ParseChunksBuf [x] s [] ps
986 let next = ParseChunksInit [x] s
987 in return $ D.Skip $ ParseChunksYield pb next
988 PRD.IError err -> throwM $ ParseError err
989 D.Skip s -> return $ D.Skip $ ParseChunksInit [] s
990 D.Stop -> return D.Stop
992 -- Buffer is not empty, go to buffered processing loop
993 stepOuter _ (ParseChunksInit src st) = do
997 return $ D.Skip $ ParseChunksBuf src st [] ps
999 let next = ParseChunksInit src st
1000 in return $ D.Skip $ ParseChunksYield pb next
1001 PRD.IError err -> throwM $ ParseError err
1003 -- This is a simplified ParseChunksInit
1004 stepOuter _ (ParseChunksInitBuf src) = do
1008 return $ D.Skip $ ParseChunksExtract src [] ps
1010 let next = ParseChunksInitBuf src
1011 in return $ D.Skip $ ParseChunksYield pb next
1012 PRD.IError err -> throwM $ ParseError err
1014 -- XXX we just discard any leftover input at the end
1015 stepOuter _ (ParseChunksInitLeftOver _) = return D.Stop
1017 -- Buffer is empty, process elements from the stream
1018 stepOuter gst (ParseChunksStream st backBuf pst) = do
1019 r <- step (adaptState gst) st
1024 PR.Partial 0 pst1 ->
1025 return $ D.Skip $ ParseChunksStream s [] pst1
1026 PR.Partial n pst1 -> do
1028 (n <= sum (map Array.length (x:backBuf)))
1030 let src0 = takeArrayListRev n (x:backBuf)
1031 src = Prelude.reverse src0
1032 return $ D.Skip $ ParseChunksBuf src s [] pst1
1033 PR.Continue 0 pst1 ->
1034 return $ D.Skip $ ParseChunksStream s (x:backBuf) pst1
1035 PR.Continue n pst1 -> do
1037 (n <= sum (map Array.length (x:backBuf)))
1039 let (src0, buf1) = splitAtArrayListRev n (x:backBuf)
1040 src = Prelude.reverse src0
1041 return $ D.Skip $ ParseChunksBuf src s buf1 pst1
1044 ParseChunksYield b (ParseChunksInit [] s)
1047 (n <= sum (map Array.length (x:backBuf)))
1049 let src0 = takeArrayListRev n (x:backBuf)
1050 let src = Prelude.reverse src0
1052 ParseChunksYield b (ParseChunksInit src s)
1053 PR.Error err -> throwM $ ParseError err
1054 D.Skip s -> return $ D.Skip $ ParseChunksStream s backBuf pst
1055 D.Stop -> return $ D.Skip $ ParseChunksStop backBuf pst
1057 -- go back to stream processing mode
1058 stepOuter _ (ParseChunksBuf [] s buf pst) =
1059 return $ D.Skip $ ParseChunksStream s buf pst
1061 -- buffered processing loop
1062 stepOuter _ (ParseChunksBuf (x:xs) s backBuf pst) = do
1065 PR.Partial 0 pst1 ->
1066 return $ D.Skip $ ParseChunksBuf xs s [] pst1
1067 PR.Partial n pst1 -> do
1068 assert (n <= sum (map Array.length (x:backBuf))) (return ())
1069 let src0 = takeArrayListRev n (x:backBuf)
1070 src = Prelude.reverse src0 ++ xs
1071 return $ D.Skip $ ParseChunksBuf src s [] pst1
1072 PR.Continue 0 pst1 ->
1073 return $ D.Skip $ ParseChunksBuf xs s (x:backBuf) pst1
1074 PR.Continue n pst1 -> do
1075 assert (n <= sum (map Array.length (x:backBuf))) (return ())
1076 let (src0, buf1) = splitAtArrayListRev n (x:backBuf)
1077 src = Prelude.reverse src0 ++ xs
1078 return $ D.Skip $ ParseChunksBuf src s buf1 pst1
1080 return $ D.Skip $ ParseChunksYield b (ParseChunksInit xs s)
1082 assert (n <= sum (map Array.length (x:backBuf))) (return ())
1083 let src0 = takeArrayListRev n (x:backBuf)
1084 src = Prelude.reverse src0 ++ xs
1085 return $ D.Skip $ ParseChunksYield b (ParseChunksInit src s)
1086 PR.Error err -> throwM $ ParseError err
1088 -- This is a simplified ParseChunksBuf
1089 stepOuter _ (ParseChunksExtract [] buf pst) =
1090 return $ D.Skip $ ParseChunksStop buf pst
1092 stepOuter _ (ParseChunksExtract (x:xs) backBuf pst) = do
1095 PR.Partial 0 pst1 ->
1096 return $ D.Skip $ ParseChunksExtract xs [] pst1
1097 PR.Partial n pst1 -> do
1098 assert (n <= sum (map Array.length (x:backBuf))) (return ())
1099 let src0 = takeArrayListRev n (x:backBuf)
1100 src = Prelude.reverse src0 ++ xs
1101 return $ D.Skip $ ParseChunksExtract src [] pst1
1102 PR.Continue 0 pst1 ->
1103 return $ D.Skip $ ParseChunksExtract xs (x:backBuf) pst1
1104 PR.Continue n pst1 -> do
1105 assert (n <= sum (map Array.length (x:backBuf))) (return ())
1106 let (src0, buf1) = splitAtArrayListRev n (x:backBuf)
1107 src = Prelude.reverse src0 ++ xs
1108 return $ D.Skip $ ParseChunksExtract src buf1 pst1
1110 return $ D.Skip $ ParseChunksYield b (ParseChunksInitBuf xs)
1112 assert (n <= sum (map Array.length (x:backBuf))) (return ())
1113 let src0 = takeArrayListRev n (x:backBuf)
1114 src = Prelude.reverse src0 ++ xs
1115 return $ D.Skip $ ParseChunksYield b (ParseChunksInitBuf src)
1116 PR.Error err -> throwM $ ParseError err
1118 -- This is a simplified ParseChunksExtract
1119 stepOuter _ (ParseChunksStop backBuf pst) = do
1122 PR.Partial _ _ -> error "runArrayFoldManyD: Partial in extract"
1123 PR.Continue 0 _ -> error "runArrayFoldManyD: Continue 0 in extract"
1124 PR.Continue n pst1 -> do
1125 assert (n <= sum (map Array.length backBuf)) (return ())
1126 let (src0, buf1) = splitAtArrayListRev n backBuf
1127 src = Prelude.reverse src0
1128 return $ D.Skip $ ParseChunksExtract src buf1 pst1
1130 return $ D.Skip $ ParseChunksYield b (ParseChunksInitLeftOver [])
1132 assert (n <= sum (map Array.length backBuf)) (return ())
1133 let src0 = takeArrayListRev n backBuf
1134 src = Prelude.reverse src0
1135 return $ D.Skip $ ParseChunksYield b (ParseChunksInitBuf src)
1136 PR.Error err -> throwM $ ParseError err
1138 stepOuter _ (ParseChunksYield a next) = return $ D.Yield a next
1140-- | Apply an 'ArrayFold' repeatedly on an array stream and emit the
1141-- fold outputs in the output stream.
1143-- See "Streamly.Prelude.foldMany" for more details.
1146{-# INLINE runArrayFoldMany #-}
1148 :: (IsStream t, MonadThrow m, Unboxed a)
1152runArrayFoldMany p m = fromStreamD $ runArrayFoldManyD 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