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 Foreign.ForeignPtr (touchForeignPtr)
62import Foreign.Ptr (minusPtr, plusPtr, castPtr)
63import Streamly.Internal.Data.Unboxed (Storable, peek, sizeOf)
64import Fusion.Plugin.Types (Fuse(..))
65import GHC.Exts (SpecConstrAnnotation(..))
66import GHC.ForeignPtr (ForeignPtr(..))
67import GHC.Ptr (Ptr(..))
68import GHC.Types (SPEC(..))
69import Prelude hiding (null, last, (!!), read, concat, unlines)
71import Streamly.Internal.Data.Array.Foreign.Mut.Type
72 (arrayToFptrContents, fptrToArrayContents, touch)
73import Streamly.Internal.Data.Array.Foreign.Type (Array(..))
74import Streamly.Internal.Data.Array.Stream.Fold.Foreign (ArrayFold(..))
75import Streamly.Internal.Data.Fold.Type (Fold(..))
76import Streamly.Internal.Data.Parser (ParseError(..))
77import Streamly.Internal.Data.Stream.Serial (SerialT(..))
78import Streamly.Internal.Data.Stream.IsStream.Type
79 (IsStream, fromStreamD, toStreamD)
80import Streamly.Internal.Data.SVar (adaptState, defState)
81import Streamly.Internal.Data.Array.Foreign.Mut.Type
82 (memcpy, allocBytesToElemCount)
84import qualified Streamly.Internal.Data.Array.Foreign as A
85import qualified Streamly.Internal.Data.Array.Foreign as Array
86-- import qualified Streamly.Internal.Data.Array.Stream.Fold.Foreign
88import qualified Streamly.Internal.Data.Array.Foreign.Type as A
89import qualified Streamly.Internal.Data.Array.Foreign.Mut.Type as MA
90import qualified Streamly.Internal.Data.Array.Stream.Mut.Foreign as AS
91import qualified Streamly.Internal.Data.Fold.Type as FL
92import qualified Streamly.Internal.Data.Parser as PR
93import qualified Streamly.Internal.Data.Parser.ParserD as PRD
94import qualified Streamly.Internal.Data.Stream.IsStream as S
95import qualified Streamly.Internal.Data.Stream.StreamD as D
96import qualified Streamly.Internal.Data.Stream.StreamK as K
98-- XXX Since these are immutable arrays MonadIO constraint can be removed from
101-------------------------------------------------------------------------------
103-------------------------------------------------------------------------------
105-- | @arraysOf n stream@ groups the elements in the input stream into arrays of
108-- > arraysOf n = Stream.chunksOf n (Array.writeN n)
111{-# INLINE arraysOf #-}
112arraysOf :: (IsStream t, MonadIO m, Storable a)
113 => Int -> t m a -> t m (Array a)
114arraysOf n str = fromStreamD $ A.arraysOf n (toStreamD str)
116-------------------------------------------------------------------------------
118-------------------------------------------------------------------------------
120-- XXX efficiently compare two streams of arrays. Two streams can have chunks
121-- of different sizes, we can handle that in the stream comparison abstraction.
122-- This could be useful e.g. to fast compare whether two files differ.
124-- | Convert a stream of arrays into a stream of their elements.
126-- Same as the following:
128-- > concat = Stream.unfoldMany Array.read
132concat :: (IsStream t, Monad m, Storable a) => t m (Array a) -> t m a
133-- concat m = fromStreamD $ A.flattenArrays (toStreamD m)
134-- concat m = fromStreamD $ D.concatMap A.toStreamD (toStreamD m)
135concat m = fromStreamD $ D.unfoldMany A.read (toStreamD m)
137-- | Convert a stream of arrays into a stream of their elements reversing the
138-- contents of each array before flattening.
140-- > concatRev = Stream.unfoldMany Array.readRev
143{-# INLINE concatRev #-}
144concatRev :: (IsStream t, Monad m, Storable a) => t m (Array a) -> t m a
145-- concatRev m = fromStreamD $ A.flattenArraysRev (toStreamD m)
146concatRev m = fromStreamD $ D.unfoldMany A.readRev (toStreamD m)
148-------------------------------------------------------------------------------
149-- Intersperse and append
150-------------------------------------------------------------------------------
152-- | Flatten a stream of arrays after inserting the given element between
156{-# INLINE interpose #-}
157interpose :: (Monad m, IsStream t, Storable a) => a -> t m (Array a) -> t m a
158interpose x = S.interpose x A.read
160{-# INLINE intercalateSuffix #-}
161intercalateSuffix :: (Monad m, IsStream t, Storable a)
162 => Array a -> t m (Array a) -> t m a
163intercalateSuffix = S.intercalateSuffix A.read
165-- | Flatten a stream of arrays appending the given element after each
169{-# INLINE interposeSuffix #-}
170interposeSuffix :: (Monad m, IsStream t, Storable a)
171 => a -> t m (Array a) -> t m a
172-- interposeSuffix x = fromStreamD . A.unlines x . toStreamD
173interposeSuffix x = S.interposeSuffix x A.read
175data FlattenState s a =
177 | InnerLoop s !MA.ArrayContents !(Ptr a) !(Ptr a)
179-- XXX Remove monadIO constraint
180{-# INLINE_NORMAL unlines #-}
181unlines :: forall m a. (MonadIO m, Storable a)
182 => a -> D.Stream m (Array a) -> D.Stream m a
183unlines sep (D.Stream step state) = D.Stream step' (OuterLoop state)
185 {-# INLINE_LATE step' #-}
186 step' gst (OuterLoop st) = do
187 r <- step (adaptState gst) st
189 D.Yield Array{..} s ->
190 D.Skip (InnerLoop s arrContents arrStart aEnd)
191 D.Skip s -> D.Skip (OuterLoop s)
194 step' _ (InnerLoop st _ p end) | p == end =
195 return $ D.Yield sep $ OuterLoop st
197 step' _ (InnerLoop st contents p end) = do
202 return $ D.Yield x (InnerLoop st contents (PTR_NEXT(p,a)) end)
204-------------------------------------------------------------------------------
206-------------------------------------------------------------------------------
208-- XXX These would not be needed once we implement compactLEFold, see
209-- module Streamly.Internal.Data.Array.Stream.Mut.Foreign
211{-# INLINE_NORMAL packArraysChunksOf #-}
212packArraysChunksOf :: (MonadIO m, Storable a)
213 => Int -> D.Stream m (Array a) -> D.Stream m (Array a)
214packArraysChunksOf n str =
215 D.map A.unsafeFreeze $ AS.packArraysChunksOf n $ D.map A.unsafeThaw str
217-- XXX instead of writing two different versions of this operation, we should
218-- write it as a pipe.
219{-# INLINE_NORMAL lpackArraysChunksOf #-}
220lpackArraysChunksOf :: (MonadIO m, Storable a)
221 => Int -> Fold m (Array a) () -> Fold m (Array a) ()
222lpackArraysChunksOf n fld =
223 FL.lmap A.unsafeThaw $ AS.lpackArraysChunksOf n (FL.lmap A.unsafeFreeze fld)
225-- | Coalesce adjacent arrays in incoming stream to form bigger arrays of a
226-- maximum specified size in bytes.
229{-# INLINE compact #-}
230compact :: (MonadIO m, Storable a)
231 => Int -> SerialT m (Array a) -> SerialT m (Array a)
232compact n xs = fromStreamD $ packArraysChunksOf n (toStreamD xs)
234-------------------------------------------------------------------------------
236-------------------------------------------------------------------------------
242 | Yielding arr (SplitState s arr)
245-- | Split a stream of arrays on a given separator byte, dropping the separator
246-- and coalescing all the arrays between two separators into a single array.
249{-# INLINE_NORMAL _splitOn #-}
253 -> D.Stream m (Array Word8)
254 -> D.Stream m (Array Word8)
255_splitOn byte (D.Stream step state) = D.Stream step' (Initial state)
259 {-# INLINE_LATE step' #-}
260 step' gst (Initial st) = do
264 (arr1, marr2) <- A.breakOn byte arr
265 return $ case marr2 of
266 Nothing -> D.Skip (Buffering s arr1)
267 Just arr2 -> D.Skip (Yielding arr1 (Splitting s arr2))
268 D.Skip s -> return $ D.Skip (Initial s)
269 D.Stop -> return D.Stop
271 step' gst (Buffering st buf) = do
275 (arr1, marr2) <- A.breakOn byte arr
276 buf' <- A.splice buf arr1
277 return $ case marr2 of
278 Nothing -> D.Skip (Buffering s buf')
279 Just x -> D.Skip (Yielding buf' (Splitting s x))
280 D.Skip s -> return $ D.Skip (Buffering s buf)
282 if A.byteLength buf == 0
284 else D.Skip (Yielding buf Finishing)
286 step' _ (Splitting st buf) = do
287 (arr1, marr2) <- A.breakOn byte buf
288 return $ case marr2 of
289 Nothing -> D.Skip $ Buffering st arr1
290 Just arr2 -> D.Skip $ Yielding arr1 (Splitting st arr2)
292 step' _ (Yielding arr next) = return $ D.Yield arr next
293 step' _ Finishing = return D.Stop
295-- XXX Remove MonadIO constraint.
296-- | Split a stream of arrays on a given separator byte, dropping the separator
297-- and coalescing all the arrays between two separators into a single array.
300{-# INLINE splitOn #-}
302 :: (IsStream t, MonadIO m)
307 fromStreamD $ D.splitInnerBy (A.breakOn byte) A.splice $ toStreamD s
309{-# INLINE splitOnSuffix #-}
311 :: (IsStream t, MonadIO m)
315-- splitOn byte s = fromStreamD $ A.splitOn byte $ toStreamD s
316splitOnSuffix byte s =
317 fromStreamD $ D.splitInnerBySuffix (A.breakOn byte) A.splice $ toStreamD s
319-------------------------------------------------------------------------------
320-- Elimination - Running folds
321-------------------------------------------------------------------------------
323-- XXX This should be written using CPS (as foldK) if we want it to scale wrt
324-- to the number of times it can be called on the same stream.
326{-# INLINE_NORMAL foldBreakD #-}
327foldBreakD :: forall m a b. (MonadIO m, Storable a) =>
328 Fold m a b -> D.Stream m (Array a) -> m (b, D.Stream m (Array a))
329foldBreakD (Fold fstep initial extract) stream@(D.Stream step state) = do
332 FL.Partial fs -> go SPEC state fs
333 FL.Done fb -> return $! (fb, stream)
339 r <- step defState st
341 D.Yield (Array contents start (Ptr end)) s ->
342 let fp = ForeignPtr end (arrayToFptrContents contents)
343 in goArray SPEC s fp start fs
344 D.Skip s -> go SPEC s fs
349 goArray !_ s fp@(ForeignPtr end _) !cur !fs
350 | cur == Ptr end = do
351 liftIO $ touchForeignPtr fp
353 goArray !_ st fp@(ForeignPtr end contents) !cur !fs = do
354 x <- liftIO $ peek cur
356 let next = PTR_NEXT(cur,a)
359 let arr = Array (fptrToArrayContents contents) next (Ptr end)
360 return $! (b, D.cons arr (D.Stream step st))
361 FL.Partial fs1 -> goArray SPEC st fp next fs1
363{-# INLINE_NORMAL foldBreakK #-}
364foldBreakK :: forall m a b. (MonadIO m, Storable a) =>
365 Fold m a b -> K.Stream m (Array a) -> m (b, K.Stream m (Array a))
366foldBreakK (Fold fstep initial extract) stream = do
369 FL.Partial fs -> go fs stream
370 FL.Done fb -> return (fb, stream)
376 let stop = (, K.nil) <$> extract fs
377 single a = yieldk a K.nil
378 yieldk (Array contents start (Ptr end)) r =
379 let fp = ForeignPtr end (arrayToFptrContents contents)
380 in goArray fs r fp start
381 in K.foldStream defState yieldk single stop st
383 goArray !fs st fp@(ForeignPtr end _) !cur
384 | cur == Ptr end = do
385 liftIO $ touchForeignPtr fp
387 goArray !fs st fp@(ForeignPtr end contents) !cur = do
388 x <- liftIO $ peek cur
390 let next = PTR_NEXT(cur,a)
393 let arr = Array (fptrToArrayContents contents) next (Ptr end)
394 return $! (b, K.cons arr st)
395 FL.Partial fs1 -> goArray fs1 st fp next
397-- | Fold an array stream using the supplied 'Fold'. Returns the fold result
398-- and the unconsumed stream.
400-- > foldBreak f = runArrayFoldBreak (ArrayFold.fromFold f)
404{-# INLINE_NORMAL foldBreak #-}
406 (MonadIO m, Storable a)
408 -> SerialT m (A.Array a)
409 -> m (b, SerialT m (A.Array a))
410-- foldBreak f s = fmap fromStreamD <$> foldBreakD f (toStreamD s)
411foldBreak f (SerialT s) = fmap (fmap SerialT) $ foldBreakK f s
412-- If foldBreak performs better than runArrayFoldBreak we can use a rewrite
413-- rule to rewrite runArrayFoldBreak to fold.
414-- foldBreak f = runArrayFoldBreak (ArrayFold.fromFold f)
416-------------------------------------------------------------------------------
417-- Fold to a single Array
418-------------------------------------------------------------------------------
420-- When we have to take an array partially, take the last part of the array.
421{-# INLINE takeArrayListRev #-}
422takeArrayListRev :: forall a. Storable a => Int -> [Array a] -> [Array a]
430 let len = Array.length x
432 then x : go (n - len) xs
435 else let !(Array contents _ end) = x
436 !start = end `plusPtr` negate (n * SIZE_OF(a))
437 in [Array contents start end]
439-- When we have to take an array partially, take the last part of the array in
441{-# INLINE splitAtArrayListRev #-}
442splitAtArrayListRev :: forall a. Storable a =>
443 Int -> [Array a] -> ([Array a],[Array a])
444splitAtArrayListRev n ls
446 | otherwise = go n ls
448 go :: Int -> [Array a] -> ([Array a], [Array a])
451 let len = Array.length x
452 (xs', xs'') = go (m - len) xs
457 else let !(Array contents start end) = x
458 end1 = end `plusPtr` negate (m * SIZE_OF(a))
459 arr2 = Array contents start end1
460 arr1 = Array contents end1 end
463-------------------------------------------------------------------------------
464-- Fold to a single Array
465-------------------------------------------------------------------------------
467-- XXX Both of these implementations of splicing seem to perform equally well.
468-- We need to perform benchmarks over a range of sizes though.
470-- CAUTION! length must more than equal to lengths of all the arrays in the
472{-# INLINE spliceArraysLenUnsafe #-}
473spliceArraysLenUnsafe :: (MonadIO m, Storable a)
474 => Int -> SerialT m (MA.Array a) -> m (MA.Array a)
475spliceArraysLenUnsafe len buffered = do
476 arr <- liftIO $ MA.newArray len
477 end <- S.foldlM' writeArr (return $ MA.aEnd arr) buffered
478 return $ arr {MA.aEnd = end}
482 writeArr dst (MA.Array ac src ae _) =
484 let count = ae `minusPtr` src
485 memcpy (castPtr dst) (castPtr src) count
487 return $ dst `plusPtr` count
489{-# INLINE _spliceArrays #-}
490_spliceArrays :: (MonadIO m, Storable a)
491 => SerialT m (Array a) -> m (Array a)
493 buffered <- S.foldr S.cons S.nil s
494 len <- S.sum (S.map Array.length buffered)
495 arr <- liftIO $ MA.newArray len
496 end <- S.foldlM' writeArr (return $ MA.aEnd arr) s
497 return $ A.unsafeFreeze $ arr {MA.aEnd = end}
501 writeArr dst (Array ac src ae) =
503 let count = ae `minusPtr` src
504 memcpy (castPtr dst) (castPtr src) count
506 return $ dst `plusPtr` count
508{-# INLINE _spliceArraysBuffered #-}
509_spliceArraysBuffered :: (MonadIO m, Storable a)
510 => SerialT m (Array a) -> m (Array a)
511_spliceArraysBuffered s = do
512 buffered <- S.foldr S.cons S.nil s
513 len <- S.sum (S.map Array.length buffered)
514 A.unsafeFreeze <$> spliceArraysLenUnsafe len (S.map A.unsafeThaw s)
516{-# INLINE spliceArraysRealloced #-}
517spliceArraysRealloced :: forall m a. (MonadIO m, Storable a)
518 => SerialT m (Array a) -> m (Array a)
519spliceArraysRealloced s = do
520 let n = allocBytesToElemCount (undefined :: a) (4 * 1024)
521 idst = liftIO $ MA.newArray n
523 arr <- S.foldlM' MA.spliceExp idst (S.map A.unsafeThaw s)
524 liftIO $ A.unsafeFreeze <$> MA.rightSize arr
526-- XXX This should just be "fold A.write"
528-- | Given a stream of arrays, splice them all together to generate a single
529-- array. The stream must be /finite/.
532{-# INLINE toArray #-}
533toArray :: (MonadIO m, Storable a) => SerialT m (Array a) -> m (Array a)
534toArray = spliceArraysRealloced
535-- spliceArrays = _spliceArraysBuffered
537-- exponentially increasing sizes of the chunks upto the max limit.
538-- XXX this will be easier to implement with parsers/terminating folds
539-- With this we should be able to reduce the number of chunks/allocations.
540-- The reallocation/copy based toArray can also be implemented using this.
543{-# INLINE toArraysInRange #-}
544toArraysInRange :: (IsStream t, MonadIO m, Storable a)
545 => Int -> Int -> Fold m (Array a) b -> Fold m a b
546toArraysInRange low high (Fold step initial extract) =
550-- | Fold the input to a pure buffered stream (List) of arrays.
551{-# INLINE _toArraysOf #-}
552_toArraysOf :: (MonadIO m, Storable a)
553 => Int -> Fold m a (SerialT Identity (Array a))
554_toArraysOf n = FL.chunksOf n (A.writeNF n) FL.toStream
557-------------------------------------------------------------------------------
558-- Elimination - running element parsers
559-------------------------------------------------------------------------------
561-- GHC parser does not accept {-# ANN type [] NoSpecConstr #-}, so we need
563{-# ANN type List NoSpecConstr #-}
564newtype List a = List {getList :: [a]}
566-- This can be generalized to any type provided it can be unfolded to a stream
567-- and it can be combined using a semigroup operation.
569-- XXX This should be written using CPS (as parseK) if we want it to scale wrt
570-- to the number of times it can be called on the same stream.
571{-# INLINE_NORMAL parseBreakD #-}
573 forall m a b. (MonadIO m, MonadThrow m, Storable a)
575 -> D.Stream m (Array.Array a)
576 -> m (b, D.Stream m (Array.Array a))
578 (PRD.Parser pstep initial extract) stream@(D.Stream step state) = do
582 PRD.IPartial s -> go SPEC state (List []) s
583 PRD.IDone b -> return (b, stream)
584 PRD.IError err -> throwM $ ParseError err
588 -- "backBuf" contains last few items in the stream that we may have to
591 -- XXX currently we are using a dumb list based approach for backtracking
592 -- buffer. This can be replaced by a sliding/ring buffer using Data.Array.
593 -- That will allow us more efficient random back and forth movement.
594 go !_ st backBuf !pst = do
595 r <- step defState st
597 D.Yield (Array contents start (Ptr end)) s ->
599 (ForeignPtr end (arrayToFptrContents contents)) start pst
600 D.Skip s -> go SPEC s backBuf pst
605 -- Use strictness on "cur" to keep it unboxed
606 gobuf !_ s backBuf fp@(ForeignPtr end _) !cur !pst
607 | cur == Ptr end = do
608 liftIO $ touchForeignPtr fp
609 go SPEC s backBuf pst
610 gobuf !_ s backBuf fp@(ForeignPtr end contents) !cur !pst = do
611 x <- liftIO $ peek cur
613 let next = PTR_NEXT(cur,a)
616 gobuf SPEC s (List []) fp next pst1
617 PR.Partial n pst1 -> do
618 assert (n <= Prelude.length (x:getList backBuf)) (return ())
619 let src0 = Prelude.take n (x:getList backBuf)
620 arr0 = A.fromListN n (Prelude.reverse src0)
621 arr1 = Array (fptrToArrayContents contents) next (Ptr end)
623 let !(Array cont1 start (Ptr end1)) = src
624 fp1 = ForeignPtr end1 (arrayToFptrContents cont1)
625 gobuf SPEC s (List []) fp1 start pst1
626 PR.Continue 0 pst1 ->
627 gobuf SPEC s (List (x:getList backBuf)) fp next pst1
628 PR.Continue n pst1 -> do
629 assert (n <= Prelude.length (x:getList backBuf)) (return ())
630 let (src0, buf1) = splitAt n (x:getList backBuf)
631 arr0 = A.fromListN n (Prelude.reverse src0)
632 arr1 = Array (fptrToArrayContents contents) next (Ptr end)
634 let !(Array cont1 start (Ptr end1)) = src
635 fp1 = ForeignPtr end1 (arrayToFptrContents cont1)
636 gobuf SPEC s (List buf1) fp1 start pst1
638 let arr = Array (fptrToArrayContents contents) next (Ptr end)
639 return (b, D.cons arr (D.Stream step s))
641 assert (n <= Prelude.length (x:getList backBuf)) (return ())
642 let src0 = Prelude.take n (x:getList backBuf)
643 -- XXX create the array in reverse instead
644 arr0 = A.fromListN n (Prelude.reverse src0)
645 arr1 = Array (fptrToArrayContents contents) next (Ptr end)
646 -- XXX Use StreamK to avoid adding arbitrary layers of
647 -- constructors every time.
648 str = D.cons arr0 (D.cons arr1 (D.Stream step s))
650 PR.Error err -> throwM $ ParseError err
652{-# INLINE_NORMAL parseBreakK #-}
654 forall m a b. (MonadIO m, MonadThrow m, Storable a)
656 -> K.Stream m (Array.Array a)
657 -> m (b, K.Stream m (Array.Array a))
658parseBreakK (PRD.Parser pstep initial extract) stream = do
661 PRD.IPartial s -> go s stream []
662 PRD.IDone b -> return (b, stream)
663 PRD.IError err -> throwM $ ParseError err
667 -- "backBuf" contains last few items in the stream that we may have to
670 -- XXX currently we are using a dumb list based approach for backtracking
671 -- buffer. This can be replaced by a sliding/ring buffer using Data.Array.
672 -- That will allow us more efficient random back and forth movement.
673 go !pst st backBuf = do
674 let stop = (, K.nil) <$> extract pst
675 single a = yieldk a K.nil
676 yieldk (Array contents start (Ptr end)) r =
677 let fp = ForeignPtr end (arrayToFptrContents contents)
678 in goArray pst backBuf r fp start
679 in K.foldStream defState yieldk single stop st
681 -- Use strictness on "cur" to keep it unboxed
682 goArray !pst backBuf st fp@(ForeignPtr end _) !cur
683 | cur == Ptr end = do
684 liftIO $ touchForeignPtr fp
686 goArray !pst backBuf st fp@(ForeignPtr end contents) !cur = do
687 x <- liftIO $ peek cur
689 let next = PTR_NEXT(cur,a)
692 goArray s [] st fp next
694 assert (n <= Prelude.length (x:backBuf)) (return ())
695 let src0 = Prelude.take n (x:backBuf)
696 arr0 = A.fromListN n (Prelude.reverse src0)
697 arr1 = Array (fptrToArrayContents contents) next (Ptr end)
699 let !(Array cont1 start (Ptr end1)) = src
700 fp1 = ForeignPtr end1 (arrayToFptrContents cont1)
701 goArray s [] st fp1 start
703 goArray s (x:backBuf) st fp next
704 PR.Continue n s -> do
705 assert (n <= Prelude.length (x:backBuf)) (return ())
706 let (src0, buf1) = splitAt n (x:backBuf)
707 arr0 = A.fromListN n (Prelude.reverse src0)
708 arr1 = Array (fptrToArrayContents contents) next (Ptr end)
710 let !(Array cont1 start (Ptr end1)) = src
711 fp1 = ForeignPtr end1 (arrayToFptrContents cont1)
712 goArray s buf1 st fp1 start
714 let arr = Array (fptrToArrayContents contents) next (Ptr end)
715 return (b, K.cons arr st)
717 assert (n <= Prelude.length (x:backBuf)) (return ())
718 let src0 = Prelude.take n (x:backBuf)
719 -- XXX Use fromListRevN once implemented
720 -- arr0 = A.fromListRevN n src0
721 arr0 = A.fromListN n (Prelude.reverse src0)
722 arr1 = Array (fptrToArrayContents contents) next (Ptr end)
723 str = K.cons arr0 (K.cons arr1 st)
725 PR.Error err -> throwM $ ParseError err
727-- | Parse an array stream using the supplied 'Parser'. Returns the parse
728-- result and the unconsumed stream. Throws 'ParseError' if the parse fails.
732{-# INLINE_NORMAL parseBreak #-}
734 (MonadIO m, MonadThrow m, Storable a)
736 -> SerialT m (A.Array a)
737 -> m (b, SerialT m (A.Array a))
740 fmap fromStreamD <$> parseBreakD (PRD.fromParserK p) (toStreamD s)
742parseBreak p (SerialT s) =
743 fmap (fmap SerialT) $ parseBreakK (PRD.fromParserK p) s
745-------------------------------------------------------------------------------
746-- Elimination - Running Array Folds and parsers
747-------------------------------------------------------------------------------
749-- | Note that this is not the same as using a @Parser m (Array a) b@ with the
750-- regular "Streamly.Internal.Data.IsStream.parse" function. The regular parse
751-- would consume the input arrays as single unit. This parser parses in the way
752-- as described in the ArrayFold module. The input arrays are treated as @n@
753-- element units and can be consumed partially. The remaining elements are
754-- inserted in the source stream as an array.
756{-# INLINE_NORMAL runArrayParserDBreak #-}
757runArrayParserDBreak ::
758 forall m a b. (MonadIO m, MonadThrow m, Storable a)
759 => PRD.Parser m (Array a) b
760 -> D.Stream m (Array.Array a)
761 -> m (b, D.Stream m (Array.Array a))
763 (PRD.Parser pstep initial extract)
764 stream@(D.Stream step state) = do
768 PRD.IPartial s -> go SPEC state (List []) s
769 PRD.IDone b -> return (b, stream)
770 PRD.IError err -> throwM $ ParseError err
774 -- "backBuf" contains last few items in the stream that we may have to
777 -- XXX currently we are using a dumb list based approach for backtracking
778 -- buffer. This can be replaced by a sliding/ring buffer using Data.Array.
779 -- That will allow us more efficient random back and forth movement.
780 go !_ st backBuf !pst = do
781 r <- step defState st
783 D.Yield x s -> gobuf SPEC [x] s backBuf pst
784 D.Skip s -> go SPEC s backBuf pst
789 gobuf !_ [] s backBuf !pst = go SPEC s backBuf pst
790 gobuf !_ (x:xs) s backBuf !pst = do
794 gobuf SPEC xs s (List []) pst1
795 PR.Partial n pst1 -> do
797 (n <= sum (map Array.length (x:getList backBuf)))
799 let src0 = takeArrayListRev n (x:getList backBuf)
800 src = Prelude.reverse src0 ++ xs
801 gobuf SPEC src s (List []) pst1
802 PR.Continue 0 pst1 ->
803 gobuf SPEC xs s (List (x:getList backBuf)) pst1
804 PR.Continue n pst1 -> do
806 (n <= sum (map Array.length (x:getList backBuf)))
808 let (src0, buf1) = splitAtArrayListRev n (x:getList backBuf)
809 src = Prelude.reverse src0 ++ xs
810 gobuf SPEC src s (List buf1) pst1
812 return (b, D.Stream step s)
815 (n <= sum (map Array.length (x:getList backBuf)))
817 let src0 = takeArrayListRev n (x:getList backBuf)
818 src = Prelude.reverse src0 ++ xs
819 return (b, D.append (D.fromList src) (D.Stream step s))
820 PR.Error err -> throwM $ ParseError err
823-- | Parse an array stream using the supplied 'Parser'. Returns the parse
824-- result and the unconsumed stream. Throws 'ParseError' if the parse fails.
828{-# INLINE parseArr #-}
830 (MonadIO m, MonadThrow m, Storable a)
832 -> SerialT m (A.Array a)
833 -> m (b, SerialT m (A.Array a))
834parseArr p s = fmap fromStreamD <$> parseBreakD p (toStreamD s)
837-- | Fold an array stream using the supplied array stream 'Fold'.
841{-# INLINE runArrayFold #-}
842runArrayFold :: (MonadIO m, MonadThrow m, Storable a) =>
843 ArrayFold m a b -> SerialT m (A.Array a) -> m b
844runArrayFold (ArrayFold p) s = fst <$> runArrayParserDBreak p (toStreamD s)
846-- | Like 'fold' but also returns the remaining stream.
850{-# INLINE runArrayFoldBreak #-}
851runArrayFoldBreak :: (MonadIO m, MonadThrow m, Storable a) =>
852 ArrayFold m a b -> SerialT m (A.Array a) -> m (b, SerialT m (A.Array a))
853runArrayFoldBreak (ArrayFold p) s =
854 second fromStreamD <$> runArrayParserDBreak p (toStreamD s)
856{-# ANN type ParseChunksState Fuse #-}
857data ParseChunksState x inpBuf st pst =
858 ParseChunksInit inpBuf st
859 | ParseChunksInitLeftOver inpBuf
860 | ParseChunksStream st inpBuf !pst
861 | ParseChunksBuf inpBuf st inpBuf !pst
862 | ParseChunksYield x (ParseChunksState x inpBuf st pst)
864{-# INLINE_NORMAL runArrayFoldManyD #-}
866 :: (MonadThrow m, Storable a)
868 -> D.Stream m (Array a)
871 (ArrayFold (PRD.Parser pstep initial extract)) (D.Stream step state) =
873 D.Stream stepOuter (ParseChunksInit [] state)
877 {-# INLINE_LATE stepOuter #-}
878 -- Buffer is empty, get the first element from the stream, initialize the
879 -- fold and then go to stream processing loop.
880 stepOuter gst (ParseChunksInit [] st) = do
881 r <- step (adaptState gst) st
887 return $ D.Skip $ ParseChunksBuf [x] s [] ps
889 let next = ParseChunksInit [x] s
890 in return $ D.Skip $ ParseChunksYield pb next
891 PRD.IError err -> throwM $ ParseError err
892 D.Skip s -> return $ D.Skip $ ParseChunksInit [] s
893 D.Stop -> return D.Stop
895 -- Buffer is not empty, go to buffered processing loop
896 stepOuter _ (ParseChunksInit src st) = do
900 return $ D.Skip $ ParseChunksBuf src st [] ps
902 let next = ParseChunksInit src st
903 in return $ D.Skip $ ParseChunksYield pb next
904 PRD.IError err -> throwM $ ParseError err
906 -- XXX we just discard any leftover input at the end
907 stepOuter _ (ParseChunksInitLeftOver _) = return D.Stop
909 -- Buffer is empty, process elements from the stream
910 stepOuter gst (ParseChunksStream st backBuf pst) = do
911 r <- step (adaptState gst) st
917 return $ D.Skip $ ParseChunksStream s [] pst1
918 PR.Partial n pst1 -> do
920 (n <= sum (map Array.length (x:backBuf)))
922 let src0 = takeArrayListRev n (x:backBuf)
923 src = Prelude.reverse src0
924 return $ D.Skip $ ParseChunksBuf src s [] pst1
925 PR.Continue 0 pst1 ->
926 return $ D.Skip $ ParseChunksStream s (x:backBuf) pst1
927 PR.Continue n pst1 -> do
929 (n <= sum (map Array.length (x:backBuf)))
931 let (src0, buf1) = splitAtArrayListRev n (x:backBuf)
932 src = Prelude.reverse src0
933 return $ D.Skip $ ParseChunksBuf src s buf1 pst1
936 ParseChunksYield b (ParseChunksInit [] s)
939 (n <= sum (map Array.length (x:backBuf)))
941 let src0 = takeArrayListRev n (x:backBuf)
942 let src = Prelude.reverse src0
944 ParseChunksYield b (ParseChunksInit src s)
945 PR.Error err -> throwM $ ParseError err
946 D.Skip s -> return $ D.Skip $ ParseChunksStream s backBuf pst
949 let src = Prelude.reverse backBuf
951 ParseChunksYield b (ParseChunksInitLeftOver src)
953 -- go back to stream processing mode
954 stepOuter _ (ParseChunksBuf [] s buf pst) =
955 return $ D.Skip $ ParseChunksStream s buf pst
957 -- buffered processing loop
958 stepOuter _ (ParseChunksBuf (x:xs) s backBuf pst) = do
962 return $ D.Skip $ ParseChunksBuf xs s [] pst1
963 PR.Partial n pst1 -> do
964 assert (n <= sum (map Array.length (x:backBuf))) (return ())
965 let src0 = takeArrayListRev n (x:backBuf)
966 src = Prelude.reverse src0 ++ xs
967 return $ D.Skip $ ParseChunksBuf src s [] pst1
968 PR.Continue 0 pst1 ->
969 return $ D.Skip $ ParseChunksBuf xs s (x:backBuf) pst1
970 PR.Continue n pst1 -> do
971 assert (n <= sum (map Array.length (x:backBuf))) (return ())
972 let (src0, buf1) = splitAtArrayListRev n (x:backBuf)
973 src = Prelude.reverse src0 ++ xs
974 return $ D.Skip $ ParseChunksBuf src s buf1 pst1
976 return $ D.Skip $ ParseChunksYield b (ParseChunksInit xs s)
978 assert (n <= sum (map Array.length (x:backBuf))) (return ())
979 let src0 = takeArrayListRev n (x:backBuf)
980 src = Prelude.reverse src0 ++ xs
981 return $ D.Skip $ ParseChunksYield b (ParseChunksInit src s)
982 PR.Error err -> throwM $ ParseError err
984 stepOuter _ (ParseChunksYield a next) = return $ D.Yield a next
986-- | Apply an 'ArrayFold' repeatedly on an array stream and emit the
987-- fold outputs in the output stream.
989-- See "Streamly.Prelude.foldMany" for more details.
992{-# INLINE runArrayFoldMany #-}
994 :: (IsStream t, MonadThrow m, Storable a)
998runArrayFoldMany 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