@@ -25,7 +25,7 @@ module Streamly.Array.Types
2525 -- * Construction
2626 , unsafeInlineIO
2727 , withNewArray
28− , unsafeNew
28+ , newArray
2929 , unsafeAppend
3030 , shrinkToFit
3131 , memcpy
@@ -40,6 +40,7 @@ module Streamly.Array.Types
4040 , flattenArraysRev
4141
4242 -- * Elimination
43+ , unsafeIndexIO
4344 , unsafeIndex
4445 , length
4546 , foldl'
@@ -59,6 +60,7 @@ where
5960import Control.Exception (assert)
6061import Control.DeepSeq (NFData(..))
6162import Control.Monad (when)
63+import Control.Monad.IO.Class (MonadIO(..))
6264import Data.Functor.Identity (runIdentity)
6365import Data.Word (Word8)
6466import Foreign.C.String (CString)
@@ -74,7 +76,7 @@ import Text.Read (readPrec, readListPrec, readListPrecDefault)
7476import GHC.Base (Addr#, realWorld#)
7577import GHC.Exts (IsList, IsString(..))
7678import GHC.ForeignPtr (mallocPlainForeignPtrAlignedBytes, newForeignPtr_)
77−import GHC.IO (IO(IO), unsafeDupablePerformIO)
79+import GHC.IO (IO(IO), unsafePerformIO)
7880import GHC.Ptr (Ptr(..))
7981
8082import Streamly.Fold.Types (Fold(..))
@@ -84,6 +86,10 @@ import qualified Streamly.Streams.StreamD.Type as D
8486import qualified Streamly.Streams.StreamK as K
8587import qualified GHC.Exts as Exts
8688
89+#ifdef DEVBUILD
90+import qualified Data.Foldable as F
91+#endif
92+
8793-------------------------------------------------------------------------------
8894-- Design Notes
8995-------------------------------------------------------------------------------
@@ -178,9 +184,9 @@ unsafeInlineIO (IO m) = case m realWorld# of (# _, r #) -> r
178184--
179185-- Note that this is internal routine, the reference to this array cannot be
180186-- given out until the array has been written to and frozen.
181−{-# INLINE unsafeNew #-}
182−unsafeNew :: forall a. Storable a => Int -> IO (Array a)
183−unsafeNew count = do
187+{-# INLINE newArray #-}
188+newArray :: forall a. Storable a => Int -> IO (Array a)
189+newArray count = do
184190 let size = count * sizeOf (undefined :: a)
185191 fptr <- mallocPlainForeignPtrAlignedBytes size (alignment (undefined :: a))
186192 let p = unsafeForeignPtrToPtr fptr
@@ -195,7 +201,7 @@ unsafeNew count = do
195201{-# INLINE withNewArray #-}
196202withNewArray :: forall a. Storable a => Int -> (Ptr a -> IO ()) -> IO (Array a)
197203withNewArray count f = do
198− arr <- unsafeNew count
204+ arr <- newArray count
199205 withForeignPtr (aStart arr) $ \p -> f p >> return arr
200206
201207-- XXX grow the array when we are beyond bound.
@@ -263,18 +269,23 @@ _fromCStringAddrUnsafe addr# = do
263269-- Elimination
264270-------------------------------------------------------------------------------
265271
272+-- | Return element at the specified index without checking the bounds.
273+--
274+-- Unsafe because it does not check the bounds of the array.
275+{-# INLINE_NORMAL unsafeIndexIO #-}
276+unsafeIndexIO :: forall a. Storable a => Array a -> Int -> IO a
277+unsafeIndexIO Array {..} i =
278+ withForeignPtr aStart $ \p -> do
279+ let elemSize = sizeOf (undefined :: a)
280+ elemOff = p `plusPtr` (elemSize * i)
281+ assert (i >= 0 && elemOff `plusPtr` elemSize <= aEnd)
282+ (return ())
283+ peek elemOff
284+
266285-- | Return element at the specified index without checking the bounds.
267286{-# INLINE_NORMAL unsafeIndex #-}
268287unsafeIndex :: forall a. Storable a => Array a -> Int -> a
269−unsafeIndex Array {..} i =
270− let !r = unsafeInlineIO $
271− withForeignPtr aStart $ \p -> do
272− let elemSize = sizeOf (undefined :: a)
273− elemOff = p `plusPtr` (elemSize * i)
274− assert (i >= 0 && elemOff `plusPtr` elemSize <= aEnd)
275− (return ())
276− peek elemOff
277− in r
288+unsafeIndex arr i = let !r = unsafeInlineIO $ unsafeIndexIO arr i in r
278289
279290-- | /O(1)/ Get the length of the array.
280291--
@@ -297,6 +308,12 @@ toStreamD Array{..} =
297308 {-# INLINE_LATE step #-}
298309 step _ p | p == aEnd = return D.Stop
299310 step _ p = do
311+ -- unsafeInlineIO allows us to run this in Identity monad for pure
312+ -- toList/foldr case which makes them much faster due to not
313+ -- accumulating the list and fusing better with the pure consumers.
314+ --
315+ -- This should be safe as the array contents are guaranteed to be
316+ -- evaluated/written to before we peek at them.
300317 let !x = unsafeInlineIO $ do
301318 r <- peek p
302319 touchForeignPtr aStart
@@ -314,6 +331,7 @@ toStreamDRev Array{..} =
314331 {-# INLINE_LATE step #-}
315332 step _ p | p < unsafeForeignPtrToPtr aStart = return D.Stop
316333 step _ p = do
334+ -- See comments in toStreamD for why we use unsafeInlineIO
317335 let !x = unsafeInlineIO $ do
318336 r <- peek p
319337 touchForeignPtr aStart
@@ -337,18 +355,16 @@ foldr f z arr = runIdentity $ D.foldr f z $ toStreamD arr
337355--
338356-- @since 0.7.0
339357{-# INLINE_NORMAL toArrayN #-}
340−toArrayN :: forall m a. (Monad m, Storable a) => Int -> Fold m a (Array a)
358+toArrayN :: forall m a. (MonadIO m, Storable a) => Int -> Fold m a (Array a)
341359toArrayN n = Fold step initial extract
342360
343361 where
344362
345− initial =
346− let !arr = unsafeDupablePerformIO $ unsafeNew n
347− in return arr
363+ initial = liftIO $ newArray n
348364 step arr@(Array _ end bound) _ | end == bound = return arr
349− step (Array start end bound) x =
350− let !_ = unsafeInlineIO $ poke end x
351− in return $ Array start (end `plusPtr` sizeOf (undefined :: a)) bound
365+ step (Array start end bound) x = do
366+ liftIO $ poke end x
367+ return $ Array start (end `plusPtr` sizeOf (undefined :: a)) bound
352368 extract = return
353369
354370{-
@@ -366,23 +382,23 @@ toArray = Fold step begin done
366382-}
367383
368384{-# INLINE_NORMAL fromStreamDN #-}
369−fromStreamDN :: forall m a. (Monad m, Storable a)
385+fromStreamDN :: forall m a. (MonadIO m, Storable a)
370386 => Int -> D.Stream m a -> m (Array a)
371387fromStreamDN limit str = do
372− let !arr = unsafeDupablePerformIO $ unsafeNew limit
373− end <- D.foldl' write (aEnd arr) $ D.take limit str
388+ arr <- liftIO $ newArray limit
389+ end <- D.foldlM' write (aEnd arr) $ D.take limit str
374390 return $ arr {aEnd = end}
375391
376392 where
377393
378− write ptr x =
379− let !_ = unsafeInlineIO $ poke ptr x
380− in ptr `plusPtr` sizeOf (undefined :: a)
394+ write ptr x = do
395+ liftIO $ poke ptr x
396+ return $ ptr `plusPtr` sizeOf (undefined :: a)
381397
382398-- | @fromStreamArraysOf n stream@ groups the input stream into a stream of
383399-- arrays of size n.
384400{-# INLINE fromStreamDArraysOf #-}
385−fromStreamDArraysOf :: (Monad m, Storable a)
401+fromStreamDArraysOf :: (MonadIO m, Storable a)
386402 => Int -> D.Stream m a -> D.Stream m (Array a)
387403fromStreamDArraysOf n str = D.groupsOf n (toArrayN n) str
388404
@@ -393,7 +409,7 @@ data FlattenState s a =
393409 | InnerLoop s (ForeignPtr a) (Ptr a) (Ptr a)
394410
395411{-# INLINE_NORMAL flattenArrays #-}
396−flattenArrays :: forall m a. (Monad m, Storable a)
412+flattenArrays :: forall m a. (MonadIO m, Storable a)
397413 => D.Stream m (Array a) -> D.Stream m a
398414flattenArrays (D.Stream step state) = D.Stream step' (OuterLoop state)
399415
@@ -413,7 +429,7 @@ flattenArrays (D.Stream step state) = D.Stream step' (OuterLoop state)
413429 return $ D.Skip $ OuterLoop st
414430
415431 step' _ (InnerLoop st startf p end) = do
416− let !x = unsafeInlineIO $ do
432+ x <- liftIO $ do
417433 r <- peek p
418434 touchForeignPtr startf
419435 return r
@@ -421,7 +437,7 @@ flattenArrays (D.Stream step state) = D.Stream step' (OuterLoop state)
421437 (p `plusPtr` (sizeOf (undefined :: a))) end)
422438
423439{-# INLINE_NORMAL flattenArraysRev #-}
424−flattenArraysRev :: forall m a. (Monad m, Storable a)
440+flattenArraysRev :: forall m a. (MonadIO m, Storable a)
425441 => D.Stream m (Array a) -> D.Stream m a
426442flattenArraysRev (D.Stream step state) = D.Stream step' (OuterLoop state)
427443
@@ -442,7 +458,7 @@ flattenArraysRev (D.Stream step state) = D.Stream step' (OuterLoop state)
442458 return $ D.Skip $ OuterLoop st
443459
444460 step' _ (InnerLoop st startf p end) = do
445− let !x = unsafeInlineIO $ do
461+ x <- liftIO $ do
446462 r <- peek p
447463 touchForeignPtr startf
448464 return r
@@ -454,7 +470,7 @@ flattenArraysRev (D.Stream step state) = D.Stream step' (OuterLoop state)
454470-- all the arrays.
455471--
456472{-# INLINE fromStreamD #-}
457−fromStreamD :: (Monad m, Storable a) => D.Stream m a -> m (Array a)
473+fromStreamD :: (MonadIO m, Storable a) => D.Stream m a -> m (Array a)
458474fromStreamD m = do
459475 let s = fromStreamDArraysOf defaultChunkSize m
460476 buffered <- D.foldr K.cons K.nil s
@@ -479,14 +495,14 @@ instance (Show a, Storable a) => Show (Array a) where
479495-- @since 0.7.0
480496{-# INLINABLE fromListN #-}
481497fromListN :: Storable a => Int -> [a] -> Array a
482−fromListN n xs = runIdentity $ fromStreamDN n $ D.fromList xs
498+fromListN n xs = unsafePerformIO $ fromStreamDN n $ D.fromList xs
483499
484500-- | Create an 'Array' from a list. The list must be of finite size.
485501--
486502-- @since 0.7.0
487503{-# INLINABLE fromList #-}
488504fromList :: Storable a => [a] -> Array a
489−fromList xs = runIdentity $ fromStreamD $ D.fromList xs
505+fromList xs = unsafePerformIO $ fromStreamD $ D.fromList xs
490506
491507instance (Storable a, Read a, Show a) => Read (Array a) where
492508 {-# INLINE readPrec #-}
@@ -540,7 +556,7 @@ instance (Storable a, NFData a) => NFData (Array a) where
540556
541557instance (Storable a, Ord a) => Ord (Array a) where
542558 {-# INLINE compare #-}
543− compare arr1 arr2 = runIdentity $
559+ compare arr1 arr2 = unsafePerformIO $
544560 D.cmpBy compare (toStreamD arr1) (toStreamD arr2)
545561
546562 -- Default definitions defined in base do not have an INLINE on them, so we
@@ -570,7 +586,7 @@ instance (Storable a, Ord a) => Ord (Array a) where
570586-- make the Foldable instance possible though it is much slower (7x slower).
571587--
572588{-# INLINE_NORMAL toStreamD_ #-}
573−toStreamD_ :: forall m a. Monad m => Int -> Array a -> D.Stream m a
589+toStreamD_ :: forall m a. MonadIO m => Int -> Array a -> D.Stream m a
574590toStreamD_ size Array{..} =
575591 let p = unsafeForeignPtrToPtr aStart
576592 in D.Stream step p
@@ -580,7 +596,7 @@ toStreamD_ size Array{..} =
580596 {-# INLINE_LATE step #-}
581597 step _ p | p == aEnd = return D.Stop
582598 step _ p = do
583− let !x = unsafeInlineIO $ do
599+ x <- liftIO $ do
584600 r <- peek p
585601 touchForeignPtr aStart
586602 return r
@@ -590,7 +606,7 @@ toStreamD_ size Array{..} =
590606_foldr :: forall a b. (a -> b -> b) -> b -> Array a -> b
591607_foldr f z arr@Array {..} =
592608 let !n = sizeOf (undefined :: a)
593− in runIdentity $ D.foldr f z $ toStreamD_ n arr
609+ in unsafePerformIO $ D.foldr f z $ toStreamD_ n arr
594610
595611-- | Note that the 'Foldable' instance is 7x slower than the direct
596612-- operations.