@@ -35,6 +35,7 @@ module Streamly.Internal.Data.Array.Mut.Type
3535 , touch
3636 , pin
3737 , unpin
38+ , isPinned
3839
3940 -- * Constructing and Writing
4041 -- ** Construction
@@ -48,15 +49,17 @@ module Streamly.Internal.Data.Array.Mut.Type
4849 , newArrayWith
4950
5051 -- *** Initialized Arrays
51− , withNewArrayUnsafe
52+ , withNewArrayUnsafePinned
5253
5354 -- *** From streams
5455 , ArrayUnsafe (..)
5556 , writeNWithUnsafe
5657 , writeNWith
5758 , writeNUnsafe
59+ , writeNUnsafeAs
5860 , writeN
59− , writeNAligned
61+ , writeNAs
62+ , pinnedWriteNAligned
6063
6164 , writeWith
6265 , write
@@ -185,6 +188,7 @@ module Streamly.Internal.Data.Array.Mut.Type
185188
186189 -- ** Construct from streams
187190 , chunksOf
191+ , chunksOfAs
188192 , arrayStreamKFromStreamD
189193 , writeChunks
190194
@@ -236,6 +240,7 @@ import Foreign.Ptr (plusPtr, minusPtr, nullPtr)
236240import Streamly.Internal.Data.Unbox
237241 ( MutableByteArray(..)
238242 , Unbox(..)
243+ , PinnedState(..)
239244 , getMutableByteArray#
240245 , touch
241246 )
@@ -366,6 +371,10 @@ unpin arr@MutArray{..} = do
366371 contents <- Unboxed.unpin arrContents
367372 return $ arr {arrContents = contents}
368373
374+{-# INLINE isPinned #-}
375+isPinned :: MutArray a -> Bool
376+isPinned MutArray{..} = Unboxed.isPinned arrContents
377+
369378-------------------------------------------------------------------------------
370379-- Construction
371380-------------------------------------------------------------------------------
@@ -412,6 +421,20 @@ nil ::
412421 MutArray a
413422nil = MutArray Unboxed.nil 0 0 0
414423
424+{-# INLINE newBytesAs #-}
425+newBytesAs :: MonadIO m =>
426+#ifdef DEVBUILD
427+ Unbox a =>
428+#endif
429+ PinnedState -> Int -> m (MutArray a)
430+newBytesAs ps bytes = do
431+ contents <- liftIO $ Unboxed.newBytes ps bytes
432+ return $ MutArray
433+ { arrContents = contents
434+ , arrStart = 0
435+ , arrEnd = 0
436+ , arrBound = bytes
437+ }
415438
416439-- | Allocates a pinned empty array that can hold 'count' items. The memory of
417440-- the array is uninitialized and the allocation is aligned as per the
@@ -424,14 +447,7 @@ newPinnedBytes :: MonadIO m =>
424447 Unbox a =>
425448#endif
426449 Int -> m (MutArray a)
427−newPinnedBytes bytes = do
428− contents <- liftIO $ Unboxed.newPinnedBytes bytes
429− return $ MutArray
430− { arrContents = contents
431− , arrStart = 0
432− , arrEnd = 0
433− , arrBound = bytes
434− }
450+newPinnedBytes = newBytesAs Pinned
435451
436452-- | Like 'newArrayWith' but using an allocator is a pinned memory allocator and
437453-- the alignment is dictated by the 'Unboxed' instance of the type.
@@ -442,6 +458,13 @@ newAlignedPinned :: (MonadIO m, Unbox a) => Int -> Int -> m (MutArray a)
442458newAlignedPinned =
443459 newArrayWith (\s a -> liftIO $ Unboxed.newAlignedPinnedBytes s a)
444460
461+{-# INLINE newAs #-}
462+newAs :: (MonadIO m, Unbox a) => PinnedState -> Int -> m (MutArray a)
463+newAs ps =
464+ newArrayWith
465+ (\s _ -> liftIO $ Unboxed.newBytes ps s)
466+ (error "new: alignment is not used in unpinned arrays.")
467+
445468-- XXX can unaligned allocation be more efficient when alignment is not needed?
446469--
447470-- | Allocates an empty pinned array that can hold 'count' items. The memory of
@@ -450,20 +473,14 @@ newAlignedPinned =
450473--
451474{-# INLINE newPinned #-}
452475newPinned :: forall m a. (MonadIO m, Unbox a) => Int -> m (MutArray a)
453−newPinned =
454− newArrayWith
455− (\s _ -> liftIO $ Unboxed.newPinnedBytes s)
456− (error "newPinned: alignSize is not used")
476+newPinned = newAs Pinned
457477
458478-- | Allocates an empty unpinned array that can hold 'count' items. The memory
459479-- of the array is uninitialized.
460480--
461481{-# INLINE new #-}
462482new :: (MonadIO m, Unbox a) => Int -> m (MutArray a)
463−new =
464− newArrayWith
465− (\s _ -> liftIO $ Unboxed.newUnpinnedBytes s)
466− (error "new: alignment is not used in unpinned arrays.")
483+new = newAs Unpinned
467484
468485-- XXX This should create a full length uninitialzed array so that the pointer
469486-- can be used.
@@ -472,10 +489,10 @@ new =
472489-- the array start pointer.
473490--
474491-- /Internal/
475−{-# INLINE withNewArrayUnsafe #-}
476−withNewArrayUnsafe ::
492+{-# INLINE withNewArrayUnsafePinned #-}
493+withNewArrayUnsafePinned ::
477494 (MonadIO m, Unbox a) => Int -> (Ptr a -> m ()) -> m (MutArray a)
478−withNewArrayUnsafe count f = do
495+withNewArrayUnsafePinned count f = do
479496 arr <- newPinned count
480497 asPtrUnsafe arr
481498 $ \p -> f p >> return arr
@@ -736,7 +753,10 @@ reallocExplicit elemSize newCapacityInBytes MutArray{..} = do
736753
737754 -- Allocate new array
738755 let newCapMaxInBytes = roundUpLargeArray newCapacityInBytes
739− contents <- Unboxed.newPinnedBytes newCapMaxInBytes
756+ contents <-
757+ if Unboxed.isPinned arrContents
758+ then Unboxed.newPinnedBytes newCapMaxInBytes
759+ else Unboxed.newUnpinnedBytes newCapMaxInBytes
740760 let !(MutableByteArray mbarrFrom#) = arrContents
741761 !(MutableByteArray mbarrTo#) = contents
742762
@@ -1299,20 +1319,10 @@ data GroupState s contents start end bound
12991319 contents start end bound (GroupState s contents start end bound)
13001320 | GroupFinish
13011321
1302−-- | @chunksOf n stream@ groups the input stream into a stream of
1303−-- arrays of size n.
1304−--
1305−-- @chunksOf n = StreamD.foldMany (MutArray.writeN n)@
1306−--
1307−-- /Pre-release/
1308−{-# INLINE_NORMAL chunksOf #-}
1309−chunksOf :: forall m a. (MonadIO m, Unbox a)
1310− => Int -> D.Stream m a -> D.Stream m (MutArray a)
1311−-- XXX the idiomatic implementation leads to large regression in the D.reverse'
1312−-- benchmark. It seems it has difficulty producing optimized code when
1313−-- converting to StreamK. Investigate GHC optimizations.
1314−-- chunksOf n = D.foldMany (writeN n)
1315−chunksOf n (D.Stream step state) =
1322+{-# INLINE_NORMAL chunksOfAs #-}
1323+chunksOfAs :: forall m a. (MonadIO m, Unbox a)
1324+ => PinnedState -> Int -> D.Stream m a -> D.Stream m (MutArray a)
1325+chunksOfAs ps n (D.Stream step state) =
13161326 D.Stream step' (GroupStart state)
13171327
13181328 where
@@ -1324,7 +1334,7 @@ chunksOf n (D.Stream step state) =
13241334 error $ "Streamly.Internal.Data.MutArray.Mut.Type.chunksOf: "
13251335 ++ "the size of arrays [" ++ show n
13261336 ++ "] must be a natural number"
1327− (MutArray contents start end bound :: MutArray a) <- liftIO $ newPinned n
1337+ (MutArray contents start end bound :: MutArray a) <- newAs ps n
13281338 return $ D.Skip (GroupBuffer st contents start end bound)
13291339
13301340 step' gst (GroupBuffer st contents start end bound) = do
@@ -1350,6 +1360,22 @@ chunksOf n (D.Stream step state) =
13501360
13511361 step' _ GroupFinish = return D.Stop
13521362
1363+-- | @chunksOf n stream@ groups the input stream into a stream of
1364+-- arrays of size n.
1365+--
1366+-- @chunksOf n = StreamD.foldMany (MutArray.writeN n)@
1367+--
1368+-- /Pre-release/
1369+{-# INLINE_NORMAL chunksOf #-}
1370+chunksOf :: forall m a. (MonadIO m, Unbox a)
1371+ => Int -> D.Stream m a -> D.Stream m (MutArray a)
1372+-- XXX the idiomatic implementation leads to large regression in the D.reverse'
1373+-- benchmark. It seems it has difficulty producing optimized code when
1374+-- converting to StreamK. Investigate GHC optimizations.
1375+-- chunksOf n = D.foldMany (writeN n)
1376+chunksOf = chunksOfAs Unpinned
1377+
1378+-- XXX This should take a PinnedState
13531379-- XXX buffer to a list instead?
13541380-- | Buffer the stream into arrays in memory.
13551381{-# INLINE arrayStreamKFromStreamD #-}
@@ -1751,18 +1777,23 @@ writeNWithUnsafe alloc n = fromArrayUnsafe <$> FL.foldlM' step initial
17511777 return
17521778 $ ArrayUnsafe contents start (INDEX_NEXT(end,a))
17531779
1780+{-# INLINE_NORMAL writeNUnsafeAs #-}
1781+writeNUnsafeAs :: forall m a. (MonadIO m, Unbox a)
1782+ => PinnedState -> Int -> Fold m a (MutArray a)
1783+writeNUnsafeAs ps = writeNWithUnsafe (newAs ps)
1784+
17541785-- | Like 'writeN' but does not check the array bounds when writing. The fold
17551786-- driver must not call the step function more than 'n' times otherwise it will
17561787-- corrupt the memory and crash. This function exists mainly because any
17571788-- conditional in the step function blocks fusion causing 10x performance
17581789-- slowdown.
17591790--
1760−-- >>> writeNUnsafe = MutArray.writeNWithUnsafe MutArray.newPinned
1791+-- >>> writeNUnsafe = MutArray.writeNWithUnsafe MutArray.new
17611792--
17621793{-# INLINE_NORMAL writeNUnsafe #-}
17631794writeNUnsafe :: forall m a. (MonadIO m, Unbox a)
17641795 => Int -> Fold m a (MutArray a)
1765−writeNUnsafe = writeNWithUnsafe newPinned
1796+writeNUnsafe = writeNUnsafeAs Unpinned
17661797
17671798-- | @writeNWith alloc n@ folds a maximum of @n@ elements into an array
17681799-- allocated using the @alloc@ function.
@@ -1775,16 +1806,24 @@ writeNWith :: forall m a. (MonadIO m, Unbox a)
17751806 => (Int -> m (MutArray a)) -> Int -> Fold m a (MutArray a)
17761807writeNWith alloc n = FL.take n (writeNWithUnsafe alloc n)
17771808
1809+{-# INLINE_NORMAL writeNAs #-}
1810+writeNAs ::
1811+ forall m a. (MonadIO m, Unbox a)
1812+ => PinnedState
1813+ -> Int
1814+ -> Fold m a (MutArray a)
1815+writeNAs ps = writeNWith (newAs ps)
1816+
17781817-- | @writeN n@ folds a maximum of @n@ elements from the input stream to an
17791818-- 'MutArray'.
17801819--
1781−-- >>> writeN = MutArray.writeNWith MutArray.newPinned
1820+-- >>> writeN = MutArray.writeNWith MutArray.new
17821821-- >>> writeN n = Fold.take n (MutArray.writeNUnsafe n)
1783−-- >>> writeN n = MutArray.writeAppendN n (MutArray.newPinned n)
1822+-- >>> writeN n = MutArray.writeAppendN n (MutArray.new n)
17841823--
17851824{-# INLINE_NORMAL writeN #-}
17861825writeN :: forall m a. (MonadIO m, Unbox a) => Int -> Fold m a (MutArray a)
1787−writeN = writeNWith newPinned
1826+writeN = writeNAs Unpinned
17881827
17891828-- | Like writeNWithUnsafe but writes the array in reverse order.
17901829--
@@ -1820,20 +1859,20 @@ writeRevNWith alloc n = FL.take n (writeRevNWithUnsafe alloc n)
18201859-- /Pre-release/
18211860{-# INLINE_NORMAL writeRevN #-}
18221861writeRevN :: forall m a. (MonadIO m, Unbox a) => Int -> Fold m a (MutArray a)
1823−writeRevN = writeRevNWith newPinned
1862+writeRevN = writeRevNWith new
18241863
1825−-- | @writeNAligned align n@ folds a maximum of @n@ elements from the input
1826−-- stream to a 'MutArray' aligned to the given size.
1864+-- | @pinnedWriteNAligned align n@ folds a maximum of @n@ elements from the
1865+-- input stream to a 'MutArray' aligned to the given size.
18271866--
1828−-- >>> writeNAligned align = MutArray.writeNWith (MutArray.newAlignedPinned align)
1829−-- >>> writeNAligned align n = MutArray.writeAppendN n (MutArray.newAlignedPinned align n)
1867+-- >>> pinnedWriteNAligned align = MutArray.writeNWith (MutArray.newAlignedPinned align)
1868+-- >>> pinnedWriteNAligned align n = MutArray.writeAppendN n (MutArray.newAlignedPinned align n)
18301869--
18311870-- /Pre-release/
18321871--
1833−{-# INLINE_NORMAL writeNAligned #-}
1834−writeNAligned :: forall m a. (MonadIO m, Unbox a)
1872+{-# INLINE_NORMAL pinnedWriteNAligned #-}
1873+pinnedWriteNAligned :: forall m a. (MonadIO m, Unbox a)
18351874 => Int -> Int -> Fold m a (MutArray a)
1836−writeNAligned align = writeNWith (newAlignedPinned align)
1875+pinnedWriteNAligned align = writeNWith (newAlignedPinned align)
18371876
18381877-- XXX Buffer to a list instead?
18391878--
@@ -1876,7 +1915,7 @@ writeChunks n = FL.many (writeN n) FL.toStreamK
18761915--
18771916-- /Caution! Do not use this on infinite streams./
18781917--
1879−-- >>> f n = MutArray.writeAppendWith (* 2) (MutArray.newPinned n)
1918+-- >>> f n = MutArray.writeAppendWith (* 2) (MutArray.new n)
18801919-- >>> writeWith n = Fold.rmapM MutArray.rightSize (f n)
18811920-- >>> writeWith n = Fold.rmapM MutArray.fromArrayStreamK (MutArray.writeChunks n)
18821921--
@@ -1884,7 +1923,7 @@ writeChunks n = FL.many (writeN n) FL.toStreamK
18841923{-# INLINE_NORMAL writeWith #-}
18851924writeWith :: forall m a. (MonadIO m, Unbox a)
18861925 => Int -> Fold m a (MutArray a)
1887−-- writeWith n = FL.rmapM rightSize $ writeAppendWith (* 2) (newPinned n)
1926+-- writeWith n = FL.rmapM rightSize $ writeAppendWith (* 2) (new n)
18881927writeWith elemCount =
18891928 FL.rmapM extract $ FL.foldlM' step initial
18901929
@@ -1892,7 +1931,7 @@ writeWith elemCount =
18921931
18931932 initial = do
18941933 when (elemCount < 0) $ error "writeWith: elemCount is negative"
1895− liftIO $ newPinned elemCount
1934+ liftIO $ new elemCount
18961935
18971936 step arr@(MutArray _ start end bound) x
18981937 | INDEX_NEXT(end,a) > bound = do
@@ -1928,7 +1967,7 @@ fromStreamDN :: forall m a. (MonadIO m, Unbox a)
19281967 => Int -> D.Stream m a -> m (MutArray a)
19291968-- fromStreamDN n = D.fold (writeN n)
19301969fromStreamDN limit str = do
1931− (arr :: MutArray a) <- liftIO $ newPinned limit
1970+ (arr :: MutArray a) <- liftIO $ new limit
19321971 end <- D.foldlM' (fwrite (arrContents arr)) (return $ arrEnd arr) $ D.take limit str
19331972 return $ arr {arrEnd = end}
19341973
@@ -2040,7 +2079,7 @@ spliceCopy arr1 arr2 = liftIO $ do
20402079 start2 = arrStart arr2
20412080 len1 = arrEnd arr1 - start1
20422081 len2 = arrEnd arr2 - start2
2043− newArrContents <- liftIO $ Unboxed.newPinnedBytes (len1 + len2)
2082+ newArrContents <- liftIO $ Unboxed.newUnpinnedBytes (len1 + len2)
20442083 let len = len1 + len2
20452084 newArr = MutArray newArrContents 0 len len
20462085 putSliceUnsafe arr1 start1 newArr 0 len1
@@ -2251,12 +2290,10 @@ cast arr =
22512290--
22522291asPtrUnsafe :: MonadIO m => MutArray a -> (Ptr a -> m b) -> m b
22532292asPtrUnsafe arr f = do
2254− let contents = arrContents arr
2255− !ptr = Ptr (byteArrayContents#
2293+ contents <- liftIO $ Unboxed.pin $ arrContents arr
2294+ let !ptr = Ptr (byteArrayContents#
22562295 (unsafeCoerce# (getMutableByteArray# contents)))
2257− -- XXX Check if the array is pinned, if not, copy it to a pinned array
2258− -- XXX We should probably pass to the IO action the byte length of the array
2259− -- as well so that bounds can be checked.
2296+
22602297 r <- f (ptr `plusPtr` arrStart arr)
22612298 liftIO $ touch contents
22622299 return r