@@ -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-------------------------------------------------------------------------------
0 commit comments