1{-# OPTIONS_GHC -Wno-deprecations #-}
3-- Module : Streamly.Internal.Data.Array.Stream
4-- Copyright : (c) 2019 Composewell Technologies
5-- License : BSD3-3-Clause
6-- Maintainer : streamly@composewell.com
9-- Combinators to efficiently manipulate streams of immutable arrays.
11-- We can either push these in the MutArray module with a "chunks" prefix or
12-- keep this as a separate module and release it.
14module Streamly.Internal.Data.Array.Stream
18 , Array.pinnedChunksOf
21 -- * Flattening to elements
25 , Array.flattenArraysRev
33 -- The byte level foldBreak can work as efficiently as the chunk level. We
34 -- can flatten the stream to byte stream and use that. But if we want the
35 -- remaining stream to be a chunk stream then this could be handy. But it
36 -- could also be implemented using parseBreak.
39 -- The byte level parseBreak cannot work efficiently. Because the stream
40 -- will have to be a StreamK for backtracking, StreamK at byte level would
42 , parseBreak -- StreamK.parseBreakChunks
50 -- XXX Use parseBreakChunks/parseChunks instead
51 -- foldBreak can be implemented using parseBreak. Use StreamK.
55 , runArrayParserDBreak -- StreamK.parseBreakChunks
61 -- We can use something like foldManyChunks, parseManyChunks with a take
63 , lpackArraysChunksOf -- Fold.compactChunks
64 , compact -- rechunk, compactChunks
67 -- We can use something like foldManyChunks, parseManyChunks with an
68 -- appropriate splitting fold.
69 , splitOn -- Stream.rechunkOn
70 , splitOnSuffix -- Stream.rechunkOnSuffix
74#include "ArrayMacros.h"
77import Data.Bifunctor (second)
78import Control.Exception (assert)
79import Control.Monad.IO.Class (MonadIO(..))
80import Data.Proxy (Proxy(..))
81import Data.Word (Word8)
82import Streamly.Internal.Data.Unbox (Unbox(..))
83import Fusion.Plugin.Types (Fuse(..))
84import GHC.Exts (SpecConstrAnnotation(..))
85import GHC.Types (SPEC(..))
86import Prelude hiding (null, last, (!!), read, concat, unlines)
88import Streamly.Data.Fold (Fold)
89import Streamly.Internal.Data.Array.Type (Array(..))
90import Streamly.Internal.Data.Fold.Chunked (ChunkFold(..))
91import Streamly.Internal.Data.Parser (ParseError(..))
92import Streamly.Internal.Data.Stream (Stream)
93import Streamly.Internal.Data.StreamK (StreamK, fromStream, toStream)
94import Streamly.Internal.Data.SVar.Type (adaptState, defState)
95import Streamly.Internal.Data.Tuple.Strict (Tuple'(..))
97import qualified Streamly.Data.Fold as FL
98import qualified Streamly.Internal.Data.Array as A
99import qualified Streamly.Internal.Data.Array as Array
100import qualified Streamly.Internal.Data.MutArray.Type as MA
101import qualified Streamly.Internal.Data.MutArray.Stream as AS
102import qualified Streamly.Internal.Data.Fold.Type as FL (Fold(..), Step(..))
103import qualified Streamly.Internal.Data.Parser as PR
104import qualified Streamly.Internal.Data.Parser as PRD
105 (Parser(..), Initial(..))
106import qualified Streamly.Internal.Data.Stream as D
107import qualified Streamly.Internal.Data.StreamK as K
109-- XXX Since these are immutable arrays MonadIO constraint can be removed from
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 :: (Monad m, Unbox a) => Stream m (Array a) -> Stream m a
129-- concat m = fromStreamD $ A.flattenArrays (toStreamD m)
130-- concat m = fromStreamD $ D.concatMap A.toStreamD (toStreamD m)
131concat = D.unfoldMany A.reader
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.readerRev
139{-# INLINE concatRev #-}
140concatRev :: (Monad m, Unbox a) => Stream m (Array a) -> Stream m a
141-- concatRev m = fromStreamD $ A.flattenArraysRev (toStreamD m)
142concatRev = D.unfoldMany A.readerRev
144-------------------------------------------------------------------------------
145-- Intersperse and append
146-------------------------------------------------------------------------------
148-- | Flatten a stream of arrays after inserting the given element between
152{-# INLINE interpose #-}
153interpose :: (Monad m, Unbox a) => a -> Stream m (Array a) -> Stream m a
154interpose x = D.interpose x A.reader
156{-# INLINE intercalateSuffix #-}
157intercalateSuffix :: (Monad m, Unbox a)
158 => Array a -> Stream m (Array a) -> Stream m a
159intercalateSuffix = D.intercalateSuffix A.reader
161-- | Flatten a stream of arrays appending the given element after each
165{-# INLINE interposeSuffix #-}
166interposeSuffix :: (Monad m, Unbox a)
167 => a -> Stream m (Array a) -> Stream m a
168-- interposeSuffix x = fromStreamD . A.unlines x . toStreamD
169interposeSuffix x = D.interposeSuffix x A.reader
173 | InnerLoop s !MA.MutableByteArray !Int !Int
175-- XXX This is a special case of interposeSuffix, can be removed.
176-- XXX Remove monadIO constraint
177{-# INLINE_NORMAL unlines #-}
178unlines :: forall m a. (MonadIO m, Unbox a)
179 => a -> D.Stream m (Array a) -> D.Stream m a
180unlines sep (D.Stream step state) = D.Stream step' (OuterLoop state)
182 {-# INLINE_LATE step' #-}
183 step' gst (OuterLoop st) = do
184 r <- step (adaptState gst) st
186 D.Yield Array{..} s ->
187 D.Skip (InnerLoop s arrContents arrStart arrEnd)
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 contents p end) = do
195 x <- liftIO $ peekByteIndex p contents
196 return $ D.Yield x (InnerLoop st contents (INDEX_NEXT(p,a)) end)
198-------------------------------------------------------------------------------
200-------------------------------------------------------------------------------
202-- XXX These would not be needed once we implement compactLEFold, see
203-- module Streamly.Internal.Data.Stream.MutChunked
205-- XXX Note that this thaws immutable arrays for appending, that may be
206-- problematic if multiple users do the same thing, however, immutable arrays
207-- would usually have no capacity to append, therefore, a copy will be forced
208-- anyway. Confirm this. We can forcefully trim the array capacity before thaw
210{-# INLINE_NORMAL packArraysChunksOf #-}
211packArraysChunksOf :: (MonadIO m, Unbox a)
212 => Int -> D.Stream m (Array a) -> D.Stream m (Array a)
213packArraysChunksOf n str =
214 D.map A.unsafeFreeze $ AS.packArraysChunksOf n $ D.map A.unsafeThaw str
216-- XXX instead of writing two different versions of this operation, we should
217-- write it as a pipe.
219-- XXX Confirm that immutable arrays won't be modified.
220{-# INLINE_NORMAL lpackArraysChunksOf #-}
221lpackArraysChunksOf :: (MonadIO m, Unbox a)
222 => Int -> Fold m (Array a) () -> Fold m (Array a) ()
223lpackArraysChunksOf n fld =
224 FL.lmap A.unsafeThaw $ AS.lpackArraysChunksOf n (FL.lmap A.unsafeFreeze fld)
226-- | Coalesce adjacent arrays in incoming stream to form bigger arrays of a
227-- maximum specified size in bytes.
230{-# INLINE compact #-}
231compact :: (MonadIO m, Unbox a)
232 => Int -> Stream m (Array a) -> Stream m (Array a)
233compact = packArraysChunksOf
235-------------------------------------------------------------------------------
237-------------------------------------------------------------------------------
243 | Yielding arr (SplitState s arr)
246-- | Split a stream of arrays on a given separator byte, dropping the separator
247-- and coalescing all the arrays between two separators into a single array.
250{-# INLINE_NORMAL _splitOn #-}
254 -> D.Stream m (Array Word8)
255 -> D.Stream m (Array Word8)
256_splitOn byte (D.Stream step state) = D.Stream step' (Initial state)
260 {-# INLINE_LATE step' #-}
261 step' gst (Initial st) = do
265 (arr1, marr2) <- A.breakOn byte arr
266 return $ case marr2 of
267 Nothing -> D.Skip (Buffering s arr1)
268 Just arr2 -> D.Skip (Yielding arr1 (Splitting s arr2))
269 D.Skip s -> return $ D.Skip (Initial s)
270 D.Stop -> return D.Stop
272 step' gst (Buffering st buf) = do
276 (arr1, marr2) <- A.breakOn byte arr
277 buf' <- A.splice buf arr1
278 return $ case marr2 of
279 Nothing -> D.Skip (Buffering s buf')
280 Just x -> D.Skip (Yielding buf' (Splitting s x))
281 D.Skip s -> return $ D.Skip (Buffering s buf)
283 if A.byteLength buf == 0
285 else D.Skip (Yielding buf Finishing)
287 step' _ (Splitting st buf) = do
288 (arr1, marr2) <- A.breakOn byte buf
289 return $ case marr2 of
290 Nothing -> D.Skip $ Buffering st arr1
291 Just arr2 -> D.Skip $ Yielding arr1 (Splitting st arr2)
293 step' _ (Yielding arr next) = return $ D.Yield arr next
294 step' _ Finishing = return D.Stop
296-- XXX Remove MonadIO constraint.
297-- | Split a stream of arrays on a given separator byte, dropping the separator
298-- and coalescing all the arrays between two separators into a single array.
301{-# INLINE splitOn #-}
305 -> Stream m (Array Word8)
306 -> Stream m (Array Word8)
307splitOn byte = D.splitInnerBy (A.breakOn byte) A.splice
309{-# INLINE splitOnSuffix #-}
313 -> Stream m (Array Word8)
314 -> Stream m (Array Word8)
315-- splitOn byte s = fromStreamD $ A.splitOn byte $ toStreamD s
316splitOnSuffix byte = D.splitInnerBySuffix (A.breakOn byte) A.splice
318-------------------------------------------------------------------------------
319-- Elimination - Running folds
320-------------------------------------------------------------------------------
322{-# INLINE_NORMAL foldBreakD #-}
323foldBreakD :: forall m a b. (MonadIO m, Unbox a) =>
324 Fold m a b -> D.Stream m (Array a) -> m (b, D.Stream m (Array a))
325foldBreakD (FL.Fold fstep initial _ final) stream@(D.Stream step state) = do
328 FL.Partial fs -> go SPEC state fs
329 FL.Done fb -> return $! (fb, stream)
335 r <- step defState st
337 D.Yield (Array contents start end) s ->
338 let fp = Tuple' end contents
339 in goArray SPEC s fp start fs
340 D.Skip s -> go SPEC s fs
345 goArray !_ s (Tuple' end _) !cur !fs
348 goArray !_ st fp@(Tuple' end contents) !cur !fs = do
349 x <- liftIO $ peekByteIndex cur contents
351 let next = INDEX_NEXT(cur,a)
354 let arr = Array contents next end
355 return $! (b, D.cons arr (D.Stream step st))
356 FL.Partial fs1 -> goArray SPEC st fp next fs1
358{-# INLINE_NORMAL foldBreakK #-}
359foldBreakK :: forall m a b. (MonadIO m, Unbox a) =>
360 Fold m a b -> K.StreamK m (Array a) -> m (b, K.StreamK m (Array a))
361foldBreakK (FL.Fold fstep initial _ final) stream = do
364 FL.Partial fs -> go fs stream
365 FL.Done fb -> return (fb, stream)
371 let stop = (, K.nil) <$> final fs
372 single a = yieldk a K.nil
373 yieldk (Array contents start end) r =
374 let fp = Tuple' end contents
375 in goArray fs r fp start
376 in K.foldStream defState yieldk single stop st
378 goArray !fs st (Tuple' end _) !cur
381 goArray !fs st fp@(Tuple' end contents) !cur = do
382 x <- liftIO $ peekByteIndex cur contents
384 let next = INDEX_NEXT(cur,a)
387 let arr = Array contents next end
388 return $! (b, K.cons arr st)
389 FL.Partial fs1 -> goArray fs1 st fp next
391-- | Fold an array stream using the supplied 'Fold'. Returns the fold result
392-- and the unconsumed stream.
394-- > foldBreak f = runArrayFoldBreak (ChunkFold.fromFold f)
398{-# INLINE_NORMAL foldBreak #-}
402 -> StreamK m (A.Array a)
403 -> m (b, StreamK m (A.Array a))
404-- foldBreak f s = fmap fromStreamD <$> foldBreakD f (toStreamD s)
405foldBreak = foldBreakK
406-- If foldBreak performs better than runArrayFoldBreak we can use a rewrite
407-- rule to rewrite runArrayFoldBreak to fold.
408-- foldBreak f = runArrayFoldBreak (ChunkFold.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. Unbox 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. Unbox 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-------------------------------------------------------------------------------
463-- XXX Both of these implementations of splicing seem to perform equally well.
464-- We need to perform benchmarks over a range of sizes though.
466-- CAUTION! length must more than equal to lengths of all the arrays in the
468{-# INLINE spliceArraysLenUnsafe #-}
469spliceArraysLenUnsafe :: (MonadIO m, Unbox a)
470 => Int -> Stream m (MutArray a) -> m (MutArray a)
471spliceArraysLenUnsafe len buffered = do
472 -- XXX The new array's pinned state should depend on the first element
473 -- of the stream. We should uncons the stream and do the required.
475 D.foldlM' MA.spliceUnsafe (return arr) buffered
477{-# INLINE _spliceArrays #-}
478_spliceArrays :: (MonadIO m, Unbox a)
479 => Stream m (Array a) -> m (Array a)
481 buffered <- D.foldr K.cons K.nil s
482 len <- K.fold FL.sum (fmap Array.length buffered)
483 -- XXX The new array's pinned state should depend on the first element
484 -- of the stream. We should uncons the stream and do the required.
486 final <- D.foldlM' writeArr (return arr) (toStream buffered)
487 return $ A.unsafeFreeze final
491 writeArr dst arr = MA.spliceUnsafe dst (A.unsafeThaw arr)
493{-# INLINE _spliceArraysBuffered #-}
494_spliceArraysBuffered :: (MonadIO m, Unbox a)
495 => Stream m (Array a) -> m (Array a)
496_spliceArraysBuffered s = do
497 buffered <- D.foldr K.cons K.nil s
498 len <- K.fold FL.sum (fmap Array.length buffered)
500 spliceArraysLenUnsafe len (fmap A.unsafeThaw (toStream buffered))
504{-# INLINE spliceArraysRealloced #-}
505spliceArraysRealloced :: forall m a. (MonadIO m, Unbox a)
506 => Stream m (Array a) -> m (Array a)
507spliceArraysRealloced s = do
514 (pure (A.unsafeThaw a))
515 (fmap A.unsafeThaw strm)
516 liftIO $ A.unsafeFreeze <$> MA.rightSize arr
517 Nothing -> pure A.nil
519-- XXX This should just be "fold A.write"
521-- | Given a stream of arrays, splice them all together to generate a single
522-- array. The stream must be /finite/.
525{-# INLINE toArray #-}
526toArray :: (MonadIO m, Unbox a) => Stream m (Array a) -> m (Array a)
527toArray = spliceArraysRealloced
528-- spliceArrays = _spliceArraysBuffered
530-- exponentially increasing sizes of the chunks upto the max limit.
531-- XXX this will be easier to implement with parsers/terminating folds
532-- With this we should be able to reduce the number of chunks/allocations.
533-- The reallocation/copy based toArray can also be implemented using this.
536{-# INLINE toArraysInRange #-}
537toArraysInRange :: (MonadIO m, Unbox a)
538 => Int -> Int -> Fold m (Array a) b -> Fold m a b
539toArraysInRange low high (Fold step initial extract) =
543-- | Fold the input to a pure buffered stream (List) of arrays.
544{-# INLINE _toArraysOf #-}
545_toArraysOf :: (MonadIO m, Unbox a)
546 => Int -> Fold m a (Stream Identity (Array a))
547_toArraysOf n = FL.groupsOf n (A.writeNF n) FL.toStream
550-------------------------------------------------------------------------------
551-- Elimination - running element parsers
552-------------------------------------------------------------------------------
554-- GHC parser does not accept {-# ANN type [] NoSpecConstr #-}, so we need
556{-# ANN type List NoSpecConstr #-}
557newtype List a = List {getList :: [a]}
560-- This can be generalized to any type provided it can be unfolded to a stream
561-- and it can be combined using a semigroup operation.
563-- XXX This should be written using CPS (as parseK) if we want it to scale wrt
564-- to the number of times it can be called on the same stream.
565{-# INLINE_NORMAL parseBreakD #-}
567 forall m a b. (MonadIO m, MonadThrow m, Unbox a)
569 -> D.Stream m (Array.Array a)
570 -> m (b, D.Stream m (Array.Array a))
572 (PRD.Parser pstep initial extract) stream@(D.Stream step state) = do
576 PRD.IPartial s -> go SPEC state (List []) s
577 PRD.IDone b -> return (b, stream)
578 PRD.IError err -> throwM $ ParseError err
582 -- "backBuf" contains last few items in the stream that we may have to
585 -- XXX currently we are using a dumb list based approach for backtracking
586 -- buffer. This can be replaced by a sliding/ring buffer using Data.Array.
587 -- That will allow us more efficient random back and forth movement.
588 go !_ st backBuf !pst = do
589 r <- step defState st
591 D.Yield (Array contents start end) s ->
593 (Tuple' end contents) start pst
594 D.Skip s -> go SPEC s backBuf pst
599 -- Use strictness on "cur" to keep it unboxed
600 gobuf !_ s backBuf (Tuple' end _) !cur !pst
602 go SPEC s backBuf pst
603 gobuf !_ s backBuf fp@(Tuple' end contents) !cur !pst = do
604 x <- liftIO $ peekByteIndex contents cur
606 let next = INDEX_NEXT(cur,a)
609 gobuf SPEC s (List []) fp next pst1
610 PR.Partial n pst1 -> do
611 assert (n <= Prelude.length (x:getList backBuf)) (return ())
612 let src0 = Prelude.take n (x:getList backBuf)
613 arr0 = A.fromListN n (Prelude.reverse src0)
614 arr1 = Array contents next end
616 let !(Array cont1 start end1) = src
617 fp1 = Tuple' end1 cont1
618 gobuf SPEC s (List []) fp1 start pst1
619 PR.Continue 0 pst1 ->
620 gobuf SPEC s (List (x:getList backBuf)) fp next pst1
621 PR.Continue n pst1 -> do
622 assert (n <= Prelude.length (x:getList backBuf)) (return ())
623 let (src0, buf1) = splitAt n (x:getList backBuf)
624 arr0 = A.fromListN n (Prelude.reverse src0)
625 arr1 = Array contents next end
627 let !(Array cont1 start end1) = src
628 fp1 = Tuple' end1 cont1
629 gobuf SPEC s (List buf1) fp1 start pst1
631 let arr = Array contents next end
632 return (b, D.cons arr (D.Stream step s))
634 assert (n <= Prelude.length (x:getList backBuf)) (return ())
635 let src0 = Prelude.take n (x:getList backBuf)
636 -- XXX create the array in reverse instead
637 arr0 = A.fromListN n (Prelude.reverse src0)
638 arr1 = Array contents next end
639 -- XXX Use StreamK to avoid adding arbitrary layers of
640 -- constructors every time.
641 str = D.cons arr0 (D.cons arr1 (D.Stream step s))
643 PR.Error err -> throwM $ ParseError err
646{-# INLINE_NORMAL parseBreakK #-}
648 forall m a b. (MonadIO m, Unbox a)
650 -> K.StreamK m (Array.Array a)
651 -> m (Either ParseError b, K.StreamK m (Array.Array a))
652parseBreakK (PRD.Parser pstep initial extract) stream = do
655 PRD.IPartial s -> go s stream []
656 PRD.IDone b -> return (Right b, stream)
657 PRD.IError err -> return (Left (ParseError err), stream)
661 -- "backBuf" contains last few items in the stream that we may have to
664 -- XXX currently we are using a dumb list based approach for backtracking
665 -- buffer. This can be replaced by a sliding/ring buffer using Data.Array.
666 -- That will allow us more efficient random back and forth movement.
667 go !pst st backBuf = do
668 let stop = goStop pst backBuf -- (, K.nil) <$> extract pst
669 single a = yieldk a K.nil
670 yieldk arr r = goArray pst backBuf r arr
671 in K.foldStream defState yieldk single stop st
673 -- Use strictness on "cur" to keep it unboxed
674 goArray !pst backBuf st (Array _ cur end) | cur == end = go pst st backBuf
675 goArray !pst backBuf st (Array contents cur end) = do
676 x <- liftIO $ peekByteIndex cur contents
678 let next = INDEX_NEXT(cur,a)
681 goArray s [] st (Array contents next end)
683 assert (n <= Prelude.length (x:backBuf)) (return ())
684 let src0 = Prelude.take n (x:backBuf)
685 arr0 = A.fromListN n (Prelude.reverse src0)
686 arr1 = Array contents next end
690 goArray s (x:backBuf) st (Array contents next end)
691 PR.Continue n s -> do
692 assert (n <= Prelude.length (x:backBuf)) (return ())
693 let (src0, buf1) = splitAt n (x:backBuf)
694 arr0 = A.fromListN n (Prelude.reverse src0)
695 arr1 = Array contents next end
697 goArray s buf1 st src
699 let arr = Array contents next end
700 return (Right b, K.cons arr st)
702 assert (n <= Prelude.length (x:backBuf)) (return ())
703 let src0 = Prelude.take n (x:backBuf)
704 -- XXX Use fromListRevN once implemented
705 -- arr0 = A.fromListRevN n src0
706 arr0 = A.fromListN n (Prelude.reverse src0)
707 arr1 = Array contents next end
708 str = K.cons arr0 (K.cons arr1 st)
709 return (Right b, str)
711 let n = Prelude.length backBuf
712 arr0 = A.fromListN n (Prelude.reverse backBuf)
713 arr1 = Array contents cur end
714 str = K.cons arr0 (K.cons arr1 stream)
715 return (Left (ParseError err), str)
717 -- This is a simplified goArray
718 goExtract !pst backBuf (Array _ cur end)
719 | cur == end = goStop pst backBuf
720 goExtract !pst backBuf (Array contents cur end) = do
721 x <- liftIO $ peekByteIndex cur contents
723 let next = INDEX_NEXT(cur,a)
726 goExtract s [] (Array contents next end)
728 assert (n <= Prelude.length (x:backBuf)) (return ())
729 let src0 = Prelude.take n (x:backBuf)
730 arr0 = A.fromListN n (Prelude.reverse src0)
731 arr1 = Array contents next end
735 goExtract s backBuf (Array contents next end)
736 PR.Continue n s -> do
737 assert (n <= Prelude.length (x:backBuf)) (return ())
738 let (src0, buf1) = splitAt n (x:backBuf)
739 arr0 = A.fromListN n (Prelude.reverse src0)
740 arr1 = Array contents next end
744 let arr = Array contents next end
745 return (Right b, K.fromPure arr)
747 assert (n <= Prelude.length backBuf) (return ())
748 let src0 = Prelude.take n backBuf
749 -- XXX Use fromListRevN once implemented
750 -- arr0 = A.fromListRevN n src0
751 arr0 = A.fromListN n (Prelude.reverse src0)
752 arr1 = Array contents next end
753 str = K.cons arr0 (K.fromPure arr1)
754 return (Right b, str)
756 let n = Prelude.length backBuf
757 arr0 = A.fromListN n (Prelude.reverse backBuf)
758 arr1 = Array contents cur end
759 str = K.cons arr0 (K.cons arr1 stream)
760 return (Left (ParseError err), str)
762 -- This is a simplified goExtract
763 {-# INLINE goStop #-}
764 goStop !pst backBuf = do
767 PR.Partial _ _ -> error "Bug: parseBreak: Partial in extract"
770 PR.Continue n s -> do
771 assert (n <= Prelude.length backBuf) (return ())
772 let (src0, buf1) = splitAt n backBuf
773 arr = A.fromListN n (Prelude.reverse src0)
776 return (Right b, K.nil)
778 assert (n <= Prelude.length backBuf) (return ())
779 let src0 = Prelude.take n backBuf
780 -- XXX Use fromListRevN once implemented
781 -- arr0 = A.fromListRevN n src0
782 arr0 = A.fromListN n (Prelude.reverse src0)
783 return (Right b, K.fromPure arr0)
785 let n = Prelude.length backBuf
786 arr0 = A.fromListN n (Prelude.reverse backBuf)
787 return (Left (ParseError err), K.fromPure arr0)
789-- | Parse an array stream using the supplied 'Parser'. Returns the parse
790-- result and the unconsumed stream. Throws 'ParseError' if the parse fails.
794{-# INLINE_NORMAL parseBreak #-}
798 -> StreamK m (A.Array a)
799 -> m (Either ParseError b, StreamK m (A.Array a))
802 fmap fromStreamD <$> parseBreakD (PRD.fromParserK p) (toStreamD s)
804parseBreak = parseBreakK
806-------------------------------------------------------------------------------
807-- Elimination - Running Array Folds and parsers
808-------------------------------------------------------------------------------
810-- | Note that this is not the same as using a @Parser (Array a) m b@ with the
811-- regular "Streamly.Internal.Data.IsStream.parse" function. The regular parse
812-- would consume the input arrays as single unit. This parser parses in the way
813-- as described in the ChunkFold module. The input arrays are treated as @n@
814-- element units and can be consumed partially. The remaining elements are
815-- inserted in the source stream as an array.
817{-# INLINE_NORMAL runArrayParserDBreak #-}
818runArrayParserDBreak ::
819 forall m a b. (MonadIO m, Unbox a)
820 => PRD.Parser (Array a) m b
821 -> D.Stream m (Array.Array a)
822 -> m (Either ParseError b, D.Stream m (Array.Array a))
824 (PRD.Parser pstep initial extract)
825 stream@(D.Stream step state) = do
829 PRD.IPartial s -> go SPEC state (List []) s
830 PRD.IDone b -> return (Right b, stream)
831 PRD.IError err -> return (Left (ParseError err), stream)
835 -- "backBuf" contains last few items in the stream that we may have to
838 -- XXX currently we are using a dumb list based approach for backtracking
839 -- buffer. This can be replaced by a sliding/ring buffer using Data.Array.
840 -- That will allow us more efficient random back and forth movement.
841 go _ st backBuf !pst = do
842 r <- step defState st
844 D.Yield x s -> gobuf SPEC [x] s backBuf pst
845 D.Skip s -> go SPEC s backBuf pst
846 D.Stop -> goStop backBuf pst
848 gobuf !_ [] s backBuf !pst = go SPEC s backBuf pst
849 gobuf !_ (x:xs) s backBuf !pst = do
853 gobuf SPEC xs s (List []) pst1
854 PR.Partial n pst1 -> do
856 (n <= sum (map Array.length (x:getList backBuf)))
858 let src0 = takeArrayListRev n (x:getList backBuf)
859 src = Prelude.reverse src0 ++ xs
860 gobuf SPEC src s (List []) pst1
861 PR.Continue 0 pst1 ->
862 gobuf SPEC xs s (List (x:getList backBuf)) pst1
863 PR.Continue n pst1 -> do
865 (n <= sum (map Array.length (x:getList backBuf)))
867 let (src0, buf1) = splitAtArrayListRev n (x:getList backBuf)
868 src = Prelude.reverse src0 ++ xs
869 gobuf SPEC src s (List buf1) pst1
871 let str = D.append (D.fromList xs) (D.Stream step s)
872 return (Right b, str)
875 (n <= sum (map Array.length (x:getList backBuf)))
877 let src0 = takeArrayListRev n (x:getList backBuf)
878 src = Prelude.reverse src0 ++ xs
879 return (Right b, D.append (D.fromList src) (D.Stream step s))
881 let src0 = x:getList backBuf
882 src = Prelude.reverse src0 ++ x:xs
883 strm = D.append (D.fromList src) (D.Stream step s)
884 return (Left (ParseError err), strm)
886 -- This is a simplified gobuf
887 goExtract _ [] backBuf !pst = goStop backBuf pst
888 goExtract _ (x:xs) backBuf !pst = do
892 goExtract SPEC xs (List []) pst1
893 PR.Partial n pst1 -> do
895 (n <= sum (map Array.length (x:getList backBuf)))
897 let src0 = takeArrayListRev n (x:getList backBuf)
898 src = Prelude.reverse src0 ++ xs
899 goExtract SPEC src (List []) pst1
900 PR.Continue 0 pst1 ->
901 goExtract SPEC xs (List (x:getList backBuf)) pst1
902 PR.Continue n pst1 -> do
904 (n <= sum (map Array.length (x:getList backBuf)))
906 let (src0, buf1) = splitAtArrayListRev n (x:getList backBuf)
907 src = Prelude.reverse src0 ++ xs
908 goExtract SPEC src (List buf1) pst1
910 return (Right b, D.fromList xs)
913 (n <= sum (map Array.length (x:getList backBuf)))
915 let src0 = takeArrayListRev n (x:getList backBuf)
916 src = Prelude.reverse src0 ++ xs
917 return (Right b, D.fromList src)
919 let src0 = getList backBuf
920 src = Prelude.reverse src0 ++ x:xs
921 return (Left (ParseError err), D.fromList src)
923 -- This is a simplified goExtract
924 {-# INLINE goStop #-}
925 goStop backBuf pst = do
928 PR.Partial _ _ -> error "Bug: runArrayParserDBreak: Partial in extract"
929 PR.Continue 0 pst1 ->
931 PR.Continue n pst1 -> do
933 (n <= sum (map Array.length (getList backBuf)))
935 let (src0, buf1) = splitAtArrayListRev n (getList backBuf)
936 src = Prelude.reverse src0
937 goExtract SPEC src (List buf1) pst1
938 PR.Done 0 b -> return (Right b, D.nil)
941 (n <= sum (map Array.length (getList backBuf)))
943 let src0 = takeArrayListRev n (getList backBuf)
944 src = Prelude.reverse src0
945 return (Right b, D.fromList src)
947 let src0 = getList backBuf
948 src = Prelude.reverse src0
949 return (Left (ParseError err), D.fromList src)
952-- | Parse an array stream using the supplied 'Parser'. Returns the parse
953-- result and the unconsumed stream. Throws 'ParseError' if the parse fails.
957{-# INLINE parseArr #-}
959 (MonadIO m, MonadThrow m, Unbox a)
961 -> Stream m (A.Array a)
962 -> m (b, Stream m (A.Array a))
963parseArr p s = fmap fromStreamD <$> parseBreakD p (toStreamD s)
966-- | Fold an array stream using the supplied array stream 'Fold'.
970{-# INLINE runArrayFold #-}
971runArrayFold :: (MonadIO m, Unbox a) =>
972 ChunkFold m a b -> StreamK m (A.Array a) -> m (Either ParseError b)
973runArrayFold (ChunkFold p) s = fst <$> runArrayParserDBreak p (toStream s)
975-- | Like 'fold' but also returns the remaining stream.
979{-# INLINE runArrayFoldBreak #-}
980runArrayFoldBreak :: (MonadIO m, Unbox a) =>
981 ChunkFold m a b -> StreamK m (A.Array a) -> m (Either ParseError b, StreamK m (A.Array a))
982runArrayFoldBreak (ChunkFold p) s =
983 second fromStream <$> runArrayParserDBreak p (toStream s)
985{-# ANN type ParseChunksState Fuse #-}
986data ParseChunksState x inpBuf st pst =
987 ParseChunksInit inpBuf st
988 | ParseChunksInitBuf inpBuf
989 | ParseChunksInitLeftOver inpBuf
990 | ParseChunksStream st inpBuf !pst
991 | ParseChunksStop inpBuf !pst
992 | ParseChunksBuf inpBuf st inpBuf !pst
993 | ParseChunksExtract inpBuf inpBuf !pst
994 | ParseChunksYield x (ParseChunksState x inpBuf st pst)
996{-# INLINE_NORMAL runArrayFoldManyD #-}
998 :: (Monad m, Unbox a)
1000 -> D.Stream m (Array a)
1001 -> D.Stream m (Either ParseError b)
1003 (ChunkFold (PRD.Parser pstep initial extract)) (D.Stream step state) =
1005 D.Stream stepOuter (ParseChunksInit [] state)
1009 {-# INLINE_LATE stepOuter #-}
1010 -- Buffer is empty, get the first element from the stream, initialize the
1011 -- fold and then go to stream processing loop.
1012 stepOuter gst (ParseChunksInit [] st) = do
1013 r <- step (adaptState gst) st
1019 return $ D.Skip $ ParseChunksBuf [x] s [] ps
1021 let next = ParseChunksInit [x] s
1022 return $ D.Skip $ ParseChunksYield (Right pb) next
1023 PRD.IError err -> do
1024 let next = ParseChunksInitLeftOver []
1027 $ ParseChunksYield (Left (ParseError err)) next
1028 D.Skip s -> return $ D.Skip $ ParseChunksInit [] s
1029 D.Stop -> return D.Stop
1031 -- Buffer is not empty, go to buffered processing loop
1032 stepOuter _ (ParseChunksInit src st) = do
1036 return $ D.Skip $ ParseChunksBuf src st [] ps
1038 let next = ParseChunksInit src st
1039 in return $ D.Skip $ ParseChunksYield (Right pb) next
1040 PRD.IError err -> do
1041 let next = ParseChunksInitLeftOver []
1044 $ ParseChunksYield (Left (ParseError err)) next
1046 -- This is a simplified ParseChunksInit
1047 stepOuter _ (ParseChunksInitBuf src) = do
1051 return $ D.Skip $ ParseChunksExtract src [] ps
1053 let next = ParseChunksInitBuf src
1054 in return $ D.Skip $ ParseChunksYield (Right pb) next
1055 PRD.IError err -> do
1056 let next = ParseChunksInitLeftOver []
1059 $ ParseChunksYield (Left (ParseError err)) next
1061 -- XXX we just discard any leftover input at the end
1062 stepOuter _ (ParseChunksInitLeftOver _) = return D.Stop
1064 -- Buffer is empty, process elements from the stream
1065 stepOuter gst (ParseChunksStream st backBuf pst) = do
1066 r <- step (adaptState gst) st
1071 PR.Partial 0 pst1 ->
1072 return $ D.Skip $ ParseChunksStream s [] pst1
1073 PR.Partial n pst1 -> do
1075 (n <= sum (map Array.length (x:backBuf)))
1077 let src0 = takeArrayListRev n (x:backBuf)
1078 src = Prelude.reverse src0
1079 return $ D.Skip $ ParseChunksBuf src s [] pst1
1080 PR.Continue 0 pst1 ->
1081 return $ D.Skip $ ParseChunksStream s (x:backBuf) pst1
1082 PR.Continue n pst1 -> do
1084 (n <= sum (map Array.length (x:backBuf)))
1086 let (src0, buf1) = splitAtArrayListRev n (x:backBuf)
1087 src = Prelude.reverse src0
1088 return $ D.Skip $ ParseChunksBuf src s buf1 pst1
1091 ParseChunksYield (Right b) (ParseChunksInit [] s)
1094 (n <= sum (map Array.length (x:backBuf)))
1096 let src0 = takeArrayListRev n (x:backBuf)
1097 src = Prelude.reverse src0
1098 next = ParseChunksInit src s
1101 $ ParseChunksYield (Right b) next
1103 let next = ParseChunksInitLeftOver []
1106 $ ParseChunksYield (Left (ParseError err)) next
1108 D.Skip s -> return $ D.Skip $ ParseChunksStream s backBuf pst
1109 D.Stop -> return $ D.Skip $ ParseChunksStop backBuf pst
1111 -- go back to stream processing mode
1112 stepOuter _ (ParseChunksBuf [] s buf pst) =
1113 return $ D.Skip $ ParseChunksStream s buf pst
1115 -- buffered processing loop
1116 stepOuter _ (ParseChunksBuf (x:xs) s backBuf pst) = do
1119 PR.Partial 0 pst1 ->
1120 return $ D.Skip $ ParseChunksBuf xs s [] pst1
1121 PR.Partial n pst1 -> do
1122 assert (n <= sum (map Array.length (x:backBuf))) (return ())
1123 let src0 = takeArrayListRev n (x:backBuf)
1124 src = Prelude.reverse src0 ++ xs
1125 return $ D.Skip $ ParseChunksBuf src s [] pst1
1126 PR.Continue 0 pst1 ->
1127 return $ D.Skip $ ParseChunksBuf xs s (x:backBuf) pst1
1128 PR.Continue n pst1 -> do
1129 assert (n <= sum (map Array.length (x:backBuf))) (return ())
1130 let (src0, buf1) = splitAtArrayListRev n (x:backBuf)
1131 src = Prelude.reverse src0 ++ xs
1132 return $ D.Skip $ ParseChunksBuf src s buf1 pst1
1136 $ ParseChunksYield (Right b) (ParseChunksInit xs s)
1138 assert (n <= sum (map Array.length (x:backBuf))) (return ())
1139 let src0 = takeArrayListRev n (x:backBuf)
1140 src = Prelude.reverse src0 ++ xs
1143 $ ParseChunksYield (Right b) (ParseChunksInit src s)
1145 let next = ParseChunksInitLeftOver []
1148 $ ParseChunksYield (Left (ParseError err)) next
1150 -- This is a simplified ParseChunksBuf
1151 stepOuter _ (ParseChunksExtract [] buf pst) =
1152 return $ D.Skip $ ParseChunksStop buf pst
1154 stepOuter _ (ParseChunksExtract (x:xs) backBuf pst) = do
1157 PR.Partial 0 pst1 ->
1158 return $ D.Skip $ ParseChunksExtract xs [] pst1
1159 PR.Partial n pst1 -> do
1160 assert (n <= sum (map Array.length (x:backBuf))) (return ())
1161 let src0 = takeArrayListRev n (x:backBuf)
1162 src = Prelude.reverse src0 ++ xs
1163 return $ D.Skip $ ParseChunksExtract src [] pst1
1164 PR.Continue 0 pst1 ->
1165 return $ D.Skip $ ParseChunksExtract xs (x:backBuf) pst1
1166 PR.Continue n pst1 -> do
1167 assert (n <= sum (map Array.length (x:backBuf))) (return ())
1168 let (src0, buf1) = splitAtArrayListRev n (x:backBuf)
1169 src = Prelude.reverse src0 ++ xs
1170 return $ D.Skip $ ParseChunksExtract src buf1 pst1
1174 $ ParseChunksYield (Right b) (ParseChunksInitBuf xs)
1176 assert (n <= sum (map Array.length (x:backBuf))) (return ())
1177 let src0 = takeArrayListRev n (x:backBuf)
1178 src = Prelude.reverse src0 ++ xs
1181 $ ParseChunksYield (Right b) (ParseChunksInitBuf src)
1183 let next = ParseChunksInitLeftOver []
1186 $ ParseChunksYield (Left (ParseError err)) next
1189 -- This is a simplified ParseChunksExtract
1190 stepOuter _ (ParseChunksStop backBuf pst) = do
1193 PR.Partial _ _ -> error "runArrayFoldManyD: Partial in extract"
1194 PR.Continue 0 pst1 ->
1195 return $ D.Skip $ ParseChunksStop backBuf pst1
1196 PR.Continue n pst1 -> do
1197 assert (n <= sum (map Array.length backBuf)) (return ())
1198 let (src0, buf1) = splitAtArrayListRev n backBuf
1199 src = Prelude.reverse src0
1200 return $ D.Skip $ ParseChunksExtract src buf1 pst1
1204 $ ParseChunksYield (Right b) (ParseChunksInitLeftOver [])
1206 assert (n <= sum (map Array.length backBuf)) (return ())
1207 let src0 = takeArrayListRev n backBuf
1208 src = Prelude.reverse src0
1211 $ ParseChunksYield (Right b) (ParseChunksInitBuf src)
1213 let next = ParseChunksInitLeftOver []
1216 $ ParseChunksYield (Left (ParseError err)) next
1218 stepOuter _ (ParseChunksYield a next) = return $ D.Yield a next
1220-- | Apply an 'ChunkFold' repeatedly on an array stream and emit the
1221-- fold outputs in the output stream.
1223-- See "Streamly.Data.Stream.foldMany" for more details.
1226{-# INLINE runArrayFoldMany #-}
1228 :: (Monad m, Unbox a)
1230 -> StreamK m (Array a)
1231 -> StreamK m (Either ParseError b)
1232runArrayFoldMany p m = fromStream $ runArrayFoldManyD p (toStream 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