@@ -25,7 +25,7 @@ module Streamly.Array.Types
25 -- * Construction
25 -- * Construction
26 , unsafeInlineIO
26 , unsafeInlineIO
27 , withNewArray
27 , withNewArray
28− , unsafeNew
28+ , newArray
29 , unsafeAppend
29 , unsafeAppend
30 , shrinkToFit
30 , shrinkToFit
@@ -40,6 +40,7 @@ module Streamly.Array.Types
40 , flattenArraysRev
40 , flattenArraysRev
42 -- * Elimination
42 -- * Elimination
43 , unsafeIndex
44 , unsafeIndex
@@ -59,6 +60,7 @@ where
59import Control.Exception (assert)
60import Control.Exception (assert)
60import Control.DeepSeq (NFData(..))
61import Control.DeepSeq (NFData(..))
61import Control.Monad (when)
62import Control.Monad (when)
63+import Control.Monad.IO.Class (MonadIO(..))
62import Data.Functor.Identity (runIdentity)
64import Data.Functor.Identity (runIdentity)
63import Data.Word (Word8)
65import Data.Word (Word8)
64import Foreign.C.String (CString)
66import Foreign.C.String (CString)
@@ -74,7 +76,7 @@ import Text.Read (readPrec, readListPrec, readListPrecDefault)
74import GHC.Base (Addr#, realWorld#)
76import GHC.Base (Addr#, realWorld#)
75import GHC.Exts (IsList, IsString(..))
77import GHC.Exts (IsList, IsString(..))
76import GHC.ForeignPtr (mallocPlainForeignPtrAlignedBytes, newForeignPtr_)
78import GHC.ForeignPtr (mallocPlainForeignPtrAlignedBytes, newForeignPtr_)
77−import GHC.IO (IO(IO), unsafeDupablePerformIO)
79+import GHC.IO (IO(IO), unsafePerformIO)
78import GHC.Ptr (Ptr(..))
80import GHC.Ptr (Ptr(..))
80import Streamly.Fold.Types (Fold(..))
82import Streamly.Fold.Types (Fold(..))
@@ -84,6 +86,10 @@ import qualified Streamly.Streams.StreamD.Type as D
84import qualified Streamly.Streams.StreamK as K
86import qualified Streamly.Streams.StreamK as K
85import qualified GHC.Exts as Exts
87import qualified GHC.Exts as Exts
90+import qualified Data.Foldable as F
87-------------------------------------------------------------------------------
93-------------------------------------------------------------------------------
88-- Design Notes
94-- Design Notes
89-------------------------------------------------------------------------------
95-------------------------------------------------------------------------------
@@ -178,9 +184,9 @@ unsafeInlineIO (IO m) = case m realWorld# of (# _, r #) -> r
179-- Note that this is internal routine, the reference to this array cannot be
185-- Note that this is internal routine, the reference to this array cannot be
180-- given out until the array has been written to and frozen.
186-- given out until the array has been written to and frozen.
181−{-# INLINE unsafeNew #-}
187+{-# INLINE newArray #-}
182−unsafeNew :: forall a. Storable a => Int -> IO (Array a)
188+newArray :: forall a. Storable a => Int -> IO (Array a)
183−unsafeNew count = do
189+newArray count = do
184 let size = count * sizeOf (undefined :: a)
190 let size = count * sizeOf (undefined :: a)
185 fptr <- mallocPlainForeignPtrAlignedBytes size (alignment (undefined :: a))
191 fptr <- mallocPlainForeignPtrAlignedBytes size (alignment (undefined :: a))
186 let p = unsafeForeignPtrToPtr fptr
192 let p = unsafeForeignPtrToPtr fptr
@@ -195,7 +201,7 @@ unsafeNew count = do
195{-# INLINE withNewArray #-}
201{-# INLINE withNewArray #-}
196withNewArray :: forall a. Storable a => Int -> (Ptr a -> IO ()) -> IO (Array a)
202withNewArray :: forall a. Storable a => Int -> (Ptr a -> IO ()) -> IO (Array a)
197withNewArray count f = do
203withNewArray count f = do
198− arr <- unsafeNew count
204+ arr <- newArray count
199 withForeignPtr (aStart arr) $ \p -> f p >> return arr
205 withForeignPtr (aStart arr) $ \p -> f p >> return arr
201-- XXX grow the array when we are beyond bound.
207-- XXX grow the array when we are beyond bound.
@@ -263,18 +269,23 @@ _fromCStringAddrUnsafe addr# = do
263-- Elimination
269-- Elimination
264-------------------------------------------------------------------------------
270-------------------------------------------------------------------------------
272+-- | Return element at the specified index without checking the bounds.
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)
266-- | Return element at the specified index without checking the bounds.
285-- | Return element at the specified index without checking the bounds.
267{-# INLINE_NORMAL unsafeIndex #-}
286{-# INLINE_NORMAL unsafeIndex #-}
268unsafeIndex :: forall a. Storable a => Array a -> Int -> a
287unsafeIndex :: forall a. Storable a => Array a -> Int -> a
269−unsafeIndex Array {..} i =
270− let !r = unsafeInlineIO $
288+unsafeIndex arr i = let !r = unsafeInlineIO $ unsafeIndexIO arr i in r
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)
279-- | /O(1)/ Get the length of the array.
290-- | /O(1)/ Get the length of the array.
@@ -297,6 +308,12 @@ toStreamD Array{..} =
297 {-# INLINE_LATE step #-}
308 {-# INLINE_LATE step #-}
298 step _ p | p == aEnd = return D.Stop
309 step _ p | p == aEnd = return D.Stop
299 step _ p = do
310 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.
315+ -- This should be safe as the array contents are guaranteed to be
316+ -- evaluated/written to before we peek at them.
300 let !x = unsafeInlineIO $ do
317 let !x = unsafeInlineIO $ do
301 r <- peek p
318 r <- peek p
302 touchForeignPtr aStart
319 touchForeignPtr aStart
@@ -314,6 +331,7 @@ toStreamDRev Array{..} =
314 {-# INLINE_LATE step #-}
331 {-# INLINE_LATE step #-}
315 step _ p | p < unsafeForeignPtrToPtr aStart = return D.Stop
332 step _ p | p < unsafeForeignPtrToPtr aStart = return D.Stop
316 step _ p = do
333 step _ p = do
334+ -- See comments in toStreamD for why we use unsafeInlineIO
317 let !x = unsafeInlineIO $ do
335 let !x = unsafeInlineIO $ do
318 r <- peek p
336 r <- peek p
319 touchForeignPtr aStart
337 touchForeignPtr aStart
@@ -337,18 +355,16 @@ foldr f z arr = runIdentity $ D.foldr f z $ toStreamD arr
338-- @since 0.7.0
356-- @since 0.7.0
339{-# INLINE_NORMAL toArrayN #-}
357{-# 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)
341toArrayN n = Fold step initial extract
359toArrayN n = Fold step initial extract
345− initial =
363+ initial = liftIO $ newArray n
346− let !arr = unsafeDupablePerformIO $ unsafeNew n
348 step arr@(Array _ end bound) _ | end == bound = return arr
364 step arr@(Array _ end bound) _ | end == bound = return arr
349− step (Array start end bound) x =
365+ step (Array start end bound) x = do
350− let !_ = unsafeInlineIO $ poke end x
366+ liftIO $ poke end x
351− in return $ Array start (end `plusPtr` sizeOf (undefined :: a)) bound
367+ return $ Array start (end `plusPtr` sizeOf (undefined :: a)) bound
352 extract = return
368 extract = return
@@ -366,23 +382,23 @@ toArray = Fold step begin done
368{-# INLINE_NORMAL fromStreamDN #-}
384{-# INLINE_NORMAL fromStreamDN #-}
369−fromStreamDN :: forall m a. (Monad m, Storable a)
385+fromStreamDN :: forall m a. (MonadIO m, Storable a)
370 => Int -> D.Stream m a -> m (Array a)
386 => Int -> D.Stream m a -> m (Array a)
371fromStreamDN limit str = do
387fromStreamDN limit str = do
372− let !arr = unsafeDupablePerformIO $ unsafeNew limit
388+ arr <- liftIO $ newArray limit
373− end <- D.foldl' write (aEnd arr) $ D.take limit str
389+ end <- D.foldlM' write (aEnd arr) $ D.take limit str
374 return $ arr {aEnd = end}
390 return $ arr {aEnd = end}
378− write ptr x =
394+ write ptr x = do
379− let !_ = unsafeInlineIO $ poke ptr x
395+ liftIO $ poke ptr x
380− in ptr `plusPtr` sizeOf (undefined :: a)
396+ return $ ptr `plusPtr` sizeOf (undefined :: a)
382-- | @fromStreamArraysOf n stream@ groups the input stream into a stream of
398-- | @fromStreamArraysOf n stream@ groups the input stream into a stream of
383-- arrays of size n.
399-- arrays of size n.
384{-# INLINE fromStreamDArraysOf #-}
400{-# INLINE fromStreamDArraysOf #-}
385−fromStreamDArraysOf :: (Monad m, Storable a)
401+fromStreamDArraysOf :: (MonadIO m, Storable a)
386 => Int -> D.Stream m a -> D.Stream m (Array a)
402 => Int -> D.Stream m a -> D.Stream m (Array a)
387fromStreamDArraysOf n str = D.groupsOf n (toArrayN n) str
403fromStreamDArraysOf n str = D.groupsOf n (toArrayN n) str
@@ -393,7 +409,7 @@ data FlattenState s a =
393 | InnerLoop s (ForeignPtr a) (Ptr a) (Ptr a)
409 | InnerLoop s (ForeignPtr a) (Ptr a) (Ptr a)
395{-# INLINE_NORMAL flattenArrays #-}
411{-# INLINE_NORMAL flattenArrays #-}
396−flattenArrays :: forall m a. (Monad m, Storable a)
412+flattenArrays :: forall m a. (MonadIO m, Storable a)
397 => D.Stream m (Array a) -> D.Stream m a
413 => D.Stream m (Array a) -> D.Stream m a
398flattenArrays (D.Stream step state) = D.Stream step' (OuterLoop state)
414flattenArrays (D.Stream step state) = D.Stream step' (OuterLoop state)
@@ -413,7 +429,7 @@ flattenArrays (D.Stream step state) = D.Stream step' (OuterLoop state)
413 return $ D.Skip $ OuterLoop st
429 return $ D.Skip $ OuterLoop st
415 step' _ (InnerLoop st startf p end) = do
431 step' _ (InnerLoop st startf p end) = do
416− let !x = unsafeInlineIO $ do
432+ x <- liftIO $ do
417 r <- peek p
433 r <- peek p
418 touchForeignPtr startf
434 touchForeignPtr startf
@@ -421,7 +437,7 @@ flattenArrays (D.Stream step state) = D.Stream step' (OuterLoop state)
421 (p `plusPtr` (sizeOf (undefined :: a))) end)
437 (p `plusPtr` (sizeOf (undefined :: a))) end)
423{-# INLINE_NORMAL flattenArraysRev #-}
439{-# INLINE_NORMAL flattenArraysRev #-}
424−flattenArraysRev :: forall m a. (Monad m, Storable a)
440+flattenArraysRev :: forall m a. (MonadIO m, Storable a)
425 => D.Stream m (Array a) -> D.Stream m a
441 => D.Stream m (Array a) -> D.Stream m a
426flattenArraysRev (D.Stream step state) = D.Stream step' (OuterLoop state)
442flattenArraysRev (D.Stream step state) = D.Stream step' (OuterLoop state)
@@ -442,7 +458,7 @@ flattenArraysRev (D.Stream step state) = D.Stream step' (OuterLoop state)
442 return $ D.Skip $ OuterLoop st
458 return $ D.Skip $ OuterLoop st
444 step' _ (InnerLoop st startf p end) = do
460 step' _ (InnerLoop st startf p end) = do
445− let !x = unsafeInlineIO $ do
461+ x <- liftIO $ do
446 r <- peek p
462 r <- peek p
447 touchForeignPtr startf
463 touchForeignPtr startf
@@ -454,7 +470,7 @@ flattenArraysRev (D.Stream step state) = D.Stream step' (OuterLoop state)
454-- all the arrays.
470-- all the arrays.
456{-# INLINE fromStreamD #-}
472{-# 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)
458fromStreamD m = do
474fromStreamD m = do
459 let s = fromStreamDArraysOf defaultChunkSize m
475 let s = fromStreamDArraysOf defaultChunkSize m
460 buffered <- D.foldr K.cons K.nil s
476 buffered <- D.foldr K.cons K.nil s
@@ -479,14 +495,14 @@ instance (Show a, Storable a) => Show (Array a) where
479-- @since 0.7.0
495-- @since 0.7.0
480{-# INLINABLE fromListN #-}
496{-# INLINABLE fromListN #-}
481fromListN :: Storable a => Int -> [a] -> Array a
497fromListN :: 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
484-- | Create an 'Array' from a list. The list must be of finite size.
500-- | Create an 'Array' from a list. The list must be of finite size.
486-- @since 0.7.0
502-- @since 0.7.0
487{-# INLINABLE fromList #-}
503{-# INLINABLE fromList #-}
488fromList :: Storable a => [a] -> Array a
504fromList :: Storable a => [a] -> Array a
489−fromList xs = runIdentity $ fromStreamD $ D.fromList xs
505+fromList xs = unsafePerformIO $ fromStreamD $ D.fromList xs
491instance (Storable a, Read a, Show a) => Read (Array a) where
507instance (Storable a, Read a, Show a) => Read (Array a) where
492 {-# INLINE readPrec #-}
508 {-# INLINE readPrec #-}
@@ -540,7 +556,7 @@ instance (Storable a, NFData a) => NFData (Array a) where
541instance (Storable a, Ord a) => Ord (Array a) where
557instance (Storable a, Ord a) => Ord (Array a) where
542 {-# INLINE compare #-}
558 {-# INLINE compare #-}
543− compare arr1 arr2 = runIdentity $
559+ compare arr1 arr2 = unsafePerformIO $
544 D.cmpBy compare (toStreamD arr1) (toStreamD arr2)
560 D.cmpBy compare (toStreamD arr1) (toStreamD arr2)
546 -- Default definitions defined in base do not have an INLINE on them, so we
562 -- 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
570-- make the Foldable instance possible though it is much slower (7x slower).
586-- make the Foldable instance possible though it is much slower (7x slower).
572{-# INLINE_NORMAL toStreamD_ #-}
588{-# 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
574toStreamD_ size Array{..} =
590toStreamD_ size Array{..} =
575 let p = unsafeForeignPtrToPtr aStart
591 let p = unsafeForeignPtrToPtr aStart
576 in D.Stream step p
592 in D.Stream step p
@@ -580,7 +596,7 @@ toStreamD_ size Array{..} =
580 {-# INLINE_LATE step #-}
596 {-# INLINE_LATE step #-}
581 step _ p | p == aEnd = return D.Stop
597 step _ p | p == aEnd = return D.Stop
582 step _ p = do
598 step _ p = do
583− let !x = unsafeInlineIO $ do
599+ x <- liftIO $ do
584 r <- peek p
600 r <- peek p
585 touchForeignPtr aStart
601 touchForeignPtr aStart
@@ -590,7 +606,7 @@ toStreamD_ size Array{..} =
590_foldr :: forall a b. (a -> b -> b) -> b -> Array a -> b
606_foldr :: forall a b. (a -> b -> b) -> b -> Array a -> b
591_foldr f z arr@Array {..} =
607_foldr f z arr@Array {..} =
592 let !n = sizeOf (undefined :: a)
608 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
595-- | Note that the 'Foldable' instance is 7x slower than the direct
611-- | Note that the 'Foldable' instance is 7x slower than the direct
596-- operations.
612-- operations.