on master help sign in
Add fromPureStream and fromByteStr# to Arrays
commit3236d7377782ee1cbe5ece1b5b568c75e13c08a6 authorRanjeet Kumar Ranjan <ranjeetr@composewell.com> dateTue 23 May 2023, 10:45 (3 years ago) parentb925b71
4 files changed+92−1
core/src/Streamly/Internal/Data/Array/Generic.hs+14 core/src/Streamly/Internal/Data/Array/Generic/Mut/Type.hs+8 core/src/Streamly/Internal/Data/Array/Mut/Type.hs+9 core/src/Streamly/Internal/Data/Array/Type.hs+61−1
core/src/Streamly/Internal/Data/Array/Generic.hs+14
@@ -18,6 +18,8 @@ module Streamly.Internal.Data.Array.Generic
1818
1919 , fromStreamN
2020 , fromStream
21+ , fromPureStream
22+ , fromByteStr#
2123
2224 , fromListN
2325 , fromList
@@ -47,7 +49,10 @@ where
4749
4850import Control.Monad (replicateM)
4951import Control.Monad.IO.Class (MonadIO)
52+import Data.Functor.Identity (Identity(..))
53+import Data.Word (Word8)
5054import GHC.Base (MutableArray#, RealWorld)
55+import GHC.Exts (Addr#)
5156import GHC.IO (unsafePerformIO)
5257import Text.Read (readPrec)
5358
@@ -113,6 +118,15 @@ writeWith elemCount = unsafeFreeze <$> MArray.writeWith elemCount
113118write :: MonadIO m => Fold m a (Array a)
114119write = fmap unsafeFreeze MArray.write
115120
121+fromPureStream :: Stream Identity a -> Array a
122+fromPureStream x =
123+ unsafePerformIO $ fmap (unsafeFreeze) (MArray.fromPureStream x)
124+-- fromPureStream = runIdentity . D.fold (unsafeMakePure write)
125+-- fromPureStream = fromList . runIdentity . D.toList
126+
127+fromByteStr# :: Addr# -> Array Word8
128+fromByteStr# addr = fromPureStream (D.fromByteStr# addr)
129+
116130-------------------------------------------------------------------------------
117131-- Construction - from streams
118132-------------------------------------------------------------------------------
core/src/Streamly/Internal/Data/Array/Generic/Mut/Type.hs+8
@@ -29,6 +29,7 @@ module Streamly.Internal.Data.Array.Generic.Mut.Type
2929 , write
3030 , fromStreamN
3131 , fromStream
32+ , fromPureStream
3233
3334 -- , writeRevN
3435 -- , writeRev
@@ -166,6 +167,7 @@ where
166167
167168import Control.Monad (when)
168169import Control.Monad.IO.Class (MonadIO(..))
170+import Data.Functor.Identity (Identity(..))
169171import GHC.Base
170172 ( MutableArray#
171173 , RealWorld
@@ -185,6 +187,7 @@ import qualified Streamly.Internal.Data.Fold.Type as FL
185187import qualified Streamly.Internal.Data.Producer as Producer
186188import qualified Streamly.Internal.Data.Stream.StreamD.Type as D
187189import qualified Streamly.Internal.Data.Stream.StreamD.Generate as D
190+import qualified Streamly.Internal.Data.Stream.StreamD.Lift as D
188191import qualified Streamly.Internal.Data.Stream.StreamK.Type as K
189192
190193import Prelude hiding (read, length)
@@ -658,6 +661,11 @@ fromListN n xs = fromStreamN n $ D.fromList xs
658661fromList :: MonadIO m => [a] -> m (MutArray a)
659662fromList xs = fromStream $ D.fromList xs
660663
664+{-# INLINABLE fromPureStream #-}
665+fromPureStream :: MonadIO m => Stream Identity a -> m (MutArray a)
666+fromPureStream xs =
667+ liftIO $ D.fold write $ D.morphInner (return . runIdentity) xs
668+
661669-------------------------------------------------------------------------------
662670-- Unfolds
663671-------------------------------------------------------------------------------
core/src/Streamly/Internal/Data/Array/Mut/Type.hs+9
@@ -71,6 +71,7 @@ module Streamly.Internal.Data.Array.Mut.Type
7171 , fromListRev
7272 , fromStreamDN
7373 , fromStreamD
74+ , fromPureStream
7475
7576 -- * Random writes
7677 , putIndex
@@ -227,6 +228,7 @@ where
227228import Control.Monad (when, void)
228229import Control.Monad.IO.Class (MonadIO(..))
229230import Data.Bits (shiftR, (.|.), (.&.))
231+import Data.Functor.Identity (Identity(..))
230232import Data.Proxy (Proxy(..))
231233import Data.Word (Word8)
232234import Foreign.C.Types (CSize(..), CInt(..))
@@ -262,6 +264,7 @@ import Streamly.Internal.System.IO (arrayPayloadSize, defaultChunkSize)
262264import qualified Streamly.Internal.Data.Fold.Type as FL
263265import qualified Streamly.Internal.Data.Producer as Producer
264266import qualified Streamly.Internal.Data.Stream.StreamD.Type as D
267+import qualified Streamly.Internal.Data.Stream.StreamD.Lift as D
265268import qualified Streamly.Internal.Data.Stream.StreamK.Type as K
266269import qualified Streamly.Internal.Data.Unbox as Unboxed
267270import qualified Prelude
@@ -1949,6 +1952,12 @@ fromListN n xs = fromStreamDN n $ D.fromList xs
19491952fromListRevN :: (MonadIO m, Unbox a) => Int -> [a] -> m (MutArray a)
19501953fromListRevN n xs = D.fold (writeRevN n) $ D.fromList xs
19511954
1955+-- | Convert a pure stream in Identity monad to a mutable array.
1956+{-# INLINABLE fromPureStream #-}
1957+fromPureStream :: (MonadIO m, Unbox a) => Stream Identity a -> m (MutArray a)
1958+fromPureStream xs =
1959+ liftIO $ D.fold write $ D.morphInner (return . runIdentity) xs
1960+
19521961-------------------------------------------------------------------------------
19531962-- convert stream to a single array
19541963-------------------------------------------------------------------------------
core/src/Streamly/Internal/Data/Array/Type.hs+61−1
@@ -34,6 +34,8 @@ module Streamly.Internal.Data.Array.Type
3434 , fromListRevN
3535 , fromStreamDN
3636 , fromStreamD
37+ , fromPureStream
38+ , fromByteStr#
3739
3840 -- * Split
3941 , breakOn
@@ -66,6 +68,7 @@ module Streamly.Internal.Data.Array.Type
6668 , MA.ArrayUnsafe (..)
6769 , writeNAligned
6870 , write
71+ , unsafeMakePure
6972
7073 -- * Streams of arrays
7174 , chunksOf
@@ -85,7 +88,7 @@ import Data.Functor.Identity (Identity(..))
8588import Data.Proxy (Proxy(..))
8689import Data.Word (Word8)
8790import GHC.Base (build)
88−import GHC.Exts (IsList, IsString(..))
91+import GHC.Exts (IsList, IsString(..), Addr#)
8992
9093import GHC.IO (unsafePerformIO)
9194import GHC.Ptr (Ptr(..))
@@ -101,6 +104,7 @@ import Prelude hiding (Foldable(..), read, unlines, splitAt)
101104import qualified GHC.Exts as Exts
102105import qualified Streamly.Internal.Data.Array.Mut.Type as MA
103106import qualified Streamly.Internal.Data.Stream.StreamD.Type as D
107+import qualified Streamly.Internal.Data.Stream.StreamD.Generate as D
104108import qualified Streamly.Internal.Data.Stream.StreamK.Type as K
105109import qualified Streamly.Internal.Data.Unbox as Unboxed
106110import qualified Streamly.Internal.Data.Unfold.Type as Unfold
@@ -495,6 +499,62 @@ writeWith elemCount = unsafeFreeze <$> MA.writeWith elemCount
495499write :: forall m a. (MonadIO m, Unbox a) => Fold m a (Array a)
496500write = fmap unsafeFreeze MA.write
497501
502+-- | Fold "step" has a dependency on "initial", and each step is dependent on
503+-- the previous invocation of step due to state passing, finally extract
504+-- depends on the result of step, therefore, as long as the fold is driven in
505+-- the correct order the operations would be correctly ordered. We need to
506+-- ensure that we strictly evaluate the previous step completely before the
507+-- next step.
508+--
509+-- To not share the same array we need to make sure that the result of
510+-- "initial" is not shared. Existential type ensures that it does not get
511+-- shared across different folds. However, if we invoke "initial" multiple
512+-- times for the same fold, there is a possiblity of sharing the two because
513+-- the compiler would consider it as a pure value. One such example is the
514+-- chunksOf combinator, or using an array creation fold with foldMany
515+-- combinator. Is there a proper way in GHC to tell it to not share a pure
516+-- expression in a particular case?
517+--
518+-- For this reason array creation folds have a MonadIO constraint. Pure folds
519+-- could be unsafe and dangerous. This is dangerous especially when used with
520+-- foldMany like operations.
521+--
522+-- >>> import qualified Streamly.Internal.Data.Array.Type as Array
523+-- >>> unsafePureWrite = Array.unsafeMakePure Array.write
524+--
525+{-# INLINE unsafeMakePure #-}
526+unsafeMakePure :: Monad m => Fold IO a b -> Fold m a b
527+unsafeMakePure (Fold step initial extract) =
528+ Fold (\x a -> return $! unsafeInlineIO (step x a))
529+ (return $! unsafePerformIO initial)
530+ (\s -> return $! unsafeInlineIO $ extract s)
531+
532+-- | Convert a pure stream in Identity monad to an immutable array.
533+--
534+-- Same as the following but with better performance:
535+--
536+-- >>> fromPureStream = Array.fromList . runIdentity . Stream.toList
537+--
538+fromPureStream :: Unbox a => Stream Identity a -> Array a
539+fromPureStream x = unsafePerformIO $ fmap (unsafeFreeze) (MA.fromPureStream x)
540+-- fromPureStream = runIdentity . D.fold (unsafeMakePure write)
541+-- fromPureStream = fromList . runIdentity . D.toList
542+
543+-- | Copy a null terminated immutable 'Addr#' Word8 sequence into an array.
544+--
545+-- /Unsafe:/ The caller is responsible for safe addressing.
546+--
547+-- Note that this is completely safe when reading from Haskell string
548+-- literals because they are guaranteed to be NULL terminated:
549+--
550+-- >>> :set -XMagicHash
551+-- >>> import qualified Streamly.Internal.Data.Array.Type as Array
552+-- >>> Array.toList $ Array.fromByteStr# "\1\2\3\0"#
553+-- [1,2,3]
554+--
555+fromByteStr# :: Addr# -> Array Word8
556+fromByteStr# addr = fromPureStream (D.fromByteStr# addr)
557+
498558-------------------------------------------------------------------------------
499559-- Instances
500560-------------------------------------------------------------------------------