on master help sign in
Refactor array modules (factor out array stream)
f448fb5Harendra Kumar
benchmark/Streamly/Benchmark/Data/Stream/StreamD.hs+1−1
@@ -408,7 +408,7 @@ inspect $ 'concatMapRepl `hasNoType` ''SPEC
408concatUnfoldRepl :: Int -> Int -> Int -> IO ()
408concatUnfoldRepl :: Int -> Int -> Int -> IO ()
409concatUnfoldRepl outer inner n =
409concatUnfoldRepl outer inner n =
410 S.drain
410 S.drain
411− $ S.concatMapU
411+ $ S.concatUnfold
412 (UF.lmap return (UF.replicateM inner))
412 (UF.lmap return (UF.replicateM inner))
413 (sourceUnfoldrMN outer n)
413 (sourceUnfoldrMN outer n)
414
414
benchmark/Streamly/Benchmark/FileSystem/Handle/Read.hs+6−5
@@ -51,6 +51,7 @@ import Streamly.Benchmark.Common.Handle
51#ifdef INSPECTION
51#ifdef INSPECTION
52import Streamly.Internal.Data.Stream.StreamD.Type (Step(..), GroupState)
52import Streamly.Internal.Data.Stream.StreamD.Type (Step(..), GroupState)
53
53
54+import qualified Streamly.Internal.Data.Array.Storable.Foreign.Mut.Types as MA
54import qualified Streamly.Internal.Data.Stream.StreamD as D
55import qualified Streamly.Internal.Data.Stream.StreamD as D
55import qualified Streamly.Internal.Data.Unfold as IUF
56import qualified Streamly.Internal.Data.Unfold as IUF
56
57
@@ -162,7 +163,7 @@ readLast = S.last . S.unfold FH.read
162inspect $ hasNoTypeClasses 'readLast
163inspect $ hasNoTypeClasses 'readLast
163inspect $ 'readLast `hasNoType` ''Step -- S.unfold
164inspect $ 'readLast `hasNoType` ''Step -- S.unfold
164inspect $ 'readLast `hasNoType` ''IUF.ConcatState -- FH.read/UF.concat
165inspect $ 'readLast `hasNoType` ''IUF.ConcatState -- FH.read/UF.concat
165−inspect $ 'readLast `hasNoType` ''A.ReadUState -- FH.read/A.read
166+inspect $ 'readLast `hasNoType` ''MA.ReadUState -- FH.read/A.read
166#endif
167#endif
167
168
168-- assert that flattenArrays constructors are not present
169-- assert that flattenArrays constructors are not present
@@ -174,7 +175,7 @@ readCountBytes = S.length . S.unfold FH.read
174inspect $ hasNoTypeClasses 'readCountBytes
175inspect $ hasNoTypeClasses 'readCountBytes
175inspect $ 'readCountBytes `hasNoType` ''Step -- S.unfold
176inspect $ 'readCountBytes `hasNoType` ''Step -- S.unfold
176inspect $ 'readCountBytes `hasNoType` ''IUF.ConcatState -- FH.read/UF.concat
177inspect $ 'readCountBytes `hasNoType` ''IUF.ConcatState -- FH.read/UF.concat
177−inspect $ 'readCountBytes `hasNoType` ''A.ReadUState -- FH.read/A.read
178+inspect $ 'readCountBytes `hasNoType` ''MA.ReadUState -- FH.read/A.read
178#endif
179#endif
179
180
180-- | Count the number of lines in a file.
181-- | Count the number of lines in a file.
@@ -189,7 +190,7 @@ readCountLines =
189inspect $ hasNoTypeClasses 'readCountLines
190inspect $ hasNoTypeClasses 'readCountLines
190inspect $ 'readCountLines `hasNoType` ''Step
191inspect $ 'readCountLines `hasNoType` ''Step
191inspect $ 'readCountLines `hasNoType` ''IUF.ConcatState -- FH.read/UF.concat
192inspect $ 'readCountLines `hasNoType` ''IUF.ConcatState -- FH.read/UF.concat
192−inspect $ 'readCountLines `hasNoType` ''A.ReadUState -- FH.read/A.read
193+inspect $ 'readCountLines `hasNoType` ''MA.ReadUState -- FH.read/A.read
193#endif
194#endif
194
195
195-- | Count the number of words in a file.
196-- | Count the number of words in a file.
@@ -213,7 +214,7 @@ readSumBytes = S.sum . S.unfold FH.read
213inspect $ hasNoTypeClasses 'readSumBytes
214inspect $ hasNoTypeClasses 'readSumBytes
214inspect $ 'readSumBytes `hasNoType` ''Step
215inspect $ 'readSumBytes `hasNoType` ''Step
215inspect $ 'readSumBytes `hasNoType` ''IUF.ConcatState -- FH.read/UF.concat
216inspect $ 'readSumBytes `hasNoType` ''IUF.ConcatState -- FH.read/UF.concat
216−inspect $ 'readSumBytes `hasNoType` ''A.ReadUState -- FH.read/A.read
217+inspect $ 'readSumBytes `hasNoType` ''MA.ReadUState -- FH.read/A.read
217#endif
218#endif
218
219
219-- XXX When we mark this with INLINE and we have two benchmarks using S.drain
220-- XXX When we mark this with INLINE and we have two benchmarks using S.drain
@@ -335,7 +336,7 @@ inspect $ 'chunksOf `hasNoType` ''Step
335inspect $ 'chunksOf `hasNoType` ''GroupState
336inspect $ 'chunksOf `hasNoType` ''GroupState
336inspect $ 'chunksOf `hasNoType` ''AT.ArrayUnsafe -- AT.writeNUnsafe
337inspect $ 'chunksOf `hasNoType` ''AT.ArrayUnsafe -- AT.writeNUnsafe
337inspect $ 'chunksOf `hasNoType` ''IUF.ConcatState -- FH.read/UF.concat
338inspect $ 'chunksOf `hasNoType` ''IUF.ConcatState -- FH.read/UF.concat
338−inspect $ 'chunksOf `hasNoType` ''A.ReadUState -- FH.read/A.read
339+inspect $ 'chunksOf `hasNoType` ''MA.ReadUState -- FH.read/A.read
339#endif
340#endif
340
341
341o_1_space_reduce_read_grouped :: BenchEnv -> [Benchmark]
342o_1_space_reduce_read_grouped :: BenchEnv -> [Benchmark]
benchmark/Streamly/Benchmark/FileSystem/Handle/ReadWrite.hs+7−6
@@ -42,8 +42,9 @@ import Streamly.Internal.Data.Stream.StreamD.Type (Step(..))
42
42
43import qualified Streamly.Internal.Data.Stream.StreamD.Type as D
43import qualified Streamly.Internal.Data.Stream.StreamD.Type as D
44import qualified Streamly.Internal.Data.Tuple.Strict as Strict
44import qualified Streamly.Internal.Data.Tuple.Strict as Strict
45−import qualified Streamly.Internal.Data.Array.Storable.Foreign as A
45+import qualified Streamly.Internal.Data.Array.Stream.Mut.Foreign as MAS
46import qualified Streamly.Internal.Data.Array.Storable.Foreign.Types as AT
46import qualified Streamly.Internal.Data.Array.Storable.Foreign.Types as AT
47+import qualified Streamly.Internal.Data.Array.Storable.Foreign.Mut.Types as MA
47
48
48import Test.Inspection
49import Test.Inspection
49#endif
50#endif
@@ -83,7 +84,7 @@ copyStream inh outh = S.fold (FH.write outh) (S.unfold FH.read inh)
83inspect $ hasNoTypeClasses 'copyStream
84inspect $ hasNoTypeClasses 'copyStream
84inspect $ 'copyStream `hasNoType` ''Step -- S.unfold
85inspect $ 'copyStream `hasNoType` ''Step -- S.unfold
85inspect $ 'copyStream `hasNoType` ''IUF.ConcatState -- FH.read/UF.concat
86inspect $ 'copyStream `hasNoType` ''IUF.ConcatState -- FH.read/UF.concat
86−inspect $ 'copyStream `hasNoType` ''A.ReadUState -- FH.read/A.read
87+inspect $ 'copyStream `hasNoType` ''MA.ReadUState -- FH.read/A.read
87inspect $ 'copyStream `hasNoType` ''AT.ArrayUnsafe -- FH.write/writeNUnsafe
88inspect $ 'copyStream `hasNoType` ''AT.ArrayUnsafe -- FH.write/writeNUnsafe
88inspect $ 'copyStream `hasNoType` ''Strict.Tuple3' -- FH.write/chunksOf
89inspect $ 'copyStream `hasNoType` ''Strict.Tuple3' -- FH.write/chunksOf
89#endif
90#endif
@@ -109,7 +110,7 @@ readFromBytesNull inh devNull = IFH.fromBytes devNull $ S.unfold FH.read inh
109#ifdef INSPECTION
110#ifdef INSPECTION
110inspect $ hasNoTypeClasses 'readFromBytesNull
111inspect $ hasNoTypeClasses 'readFromBytesNull
111inspect $ 'readFromBytesNull `hasNoType` ''Step
112inspect $ 'readFromBytesNull `hasNoType` ''Step
112−inspect $ 'readFromBytesNull `hasNoType` ''AT.SpliceState
113+inspect $ 'readFromBytesNull `hasNoType` ''MAS.SpliceState
113inspect $ 'readFromBytesNull `hasNoType` ''AT.ArrayUnsafe -- FH.fromBytes/S.arraysOf
114inspect $ 'readFromBytesNull `hasNoType` ''AT.ArrayUnsafe -- FH.fromBytes/S.arraysOf
114inspect $ 'readFromBytesNull `hasNoType` ''D.GroupState
115inspect $ 'readFromBytesNull `hasNoType` ''D.GroupState
115#endif
116#endif
@@ -123,7 +124,7 @@ readWithBufferOfFromBytesNull inh devNull =
123#ifdef INSPECTION
124#ifdef INSPECTION
124inspect $ hasNoTypeClasses 'readWithBufferOfFromBytesNull
125inspect $ hasNoTypeClasses 'readWithBufferOfFromBytesNull
125inspect $ 'readWithBufferOfFromBytesNull `hasNoType` ''Step
126inspect $ 'readWithBufferOfFromBytesNull `hasNoType` ''Step
126−inspect $ 'readWithBufferOfFromBytesNull `hasNoType` ''AT.SpliceState
127+inspect $ 'readWithBufferOfFromBytesNull `hasNoType` ''MAS.SpliceState
127inspect $ 'readWithBufferOfFromBytesNull `hasNoType` ''AT.ArrayUnsafe -- FH.fromBytes/S.arraysOf
128inspect $ 'readWithBufferOfFromBytesNull `hasNoType` ''AT.ArrayUnsafe -- FH.fromBytes/S.arraysOf
128inspect $ 'readWithBufferOfFromBytesNull `hasNoType` ''D.GroupState
129inspect $ 'readWithBufferOfFromBytesNull `hasNoType` ''D.GroupState
129#endif
130#endif
@@ -173,7 +174,7 @@ writeReadWithBufferOf inh devNull = IUF.fold unf fld (defaultChunkSize, inh)
173inspect $ hasNoTypeClasses 'writeReadWithBufferOf
174inspect $ hasNoTypeClasses 'writeReadWithBufferOf
174inspect $ 'writeReadWithBufferOf `hasNoType` ''Step
175inspect $ 'writeReadWithBufferOf `hasNoType` ''Step
175inspect $ 'writeReadWithBufferOf `hasNoType` ''IUF.ConcatState -- FH.read/UF.concat
176inspect $ 'writeReadWithBufferOf `hasNoType` ''IUF.ConcatState -- FH.read/UF.concat
176−inspect $ 'writeReadWithBufferOf `hasNoType` ''A.ReadUState -- FH.read/A.read
177+inspect $ 'writeReadWithBufferOf `hasNoType` ''MA.ReadUState -- FH.read/A.read
177inspect $ 'writeReadWithBufferOf `hasNoType` ''AT.ArrayUnsafe -- FH.write/writeNUnsafe
178inspect $ 'writeReadWithBufferOf `hasNoType` ''AT.ArrayUnsafe -- FH.write/writeNUnsafe
178#endif
179#endif
179
180
@@ -191,7 +192,7 @@ writeRead inh devNull = IUF.fold unf fld inh
191inspect $ hasNoTypeClasses 'writeRead
192inspect $ hasNoTypeClasses 'writeRead
192inspect $ 'writeRead `hasNoType` ''Step
193inspect $ 'writeRead `hasNoType` ''Step
193inspect $ 'writeRead `hasNoType` ''IUF.ConcatState -- FH.read/UF.concat
194inspect $ 'writeRead `hasNoType` ''IUF.ConcatState -- FH.read/UF.concat
194−inspect $ 'writeRead `hasNoType` ''A.ReadUState -- FH.read/A.read
195+inspect $ 'writeRead `hasNoType` ''MA.ReadUState -- FH.read/A.read
195inspect $ 'writeRead `hasNoType` ''AT.ArrayUnsafe -- FH.write/writeNUnsafe
196inspect $ 'writeRead `hasNoType` ''AT.ArrayUnsafe -- FH.write/writeNUnsafe
196#endif
197#endif
197
198
benchmark/Streamly/Benchmark/Prelude/Serial/Split.hs+6−5
@@ -1,4 +1,4 @@
1− -- |
1+-- |
2-- Module : Streamly.Benchmark.Prelude.Serial.Split
2-- Module : Streamly.Benchmark.Prelude.Serial.Split
3-- Copyright : (c) 2019 Composewell Technologies
3-- Copyright : (c) 2019 Composewell Technologies
4-- License : BSD-3-Clause
4-- License : BSD-3-Clause
@@ -41,6 +41,7 @@ import Streamly.Benchmark.Common.Handle
41#ifdef INSPECTION
41#ifdef INSPECTION
42import Streamly.Internal.Data.Stream.StreamD.Type (Step(..))
42import Streamly.Internal.Data.Stream.StreamD.Type (Step(..))
43
43
44+import qualified Streamly.Internal.Data.Array.Storable.Foreign.Mut.Types as MA
44import qualified Streamly.Internal.Data.Unfold as IUF
45import qualified Streamly.Internal.Data.Unfold as IUF
45
46
46import Test.Inspection
47import Test.Inspection
@@ -66,7 +67,7 @@ splitOn inh =
66inspect $ hasNoTypeClasses 'splitOn
67inspect $ hasNoTypeClasses 'splitOn
67inspect $ 'splitOn `hasNoType` ''Step
68inspect $ 'splitOn `hasNoType` ''Step
68inspect $ 'splitOn `hasNoType` ''IUF.ConcatState -- FH.read/UF.concat
69inspect $ 'splitOn `hasNoType` ''IUF.ConcatState -- FH.read/UF.concat
69−inspect $ 'splitOn `hasNoType` ''A.ReadUState -- FH.read/A.read
70+inspect $ 'splitOn `hasNoType` ''MA.ReadUState -- FH.read/A.read
70#endif
71#endif
71
72
72-- | Split suffix on line feed.
73-- | Split suffix on line feed.
@@ -79,7 +80,7 @@ splitOnSuffix inh =
79inspect $ hasNoTypeClasses 'splitOnSuffix
80inspect $ hasNoTypeClasses 'splitOnSuffix
80inspect $ 'splitOnSuffix `hasNoType` ''Step
81inspect $ 'splitOnSuffix `hasNoType` ''Step
81inspect $ 'splitOnSuffix `hasNoType` ''IUF.ConcatState -- FH.read/UF.concat
82inspect $ 'splitOnSuffix `hasNoType` ''IUF.ConcatState -- FH.read/UF.concat
82−inspect $ 'splitOnSuffix `hasNoType` ''A.ReadUState -- FH.read/A.read
83+inspect $ 'splitOnSuffix `hasNoType` ''MA.ReadUState -- FH.read/A.read
83#endif
84#endif
84
85
85-- | Split suffix with line feed.
86-- | Split suffix with line feed.
@@ -92,7 +93,7 @@ splitWithSuffix inh =
92inspect $ hasNoTypeClasses 'splitWithSuffix
93inspect $ hasNoTypeClasses 'splitWithSuffix
93inspect $ 'splitWithSuffix `hasNoType` ''Step
94inspect $ 'splitWithSuffix `hasNoType` ''Step
94inspect $ 'splitWithSuffix `hasNoType` ''IUF.ConcatState -- FH.read/UF.concat
95inspect $ 'splitWithSuffix `hasNoType` ''IUF.ConcatState -- FH.read/UF.concat
95−inspect $ 'splitWithSuffix `hasNoType` ''A.ReadUState -- FH.read/A.read
96+inspect $ 'splitWithSuffix `hasNoType` ''MA.ReadUState -- FH.read/A.read
96#endif
97#endif
97
98
98-- | Split on line feed.
99-- | Split on line feed.
@@ -123,7 +124,7 @@ wordsBy inh =
123inspect $ hasNoTypeClasses 'wordsBy
124inspect $ hasNoTypeClasses 'wordsBy
124inspect $ 'wordsBy `hasNoType` ''Step
125inspect $ 'wordsBy `hasNoType` ''Step
125inspect $ 'wordsBy `hasNoType` ''IUF.ConcatState -- FH.read/UF.concat
126inspect $ 'wordsBy `hasNoType` ''IUF.ConcatState -- FH.read/UF.concat
126−inspect $ 'wordsBy `hasNoType` ''A.ReadUState -- FH.read/A.read
127+inspect $ 'wordsBy `hasNoType` ''MA.ReadUState -- FH.read/A.read
127#endif
128#endif
128
129
129-- | Split on a word8 sequence.
130-- | Split on a word8 sequence.
benchmark/Streamly/Benchmark/Unicode/Stream.hs+3−3
@@ -40,8 +40,8 @@ import Streamly.Benchmark.Common.Handle
40import Foreign.Storable (Storable)
40import Foreign.Storable (Storable)
41import Streamly.Internal.Data.Stream.StreamD.Type (Step(..))
41import Streamly.Internal.Data.Stream.StreamD.Type (Step(..))
42import qualified Streamly.Internal.Data.Tuple.Strict as Strict
42import qualified Streamly.Internal.Data.Tuple.Strict as Strict
43−import qualified Streamly.Internal.Data.Array.Storable.Foreign as A
44import qualified Streamly.Internal.Data.Array.Storable.Foreign.Types as AT
43import qualified Streamly.Internal.Data.Array.Storable.Foreign.Types as AT
44+import qualified Streamly.Internal.Data.Array.Storable.Foreign.Mut.Types as MA
45
45
46import Test.Inspection
46import Test.Inspection
47#endif
47#endif
@@ -201,7 +201,7 @@ copyStreamLatin1' inh outh =
201inspect $ hasNoTypeClasses 'copyStreamLatin1'
201inspect $ hasNoTypeClasses 'copyStreamLatin1'
202inspect $ 'copyStreamLatin1' `hasNoType` ''Step
202inspect $ 'copyStreamLatin1' `hasNoType` ''Step
203inspect $ 'copyStreamLatin1' `hasNoType` ''IUF.ConcatState -- FH.read/UF.concat
203inspect $ 'copyStreamLatin1' `hasNoType` ''IUF.ConcatState -- FH.read/UF.concat
204−inspect $ 'copyStreamLatin1' `hasNoType` ''A.ReadUState -- FH.read/A.read
204+inspect $ 'copyStreamLatin1' `hasNoType` ''MA.ReadUState -- FH.read/A.read
205inspect $ 'copyStreamLatin1' `hasNoType` ''AT.ArrayUnsafe -- FH.write/writeNUnsafe
205inspect $ 'copyStreamLatin1' `hasNoType` ''AT.ArrayUnsafe -- FH.write/writeNUnsafe
206inspect $ 'copyStreamLatin1' `hasNoType` ''Strict.Tuple3' -- FH.write/chunksOf
206inspect $ 'copyStreamLatin1' `hasNoType` ''Strict.Tuple3' -- FH.write/chunksOf
207#endif
207#endif
@@ -218,7 +218,7 @@ copyStreamLatin1 inh outh =
218inspect $ hasNoTypeClasses 'copyStreamLatin1
218inspect $ hasNoTypeClasses 'copyStreamLatin1
219inspect $ 'copyStreamLatin1 `hasNoType` ''Step
219inspect $ 'copyStreamLatin1 `hasNoType` ''Step
220inspect $ 'copyStreamLatin1 `hasNoType` ''IUF.ConcatState -- FH.read/UF.concat
220inspect $ 'copyStreamLatin1 `hasNoType` ''IUF.ConcatState -- FH.read/UF.concat
221−inspect $ 'copyStreamLatin1 `hasNoType` ''A.ReadUState -- FH.read/A.read
221+inspect $ 'copyStreamLatin1 `hasNoType` ''MA.ReadUState -- FH.read/A.read
222inspect $ 'copyStreamLatin1 `hasNoType` ''AT.ArrayUnsafe -- FH.write/writeNUnsafe
222inspect $ 'copyStreamLatin1 `hasNoType` ''AT.ArrayUnsafe -- FH.write/writeNUnsafe
223inspect $ 'copyStreamLatin1 `hasNoType` ''Strict.Tuple3' -- FH.write/chunksOf
223inspect $ 'copyStreamLatin1 `hasNoType` ''Strict.Tuple3' -- FH.write/chunksOf
224#endif
224#endif
src/Streamly/FileSystem/FD.hs+1−1
@@ -137,7 +137,7 @@ import Streamly.Internal.Data.Stream.Serial (SerialT)
137import Streamly.Internal.Data.Stream.StreamK.Type (IsStream, mkStream)
137import Streamly.Internal.Data.Stream.StreamK.Type (IsStream, mkStream)
138
138
139#if !defined(mingw32_HOST_OS)
139#if !defined(mingw32_HOST_OS)
140−import Streamly.Internal.Data.Array.Storable.Foreign.Types (groupIOVecsOf)
140+import Streamly.Internal.Memory.ArrayStream (groupIOVecsOf)
141import Streamly.Internal.Data.Stream.StreamD (toStreamD)
141import Streamly.Internal.Data.Stream.StreamD (toStreamD)
142import Streamly.Internal.Data.Stream.StreamD.Type (fromStreamD)
142import Streamly.Internal.Data.Stream.StreamD.Type (fromStreamD)
143import qualified Streamly.FileSystem.FDIO as RawIO hiding (write)
143import qualified Streamly.FileSystem.FDIO as RawIO hiding (write)
src/Streamly/Internal/Data/Array/PrimInclude.hs+1−1
@@ -173,7 +173,7 @@ last arr = readIndex arr (length arr - 1)
173concat :: (IsStream t, MonadIO m, Prim a) => t m (Array a) -> t m a
173concat :: (IsStream t, MonadIO m, Prim a) => t m (Array a) -> t m a
174-- concat m = D.fromStreamD $ A.flattenArrays (D.toStreamD m)
174-- concat m = D.fromStreamD $ A.flattenArrays (D.toStreamD m)
175-- concat m = D.fromStreamD $ D.concatMap A.toStreamD (D.toStreamD m)
175-- concat m = D.fromStreamD $ D.concatMap A.toStreamD (D.toStreamD m)
176−concat m = D.fromStreamD $ D.concatMapU read (D.toStreamD m)
176+concat m = D.fromStreamD $ D.concatUnfold read (D.toStreamD m)
177
177
178-- | Coalesce adjacent arrays in incoming stream to form bigger arrays of a
178-- | Coalesce adjacent arrays in incoming stream to form bigger arrays of a
179-- maximum specified size in bytes.
179-- maximum specified size in bytes.
src/Streamly/Internal/Data/Array/Storable/Foreign.hs+3−26
@@ -68,7 +68,6 @@ module Streamly.Internal.Data.Array.Storable.Foreign
68 , A.toStream
68 , A.toStream
69 , A.toStreamRev
69 , A.toStreamRev
70 , read
70 , read
71− , ReadUState (..)
72 , unsafeRead
71 , unsafeRead
73 -- , readChunksOf
72 -- , readChunksOf
74
73
@@ -147,7 +146,7 @@ import Data.Semigroup ((<>))
147import Data.Word (Word8)
146import Data.Word (Word8)
148-- import Data.Functor.Identity (Identity)
147-- import Data.Functor.Identity (Identity)
149import Foreign.C.String (CString)
148import Foreign.C.String (CString)
150−import Foreign.ForeignPtr (withForeignPtr, touchForeignPtr, castForeignPtr)
149+import Foreign.ForeignPtr (withForeignPtr, castForeignPtr)
151import Foreign.Ptr (plusPtr, castPtr)
150import Foreign.Ptr (plusPtr, castPtr)
152import Foreign.Storable (Storable(..))
151import Foreign.Storable (Storable(..))
153import Prelude hiding (length, null, last, map, (!!), read, concat)
152import Prelude hiding (length, null, last, map, (!!), read, concat)
@@ -169,6 +168,7 @@ import qualified Streamly.Internal.Data.Fold as FL
169import qualified Streamly.Internal.Data.Stream.Prelude as P
168import qualified Streamly.Internal.Data.Stream.Prelude as P
170import qualified Streamly.Internal.Data.Stream.Serial as Serial
169import qualified Streamly.Internal.Data.Stream.Serial as Serial
171import qualified Streamly.Internal.Data.Stream.StreamD as D
170import qualified Streamly.Internal.Data.Stream.StreamD as D
171+import qualified Streamly.Internal.Data.Unfold as Unfold
172import qualified Streamly.Memory.Ring as RB
172import qualified Streamly.Memory.Ring as RB
173
173
174-------------------------------------------------------------------------------
174-------------------------------------------------------------------------------
@@ -204,35 +204,12 @@ fromStream = P.foldOnce A.write
204-- Elimination
204-- Elimination
205-------------------------------------------------------------------------------
205-------------------------------------------------------------------------------
206
206
207−data ReadUState a = ReadUState
208− {-# UNPACK #-} !(ForeignPtr a) -- foreign ptr with end of array pointer
209− {-# UNPACK #-} !(Ptr a) -- current pointer
210−
211-- | Unfold an array into a stream.
207-- | Unfold an array into a stream.
212--
208--
213-- @since 0.7.0
209-- @since 0.7.0
214{-# INLINE_NORMAL read #-}
210{-# INLINE_NORMAL read #-}
215read :: forall m a. (Monad m, Storable a) => Unfold m (Array a) a
211read :: forall m a. (Monad m, Storable a) => Unfold m (Array a) a
216−read = Unfold step inject
212+read = Unfold.lmap A.unsafeThaw MA.read
217− where
218−
219− inject (Array (ForeignPtr start contents) (Ptr end)) =
220− return $ ReadUState (ForeignPtr end contents) (Ptr start)
221−
222− {-# INLINE_LATE step #-}
223− step (ReadUState fp@(ForeignPtr end _) p) | p == Ptr end =
224− let x = A.unsafeInlineIO $ touchForeignPtr fp
225− in x `seq` return D.Stop
226− step (ReadUState fp p) = do
227− -- unsafeInlineIO allows us to run this in Identity monad for pure
228− -- toList/foldr case which makes them much faster due to not
229− -- accumulating the list and fusing better with the pure consumers.
230− --
231− -- This should be safe as the array contents are guaranteed to be
232− -- evaluated/written to before we peek at them.
233− let !x = A.unsafeInlineIO $ peek p
234− return $ D.Yield x
235− (ReadUState fp (p `plusPtr` sizeOf (undefined :: a)))
236
213
237-- | Unfold an array into a stream, does not check the end of the array, the
214-- | Unfold an array into a stream, does not check the end of the array, the
238-- user is responsible for terminating the stream within the array bounds. For
215-- user is responsible for terminating the stream within the array bounds. For
src/Streamly/Internal/Data/Array/Storable/Foreign/Mut/Types.hs+423−586
@@ -1,71 +1,69 @@
1{-# LANGUAGE UnboxedTuples #-}
1{-# LANGUAGE UnboxedTuples #-}
2
2
3−#include "inline.hs"
4−
5-- |
3-- |
6-- Module : Streamly.Internal.Data.Array.Storable.Foreign.Mut.Types
4-- Module : Streamly.Internal.Data.Array.Storable.Foreign.Mut.Types
7-- Copyright : (c) 2020 Composewell Technologies
5-- Copyright : (c) 2020 Composewell Technologies
8−--
9-- License : BSD3-3-Clause
6-- License : BSD3-3-Clause
10-- Maintainer : streamly@composewell.com
7-- Maintainer : streamly@composewell.com
11-- Stability : experimental
8-- Stability : experimental
12-- Portability : GHC
9-- Portability : GHC
13--
10--
11+-- Unboxed pinned mutable array type for 'Storable' types with an option to use
12+-- foreign (non-GHC) memory allocators. Fulfils the following goals:
13+--
14+-- * Random access (array)
15+-- * Efficient storage (unboxed)
16+-- * Performance (unboxed access)
17+-- * Performance - in-place operations (mutable)
18+-- * Performance - GC (pinned, mutable)
19+-- * interfacing with OS (pinned)
20+-- * Fragmentation control (foreign allocators)
21+--
22+-- Stream and Fold APIs allow easy, efficient and convenient operations on
23+-- arrays.
24+
14module Streamly.Internal.Data.Array.Storable.Foreign.Mut.Types
25module Streamly.Internal.Data.Array.Storable.Foreign.Mut.Types
15 (
26 (
27+ -- * Type
28+ -- $arrayNotes
16 Array (..)
29 Array (..)
17− , mutableArray
18
30
19 -- * Construction
31 -- * Construction
32+ , mutableArray
20 , withNewArray
33 , withNewArray
21 , newArray
34 , newArray
22 , newArrayAligned
35 , newArrayAligned
23 , newArrayAlignedUnmanaged
36 , newArrayAlignedUnmanaged
24 , newArrayAlignedAllocWith
37 , newArrayAlignedAllocWith
25− , unsafeSnoc
26− , snoc
27− , spliceWith
28− , spliceWithDoubling
29− , spliceTwo
30
38
39+ -- * From containers
31 , fromList
40 , fromList
32 , fromListN
41 , fromListN
33 , fromStreamDN
42 , fromStreamDN
34− -- , fromStreamD
43+ , fromStreamD
35
44
36− -- * Streams of arrays
45+ -- * Resizing
37− , fromStreamDArraysOf
46+ , realloc
38− , FlattenState (..) -- for inspection testing
47+ , shrinkToFit
39− , flattenArrays
40− , flattenArraysRev
41− , packArraysChunksOf
42− , SpliceState (..)
43− , lpackArraysChunksOf
44−#if !defined(mingw32_HOST_OS)
45− , groupIOVecsOf
46−#endif
47− , splitOn
48− , breakOn
49
48
50− -- * Elimination
49+ -- * Size
51− , unsafeIndexIO
52− , unsafeIndex
53 , length
50 , length
54 , byteLength
51 , byteLength
55 , byteCapacity
52 , byteCapacity
56− , foldl'
57− , foldr
58− , splitAt
59
53
60− , toStreamD
54+ -- * Random access
61− , toStreamDRev
55+ , unsafeIndexIO
62− , toStreamK
56+ , unsafeIndex
63− , toStreamKRev
57+
64− , toList
58+ -- * Mutation
59+ , unsafeSnoc
60+ , snoc
65
61
66− , unlines
62+ -- * Folding
63+ , foldl'
64+ , foldr
67
65
68− -- * Folds
66+ -- * Composable Folds
69 , toArrayMinChunk
67 , toArrayMinChunk
70 , writeNAllocWith
68 , writeNAllocWith
71 , writeN
69 , writeN
@@ -76,24 +74,50 @@ module Streamly.Internal.Data.Array.Storable.Foreign.Mut.Types
76 , write
74 , write
77 , writeAligned
75 , writeAligned
78
76
77+ -- * Unfolds
78+ , ReadUState
79+ , read
80+ , flattenArrays
81+ , flattenArraysRev
82+
83+ -- * To containers
84+ , toStreamD
85+ , toStreamDRev
86+ , toStreamK
87+ , toStreamKRev
88+ , toList
89+
90+ -- * Combining
91+ , spliceWith
92+ , spliceWithDoubling
93+ , spliceTwo
94+
95+ -- * Splitting
96+ , breakOn
97+ , splitAt
98+
99+ -- * Stream of arrays
100+ , arraysOf
101+ , bufferChunks
102+ , writeChunks
103+
79 -- * Utilities
104 -- * Utilities
80 , defaultChunkSize
105 , defaultChunkSize
81 , mkChunkSize
106 , mkChunkSize
82 , mkChunkSizeKB
107 , mkChunkSizeKB
108+ , bytesToElemCount
83 , unsafeInlineIO
109 , unsafeInlineIO
84− , realloc
85− , shrinkToFit
86 , memcpy
110 , memcpy
87 , memcmp
111 , memcmp
88− , bytesToElemCount
89 )
112 )
90where
113where
91
114
115+#include "inline.hs"
116+
92import Control.Exception (assert)
117import Control.Exception (assert)
93import Control.DeepSeq (NFData(..))
118import Control.DeepSeq (NFData(..))
94import Control.Monad (when, void)
119import Control.Monad (when, void)
95import Control.Monad.IO.Class (MonadIO(..))
120import Control.Monad.IO.Class (MonadIO(..))
96−import Data.Bifunctor (first)
97import Data.Functor.Identity (runIdentity)
121import Data.Functor.Identity (runIdentity)
98#if __GLASGOW_HASKELL__ < 808
122#if __GLASGOW_HASKELL__ < 808
99import Data.Semigroup (Semigroup(..))
123import Data.Semigroup (Semigroup(..))
@@ -109,11 +133,8 @@ import GHC.Exts (IsList, IsString(..))
109import GHC.ForeignPtr (ForeignPtr(..))
133import GHC.ForeignPtr (ForeignPtr(..))
110import GHC.IO (IO(IO), unsafePerformIO)
134import GHC.IO (IO(IO), unsafePerformIO)
111import GHC.Ptr (Ptr(..))
135import GHC.Ptr (Ptr(..))
112−#if !defined(mingw32_HOST_OS)
113−import Streamly.FileSystem.FDIO (IOVec(..))
114−#endif
115import Streamly.Internal.Data.Fold.Types (Fold(..))
136import Streamly.Internal.Data.Fold.Types (Fold(..))
116−import Streamly.Internal.Data.Tuple.Strict (Tuple'(..))
137+import Streamly.Internal.Data.Unfold.Types (Unfold(..))
117import Streamly.Internal.Data.SVar (adaptState)
138import Streamly.Internal.Data.SVar (adaptState)
118import Text.Read (readPrec, readListPrec, readListPrecDefault)
139import Text.Read (readPrec, readListPrec, readListPrecDefault)
119
140
@@ -137,61 +158,27 @@ plusForeignPtr :: ForeignPtr a -> Int -> ForeignPtr b
137plusForeignPtr (ForeignPtr addr c) (I# d) = ForeignPtr (plusAddr# addr d) c
158plusForeignPtr (ForeignPtr addr c) (I# d) = ForeignPtr (plusAddr# addr d) c
138#endif
159#endif
139
160
140−-------------------------------------------------------------------------------
141−-- Design Notes
142−-------------------------------------------------------------------------------
143−
144−-- There are two goals that we want to fulfill with arrays. One, holding large
145−-- amounts of data in non-GC memory and the ability to pass this data to and
146−-- from the operating system without an extra copy overhead. Two, allow random
147−-- access to elements based on their indices. The first one falls in the
148−-- category of storage buffers while the second one falls in the category of
149−-- maps/multisets/hashmaps.
150−--
151−-- For the first requirement we use an array of Storables and store it in a
152−-- ForeignPtr. We can have both immutable and mutable variants of this array
153−-- using wrappers over the same underlying type.
154−--
155−-- For the second requirement, we need a separate type for arrays of
156−-- polymorphic values, for example vectors of handler functions, lookup tables.
157−-- We can call this type a "vector" in contrast to arrays. It should not
158−-- require Storable instance for the type. In that case we need to use an
159−-- Array# instead of a ForeignPtr. This type of array would not reduce the GC
160−-- overhead much because each element of the array still needs to be scanned by
161−-- the GC. However, it can store polymorphic elements and allow random access
162−-- to those. But in most cases random access means storage, and it means we
163−-- need to avoid GC scanning except in cases of trivially small storage. One
164−-- way to achieve that would be to put the array in a Compact region. However,
165−-- if and when we mutate this, we will have to use a manual GC copying out to
166−-- another CR and freeing the old one.
167−
168-------------------------------------------------------------------------------
161-------------------------------------------------------------------------------
169-- Array Data Type
162-- Array Data Type
170-------------------------------------------------------------------------------
163-------------------------------------------------------------------------------
171
164
172−-- We require that an array stores only Storable. Arrays are used for buffering
165+-- $arrayNotes
173−-- data while streams are used for processing. If you want something to be
174−-- buffered it better be Storable so that we can store it in non-GC memory.
175--
166--
176−-- We can use a "Storable" constraint in the Array type and the Constraint can
167+-- We can use a 'Storable' constraint in the Array type and the constraint can
177-- be automatically provided to a function that pattern matches on the Array
168-- be automatically provided to a function that pattern matches on the Array
178-- type. However, it has huge performance cost, so we do not use it.
169-- type. However, it has huge performance cost, so we do not use it.
179−-- XXX Can this cost be alleviated in GHC-8.10 specialization fix?
170+-- Investigate a GHC improvement possiblity.
180--
171--
181−-- XXX Another way to not require the Storable constraint in array operations
172+-- XXX Rename the fields to better names.
182−-- is to store the elemSize in the array at construction and use that instead
183−-- of using sizeOf. Need to charaterize perf cost of this.
184−--
185−-- XXX rename the fields to "start, next, end".
186--
173--
187data Array a =
174data Array a =
188#ifdef DEVBUILD
175#ifdef DEVBUILD
189 Storable a =>
176 Storable a =>
190#endif
177#endif
191 Array
178 Array
192− { aStart :: {-# UNPACK #-} !(ForeignPtr a) -- first address
179+ { aStart :: {-# UNPACK #-} !(ForeignPtr a) -- ^ first address
193− , aEnd :: {-# UNPACK #-} !(Ptr a) -- first unused address
180+ , aEnd :: {-# UNPACK #-} !(Ptr a) -- ^ first unused address
194− , aBound :: {-# UNPACK #-} !(Ptr a) -- first address beyond allocated memory
181+ , aBound :: {-# UNPACK #-} !(Ptr a) -- ^ first address beyond allocated memory
195 }
182 }
196
183
197{-# INLINE mutableArray #-}
184{-# INLINE mutableArray #-}
@@ -318,6 +305,10 @@ withNewArray count f = do
318 arr <- newArray count
305 arr <- newArray count
319 withForeignPtr (aStart arr) $ \p -> f p >> return arr
306 withForeignPtr (aStart arr) $ \p -> f p >> return arr
320
307
308+-------------------------------------------------------------------------------
309+-- snoc
310+-------------------------------------------------------------------------------
311+
321-- XXX grow the array when we are beyond bound.
312-- XXX grow the array when we are beyond bound.
322--
313--
323-- Internal routine for when the array is being created. Appends one item at
314-- Internal routine for when the array is being created. Appends one item at
@@ -358,6 +349,10 @@ snoc arr@Array {..} x =
358 touchForeignPtr aStart
349 touchForeignPtr aStart
359 return $ arr {aEnd = aEnd `plusPtr` sizeOf (undefined :: a)}
350 return $ arr {aEnd = aEnd `plusPtr` sizeOf (undefined :: a)}
360
351
352+-------------------------------------------------------------------------------
353+-- re-allocate
354+-------------------------------------------------------------------------------
355+
361-- | Reallocate the array to the specified size in bytes. If the size is less
356-- | Reallocate the array to the specified size in bytes. If the size is less
362-- than the original array the array gets truncated.
357-- than the original array the array gets truncated.
363{-# NOINLINE reallocAligned #-}
358{-# NOINLINE reallocAligned #-}
@@ -381,117 +376,59 @@ reallocAligned alignSize newSize Array{..} = do
381realloc :: forall a. Storable a => Int -> Array a -> IO (Array a)
376realloc :: forall a. Storable a => Int -> Array a -> IO (Array a)
382realloc = reallocAligned (alignment (undefined :: a))
377realloc = reallocAligned (alignment (undefined :: a))
383
378
384−-- | Create an 'Array' from the first N elements of a list. The array is
379+-------------------------------------------------------------------------------
385−-- allocated to size N, if the list terminates before N elements then the
380+-- Elimination
386−-- array may hold less than N elements.
381+-------------------------------------------------------------------------------
387−--
388−-- @since 0.7.0
389−{-# INLINABLE fromListN #-}
390−fromListN :: Storable a => Int -> [a] -> Array a
391−fromListN n xs = unsafePerformIO $ fromStreamDN n $ D.fromList xs
392
382
393−-- | Create an 'Array' from a list. The list must be of finite size.
383+-- | Return element at the specified index without checking the bounds.
394--
384--
395−-- @since 0.7.0
385+-- Unsafe because it does not check the bounds of the array.
396−{-# INLINABLE fromList #-}
397−fromList :: Storable a => [a] -> Array a
398−fromList xs = unsafePerformIO $ fromStreamD $ D.fromList xs
399−
400−{-# INLINE_NORMAL fromStreamDN #-}
386+{-# INLINE_NORMAL unsafeIndexIO #-}
401−fromStreamDN :: forall m a. (MonadIO m, Storable a)
387+unsafeIndexIO :: forall a. Storable a => Array a -> Int -> IO a
402− => Int -> D.Stream m a -> m (Array a)
388+unsafeIndexIO Array {..} i =
403−fromStreamDN limit str = do
389+ withForeignPtr aStart $ \p -> do
404− arr <- liftIO $ newArray limit
390+ let elemSize = sizeOf (undefined :: a)
405− end <- D.foldlM' fwrite (return $ aEnd arr) $ D.take limit str
391+ elemOff = p `plusPtr` (elemSize * i)
406− return $ arr {aEnd = end}
392+ assert (i >= 0 && elemOff `plusPtr` elemSize <= aEnd)
407−
393+ (return ())
408− where
394+ peek elemOff
409
395
410− fwrite ptr x = do
396+-- | Return element at the specified index without checking the bounds.
411− liftIO $ poke ptr x
397+{-# INLINE_NORMAL unsafeIndex #-}
412− return $ ptr `plusPtr` sizeOf (undefined :: a)
398+unsafeIndex :: forall a. Storable a => Array a -> Int -> a
399+unsafeIndex arr i = let !r = unsafeInlineIO $ unsafeIndexIO arr i in r
413
400
414−-- CAUTION: a very large number (millions) of arrays can degrade performance
401+-- | /O(1)/ Get the byte length of the array.
415−-- due to GC overhead because we need to buffer the arrays before we flatten
416−-- all the arrays.
417−--
418−-- We could take the approach of doubling the memory allocation on each
419−-- overflow. This would result in more or less the same amount of copying as in
420−-- the chunking approach. However, if we have to shrink in the end then it may
421−-- result in an extra copy of the entire data.
422--
402--
423−{-# INLINE fromStreamD #-}
403+-- @since 0.7.0
424−fromStreamD :: (MonadIO m, Storable a) => D.Stream m a -> m (Array a)
425−fromStreamD m = do
426− let s = fromStreamDArraysOf defaultChunkSize m
427− buffered <- D.foldr K.cons K.nil s
428− len <- K.foldl' (+) 0 (K.map length buffered)
429− fromStreamDN len $ flattenArrays $ D.fromStreamK buffered
430−{-
431−fromStreamD m = foldOnce write m
432− where
433− foldOnce (Fold step begin done) = D.foldlMx' step begin done
434−-}
435−
436−
437−-- Splice two immutable arrays creating a new array.
438−{-# INLINE spliceTwo #-}
439−spliceTwo :: (MonadIO m, Storable a) => Array a -> Array a -> m (Array a)
440−spliceTwo arr1 arr2 = do
441− let src1 = unsafeForeignPtrToPtr (aStart arr1)
442− src2 = unsafeForeignPtrToPtr (aStart arr2)
443− len1 = aEnd arr1 `minusPtr` src1
444− len2 = aEnd arr2 `minusPtr` src2
445−
446− arr <- liftIO $ newArray (len1 + len2)
447− let dst = unsafeForeignPtrToPtr (aStart arr)
448−
449− -- XXX Should we use copyMutableByteArray# instead? Is there an overhead to
450− -- ccall?
451− liftIO $ do
452− memcpy (castPtr dst) (castPtr src1) len1
453− touchForeignPtr (aStart arr1)
454− memcpy (castPtr (dst `plusPtr` len1)) (castPtr src2) len2
455− touchForeignPtr (aStart arr2)
456− return arr { aEnd = dst `plusPtr` (len1 + len2) }
457−
458−-- Splice an array into a pre-reserved mutable array. The user must ensure
459−-- that there is enough space in the mutable array.
460−{-# INLINE spliceWith #-}
461−spliceWith :: (MonadIO m) => Array a -> Array a -> m (Array a)
462−spliceWith dst@(Array _ end bound) src =
463− liftIO $ do
464− let srcLen = byteLength src
404+{-# INLINE byteLength #-}
465− if end `plusPtr` srcLen > bound
405+byteLength :: Array a -> Int
466− then error
406+byteLength Array{..} =
467− "Bug: spliceIntoUnsafe: Not enough space in the target array"
407+ let p = unsafeForeignPtrToPtr aStart
468− else withForeignPtr (aStart dst) $ \_ ->
408+ len = aEnd `minusPtr` p
469− withForeignPtr (aStart src) $ \psrc -> do
409+ in assert (len >= 0) len
470− let pdst = aEnd dst
471− memcpy (castPtr pdst) (castPtr psrc) srcLen
472− return $ dst {aEnd = pdst `plusPtr` srcLen}
473
410
474−-- Splice a new array into a preallocated mutable array, doubling the space if
411+-- | /O(1)/ Get the length of the array i.e. the number of elements in the
475−-- there is no space in the target array.
412+-- array.
476−{-# INLINE spliceWithDoubling #-}
413+--
477−spliceWithDoubling :: (MonadIO m, Storable a)
414+-- @since 0.7.0
478− => Array a -> Array a -> m (Array a)
415+{-# INLINE length #-}
479−spliceWithDoubling dst@(Array start end bound) src = do
416+length :: forall a. Storable a => Array a -> Int
480− assert (end <= bound) (return ())
417+length arr = byteLength arr `div` sizeOf (undefined :: a)
481− let srcLen = aEnd src `minusPtr` unsafeForeignPtrToPtr (aStart src)
482
418
483− dst1 <-
419+-- | Get the total capacity of an array. An array may have space reserved
484− if end `plusPtr` srcLen >= bound
420+-- beyond the current used length of the array.
485− then do
421+--
422+-- /Internal/
423+{-# INLINE byteCapacity #-}
424+byteCapacity :: Array a -> Int
425+byteCapacity Array{..} =
486− let oldStart = unsafeForeignPtrToPtr start
426+ let p = unsafeForeignPtrToPtr aStart
487− oldSize = end `minusPtr` oldStart
427+ len = aBound `minusPtr` p
488− newSize = max (oldSize * 2) (oldSize + srcLen)
428+ in assert (len >= 0) len
489− liftIO $ realloc newSize dst
490− else return dst
491− spliceWith dst1 src
492
429
493-------------------------------------------------------------------------------
430-------------------------------------------------------------------------------
494−-- Streams of arrays
431+-- Streams of arrays - Creation
495-------------------------------------------------------------------------------
432-------------------------------------------------------------------------------
496
433
497data GroupState s start end bound
434data GroupState s start end bound
@@ -500,13 +437,20 @@ data GroupState s start end bound
500 | GroupYield start end bound (GroupState s start end bound)
437 | GroupYield start end bound (GroupState s start end bound)
501 | GroupFinish
438 | GroupFinish
502
439
503−-- | @fromStreamArraysOf n stream@ groups the input stream into a stream of
440+-- | @arraysOf n stream@ groups the input stream into a stream of
504-- arrays of size n.
441-- arrays of size n.
442+--
443+-- @arraysOf n = StreamD.foldMany (Array.writeN n)@
444+--
445+-- /Internal/
505−{-# INLINE_NORMAL fromStreamDArraysOf #-}
446+{-# INLINE_NORMAL arraysOf #-}
506−fromStreamDArraysOf :: forall m a. (MonadIO m, Storable a)
447+arraysOf :: forall m a. (MonadIO m, Storable a)
507 => Int -> D.Stream m a -> D.Stream m (Array a)
448 => Int -> D.Stream m a -> D.Stream m (Array a)
508−-- fromStreamDArraysOf n str = D.groupsOf n (writeN n) str
449+-- XXX the idiomatic implementation leads to large regression in the D.reverse'
450+-- benchmark. It seems it has difficulty producing optimized code when
451+-- converting to StreamK. Investigate GHC optimizations.
452+-- arraysOf n = D.foldMany (writeN n)
509−fromStreamDArraysOf n (D.Stream step state) =
453+arraysOf n (D.Stream step state) =
510 D.Stream step' (GroupStart state)
454 D.Stream step' (GroupStart state)
511
455
512 where
456 where
@@ -538,12 +482,57 @@ fromStreamDArraysOf n (D.Stream step state) =
538
482
539 step' _ GroupFinish = return D.Stop
483 step' _ GroupFinish = return D.Stop
540
484
541−-- XXX concatMap does not seem to have the best possible performance so we have
485+-- XXX buffer to a list instead?
542−-- a custom way to concat arrays.
486+-- | Buffer the stream into arrays in memory.
487+{-# INLINE bufferChunks #-}
488+bufferChunks :: (MonadIO m, Storable a) =>
489+ D.Stream m a -> m (K.Stream m (Array a))
490+bufferChunks m = D.foldr K.cons K.nil $ arraysOf defaultChunkSize m
491+
492+-------------------------------------------------------------------------------
493+-- Streams of arrays - flatten
494+-------------------------------------------------------------------------------
495+
496+data ReadUState a = ReadUState
497+ {-# UNPACK #-} !(ForeignPtr a) -- foreign ptr with end of array pointer
498+ {-# UNPACK #-} !(Ptr a) -- current pointer
499+
500+-- | Unfold an array into a stream.
501+--
502+-- @since 0.7.0
503+{-# INLINE_NORMAL read #-}
504+read :: forall m a. (Monad m, Storable a) => Unfold m (Array a) a
505+read = Unfold step inject
506+ where
507+
508+ inject (Array (ForeignPtr start contents) (Ptr end) _) =
509+ return $ ReadUState (ForeignPtr end contents) (Ptr start)
510+
511+ {-# INLINE_LATE step #-}
512+ step (ReadUState fp@(ForeignPtr end _) p) | p == Ptr end =
513+ let x = unsafeInlineIO $ touchForeignPtr fp
514+ in x `seq` return D.Stop
515+ step (ReadUState fp p) = do
516+ -- unsafeInlineIO allows us to run this in Identity monad for pure
517+ -- toList/foldr case which makes them much faster due to not
518+ -- accumulating the list and fusing better with the pure consumers.
519+ --
520+ -- This should be safe as the array contents are guaranteed to be
521+ -- evaluated/written to before we peek at them.
522+ let !x = unsafeInlineIO $ peek p
523+ return $ D.Yield x
524+ (ReadUState fp (p `plusPtr` sizeOf (undefined :: a)))
525+
543data FlattenState s a =
526data FlattenState s a =
544 OuterLoop s
527 OuterLoop s
545 | InnerLoop s !(ForeignPtr a) !(Ptr a) !(Ptr a)
528 | InnerLoop s !(ForeignPtr a) !(Ptr a) !(Ptr a)
546
529
530+-- | Use the "read" unfold instead.
531+--
532+-- @flattenArrays = concatUnfold read@
533+--
534+-- We can try this if there are any fusion issues in the unfold.
535+--
547{-# INLINE_NORMAL flattenArrays #-}
536{-# INLINE_NORMAL flattenArrays #-}
548flattenArrays :: forall m a. (MonadIO m, Storable a)
537flattenArrays :: forall m a. (MonadIO m, Storable a)
549 => D.Stream m (Array a) -> D.Stream m a
538 => D.Stream m (Array a) -> D.Stream m a
@@ -601,350 +590,43 @@ flattenArraysRev (D.Stream step state) = D.Stream step' (OuterLoop state)
601 return $ D.Yield x (InnerLoop st startf
590 return $ D.Yield x (InnerLoop st startf
602 (p `plusPtr` negate (sizeOf (undefined :: a))) end)
591 (p `plusPtr` negate (sizeOf (undefined :: a))) end)
603
592
604−data SpliceState s arr
593+-------------------------------------------------------------------------------
605− = SpliceInitial s
594+-- to Lists and streams
606− | SpliceBuffering s arr
595+-------------------------------------------------------------------------------
607− | SpliceYielding arr (SpliceState s arr)
608− | SpliceFinish
609−
610−-- XXX can use general grouping combinators to achieve this?
611−-- | Coalesce adjacent arrays in incoming stream to form bigger arrays of a
612−-- maximum specified size. Note that if a single array is bigger than the
613−-- specified size we do not split it to fit. When we coalesce multiple arrays
614−-- if the size would exceed the specified size we do not coalesce therefore the
615−-- actual array size may be less than the specified chunk size.
616−--
617−-- @since 0.7.0
618−{-# INLINE_NORMAL packArraysChunksOf #-}
619−packArraysChunksOf :: (MonadIO m, Storable a)
620− => Int -> D.Stream m (Array a) -> D.Stream m (Array a)
621−packArraysChunksOf n (D.Stream step state) =
622− D.Stream step' (SpliceInitial state)
623
596
597+-- Use foldr/build fusion to fuse with list consumers
598+-- This can be useful when using the IsList instance
599+{-# INLINE_LATE toListFB #-}
600+toListFB :: forall a b. Storable a => (a -> b -> b) -> b -> Array a -> b
601+toListFB c n Array{..} = go (unsafeForeignPtrToPtr aStart)
624 where
602 where
625
603
626− {-# INLINE_LATE step' #-}
604+ go p | p == aEnd = n
627− step' gst (SpliceInitial st) = do
605+ go p =
628− when (n <= 0) $
606+ -- unsafeInlineIO allows us to run this in Identity monad for pure
629− -- XXX we can pass the module string from the higher level API
607+ -- toList/foldr case which makes them much faster due to not
630− error $ "Streamly.Internal.Data.Array.Storable.Foreign.Mut.Types.packArraysChunksOf: the size of "
608+ -- accumulating the list and fusing better with the pure consumers.
631− ++ "arrays [" ++ show n ++ "] must be a natural number"
609+ --
632− r <- step gst st
610+ -- This should be safe as the array contents are guaranteed to be
633− case r of
611+ -- evaluated/written to before we peek at them.
634− D.Yield arr s -> return $
612+ let !x = unsafeInlineIO $ do
635− let len = byteLength arr
613+ r <- peek p
636− in if len >= n
614+ touchForeignPtr aStart
637− then D.Skip (SpliceYielding arr (SpliceInitial s))
615+ return r
638− else D.Skip (SpliceBuffering s arr)
616+ in c x (go (p `plusPtr` sizeOf (undefined :: a)))
639− D.Skip s -> return $ D.Skip (SpliceInitial s)
640− D.Stop -> return D.Stop
641−
642− step' gst (SpliceBuffering st buf) = do
643− r <- step gst st
644− case r of
645− D.Yield arr s -> do
646− let len = byteLength buf + byteLength arr
647− if len > n
648− then return $
649− D.Skip (SpliceYielding buf (SpliceBuffering s arr))
650− else do
651− buf' <- if byteCapacity buf < n
652− then liftIO $ realloc n buf
653− else return buf
654− buf'' <- spliceWith buf' arr
655− return $ D.Skip (SpliceBuffering s buf'')
656− D.Skip s -> return $ D.Skip (SpliceBuffering s buf)
657− D.Stop -> return $ D.Skip (SpliceYielding buf SpliceFinish)
658−
659− step' _ SpliceFinish = return D.Stop
660−
661− step' _ (SpliceYielding arr next) = return $ D.Yield arr next
662−
663−-- XXX instead of writing two different versions of this operation, we should
664−-- write it as a pipe.
665−{-# INLINE_NORMAL lpackArraysChunksOf #-}
666−lpackArraysChunksOf :: (MonadIO m, Storable a)
667− => Int -> Fold m (Array a) () -> Fold m (Array a) ()
668−lpackArraysChunksOf n (Fold step1 initial1 extract1) =
669− Fold step initial extract
670
617
671− where
618+-- | Convert an 'Array' into a list.
619+--
620+-- @since 0.7.0
621+{-# INLINE toList #-}
622+toList :: Storable a => Array a -> [a]
623+toList s = build (\c n -> toListFB c n s)
672
624
673− initial = do
625+-- | Use the 'read' unfold instead.
674− when (n <= 0) $
675− -- XXX we can pass the module string from the higher level API
676− error $ "Streamly.Internal.Data.Array.Storable.Foreign.Mut.Types.packArraysChunksOf: the size of "
677− ++ "arrays [" ++ show n ++ "] must be a natural number"
678−
679− r <- initial1
680− return $ first (Tuple' Nothing) r
681−
682− extract (Tuple' Nothing r1) = extract1 r1
683− extract (Tuple' (Just buf) r1) = do
684− r <- step1 r1 buf
685− case r of
686− FL.Partial rr -> extract1 rr
687− FL.Done _ -> return ()
688−
689− step (Tuple' Nothing r1) arr =
690− let len = byteLength arr
691− in if len >= n
692− then do
693− r <- step1 r1 arr
694− case r of
695− FL.Done _ -> return $ FL.Done ()
696− FL.Partial s -> do
697− extract1 s
698− res <- initial1
699− return $ first (Tuple' Nothing) res
700− else return $ FL.Partial $ Tuple' (Just arr) r1
701−
702− step (Tuple' (Just buf) r1) arr = do
703− let len = byteLength buf + byteLength arr
704− buf' <- if byteCapacity buf < len
705− then liftIO $ realloc (max n len) buf
706− else return buf
707− buf'' <- spliceWith buf' arr
708−
709− -- XXX this is common in both the equations of step
710− if len >= n
711− then do
712− r <- step1 r1 buf''
713− case r of
714− FL.Done _ -> return $ FL.Done ()
715− FL.Partial s -> do
716− extract1 s
717− res <- initial1
718− return $ first (Tuple' Nothing) res
719− else return $ FL.Partial $ Tuple' (Just buf'') r1
720−
721−#if !defined(mingw32_HOST_OS)
722−data GatherState s arr
723− = GatherInitial s
724− | GatherBuffering s arr Int
725− | GatherYielding arr (GatherState s arr)
726− | GatherFinish
727−
728−-- | @groupIOVecsOf maxBytes maxEntries@ groups arrays in the incoming stream
729−-- to create a stream of 'IOVec' arrays with a maximum of @maxBytes@ bytes in
730−-- each array and a maximum of @maxEntries@ entries in each array.
731−--
732−-- @since 0.7.0
733−{-# INLINE_NORMAL groupIOVecsOf #-}
734−groupIOVecsOf :: MonadIO m
735− => Int -> Int -> D.Stream m (Array a) -> D.Stream m (Array IOVec)
736−groupIOVecsOf n maxIOVLen (D.Stream step state) =
737− D.Stream step' (GatherInitial state)
738−
739− where
740−
741− {-# INLINE_LATE step' #-}
742− step' gst (GatherInitial st) = do
743− when (n <= 0) $
744− -- XXX we can pass the module string from the higher level API
745− error $ "Streamly.Internal.Data.Array.Storable.Foreign.Mut.Types.groupIOVecsOf: the size of "
746− ++ "groups [" ++ show n ++ "] must be a natural number"
747− when (maxIOVLen <= 0) $
748− -- XXX we can pass the module string from the higher level API
749− error $ "Streamly.Internal.Data.Array.Storable.Foreign.Mut.Types.groupIOVecsOf: the number of "
750− ++ "IOVec entries [" ++ show n ++ "] must be a natural number"
751− r <- step (adaptState gst) st
752− case r of
753− D.Yield arr s -> do
754− let p = unsafeForeignPtrToPtr (aStart arr)
755− len = byteLength arr
756− iov <- liftIO $ newArray maxIOVLen
757− iov' <- liftIO $ unsafeSnoc iov (IOVec (castPtr p)
758− (fromIntegral len))
759− if len >= n
760− then return $ D.Skip (GatherYielding iov' (GatherInitial s))
761− else return $ D.Skip (GatherBuffering s iov' len)
762− D.Skip s -> return $ D.Skip (GatherInitial s)
763− D.Stop -> return D.Stop
764−
765− step' gst (GatherBuffering st iov len) = do
766− r <- step (adaptState gst) st
767− case r of
768− D.Yield arr s -> do
769− let p = unsafeForeignPtrToPtr (aStart arr)
770− alen = byteLength arr
771− len' = len + alen
772− if len' > n || length iov >= maxIOVLen
773− then do
774− iov' <- liftIO $ newArray maxIOVLen
775− iov'' <- liftIO $ unsafeSnoc iov' (IOVec (castPtr p)
776− (fromIntegral alen))
777− return $ D.Skip (GatherYielding iov
778− (GatherBuffering s iov'' alen))
779− else do
780− iov' <- liftIO $ unsafeSnoc iov (IOVec (castPtr p)
781− (fromIntegral alen))
782− return $ D.Skip (GatherBuffering s iov' len')
783− D.Skip s -> return $ D.Skip (GatherBuffering s iov len)
784− D.Stop -> return $ D.Skip (GatherYielding iov GatherFinish)
785−
786− step' _ GatherFinish = return D.Stop
787−
788− step' _ (GatherYielding iov next) = return $ D.Yield iov next
789−#endif
790−
791−
792−data SplitState s arr
793− = Initial s
794− | Buffering s arr
795− | Splitting s arr
796− | Yielding arr (SplitState s arr)
797− | Finishing
798−
799−-- | Split a stream of arrays on a given separator byte, dropping the separator
800−-- and coalescing all the arrays between two separators into a single array.
801−--
802−-- @since 0.7.0
803−{-# INLINE_NORMAL splitOn #-}
804−splitOn
805− :: MonadIO m
806− => Word8
807− -> D.Stream m (Array Word8)
808− -> D.Stream m (Array Word8)
809−splitOn byte (D.Stream step state) = D.Stream step' (Initial state)
810−
811− where
812−
813− {-# INLINE_LATE step' #-}
814− step' gst (Initial st) = do
815− r <- step gst st
816− case r of
817− D.Yield arr s -> do
818− (arr1, marr2) <- breakOn byte arr
819− return $ case marr2 of
820− Nothing -> D.Skip (Buffering s arr1)
821− Just arr2 -> D.Skip (Yielding arr1 (Splitting s arr2))
822− D.Skip s -> return $ D.Skip (Initial s)
823− D.Stop -> return D.Stop
824−
825− step' gst (Buffering st buf) = do
826− r <- step gst st
827− case r of
828− D.Yield arr s -> do
829− (arr1, marr2) <- breakOn byte arr
830− buf' <- spliceTwo buf arr1
831− return $ case marr2 of
832− Nothing -> D.Skip (Buffering s buf')
833− Just x -> D.Skip (Yielding buf' (Splitting s x))
834− D.Skip s -> return $ D.Skip (Buffering s buf)
835− D.Stop -> return $
836− if byteLength buf == 0
837− then D.Stop
838− else D.Skip (Yielding buf Finishing)
839−
840− step' _ (Splitting st buf) = do
841− (arr1, marr2) <- breakOn byte buf
842− return $ case marr2 of
843− Nothing -> D.Skip $ Buffering st arr1
844− Just arr2 -> D.Skip $ Yielding arr1 (Splitting st arr2)
845−
846− step' _ (Yielding arr next) = return $ D.Yield arr next
847− step' _ Finishing = return D.Stop
848−
849−
850−-- Drops the separator byte
851−{-# INLINE breakOn #-}
852−breakOn :: MonadIO m
853− => Word8 -> Array Word8 -> m (Array Word8, Maybe (Array Word8))
854−breakOn sep arr@Array{..} = liftIO $ do
855− let p = unsafeForeignPtrToPtr aStart
856− loc <- c_memchr p sep (fromIntegral $ aEnd `minusPtr` p)
857− return $
858− if loc == nullPtr
859− then (arr, Nothing)
860− else
861− ( Array
862− { aStart = aStart
863− , aEnd = loc
864− , aBound = loc
865− }
866− , Just $ Array
867− { aStart = aStart `plusForeignPtr` (loc `minusPtr` p + 1)
868− , aEnd = aEnd
869− , aBound = aBound
870− }
871− )
872−
873−-------------------------------------------------------------------------------
874−-- Elimination
875−-------------------------------------------------------------------------------
876−
877−-- | Return element at the specified index without checking the bounds.
878--
626--
879−-- Unsafe because it does not check the bounds of the array.
627+-- @toStreamD = D.unfold read@
880−{-# INLINE_NORMAL unsafeIndexIO #-}
881−unsafeIndexIO :: forall a. Storable a => Array a -> Int -> IO a
882−unsafeIndexIO Array {..} i =
883− withForeignPtr aStart $ \p -> do
884− let elemSize = sizeOf (undefined :: a)
885− elemOff = p `plusPtr` (elemSize * i)
886− assert (i >= 0 && elemOff `plusPtr` elemSize <= aEnd)
887− (return ())
888− peek elemOff
889−
890−-- | Return element at the specified index without checking the bounds.
891−{-# INLINE_NORMAL unsafeIndex #-}
892−unsafeIndex :: forall a. Storable a => Array a -> Int -> a
893−unsafeIndex arr i = let !r = unsafeInlineIO $ unsafeIndexIO arr i in r
894−
895−-- | /O(1)/ Get the byte length of the array.
896−--
897−-- @since 0.7.0
898−{-# INLINE byteLength #-}
899−byteLength :: Array a -> Int
900−byteLength Array{..} =
901− let p = unsafeForeignPtrToPtr aStart
902− len = aEnd `minusPtr` p
903− in assert (len >= 0) len
904−
905−-- | /O(1)/ Get the length of the array i.e. the number of elements in the
906−-- array.
907--
628--
908−-- @since 0.7.0
629+-- We can try this if the unfold has any performance issues.
909−{-# INLINE length #-}
910−length :: forall a. Storable a => Array a -> Int
911−length arr = byteLength arr `div` sizeOf (undefined :: a)
912−
913−{-# INLINE byteCapacity #-}
914−byteCapacity :: Array a -> Int
915−byteCapacity Array{..} =
916− let p = unsafeForeignPtrToPtr aStart
917− len = aBound `minusPtr` p
918− in assert (len >= 0) len
919−
920−-- Use foldr/build fusion to fuse with list consumers
921−-- This can be useful when using the IsList instance
922−{-# INLINE_LATE toListFB #-}
923−toListFB :: forall a b. Storable a => (a -> b -> b) -> b -> Array a -> b
924−toListFB c n Array{..} = go (unsafeForeignPtrToPtr aStart)
925− where
926−
927− go p | p == aEnd = n
928− go p =
929− -- unsafeInlineIO allows us to run this in Identity monad for pure
930− -- toList/foldr case which makes them much faster due to not
931− -- accumulating the list and fusing better with the pure consumers.
932− --
933− -- This should be safe as the array contents are guaranteed to be
934− -- evaluated/written to before we peek at them.
935− let !x = unsafeInlineIO $ do
936− r <- peek p
937− touchForeignPtr aStart
938− return r
939− in c x (go (p `plusPtr` sizeOf (undefined :: a)))
940−
941−-- | Convert an 'Array' into a list.
942−--
943−-- @since 0.7.0
944−{-# INLINE toList #-}
945−toList :: Storable a => Array a -> [a]
946−toList s = build (\c n -> toListFB c n s)
947−
948{-# INLINE_NORMAL toStreamD #-}
630{-# INLINE_NORMAL toStreamD #-}
949toStreamD :: forall m a. (Monad m, Storable a) => Array a -> D.Stream m a
631toStreamD :: forall m a. (Monad m, Storable a) => Array a -> D.Stream m a
950toStreamD Array{..} =
632toStreamD Array{..} =
@@ -1019,70 +701,22 @@ toStreamKRev Array {..} =
1019 return r
701 return r
1020 in x `K.cons` go (p `plusPtr` negate (sizeOf (undefined :: a)))
702 in x `K.cons` go (p `plusPtr` negate (sizeOf (undefined :: a)))
1021
703
704+-------------------------------------------------------------------------------
705+-- Folding
706+-------------------------------------------------------------------------------
707+
708+-- | Strict left fold of an array.
1022{-# INLINE_NORMAL foldl' #-}
709{-# INLINE_NORMAL foldl' #-}
1023foldl' :: forall a b. Storable a => (b -> a -> b) -> b -> Array a -> b
710foldl' :: forall a b. Storable a => (b -> a -> b) -> b -> Array a -> b
1024foldl' f z arr = runIdentity $ D.foldl' f z $ toStreamD arr
711foldl' f z arr = runIdentity $ D.foldl' f z $ toStreamD arr
1025
712
713+-- | Right fold of an array.
1026{-# INLINE_NORMAL foldr #-}
714{-# INLINE_NORMAL foldr #-}
1027foldr :: Storable a => (a -> b -> b) -> b -> Array a -> b
715foldr :: Storable a => (a -> b -> b) -> b -> Array a -> b
1028foldr f z arr = runIdentity $ D.foldr f z $ toStreamD arr
716foldr f z arr = runIdentity $ D.foldr f z $ toStreamD arr
1029
717
1030−
1031−-- | Create two slices of an array without copying the original array. The
1032−-- specified index @i@ is the first index of the second slice.
1033−--
1034−-- @since 0.7.0
1035−splitAt :: forall a. Storable a => Int -> Array a -> (Array a, Array a)
1036−splitAt i arr@Array{..} =
1037− let maxIndex = length arr - 1
1038− in if i < 0
1039− then error "sliceAt: negative array index"
1040− else if i > maxIndex
1041− then error $ "sliceAt: specified array index " ++ show i
1042− ++ " is beyond the maximum index " ++ show maxIndex
1043− else let off = i * sizeOf (undefined :: a)
1044− p = unsafeForeignPtrToPtr aStart `plusPtr` off
1045− in ( Array
1046− { aStart = aStart
1047− , aEnd = p
1048− , aBound = p
1049− }
1050− , Array
1051− { aStart = aStart `plusForeignPtr` off
1052− , aEnd = aEnd
1053− , aBound = aBound
1054− }
1055− )
1056−
1057−
1058−{-# INLINE_NORMAL unlines #-}
1059−unlines :: forall m a. (MonadIO m, Storable a)
1060− => a -> D.Stream m (Array a) -> D.Stream m a
1061−unlines sep (D.Stream step state) = D.Stream step' (OuterLoop state)
1062− where
1063− {-# INLINE_LATE step' #-}
1064− step' gst (OuterLoop st) = do
1065− r <- step (adaptState gst) st
1066− return $ case r of
1067− D.Yield Array{..} s ->
1068− let p = unsafeForeignPtrToPtr aStart
1069− in D.Skip (InnerLoop s aStart p aEnd)
1070− D.Skip s -> D.Skip (OuterLoop s)
1071− D.Stop -> D.Stop
1072−
1073− step' _ (InnerLoop st _ p end) | p == end =
1074− return $ D.Yield sep $ OuterLoop st
1075−
1076− step' _ (InnerLoop st startf p end) = do
1077− x <- liftIO $ do
1078− r <- peek p
1079− touchForeignPtr startf
1080− return r
1081− return $ D.Yield x (InnerLoop st startf
1082− (p `plusPtr` sizeOf (undefined :: a)) end)
1083−
1084-------------------------------------------------------------------------------
718-------------------------------------------------------------------------------
1085−-- Folds
719+-- Write Folds to fold streams into arrays
1086-------------------------------------------------------------------------------
720-------------------------------------------------------------------------------
1087
721
1088{-# INLINE_NORMAL writeNAllocWith #-}
722{-# INLINE_NORMAL writeNAllocWith #-}
@@ -1104,6 +738,8 @@ writeNAllocWith alloc n = Fold step initial extract
1104-- | @writeN n@ folds a maximum of @n@ elements from the input stream to an
738-- | @writeN n@ folds a maximum of @n@ elements from the input stream to an
1105-- 'Array'.
739-- 'Array'.
1106--
740--
741+-- @writeN n = Fold.takeLE n writeNUnsafe@
742+--
1107-- @since 0.7.0
743-- @since 0.7.0
1108{-# INLINE_NORMAL writeN #-}
744{-# INLINE_NORMAL writeN #-}
1109writeN :: forall m a. (MonadIO m, Storable a) => Int -> Fold m a (Array a)
745writeN :: forall m a. (MonadIO m, Storable a) => Int -> Fold m a (Array a)
@@ -1162,6 +798,26 @@ writeNUnsafe n = Fold step initial extract
1162
798
1163 extract (ArrayUnsafe start end) = return $ Array start end end -- liftIO . shrinkToFit
799 extract (ArrayUnsafe start end) = return $ Array start end end -- liftIO . shrinkToFit
1164
800
801+-- XXX Buffer to a list instead?
802+--
803+-- | Buffer a stream into a stream of arrays.
804+--
805+-- @writeChunks = Fold.many Fold.toStream (Array.writeN n)@
806+--
807+-- See 'bufferChunks'.
808+--
809+-- /Unimplemented/
810+--
811+{-# INLINE_NORMAL writeChunks #-}
812+writeChunks :: -- (MonadIO m, Storable a) =>
813+ Int -> Fold m a (D.Stream m (Array a))
814+writeChunks = undefined -- Fold.many Fold.toStream (Array.writeN n)
815+
816+-- XXX Compare toArrayMinChunk with fromStreamD which uses an array of streams
817+-- implementation. We can write this using writeChunks above if that is faster.
818+-- If toArrayMinChunk is faster then we should use that to implement
819+-- fromStreamD.
820+--
1165-- XXX The realloc based implementation needs to make one extra copy if we use
821-- XXX The realloc based implementation needs to make one extra copy if we use
1166-- shrinkToFit. On the other hand, the stream of arrays implementation may
822-- shrinkToFit. On the other hand, the stream of arrays implementation may
1167-- buffer the array chunk pointers in memory but it does not have to shrink as
823-- buffer the array chunk pointers in memory but it does not have to shrink as
@@ -1224,6 +880,184 @@ writeAligned alignSize =
1224 (bytesToElemCount (undefined :: a)
880 (bytesToElemCount (undefined :: a)
1225 (mkChunkSize 1024))
881 (mkChunkSize 1024))
1226
882
883+-------------------------------------------------------------------------------
884+-- construct from streams, known size
885+-------------------------------------------------------------------------------
886+
887+-- | Use the 'writeN' fold instead.
888+--
889+-- @fromStreamDN n = D.fold (writeN n)@
890+--
891+{-# INLINE_NORMAL fromStreamDN #-}
892+fromStreamDN :: forall m a. (MonadIO m, Storable a)
893+ => Int -> D.Stream m a -> m (Array a)
894+fromStreamDN limit str = do
895+ arr <- liftIO $ newArray limit
896+ end <- D.foldlM' fwrite (return $ aEnd arr) $ D.take limit str
897+ return $ arr {aEnd = end}
898+
899+ where
900+
901+ fwrite ptr x = do
902+ liftIO $ poke ptr x
903+ return $ ptr `plusPtr` sizeOf (undefined :: a)
904+
905+-- | Create an 'Array' from the first N elements of a list. The array is
906+-- allocated to size N, if the list terminates before N elements then the
907+-- array may hold less than N elements.
908+--
909+-- @since 0.7.0
910+{-# INLINABLE fromListN #-}
911+fromListN :: Storable a => Int -> [a] -> Array a
912+fromListN n xs = unsafePerformIO $ fromStreamDN n $ D.fromList xs
913+
914+-------------------------------------------------------------------------------
915+-- convert stream to a single array
916+-------------------------------------------------------------------------------
917+
918+-- CAUTION: a very large number (millions) of arrays can degrade performance
919+-- due to GC overhead because we need to buffer the arrays before we flatten
920+-- all the arrays.
921+--
922+-- XXX Compare if this is faster or "fold write".
923+--
924+-- | We could take the approach of doubling the memory allocation on each
925+-- overflow. This would result in more or less the same amount of copying as in
926+-- the chunking approach. However, if we have to shrink in the end then it may
927+-- result in an extra copy of the entire data.
928+--
929+-- @
930+-- fromStreamD = StreamD.fold Array.write
931+-- @
932+--
933+{-# INLINE fromStreamD #-}
934+fromStreamD :: (MonadIO m, Storable a) => D.Stream m a -> m (Array a)
935+fromStreamD m = do
936+ buffered <- bufferChunks m
937+ len <- K.foldl' (+) 0 (K.map length buffered)
938+ fromStreamDN len $ D.concatUnfold read $ D.fromStreamK buffered
939+
940+-- | Create an 'Array' from a list. The list must be of finite size.
941+--
942+-- @since 0.7.0
943+{-# INLINABLE fromList #-}
944+fromList :: Storable a => [a] -> Array a
945+fromList xs = unsafePerformIO $ fromStreamD $ D.fromList xs
946+
947+-------------------------------------------------------------------------------
948+-- Combining
949+-------------------------------------------------------------------------------
950+
951+-- | Copy two arrays into a newly allocated array.
952+{-# INLINE spliceTwo #-}
953+spliceTwo :: (MonadIO m, Storable a) => Array a -> Array a -> m (Array a)
954+spliceTwo arr1 arr2 = do
955+ let src1 = unsafeForeignPtrToPtr (aStart arr1)
956+ src2 = unsafeForeignPtrToPtr (aStart arr2)
957+ len1 = aEnd arr1 `minusPtr` src1
958+ len2 = aEnd arr2 `minusPtr` src2
959+
960+ arr <- liftIO $ newArray (len1 + len2)
961+ let dst = unsafeForeignPtrToPtr (aStart arr)
962+
963+ -- XXX Should we use copyMutableByteArray# instead? Is there an overhead to
964+ -- ccall?
965+ liftIO $ do
966+ memcpy (castPtr dst) (castPtr src1) len1
967+ touchForeignPtr (aStart arr1)
968+ memcpy (castPtr (dst `plusPtr` len1)) (castPtr src2) len2
969+ touchForeignPtr (aStart arr2)
970+ return arr { aEnd = dst `plusPtr` (len1 + len2) }
971+
972+-- | Splice an array into a pre-reserved mutable array. The user must ensure
973+-- that there is enough space in the mutable array, otherwise the splicing
974+-- fails.
975+{-# INLINE spliceWith #-}
976+spliceWith :: (MonadIO m) => Array a -> Array a -> m (Array a)
977+spliceWith dst@(Array _ end bound) src =
978+ liftIO $ do
979+ let srcLen = byteLength src
980+ if end `plusPtr` srcLen > bound
981+ then error
982+ "Bug: spliceWith: Not enough space in the target array"
983+ else withForeignPtr (aStart dst) $ \_ ->
984+ withForeignPtr (aStart src) $ \psrc -> do
985+ let pdst = aEnd dst
986+ memcpy (castPtr pdst) (castPtr psrc) srcLen
987+ return $ dst {aEnd = pdst `plusPtr` srcLen}
988+
989+-- | Splice a new array into a preallocated mutable array, doubling the space
990+-- if there is no space in the target array.
991+{-# INLINE spliceWithDoubling #-}
992+spliceWithDoubling :: (MonadIO m, Storable a)
993+ => Array a -> Array a -> m (Array a)
994+spliceWithDoubling dst@(Array start end bound) src = do
995+ assert (end <= bound) (return ())
996+ let srcLen = aEnd src `minusPtr` unsafeForeignPtrToPtr (aStart src)
997+
998+ dst1 <-
999+ if end `plusPtr` srcLen >= bound
1000+ then do
1001+ let oldStart = unsafeForeignPtrToPtr start
1002+ oldSize = end `minusPtr` oldStart
1003+ newSize = max (oldSize * 2) (oldSize + srcLen)
1004+ liftIO $ realloc newSize dst
1005+ else return dst
1006+ spliceWith dst1 src
1007+
1008+-------------------------------------------------------------------------------
1009+-- Splitting
1010+-------------------------------------------------------------------------------
1011+
1012+-- | Drops the separator byte
1013+{-# INLINE breakOn #-}
1014+breakOn :: MonadIO m
1015+ => Word8 -> Array Word8 -> m (Array Word8, Maybe (Array Word8))
1016+breakOn sep arr@Array{..} = liftIO $ do
1017+ let p = unsafeForeignPtrToPtr aStart
1018+ loc <- c_memchr p sep (fromIntegral $ aEnd `minusPtr` p)
1019+ return $
1020+ if loc == nullPtr
1021+ then (arr, Nothing)
1022+ else
1023+ ( Array
1024+ { aStart = aStart
1025+ , aEnd = loc
1026+ , aBound = loc
1027+ }
1028+ , Just $ Array
1029+ { aStart = aStart `plusForeignPtr` (loc `minusPtr` p + 1)
1030+ , aEnd = aEnd
1031+ , aBound = aBound
1032+ }
1033+ )
1034+
1035+-- | Create two slices of an array without copying the original array. The
1036+-- specified index @i@ is the first index of the second slice.
1037+--
1038+-- @since 0.7.0
1039+splitAt :: forall a. Storable a => Int -> Array a -> (Array a, Array a)
1040+splitAt i arr@Array{..} =
1041+ let maxIndex = length arr - 1
1042+ in if i < 0
1043+ then error "sliceAt: negative array index"
1044+ else if i > maxIndex
1045+ then error $ "sliceAt: specified array index " ++ show i
1046+ ++ " is beyond the maximum index " ++ show maxIndex
1047+ else let off = i * sizeOf (undefined :: a)
1048+ p = unsafeForeignPtrToPtr aStart `plusPtr` off
1049+ in ( Array
1050+ { aStart = aStart
1051+ , aEnd = p
1052+ , aBound = p
1053+ }
1054+ , Array
1055+ { aStart = aStart `plusForeignPtr` off
1056+ , aEnd = aEnd
1057+ , aBound = aBound
1058+ }
1059+ )
1060+
1227-------------------------------------------------------------------------------
1061-------------------------------------------------------------------------------
1228-- Instances
1062-- Instances
1229-------------------------------------------------------------------------------
1063-------------------------------------------------------------------------------
@@ -1346,6 +1180,9 @@ instance Foldable Array where
1346-- Semigroup and Monoid
1180-- Semigroup and Monoid
1347-------------------------------------------------------------------------------
1181-------------------------------------------------------------------------------
1348
1182
1183+-- Note: we cannot splice the second array into the free space of the first
1184+-- array because that would require a monadic API due to mutation.
1185+-- | Copies the two arrays into a newly allocated array.
1349instance Storable a => Semigroup (Array a) where
1186instance Storable a => Semigroup (Array a) where
1350 {-# INLINE (<>) #-}
1187 {-# INLINE (<>) #-}
1351 arr1 <> arr2 = unsafePerformIO $ spliceTwo arr1 arr2
1188 arr1 <> arr2 = unsafePerformIO $ spliceTwo arr1 arr2
src/Streamly/Internal/Data/Array/Storable/Foreign/Types.hs+36−111
@@ -9,8 +9,11 @@
9-- Stability : experimental
9-- Stability : experimental
10-- Portability : GHC
10-- Portability : GHC
11--
11--
12+-- See notes in "Streamly.Internal.Data.Array.Storable.Foreign.Mut.Types"
13+--
12module Streamly.Internal.Data.Array.Storable.Foreign.Types
14module Streamly.Internal.Data.Array.Storable.Foreign.Types
13 (
15 (
16+ -- $arrayNotes
14 Array (..)
17 Array (..)
15
18
16 -- * Freezing and Thawing
19 -- * Freezing and Thawing
@@ -27,19 +30,9 @@ module Streamly.Internal.Data.Array.Storable.Foreign.Types
27 , fromList
30 , fromList
28 , fromListN
31 , fromListN
29 , fromStreamDN
32 , fromStreamDN
33+ , fromStreamD
30
34
31− -- * Streams of arrays
35+ -- * Split
32− , fromStreamDArraysOf
33− , MA.FlattenState (..) -- for inspection testing
34− , flattenArrays
35− , flattenArraysRev
36− , packArraysChunksOf
37− , MA.SpliceState (..)
38− , lpackArraysChunksOf
39−#if !defined(mingw32_HOST_OS)
40− , groupIOVecsOf
41−#endif
42− , splitOn
43 , breakOn
36 , breakOn
44
37
45 -- * Elimination
38 -- * Elimination
@@ -60,8 +53,6 @@ module Streamly.Internal.Data.Array.Storable.Foreign.Types
60 , toStreamRev
53 , toStreamRev
61 , toList
54 , toList
62
55
63− , unlines
64−
65 -- * Folds
56 -- * Folds
66 , toArrayMinChunk
57 , toArrayMinChunk
67 , writeN
58 , writeN
@@ -72,6 +63,12 @@ module Streamly.Internal.Data.Array.Storable.Foreign.Types
72 , write
63 , write
73 , writeAligned
64 , writeAligned
74
65
66+ -- * Streams of arrays
67+ , arraysOf
68+ , bufferChunks
69+ , flattenArrays
70+ , flattenArraysRev
71+
75 -- * Utilities
72 -- * Utilities
76 , MA.defaultChunkSize
73 , MA.defaultChunkSize
77 , MA.mkChunkSize
74 , MA.mkChunkSize
@@ -104,70 +101,31 @@ import Text.Read (readPrec, readListPrec, readListPrecDefault)
104
101
105import Prelude hiding (length, foldr, read, unlines, splitAt)
102import Prelude hiding (length, foldr, read, unlines, splitAt)
106
103
104+import qualified Streamly.Internal.Data.Array.Storable.Foreign.Mut.Types as MA
107import qualified Streamly.Internal.Data.Stream.StreamD.Type as D
105import qualified Streamly.Internal.Data.Stream.StreamD.Type as D
108import qualified Streamly.Internal.Data.Stream.StreamK.Type as K
106import qualified Streamly.Internal.Data.Stream.StreamK.Type as K
109−import qualified Streamly.Internal.Data.Array.Storable.Foreign.Mut.Types as MA
110−import qualified Streamly.Internal.Data.Fold.Types as Fold
111import qualified GHC.Exts as Exts
107import qualified GHC.Exts as Exts
112
108
113#if __GLASGOW_HASKELL__ < 808
109#if __GLASGOW_HASKELL__ < 808
114import Data.Semigroup (Semigroup(..))
110import Data.Semigroup (Semigroup(..))
115#endif
111#endif
116
112
117−#if !defined(mingw32_HOST_OS)
118−import Streamly.FileSystem.FDIO (IOVec(..))
119−#endif
120−
121#ifdef DEVBUILD
113#ifdef DEVBUILD
122import qualified Data.Foldable as F
114import qualified Data.Foldable as F
123#endif
115#endif
124
116
125−-------------------------------------------------------------------------------
126−-- Design Notes
127−-------------------------------------------------------------------------------
128−
129−-- There are two goals that we want to fulfill with arrays. One, holding large
130−-- amounts of data in non-GC memory and the ability to pass this data to and
131−-- from the operating system without an extra copy overhead. Two, allow random
132−-- access to elements based on their indices. The first one falls in the
133−-- category of storage buffers while the second one falls in the category of
134−-- maps/multisets/hashmaps.
135−--
136−-- For the first requirement we use an array of Storables and store it in a
137−-- ForeignPtr. We can have both immutable and mutable variants of this array
138−-- using wrappers over the same underlying type.
139−--
140−-- For the second requirement, we need a separate type for arrays of
141−-- polymorphic values, for example vectors of handler functions, lookup tables.
142−-- We can call this type a "vector" in contrast to arrays. It should not
143−-- require Storable instance for the type. In that case we need to use an
144−-- Array# instead of a ForeignPtr. This type of array would not reduce the GC
145−-- overhead much because each element of the array still needs to be scanned by
146−-- the GC. However, it can store polymorphic elements and allow random access
147−-- to those. But in most cases random access means storage, and it means we
148−-- need to avoid GC scanning except in cases of trivially small storage. One
149−-- way to achieve that would be to put the array in a Compact region. However,
150−-- if and when we mutate this, we will have to use a manual GC copying out to
151−-- another CR and freeing the old one.
152−
153-------------------------------------------------------------------------------
117-------------------------------------------------------------------------------
154-- Array Data Type
118-- Array Data Type
155-------------------------------------------------------------------------------
119-------------------------------------------------------------------------------
156
120
157−-- We require that an array stores only Storable. Arrays are used for buffering
121+-- $arrayNotes
158−-- data while streams are used for processing. If you want something to be
159−-- buffered it better be Storable so that we can store it in non-GC memory.
160--
122--
161−-- We can use a "Storable" constraint in the Array type and the Constraint can
123+-- We can use a 'Storable' constraint in the Array type and the constraint can
162-- be automatically provided to a function that pattern matches on the Array
124-- be automatically provided to a function that pattern matches on the Array
163-- type. However, it has huge performance cost, so we do not use it.
125-- type. However, it has huge performance cost, so we do not use it.
164−-- XXX Can this cost be alleviated in GHC-8.10 specialization fix?
126+-- Investigate a GHC improvement possiblity.
165--
127--
166−-- XXX Another way to not require the Storable constraint in array operations
128+-- XXX Rename the fields to better names.
167−-- is to store the elemSize in the array at construction and use that instead
168−-- of using sizeOf. Need to charaterize perf cost of this.
169−--
170−-- XXX rename the fields to "start, next, end".
171--
129--
172data Array a =
130data Array a =
173#ifdef DEVBUILD
131#ifdef DEVBUILD
@@ -343,20 +301,33 @@ fromStreamDN :: forall m a. (MonadIO m, Storable a)
343 => Int -> D.Stream m a -> m (Array a)
301 => Int -> D.Stream m a -> m (Array a)
344fromStreamDN limit str = unsafeFreeze <$> MA.fromStreamDN limit str
302fromStreamDN limit str = unsafeFreeze <$> MA.fromStreamDN limit str
345
303
304+{-# INLINE_NORMAL fromStreamD #-}
305+fromStreamD :: forall m a. (MonadIO m, Storable a)
306+ => D.Stream m a -> m (Array a)
307+fromStreamD str = unsafeFreeze <$> MA.fromStreamD str
308+
346-------------------------------------------------------------------------------
309-------------------------------------------------------------------------------
347-- Streams of arrays
310-- Streams of arrays
348-------------------------------------------------------------------------------
311-------------------------------------------------------------------------------
349
312
313+{-# INLINE bufferChunks #-}
314+bufferChunks :: (MonadIO m, Storable a) =>
315+ D.Stream m a -> m (K.Stream m (Array a))
316+bufferChunks m = D.foldr K.cons K.nil $ arraysOf MA.defaultChunkSize m
317+
350−-- | @fromStreamArraysOf n stream@ groups the input stream into a stream of
318+-- | @arraysOf n stream@ groups the input stream into a stream of
351-- arrays of size n.
319-- arrays of size n.
352−{-# INLINE_NORMAL fromStreamDArraysOf #-}
320+{-# INLINE_NORMAL arraysOf #-}
353−fromStreamDArraysOf :: forall m a. (MonadIO m, Storable a)
321+arraysOf :: forall m a. (MonadIO m, Storable a)
354 => Int -> D.Stream m a -> D.Stream m (Array a)
322 => Int -> D.Stream m a -> D.Stream m (Array a)
355−-- fromStreamDArraysOf n str = D.groupsOf n (writeN n) str
356−fromStreamDArraysOf n str = D.map unsafeFreeze $ MA.fromStreamDArraysOf n str
323+arraysOf n str = D.map unsafeFreeze $ MA.arraysOf n str
357
324
358−-- XXX concatMap does not seem to have the best possible performance so we have
325+-- | Use the "read" unfold instead.
359−-- a custom way to concat arrays.
326+--
327+-- @flattenArrays = concatUnfold read@
328+--
329+-- We can try this if there are any fusion issues in the unfold.
330+--
360{-# INLINE_NORMAL flattenArrays #-}
331{-# INLINE_NORMAL flattenArrays #-}
361flattenArrays :: forall m a. (MonadIO m, Storable a)
332flattenArrays :: forall m a. (MonadIO m, Storable a)
362 => D.Stream m (Array a) -> D.Stream m a
333 => D.Stream m (Array a) -> D.Stream m a
@@ -367,47 +338,6 @@ flattenArraysRev :: forall m a. (MonadIO m, Storable a)
367 => D.Stream m (Array a) -> D.Stream m a
338 => D.Stream m (Array a) -> D.Stream m a
368flattenArraysRev = MA.flattenArraysRev . D.map unsafeThaw
339flattenArraysRev = MA.flattenArraysRev . D.map unsafeThaw
369
340
370−
371−{-# INLINE_NORMAL packArraysChunksOf #-}
372−packArraysChunksOf :: (MonadIO m, Storable a)
373− => Int -> D.Stream m (Array a) -> D.Stream m (Array a)
374−packArraysChunksOf n str =
375− D.map unsafeFreeze $ MA.packArraysChunksOf n $ D.map unsafeThaw str
376−
377−-- XXX instead of writing two different versions of this operation, we should
378−-- write it as a pipe.
379−{-# INLINE_NORMAL lpackArraysChunksOf #-}
380−lpackArraysChunksOf :: (MonadIO m, Storable a)
381− => Int -> Fold m (Array a) () -> Fold m (Array a) ()
382−lpackArraysChunksOf n fld =
383− Fold.map unsafeThaw $ MA.lpackArraysChunksOf n (Fold.map unsafeFreeze fld)
384−
385−#if !defined(mingw32_HOST_OS)
386−
387−-- | @groupIOVecsOf maxBytes maxEntries@ groups arrays in the incoming stream
388−-- to create a stream of 'IOVec' arrays with a maximum of @maxBytes@ bytes in
389−-- each array and a maximum of @maxEntries@ entries in each array.
390−--
391−-- @since 0.7.0
392−{-# INLINE_NORMAL groupIOVecsOf #-}
393−groupIOVecsOf :: MonadIO m
394− => Int -> Int -> D.Stream m (Array a) -> D.Stream m (Array IOVec)
395−groupIOVecsOf n maxIOVLen str =
396− D.map unsafeFreeze $ MA.groupIOVecsOf n maxIOVLen $ D.map unsafeThaw str
397−#endif
398−
399−-- | Split a stream of arrays on a given separator byte, dropping the separator
400−-- and coalescing all the arrays between two separators into a single array.
401−--
402−-- @since 0.7.0
403−{-# INLINE_NORMAL splitOn #-}
404−splitOn
405− :: MonadIO m
406− => Word8
407− -> D.Stream m (Array Word8)
408− -> D.Stream m (Array Word8)
409−splitOn byte str = D.map unsafeFreeze $ MA.splitOn byte $ D.map unsafeThaw str
410−
411-- Drops the separator byte
341-- Drops the separator byte
412{-# INLINE breakOn #-}
342{-# INLINE breakOn #-}
413breakOn :: MonadIO m
343breakOn :: MonadIO m
@@ -508,11 +438,6 @@ splitAt i arr = (unsafeFreeze a, unsafeFreeze b)
508toList :: Storable a => Array a -> [a]
438toList :: Storable a => Array a -> [a]
509toList = MA.toList . unsafeThaw
439toList = MA.toList . unsafeThaw
510
440
511−{-# INLINE_NORMAL unlines #-}
512−unlines :: forall m a. (MonadIO m, Storable a)
513− => a -> D.Stream m (Array a) -> D.Stream m a
514−unlines sep str = MA.unlines sep (D.map unsafeThaw str)
515−
516-------------------------------------------------------------------------------
441-------------------------------------------------------------------------------
517-- Folds
442-- Folds
518-------------------------------------------------------------------------------
443-------------------------------------------------------------------------------
src/Streamly/Internal/Data/Array/Stream/Mut/Foreign.hsnew file+311
@@ -0,0 +1,311 @@
1+-- |
2+-- Module : Streamly.Internal.Data.Array.Stream.Mut.Foreign
3+-- Copyright : (c) 2019 Composewell Technologies
4+-- License : BSD3-3-Clause
5+-- Maintainer : streamly@composewell.com
6+-- Stability : experimental
7+-- Portability : GHC
8+--
9+-- Combinators to efficiently manipulate streams of mutable arrays.
10+--
11+module Streamly.Internal.Data.Array.Stream.Mut.Foreign
12+ (
13+ -- * Generation
14+ arraysOf
15+
16+ -- * Compaction
17+ , packArraysChunksOf
18+ , SpliceState (..)
19+ , lpackArraysChunksOf
20+#if !defined(mingw32_HOST_OS)
21+ , groupIOVecsOf
22+#endif
23+ , compact
24+ , compactLE
25+ , compactEQ
26+ , compactGE
27+ )
28+where
29+
30+#include "inline.hs"
31+
32+import Control.Monad.IO.Class (MonadIO(..))
33+import Control.Monad (when)
34+import Data.Bifunctor (first)
35+import Foreign.Storable (Storable(..))
36+#if !defined(mingw32_HOST_OS)
37+import Foreign.ForeignPtr.Unsafe (unsafeForeignPtrToPtr)
38+import Foreign.Ptr (castPtr)
39+import Streamly.FileSystem.FDIO (IOVec(..))
40+import Streamly.Internal.Data.Array.Storable.Foreign.Mut.Types (length)
41+import Streamly.Internal.Data.SVar (adaptState)
42+#endif
43+import Streamly.Internal.Data.Array.Storable.Foreign.Mut.Types (Array(..))
44+import Streamly.Internal.Data.Fold.Types (Fold(..))
45+import Streamly.Internal.Data.Stream.Serial (SerialT)
46+import Streamly.Internal.Data.Stream.StreamK.Type (IsStream)
47+import Streamly.Internal.Data.Tuple.Strict (Tuple'(..))
48+
49+import qualified Streamly.Internal.Data.Array.Storable.Foreign.Mut.Types as MArray
50+import qualified Streamly.Internal.Data.Fold.Types as FL
51+import qualified Streamly.Internal.Data.Stream.StreamD as D
52+
53+import Prelude hiding (length)
54+
55+-- | @arraysOf n stream@ groups the elements in the input stream into arrays of
56+-- @n@ elements each.
57+--
58+-- Same as the following but may be more efficient:
59+--
60+-- > arraysOf n = Stream.foldMany (MArray.writeN n)
61+--
62+-- /Internal/
63+{-# INLINE arraysOf #-}
64+arraysOf :: (IsStream t, MonadIO m, Storable a)
65+ => Int -> t m a -> t m (Array a)
66+arraysOf n = D.fromStreamD . MArray.arraysOf n . D.toStreamD
67+
68+-------------------------------------------------------------------------------
69+-- Compact
70+-------------------------------------------------------------------------------
71+
72+data SpliceState s arr
73+ = SpliceInitial s
74+ | SpliceBuffering s arr
75+ | SpliceYielding arr (SpliceState s arr)
76+ | SpliceFinish
77+
78+-- | This mutates the first array (if it has space) to append values from the
79+-- second one. This would work for immutable arrays as well because an
80+-- immutable array never has space so a new array is allocated instead of
81+-- mutating it.
82+--
83+-- | Coalesce adjacent arrays in incoming stream to form bigger arrays of a
84+-- maximum specified size. Note that if a single array is bigger than the
85+-- specified size we do not split it to fit. When we coalesce multiple arrays
86+-- if the size would exceed the specified size we do not coalesce therefore the
87+-- actual array size may be less than the specified chunk size.
88+--
89+-- @since 0.7.0
90+{-# INLINE_NORMAL packArraysChunksOf #-}
91+packArraysChunksOf :: (MonadIO m, Storable a)
92+ => Int -> D.Stream m (Array a) -> D.Stream m (Array a)
93+packArraysChunksOf n (D.Stream step state) =
94+ D.Stream step' (SpliceInitial state)
95+
96+ where
97+
98+ {-# INLINE_LATE step' #-}
99+ step' gst (SpliceInitial st) = do
100+ when (n <= 0) $
101+ -- XXX we can pass the module string from the higher level API
102+ error $ "Streamly.Internal.Data.Array.Storable.Foreign.Mut.Types.packArraysChunksOf: the size of "
103+ ++ "arrays [" ++ show n ++ "] must be a natural number"
104+ r <- step gst st
105+ case r of
106+ D.Yield arr s -> return $
107+ let len = MArray.byteLength arr
108+ in if len >= n
109+ then D.Skip (SpliceYielding arr (SpliceInitial s))
110+ else D.Skip (SpliceBuffering s arr)
111+ D.Skip s -> return $ D.Skip (SpliceInitial s)
112+ D.Stop -> return D.Stop
113+
114+ step' gst (SpliceBuffering st buf) = do
115+ r <- step gst st
116+ case r of
117+ D.Yield arr s -> do
118+ let len = MArray.byteLength buf + MArray.byteLength arr
119+ if len > n
120+ then return $
121+ D.Skip (SpliceYielding buf (SpliceBuffering s arr))
122+ else do
123+ buf' <- if MArray.byteCapacity buf < n
124+ then liftIO $ MArray.realloc n buf
125+ else return buf
126+ buf'' <- MArray.spliceWith buf' arr
127+ return $ D.Skip (SpliceBuffering s buf'')
128+ D.Skip s -> return $ D.Skip (SpliceBuffering s buf)
129+ D.Stop -> return $ D.Skip (SpliceYielding buf SpliceFinish)
130+
131+ step' _ SpliceFinish = return D.Stop
132+
133+ step' _ (SpliceYielding arr next) = return $ D.Yield arr next
134+
135+{-# INLINE_NORMAL lpackArraysChunksOf #-}
136+lpackArraysChunksOf :: (MonadIO m, Storable a)
137+ => Int -> Fold m (Array a) () -> Fold m (Array a) ()
138+lpackArraysChunksOf n (Fold step1 initial1 extract1) =
139+ Fold step initial extract
140+
141+ where
142+
143+ initial = do
144+ when (n <= 0) $
145+ -- XXX we can pass the module string from the higher level API
146+ error $ "Streamly.Internal.Data.Array.Storable.Foreign.Mut.Types.packArraysChunksOf: the size of "
147+ ++ "arrays [" ++ show n ++ "] must be a natural number"
148+
149+ r <- initial1
150+ return $ first (Tuple' Nothing) r
151+
152+ extract (Tuple' Nothing r1) = extract1 r1
153+ extract (Tuple' (Just buf) r1) = do
154+ r <- step1 r1 buf
155+ case r of
156+ FL.Partial rr -> extract1 rr
157+ FL.Done _ -> return ()
158+
159+ step (Tuple' Nothing r1) arr =
160+ let len = MArray.byteLength arr
161+ in if len >= n
162+ then do
163+ r <- step1 r1 arr
164+ case r of
165+ FL.Done _ -> return $ FL.Done ()
166+ FL.Partial s -> do
167+ extract1 s
168+ res <- initial1
169+ return $ first (Tuple' Nothing) res
170+ else return $ FL.Partial $ Tuple' (Just arr) r1
171+
172+ step (Tuple' (Just buf) r1) arr = do
173+ let len = MArray.byteLength buf + MArray.byteLength arr
174+ buf' <- if MArray.byteCapacity buf < len
175+ then liftIO $ MArray.realloc (max n len) buf
176+ else return buf
177+ buf'' <- MArray.spliceWith buf' arr
178+
179+ -- XXX this is common in both the equations of step
180+ if len >= n
181+ then do
182+ r <- step1 r1 buf''
183+ case r of
184+ FL.Done _ -> return $ FL.Done ()
185+ FL.Partial s -> do
186+ extract1 s
187+ res <- initial1
188+ return $ first (Tuple' Nothing) res
189+ else return $ FL.Partial $ Tuple' (Just buf'') r1
190+
191+-- XXX replace by compactLE
192+--
193+-- | Coalesce adjacent arrays in incoming stream to form bigger arrays of a
194+-- maximum specified size in bytes.
195+--
196+-- @since 0.7.0
197+{-# INLINE compact #-}
198+compact :: (MonadIO m, Storable a)
199+ => Int -> SerialT m (Array a) -> SerialT m (Array a)
200+compact n xs = D.fromStreamD $ packArraysChunksOf n (D.toStreamD xs)
201+
202+-- XXX Replace the above functions by a compactLEFold
203+-- | Coalesce adjacent arrays in incoming stream to form bigger arrays of a
204+-- maximum specified size. Note that if a single array is bigger than the
205+-- specified size we do not split it to fit. When we coalesce multiple arrays
206+-- if the size would exceed the specified size we do not coalesce therefore the
207+-- actual array size may be less than the specified chunk size.
208+--
209+-- /Unimplemented/
210+{-# INLINE_NORMAL compactLEFold #-}
211+compactLEFold :: -- (MonadIO m, Storable a) =>
212+ Int -> Fold m (Array a) (Array a)
213+compactLEFold = undefined
214+
215+compactLE :: (MonadIO m {-, Storable a-}) =>
216+ Int -> SerialT m (Array a) -> SerialT m (Array a)
217+compactLE n xs = D.fromStreamD $ D.foldMany (compactLEFold n) (D.toStreamD xs)
218+
219+-- | Like 'compact' but generates arrays of exactly equal to the size specified
220+-- except for the last array in the stream which could be shorter.
221+--
222+-- /Unimplemented/
223+{-# INLINE compactEQ #-}
224+compactEQ :: -- (MonadIO m, Storable a) =>
225+ Int -> SerialT m (Array a) -> SerialT m (Array a)
226+compactEQ _n _xs = undefined
227+ -- D.fromStreamD $ D.foldMany (compactEQFold n) (D.toStreamD xs)
228+
229+-- | Like 'compact' but generates arrays of size greater than or equal to the
230+-- specified except for the last array in the stream which could be shorter.
231+--
232+-- /Unimplemented/
233+{-# INLINE compactGE #-}
234+compactGE :: -- (MonadIO m, Storable a) =>
235+ Int -> SerialT m (Array a) -> SerialT m (Array a)
236+compactGE _n _xs = undefined
237+ -- D.fromStreamD $ D.foldMany (compactGEFold n) (D.toStreamD xs)
238+
239+-------------------------------------------------------------------------------
240+-- IOVec
241+-------------------------------------------------------------------------------
242+
243+#if !defined(mingw32_HOST_OS)
244+data GatherState s arr
245+ = GatherInitial s
246+ | GatherBuffering s arr Int
247+ | GatherYielding arr (GatherState s arr)
248+ | GatherFinish
249+
250+-- | @groupIOVecsOf maxBytes maxEntries@ groups arrays in the incoming stream
251+-- to create a stream of 'IOVec' arrays with a maximum of @maxBytes@ bytes in
252+-- each array and a maximum of @maxEntries@ entries in each array.
253+--
254+-- @since 0.7.0
255+{-# INLINE_NORMAL groupIOVecsOf #-}
256+groupIOVecsOf :: MonadIO m
257+ => Int -> Int -> D.Stream m (Array a) -> D.Stream m (Array IOVec)
258+groupIOVecsOf n maxIOVLen (D.Stream step state) =
259+ D.Stream step' (GatherInitial state)
260+
261+ where
262+
263+ {-# INLINE_LATE step' #-}
264+ step' gst (GatherInitial st) = do
265+ when (n <= 0) $
266+ -- XXX we can pass the module string from the higher level API
267+ error $ "Streamly.Internal.Data.Array.Storable.Foreign.Mut.Types.groupIOVecsOf: the size of "
268+ ++ "groups [" ++ show n ++ "] must be a natural number"
269+ when (maxIOVLen <= 0) $
270+ -- XXX we can pass the module string from the higher level API
271+ error $ "Streamly.Internal.Data.Array.Storable.Foreign.Mut.Types.groupIOVecsOf: the number of "
272+ ++ "IOVec entries [" ++ show n ++ "] must be a natural number"
273+ r <- step (adaptState gst) st
274+ case r of
275+ D.Yield arr s -> do
276+ let p = unsafeForeignPtrToPtr (aStart arr)
277+ len = MArray.byteLength arr
278+ iov <- liftIO $ MArray.newArray maxIOVLen
279+ iov' <- liftIO $ MArray.unsafeSnoc iov (IOVec (castPtr p)
280+ (fromIntegral len))
281+ if len >= n
282+ then return $ D.Skip (GatherYielding iov' (GatherInitial s))
283+ else return $ D.Skip (GatherBuffering s iov' len)
284+ D.Skip s -> return $ D.Skip (GatherInitial s)
285+ D.Stop -> return D.Stop
286+
287+ step' gst (GatherBuffering st iov len) = do
288+ r <- step (adaptState gst) st
289+ case r of
290+ D.Yield arr s -> do
291+ let p = unsafeForeignPtrToPtr (aStart arr)
292+ alen = MArray.byteLength arr
293+ len' = len + alen
294+ if len' > n || length iov >= maxIOVLen
295+ then do
296+ iov' <- liftIO $ MArray.newArray maxIOVLen
297+ iov'' <- liftIO $ MArray.unsafeSnoc iov' (IOVec (castPtr p)
298+ (fromIntegral alen))
299+ return $ D.Skip (GatherYielding iov
300+ (GatherBuffering s iov'' alen))
301+ else do
302+ iov' <- liftIO $ MArray.unsafeSnoc iov (IOVec (castPtr p)
303+ (fromIntegral alen))
304+ return $ D.Skip (GatherBuffering s iov' len')
305+ D.Skip s -> return $ D.Skip (GatherBuffering s iov len)
306+ D.Stop -> return $ D.Skip (GatherYielding iov GatherFinish)
307+
308+ step' _ GatherFinish = return D.Stop
309+
310+ step' _ (GatherYielding iov next) = return $ D.Yield iov next
311+#endif
src/Streamly/Internal/Data/Stream/IsStream/Nesting.hs+9−8
@@ -254,8 +254,7 @@ import Foreign.Storable (Storable)
254import Streamly.Internal.Data.Fold.Types (Fold (..), Fold2 (..))
254import Streamly.Internal.Data.Fold.Types (Fold (..), Fold2 (..))
255import Streamly.Internal.Data.Parser (Parser (..))
255import Streamly.Internal.Data.Parser (Parser (..))
256import Streamly.Internal.Data.Unfold.Types (Unfold)
256import Streamly.Internal.Data.Unfold.Types (Unfold)
257−import Streamly.Internal.Data.Array.Storable.Foreign.Types
257+import Streamly.Internal.Data.Array.Storable.Foreign.Types (Array)
258− (Array, writeNUnsafe)
259import Streamly.Internal.Data.SVar (MonadAsync)
258import Streamly.Internal.Data.SVar (MonadAsync)
260import Streamly.Internal.Data.Stream.Ahead (ahead)
259import Streamly.Internal.Data.Stream.Ahead (ahead)
261import Streamly.Internal.Data.Stream.Async (async, wAsync)
260import Streamly.Internal.Data.Stream.Async (async, wAsync)
@@ -650,7 +649,7 @@ concatMapEitherWith = undefined
650-- @since 0.7.0
649-- @since 0.7.0
651{-# INLINE concatUnfold #-}
650{-# INLINE concatUnfold #-}
652concatUnfold ::(IsStream t, Monad m) => Unfold m a b -> t m a -> t m b
651concatUnfold ::(IsStream t, Monad m) => Unfold m a b -> t m a -> t m b
653−concatUnfold u m = fromStreamD $ D.concatMapU u (toStreamD m)
652+concatUnfold u m = fromStreamD $ D.concatUnfold u (toStreamD m)
654
653
655-- | Like 'concatUnfold' but interleaves the streams in the same way as
654-- | Like 'concatUnfold' but interleaves the streams in the same way as
656-- 'interleave' behaves instead of appending them.
655-- 'interleave' behaves instead of appending them.
@@ -749,7 +748,7 @@ gintercalate unf1 str1 unf2 str2 =
749intercalate :: (IsStream t, Monad m)
748intercalate :: (IsStream t, Monad m)
750 => b -> Unfold m b c -> t m b -> t m c
749 => b -> Unfold m b c -> t m b -> t m c
751intercalate seed unf str = D.fromStreamD $
750intercalate seed unf str = D.fromStreamD $
752− D.concatMapU unf $ D.intersperse seed (toStreamD str)
751+ D.concatUnfold unf $ D.intersperse seed (toStreamD str)
753
752
754-- | 'interleaveSuffix' followed by unfold and concat.
753-- | 'interleaveSuffix' followed by unfold and concat.
755--
754--
@@ -777,7 +776,7 @@ gintercalateSuffix unf1 str1 unf2 str2 =
777{-# INLINE intercalateSuffix #-}
776{-# INLINE intercalateSuffix #-}
778intercalateSuffix :: (IsStream t, Monad m)
777intercalateSuffix :: (IsStream t, Monad m)
779 => b -> Unfold m b c -> t m b -> t m c
778 => b -> Unfold m b c -> t m b -> t m c
780−intercalateSuffix seed unf str = fromStreamD $ D.concatMapU unf
779+intercalateSuffix seed unf str = fromStreamD $ D.concatUnfold unf
781 $ D.intersperseSuffix (return seed) (D.toStreamD str)
780 $ D.intersperseSuffix (return seed) (D.toStreamD str)
782
781
783------------------------------------------------------------------------------
782------------------------------------------------------------------------------
@@ -1570,12 +1569,14 @@ splitOnSuffixSeqAny subseq f m = undefined
1570-- This can be considered as an n-fold version of 'takeLE' where we apply
1569-- This can be considered as an n-fold version of 'takeLE' where we apply
1571-- 'takeLE' repeatedly on the leftover stream until the stream exhausts.
1570-- 'takeLE' repeatedly on the leftover stream until the stream exhausts.
1572--
1571--
1572+-- @chunksOf n f = foldMany (FL.takeLE n f)@
1573+--
1573-- @since 0.7.0
1574-- @since 0.7.0
1574{-# INLINE chunksOf #-}
1575{-# INLINE chunksOf #-}
1575chunksOf
1576chunksOf
1576 :: (IsStream t, Monad m)
1577 :: (IsStream t, Monad m)
1577 => Int -> Fold m a b -> t m a -> t m b
1578 => Int -> Fold m a b -> t m a -> t m b
1578−chunksOf n f = foldMany (FL.takeLE n f)
1579+chunksOf n f = D.fromStreamD . D.chunksOf n f . D.toStreamD
1579
1580
1580-- |
1581-- |
1581--
1582--
@@ -1591,13 +1592,13 @@ chunksOf2 n action f m = D.fromStreamD $ D.groupsOf2 n action f (D.toStreamD m)
1591--
1592--
1592-- Same as the following but may be more efficient:
1593-- Same as the following but may be more efficient:
1593--
1594--
1594−-- > arraysOf n = S.chunksOf n (A.writeN n)
1595+-- > arraysOf n = Stream.foldMany (A.writeN n)
1595--
1596--
1596-- /Internal/
1597-- /Internal/
1597{-# INLINE arraysOf #-}
1598{-# INLINE arraysOf #-}
1598arraysOf :: (IsStream t, MonadIO m, Storable a)
1599arraysOf :: (IsStream t, MonadIO m, Storable a)
1599 => Int -> t m a -> t m (Array a)
1600 => Int -> t m a -> t m (Array a)
1600−arraysOf n = chunksOf n (writeNUnsafe n)
1601+arraysOf n = D.fromStreamD . A.arraysOf n . D.toStreamD
1601
1602
1602-- XXX we can implement this by repeatedly applying the 'lrunFor' fold.
1603-- XXX we can implement this by repeatedly applying the 'lrunFor' fold.
1603-- XXX add this example after fixing the serial stream rate control
1604-- XXX add this example after fixing the serial stream rate control
src/Streamly/Internal/Data/Stream/StreamD/Generate.hs−22
@@ -97,7 +97,6 @@ import Streamly.Internal.Data.Time.Units (toRelTime64, RelTime64)
97import Streamly.Internal.Data.Time.Clock (Clock(Monotonic), getTime)
97import Streamly.Internal.Data.Time.Clock (Clock(Monotonic), getTime)
98import Streamly.Internal.Data.Time.Units
98import Streamly.Internal.Data.Time.Units
99 (MicroSecond64(..), fromAbsTime, toAbsTime, AbsTime)
99 (MicroSecond64(..), fromAbsTime, toAbsTime, AbsTime)
100−import Streamly.Internal.Data.Unfold.Types (Unfold(..))
101
100
102import qualified Streamly.Internal.Data.IORef.Prim as Prim
101import qualified Streamly.Internal.Data.IORef.Prim as Prim
103
102
@@ -132,27 +131,6 @@ cons x (Stream step state) = Stream step1 Nothing
132 Skip s -> Skip (Just s)
131 Skip s -> Skip (Just s)
133 Stop -> Stop
132 Stop -> Stop
134
133
135−------------------------------------------------------------------------------
136−-- From 'Unfold'
137−------------------------------------------------------------------------------
138−
139−data UnfoldState s = UnfoldNothing | UnfoldJust s
140−
141−-- | Convert an 'Unfold' into a 'Stream' by supplying it a seed.
142−--
143−{-# INLINE_NORMAL unfold #-}
144−unfold :: Monad m => Unfold m a b -> a -> Stream m b
145−unfold (Unfold ustep inject) seed = Stream step UnfoldNothing
146− where
147− {-# INLINE_LATE step #-}
148− step _ UnfoldNothing = inject seed >>= return . Skip . UnfoldJust
149− step _ (UnfoldJust st) = do
150− r <- ustep st
151− return $ case r of
152− Yield x s -> Yield x (UnfoldJust s)
153− Skip s -> Skip (UnfoldJust s)
154− Stop -> Stop
155−
156------------------------------------------------------------------------------
134------------------------------------------------------------------------------
157-- Unfolding
135-- Unfolding
158------------------------------------------------------------------------------
136------------------------------------------------------------------------------
src/Streamly/Internal/Data/Stream/StreamD/Nesting.hs+2−38
@@ -83,8 +83,7 @@ module Streamly.Internal.Data.Stream.StreamD.Nesting
83 -- | Generate streams by using an unfold on each element of an input
83 -- | Generate streams by using an unfold on each element of an input
84 -- stream, append the resulting streams and flatten. A special case of
84 -- stream, append the resulting streams and flatten. A special case of
85 -- gintercalate.
85 -- gintercalate.
86− , ConcatMapUState (..)
86+ , concatUnfold
87− , concatMapU
88 , ConcatUnfoldInterleaveState (..)
87 , ConcatUnfoldInterleaveState (..)
89 , concatUnfoldInterleave
88 , concatUnfoldInterleave
90 , concatUnfoldRoundrobin
89 , concatUnfoldRoundrobin
@@ -125,6 +124,7 @@ module Streamly.Internal.Data.Stream.StreamD.Nesting
125
124
126 -- ** Grouping
125 -- ** Grouping
127 -- | Group segments of a stream and fold. Special case of parsing.
126 -- | Group segments of a stream and fold. Special case of parsing.
127+ , chunksOf
128 , groupsOf2
128 , groupsOf2
129 , groupsBy
129 , groupsBy
130 , groupsRollingBy
130 , groupsRollingBy
@@ -479,42 +479,6 @@ mergeBy cmp = mergeByM (\a b -> return $ cmp a b)
479-- Combine N Streams - concatUnfold
479-- Combine N Streams - concatUnfold
480------------------------------------------------------------------------------
480------------------------------------------------------------------------------
481
481
482−-- Define a unique structure to use in inspection testing
483−data ConcatMapUState o i =
484− ConcatMapUOuter o
485− | ConcatMapUInner o i
486−
487−-- | @concatMapU unfold stream@ uses @unfold@ to map the input stream elements
488−-- to streams and then flattens the generated streams into a single output
489−-- stream.
490−
491−-- This is like 'concatMap' but uses an unfold with an explicit state to
492−-- generate the stream instead of a 'Stream' type generator. This allows better
493−-- optimization via fusion. This can be many times more efficient than
494−-- 'concatMap'.
495−
496−{-# INLINE_NORMAL concatMapU #-}
497−concatMapU :: Monad m => Unfold m a b -> Stream m a -> Stream m b
498−concatMapU (Unfold istep inject) (Stream ostep ost) =
499− Stream step (ConcatMapUOuter ost)
500− where
501− {-# INLINE_LATE step #-}
502− step gst (ConcatMapUOuter o) = do
503− r <- ostep (adaptState gst) o
504− case r of
505− Yield a o' -> do
506− i <- inject a
507− i `seq` return (Skip (ConcatMapUInner o' i))
508− Skip o' -> return $ Skip (ConcatMapUOuter o')
509− Stop -> return $ Stop
510−
511− step _ (ConcatMapUInner o i) = do
512− r <- istep i
513− return $ case r of
514− Yield x i' -> Yield x (ConcatMapUInner o i')
515− Skip i' -> Skip (ConcatMapUInner o i')
516− Stop -> Skip (ConcatMapUOuter o)
517−
518data ConcatUnfoldInterleaveState o i =
482data ConcatUnfoldInterleaveState o i =
519 ConcatUnfoldInterleaveOuter o [i]
483 ConcatUnfoldInterleaveOuter o [i]
520 | ConcatUnfoldInterleaveInner o [i]
484 | ConcatUnfoldInterleaveInner o [i]
src/Streamly/Internal/Data/Stream/StreamD/Step.hsnew file+39
@@ -0,0 +1,39 @@
1+-- |
2+-- Module : Streamly.Internal.Data.Stream.StreamD.Step
3+-- Copyright : (c) 2018 Composewell Technologies
4+-- License : BSD-3-Clause
5+-- Maintainer : streamly@composewell.com
6+-- Stability : experimental
7+-- Portability : GHC
8+
9+module Streamly.Internal.Data.Stream.StreamD.Step
10+ (
11+ -- * The stream type
12+ Step (..)
13+ )
14+where
15+
16+import Fusion.Plugin.Types (Fuse(..))
17+
18+-- | A stream is a succession of 'Step's. A 'Yield' produces a single value and
19+-- the next state of the stream. 'Stop' indicates there are no more values in
20+-- the stream.
21+{-# ANN type Step Fuse #-}
22+data Step s a = Yield a s | Skip s | Stop
23+
24+instance Functor (Step s) where
25+ {-# INLINE fmap #-}
26+ fmap f (Yield x s) = Yield (f x) s
27+ fmap _ (Skip s) = Skip s
28+ fmap _ Stop = Stop
29+
30+{-
31+yield :: Monad m => a -> s -> m (Step s a)
32+yield a = return . Yield a
33+
34+skip :: Monad m => s -> m (Step s a)
35+skip = return . Skip
36+
37+stop :: Monad m => m (Step s a)
38+stop = return Stop
39+-}
src/Streamly/Internal/Data/Stream/StreamD/Transform.hs+13−13
@@ -1102,11 +1102,11 @@ reverse m = Stream step Nothing
1102reverse' :: forall m a. (MonadIO m, Storable a) => Stream m a -> Stream m a
1102reverse' :: forall m a. (MonadIO m, Storable a) => Stream m a -> Stream m a
1103{-
1103{-
1104-- This commented implementation copies the whole stream into one single array
1104-- This commented implementation copies the whole stream into one single array
1105−-- and then streams from that array, this is 3-4x faster than the chunked code
1105+-- and then streams from that array, this has exactly the same performance as
1106−-- that follows. Though this could be problematic due to unbounded large
1106+-- the chunked code that follows. Though this could be problematic due to
1107−-- allocations. We need to figure out why the chunked code is slower and if we
1107+-- unbounded large allocations. However, if we use an idiomatic implementation
1108−-- can optimize the chunked code to work as fast as this one. It may be a
1108+-- of arraysOf instead of the custom implementation then the chunked code
1109−-- fusion issue?
1109+-- becomes worse by 6 times. Need to investigate if that can be improved.
1110import Foreign.ForeignPtr (touchForeignPtr)
1110import Foreign.ForeignPtr (touchForeignPtr)
1111import Foreign.ForeignPtr.Unsafe (unsafeForeignPtrToPtr)
1111import Foreign.ForeignPtr.Unsafe (unsafeForeignPtrToPtr)
1112import Foreign.Ptr (Ptr, plusPtr)
1112import Foreign.Ptr (Ptr, plusPtr)
@@ -1115,8 +1115,8 @@ reverse' m = Stream step Nothing
1115 {-# INLINE_LATE step #-}
1115 {-# INLINE_LATE step #-}
1116 step _ Nothing = do
1116 step _ Nothing = do
1117 arr <- A.fromStreamD m
1117 arr <- A.fromStreamD m
1118− let p = aEnd arr `plusPtr` negate (sizeOf (undefined :: a))
1118+ let p = A.aEnd arr `plusPtr` negate (sizeOf (undefined :: a))
1119− return $ Skip $ Just (aStart arr, p)
1119+ return $ Skip $ Just (A.aStart arr, p)
1120
1120
1121 step _ (Just (start, p)) | p < unsafeForeignPtrToPtr start = return Stop
1121 step _ (Just (start, p)) | p < unsafeForeignPtrToPtr start = return Stop
1122
1122
@@ -1128,12 +1128,12 @@ reverse' m = Stream step Nothing
1128 next = p `plusPtr` negate (sizeOf (undefined :: a))
1128 next = p `plusPtr` negate (sizeOf (undefined :: a))
1129 return $ Yield x (Just (start, next))
1129 return $ Yield x (Just (start, next))
1130-}
1130-}
1131−reverse' m =
1131+reverse' =
1132− A.flattenArraysRev
1132+ A.flattenArraysRev -- concatUnfold A.readRev
1133− $ fromStreamK
1133+ . fromStreamK
1134− $ K.reverse
1134+ . K.reverse
1135− $ toStreamK
1135+ . toStreamK
1136− $ A.fromStreamDArraysOf A.defaultChunkSize m
1136+ . A.arraysOf A.defaultChunkSize
1137
1137
1138------------------------------------------------------------------------------
1138------------------------------------------------------------------------------
1139-- Position Indexing
1139-- Position Indexing
src/Streamly/Internal/Data/Stream/StreamD/Type.hs+77−13
@@ -26,6 +26,9 @@ module Streamly.Internal.Data.Stream.StreamD.Type
26 , consM
26 , consM
27 , uncons
27 , uncons
28
28
29+ -- * From Unfold
30+ , unfold
31+
29 -- * From Values
32 -- * From Values
30 , yield
33 , yield
31 , yieldM
34 , yieldM
@@ -70,12 +73,15 @@ module Streamly.Internal.Data.Stream.StreamD.Type
70 , takeWhileM
73 , takeWhileM
71
74
72 -- * Nesting
75 -- * Nesting
76+ , ConcatMapUState (..)
77+ , concatUnfold
73 , concatMap
78 , concatMap
74 , concatMapM
79 , concatMapM
75 , GroupState (..) -- for inspection testing
80 , GroupState (..) -- for inspection testing
76 , foldMany
81 , foldMany
77 , foldMany1
82 , foldMany1
78 , groupsOf2
83 , groupsOf2
84+ , chunksOf
79 )
85 )
80where
86where
81
87
@@ -90,7 +96,9 @@ import GHC.Types (SPEC(..))
90import Prelude hiding (map, mapM, foldr, take, concatMap, takeWhile)
96import Prelude hiding (map, mapM, foldr, take, concatMap, takeWhile)
91
97
92import Streamly.Internal.Data.Fold.Types (Fold(..), Fold2(..))
98import Streamly.Internal.Data.Fold.Types (Fold(..), Fold2(..))
99+import Streamly.Internal.Data.Stream.StreamD.Step (Step (..))
93import Streamly.Internal.Data.SVar (State, adaptState, defState)
100import Streamly.Internal.Data.SVar (State, adaptState, defState)
101+import Streamly.Internal.Data.Unfold.Types (Unfold(..))
94
102
95import qualified Streamly.Internal.Data.Fold.Types as FL
103import qualified Streamly.Internal.Data.Fold.Types as FL
96import qualified Streamly.Internal.Data.Stream.StreamK.Type as K
104import qualified Streamly.Internal.Data.Stream.StreamK.Type as K
@@ -99,18 +107,6 @@ import qualified Streamly.Internal.Data.Stream.StreamK.Type as K
99-- The direct style stream type
107-- The direct style stream type
100------------------------------------------------------------------------------
108------------------------------------------------------------------------------
101
109
102−-- | A stream is a succession of 'Step's. A 'Yield' produces a single value and
103−-- the next state of the stream. 'Stop' indicates there are no more values in
104−-- the stream.
105−{-# ANN type Step Fuse #-}
106−data Step s a = Yield a s | Skip s | Stop
107−
108−instance Functor (Step s) where
109− {-# INLINE fmap #-}
110− fmap f (Yield x s) = Yield (f x) s
111− fmap _ (Skip s) = Skip s
112− fmap _ Stop = Stop
113−
114-- gst = global state
110-- gst = global state
115-- | A stream consists of a step function that generates the next step given a
111-- | A stream consists of a step function that generates the next step given a
116-- current state, and the current state.
112-- current state, and the current state.
@@ -164,6 +160,27 @@ uncons (UnStream step state) = go state
164 Skip s -> go s
160 Skip s -> go s
165 Stop -> return Nothing
161 Stop -> return Nothing
166
162
163+------------------------------------------------------------------------------
164+-- From 'Unfold'
165+------------------------------------------------------------------------------
166+
167+data UnfoldState s = UnfoldNothing | UnfoldJust s
168+
169+-- | Convert an 'Unfold' into a 'Stream' by supplying it a seed.
170+--
171+{-# INLINE_NORMAL unfold #-}
172+unfold :: Monad m => Unfold m a b -> a -> Stream m b
173+unfold (Unfold ustep inject) seed = Stream step UnfoldNothing
174+ where
175+ {-# INLINE_LATE step #-}
176+ step _ UnfoldNothing = inject seed >>= return . Skip . UnfoldJust
177+ step _ (UnfoldJust st) = do
178+ r <- ustep st
179+ return $ case r of
180+ Yield x s -> Yield x (UnfoldJust s)
181+ Skip s -> Skip (UnfoldJust s)
182+ Stop -> Stop
183+
167------------------------------------------------------------------------------
184------------------------------------------------------------------------------
168-- From Values
185-- From Values
169------------------------------------------------------------------------------
186------------------------------------------------------------------------------
@@ -204,6 +221,7 @@ fromList = Stream step
204-- Conversions From/To
221-- Conversions From/To
205------------------------------------------------------------------------------
222------------------------------------------------------------------------------
206
223
224+-- | Convert a CPS encoded StreamK to direct style step encoded StreamD
207{-# INLINE_LATE fromStreamK #-}
225{-# INLINE_LATE fromStreamK #-}
208fromStreamK :: Monad m => K.Stream m a -> Stream m a
226fromStreamK :: Monad m => K.Stream m a -> Stream m a
209fromStreamK = Stream step
227fromStreamK = Stream step
@@ -214,7 +232,7 @@ fromStreamK = Stream step
214 yieldk a r = return $ Yield a r
232 yieldk a r = return $ Yield a r
215 in K.foldStreamShared gst yieldk single stop m1
233 in K.foldStreamShared gst yieldk single stop m1
216
234
217−-- Convert a direct stream to and from CPS encoded stream
235+-- | Convert a direct style step encoded StreamD to a CPS encoded StreamK
218{-# INLINE_LATE toStreamK #-}
236{-# INLINE_LATE toStreamK #-}
219toStreamK :: Monad m => Stream m a -> K.Stream m a
237toStreamK :: Monad m => Stream m a -> K.Stream m a
220toStreamK (Stream step state) = go state
238toStreamK (Stream step state) = go state
@@ -235,10 +253,12 @@ toStreamK (Stream step state) = go state
235 forall s. fromStreamK (toStreamK s) = s #-}
253 forall s. fromStreamK (toStreamK s) = s #-}
236#endif
254#endif
237
255
256+-- XXX Rename to toStream or move to some IsStream common module
238{-# INLINE fromStreamD #-}
257{-# INLINE fromStreamD #-}
239fromStreamD :: (K.IsStream t, Monad m) => Stream m a -> t m a
258fromStreamD :: (K.IsStream t, Monad m) => Stream m a -> t m a
240fromStreamD = K.fromStream . toStreamK
259fromStreamD = K.fromStream . toStreamK
241
260
261+-- XXX Rename to toStream or move to some IsStream common module
242{-# INLINE toStreamD #-}
262{-# INLINE toStreamD #-}
243toStreamD :: (K.IsStream t, Monad m) => t m a -> Stream m a
263toStreamD :: (K.IsStream t, Monad m) => t m a -> Stream m a
244toStreamD = fromStreamK . K.toStream
264toStreamD = fromStreamK . K.toStream
@@ -659,6 +679,46 @@ instance Applicative f => Applicative (Stream f) where
659 {-# INLINE (<*) #-}
679 {-# INLINE (<*) #-}
660 (<*) = apDiscardSnd
680 (<*) = apDiscardSnd
661
681
682+------------------------------------------------------------------------------
683+-- Combine N Streams - concatUnfold
684+------------------------------------------------------------------------------
685+
686+-- Define a unique structure to use in inspection testing
687+data ConcatMapUState o i =
688+ ConcatMapUOuter o
689+ | ConcatMapUInner o i
690+
691+-- | @concatUnfold unfold stream@ uses @unfold@ to map the input stream elements
692+-- to streams and then flattens the generated streams into a single output
693+-- stream.
694+
695+-- This is like 'concatMap' but uses an unfold with an explicit state to
696+-- generate the stream instead of a 'Stream' type generator. This allows better
697+-- optimization via fusion. This can be many times more efficient than
698+-- 'concatMap'.
699+
700+{-# INLINE_NORMAL concatUnfold #-}
701+concatUnfold :: Monad m => Unfold m a b -> Stream m a -> Stream m b
702+concatUnfold (Unfold istep inject) (Stream ostep ost) =
703+ Stream step (ConcatMapUOuter ost)
704+ where
705+ {-# INLINE_LATE step #-}
706+ step gst (ConcatMapUOuter o) = do
707+ r <- ostep (adaptState gst) o
708+ case r of
709+ Yield a o' -> do
710+ i <- inject a
711+ i `seq` return (Skip (ConcatMapUInner o' i))
712+ Skip o' -> return $ Skip (ConcatMapUOuter o')
713+ Stop -> return $ Stop
714+
715+ step _ (ConcatMapUInner o i) = do
716+ r <- istep i
717+ return $ case r of
718+ Yield x i' -> Yield x (ConcatMapUInner o i')
719+ Skip i' -> Skip (ConcatMapUInner o i')
720+ Stop -> Skip (ConcatMapUOuter o)
721+
662------------------------------------------------------------------------------
722------------------------------------------------------------------------------
663-- Combine N Streams - concatMap
723-- Combine N Streams - concatMap
664------------------------------------------------------------------------------
724------------------------------------------------------------------------------
@@ -843,6 +903,10 @@ foldMany1 (Fold fstep initial extract) (Stream step state) =
843 step' _ (FoldMany1Yield b next) = return $ Yield b next
903 step' _ (FoldMany1Yield b next) = return $ Yield b next
844 step' _ FoldMany1Done = return Stop
904 step' _ FoldMany1Done = return Stop
845
905
906+{-# INLINE chunksOf #-}
907+chunksOf :: Monad m => Int -> Fold m a b -> Stream m a -> Stream m b
908+chunksOf n f = foldMany (FL.takeLE n f)
909+
846data GroupState2 s fs
910data GroupState2 s fs
847 = GroupStart2 s
911 = GroupStart2 s
848 | GroupBuffer2 s fs Int
912 | GroupBuffer2 s fs Int
src/Streamly/Internal/Data/Unfold.hs+2−13
@@ -159,16 +159,16 @@ import Streamly.Internal.Data.IOFinalizer
159 (newIOFinalizer, runIOFinalizer, clearingIOFinalizer)
159 (newIOFinalizer, runIOFinalizer, clearingIOFinalizer)
160import Streamly.Internal.Data.Stream.StreamD.Type (Stream(..), Step(..))
160import Streamly.Internal.Data.Stream.StreamD.Type (Stream(..), Step(..))
161import Streamly.Internal.Data.Time.Clock (Clock(Monotonic), getTime)
161import Streamly.Internal.Data.Time.Clock (Clock(Monotonic), getTime)
162−import Streamly.Internal.Data.Unfold.Types (Unfold(..))
162+import Streamly.Internal.Data.Unfold.Types (Unfold(..), lmap)
163import System.Mem (performMajorGC)
163import System.Mem (performMajorGC)
164
164
165import qualified Prelude
165import qualified Prelude
166import qualified Control.Monad.Catch as MC
166import qualified Control.Monad.Catch as MC
167import qualified Data.Tuple as Tuple
167import qualified Data.Tuple as Tuple
168import qualified Streamly.Internal.Data.Fold.Types as FL
168import qualified Streamly.Internal.Data.Fold.Types as FL
169+import qualified Streamly.Internal.Data.Stream.StreamD.Type as D
169import qualified Streamly.Internal.Data.Stream.StreamK as K (uncons)
170import qualified Streamly.Internal.Data.Stream.StreamK as K (uncons)
170import qualified Streamly.Internal.Data.Stream.StreamK.Type as K
171import qualified Streamly.Internal.Data.Stream.StreamK.Type as K
171−import qualified Streamly.Internal.Data.Stream.StreamD as D (unfold)
172
172
173import Streamly.Internal.Data.SVar
173import Streamly.Internal.Data.SVar
174import Prelude
174import Prelude
@@ -179,17 +179,6 @@ import Prelude
179-- Input operations
179-- Input operations
180-------------------------------------------------------------------------------
180-------------------------------------------------------------------------------
181
181
182−-- | Map a function on the input argument of the 'Unfold'.
183−--
184−-- @
185−-- lmap f = concat (singleton f)
186−-- @
187−--
188−-- /Internal/
189−{-# INLINE_NORMAL lmap #-}
190−lmap :: (a -> c) -> Unfold m c b -> Unfold m a b
191−lmap f (Unfold ustep uinject) = Unfold ustep (uinject . f)
192−
193-- | Map an action on the input argument of the 'Unfold'.
182-- | Map an action on the input argument of the 'Unfold'.
194--
183--
195-- @
184-- @
src/Streamly/Internal/Data/Unfold/Types.hs+15−1
@@ -8,10 +8,13 @@
8
8
9module Streamly.Internal.Data.Unfold.Types
9module Streamly.Internal.Data.Unfold.Types
10 ( Unfold (..)
10 ( Unfold (..)
11+ , lmap
11 )
12 )
12where
13where
13
14
15+#include "inline.hs"
16+
14−import Streamly.Internal.Data.Stream.StreamD.Type (Step(..))
17+import Streamly.Internal.Data.Stream.StreamD.Step (Step(..))
15
18
16------------------------------------------------------------------------------
19------------------------------------------------------------------------------
17-- Monadic Unfolds
20-- Monadic Unfolds
@@ -25,3 +28,14 @@ import Streamly.Internal.Data.Stream.StreamD.Type (Step(..))
25data Unfold m a b =
28data Unfold m a b =
26 -- | @Unfold step inject@
29 -- | @Unfold step inject@
27 forall s. Unfold (s -> m (Step s b)) (a -> m s)
30 forall s. Unfold (s -> m (Step s b)) (a -> m s)
31+
32+-- | Map a function on the input argument of the 'Unfold'.
33+--
34+-- @
35+-- lmap f = concat (singleton f)
36+-- @
37+--
38+-- /Internal/
39+{-# INLINE_NORMAL lmap #-}
40+lmap :: (a -> c) -> Unfold m c b -> Unfold m a b
41+lmap f (Unfold ustep uinject) = Unfold ustep (uinject . f)
src/Streamly/Internal/FileSystem/Handle.hs+2−2
@@ -114,11 +114,11 @@ import Streamly.Data.Fold (Fold)
114import Streamly.Internal.Data.Fold.Types (Fold2(..))
114import Streamly.Internal.Data.Fold.Types (Fold2(..))
115import Streamly.Internal.Data.Unfold.Types (Unfold(..))
115import Streamly.Internal.Data.Unfold.Types (Unfold(..))
116import Streamly.Internal.Data.Array.Storable.Foreign.Types
116import Streamly.Internal.Data.Array.Storable.Foreign.Types
117− (Array(..), writeNUnsafe, defaultChunkSize, lpackArraysChunksOf
117+ (Array(..), writeNUnsafe, defaultChunkSize , unsafeFreezeWithShrink)
118− , unsafeFreezeWithShrink)
119import Streamly.Internal.Data.Array.Storable.Foreign.Mut.Types (mutableArray)
118import Streamly.Internal.Data.Array.Storable.Foreign.Mut.Types (mutableArray)
120import Streamly.Internal.Data.Stream.Serial (SerialT)
119import Streamly.Internal.Data.Stream.Serial (SerialT)
121import Streamly.Internal.Data.Stream.StreamK.Type (IsStream, mkStream)
120import Streamly.Internal.Data.Stream.StreamK.Type (IsStream, mkStream)
121+import Streamly.Internal.Memory.ArrayStream (lpackArraysChunksOf)
122-- import Streamly.String (encodeUtf8, decodeUtf8, foldLines)
122-- import Streamly.String (encodeUtf8, decodeUtf8, foldLines)
123
123
124import qualified Streamly.Data.Fold as FL
124import qualified Streamly.Data.Fold as FL
src/Streamly/Internal/Memory/ArrayStream.hs+189−36
@@ -1,15 +1,12 @@
1−#include "inline.hs"
2−
3-- |
1-- |
4-- Module : Streamly.Internal.Memory.ArrayStream
2-- Module : Streamly.Internal.Memory.ArrayStream
5−-- Copyright : (c) 2020 Composewell Technologies
3+-- Copyright : (c) 2019 Composewell Technologies
6−--
7-- License : BSD3-3-Clause
4-- License : BSD3-3-Clause
8-- Maintainer : streamly@composewell.com
5-- Maintainer : streamly@composewell.com
9-- Stability : experimental
6-- Stability : experimental
10-- Portability : GHC
7-- Portability : GHC
11--
8--
12−-- Combinators to efficiently manipulate streams of arrays.
9+-- Combinators to efficiently manipulate streams of immutable arrays.
13--
10--
14module Streamly.Internal.Memory.ArrayStream
11module Streamly.Internal.Memory.ArrayStream
15 (
12 (
@@ -22,35 +19,72 @@ module Streamly.Internal.Memory.ArrayStream
22 , interpose
19 , interpose
23 , interposeSuffix
20 , interposeSuffix
24 , intercalateSuffix
21 , intercalateSuffix
25−
22+ , unlines
26− -- * Transformation
27− , splitOn
28− , splitOnSuffix
29− , compact -- compact
30
23
31 -- * Elimination
24 -- * Elimination
32 , toArray
25 , toArray
26+
27+ -- * Compaction
28+ , lpackArraysChunksOf
29+#if !defined(mingw32_HOST_OS)
30+ , groupIOVecsOf
31+#endif
32+ , compact
33+
34+ -- * Splitting
35+ , splitOn
36+ , splitOnSuffix
33 )
37 )
34where
38where
35
39
40+#include "inline.hs"
41+
36import Control.Monad.IO.Class (MonadIO(..))
42import Control.Monad.IO.Class (MonadIO(..))
37−-- import Data.Functor.Identity (Identity)
38import Data.Word (Word8)
43import Data.Word (Word8)
39−import Foreign.ForeignPtr (withForeignPtr)
44+import Foreign.ForeignPtr (withForeignPtr, touchForeignPtr)
45+import Foreign.ForeignPtr.Unsafe (unsafeForeignPtrToPtr)
40import Foreign.Ptr (minusPtr, plusPtr, castPtr)
46import Foreign.Ptr (minusPtr, plusPtr, castPtr)
41import Foreign.Storable (Storable(..))
47import Foreign.Storable (Storable(..))
48+import GHC.ForeignPtr (ForeignPtr(..))
49+import GHC.Ptr (Ptr(..))
42−import Prelude hiding (length, null, last, map, (!!), read, concat)
50+import Prelude hiding (length, null, last, map, (!!), read, concat, unlines)
43
51
52+#if !defined(mingw32_HOST_OS)
53+import Streamly.FileSystem.FDIO (IOVec(..))
54+#endif
44import Streamly.Internal.Data.Array.Storable.Foreign.Types (Array(..), length)
55import Streamly.Internal.Data.Array.Storable.Foreign.Types (Array(..), length)
56+import Streamly.Internal.Data.Fold.Types (Fold(..))
45import Streamly.Internal.Data.Stream.Serial (SerialT)
57import Streamly.Internal.Data.Stream.Serial (SerialT)
46import Streamly.Internal.Data.Stream.StreamK.Type (IsStream)
58import Streamly.Internal.Data.Stream.StreamK.Type (IsStream)
59+import Streamly.Internal.Data.SVar (adaptState)
47
60
48import qualified Streamly.Internal.Data.Array.Storable.Foreign as A
61import qualified Streamly.Internal.Data.Array.Storable.Foreign as A
49import qualified Streamly.Internal.Data.Array.Storable.Foreign.Types as A
62import qualified Streamly.Internal.Data.Array.Storable.Foreign.Types as A
50import qualified Streamly.Internal.Data.Array.Storable.Foreign.Mut.Types as MA
63import qualified Streamly.Internal.Data.Array.Storable.Foreign.Mut.Types as MA
64+import qualified Streamly.Internal.Data.Array.Stream.Mut.Foreign as AS
65+import qualified Streamly.Internal.Data.Fold.Types as FL
51import qualified Streamly.Internal.Data.Stream.IsStream as S
66import qualified Streamly.Internal.Data.Stream.IsStream as S
52import qualified Streamly.Internal.Data.Stream.StreamD as D
67import qualified Streamly.Internal.Data.Stream.StreamD as D
53
68
69+-------------------------------------------------------------------------------
70+-- Generation
71+-------------------------------------------------------------------------------
72+
73+-- | @arraysOf n stream@ groups the elements in the input stream into arrays of
74+-- @n@ elements each.
75+--
76+-- > arraysOf n = Stream.chunksOf n (Array.writeN n)
77+--
78+-- /Internal/
79+{-# INLINE arraysOf #-}
80+arraysOf :: (IsStream t, MonadIO m, Storable a)
81+ => Int -> t m a -> t m (Array a)
82+arraysOf n str = D.fromStreamD $ A.arraysOf n (D.toStreamD str)
83+
84+-------------------------------------------------------------------------------
85+-- Append
86+-------------------------------------------------------------------------------
87+
54-- XXX efficiently compare two streams of arrays. Two streams can have chunks
88-- XXX efficiently compare two streams of arrays. Two streams can have chunks
55-- of different sizes, we can handle that in the stream comparison abstraction.
89-- of different sizes, we can handle that in the stream comparison abstraction.
56-- This could be useful e.g. to fast compare whether two files differ.
90-- This could be useful e.g. to fast compare whether two files differ.
@@ -66,7 +100,7 @@ import qualified Streamly.Internal.Data.Stream.StreamD as D
66concat :: (IsStream t, MonadIO m, Storable a) => t m (Array a) -> t m a
100concat :: (IsStream t, MonadIO m, Storable a) => t m (Array a) -> t m a
67-- concat m = D.fromStreamD $ A.flattenArrays (D.toStreamD m)
101-- concat m = D.fromStreamD $ A.flattenArrays (D.toStreamD m)
68-- concat m = D.fromStreamD $ D.concatMap A.toStreamD (D.toStreamD m)
102-- concat m = D.fromStreamD $ D.concatMap A.toStreamD (D.toStreamD m)
69−concat m = D.fromStreamD $ D.concatMapU A.read (D.toStreamD m)
103+concat m = D.fromStreamD $ D.concatUnfold A.read (D.toStreamD m)
70
104
71-- XXX should we have a reverseArrays API to reverse the stream of arrays
105-- XXX should we have a reverseArrays API to reverse the stream of arrays
72-- instead?
106-- instead?
@@ -79,6 +113,10 @@ concat m = D.fromStreamD $ D.concatMapU A.read (D.toStreamD m)
79concatRev :: (IsStream t, MonadIO m, Storable a) => t m (Array a) -> t m a
113concatRev :: (IsStream t, MonadIO m, Storable a) => t m (Array a) -> t m a
80concatRev m = D.fromStreamD $ A.flattenArraysRev (D.toStreamD m)
114concatRev m = D.fromStreamD $ A.flattenArraysRev (D.toStreamD m)
81
115
116+-------------------------------------------------------------------------------
117+-- Intersperse and append
118+-------------------------------------------------------------------------------
119+
82-- | Flatten a stream of arrays after inserting the given element between
120-- | Flatten a stream of arrays after inserting the given element between
83-- arrays.
121-- arrays.
84--
122--
@@ -102,6 +140,140 @@ interposeSuffix :: (MonadIO m, IsStream t, Storable a)
102-- interposeSuffix x = D.fromStreamD . A.unlines x . D.toStreamD
140-- interposeSuffix x = D.fromStreamD . A.unlines x . D.toStreamD
103interposeSuffix x = S.interposeSuffix x A.read
141interposeSuffix x = S.interposeSuffix x A.read
104
142
143+data FlattenState s a =
144+ OuterLoop s
145+ | InnerLoop s !(ForeignPtr a) !(Ptr a) !(Ptr a)
146+
147+{-# INLINE_NORMAL unlines #-}
148+unlines :: forall m a. (MonadIO m, Storable a)
149+ => a -> D.Stream m (Array a) -> D.Stream m a
150+unlines sep (D.Stream step state) = D.Stream step' (OuterLoop state)
151+ where
152+ {-# INLINE_LATE step' #-}
153+ step' gst (OuterLoop st) = do
154+ r <- step (adaptState gst) st
155+ return $ case r of
156+ D.Yield Array{..} s ->
157+ let p = unsafeForeignPtrToPtr aStart
158+ in D.Skip (InnerLoop s aStart p aEnd)
159+ D.Skip s -> D.Skip (OuterLoop s)
160+ D.Stop -> D.Stop
161+
162+ step' _ (InnerLoop st _ p end) | p == end =
163+ return $ D.Yield sep $ OuterLoop st
164+
165+ step' _ (InnerLoop st startf p end) = do
166+ x <- liftIO $ do
167+ r <- peek p
168+ touchForeignPtr startf
169+ return r
170+ return $ D.Yield x (InnerLoop st startf
171+ (p `plusPtr` sizeOf (undefined :: a)) end)
172+
173+-------------------------------------------------------------------------------
174+-- Compact
175+-------------------------------------------------------------------------------
176+
177+{-# INLINE_NORMAL packArraysChunksOf #-}
178+packArraysChunksOf :: (MonadIO m, Storable a)
179+ => Int -> D.Stream m (Array a) -> D.Stream m (Array a)
180+packArraysChunksOf n str =
181+ D.map A.unsafeFreeze $ AS.packArraysChunksOf n $ D.map A.unsafeThaw str
182+
183+-- XXX instead of writing two different versions of this operation, we should
184+-- write it as a pipe.
185+{-# INLINE_NORMAL lpackArraysChunksOf #-}
186+lpackArraysChunksOf :: (MonadIO m, Storable a)
187+ => Int -> Fold m (Array a) () -> Fold m (Array a) ()
188+lpackArraysChunksOf n fld =
189+ FL.map A.unsafeThaw $ AS.lpackArraysChunksOf n (FL.map A.unsafeFreeze fld)
190+
191+#if !defined(mingw32_HOST_OS)
192+
193+-- | @groupIOVecsOf maxBytes maxEntries@ groups arrays in the incoming stream
194+-- to create a stream of 'IOVec' arrays with a maximum of @maxBytes@ bytes in
195+-- each array and a maximum of @maxEntries@ entries in each array.
196+--
197+-- @since 0.7.0
198+{-# INLINE_NORMAL groupIOVecsOf #-}
199+groupIOVecsOf :: MonadIO m
200+ => Int -> Int -> D.Stream m (Array a) -> D.Stream m (Array IOVec)
201+groupIOVecsOf n maxIOVLen str =
202+ D.map A.unsafeFreeze
203+ $ AS.groupIOVecsOf n maxIOVLen
204+ $ D.map A.unsafeThaw str
205+#endif
206+
207+-- | Coalesce adjacent arrays in incoming stream to form bigger arrays of a
208+-- maximum specified size in bytes.
209+--
210+-- @since 0.7.0
211+{-# INLINE compact #-}
212+compact :: (MonadIO m, Storable a)
213+ => Int -> SerialT m (Array a) -> SerialT m (Array a)
214+compact n xs = D.fromStreamD $ packArraysChunksOf n (D.toStreamD xs)
215+
216+-------------------------------------------------------------------------------
217+-- Split
218+-------------------------------------------------------------------------------
219+
220+data SplitState s arr
221+ = Initial s
222+ | Buffering s arr
223+ | Splitting s arr
224+ | Yielding arr (SplitState s arr)
225+ | Finishing
226+
227+-- | Split a stream of arrays on a given separator byte, dropping the separator
228+-- and coalescing all the arrays between two separators into a single array.
229+--
230+-- @since 0.7.0
231+{-# INLINE_NORMAL _splitOn #-}
232+_splitOn
233+ :: MonadIO m
234+ => Word8
235+ -> D.Stream m (Array Word8)
236+ -> D.Stream m (Array Word8)
237+_splitOn byte (D.Stream step state) = D.Stream step' (Initial state)
238+
239+ where
240+
241+ {-# INLINE_LATE step' #-}
242+ step' gst (Initial st) = do
243+ r <- step gst st
244+ case r of
245+ D.Yield arr s -> do
246+ (arr1, marr2) <- A.breakOn byte arr
247+ return $ case marr2 of
248+ Nothing -> D.Skip (Buffering s arr1)
249+ Just arr2 -> D.Skip (Yielding arr1 (Splitting s arr2))
250+ D.Skip s -> return $ D.Skip (Initial s)
251+ D.Stop -> return D.Stop
252+
253+ step' gst (Buffering st buf) = do
254+ r <- step gst st
255+ case r of
256+ D.Yield arr s -> do
257+ (arr1, marr2) <- A.breakOn byte arr
258+ buf' <- A.spliceTwo buf arr1
259+ return $ case marr2 of
260+ Nothing -> D.Skip (Buffering s buf')
261+ Just x -> D.Skip (Yielding buf' (Splitting s x))
262+ D.Skip s -> return $ D.Skip (Buffering s buf)
263+ D.Stop -> return $
264+ if A.byteLength buf == 0
265+ then D.Stop
266+ else D.Skip (Yielding buf Finishing)
267+
268+ step' _ (Splitting st buf) = do
269+ (arr1, marr2) <- A.breakOn byte buf
270+ return $ case marr2 of
271+ Nothing -> D.Skip $ Buffering st arr1
272+ Just arr2 -> D.Skip $ Yielding arr1 (Splitting st arr2)
273+
274+ step' _ (Yielding arr next) = return $ D.Yield arr next
275+ step' _ Finishing = return D.Stop
276+
105-- | Split a stream of arrays on a given separator byte, dropping the separator
277-- | Split a stream of arrays on a given separator byte, dropping the separator
106-- and coalescing all the arrays between two separators into a single array.
278-- and coalescing all the arrays between two separators into a single array.
107--
279--
@@ -125,28 +297,9 @@ splitOnSuffix
125splitOnSuffix byte s =
297splitOnSuffix byte s =
126 D.fromStreamD $ D.splitInnerBySuffix (A.breakOn byte) A.spliceTwo $ D.toStreamD s
298 D.fromStreamD $ D.splitInnerBySuffix (A.breakOn byte) A.spliceTwo $ D.toStreamD s
127
299
128−-- | Coalesce adjacent arrays in incoming stream to form bigger arrays of a
300+-------------------------------------------------------------------------------
129−-- maximum specified size in bytes.
301+-- Elimination
130−--
302+-------------------------------------------------------------------------------
131−-- @since 0.7.0
132−{-# INLINE compact #-}
133−compact :: (MonadIO m, Storable a)
134− => Int -> SerialT m (Array a) -> SerialT m (Array a)
135−compact n xs = D.fromStreamD $ A.packArraysChunksOf n (D.toStreamD xs)
136−
137−-- | @arraysOf n stream@ groups the elements in the input stream into arrays of
138−-- @n@ elements each.
139−--
140−-- Same as the following but more efficient:
141−--
142−-- > arraysOf n = S.chunksOf n (A.writeN n)
143−--
144−-- @since 0.7.0
145−{-# INLINE arraysOf #-}
146−arraysOf :: (IsStream t, MonadIO m, Storable a)
147− => Int -> t m a -> t m (Array a)
148−arraysOf n str =
149− D.fromStreamD $ A.fromStreamDArraysOf n (D.toStreamD str)
150
303
151-- XXX Both of these implementations of splicing seem to perform equally well.
304-- XXX Both of these implementations of splicing seem to perform equally well.
152-- We need to perform benchmarks over a range of sizes though.
305-- We need to perform benchmarks over a range of sizes though.
src/Streamly/Internal/Network/Socket.hs+2−1
@@ -89,7 +89,8 @@ import qualified Network.Socket as Net
89
89
90import Streamly.Prelude (MonadAsync)
90import Streamly.Prelude (MonadAsync)
91import Streamly.Internal.Data.Unfold.Types (Unfold(..))
91import Streamly.Internal.Data.Unfold.Types (Unfold(..))
92+import Streamly.Internal.Memory.ArrayStream (lpackArraysChunksOf)
92−import Streamly.Internal.Data.Array.Storable.Foreign.Types (Array(..), lpackArraysChunksOf)
93+import Streamly.Internal.Data.Array.Storable.Foreign.Types (Array(..))
93import Streamly.Internal.Data.Array.Storable.Foreign.Mut.Types (mutableArray)
94import Streamly.Internal.Data.Array.Storable.Foreign.Mut.Types (mutableArray)
94import Streamly.Internal.Data.Stream.Serial (SerialT)
95import Streamly.Internal.Data.Stream.Serial (SerialT)
95import Streamly.Internal.Data.Stream.StreamK.Type (IsStream, mkStream)
96import Streamly.Internal.Data.Stream.StreamK.Type (IsStream, mkStream)
streamly.cabal+2
@@ -430,6 +430,7 @@ library
430 , Streamly.Internal.Data.SVar
430 , Streamly.Internal.Data.SVar
431 , Streamly.Internal.Data.Stream.StreamK.Type
431 , Streamly.Internal.Data.Stream.StreamK.Type
432 , Streamly.Internal.Data.Fold.Types
432 , Streamly.Internal.Data.Fold.Types
433+ , Streamly.Internal.Data.Stream.StreamD.Step
433 , Streamly.Internal.Data.Stream.StreamD.Type
434 , Streamly.Internal.Data.Stream.StreamD.Type
434 , Streamly.Internal.Data.Stream.StreamDK.Type
435 , Streamly.Internal.Data.Stream.StreamDK.Type
435 , Streamly.Internal.Data.Unfold.Types
436 , Streamly.Internal.Data.Unfold.Types
@@ -501,6 +502,7 @@ library
501 , Streamly.Internal.Data.Array.Prim
502 , Streamly.Internal.Data.Array.Prim
502 , Streamly.Internal.Data.Array.Prim.Pinned
503 , Streamly.Internal.Data.Array.Prim.Pinned
503 , Streamly.Internal.Data.SmallArray
504 , Streamly.Internal.Data.SmallArray
505+ , Streamly.Internal.Data.Array.Stream.Mut.Foreign
504 , Streamly.Internal.Memory.ArrayStream
506 , Streamly.Internal.Memory.ArrayStream
505
507
506 -- streamly-unicode
508 -- streamly-unicode