@@ -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-------------------------------------------------------------------------------