@@ -22,6 +22,10 @@ module Streamly.Internal.Data.Array.Stream.Foreign
24 -- * Elimination
24 -- * Elimination
27 -- * Compaction
31 -- * Compaction
@@ -39,30 +43,40 @@ where
40#include "inline.hs"
44#include "inline.hs"
46+import Control.Exception (assert)
47+import Control.Monad.Catch (MonadThrow, throwM)
42import Control.Monad.IO.Class (MonadIO(..))
48import Control.Monad.IO.Class (MonadIO(..))
43import Data.Word (Word8)
49import Data.Word (Word8)
44import Foreign.ForeignPtr (withForeignPtr, touchForeignPtr)
50import Foreign.ForeignPtr (withForeignPtr, touchForeignPtr)
45import Foreign.ForeignPtr.Unsafe (unsafeForeignPtrToPtr)
51import Foreign.ForeignPtr.Unsafe (unsafeForeignPtrToPtr)
46import Foreign.Ptr (minusPtr, plusPtr, castPtr)
52import Foreign.Ptr (minusPtr, plusPtr, castPtr)
47import Foreign.Storable (Storable(..))
53import Foreign.Storable (Storable(..))
54+import Fusion.Plugin.Types (Fuse(..))
55+import GHC.Exts (SpecConstrAnnotation(..))
48import GHC.ForeignPtr (ForeignPtr(..))
56import GHC.ForeignPtr (ForeignPtr(..))
49import GHC.Ptr (Ptr(..))
57import GHC.Ptr (Ptr(..))
58+import GHC.Types (SPEC(..))
50−import Prelude hiding (length, null, last, map, (!!), read, concat, unlines)
59+import Prelude hiding (null, last, (!!), read, concat, unlines)
52#if !defined(mingw32_HOST_OS)
61#if !defined(mingw32_HOST_OS)
53import Streamly.Internal.FileSystem.FDIO (IOVec(..))
62import Streamly.Internal.FileSystem.FDIO (IOVec(..))
55−import Streamly.Internal.Data.Array.Foreign.Type (Array(..), length)
64+import Streamly.Internal.Data.Array.Foreign.Type (Array(..))
56import Streamly.Internal.Data.Fold.Type (Fold(..))
65import Streamly.Internal.Data.Fold.Type (Fold(..))
66+import Streamly.Internal.Data.Parser (ParseError(..))
57import Streamly.Internal.Data.Stream.Serial (SerialT)
67import Streamly.Internal.Data.Stream.Serial (SerialT)
58import Streamly.Internal.Data.Stream.StreamK.Type (IsStream)
68import Streamly.Internal.Data.Stream.StreamK.Type (IsStream)
59−import Streamly.Internal.Data.SVar (adaptState)
69+import Streamly.Internal.Data.SVar (adaptState, defState)
61import qualified Streamly.Internal.Data.Array.Foreign as A
71import qualified Streamly.Internal.Data.Array.Foreign as A
72+import qualified Streamly.Internal.Data.Array.Foreign as Array
62import qualified Streamly.Internal.Data.Array.Foreign.Type as A
73import qualified Streamly.Internal.Data.Array.Foreign.Type as A
63import qualified Streamly.Internal.Data.Array.Foreign.Mut.Type as MA
74import qualified Streamly.Internal.Data.Array.Foreign.Mut.Type as MA
64import qualified Streamly.Internal.Data.Array.Stream.Mut.Foreign as AS
75import qualified Streamly.Internal.Data.Array.Stream.Mut.Foreign as AS
76+import qualified Streamly.Internal.Data.Array.Stream.Fold.Foreign as ASF
65import qualified Streamly.Internal.Data.Fold.Type as FL
77import qualified Streamly.Internal.Data.Fold.Type as FL
78+import qualified Streamly.Internal.Data.Parser as PR
79+import qualified Streamly.Internal.Data.Parser.ParserD as PRD
66import qualified Streamly.Internal.Data.Stream.IsStream as S
80import qualified Streamly.Internal.Data.Stream.IsStream as S
67import qualified Streamly.Internal.Data.Stream.StreamD as D
81import qualified Streamly.Internal.Data.Stream.StreamD as D
@@ -300,6 +314,56 @@ splitOnSuffix byte s =
300-- Elimination
314-- Elimination
301-------------------------------------------------------------------------------
315-------------------------------------------------------------------------------
317+-- When we have to take an array partially, take the last part of the array.
318+{-# INLINE takeArrayListRev #-}
319+takeArrayListRev :: forall a. Storable a => Int -> [Array a] -> [Array a]
320+takeArrayListRev = go
325+ go n _ | n <= 0 = []
327+ let len = Array.length x
329+ then x : go (n - len) xs
332+ else let !(Array (ForeignPtr _ contents) end) = x
333+ sz = sizeOf (undefined :: a)
334+ !(Ptr start) = end `plusPtr` negate (n * sz)
335+ in [Array (ForeignPtr start contents) end]
337+-- When we have to take an array partially, take the last part of the array in
339+{-# INLINE splitAtArrayListRev #-}
340+splitAtArrayListRev :: forall a. Storable a =>
341+ Int -> [Array a] -> ([Array a],[Array a])
342+splitAtArrayListRev n ls
344+ | otherwise = go n ls
346+ go :: Int -> [Array a] -> ([Array a], [Array a])
349+ let len = Array.length x
350+ (xs', xs'') = go (m - len) xs
355+ else let !(Array (ForeignPtr start contents) end) = x
356+ sz = sizeOf (undefined :: a)
357+ end1 = end `plusPtr` negate (m * sz)
358+ arr2 = Array (ForeignPtr start contents) end1
359+ !(Ptr addrEnd1) = end1
360+ arr1 = Array (ForeignPtr addrEnd1 contents) end
361+ in ([arr1], arr2:xs)
363+-------------------------------------------------------------------------------
364+-- Fold to a single Array
365+-------------------------------------------------------------------------------
303-- XXX Both of these implementations of splicing seem to perform equally well.
367-- XXX Both of these implementations of splicing seem to perform equally well.
304-- We need to perform benchmarks over a range of sizes though.
368-- We need to perform benchmarks over a range of sizes though.
@@ -326,7 +390,7 @@ _spliceArrays :: (MonadIO m, Storable a)
326 => SerialT m (Array a) -> m (Array a)
390 => SerialT m (Array a) -> m (Array a)
327_spliceArrays s = do
391_spliceArrays s = do
328 buffered <- S.foldr S.cons S.nil s
392 buffered <- S.foldr S.cons S.nil s
329− len <- S.sum (S.map length buffered)
393+ len <- S.sum (S.map Array.length buffered)
330 arr <- liftIO $ MA.newArray len
394 arr <- liftIO $ MA.newArray len
331 end <- S.foldlM' writeArr (return $ MA.aEnd arr) s
395 end <- S.foldlM' writeArr (return $ MA.aEnd arr) s
332 return $ A.unsafeFreeze $ arr {MA.aEnd = end}
396 return $ A.unsafeFreeze $ arr {MA.aEnd = end}
@@ -344,7 +408,7 @@ _spliceArraysBuffered :: (MonadIO m, Storable a)
344 => SerialT m (Array a) -> m (Array a)
408 => SerialT m (Array a) -> m (Array a)
345_spliceArraysBuffered s = do
409_spliceArraysBuffered s = do
346 buffered <- S.foldr S.cons S.nil s
410 buffered <- S.foldr S.cons S.nil s
347− len <- S.sum (S.map length buffered)
411+ len <- S.sum (S.map Array.length buffered)
348 A.unsafeFreeze <$> spliceArraysLenUnsafe len (S.map A.unsafeThaw s)
412 A.unsafeFreeze <$> spliceArraysLenUnsafe len (S.map A.unsafeThaw s)
350{-# INLINE spliceArraysRealloced #-}
414{-# INLINE spliceArraysRealloced #-}
@@ -357,6 +421,8 @@ spliceArraysRealloced s = do
357 arr <- S.foldlM' MA.spliceWithDoubling idst (S.map A.unsafeThaw s)
421 arr <- S.foldlM' MA.spliceWithDoubling idst (S.map A.unsafeThaw s)
358 liftIO $ A.unsafeFreeze <$> MA.shrinkToFit arr
422 liftIO $ A.unsafeFreeze <$> MA.shrinkToFit arr
424+-- XXX This should just be "fold A.write"
360-- | Given a stream of arrays, splice them all together to generate a single
426-- | Given a stream of arrays, splice them all together to generate a single
361-- array. The stream must be /finite/.
427-- array. The stream must be /finite/.
@@ -385,3 +451,242 @@ _toArraysOf :: (MonadIO m, Storable a)
385 => Int -> Fold m a (SerialT Identity (Array a))
451 => Int -> Fold m a (SerialT Identity (Array a))
386_toArraysOf n = FL.chunksOf n (A.writeNF n) FL.toStream
452_toArraysOf n = FL.chunksOf n (A.writeNF n) FL.toStream
455+-------------------------------------------------------------------------------
457+-------------------------------------------------------------------------------
459+-- GHC parser does not accept {-# ANN type [] NoSpecConstr #-}, so we need
460+-- to make a newtype.
461+{-# ANN type List NoSpecConstr #-}
462+newtype List a = List {getList :: [a]}
464+{-# INLINE_NORMAL parseD #-}
466+ forall m a b. (MonadIO m, MonadThrow m, Storable a)
467+ => PRD.Parser m (Array a) b
468+ -> D.Stream m (Array.Array a)
469+ -> m (b, D.Stream m (Array.Array a))
470+parseD (PRD.Parser pstep initial extract) stream@(D.Stream step state) = do
473+ PRD.IPartial s -> go SPEC state (List []) s
474+ PRD.IDone b -> return (b, stream)
475+ PRD.IError err -> throwM $ ParseError err
479+ -- "backBuf" contains last few items in the stream that we may have to
482+ -- XXX currently we are using a dumb list based approach for backtracking
483+ -- buffer. This can be replaced by a sliding/ring buffer using Data.Array.
484+ -- That will allow us more efficient random back and forth movement.
486+ go !_ st backBuf !pst = do
487+ r <- step defState st
489+ D.Yield x s -> gobuf SPEC [x] s backBuf pst
490+ D.Skip s -> go SPEC s backBuf pst
495+ gobuf !_ [] s backBuf !pst = go SPEC s backBuf pst
496+ gobuf !_ (x:xs) s backBuf !pst = do
499+ PR.Partial 0 pst1 ->
500+ gobuf SPEC xs s (List []) pst1
501+ PR.Partial n pst1 -> do
503+ (n <= sum (map Array.length (x:getList backBuf)))
505+ let src0 = takeArrayListRev n (x:getList backBuf)
506+ src = Prelude.reverse src0 ++ xs
507+ gobuf SPEC src s (List []) pst1
508+ PR.Continue 0 pst1 ->
509+ gobuf SPEC xs s (List (x:getList backBuf)) pst1
510+ PR.Continue n pst1 -> do
512+ (n <= sum (map Array.length (x:getList backBuf)))
514+ let (src0, buf1) = splitAtArrayListRev n (x:getList backBuf)
515+ src = Prelude.reverse src0 ++ xs
516+ gobuf SPEC src s (List buf1) pst1
518+ return (b, D.Stream step s)
521+ (n <= sum (map Array.length (x:getList backBuf)))
523+ let src0 = takeArrayListRev n (x:getList backBuf)
524+ src = Prelude.reverse src0 ++ xs
525+ return (b, D.append (D.fromList src) (D.Stream step s))
526+ PR.Error err -> throwM $ ParseError err
529+-- | Parse an array stream using the supplied 'Parser'. Returns the parse
530+-- result and the unconsumed stream. Throws 'ParseError' if the parse fails.
536+ (MonadIO m, MonadThrow m, Storable a)
538+ -> SerialT m (A.Array a)
539+ -> m (b, SerialT m (A.Array a))
540+parse p s = fmap D.fromStreamD <$> parseD p (D.toStreamD s)
543+-- | Fold an array stream using the supplied array stream 'Fold'.
548+fold :: (MonadIO m, MonadThrow m, Storable a) =>
549+ ASF.Fold m a b -> SerialT m (A.Array a) -> m b
550+fold (ASF.Fold p) s = fst <$> parseD p (D.toStreamD s)
552+{-# ANN type ParseChunksState Fuse #-}
553+data ParseChunksState x inpBuf st pst =
554+ ParseChunksInit inpBuf st
555+ | ParseChunksInitLeftOver inpBuf
556+ | ParseChunksStream st inpBuf !pst
557+ | ParseChunksBuf inpBuf st inpBuf !pst
558+ | ParseChunksYield x (ParseChunksState x inpBuf st pst)
560+{-# INLINE_NORMAL foldManyD #-}
562+ :: (MonadThrow m, Storable a)
564+ -> D.Stream m (Array a)
566+foldManyD (ASF.Fold (PRD.Parser pstep initial extract)) (D.Stream step state) =
567+ D.Stream stepOuter (ParseChunksInit [] state)
571+ {-# INLINE_LATE stepOuter #-}
572+ -- Buffer is empty, get the first element from the stream, initialize the
573+ -- fold and then go to stream processing loop.
574+ stepOuter gst (ParseChunksInit [] st) = do
575+ r <- step (adaptState gst) st
581+ return $ D.Skip $ ParseChunksBuf [x] s [] ps
583+ let next = ParseChunksInit [x] s
584+ in return $ D.Skip $ ParseChunksYield pb next
585+ PRD.IError err -> throwM $ ParseError err
586+ D.Skip s -> return $ D.Skip $ ParseChunksInit [] s
587+ D.Stop -> return D.Stop
589+ -- Buffer is not empty, go to buffered processing loop
590+ stepOuter _ (ParseChunksInit src st) = do
594+ return $ D.Skip $ ParseChunksBuf src st [] ps
596+ let next = ParseChunksInit src st
597+ in return $ D.Skip $ ParseChunksYield pb next
598+ PRD.IError err -> throwM $ ParseError err
600+ -- XXX we just discard any leftover input at the end
601+ stepOuter _ (ParseChunksInitLeftOver _) = return D.Stop
603+ -- Buffer is empty, process elements from the stream
604+ stepOuter gst (ParseChunksStream st backBuf pst) = do
605+ r <- step (adaptState gst) st
610+ PR.Partial 0 pst1 ->
611+ return $ D.Skip $ ParseChunksStream s [] pst1
612+ PR.Partial n pst1 -> do
614+ (n <= sum (map Array.length (x:backBuf)))
616+ let src0 = takeArrayListRev n (x:backBuf)
617+ src = Prelude.reverse src0
618+ return $ D.Skip $ ParseChunksBuf src s [] pst1
619+ PR.Continue 0 pst1 ->
620+ return $ D.Skip $ ParseChunksStream s (x:backBuf) pst1
621+ PR.Continue n pst1 -> do
623+ (n <= sum (map Array.length (x:backBuf)))
625+ let (src0, buf1) = splitAtArrayListRev n (x:backBuf)
626+ src = Prelude.reverse src0
627+ return $ D.Skip $ ParseChunksBuf src s buf1 pst1
630+ ParseChunksYield b (ParseChunksInit [] s)
633+ (n <= sum (map Array.length (x:backBuf)))
635+ let src0 = takeArrayListRev n (x:backBuf)
636+ let src = Prelude.reverse src0
638+ ParseChunksYield b (ParseChunksInit src s)
639+ PR.Error err -> throwM $ ParseError err
640+ D.Skip s -> return $ D.Skip $ ParseChunksStream s backBuf pst
643+ let src = Prelude.reverse backBuf
645+ ParseChunksYield b (ParseChunksInitLeftOver src)
647+ -- go back to stream processing mode
648+ stepOuter _ (ParseChunksBuf [] s buf pst) =
649+ return $ D.Skip $ ParseChunksStream s buf pst
651+ -- buffered processing loop
652+ stepOuter _ (ParseChunksBuf (x:xs) s backBuf pst) = do
655+ PR.Partial 0 pst1 ->
656+ return $ D.Skip $ ParseChunksBuf xs s [] pst1
657+ PR.Partial n pst1 -> do
658+ assert (n <= sum (map Array.length (x:backBuf))) (return ())
659+ let src0 = takeArrayListRev n (x:backBuf)
660+ src = Prelude.reverse src0 ++ xs
661+ return $ D.Skip $ ParseChunksBuf src s [] pst1
662+ PR.Continue 0 pst1 ->
663+ return $ D.Skip $ ParseChunksBuf xs s (x:backBuf) pst1
664+ PR.Continue n pst1 -> do
665+ assert (n <= sum (map Array.length (x:backBuf))) (return ())
666+ let (src0, buf1) = splitAtArrayListRev n (x:backBuf)
667+ src = Prelude.reverse src0 ++ xs
668+ return $ D.Skip $ ParseChunksBuf src s buf1 pst1
670+ return $ D.Skip $ ParseChunksYield b (ParseChunksInit xs s)
672+ assert (n <= sum (map Array.length (x:backBuf))) (return ())
673+ let src0 = takeArrayListRev n (x:backBuf)
674+ src = Prelude.reverse src0 ++ xs
675+ return $ D.Skip $ ParseChunksYield b (ParseChunksInit src s)
676+ PR.Error err -> throwM $ ParseError err
678+ stepOuter _ (ParseChunksYield a next) = return $ D.Yield a next
680+-- | Apply an array stream 'Fold' repeatedly on an array stream and emit the
681+-- fold outputs in the output stream.
683+-- See "Streamly.Prelude.foldMany" for more details.
686+{-# INLINE foldMany #-}
688+ :: (IsStream t, MonadThrow m, Storable a)
692+foldMany p m = D.fromStreamD $ foldManyD p (D.toStreamD m)