@@ -54,6 +54,11 @@ module Streamly.Internal.Data.Array.Type
5454 , clone
5555 , pinnedClone
5656
57+ -- * Unfolds
58+ , reader
59+ , readerUnsafe
60+ , producer -- experimental
61+
5762 -- ** Elimination
5863 , unsafeIndexIO
5964 , getIndexUnsafe
@@ -76,7 +81,7 @@ module Streamly.Internal.Data.Array.Type
7681 , pinnedWriteN
7782 , writeNUnsafe
7883 , pinnedWriteNUnsafe
79− , MA.ArrayUnsafe (..)
84+ -- , MA.ArrayUnsafe (..)
8085 , pinnedWriteNAligned
8186 , write
8287 , pinnedWrite
@@ -121,6 +126,7 @@ import GHC.Exts (IsList, IsString(..), Addr#)
121126
122127import GHC.IO (unsafePerformIO)
123128import GHC.Ptr (Ptr(..))
129+import Streamly.Internal.Data.Producer.Type (Producer(..))
124130import Streamly.Internal.Data.MutArray.Type (MutArray(..))
125131import Streamly.Internal.Data.MutByteArray.Type (MutByteArray)
126132import Streamly.Internal.Data.Fold.Type (Fold(..))
@@ -137,6 +143,7 @@ import qualified Streamly.Internal.Data.MutArray.Type as MA
137143import qualified Streamly.Internal.Data.Stream.Type as D
138144import qualified Streamly.Internal.Data.StreamK.Type as K
139145import qualified Streamly.Internal.Data.MutByteArray.Type as Unboxed
146+import qualified Streamly.Internal.Data.Producer as Producer
140147import qualified Streamly.Internal.Data.Unfold.Type as Unfold
141148import qualified Text.ParserCombinators.ReadPrec as ReadPrec
142149
@@ -414,16 +421,15 @@ pinnedChunksOf :: forall m a. (MonadIO m, Unbox a)
414421 => Int -> D.Stream m a -> D.Stream m (Array a)
415422pinnedChunksOf n str = D.map unsafeFreeze $ MA.pinnedChunksOf n str
416423
417−-- | Use the "reader" unfold instead.
418−--
419−-- @concatChunks = unfoldMany reader@
424+-- | Convert a stream of arrays into a stream of their elements.
420425--
421−-- We can try this if there are any fusion issues in the unfold.
426+-- >>> concatChunks = Stream.unfoldMany Array.reader
422427--
423428{-# INLINE_NORMAL concatChunks #-}
424−concatChunks :: forall m a. (MonadIO m, Unbox a)
425− => D.Stream m (Array a) -> D.Stream m a
429+concatChunks :: (MonadIO m, Unbox a) => Stream m (Array a) -> Stream m a
430+-- XXX this requires MonadIO whereas the unfoldMany version does not
426431concatChunks = MA.concatChunks . D.map unsafeThaw
432+-- concatChunks = D.unfoldMany reader
427433
428434{-# DEPRECATED flattenArrays "Please use \"unfoldMany reader\" instead." #-}
429435flattenArrays :: forall m a. (MonadIO m, Unbox a)
@@ -488,6 +494,49 @@ byteLength = MA.byteLength . unsafeThaw
488494length :: Unbox a => Array a -> Int
489495length arr = MA.length (unsafeThaw arr)
490496
497+{-# INLINE_NORMAL producer #-}
498+producer :: forall m a. (Monad m, Unbox a) => Producer m (Array a) a
499+producer =
500+ Producer.translate unsafeThaw unsafeFreeze
501+ $ MA.producerWith (return . unsafeInlineIO)
502+
503+-- | Unfold an array into a stream.
504+--
505+{-# INLINE_NORMAL reader #-}
506+reader :: forall m a. (Monad m, Unbox a) => Unfold m (Array a) a
507+reader = Producer.simplify producer
508+
509+-- | Unfold an array into a stream, does not check the end of the array, the
510+-- user is responsible for terminating the stream within the array bounds. For
511+-- high performance application where the end condition can be determined by
512+-- a terminating fold.
513+--
514+-- Written in the hope that it may be faster than "read", however, in the case
515+-- for which this was written, "read" proves to be faster even though the core
516+-- generated with unsafeRead looks simpler.
517+--
518+-- /Pre-release/
519+--
520+{-# INLINE_NORMAL readerUnsafe #-}
521+readerUnsafe :: forall m a. (Monad m, Unbox a) => Unfold m (Array a) a
522+readerUnsafe = Unfold step inject
523+ where
524+
525+ inject (Array contents start end) =
526+ return (MA.ArrayUnsafe contents end start)
527+
528+ {-# INLINE_LATE step #-}
529+ step (MA.ArrayUnsafe contents end p) = do
530+ -- unsafeInlineIO allows us to run this in Identity monad for pure
531+ -- toList/foldr case which makes them much faster due to not
532+ -- accumulating the list and fusing better with the pure consumers.
533+ --
534+ -- This should be safe as the array contents are guaranteed to be
535+ -- evaluated/written to before we peek at them.
536+ let !x = unsafeInlineIO $ peekAt p contents
537+ let !p1 = INDEX_NEXT(p,a)
538+ return $ D.Yield x (MA.ArrayUnsafe contents end p1)
539+
491540-- | Unfold an array into a stream in reverse order.
492541--
493542{-# INLINE_NORMAL readerRev #-}