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
41#if !defined(mingw32_HOST_OS)
54import Data.Bifunctor (second)
55import Control.Exception (assert)
56import Control.Monad.Catch (MonadThrow, throwM)
57import Control.Monad.IO.Class (MonadIO(..))
58#if __GLASGOW_HASKELL__ < 808
59import Data.Semigroup (Semigroup(..))
61import Data.Word (Word8)
62import Foreign.ForeignPtr (touchForeignPtr)
63import Foreign.ForeignPtr.Unsafe (unsafeForeignPtrToPtr)
64import Foreign.Ptr (minusPtr, plusPtr, castPtr)
65import Foreign.Storable (Storable(..))
66import Fusion.Plugin.Types (Fuse(..))
67import GHC.Exts (SpecConstrAnnotation(..))
68import GHC.ForeignPtr (ForeignPtr(..))
69import GHC.Ptr (Ptr(..))
70import GHC.Types (SPEC(..))
71import Prelude hiding (null, last, (!!), read, concat, unlines)
73#if !defined(mingw32_HOST_OS)
74import Streamly.Internal.FileSystem.FDIO (IOVec(..))
77import Streamly.Internal.BaseCompat
78import Streamly.Internal.Data.Array.Foreign.Type (Array(..))
79import Streamly.Internal.Data.Fold.Type (Fold(..))
80import Streamly.Internal.Data.Parser (ParseError(..))
81import Streamly.Internal.Data.Stream.Serial (SerialT)
82import Streamly.Internal.Data.Stream.StreamK.Type (IsStream)
83import Streamly.Internal.Data.SVar (adaptState, defState)
84import Streamly.Internal.Data.Array.Foreign.Mut.Type (memcpy, bytesToElemCount)
85import Streamly.Internal.System.IO (mkChunkSizeKB)
87import qualified Streamly.Internal.Data.Array.Foreign as A
88import qualified Streamly.Internal.Data.Array.Foreign as Array
89import qualified Streamly.Internal.Data.Array.Foreign.Type as A
90import qualified Streamly.Internal.Data.Array.Foreign.Mut.Type as MA
91import qualified Streamly.Internal.Data.Array.Stream.Mut.Foreign as AS
92import qualified Streamly.Internal.Data.Array.Stream.Fold.Foreign as ASF
93import qualified Streamly.Internal.Data.Fold.Type as FL
94import qualified Streamly.Internal.Data.Parser as PR
95import qualified Streamly.Internal.Data.Parser.ParserD as PRD
96import qualified Streamly.Internal.Data.Stream.IsStream as S
97import qualified Streamly.Internal.Data.Stream.StreamD as D
99-------------------------------------------------------------------------------
101-------------------------------------------------------------------------------
103-- | @arraysOf n stream@ groups the elements in the input stream into arrays of
106-- > arraysOf n = Stream.chunksOf n (Array.writeN n)
109{-# INLINE arraysOf #-}
110arraysOf :: (IsStream t, MonadIO m, Storable a)
111 => Int -> t m a -> t m (Array a)
112arraysOf n str = D.fromStreamD $ A.arraysOf n (D.toStreamD str)
114-------------------------------------------------------------------------------
116-------------------------------------------------------------------------------
118-- XXX efficiently compare two streams of arrays. Two streams can have chunks
119-- of different sizes, we can handle that in the stream comparison abstraction.
120-- This could be useful e.g. to fast compare whether two files differ.
122-- | Convert a stream of arrays into a stream of their elements.
124-- Same as the following but more efficient:
126-- > concat = Stream.unfoldMany Array.read
130concat :: (IsStream t, MonadIO m, Storable a) => t m (Array a) -> t m a
131-- concat m = D.fromStreamD $ A.flattenArrays (D.toStreamD m)
132-- concat m = D.fromStreamD $ D.concatMap A.toStreamD (D.toStreamD m)
133concat m = D.fromStreamD $ D.unfoldMany A.read (D.toStreamD m)
135-- | Convert a stream of arrays into a stream of their elements reversing the
136-- contents of each array before flattening.
138-- > concatRev = Stream.unfoldMany Array.readRev
141{-# INLINE concatRev #-}
142concatRev :: (IsStream t, MonadIO m, Storable a) => t m (Array a) -> t m a
143concatRev m = D.fromStreamD $ A.flattenArraysRev (D.toStreamD m)
145-------------------------------------------------------------------------------
146-- Intersperse and append
147-------------------------------------------------------------------------------
149-- | Flatten a stream of arrays after inserting the given element between
153{-# INLINE interpose #-}
154interpose :: (MonadIO m, IsStream t, Storable a) => a -> t m (Array a) -> t m a
155interpose x = S.interpose x A.read
157{-# INLINE intercalateSuffix #-}
158intercalateSuffix :: (MonadIO m, IsStream t, Storable a)
159 => Array a -> t m (Array a) -> t m a
160intercalateSuffix = S.intercalateSuffix A.read
162-- | Flatten a stream of arrays appending the given element after each
166{-# INLINE interposeSuffix #-}
167interposeSuffix :: (MonadIO m, IsStream t, Storable a)
168 => a -> t m (Array a) -> t m a
169-- interposeSuffix x = D.fromStreamD . A.unlines x . D.toStreamD
170interposeSuffix x = S.interposeSuffix x A.read
172data FlattenState s a =
174 | InnerLoop s !(ForeignPtr a) !(Ptr a) !(Ptr a)
176{-# INLINE_NORMAL unlines #-}
177unlines :: forall m a. (MonadIO m, Storable 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 let p = unsafeForeignPtrToPtr aStart
187 in D.Skip (InnerLoop s aStart p aEnd)
188 D.Skip s -> D.Skip (OuterLoop s)
191 step' _ (InnerLoop st _ p end) | p == end =
192 return $ D.Yield sep $ OuterLoop st
194 step' _ (InnerLoop st startf p end) = do
197 touchForeignPtr startf
199 return $ D.Yield x (InnerLoop st startf
200 (p `plusPtr` sizeOf (undefined :: a)) end)
202-------------------------------------------------------------------------------
204-------------------------------------------------------------------------------
206-- XXX These would not be needed once we implement compactLEFold, see
207-- module Streamly.Internal.Data.Array.Stream.Mut.Foreign
209{-# INLINE_NORMAL packArraysChunksOf #-}
210packArraysChunksOf :: (MonadIO m, Storable a)
211 => Int -> D.Stream m (Array a) -> D.Stream m (Array a)
212packArraysChunksOf n str =
213 D.map A.unsafeFreeze $ AS.packArraysChunksOf n $ D.map A.unsafeThaw str
215-- XXX instead of writing two different versions of this operation, we should
216-- write it as a pipe.
217{-# INLINE_NORMAL lpackArraysChunksOf #-}
218lpackArraysChunksOf :: (MonadIO m, Storable a)
219 => Int -> Fold m (Array a) () -> Fold m (Array a) ()
220lpackArraysChunksOf n fld =
221 FL.map A.unsafeThaw $ AS.lpackArraysChunksOf n (FL.map A.unsafeFreeze fld)
223#if !defined(mingw32_HOST_OS)
225-- | @groupIOVecsOf maxBytes maxEntries@ groups arrays in the incoming stream
226-- to create a stream of 'IOVec' arrays with a maximum of @maxBytes@ bytes in
227-- each array and a maximum of @maxEntries@ entries in each array.
230{-# INLINE_NORMAL groupIOVecsOf #-}
231groupIOVecsOf :: MonadIO m
232 => Int -> Int -> D.Stream m (Array a) -> D.Stream m (Array IOVec)
233groupIOVecsOf n maxIOVLen str =
235 $ AS.groupIOVecsOf n maxIOVLen
236 $ D.map A.unsafeThaw str
239-- | Coalesce adjacent arrays in incoming stream to form bigger arrays of a
240-- maximum specified size in bytes.
243{-# INLINE compact #-}
244compact :: (MonadIO m, Storable a)
245 => Int -> SerialT m (Array a) -> SerialT m (Array a)
246compact n xs = D.fromStreamD $ packArraysChunksOf n (D.toStreamD xs)
248-------------------------------------------------------------------------------
250-------------------------------------------------------------------------------
256 | Yielding arr (SplitState s arr)
259-- | Split a stream of arrays on a given separator byte, dropping the separator
260-- and coalescing all the arrays between two separators into a single array.
263{-# INLINE_NORMAL _splitOn #-}
267 -> D.Stream m (Array Word8)
268 -> D.Stream m (Array Word8)
269_splitOn byte (D.Stream step state) = D.Stream step' (Initial state)
273 {-# INLINE_LATE step' #-}
274 step' gst (Initial st) = do
278 (arr1, marr2) <- A.breakOn byte arr
279 return $ case marr2 of
280 Nothing -> D.Skip (Buffering s arr1)
281 Just arr2 -> D.Skip (Yielding arr1 (Splitting s arr2))
282 D.Skip s -> return $ D.Skip (Initial s)
283 D.Stop -> return D.Stop
285 step' gst (Buffering st buf) = do
289 (arr1, marr2) <- A.breakOn byte arr
290 buf' <- A.spliceTwo buf arr1
291 return $ case marr2 of
292 Nothing -> D.Skip (Buffering s buf')
293 Just x -> D.Skip (Yielding buf' (Splitting s x))
294 D.Skip s -> return $ D.Skip (Buffering s buf)
296 if A.byteLength buf == 0
298 else D.Skip (Yielding buf Finishing)
300 step' _ (Splitting st buf) = do
301 (arr1, marr2) <- A.breakOn byte buf
302 return $ case marr2 of
303 Nothing -> D.Skip $ Buffering st arr1
304 Just arr2 -> D.Skip $ Yielding arr1 (Splitting st arr2)
306 step' _ (Yielding arr next) = return $ D.Yield arr next
307 step' _ Finishing = return D.Stop
309-- | Split a stream of arrays on a given separator byte, dropping the separator
310-- and coalescing all the arrays between two separators into a single array.
313{-# INLINE splitOn #-}
315 :: (IsStream t, MonadIO m)
320 D.fromStreamD $ D.splitInnerBy (A.breakOn byte) A.spliceTwo $ D.toStreamD s
322{-# INLINE splitOnSuffix #-}
324 :: (IsStream t, MonadIO m)
328-- splitOn byte s = D.fromStreamD $ A.splitOn byte $ D.toStreamD s
329splitOnSuffix byte s =
330 D.fromStreamD $ D.splitInnerBySuffix (A.breakOn byte) A.spliceTwo $ D.toStreamD s
332-------------------------------------------------------------------------------
333-- Elimination - Running folds
334-------------------------------------------------------------------------------
336-- XXX This should be written using CPS (as foldK) if we want it to scale wrt
337-- to the number of times it can be called on the same stream.
339{-# INLINE_NORMAL foldD #-}
340foldD :: forall m a b. (MonadIO m, Storable a) =>
341 Fold m a b -> D.Stream m (Array a) -> m (b, D.Stream m (Array a))
342foldD (Fold fstep initial extract) stream@(D.Stream step state) = do
345 FL.Partial fs -> go SPEC state fs
346 FL.Done fb -> return $! (fb, stream)
352 r <- step defState st
354 D.Yield (Array (ForeignPtr start contents) (Ptr end)) s ->
355 goArray SPEC s (ForeignPtr end contents) (Ptr start) fs
356 D.Skip s -> go SPEC s fs
361 goArray !_ s fp@(ForeignPtr end _) !cur !fs
362 | cur == Ptr end = do
363 liftIO $ touchForeignPtr fp
365 goArray !_ st fp@(ForeignPtr end contents) !cur !fs = do
366 x <- liftIO $ peek cur
368 let elemSize = sizeOf (undefined :: a)
369 next = cur `plusPtr` elemSize
372 let !(Ptr curAddr) = next
373 arr = Array (ForeignPtr curAddr contents) (Ptr end)
374 return $! (b, D.cons arr (D.Stream step st))
375 FL.Partial fs1 -> goArray SPEC st fp next fs1
377-- | Fold an array stream using the supplied 'Fold'. Returns the fold result
378-- and the unconsumed stream.
382{-# INLINE_NORMAL fold #-}
384 (MonadIO m, Storable a)
386 -> SerialT m (A.Array a)
387 -> m (b, SerialT m (A.Array a))
388fold f s = fmap D.fromStreamD <$> foldD f (D.toStreamD s)
390-------------------------------------------------------------------------------
391-- Fold to a single Array
392-------------------------------------------------------------------------------
394-- When we have to take an array partially, take the last part of the array.
395{-# INLINE takeArrayListRev #-}
396takeArrayListRev :: forall a. Storable a => Int -> [Array a] -> [Array a]
404 let len = Array.length x
406 then x : go (n - len) xs
409 else let !(Array (ForeignPtr _ contents) end) = x
410 sz = sizeOf (undefined :: a)
411 !(Ptr start) = end `plusPtr` negate (n * sz)
412 in [Array (ForeignPtr start contents) end]
414-- When we have to take an array partially, take the last part of the array in
416{-# INLINE splitAtArrayListRev #-}
417splitAtArrayListRev :: forall a. Storable a =>
418 Int -> [Array a] -> ([Array a],[Array a])
419splitAtArrayListRev n ls
421 | otherwise = go n ls
423 go :: Int -> [Array a] -> ([Array a], [Array a])
426 let len = Array.length x
427 (xs', xs'') = go (m - len) xs
432 else let !(Array (ForeignPtr start contents) end) = x
433 sz = sizeOf (undefined :: a)
434 end1 = end `plusPtr` negate (m * sz)
435 arr2 = Array (ForeignPtr start contents) end1
436 !(Ptr addrEnd1) = end1
437 arr1 = Array (ForeignPtr addrEnd1 contents) end
440-------------------------------------------------------------------------------
441-- Fold to a single Array
442-------------------------------------------------------------------------------
444-- XXX Both of these implementations of splicing seem to perform equally well.
445-- We need to perform benchmarks over a range of sizes though.
447-- CAUTION! length must more than equal to lengths of all the arrays in the
449{-# INLINE spliceArraysLenUnsafe #-}
450spliceArraysLenUnsafe :: (MonadIO m, Storable a)
451 => Int -> SerialT m (MA.Array a) -> m (MA.Array a)
452spliceArraysLenUnsafe len buffered = do
453 arr <- liftIO $ MA.newArray len
454 end <- S.foldlM' writeArr (return $ MA.aEnd arr) buffered
455 return $ arr {MA.aEnd = end}
459 writeArr dst (MA.Array as ae _) =
460 liftIO $ unsafeWithForeignPtr as $ \src -> do
461 let count = ae `minusPtr` src
462 memcpy (castPtr dst) (castPtr src) count
463 return $ dst `plusPtr` count
465{-# INLINE _spliceArrays #-}
466_spliceArrays :: (MonadIO m, Storable a)
467 => SerialT m (Array a) -> m (Array a)
469 buffered <- S.foldr S.cons S.nil s
470 len <- S.sum (S.map Array.length buffered)
471 arr <- liftIO $ MA.newArray len
472 end <- S.foldlM' writeArr (return $ MA.aEnd arr) s
473 return $ A.unsafeFreeze $ arr {MA.aEnd = end}
477 writeArr dst (Array as ae) =
478 liftIO $ unsafeWithForeignPtr as $ \src -> do
479 let count = ae `minusPtr` src
480 memcpy (castPtr dst) (castPtr src) count
481 return $ dst `plusPtr` count
483{-# INLINE _spliceArraysBuffered #-}
484_spliceArraysBuffered :: (MonadIO m, Storable a)
485 => SerialT m (Array a) -> m (Array a)
486_spliceArraysBuffered s = do
487 buffered <- S.foldr S.cons S.nil s
488 len <- S.sum (S.map Array.length buffered)
489 A.unsafeFreeze <$> spliceArraysLenUnsafe len (S.map A.unsafeThaw s)
491{-# INLINE spliceArraysRealloced #-}
492spliceArraysRealloced :: forall m a. (MonadIO m, Storable a)
493 => SerialT m (Array a) -> m (Array a)
494spliceArraysRealloced s = do
495 let idst = liftIO $ MA.newArray (bytesToElemCount (undefined :: a)
498 arr <- S.foldlM' MA.spliceWithDoubling idst (S.map A.unsafeThaw s)
499 liftIO $ A.unsafeFreeze <$> MA.shrinkToFit arr
501-- XXX This should just be "fold A.write"
503-- | Given a stream of arrays, splice them all together to generate a single
504-- array. The stream must be /finite/.
507{-# INLINE toArray #-}
508toArray :: (MonadIO m, Storable a) => SerialT m (Array a) -> m (Array a)
509toArray = spliceArraysRealloced
510-- spliceArrays = _spliceArraysBuffered
512-- exponentially increasing sizes of the chunks upto the max limit.
513-- XXX this will be easier to implement with parsers/terminating folds
514-- With this we should be able to reduce the number of chunks/allocations.
515-- The reallocation/copy based toArray can also be implemented using this.
518{-# INLINE toArraysInRange #-}
519toArraysInRange :: (IsStream t, MonadIO m, Storable a)
520 => Int -> Int -> Fold m (Array a) b -> Fold m a b
521toArraysInRange low high (Fold step initial extract) =
525-- | Fold the input to a pure buffered stream (List) of arrays.
526{-# INLINE _toArraysOf #-}
527_toArraysOf :: (MonadIO m, Storable a)
528 => Int -> Fold m a (SerialT Identity (Array a))
529_toArraysOf n = FL.chunksOf n (A.writeNF n) FL.toStream
532-------------------------------------------------------------------------------
533-- Elimination - running element parsers
534-------------------------------------------------------------------------------
536-- GHC parser does not accept {-# ANN type [] NoSpecConstr #-}, so we need
538{-# ANN type List NoSpecConstr #-}
539newtype List a = List {getList :: [a]}
541-- This can be generalized to any type provided it can be unfolded to a stream
542-- and it can be combined using a semigroup operation.
544-- XXX This should be written using CPS (as parseK) if we want it to scale wrt
545-- to the number of times it can be called on the same stream.
546{-# INLINE_NORMAL parseD #-}
548 forall m a b. (MonadIO m, MonadThrow m, Storable a)
550 -> D.Stream m (Array.Array a)
551 -> m (b, D.Stream m (Array.Array a))
552parseD (PRD.Parser pstep initial extract) stream@(D.Stream step state) = do
555 PRD.IPartial s -> go SPEC state (List []) s
556 PRD.IDone b -> return (b, stream)
557 PRD.IError err -> throwM $ ParseError err
561 -- "backBuf" contains last few items in the stream that we may have to
564 -- XXX currently we are using a dumb list based approach for backtracking
565 -- buffer. This can be replaced by a sliding/ring buffer using Data.Array.
566 -- That will allow us more efficient random back and forth movement.
568 go !_ st backBuf !pst = do
569 r <- step defState st
571 D.Yield (Array (ForeignPtr start contents) (Ptr end)) s ->
572 gobuf SPEC s backBuf (ForeignPtr end contents) (Ptr start) pst
573 D.Skip s -> go SPEC s backBuf pst
578 -- Use strictness on "cur" to keep it unboxed
579 gobuf !_ s backBuf fp@(ForeignPtr end _) !cur !pst
580 | cur == Ptr end = do
581 liftIO $ touchForeignPtr fp
582 go SPEC s backBuf pst
583 gobuf !_ s backBuf fp@(ForeignPtr end contents) !cur !pst = do
584 x <- liftIO $ peek cur
586 let elemSize = sizeOf (undefined :: a)
587 next = cur `plusPtr` elemSize
590 gobuf SPEC s (List []) fp next pst1
591 PR.Partial n pst1 -> do
592 assert (n <= Prelude.length (x:getList backBuf)) (return ())
593 let src0 = Prelude.take n (x:getList backBuf)
594 arr0 = A.fromListN n (Prelude.reverse src0)
595 !(Ptr curAddr) = next
596 arr1 = Array (ForeignPtr curAddr contents) (Ptr end)
598 let !(Array (ForeignPtr start cont1) (Ptr end1)) = src
599 gobuf SPEC s (List []) (ForeignPtr end1 cont1) (Ptr start) pst1
600 PR.Continue 0 pst1 ->
601 gobuf SPEC s (List (x:getList backBuf)) fp next pst1
602 PR.Continue n pst1 -> do
603 assert (n <= Prelude.length (x:getList backBuf)) (return ())
604 let (src0, buf1) = splitAt n (x:getList backBuf)
605 arr0 = A.fromListN n (Prelude.reverse src0)
606 !(Ptr curAddr) = next
607 arr1 = Array (ForeignPtr curAddr contents) (Ptr end)
609 let !(Array (ForeignPtr start cont1) (Ptr end1)) = src
610 gobuf SPEC s (List buf1) (ForeignPtr end1 cont1) (Ptr start) pst1
612 let !(Ptr curAddr) = next
613 arr = Array (ForeignPtr curAddr contents) (Ptr end)
614 return (b, D.cons arr (D.Stream step s))
616 assert (n <= Prelude.length (x:getList backBuf)) (return ())
617 let src0 = Prelude.take n (x:getList backBuf)
618 -- XXX create the array in reverse instead
619 arr0 = A.fromListN n (Prelude.reverse src0)
620 !(Ptr curAddr) = next
621 arr1 = Array (ForeignPtr curAddr contents) (Ptr end)
622 -- XXX Use StreamK to avoid adding arbitrary layers of
623 -- constructors every time.
624 str = D.cons arr0 (D.cons arr1 (D.Stream step s))
626 PR.Error err -> throwM $ ParseError err
628-- | Parse an array stream using the supplied 'Parser'. Returns the parse
629-- result and the unconsumed stream. Throws 'ParseError' if the parse fails.
633{-# INLINE_NORMAL parse #-}
635 (MonadIO m, MonadThrow m, Storable a)
637 -> SerialT m (A.Array a)
638 -> m (b, SerialT m (A.Array a))
639parse p s = fmap D.fromStreamD <$> parseD p (D.toStreamD s)
641-------------------------------------------------------------------------------
642-- Elimination - Running Array Folds and parsers
643-------------------------------------------------------------------------------
645{-# INLINE_NORMAL parseArrD #-}
647 forall m a b. (MonadIO m, MonadThrow m, Storable a)
648 => PRD.Parser m (Array a) b
649 -> D.Stream m (Array.Array a)
650 -> m (b, D.Stream m (Array.Array a))
651parseArrD (PRD.Parser pstep initial extract) stream@(D.Stream step state) = do
654 PRD.IPartial s -> go SPEC state (List []) s
655 PRD.IDone b -> return (b, stream)
656 PRD.IError err -> throwM $ ParseError err
660 -- "backBuf" contains last few items in the stream that we may have to
663 -- XXX currently we are using a dumb list based approach for backtracking
664 -- buffer. This can be replaced by a sliding/ring buffer using Data.Array.
665 -- That will allow us more efficient random back and forth movement.
667 go !_ st backBuf !pst = do
668 r <- step defState st
670 D.Yield x s -> gobuf SPEC [x] s backBuf pst
671 D.Skip s -> go SPEC s backBuf pst
676 gobuf !_ [] s backBuf !pst = go SPEC s backBuf pst
677 gobuf !_ (x:xs) s backBuf !pst = do
681 gobuf SPEC xs s (List []) pst1
682 PR.Partial n pst1 -> do
684 (n <= sum (map Array.length (x:getList backBuf)))
686 let src0 = takeArrayListRev n (x:getList backBuf)
687 src = Prelude.reverse src0 ++ xs
688 gobuf SPEC src s (List []) pst1
689 PR.Continue 0 pst1 ->
690 gobuf SPEC xs s (List (x:getList backBuf)) pst1
691 PR.Continue n pst1 -> do
693 (n <= sum (map Array.length (x:getList backBuf)))
695 let (src0, buf1) = splitAtArrayListRev n (x:getList backBuf)
696 src = Prelude.reverse src0 ++ xs
697 gobuf SPEC src s (List buf1) pst1
699 return (b, D.Stream step s)
702 (n <= sum (map Array.length (x:getList backBuf)))
704 let src0 = takeArrayListRev n (x:getList backBuf)
705 src = Prelude.reverse src0 ++ xs
706 return (b, D.append (D.fromList src) (D.Stream step s))
707 PR.Error err -> throwM $ ParseError err
710-- | Parse an array stream using the supplied 'Parser'. Returns the parse
711-- result and the unconsumed stream. Throws 'ParseError' if the parse fails.
715{-# INLINE parseArr #-}
717 (MonadIO m, MonadThrow m, Storable a)
719 -> SerialT m (A.Array a)
720 -> m (b, SerialT m (A.Array a))
721parseArr p s = fmap D.fromStreamD <$> parseD p (D.toStreamD s)
724-- | Fold an array stream using the supplied array stream 'Fold'.
728{-# INLINE foldArr #-}
729foldArr :: (MonadIO m, MonadThrow m, Storable a) =>
730 ASF.Fold m a b -> SerialT m (A.Array a) -> m b
731foldArr (ASF.Fold p) s = fst <$> parseArrD p (D.toStreamD s)
733-- | Like 'fold' but also returns the remaining stream.
737{-# INLINE foldArr_ #-}
738foldArr_ :: (MonadIO m, MonadThrow m, Storable a) =>
739 ASF.Fold m a b -> SerialT m (A.Array a) -> m (b, SerialT m (A.Array a))
740foldArr_ (ASF.Fold p) s = second D.fromStreamD <$> parseArrD p (D.toStreamD s)
742{-# ANN type ParseChunksState Fuse #-}
743data ParseChunksState x inpBuf st pst =
744 ParseChunksInit inpBuf st
745 | ParseChunksInitLeftOver inpBuf
746 | ParseChunksStream st inpBuf !pst
747 | ParseChunksBuf inpBuf st inpBuf !pst
748 | ParseChunksYield x (ParseChunksState x inpBuf st pst)
750{-# INLINE_NORMAL foldArrManyD #-}
752 :: (MonadThrow m, Storable a)
754 -> D.Stream m (Array a)
756foldArrManyD (ASF.Fold (PRD.Parser pstep initial extract)) (D.Stream step state) =
757 D.Stream stepOuter (ParseChunksInit [] state)
761 {-# INLINE_LATE stepOuter #-}
762 -- Buffer is empty, get the first element from the stream, initialize the
763 -- fold and then go to stream processing loop.
764 stepOuter gst (ParseChunksInit [] st) = do
765 r <- step (adaptState gst) st
771 return $ D.Skip $ ParseChunksBuf [x] s [] ps
773 let next = ParseChunksInit [x] s
774 in return $ D.Skip $ ParseChunksYield pb next
775 PRD.IError err -> throwM $ ParseError err
776 D.Skip s -> return $ D.Skip $ ParseChunksInit [] s
777 D.Stop -> return D.Stop
779 -- Buffer is not empty, go to buffered processing loop
780 stepOuter _ (ParseChunksInit src st) = do
784 return $ D.Skip $ ParseChunksBuf src st [] ps
786 let next = ParseChunksInit src st
787 in return $ D.Skip $ ParseChunksYield pb next
788 PRD.IError err -> throwM $ ParseError err
790 -- XXX we just discard any leftover input at the end
791 stepOuter _ (ParseChunksInitLeftOver _) = return D.Stop
793 -- Buffer is empty, process elements from the stream
794 stepOuter gst (ParseChunksStream st backBuf pst) = do
795 r <- step (adaptState gst) st
801 return $ D.Skip $ ParseChunksStream s [] pst1
802 PR.Partial n pst1 -> do
804 (n <= sum (map Array.length (x:backBuf)))
806 let src0 = takeArrayListRev n (x:backBuf)
807 src = Prelude.reverse src0
808 return $ D.Skip $ ParseChunksBuf src s [] pst1
809 PR.Continue 0 pst1 ->
810 return $ D.Skip $ ParseChunksStream s (x:backBuf) pst1
811 PR.Continue n pst1 -> do
813 (n <= sum (map Array.length (x:backBuf)))
815 let (src0, buf1) = splitAtArrayListRev n (x:backBuf)
816 src = Prelude.reverse src0
817 return $ D.Skip $ ParseChunksBuf src s buf1 pst1
820 ParseChunksYield b (ParseChunksInit [] s)
823 (n <= sum (map Array.length (x:backBuf)))
825 let src0 = takeArrayListRev n (x:backBuf)
826 let src = Prelude.reverse src0
828 ParseChunksYield b (ParseChunksInit src s)
829 PR.Error err -> throwM $ ParseError err
830 D.Skip s -> return $ D.Skip $ ParseChunksStream s backBuf pst
833 let src = Prelude.reverse backBuf
835 ParseChunksYield b (ParseChunksInitLeftOver src)
837 -- go back to stream processing mode
838 stepOuter _ (ParseChunksBuf [] s buf pst) =
839 return $ D.Skip $ ParseChunksStream s buf pst
841 -- buffered processing loop
842 stepOuter _ (ParseChunksBuf (x:xs) s backBuf pst) = do
846 return $ D.Skip $ ParseChunksBuf xs s [] pst1
847 PR.Partial n pst1 -> do
848 assert (n <= sum (map Array.length (x:backBuf))) (return ())
849 let src0 = takeArrayListRev n (x:backBuf)
850 src = Prelude.reverse src0 ++ xs
851 return $ D.Skip $ ParseChunksBuf src s [] pst1
852 PR.Continue 0 pst1 ->
853 return $ D.Skip $ ParseChunksBuf xs s (x:backBuf) pst1
854 PR.Continue n pst1 -> do
855 assert (n <= sum (map Array.length (x:backBuf))) (return ())
856 let (src0, buf1) = splitAtArrayListRev n (x:backBuf)
857 src = Prelude.reverse src0 ++ xs
858 return $ D.Skip $ ParseChunksBuf src s buf1 pst1
860 return $ D.Skip $ ParseChunksYield b (ParseChunksInit xs s)
862 assert (n <= sum (map Array.length (x:backBuf))) (return ())
863 let src0 = takeArrayListRev n (x:backBuf)
864 src = Prelude.reverse src0 ++ xs
865 return $ D.Skip $ ParseChunksYield b (ParseChunksInit src s)
866 PR.Error err -> throwM $ ParseError err
868 stepOuter _ (ParseChunksYield a next) = return $ D.Yield a next
870-- | Apply an array stream 'Fold' repeatedly on an array stream and emit the
871-- fold outputs in the output stream.
873-- See "Streamly.Prelude.foldMany" for more details.
876{-# INLINE foldArrMany #-}
878 :: (IsStream t, MonadThrow m, Storable a)
882foldArrMany p m = D.fromStreamD $ foldArrManyD p (D.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