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
15{-# DEPRECATED "Please use \"Streamly.Internal.Data.Array\" instead." #-}
19 , Array.pinnedChunksOf
22 -- * Flattening to elements
26 , Array.flattenArraysRev
28 , Array.interposeSuffix
29 , Array.intercalateSuffix
34 -- The byte level foldBreak can work as efficiently as the chunk level. We
35 -- can flatten the stream to byte stream and use that. But if we want the
36 -- remaining stream to be a chunk stream then this could be handy. But it
37 -- could also be implemented using parseBreak.
40 -- This is chunked parseBreak. A byte level parseBreak cannot work
41 -- efficiently. Because the stream will have to be a StreamK for
42 -- backtracking, StreamK at byte level would not be efficient.
43 -- parseBreak p = K.parseBreakChunks (ParserK.adaptC p)
52 -- XXX Use parseBreakChunks/parseChunks instead
53 -- foldBreak can be implemented using parseBreak. Use StreamK.
57 , runArrayParserDBreak -- StreamK.parseBreakChunks
63 -- We can use something like foldManyChunks, parseManyChunks with a take
65 , lpackArraysChunksOf -- Fold.compactChunks
66 , compact -- rechunk, compactChunks
69 -- We can use something like foldManyChunks, parseManyChunks with an
70 -- appropriate splitting fold.
71 , splitOn -- Stream.rechunkOn
72 , splitOnSuffix -- Stream.rechunkOnSuffix
76#include "ArrayMacros.h"
79import Data.Bifunctor (second)
80import Control.Exception (assert)
81import Control.Monad.IO.Class (MonadIO(..))
82-- import Data.Bifunctor (first)
83-- import Data.Either (fromRight)
84import Data.Proxy (Proxy(..))
85import Data.Word (Word8)
86import Streamly.Internal.Data.Unbox (Unbox(..))
87import Fusion.Plugin.Types (Fuse(..))
88import GHC.Exts (SpecConstrAnnotation(..))
89import GHC.Types (SPEC(..))
90import Prelude hiding (null, last, (!!), read, concat, unlines)
92import Streamly.Data.Fold (Fold)
93import Streamly.Internal.Data.Array.Type (Array(..))
94import Streamly.Internal.Data.Fold.Chunked (ChunkFold(..))
95import Streamly.Internal.Data.Parser (ParseError(..))
96import Streamly.Internal.Data.Stream (Stream)
97import Streamly.Internal.Data.StreamK (StreamK, fromStream, toStream)
98import Streamly.Internal.Data.SVar.Type (adaptState, defState)
100import qualified Streamly.Internal.Data.Array as A
101import qualified Streamly.Internal.Data.Array as Array
102import qualified Streamly.Internal.Data.Parser as PR
103import qualified Streamly.Internal.Data.Parser as PRD
104 (Parser(..), Initial(..))
105-- import qualified Streamly.Internal.Data.ParserK as ParserK
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-------------------------------------------------------------------------------
113-- Intersperse and append
114-------------------------------------------------------------------------------
116{-# INLINE_NORMAL unlines #-}
117unlines :: forall m a. (MonadIO m, Unbox a)
118 => a -> D.Stream m (Array a) -> D.Stream m a
119unlines = Array.interposeSuffix
121-------------------------------------------------------------------------------
123-------------------------------------------------------------------------------
125-- XXX instead of writing two different versions of this operation, we should
126-- write it as a pipe.
128-- XXX Confirm that immutable arrays won't be modified.
129{-# INLINE_NORMAL lpackArraysChunksOf #-}
130lpackArraysChunksOf :: (MonadIO m, Unbox a)
131 => Int -> Fold m (Array a) () -> Fold m (Array a) ()
132lpackArraysChunksOf = Array.lCompactGE
134-- | Coalesce adjacent arrays in incoming stream to form bigger arrays of a
135-- maximum specified size in bytes.
138{-# INLINE compact #-}
139compact :: (MonadIO m, Unbox a)
140 => Int -> Stream m (Array a) -> Stream m (Array a)
141compact = Array.compactLE
143-- | Given a stream of arrays, splice them all together to generate a single
144-- array. The stream must be /finite/.
147{-# INLINE toArray #-}
148toArray :: (MonadIO m, Unbox a) => Stream m (Array a) -> m (Array a)
149toArray = Array.fromChunks
151-------------------------------------------------------------------------------
153-------------------------------------------------------------------------------
155-- XXX Remove MonadIO constraint.
156-- | Split a stream of arrays on a given separator byte, dropping the separator
157-- and coalescing all the arrays between two separators into a single array.
160{-# INLINE splitOn #-}
164 -> Stream m (Array Word8)
165 -> Stream m (Array Word8)
166splitOn = Array.compactOnByte
168{-# INLINE splitOnSuffix #-}
172 -> Stream m (Array Word8)
173 -> Stream m (Array Word8)
174splitOnSuffix = Array.compactOnByteSuffix
176-------------------------------------------------------------------------------
177-- Elimination - Running folds
178-------------------------------------------------------------------------------
180{-# INLINE_NORMAL foldBreakD #-}
181foldBreakD :: forall m a b. (MonadIO m, Unbox a) =>
182 Fold m a b -> D.Stream m (Array a) -> m (b, D.Stream m (Array a))
183foldBreakD = Array.foldBreakChunks
185-- | Fold an array stream using the supplied 'Fold'. Returns the fold result
186-- and the unconsumed stream.
188-- > foldBreak f = runArrayFoldBreak (ChunkFold.fromFold f)
190-- Instead of using this we can adapt the fold to ParserK and use
191-- parseBreakChunks instead. ParserK allows composing using Monad as well.
195-- fmap (first (fromRight undefined))
196-- $ K.parseBreakChunks (ParserK.adaptC (PR.fromFold f)) s
199-- We can compare perf and remove this one or define it in terms of that.
203{-# INLINE_NORMAL foldBreak #-}
207 -> StreamK m (A.Array a)
208 -> m (b, StreamK m (A.Array a))
209foldBreak = Array.foldBreakChunksK
211-- foldBreak f s = fmap fromStreamD <$> foldBreakD f (toStreamD s)
214-- fmap (first (fromRight undefined))
215-- $ K.parseBreakChunks (ParserK.adaptC (PR.fromFold f)) s
217-- If foldBreak performs better than runArrayFoldBreak we can use a rewrite
218-- rule to rewrite runArrayFoldBreak to fold.
219-- foldBreak f = runArrayFoldBreak (ChunkFold.fromFold f)
221-------------------------------------------------------------------------------
222-- Elimination - running element parsers
223-------------------------------------------------------------------------------
225-- When we have to take an array partially, take the last part of the array.
226{-# INLINE takeArrayListRev #-}
227takeArrayListRev :: forall a. Unbox a => Int -> [Array a] -> [Array a]
235 let len = Array.length x
237 then x : go (n - len) xs
240 else let !(Array contents _ end) = x
241 !start = end - (n * SIZE_OF(a))
242 in [Array contents start end]
244-- When we have to take an array partially, take the last part of the array in
246{-# INLINE splitAtArrayListRev #-}
247splitAtArrayListRev ::
248 forall a. Unbox a => Int -> [Array a] -> ([Array a],[Array a])
249splitAtArrayListRev n ls
251 | otherwise = go n ls
253 go :: Int -> [Array a] -> ([Array a], [Array a])
256 let len = Array.length x
257 (xs', xs'') = go (m - len) xs
262 else let !(Array contents start end) = x
263 end1 = end - (m * SIZE_OF(a))
264 arr2 = Array contents start end1
265 arr1 = Array contents end1 end
268-- GHC parser does not accept {-# ANN type [] NoSpecConstr #-}, so we need
270{-# ANN type List NoSpecConstr #-}
271newtype List a = List {getList :: [a]}
273-- | Parse an array stream using the supplied 'Parser'. Returns the parse
274-- result and the unconsumed stream. Throws 'ParseError' if the parse fails.
276-- >> parseBreak p = K.parseBreakChunks (ParserK.adaptC p)
278-- This is redundant and we can just use parseBreakChunks, as ParserK can be
279-- composed using Monad. The only advantage of this is that we do not need to
282-- We can compare perf and remove this one or define it in terms of that.
286{-# INLINE_NORMAL parseBreak #-}
290 -> StreamK m (A.Array a)
291 -> m (Either ParseError b, StreamK m (A.Array a))
294 fmap fromStreamD <$> parseBreakD (PRD.fromParserK p) (toStreamD s)
296parseBreak = Array.parseBreakChunksK
297-- parseBreak p = K.parseBreakChunks (ParserK.adaptC p)
299-------------------------------------------------------------------------------
300-- Elimination - Running Array Folds and parsers
301-------------------------------------------------------------------------------
303-- | Note that this is not the same as using a @Parser (Array a) m b@ with the
304-- regular "Streamly.Internal.Data.IsStream.parse" function. The regular parse
305-- would consume the input arrays as single unit. This parser parses in the way
306-- as described in the ChunkFold module. The input arrays are treated as @n@
307-- element units and can be consumed partially. The remaining elements are
308-- inserted in the source stream as an array.
310{-# INLINE_NORMAL runArrayParserDBreak #-}
311runArrayParserDBreak ::
312 forall m a b. (MonadIO m, Unbox a)
313 => PRD.Parser (Array a) m b
314 -> D.Stream m (Array.Array a)
315 -> m (Either ParseError b, D.Stream m (Array.Array a))
317 (PRD.Parser pstep initial extract)
318 stream@(D.Stream step state) = do
322 PRD.IPartial s -> go SPEC state (List []) s
323 PRD.IDone b -> return (Right b, stream)
324 PRD.IError err -> return (Left (ParseError err), stream)
328 -- "backBuf" contains last few items in the stream that we may have to
331 -- XXX currently we are using a dumb list based approach for backtracking
332 -- buffer. This can be replaced by a sliding/ring buffer using Data.Array.
333 -- That will allow us more efficient random back and forth movement.
334 go _ st backBuf !pst = do
335 r <- step defState st
337 D.Yield x s -> gobuf SPEC [x] s backBuf pst
338 D.Skip s -> go SPEC s backBuf pst
339 D.Stop -> goStop backBuf pst
341 gobuf !_ [] s backBuf !pst = go SPEC s backBuf pst
342 gobuf !_ (x:xs) s backBuf !pst = do
346 gobuf SPEC xs s (List []) pst1
347 PR.Partial n pst1 -> do
349 (n <= sum (map Array.length (x:getList backBuf)))
351 let src0 = takeArrayListRev n (x:getList backBuf)
352 src = Prelude.reverse src0 ++ xs
353 gobuf SPEC src s (List []) pst1
354 PR.Continue 0 pst1 ->
355 gobuf SPEC xs s (List (x:getList backBuf)) pst1
356 PR.Continue n pst1 -> do
358 (n <= sum (map Array.length (x:getList backBuf)))
360 let (src0, buf1) = splitAtArrayListRev n (x:getList backBuf)
361 src = Prelude.reverse src0 ++ xs
362 gobuf SPEC src s (List buf1) pst1
364 let str = D.append (D.fromList xs) (D.Stream step s)
365 return (Right b, str)
368 (n <= sum (map Array.length (x:getList backBuf)))
370 let src0 = takeArrayListRev n (x:getList backBuf)
371 src = Prelude.reverse src0 ++ xs
372 return (Right b, D.append (D.fromList src) (D.Stream step s))
374 let src0 = x:getList backBuf
375 src = Prelude.reverse src0 ++ x:xs
376 strm = D.append (D.fromList src) (D.Stream step s)
377 return (Left (ParseError err), strm)
379 -- This is a simplified gobuf
380 goExtract _ [] backBuf !pst = goStop backBuf pst
381 goExtract _ (x:xs) backBuf !pst = do
385 goExtract SPEC xs (List []) pst1
386 PR.Partial n pst1 -> do
388 (n <= sum (map Array.length (x:getList backBuf)))
390 let src0 = takeArrayListRev n (x:getList backBuf)
391 src = Prelude.reverse src0 ++ xs
392 goExtract SPEC src (List []) pst1
393 PR.Continue 0 pst1 ->
394 goExtract SPEC xs (List (x:getList backBuf)) pst1
395 PR.Continue n pst1 -> do
397 (n <= sum (map Array.length (x:getList backBuf)))
399 let (src0, buf1) = splitAtArrayListRev n (x:getList backBuf)
400 src = Prelude.reverse src0 ++ xs
401 goExtract SPEC src (List buf1) pst1
403 return (Right b, D.fromList xs)
406 (n <= sum (map Array.length (x:getList backBuf)))
408 let src0 = takeArrayListRev n (x:getList backBuf)
409 src = Prelude.reverse src0 ++ xs
410 return (Right b, D.fromList src)
412 let src0 = getList backBuf
413 src = Prelude.reverse src0 ++ x:xs
414 return (Left (ParseError err), D.fromList src)
416 -- This is a simplified goExtract
417 {-# INLINE goStop #-}
418 goStop backBuf pst = do
421 PR.Partial _ _ -> error "Bug: runArrayParserDBreak: Partial in extract"
422 PR.Continue 0 pst1 ->
424 PR.Continue n pst1 -> do
426 (n <= sum (map Array.length (getList backBuf)))
428 let (src0, buf1) = splitAtArrayListRev n (getList backBuf)
429 src = Prelude.reverse src0
430 goExtract SPEC src (List buf1) pst1
431 PR.Done 0 b -> return (Right b, D.nil)
434 (n <= sum (map Array.length (getList backBuf)))
436 let src0 = takeArrayListRev n (getList backBuf)
437 src = Prelude.reverse src0
438 return (Right b, D.fromList src)
440 let src0 = getList backBuf
441 src = Prelude.reverse src0
442 return (Left (ParseError err), D.fromList src)
445-- | Parse an array stream using the supplied 'Parser'. Returns the parse
446-- result and the unconsumed stream. Throws 'ParseError' if the parse fails.
450{-# INLINE parseArr #-}
452 (MonadIO m, MonadThrow m, Unbox a)
454 -> Stream m (A.Array a)
455 -> m (b, Stream m (A.Array a))
456parseArr p s = fmap fromStreamD <$> parseBreakD p (toStreamD s)
459-- | Fold an array stream using the supplied array stream 'Fold'.
463{-# INLINE runArrayFold #-}
464runArrayFold :: (MonadIO m, Unbox a) =>
465 ChunkFold m a b -> StreamK m (A.Array a) -> m (Either ParseError b)
466runArrayFold (ChunkFold p) s = fst <$> runArrayParserDBreak p (toStream s)
468-- | Like 'fold' but also returns the remaining stream.
472{-# INLINE runArrayFoldBreak #-}
473runArrayFoldBreak :: (MonadIO m, Unbox a) =>
474 ChunkFold m a b -> StreamK m (A.Array a) -> m (Either ParseError b, StreamK m (A.Array a))
475runArrayFoldBreak (ChunkFold p) s =
476 second fromStream <$> runArrayParserDBreak p (toStream s)
478{-# ANN type ParseChunksState Fuse #-}
479data ParseChunksState x inpBuf st pst =
480 ParseChunksInit inpBuf st
481 | ParseChunksInitBuf inpBuf
482 | ParseChunksInitLeftOver inpBuf
483 | ParseChunksStream st inpBuf !pst
484 | ParseChunksStop inpBuf !pst
485 | ParseChunksBuf inpBuf st inpBuf !pst
486 | ParseChunksExtract inpBuf inpBuf !pst
487 | ParseChunksYield x (ParseChunksState x inpBuf st pst)
489{-# INLINE_NORMAL runArrayFoldManyD #-}
491 :: (Monad m, Unbox a)
493 -> D.Stream m (Array a)
494 -> D.Stream m (Either ParseError b)
496 (ChunkFold (PRD.Parser pstep initial extract)) (D.Stream step state) =
498 D.Stream stepOuter (ParseChunksInit [] state)
502 {-# INLINE_LATE stepOuter #-}
503 -- Buffer is empty, get the first element from the stream, initialize the
504 -- fold and then go to stream processing loop.
505 stepOuter gst (ParseChunksInit [] st) = do
506 r <- step (adaptState gst) st
512 return $ D.Skip $ ParseChunksBuf [x] s [] ps
514 let next = ParseChunksInit [x] s
515 return $ D.Skip $ ParseChunksYield (Right pb) next
517 let next = ParseChunksInitLeftOver []
520 $ ParseChunksYield (Left (ParseError err)) next
521 D.Skip s -> return $ D.Skip $ ParseChunksInit [] s
522 D.Stop -> return D.Stop
524 -- Buffer is not empty, go to buffered processing loop
525 stepOuter _ (ParseChunksInit src st) = do
529 return $ D.Skip $ ParseChunksBuf src st [] ps
531 let next = ParseChunksInit src st
532 in return $ D.Skip $ ParseChunksYield (Right pb) next
534 let next = ParseChunksInitLeftOver []
537 $ ParseChunksYield (Left (ParseError err)) next
539 -- This is a simplified ParseChunksInit
540 stepOuter _ (ParseChunksInitBuf src) = do
544 return $ D.Skip $ ParseChunksExtract src [] ps
546 let next = ParseChunksInitBuf src
547 in return $ D.Skip $ ParseChunksYield (Right pb) next
549 let next = ParseChunksInitLeftOver []
552 $ ParseChunksYield (Left (ParseError err)) next
554 -- XXX we just discard any leftover input at the end
555 stepOuter _ (ParseChunksInitLeftOver _) = return D.Stop
557 -- Buffer is empty, process elements from the stream
558 stepOuter gst (ParseChunksStream st backBuf pst) = do
559 r <- step (adaptState gst) st
565 return $ D.Skip $ ParseChunksStream s [] pst1
566 PR.Partial n pst1 -> do
568 (n <= sum (map Array.length (x:backBuf)))
570 let src0 = takeArrayListRev n (x:backBuf)
571 src = Prelude.reverse src0
572 return $ D.Skip $ ParseChunksBuf src s [] pst1
573 PR.Continue 0 pst1 ->
574 return $ D.Skip $ ParseChunksStream s (x:backBuf) pst1
575 PR.Continue n pst1 -> do
577 (n <= sum (map Array.length (x:backBuf)))
579 let (src0, buf1) = splitAtArrayListRev n (x:backBuf)
580 src = Prelude.reverse src0
581 return $ D.Skip $ ParseChunksBuf src s buf1 pst1
584 ParseChunksYield (Right b) (ParseChunksInit [] s)
587 (n <= sum (map Array.length (x:backBuf)))
589 let src0 = takeArrayListRev n (x:backBuf)
590 src = Prelude.reverse src0
591 next = ParseChunksInit src s
594 $ ParseChunksYield (Right b) next
596 let next = ParseChunksInitLeftOver []
599 $ ParseChunksYield (Left (ParseError err)) next
601 D.Skip s -> return $ D.Skip $ ParseChunksStream s backBuf pst
602 D.Stop -> return $ D.Skip $ ParseChunksStop backBuf pst
604 -- go back to stream processing mode
605 stepOuter _ (ParseChunksBuf [] s buf pst) =
606 return $ D.Skip $ ParseChunksStream s buf pst
608 -- buffered processing loop
609 stepOuter _ (ParseChunksBuf (x:xs) s backBuf pst) = do
613 return $ D.Skip $ ParseChunksBuf xs s [] pst1
614 PR.Partial n pst1 -> do
615 assert (n <= sum (map Array.length (x:backBuf))) (return ())
616 let src0 = takeArrayListRev n (x:backBuf)
617 src = Prelude.reverse src0 ++ xs
618 return $ D.Skip $ ParseChunksBuf src s [] pst1
619 PR.Continue 0 pst1 ->
620 return $ D.Skip $ ParseChunksBuf xs s (x:backBuf) pst1
621 PR.Continue n pst1 -> do
622 assert (n <= sum (map Array.length (x:backBuf))) (return ())
623 let (src0, buf1) = splitAtArrayListRev n (x:backBuf)
624 src = Prelude.reverse src0 ++ xs
625 return $ D.Skip $ ParseChunksBuf src s buf1 pst1
629 $ ParseChunksYield (Right b) (ParseChunksInit xs s)
631 assert (n <= sum (map Array.length (x:backBuf))) (return ())
632 let src0 = takeArrayListRev n (x:backBuf)
633 src = Prelude.reverse src0 ++ xs
636 $ ParseChunksYield (Right b) (ParseChunksInit src s)
638 let next = ParseChunksInitLeftOver []
641 $ ParseChunksYield (Left (ParseError err)) next
643 -- This is a simplified ParseChunksBuf
644 stepOuter _ (ParseChunksExtract [] buf pst) =
645 return $ D.Skip $ ParseChunksStop buf pst
647 stepOuter _ (ParseChunksExtract (x:xs) backBuf pst) = do
651 return $ D.Skip $ ParseChunksExtract xs [] pst1
652 PR.Partial n pst1 -> do
653 assert (n <= sum (map Array.length (x:backBuf))) (return ())
654 let src0 = takeArrayListRev n (x:backBuf)
655 src = Prelude.reverse src0 ++ xs
656 return $ D.Skip $ ParseChunksExtract src [] pst1
657 PR.Continue 0 pst1 ->
658 return $ D.Skip $ ParseChunksExtract xs (x:backBuf) pst1
659 PR.Continue n pst1 -> do
660 assert (n <= sum (map Array.length (x:backBuf))) (return ())
661 let (src0, buf1) = splitAtArrayListRev n (x:backBuf)
662 src = Prelude.reverse src0 ++ xs
663 return $ D.Skip $ ParseChunksExtract src buf1 pst1
667 $ ParseChunksYield (Right b) (ParseChunksInitBuf xs)
669 assert (n <= sum (map Array.length (x:backBuf))) (return ())
670 let src0 = takeArrayListRev n (x:backBuf)
671 src = Prelude.reverse src0 ++ xs
674 $ ParseChunksYield (Right b) (ParseChunksInitBuf src)
676 let next = ParseChunksInitLeftOver []
679 $ ParseChunksYield (Left (ParseError err)) next
682 -- This is a simplified ParseChunksExtract
683 stepOuter _ (ParseChunksStop backBuf pst) = do
686 PR.Partial _ _ -> error "runArrayFoldManyD: Partial in extract"
687 PR.Continue 0 pst1 ->
688 return $ D.Skip $ ParseChunksStop backBuf pst1
689 PR.Continue n pst1 -> do
690 assert (n <= sum (map Array.length backBuf)) (return ())
691 let (src0, buf1) = splitAtArrayListRev n backBuf
692 src = Prelude.reverse src0
693 return $ D.Skip $ ParseChunksExtract src buf1 pst1
697 $ ParseChunksYield (Right b) (ParseChunksInitLeftOver [])
699 assert (n <= sum (map Array.length backBuf)) (return ())
700 let src0 = takeArrayListRev n backBuf
701 src = Prelude.reverse src0
704 $ ParseChunksYield (Right b) (ParseChunksInitBuf src)
706 let next = ParseChunksInitLeftOver []
709 $ ParseChunksYield (Left (ParseError err)) next
711 stepOuter _ (ParseChunksYield a next) = return $ D.Yield a next
713-- | Apply an 'ChunkFold' repeatedly on an array stream and emit the
714-- fold outputs in the output stream.
716-- See "Streamly.Data.Stream.foldMany" for more details.
719{-# INLINE runArrayFoldMany #-}
721 :: (Monad m, Unbox a)
723 -> StreamK m (Array a)
724 -> StreamK m (Either ParseError b)
725runArrayFoldMany 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