@@ -34,6 +34,8 @@ module Streamly.Internal.Data.Array.Type
34 , fromListRevN
34 , fromListRevN
35 , fromStreamDN
35 , fromStreamDN
36 , fromStreamD
36 , fromStreamD
38 -- * Split
40 -- * Split
@@ -66,6 +68,7 @@ module Streamly.Internal.Data.Array.Type
66 , MA.ArrayUnsafe (..)
68 , MA.ArrayUnsafe (..)
67 , writeNAligned
69 , writeNAligned
70 -- * Streams of arrays
73 -- * Streams of arrays
71 , chunksOf
74 , chunksOf
@@ -85,7 +88,7 @@ import Data.Functor.Identity (Identity(..))
85import Data.Proxy (Proxy(..))
88import Data.Proxy (Proxy(..))
86import Data.Word (Word8)
89import Data.Word (Word8)
87import GHC.Base (build)
90import GHC.Base (build)
88−import GHC.Exts (IsList, IsString(..))
91+import GHC.Exts (IsList, IsString(..), Addr#)
90import GHC.IO (unsafePerformIO)
93import GHC.IO (unsafePerformIO)
91import GHC.Ptr (Ptr(..))
94import GHC.Ptr (Ptr(..))
@@ -101,6 +104,7 @@ import Prelude hiding (Foldable(..), read, unlines, splitAt)
101import qualified GHC.Exts as Exts
104import qualified GHC.Exts as Exts
102import qualified Streamly.Internal.Data.Array.Mut.Type as MA
105import qualified Streamly.Internal.Data.Array.Mut.Type as MA
103import qualified Streamly.Internal.Data.Stream.StreamD.Type as D
106import qualified Streamly.Internal.Data.Stream.StreamD.Type as D
107+import qualified Streamly.Internal.Data.Stream.StreamD.Generate as D
104import qualified Streamly.Internal.Data.Stream.StreamK.Type as K
108import qualified Streamly.Internal.Data.Stream.StreamK.Type as K
105import qualified Streamly.Internal.Data.Unbox as Unboxed
109import qualified Streamly.Internal.Data.Unbox as Unboxed
106import qualified Streamly.Internal.Data.Unfold.Type as Unfold
110import qualified Streamly.Internal.Data.Unfold.Type as Unfold
@@ -495,6 +499,62 @@ writeWith elemCount = unsafeFreeze <$> MA.writeWith elemCount
495write :: forall m a. (MonadIO m, Unbox a) => Fold m a (Array a)
499write :: forall m a. (MonadIO m, Unbox a) => Fold m a (Array a)
496write = fmap unsafeFreeze MA.write
500write = fmap unsafeFreeze MA.write
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
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?
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.
522+-- >>> import qualified Streamly.Internal.Data.Array.Type as Array
523+-- >>> unsafePureWrite = Array.unsafeMakePure Array.write
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)
532+-- | Convert a pure stream in Identity monad to an immutable array.
534+-- Same as the following but with better performance:
536+-- >>> fromPureStream = Array.fromList . runIdentity . Stream.toList
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
543+-- | Copy a null terminated immutable 'Addr#' Word8 sequence into an array.
545+-- /Unsafe:/ The caller is responsible for safe addressing.
547+-- Note that this is completely safe when reading from Haskell string
548+-- literals because they are guaranteed to be NULL terminated:
550+-- >>> :set -XMagicHash
551+-- >>> import qualified Streamly.Internal.Data.Array.Type as Array
552+-- >>> Array.toList $ Array.fromByteStr# "\1\2\3\0"#
555+fromByteStr# :: Addr# -> Array Word8
556+fromByteStr# addr = fromPureStream (D.fromByteStr# addr)
498-------------------------------------------------------------------------------
558-------------------------------------------------------------------------------
499-- Instances
559-- Instances
500-------------------------------------------------------------------------------
560-------------------------------------------------------------------------------