@@ -77,6 +77,7 @@ module Streamly.Internal.Data.Array.Foreign.Mut.Types
7777 -- * Unfolds
7878 , ReadUState
7979 , read
80+ , readRev
8081 , flattenArrays
8182 , flattenArraysRev
8283
@@ -523,6 +524,33 @@ read = Unfold step inject
523524 return $ D.Yield x
524525 (ReadUState fp (p `plusPtr` sizeOf (undefined :: a)))
525526
527+-- | Unfold an array into a stream in reverse order.
528+--
529+-- /Internal/
530+{-# INLINE_NORMAL readRev #-}
531+readRev :: forall m a. (Monad m, Storable a) => Unfold m (Array a) a
532+readRev = Unfold step inject
533+ where
534+
535+ inject (Array fp end _) =
536+ let p = end `plusPtr` negate (sizeOf (undefined :: a))
537+ in return $ ReadUState fp p
538+
539+ {-# INLINE_LATE step #-}
540+ step (ReadUState fp@(ForeignPtr start _) p) | p < Ptr start =
541+ let x = unsafeInlineIO $ touchForeignPtr fp
542+ in x `seq` return D.Stop
543+ step (ReadUState fp p) = do
544+ -- unsafeInlineIO allows us to run this in Identity monad for pure
545+ -- toList/foldr case which makes them much faster due to not
546+ -- accumulating the list and fusing better with the pure consumers.
547+ --
548+ -- This should be safe as the array contents are guaranteed to be
549+ -- evaluated/written to before we peek at them.
550+ let !x = unsafeInlineIO $ peek p
551+ return $ D.Yield x
552+ (ReadUState fp (p `plusPtr` negate (sizeOf (undefined :: a))))
553+
526554data FlattenState s a =
527555 OuterLoop s
528556 | InnerLoop s !(ForeignPtr a) !(Ptr a) !(Ptr a)
@@ -561,6 +589,12 @@ flattenArrays (D.Stream step state) = D.Stream step' (OuterLoop state)
561589 return $ D.Yield x (InnerLoop st startf
562590 (p `plusPtr` sizeOf (undefined :: a)) end)
563591
592+-- | Use the "readRev" unfold instead.
593+--
594+-- @flattenArrays = concatUnfold readRev@
595+--
596+-- We can try this if there are any fusion issues in the unfold.
597+--
564598{-# INLINE_NORMAL flattenArraysRev #-}
565599flattenArraysRev :: forall m a. (MonadIO m, Storable a)
566600 => D.Stream m (Array a) -> D.Stream m a
@@ -667,6 +701,11 @@ toStreamK Array{..} =
667701 return r
668702 in x `K.cons` go (p `plusPtr` sizeOf (undefined :: a))
669703
704+-- | Use the 'readRev' unfold instead.
705+--
706+-- @toStreamDRev = D.unfold readRev@
707+--
708+-- We can try this if the unfold has any perf issues.
670709{-# INLINE_NORMAL toStreamDRev #-}
671710toStreamDRev :: forall m a. (Monad m, Storable a) => Array a -> D.Stream m a
672711toStreamDRev Array{..} =