Skip to content

Commit 3236d73

Browse files
rnjtranjanharendra-kumar
authored andcommitted
Add fromPureStream and fromByteStr# to Arrays
1 parent b925b71 commit 3236d73

File tree

4 files changed

+92
-1
lines changed

4 files changed

+92
-1
lines changed

core/src/Streamly/Internal/Data/Array/Generic.hs

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -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

4850
import Control.Monad (replicateM)
4951
import Control.Monad.IO.Class (MonadIO)
52+
import Data.Functor.Identity (Identity(..))
53+
import Data.Word (Word8)
5054
import GHC.Base (MutableArray#, RealWorld)
55+
import GHC.Exts (Addr#)
5156
import GHC.IO (unsafePerformIO)
5257
import Text.Read (readPrec)
5358

@@ -113,6 +118,15 @@ writeWith elemCount = unsafeFreeze <$> MArray.writeWith elemCount
113118
write :: MonadIO m => Fold m a (Array a)
114119
write = 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

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -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

167168
import Control.Monad (when)
168169
import Control.Monad.IO.Class (MonadIO(..))
170+
import Data.Functor.Identity (Identity(..))
169171
import GHC.Base
170172
( MutableArray#
171173
, RealWorld
@@ -185,6 +187,7 @@ import qualified Streamly.Internal.Data.Fold.Type as FL
185187
import qualified Streamly.Internal.Data.Producer as Producer
186188
import qualified Streamly.Internal.Data.Stream.StreamD.Type as D
187189
import qualified Streamly.Internal.Data.Stream.StreamD.Generate as D
190+
import qualified Streamly.Internal.Data.Stream.StreamD.Lift as D
188191
import qualified Streamly.Internal.Data.Stream.StreamK.Type as K
189192

190193
import Prelude hiding (read, length)
@@ -658,6 +661,11 @@ fromListN n xs = fromStreamN n $ D.fromList xs
658661
fromList :: MonadIO m => [a] -> m (MutArray a)
659662
fromList 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

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -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
227228
import Control.Monad (when, void)
228229
import Control.Monad.IO.Class (MonadIO(..))
229230
import Data.Bits (shiftR, (.|.), (.&.))
231+
import Data.Functor.Identity (Identity(..))
230232
import Data.Proxy (Proxy(..))
231233
import Data.Word (Word8)
232234
import Foreign.C.Types (CSize(..), CInt(..))
@@ -262,6 +264,7 @@ import Streamly.Internal.System.IO (arrayPayloadSize, defaultChunkSize)
262264
import qualified Streamly.Internal.Data.Fold.Type as FL
263265
import qualified Streamly.Internal.Data.Producer as Producer
264266
import qualified Streamly.Internal.Data.Stream.StreamD.Type as D
267+
import qualified Streamly.Internal.Data.Stream.StreamD.Lift as D
265268
import qualified Streamly.Internal.Data.Stream.StreamK.Type as K
266269
import qualified Streamly.Internal.Data.Unbox as Unboxed
267270
import qualified Prelude
@@ -1949,6 +1952,12 @@ fromListN n xs = fromStreamDN n $ D.fromList xs
19491952
fromListRevN :: (MonadIO m, Unbox a) => Int -> [a] -> m (MutArray a)
19501953
fromListRevN 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

Lines changed: 61 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -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(..))
8588
import Data.Proxy (Proxy(..))
8689
import Data.Word (Word8)
8790
import GHC.Base (build)
88-
import GHC.Exts (IsList, IsString(..))
91+
import GHC.Exts (IsList, IsString(..), Addr#)
8992

9093
import GHC.IO (unsafePerformIO)
9194
import GHC.Ptr (Ptr(..))
@@ -101,6 +104,7 @@ import Prelude hiding (Foldable(..), read, unlines, splitAt)
101104
import qualified GHC.Exts as Exts
102105
import qualified Streamly.Internal.Data.Array.Mut.Type as MA
103106
import qualified Streamly.Internal.Data.Stream.StreamD.Type as D
107+
import qualified Streamly.Internal.Data.Stream.StreamD.Generate as D
104108
import qualified Streamly.Internal.Data.Stream.StreamK.Type as K
105109
import qualified Streamly.Internal.Data.Unbox as Unboxed
106110
import qualified Streamly.Internal.Data.Unfold.Type as Unfold
@@ -495,6 +499,62 @@ writeWith elemCount = unsafeFreeze <$> MA.writeWith elemCount
495499
write :: forall m a. (MonadIO m, Unbox a) => Fold m a (Array a)
496500
write = 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

Comments
 (0)