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.Serial (SerialT)
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 aEnd)
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 -> SerialT m (Array a) -> SerialT 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 -> SerialT m (A.Array a)
400 -> m (b, SerialT 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 -> SerialT 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 => SerialT 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 => SerialT 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 => SerialT 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) => SerialT 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 (SerialT 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]}
545-- This can be generalized to any type provided it can be unfolded to a stream
546-- and it can be combined using a semigroup operation.
548-- XXX This should be written using CPS (as parseK) if we want it to scale wrt
549-- to the number of times it can be called on the same stream.
550{-# INLINE_NORMAL parseBreakD #-}
552 forall m a b. (MonadIO m, MonadThrow m, Unboxed a)
554 -> D.Stream m (Array.Array a)
555 -> m (b, D.Stream m (Array.Array a))
557 (PRD.Parser pstep initial extract) stream@(D.Stream step state) = do
561 PRD.IPartial s -> go SPEC state (List []) s
562 PRD.IDone b -> return (b, stream)
563 PRD.IError err -> throwM $ ParseError err
567 -- "backBuf" contains last few items in the stream that we may have to
570 -- XXX currently we are using a dumb list based approach for backtracking
571 -- buffer. This can be replaced by a sliding/ring buffer using Data.Array.
572 -- That will allow us more efficient random back and forth movement.
573 go !_ st backBuf !pst = do
574 r <- step defState st
576 D.Yield (Array contents start end) s ->
578 (Tuple' end contents) start pst
579 D.Skip s -> go SPEC s backBuf pst
584 -- Use strictness on "cur" to keep it unboxed
585 gobuf !_ s backBuf (Tuple' end _) !cur !pst
587 go SPEC s backBuf pst
588 gobuf !_ s backBuf fp@(Tuple' end contents) !cur !pst = do
589 x <- liftIO $ peekWith contents cur
591 let next = INDEX_NEXT(cur,a)
594 gobuf SPEC s (List []) fp next pst1
595 PR.Partial n pst1 -> do
596 assert (n <= Prelude.length (x:getList backBuf)) (return ())
597 let src0 = Prelude.take n (x:getList backBuf)
598 arr0 = A.fromListN n (Prelude.reverse src0)
599 arr1 = Array contents next end
601 let !(Array cont1 start end1) = src
602 fp1 = Tuple' end1 cont1
603 gobuf SPEC s (List []) fp1 start pst1
604 PR.Continue 0 pst1 ->
605 gobuf SPEC s (List (x:getList backBuf)) fp next pst1
606 PR.Continue n pst1 -> do
607 assert (n <= Prelude.length (x:getList backBuf)) (return ())
608 let (src0, buf1) = splitAt n (x:getList backBuf)
609 arr0 = A.fromListN n (Prelude.reverse src0)
610 arr1 = Array contents next end
612 let !(Array cont1 start end1) = src
613 fp1 = Tuple' end1 cont1
614 gobuf SPEC s (List buf1) fp1 start pst1
616 let arr = Array contents next end
617 return (b, D.cons arr (D.Stream step s))
619 assert (n <= Prelude.length (x:getList backBuf)) (return ())
620 let src0 = Prelude.take n (x:getList backBuf)
621 -- XXX create the array in reverse instead
622 arr0 = A.fromListN n (Prelude.reverse src0)
623 arr1 = Array contents next end
624 -- XXX Use StreamK to avoid adding arbitrary layers of
625 -- constructors every time.
626 str = D.cons arr0 (D.cons arr1 (D.Stream step s))
628 PR.Error err -> throwM $ ParseError err
630{-# INLINE_NORMAL parseBreakK #-}
632 forall m a b. (MonadIO m, MonadThrow m, Unboxed a)
634 -> K.Stream m (Array.Array a)
635 -> m (b, K.Stream m (Array.Array a))
636parseBreakK (PRD.Parser pstep initial extract) stream = do
639 PRD.IPartial s -> go s stream []
640 PRD.IDone b -> return (b, stream)
641 PRD.IError err -> throwM $ ParseError err
645 -- "backBuf" contains last few items in the stream that we may have to
648 -- XXX currently we are using a dumb list based approach for backtracking
649 -- buffer. This can be replaced by a sliding/ring buffer using Data.Array.
650 -- That will allow us more efficient random back and forth movement.
651 go !pst st backBuf = do
652 let stop = (, K.nil) <$> extract pst
653 single a = yieldk a K.nil
654 yieldk (Array contents start end) r =
655 let fp = Tuple' end contents
656 in goArray pst backBuf r fp start
657 in K.foldStream defState yieldk single stop st
659 -- Use strictness on "cur" to keep it unboxed
660 goArray !pst backBuf st (Tuple' end _) !cur
663 goArray !pst backBuf st fp@(Tuple' end contents) !cur = do
664 x <- liftIO $ peekWith contents cur
666 let next = INDEX_NEXT(cur,a)
669 goArray s [] st fp next
671 assert (n <= Prelude.length (x:backBuf)) (return ())
672 let src0 = Prelude.take n (x:backBuf)
673 arr0 = A.fromListN n (Prelude.reverse src0)
674 arr1 = Array contents next end
676 let !(Array cont1 start end1) = src
677 fp1 = Tuple' end1 cont1
678 goArray s [] st fp1 start
680 goArray s (x:backBuf) st fp next
681 PR.Continue n s -> do
682 assert (n <= Prelude.length (x:backBuf)) (return ())
683 let (src0, buf1) = splitAt n (x:backBuf)
684 arr0 = A.fromListN n (Prelude.reverse src0)
685 arr1 = Array contents next end
687 let !(Array cont1 start end1) = src
688 fp1 = Tuple' end1 cont1
689 goArray s buf1 st fp1 start
691 let arr = Array contents next end
692 return (b, K.cons arr st)
694 assert (n <= Prelude.length (x:backBuf)) (return ())
695 let src0 = Prelude.take n (x:backBuf)
696 -- XXX Use fromListRevN once implemented
697 -- arr0 = A.fromListRevN n src0
698 arr0 = A.fromListN n (Prelude.reverse src0)
699 arr1 = Array contents next end
700 str = K.cons arr0 (K.cons arr1 st)
702 PR.Error err -> throwM $ ParseError err
704-- | Parse an array stream using the supplied 'Parser'. Returns the parse
705-- result and the unconsumed stream. Throws 'ParseError' if the parse fails.
709{-# INLINE_NORMAL parseBreak #-}
711 (MonadIO m, MonadThrow m, Unboxed a)
713 -> SerialT m (A.Array a)
714 -> m (b, SerialT m (A.Array a))
717 fmap fromStreamD <$> parseBreakD (PRD.fromParserK p) (toStreamD s)
720 fmap (fmap Stream.fromStreamK)
721 . parseBreakK (PRD.fromParserK p)
724-------------------------------------------------------------------------------
725-- Elimination - Running Array Folds and parsers
726-------------------------------------------------------------------------------
728-- | Note that this is not the same as using a @Parser m (Array a) b@ with the
729-- regular "Streamly.Internal.Data.IsStream.parse" function. The regular parse
730-- would consume the input arrays as single unit. This parser parses in the way
731-- as described in the ArrayFold module. The input arrays are treated as @n@
732-- element units and can be consumed partially. The remaining elements are
733-- inserted in the source stream as an array.
735{-# INLINE_NORMAL runArrayParserDBreak #-}
736runArrayParserDBreak ::
737 forall m a b. (MonadIO m, MonadThrow m, Unboxed a)
738 => PRD.Parser m (Array a) b
739 -> D.Stream m (Array.Array a)
740 -> m (b, D.Stream m (Array.Array a))
742 (PRD.Parser pstep initial extract)
743 stream@(D.Stream step state) = do
747 PRD.IPartial s -> go SPEC state (List []) s
748 PRD.IDone b -> return (b, stream)
749 PRD.IError err -> throwM $ ParseError err
753 -- "backBuf" contains last few items in the stream that we may have to
756 -- XXX currently we are using a dumb list based approach for backtracking
757 -- buffer. This can be replaced by a sliding/ring buffer using Data.Array.
758 -- That will allow us more efficient random back and forth movement.
759 go !_ st backBuf !pst = do
760 r <- step defState st
762 D.Yield x s -> gobuf SPEC [x] s backBuf pst
763 D.Skip s -> go SPEC s backBuf pst
768 gobuf !_ [] s backBuf !pst = go SPEC s backBuf pst
769 gobuf !_ (x:xs) s backBuf !pst = do
773 gobuf SPEC xs s (List []) pst1
774 PR.Partial n pst1 -> do
776 (n <= sum (map Array.length (x:getList backBuf)))
778 let src0 = takeArrayListRev n (x:getList backBuf)
779 src = Prelude.reverse src0 ++ xs
780 gobuf SPEC src s (List []) pst1
781 PR.Continue 0 pst1 ->
782 gobuf SPEC xs s (List (x:getList backBuf)) pst1
783 PR.Continue n pst1 -> do
785 (n <= sum (map Array.length (x:getList backBuf)))
787 let (src0, buf1) = splitAtArrayListRev n (x:getList backBuf)
788 src = Prelude.reverse src0 ++ xs
789 gobuf SPEC src s (List buf1) pst1
791 return (b, D.Stream step s)
794 (n <= sum (map Array.length (x:getList backBuf)))
796 let src0 = takeArrayListRev n (x:getList backBuf)
797 src = Prelude.reverse src0 ++ xs
798 return (b, D.append (D.fromList src) (D.Stream step s))
799 PR.Error err -> throwM $ ParseError err
802-- | Parse an array stream using the supplied 'Parser'. Returns the parse
803-- result and the unconsumed stream. Throws 'ParseError' if the parse fails.
807{-# INLINE parseArr #-}
809 (MonadIO m, MonadThrow m, Unboxed a)
811 -> SerialT m (A.Array a)
812 -> m (b, SerialT m (A.Array a))
813parseArr p s = fmap fromStreamD <$> parseBreakD p (toStreamD s)
816-- | Fold an array stream using the supplied array stream 'Fold'.
820{-# INLINE runArrayFold #-}
821runArrayFold :: (MonadIO m, MonadThrow m, Unboxed a) =>
822 ArrayFold m a b -> SerialT m (A.Array a) -> m b
823runArrayFold (ArrayFold p) s = fst <$> runArrayParserDBreak p (toStreamD s)
825-- | Like 'fold' but also returns the remaining stream.
829{-# INLINE runArrayFoldBreak #-}
830runArrayFoldBreak :: (MonadIO m, MonadThrow m, Unboxed a) =>
831 ArrayFold m a b -> SerialT m (A.Array a) -> m (b, SerialT m (A.Array a))
832runArrayFoldBreak (ArrayFold p) s =
833 second fromStreamD <$> runArrayParserDBreak p (toStreamD s)
835{-# ANN type ParseChunksState Fuse #-}
836data ParseChunksState x inpBuf st pst =
837 ParseChunksInit inpBuf st
838 | ParseChunksInitLeftOver inpBuf
839 | ParseChunksStream st inpBuf !pst
840 | ParseChunksBuf inpBuf st inpBuf !pst
841 | ParseChunksYield x (ParseChunksState x inpBuf st pst)
843{-# INLINE_NORMAL runArrayFoldManyD #-}
845 :: (MonadThrow m, Unboxed a)
847 -> D.Stream m (Array a)
850 (ArrayFold (PRD.Parser pstep initial extract)) (D.Stream step state) =
852 D.Stream stepOuter (ParseChunksInit [] state)
856 {-# INLINE_LATE stepOuter #-}
857 -- Buffer is empty, get the first element from the stream, initialize the
858 -- fold and then go to stream processing loop.
859 stepOuter gst (ParseChunksInit [] st) = do
860 r <- step (adaptState gst) st
866 return $ D.Skip $ ParseChunksBuf [x] s [] ps
868 let next = ParseChunksInit [x] s
869 in return $ D.Skip $ ParseChunksYield pb next
870 PRD.IError err -> throwM $ ParseError err
871 D.Skip s -> return $ D.Skip $ ParseChunksInit [] s
872 D.Stop -> return D.Stop
874 -- Buffer is not empty, go to buffered processing loop
875 stepOuter _ (ParseChunksInit src st) = do
879 return $ D.Skip $ ParseChunksBuf src st [] ps
881 let next = ParseChunksInit src st
882 in return $ D.Skip $ ParseChunksYield pb next
883 PRD.IError err -> throwM $ ParseError err
885 -- XXX we just discard any leftover input at the end
886 stepOuter _ (ParseChunksInitLeftOver _) = return D.Stop
888 -- Buffer is empty, process elements from the stream
889 stepOuter gst (ParseChunksStream st backBuf pst) = do
890 r <- step (adaptState gst) st
896 return $ D.Skip $ ParseChunksStream s [] pst1
897 PR.Partial n pst1 -> do
899 (n <= sum (map Array.length (x:backBuf)))
901 let src0 = takeArrayListRev n (x:backBuf)
902 src = Prelude.reverse src0
903 return $ D.Skip $ ParseChunksBuf src s [] pst1
904 PR.Continue 0 pst1 ->
905 return $ D.Skip $ ParseChunksStream s (x:backBuf) pst1
906 PR.Continue n pst1 -> do
908 (n <= sum (map Array.length (x:backBuf)))
910 let (src0, buf1) = splitAtArrayListRev n (x:backBuf)
911 src = Prelude.reverse src0
912 return $ D.Skip $ ParseChunksBuf src s buf1 pst1
915 ParseChunksYield b (ParseChunksInit [] s)
918 (n <= sum (map Array.length (x:backBuf)))
920 let src0 = takeArrayListRev n (x:backBuf)
921 let src = Prelude.reverse src0
923 ParseChunksYield b (ParseChunksInit src s)
924 PR.Error err -> throwM $ ParseError err
925 D.Skip s -> return $ D.Skip $ ParseChunksStream s backBuf pst
928 let src = Prelude.reverse backBuf
930 ParseChunksYield b (ParseChunksInitLeftOver src)
932 -- go back to stream processing mode
933 stepOuter _ (ParseChunksBuf [] s buf pst) =
934 return $ D.Skip $ ParseChunksStream s buf pst
936 -- buffered processing loop
937 stepOuter _ (ParseChunksBuf (x:xs) s backBuf pst) = do
941 return $ D.Skip $ ParseChunksBuf xs s [] pst1
942 PR.Partial n pst1 -> do
943 assert (n <= sum (map Array.length (x:backBuf))) (return ())
944 let src0 = takeArrayListRev n (x:backBuf)
945 src = Prelude.reverse src0 ++ xs
946 return $ D.Skip $ ParseChunksBuf src s [] pst1
947 PR.Continue 0 pst1 ->
948 return $ D.Skip $ ParseChunksBuf xs s (x:backBuf) pst1
949 PR.Continue n pst1 -> do
950 assert (n <= sum (map Array.length (x:backBuf))) (return ())
951 let (src0, buf1) = splitAtArrayListRev n (x:backBuf)
952 src = Prelude.reverse src0 ++ xs
953 return $ D.Skip $ ParseChunksBuf src s buf1 pst1
955 return $ D.Skip $ ParseChunksYield b (ParseChunksInit xs s)
957 assert (n <= sum (map Array.length (x:backBuf))) (return ())
958 let src0 = takeArrayListRev n (x:backBuf)
959 src = Prelude.reverse src0 ++ xs
960 return $ D.Skip $ ParseChunksYield b (ParseChunksInit src s)
961 PR.Error err -> throwM $ ParseError err
963 stepOuter _ (ParseChunksYield a next) = return $ D.Yield a next
965-- | Apply an 'ArrayFold' repeatedly on an array stream and emit the
966-- fold outputs in the output stream.
968-- See "Streamly.Prelude.foldMany" for more details.
971{-# INLINE runArrayFoldMany #-}
973 :: (IsStream t, MonadThrow m, Unboxed a)
977runArrayFoldMany 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