on master help sign in
Remove all unsafe IO from Array.Foreign.Mut.Type
commit311f8b0c1dbc685e257a1dde6ebc3955e7f7dc69 authorAdithya Kumar <adithya@composewell.com> dateThu 21 Oct 2021, 15:22 (5 years ago) parentb06596f
4 files changed+203−112
src/Streamly/Internal/Data/Array/Foreign.hs+36−16 src/Streamly/Internal/Data/Array/Foreign/Mut.hs+2−1 src/Streamly/Internal/Data/Array/Foreign/Mut/Type.hs+56−84 src/Streamly/Internal/Data/Array/Foreign/Type.hs+109−11
src/Streamly/Internal/Data/Array/Foreign.hs+36−16
@@ -1,5 +1,3 @@
1−{-# LANGUAGE UnboxedTuples #-}
2−
31#include "inline.hs"
42
53-- |
@@ -132,13 +130,12 @@ import Prelude hiding (length, null, last, map, (!!), read, concat)
132130
133131import GHC.ForeignPtr (ForeignPtr(..))
134132import GHC.Ptr (Ptr(..))
135−import GHC.Prim (touch#)
136−import GHC.IO (IO(..))
137133
138−import Streamly.Internal.Data.Array.Foreign.Mut.Type (arrayToFptrContents)
134+import Streamly.Internal.Data.Array.Foreign.Mut.Type
135+ (ReadUState(..), arrayToFptrContents, touch, fptrToArrayContents)
139136import Streamly.Internal.Data.Array.Foreign.Type (Array(..), length)
140137import Streamly.Internal.Data.Fold.Type (Fold(..))
141−import Streamly.Internal.Data.Producer.Type (Producer)
138+import Streamly.Internal.Data.Producer.Type (Producer(..))
142139import Streamly.Internal.Data.Stream.Serial (SerialT(..))
143140import Streamly.Internal.Data.Tuple.Strict (Tuple3'(..))
144141import Streamly.Internal.Data.Unfold.Type (Unfold(..))
@@ -148,10 +145,11 @@ import qualified Streamly.Internal.Data.Array.Foreign.Mut.Type as MA
148145import qualified Streamly.Internal.Data.Array.Foreign.Mut as MA
149146import qualified Streamly.Internal.Data.Array.Foreign.Type as A
150147import qualified Streamly.Internal.Data.Fold as FL
148+import qualified Streamly.Internal.Data.Producer as Producer
149+import qualified Streamly.Internal.Data.Stream.IsStream.Type as IsStream
151150import qualified Streamly.Internal.Data.Stream.Prelude as P
152151import qualified Streamly.Internal.Data.Stream.StreamD as D
153152import qualified Streamly.Internal.Data.Unfold as Unfold
154−import qualified Streamly.Internal.Data.Producer.Type as Producer
155153import qualified Streamly.Internal.Ring.Foreign as RB
156154
157155-------------------------------------------------------------------------------
@@ -187,6 +185,32 @@ fromStream (SerialT m) = P.fold A.write m
187185-- Elimination
188186-------------------------------------------------------------------------------
189187
188+{-# INLINE_NORMAL producer #-}
189+producer :: forall m a. (Monad m, Storable a) => Producer m (Array a) a
190+producer = Producer step inject extract
191+ where
192+
193+ {-# INLINE inject #-}
194+ inject (Array contents start end) = return $ ReadUState contents end start
195+
196+ {-# INLINE_LATE step #-}
197+ step (ReadUState contents end cur)
198+ | assert (cur <= end) (cur == end) =
199+ let x = unsafeInlineIO $ touch contents
200+ in x `seq` return D.Stop
201+ step (ReadUState contents end cur) = do
202+ -- unsafeInlineIO allows us to run this in Identity monad for pure
203+ -- toList/foldr case which makes them much faster due to not
204+ -- accumulating the list and fusing better with the pure consumers.
205+ --
206+ -- This should be safe as the array contents are guaranteed to be
207+ -- evaluated/written to before we peek at them.
208+ let !x = unsafeInlineIO $ peek cur
209+ cur1 = cur `plusPtr` sizeOf (undefined :: a)
210+ return $ D.Yield x (ReadUState contents end cur1)
211+
212+ extract (ReadUState contents end cur) = return $ Array contents cur end
213+
190214-- | Unfold an array into a stream.
191215--
192216-- /Since 0.7.0 (Streamly.Memory.Array)/
@@ -194,11 +218,7 @@ fromStream (SerialT m) = P.fold A.write m
194218-- @since 0.8.0
195219{-# INLINE_NORMAL read #-}
196220read :: forall m a. (Monad m, Storable a) => Unfold m (Array a) a
197−read = Unfold.lmap A.unsafeThaw MA.read
198−
199−{-# INLINE_NORMAL producer #-}
200−producer :: forall m a. (Monad m, Storable a) => Producer m (Array a) a
201−producer = Producer.translate A.unsafeThaw A.unsafeFreeze MA.producer
221+read = Producer.simplify producer
202222
203223-- | Unfold an array into a stream, does not check the end of the array, the
204224-- user is responsible for terminating the stream within the array bounds. For
@@ -229,13 +249,11 @@ unsafeRead = Unfold step inject
229249 -- evaluated/written to before we peek at them.
230250 let !x = unsafeInlineIO $ do
231251 r <- peek (Ptr p)
232− touch contents
252+ touch (fptrToArrayContents contents)
233253 return r
234254 let !(Ptr p1) = Ptr p `plusPtr` sizeOf (undefined :: a)
235255 return $ D.Yield x (ForeignPtr p1 contents)
236256
237− touch r = IO $ \s -> case touch# r s of s' -> (# s', () #)
238−
239257-- |
240258--
241259-- >>> import qualified Streamly.Internal.Data.Array.Foreign.Type as Array
@@ -379,7 +397,9 @@ getSliceUnsafe index len (Array contents start e) =
379397splitOn :: (Monad m, Storable a) =>
380398 (a -> Bool) -> Array a -> SerialT m (Array a)
381399splitOn predicate arr =
382− fmap A.unsafeFreeze $ MA.splitOn predicate (A.unsafeThaw arr)
400+ IsStream.fromStreamD
401+ $ fmap (\(i, len) -> getSliceUnsafe i len arr)
402+ $ D.sliceOnSuffix predicate (A.toStreamD arr)
383403
384404{-# INLINE genSlicesFromLen #-}
385405genSlicesFromLen :: forall m a. (Monad m, Storable a)
src/Streamly/Internal/Data/Array/Foreign/Mut.hs+2−1
@@ -40,13 +40,14 @@ import Streamly.Internal.Data.Stream.IsStream.Type (SerialT)
4040import qualified Streamly.Internal.Data.Stream.IsStream.Type as IsStream
4141import qualified Streamly.Internal.Data.Unfold as Unfold
4242import Streamly.Internal.Data.Unfold.Type (Unfold(..))
43+import Control.Monad.IO.Class (MonadIO(..))
4344
4445-- | Split the array into a stream of slices using a predicate. The element
4546-- matching the predicate is dropped.
4647--
4748-- /Pre-release/
4849{-# INLINE splitOn #-}
49−splitOn :: (Monad m, Storable a) =>
50+splitOn :: (MonadIO m, Storable a) =>
5051 (a -> Bool) -> Array a -> SerialT m (Array a)
5152splitOn predicate arr =
5253 IsStream.fromStreamD
src/Streamly/Internal/Data/Array/Foreign/Mut/Type.hs+56−84
@@ -103,7 +103,7 @@ module Streamly.Internal.Data.Array.Foreign.Mut.Type
103103 -- * Eliminating and Reading
104104
105105 -- ** To streams
106− , ReadUState
106+ , ReadUState(..)
107107 , read
108108 , readRev
109109
@@ -212,7 +212,6 @@ import Control.DeepSeq (NFData(..))
212212import Control.Monad (when, void)
213213import Control.Monad.IO.Class (MonadIO(..))
214214import Data.Bits ((.&.))
215−import Data.Functor.Identity (Identity(runIdentity))
216215#if __GLASGOW_HASKELL__ < 808
217216import Data.Semigroup (Semigroup(..))
218217#endif
@@ -225,7 +224,7 @@ import Foreign.Marshal.Alloc (mallocBytes)
225224import Foreign.Ptr (plusPtr, minusPtr, castPtr, nullPtr)
226225import Foreign.Storable (Storable(..))
227226import GHC.Base
228− ( build, touch#, IO(..), byteArrayContents#
227+ ( touch#, IO(..), byteArrayContents#
229228 , Int(..), newAlignedPinnedByteArray#
230229 )
231230#ifndef USE_FOREIGN_PTR
@@ -248,8 +247,7 @@ import Streamly.Internal.Data.Fold.Type (Fold(..))
248247import Streamly.Internal.Data.Producer.Type (Producer (..))
249248import Streamly.Internal.Data.SVar.Type (adaptState)
250249import Streamly.Internal.Data.Unfold.Type (Unfold(..))
251−import Streamly.Internal.System.IO
252− (defaultChunkSize, arrayPayloadSize, unsafeInlineIO)
250+import Streamly.Internal.System.IO (arrayPayloadSize, defaultChunkSize)
253251import System.IO.Unsafe (unsafePerformIO)
254252
255253#ifdef DEVBUILD
@@ -1371,24 +1369,18 @@ toReadUState (Array contents start end _) = ReadUState contents end start
13711369-- | Resumable unfold of an array.
13721370--
13731371{-# INLINE_NORMAL producer #-}
1374−producer :: forall m a. (Monad m, Storable a) => Producer m (Array a) a
1372+producer :: forall m a. (MonadIO m, Storable a) => Producer m (Array a) a
13751373producer = Producer step (return . toReadUState) extract
13761374 where
13771375
13781376 {-# INLINE_LATE step #-}
13791377 step (ReadUState contents end cur)
1380− | assert (cur <= end) (cur == end) =
1381− let x = unsafeInlineIO $ touch contents
1382− in x `seq` return D.Stop
1378+ | assert (cur <= end) (cur == end) = do
1379+ liftIO $ touch contents
1380+ return D.Stop
13831381 step (ReadUState contents end cur) = do
1384− -- unsafeInlineIO allows us to run this in Identity monad for pure
1385− -- toList/foldr case which makes them much faster due to not
1386− -- accumulating the list and fusing better with the pure consumers.
1387− --
1388− -- This should be safe as the array contents are guaranteed to be
1389− -- evaluated/written to before we peek at them.
1390− let !x = unsafeInlineIO $ peek cur
1391− cur1 = cur `plusPtr` sizeOf (undefined :: a)
1382+ !x <- liftIO $ peek cur
1383+ let cur1 = cur `plusPtr` sizeOf (undefined :: a)
13921384 return $ D.Yield x (ReadUState contents end cur1)
13931385
13941386 extract (ReadUState contents end cur) = return $ Array contents cur end end
@@ -1397,14 +1389,14 @@ producer = Producer step (return . toReadUState) extract
13971389--
13981390-- @since 0.7.0
13991391{-# INLINE_NORMAL read #-}
1400−read :: forall m a. (Monad m, Storable a) => Unfold m (Array a) a
1392+read :: forall m a. (MonadIO m, Storable a) => Unfold m (Array a) a
14011393read = Producer.simplify producer
14021394
14031395-- | Unfold an array into a stream in reverse order.
14041396--
14051397-- /Pre-release/
14061398{-# INLINE_NORMAL readRev #-}
1407−readRev :: forall m a. (Monad m, Storable a) => Unfold m (Array a) a
1399+readRev :: forall m a. (MonadIO m, Storable a) => Unfold m (Array a) a
14081400readRev = Unfold step inject
14091401 where
14101402
@@ -1413,17 +1405,11 @@ readRev = Unfold step inject
14131405 in return $ ReadUState contents start p
14141406
14151407 {-# INLINE_LATE step #-}
1416− step (ReadUState contents start p) | p < start =
1417− let x = unsafeInlineIO $ touch contents
1418− in x `seq` return D.Stop
1408+ step (ReadUState contents start p) | p < start = do
1409+ liftIO $ touch contents
1410+ return D.Stop
14191411 step (ReadUState contents start p) = do
1420− -- unsafeInlineIO allows us to run this in Identity monad for pure
1421− -- toList/foldr case which makes them much faster due to not
1422− -- accumulating the list and fusing better with the pure consumers.
1423− --
1424− -- This should be safe as the array contents are guaranteed to be
1425− -- evaluated/written to before we peek at them.
1426− let !x = unsafeInlineIO $ peek p
1412+ x <- liftIO $ peek p
14271413 let cur = p `plusPtr` negate (sizeOf (undefined :: a))
14281414 return $ D.Yield x (ReadUState contents start cur)
14291415
@@ -1431,6 +1417,7 @@ readRev = Unfold step inject
14311417-- to Lists and streams
14321418-------------------------------------------------------------------------------
14331419
1420+{-
14341421-- Use foldr/build fusion to fuse with list consumers
14351422-- This can be useful when using the IsList instance
14361423{-# INLINE_LATE toListFB #-}
@@ -1452,14 +1439,23 @@ toListFB c n Array{..} = go arrStart
14521439 touch arrContents
14531440 return r
14541441 in c x (go (p `plusPtr` sizeOf (undefined :: a)))
1442+-}
14551443
1456−-- XXX Should be monadic
1444+-- XXX Monadic foldr/build fusion?
1445+-- Reference: https://www.researchgate.net/publication/220676509_Monadic_augment_and_generalised_short_cut_fusion
14571446-- | Convert an 'Array' into a list.
14581447--
14591448-- @since 0.7.0
14601449{-# INLINE toList #-}
1461−toList :: Storable a => Array a -> [a]
1462−toList s = build (\c n -> toListFB c n s)
1450+toList :: forall m a. (MonadIO m, Storable a) => Array a -> m [a]
1451+toList Array{..} = liftIO $ go arrStart
1452+ where
1453+
1454+ go p | assert (p <= aEnd) (p == aEnd) = return []
1455+ go p = do
1456+ x <- peek p
1457+ touch arrContents
1458+ (:) x <$> go (p `plusPtr` sizeOf (undefined :: a))
14631459
14641460-- | Use the 'read' unfold instead.
14651461--
@@ -1467,48 +1463,31 @@ toList s = build (\c n -> toListFB c n s)
14671463--
14681464-- We can try this if the unfold has any performance issues.
14691465{-# INLINE_NORMAL toStreamD #-}
1470−toStreamD :: forall m a. (Monad m, Storable a) => Array a -> D.Stream m a
1471−toStreamD Array{..} =
1472− let p = arrStart
1473− in D.Stream step p
1466+toStreamD :: forall m a. (MonadIO m, Storable a) => Array a -> D.Stream m a
1467+toStreamD Array{..} = D.Stream step arrStart
14741468
14751469 where
14761470
14771471 {-# INLINE_LATE step #-}
14781472 step _ p | assert (p <= aEnd) (p == aEnd) = return D.Stop
1479− step _ p = do
1480− -- unsafeInlineIO allows us to run this in Identity monad for pure
1481− -- toList/foldr case which makes them much faster due to not
1482− -- accumulating the list and fusing better with the pure consumers.
1483− --
1484− -- This should be safe as the array contents are guaranteed to be
1485− -- evaluated/written to before we peek at them.
1486− --
1487− -- XXX This may not be safe for mutable arrays.
1488− --
1489− let !x = unsafeInlineIO $ do
1490− r <- peek p
1491− touch arrContents
1492− return r
1493− return $ D.Yield x (p `plusPtr` sizeOf (undefined :: a))
1473+ step _ p = liftIO $ do
1474+ r <- peek p
1475+ touch arrContents
1476+ return $ D.Yield r (p `plusPtr` sizeOf (undefined :: a))
14941477
14951478{-# INLINE toStreamK #-}
1496−toStreamK :: forall m a. Storable a => Array a -> K.Stream m a
1497−toStreamK Array{..} =
1498− let p = arrStart
1499− in go p
1479+toStreamK :: forall m a. (MonadIO m, Storable a) => Array a -> K.Stream m a
1480+toStreamK Array{..} = go arrStart
15001481
15011482 where
15021483
15031484 go p | assert (p <= aEnd) (p == aEnd) = K.nil
15041485 | otherwise =
1505− -- See Note in toStreamD.
1506− -- XXX May not be safe for mutable arrays.
1507− let !x = unsafeInlineIO $ do
1508− r <- peek p
1509− touch arrContents
1510− return r
1511− in x `K.cons` go (p `plusPtr` sizeOf (undefined :: a))
1486+ let elemM = do
1487+ r <- peek p
1488+ touch arrContents
1489+ return r
1490+ in liftIO elemM `K.consM` go (p `plusPtr` sizeOf (undefined :: a))
15121491
15131492-- | Use the 'readRev' unfold instead.
15141493--
@@ -1516,7 +1495,7 @@ toStreamK Array{..} =
15161495--
15171496-- We can try this if the unfold has any perf issues.
15181497{-# INLINE_NORMAL toStreamDRev #-}
1519−toStreamDRev :: forall m a. (Monad m, Storable a) => Array a -> D.Stream m a
1498+toStreamDRev :: forall m a. (MonadIO m, Storable a) => Array a -> D.Stream m a
15201499toStreamDRev Array{..} =
15211500 let p = aEnd `plusPtr` negate (sizeOf (undefined :: a))
15221501 in D.Stream step p
@@ -1525,17 +1504,13 @@ toStreamDRev Array{..} =
15251504
15261505 {-# INLINE_LATE step #-}
15271506 step _ p | p < arrStart = return D.Stop
1528− step _ p = do
1529− -- See comments in toStreamD for why we use unsafeInlineIO
1530− -- XXX
1531− let !x = unsafeInlineIO $ do
1532− r <- peek p
1533− touch arrContents
1534− return r
1535− return $ D.Yield x (p `plusPtr` negate (sizeOf (undefined :: a)))
1507+ step _ p = liftIO $ do
1508+ r <- peek p
1509+ touch arrContents
1510+ return $ D.Yield r (p `plusPtr` negate (sizeOf (undefined :: a)))
15361511
15371512{-# INLINE toStreamKRev #-}
1538−toStreamKRev :: forall m a. Storable a => Array a -> K.Stream m a
1513+toStreamKRev :: forall m a. (MonadIO m, Storable a) => Array a -> K.Stream m a
15391514toStreamKRev Array {..} =
15401515 let p = aEnd `plusPtr` negate (sizeOf (undefined :: a))
15411516 in go p
@@ -1544,12 +1519,11 @@ toStreamKRev Array {..} =
15441519
15451520 go p | p < arrStart = K.nil
15461521 | otherwise =
1547− -- XXX
1548− let !x = unsafeInlineIO $ do
1549− r <- peek p
1550− touch arrContents
1551− return r
1552− in x `K.cons` go (p `plusPtr` negate (sizeOf (undefined :: a)))
1522+ let elemM = do
1523+ r <- peek p
1524+ touch arrContents
1525+ return r
1526+ in liftIO elemM `K.consM` go (p `plusPtr` negate (sizeOf (undefined :: a)))
15531527
15541528-------------------------------------------------------------------------------
15551529-- Folding
@@ -1560,15 +1534,13 @@ toStreamKRev Array {..} =
15601534--
15611535-- | Strict left fold of an array.
15621536{-# INLINE_NORMAL foldl' #-}
1563−foldl' :: forall a b. Storable a => (b -> a -> b) -> b -> Array a -> b
1564−-- XXX Should be monadic
1565−foldl' f z arr = runIdentity $ D.foldl' f z $ toStreamD arr
1537+foldl' :: (MonadIO m, Storable a) => (b -> a -> b) -> b -> Array a -> m b
1538+foldl' f z arr = D.foldl' f z $ toStreamD arr
15661539
15671540-- | Right fold of an array.
15681541{-# INLINE_NORMAL foldr #-}
1569−-- XXX Should be monadic
1570−foldr :: Storable a => (a -> b -> b) -> b -> Array a -> b
1571−foldr f z arr = runIdentity $ D.foldr f z $ toStreamD arr
1542+foldr :: (MonadIO m, Storable a) => (a -> b -> b) -> b -> Array a -> m b
1543+foldr f z arr = D.foldr f z $ toStreamD arr
15721544
15731545-------------------------------------------------------------------------------
15741546-- Folds
src/Streamly/Internal/Data/Array/Foreign/Type.hs+109−11
@@ -73,6 +73,7 @@ module Streamly.Internal.Data.Array.Foreign.Type
7373 )
7474where
7575
76+import Control.Exception (assert)
7677import Control.DeepSeq (NFData(..))
7778import Control.Monad.IO.Class (MonadIO(..))
7879import Data.Functor.Identity (Identity(..))
@@ -81,7 +82,7 @@ import Foreign.C.String (CString)
8182import Foreign.C.Types (CSize(..))
8283import Foreign.Ptr (plusPtr, castPtr)
8384import Foreign.Storable (Storable(..))
84−import GHC.Base (Addr#, nullAddr#)
85+import GHC.Base (Addr#, nullAddr#, build)
8586import GHC.Exts (IsList, IsString(..))
8687
8788#ifdef DEVBUILD
@@ -89,7 +90,8 @@ import GHC.ForeignPtr (touchForeignPtr, unsafeForeignPtrToPtr)
8990#endif
9091import GHC.IO (unsafePerformIO)
9192import GHC.Ptr (Ptr(..))
92−import Streamly.Internal.Data.Array.Foreign.Mut.Type (ArrayContents)
93+import Streamly.Internal.Data.Array.Foreign.Mut.Type
94+ (ArrayContents, ReadUState(..), touch)
9395import Streamly.Internal.Data.Fold.Type (Fold(..))
9496import Streamly.Internal.Data.Stream.Serial (SerialT(..))
9597import Streamly.Internal.Data.Unfold.Type (Unfold(..))
@@ -100,7 +102,6 @@ import Prelude hiding (length, foldr, read, unlines, splitAt)
100102import qualified Streamly.Internal.Data.Array.Foreign.Mut.Type as MA
101103import qualified Streamly.Internal.Data.Stream.StreamD.Type as D
102104import qualified Streamly.Internal.Data.Stream.StreamK.Type as K
103−import qualified Streamly.Internal.Data.Unfold.Type as Unfold
104105import qualified GHC.Exts as Exts
105106
106107import Streamly.Internal.System.IO (unsafeInlineIO, defaultChunkSize)
@@ -441,23 +442,99 @@ length arr = MA.length (unsafeThaw arr)
441442-- @since 0.8.0
442443{-# INLINE_NORMAL readRev #-}
443444readRev :: forall m a. (Monad m, Storable a) => Unfold m (Array a) a
444−readRev = Unfold.lmap unsafeThaw MA.readRev
445+readRev = Unfold step inject
446+ where
447+
448+ inject (Array contents start end) =
449+ let p = end `plusPtr` negate (sizeOf (undefined :: a))
450+ in return $ ReadUState contents start p
451+
452+ {-# INLINE_LATE step #-}
453+ step (ReadUState contents start p) | p < start =
454+ let x = unsafeInlineIO $ touch contents
455+ in x `seq` return D.Stop
456+ step (ReadUState contents start p) = do
457+ -- unsafeInlineIO allows us to run this in Identity monad for pure
458+ -- toList/foldr case which makes them much faster due to not
459+ -- accumulating the list and fusing better with the pure consumers.
460+ --
461+ -- This should be safe as the array contents are guaranteed to be
462+ -- evaluated/written to before we peek at them.
463+ let !x = unsafeInlineIO $ peek p
464+ let cur = p `plusPtr` negate (sizeOf (undefined :: a))
465+ return $ D.Yield x (ReadUState contents start cur)
466+
445467
446468{-# INLINE_NORMAL toStreamD #-}
447469toStreamD :: forall m a. (Monad m, Storable a) => Array a -> D.Stream m a
448−toStreamD arr = MA.toStreamD (unsafeThaw arr)
470+toStreamD Array{..} = D.Stream step arrStart
471+
472+ where
473+
474+ {-# INLINE_LATE step #-}
475+ step _ p | assert (p <= aEnd) (p == aEnd) = return D.Stop
476+ step _ p = do
477+ -- unsafeInlineIO allows us to run this in Identity monad for pure
478+ -- toList/foldr case which makes them much faster due to not
479+ -- accumulating the list and fusing better with the pure consumers.
480+ --
481+ -- This should be safe as the array contents are guaranteed to be
482+ -- evaluated/written to before we peek at them.
483+ --
484+ let !x = unsafeInlineIO $ do
485+ r <- peek p
486+ touch arrContents
487+ return r
488+ return $ D.Yield x (p `plusPtr` sizeOf (undefined :: a))
449489
450490{-# INLINE toStreamK #-}
451491toStreamK :: forall m a. Storable a => Array a -> K.Stream m a
452−toStreamK arr = MA.toStreamK (unsafeThaw arr)
492+toStreamK Array{..} = go arrStart
493+
494+ where
495+
496+ go p | assert (p <= aEnd) (p == aEnd) = K.nil
497+ | otherwise =
498+ -- See Note in toStreamD.
499+ let !x = unsafeInlineIO $ do
500+ r <- peek p
501+ touch arrContents
502+ return r
503+ in x `K.cons` go (p `plusPtr` sizeOf (undefined :: a))
453504
454505{-# INLINE_NORMAL toStreamDRev #-}
455506toStreamDRev :: forall m a. (Monad m, Storable a) => Array a -> D.Stream m a
456−toStreamDRev arr = MA.toStreamDRev (unsafeThaw arr)
507+toStreamDRev Array{..} =
508+ let p = aEnd `plusPtr` negate (sizeOf (undefined :: a))
509+ in D.Stream step p
510+
511+ where
512+
513+ {-# INLINE_LATE step #-}
514+ step _ p | p < arrStart = return D.Stop
515+ step _ p = do
516+ -- See comments in toStreamD for why we use unsafeInlineIO
517+ let !x = unsafeInlineIO $ do
518+ r <- peek p
519+ touch arrContents
520+ return r
521+ return $ D.Yield x (p `plusPtr` negate (sizeOf (undefined :: a)))
457522
458523{-# INLINE toStreamKRev #-}
459524toStreamKRev :: forall m a. Storable a => Array a -> K.Stream m a
460−toStreamKRev arr = MA.toStreamKRev (unsafeThaw arr)
525+toStreamKRev Array {..} =
526+ let p = aEnd `plusPtr` negate (sizeOf (undefined :: a))
527+ in go p
528+
529+ where
530+
531+ go p | p < arrStart = K.nil
532+ | otherwise =
533+ let !x = unsafeInlineIO $ do
534+ r <- peek p
535+ touch arrContents
536+ return r
537+ in x `K.cons` go (p `plusPtr` negate (sizeOf (undefined :: a)))
461538
462539-- | Convert an 'Array' into a stream.
463540--
@@ -482,11 +559,11 @@ toStreamRev = SerialT . D.toStreamK . toStreamDRev
482559
483560{-# INLINE_NORMAL foldl' #-}
484561foldl' :: forall a b. Storable a => (b -> a -> b) -> b -> Array a -> b
485−foldl' f z arr = MA.foldl' f z (unsafeThaw arr)
562+foldl' f z arr = runIdentity $ D.foldl' f z $ toStreamD arr
486563
487564{-# INLINE_NORMAL foldr #-}
488565foldr :: Storable a => (a -> b -> b) -> b -> Array a -> b
489−foldr f z arr = MA.foldr f z (unsafeThaw arr)
566+foldr f z arr = runIdentity $ D.foldr f z $ toStreamD arr
490567
491568-- | Create two slices of an array without copying the original array. The
492569-- specified index @i@ is the first index of the second slice.
@@ -497,6 +574,27 @@ splitAt i arr = (unsafeFreeze a, unsafeFreeze b)
497574 where
498575 (a, b) = MA.splitAt i (unsafeThaw arr)
499576
577+-- Use foldr/build fusion to fuse with list consumers
578+-- This can be useful when using the IsList instance
579+{-# INLINE_LATE toListFB #-}
580+toListFB :: forall a b. Storable a => (a -> b -> b) -> b -> Array a -> b
581+toListFB c n Array{..} = go arrStart
582+ where
583+
584+ go p | assert (p <= aEnd) (p == aEnd) = n
585+ go p =
586+ -- unsafeInlineIO allows us to run this in Identity monad for pure
587+ -- toList/foldr case which makes them much faster due to not
588+ -- accumulating the list and fusing better with the pure consumers.
589+ --
590+ -- This should be safe as the array contents are guaranteed to be
591+ -- evaluated/written to before we peek at them.
592+ let !x = unsafeInlineIO $ do
593+ r <- peek p
594+ touch arrContents
595+ return r
596+ in c x (go (p `plusPtr` sizeOf (undefined :: a)))
597+
500598-- | Convert an 'Array' into a list.
501599--
502600-- /Since 0.7.0 (Streamly.Memory.Array)/
@@ -504,7 +602,7 @@ splitAt i arr = (unsafeFreeze a, unsafeFreeze b)
504602-- @since 0.8.0
505603{-# INLINE toList #-}
506604toList :: Storable a => Array a -> [a]
507−toList = MA.toList . unsafeThaw
605+toList s = build (\c n -> toListFB c n s)
508606
509607-------------------------------------------------------------------------------
510608-- Folds