on master help sign in
Add Data.Array.Prim.Pinned and Data.Array.Prim

Respect the new module structure for Internal files

commit4d0631a0c6f7bfb598ffd48b77a4a7fcea1a1ac4 authoradithyaov <adi.obilisetty@gmail.com> dateThu 16 Jul 2020, 17:30 (6 years ago) parent12c672e
20 files changed+2172−454
.hlint.ignore+3−1 bench.sh+9−2 benchmark/Streamly/Benchmark/Data/Array.hs+10−17 benchmark/Streamly/Benchmark/Data/ArrayOps.hs+2−8 benchmark/streamly-benchmarks.cabal+10−2 src/Streamly/Data/Prim/Array.hs+3−4 src/Streamly/Internal/Data/Array/Prim.hs+110 src/Streamly/Internal/Data/Array/Prim/Mut/Types.hs+94 src/Streamly/Internal/Data/Array/Prim/MutTypesInclude.hs+400 src/Streamly/Internal/Data/Array/Prim/Pinned.hs+108 src/Streamly/Internal/Data/Array/Prim/Pinned/Mut/Types.hs+176 src/Streamly/Internal/Data/Array/Prim/Pinned/Types.hs+127 src/Streamly/Internal/Data/Array/Prim/Types.hs+98 src/Streamly/Internal/Data/Array/Prim/TypesInclude.hs+694 src/Streamly/Internal/Data/Array/PrimInclude.hs+185 src/Streamly/Internal/Data/Prim/Array.hs−201 src/Streamly/Internal/Data/Prim/Array/Types.hs−207 src/Streamly/Internal/Unicode/Array/Prim/Pinned.hs+84 streamly.cabal+28−3 test/Streamly/Test/Array.hs+31−9
.hlint.ignore+3−1
@@ -12,7 +12,9 @@ src/Streamly/Internal/Data/Stream/StreamD/Type.hs
1212src/Streamly/Internal/Data/Stream/StreamDK/Type.hs
1313src/Streamly/Internal/Data/Stream/StreamD.hs
1414src/Streamly/Internal/Data/Pipe/Types.hs
15−src/Streamly/Internal/Data/Prim/Array/Types.hs
1615src/Streamly/Internal/Data/SmallArray/Types.hs
1716src/Streamly/Internal/Data/Unicode/Stream.hs
1817src/Streamly/Internal/Mutable/Prim/Var.hs
18+src/Streamly/Internal/Data/Array/Prim/Types.hs
19+src/Streamly/Internal/Data/Array/Prim/MutTypesInclude.hs
20+src/Streamly/Internal/Data/Array/Prim/TypesInclude.hs
bench.sh+9−2
@@ -29,7 +29,12 @@ prelude_other_grp="\
2929 Prelude.Concurrent \
3030 Prelude.Adaptive"
3131
32−array_grp="Memory.Array Data.Array Data.Prim.Array Data.SmallArray"
32+array_grp="\
33+ Memory.Array \
34+ Data.Array \
35+ Data.Array.Prim \
36+ Data.SmallArray \
37+ Data.Array.Prim.Pinned"
3338
3439base_parser_grp="Data.Parser.ParserD Data.Parser.ParserK"
3540parser_grp="Data.Fold Data.Parser"
@@ -54,7 +59,8 @@ base_stream_cmp="Data.Stream.StreamD Data.Stream.StreamK"
5459serial_wserial_cmp="Prelude.Serial Prelude.WSerial"
5560serial_async_cmp="Prelude.Serial Prelude.Async"
5661concurrent_cmp="Prelude.Async Prelude.WAsync Prelude.Ahead Prelude.Parallel"
57−array_cmp="Memory.Array Data.Prim.Array Data.Array"
62+array_cmp="Memory.Array Data.Array.Prim Data.Array Data.Array.Prim.Pinned"
63+pinned_array_cmp="Memory.Array Data.Array.Prim.Pinned"
5864base_parser_cmp=$base_parser_grp
5965COMPARISONS="\
6066 base_stream_cmp \
@@ -62,6 +68,7 @@ COMPARISONS="\
6268 serial_async_cmp \
6369 concurrent_cmp \
6470 array_cmp \
71+ pinned_array_cmp \
6572 base_parser_cmp"
6673
6774#------------------------------------------------------------------------------
benchmark/Streamly/Benchmark/Data/Array.hs+10−17
@@ -1,6 +1,6 @@
11-- |
22-- Module : Main
3−-- Copyright : (c) 2019 Composewell Technologies
3+-- Copyright : (c) 2020 Composewell Technologies
44--
55-- License : BSD-3-Clause
66-- Maintainer : streamly@composewell.com
@@ -8,25 +8,20 @@
88{-# LANGUAGE CPP #-}
99
1010import Control.DeepSeq (NFData(..))
11−
12−#ifndef DATA_PRIM_ARRAY
13−import Control.DeepSeq (deepseq)
14−#endif
15−
1611import System.Random (randomRIO)
1712
1813import qualified Streamly.Benchmark.Data.ArrayOps as Ops
1914
20−import Streamly.Benchmark.Common hiding (benchPureSrc)
2115import Gauge
16+import Streamly.Benchmark.Common hiding (benchPureSrc)
2217
2318#ifdef MEMORY_ARRAY
2419import qualified GHC.Exts as GHC
2520import Foreign.Storable (Storable(..))
2621#endif
2722
28−#ifdef DATA_PRIM_ARRAY
29−import Data.Primitive.Types (Prim(..))
23+#if !defined(DATA_ARRAY_PRIM) && !defined(DATA_ARRAY_PRIM_PINNED)
24+import Control.DeepSeq (deepseq)
3025#endif
3126
3227-------------------------------------------------------------------------------
@@ -49,13 +44,9 @@ benchIO name src f = bench name $ nfIO $
4944-- Drain a source that generates an array in the IO monad
5045{-# INLINE benchIOSrc #-}
5146benchIOSrc ::
52−#ifndef DATA_PRIM_ARRAY
5347 NFData a =>
54−#endif
5548#ifdef MEMORY_ARRAY
5649 Storable a =>
57−#elif defined(DATA_PRIM_ARRAY)
58− Prim a =>
5950#endif
6051 String -> (Int -> IO (Ops.Stream a)) -> Benchmark
6152benchIOSrc name src = benchIO name src id
@@ -92,7 +83,7 @@ o_1_space_generation value =
9283 "writeN . IsString.fromString"
9384 (Ops.sourceIsString value)
9485#endif
95−#ifndef DATA_PRIM_ARRAY
86+#if !defined(DATA_ARRAY_PRIM) && !defined(DATA_ARRAY_PRIM_PINNED)
9687#ifdef DATA_SMALLARRAY
9788 , let testStr =
9889 "fromListN " ++
@@ -122,7 +113,7 @@ o_1_space_elimination value =
122113 , benchIOSink value "foldl'" Ops.pureFoldl'
123114 , benchIOSink value "read" Ops.unfoldReadDrain
124115 , benchIOSink value "toStreamRev" Ops.toStreamRevDrain
125−#ifndef DATA_PRIM_ARRAY
116+#if !defined(DATA_ARRAY_PRIM) && !defined(DATA_ARRAY_PRIM_PINNED)
126117#ifdef DEVBUILD
127118 , benchPureSink value "foldable/foldl'" Ops.foldableFoldl'
128119 , benchPureSink value "foldable/sum" Ops.foldableSum
@@ -154,8 +145,10 @@ moduleName :: String
154145moduleName = "Data.SmallArray"
155146#elif defined(MEMORY_ARRAY)
156147moduleName = "Memory.Array"
157−#elif defined(DATA_PRIM_ARRAY)
158−moduleName = "Data.Prim.Array"
148+#elif defined(DATA_ARRAY_PRIM)
149+moduleName = "Data.Array.Prim"
150+#elif defined(DATA_ARRAY_PRIM_PINNED)
151+moduleName = "Data.Array.Prim.Pinnned"
159152#else
160153moduleName = "Data.Array"
161154#endif
benchmark/Streamly/Benchmark/Data/ArrayOps.hs+2−8
@@ -8,8 +8,6 @@
88{-# LANGUAGE CPP #-}
99{-# LANGUAGE FlexibleContexts #-}
1010{-# LANGUAGE ScopedTypeVariables #-}
11−{-# LANGUAGE DeriveAnyClass #-}
12−{-# LANGUAGE DeriveGeneric #-}
1311
1412-- CPP:
1513-- MEMORY_ARRAY
@@ -24,8 +22,7 @@ import Prelude (Bool, Int, Maybe(..), ($), (+), (.), (==), (>), undefined)
2422
2523import qualified Prelude as P
2624import qualified Streamly as S hiding (foldMapWith, runStream)
27−import qualified Streamly.Prelude as S
28−
25+import qualified Streamly.Prelude as S
2926
3027#ifndef DATA_PRIM_ARRAY
3128#ifdef DEVBUILD
@@ -33,14 +30,11 @@ import qualified Data.Foldable as F
3330#endif
3431#endif
3532
36−#ifdef MEMORY_ARRAY
37−import qualified GHC.Exts as GHC
38−#endif
39−
4033#ifdef DATA_SMALLARRAY
4134import qualified Streamly.Internal.Data.SmallArray as A
4235type Stream = A.SmallArray
4336#elif defined(MEMORY_ARRAY)
37+import qualified GHC.Exts as GHC
4438import qualified Streamly.Memory.Array as A
4539import qualified Streamly.Internal.Memory.Array as A
4640type Stream = A.Array
benchmark/streamly-benchmarks.cabal+10−2
@@ -387,13 +387,13 @@ benchmark Data.Array
387387 other-modules: Streamly.Benchmark.Data.ArrayOps
388388 cpp-options: -DDATA_ARRAY
389389
390−benchmark Data.Prim.Array
390+benchmark Data.Array.Prim
391391 import: bench-options
392392 type: exitcode-stdio-1.0
393393 hs-source-dirs: .
394394 main-is: Streamly/Benchmark/Data/Array.hs
395395 other-modules: Streamly.Benchmark.Data.ArrayOps
396− cpp-options: -DDATA_PRIM_ARRAY
396+ cpp-options: -DDATA_ARRAY_PRIM
397397 build-depends: primitive
398398
399399benchmark Data.SmallArray
@@ -404,6 +404,14 @@ benchmark Data.SmallArray
404404 other-modules: Streamly.Benchmark.Data.ArrayOps
405405 cpp-options: -DDATA_SMALLARRAY
406406
407+benchmark Data.Array.Prim.Pinned
408+ import: bench-options
409+ type: exitcode-stdio-1.0
410+ hs-source-dirs: .
411+ main-is: Streamly/Benchmark/Data/Array.hs
412+ other-modules: Streamly.Benchmark.Data.ArrayOps
413+ cpp-options: -DDATA_ARRAY_PRIM_PINNED
414+
407415benchmark Memory.Array
408416 import: bench-options
409417 type: exitcode-stdio-1.0
src/Streamly/Data/Prim/Array.hs+3−4
@@ -8,8 +8,7 @@
88-- Portability : GHC
99--
1010module Streamly.Data.Prim.Array
11− ( PrimArray
12− , Prim
11+ ( Array
1312
1413 -- * Construction
1514 , A.fromListN
@@ -39,6 +38,6 @@ module Streamly.Data.Prim.Array
3938 )
4039where
4140
42−import Streamly.Internal.Data.Prim.Array (PrimArray, Prim)
41+import Streamly.Internal.Data.Array.Prim (Array)
4342
44−import qualified Streamly.Internal.Data.Prim.Array as A
43+import qualified Streamly.Internal.Data.Array.Prim as A
src/Streamly/Internal/Data/Array/Prim.hsnew file+110
@@ -0,0 +1,110 @@
1+{-# OPTIONS_GHC -fno-warn-orphans #-}
2+
3+#include "inline.hs"
4+
5+-- |
6+-- Module : Streamly.Internal.Data.Array.Prim
7+-- Copyright : (c) 2020 Composewell Technologies
8+--
9+-- License : BSD-3-Clause
10+-- Maintainer : streamly@composewell.com
11+-- Stability : experimental
12+-- Portability : GHC
13+--
14+module Streamly.Internal.Data.Array.Prim
15+ (
16+ Array
17+
18+ -- * Construction
19+
20+ -- Pure List APIs
21+ , A.fromListN
22+ , A.fromList
23+
24+ -- Stream Folds
25+ , fromStreamN
26+ , fromStream
27+
28+ -- Monadic APIs
29+ -- , newArray
30+ , A.writeN -- drop new
31+ , A.write -- full buffer
32+
33+ -- * Elimination
34+
35+ , A.toList
36+ , toStream
37+ , toStreamRev
38+ , read
39+ , unsafeRead
40+
41+ -- * Random Access
42+ , length
43+ , null
44+ , last
45+ -- , (!!)
46+ , readIndex
47+ , A.unsafeIndex
48+ -- , readIndices
49+ -- , readRanges
50+
51+ -- , readFrom -- read from a given position to the end of file
52+ -- , readFromRev -- read from a given position to the beginning of file
53+ -- , readTo -- read from beginning up to the given position
54+ -- , readToRev -- read from end to the given position in file
55+ -- , readFromTo
56+ -- , readFromThenTo
57+
58+ -- , readChunksOfFrom
59+ -- , ...
60+
61+ -- , writeIndex
62+ -- , writeFrom -- start writing at the given position
63+ -- , writeFromRev
64+ -- , writeTo -- write from beginning up to the given position
65+ -- , writeToRev
66+ -- , writeFromTo
67+ -- , writeFromThenTo
68+ --
69+ -- , writeChunksOfFrom
70+ -- , ...
71+
72+ -- , writeIndex
73+ -- , writeIndices
74+ -- , writeRanges
75+
76+ -- -- * Search
77+ -- , bsearch
78+ -- , bsearchIndex
79+ -- , find
80+ -- , findIndex
81+ -- , findIndices
82+
83+ -- -- * In-pace mutation (for Mutable Array type)
84+ -- , partitionBy
85+ -- , shuffleBy
86+ -- , foldtWith
87+ -- , foldbWith
88+
89+ -- * Immutable Transformations
90+-- , streamTransform
91+
92+ -- * Folding Arrays
93+ , streamFold
94+ , fold
95+
96+ -- * Folds with Array as the container
97+-- , D.lastN
98+
99+ -- * Streaming array operations
100+
101+ , concat
102+ , compact
103+
104+ )
105+where
106+
107+import Streamly.Internal.Data.Array.Prim.Types (Array(..), length)
108+import qualified Streamly.Internal.Data.Array.Prim.Types as A
109+
110+#include "Streamly/Internal/Data/Array/PrimInclude.hs"
src/Streamly/Internal/Data/Array/Prim/Mut/Types.hsnew file+94
@@ -0,0 +1,94 @@
1+{-# LANGUAGE UnboxedTuples #-}
2+
3+#include "inline.hs"
4+
5+-- |
6+-- Module : Streamly.Internal.Data.Array.Prim.Mut.Types
7+-- Copyright : (c) 2020 Composewell Technologies
8+--
9+-- License : BSD-3-Clause
10+-- Maintainer : streamly@composewell.com
11+-- Stability : experimental
12+-- Portability : GHC
13+--
14+module Streamly.Internal.Data.Array.Prim.Mut.Types
15+ (
16+ Array (..)
17+
18+ -- * Construction
19+ , newArray
20+ , unsafeWriteIndex
21+
22+ , spliceTwo
23+ , unsafeCopy
24+
25+ , fromListM
26+ , fromListNM
27+ , fromStreamDN
28+ , fromStreamD
29+
30+ -- * Streams of arrays
31+ , fromStreamDArraysOf
32+
33+ , packArraysChunksOf
34+ , lpackArraysChunksOf
35+
36+#if !defined(mingw32_HOST_OS)
37+-- , groupIOVecsOf
38+#endif
39+
40+ -- * Elimination
41+ , unsafeReadIndex
42+ , length
43+ , byteLength
44+
45+ , writeN
46+ , ArrayUnsafe(..)
47+ , writeNUnsafe
48+ , write
49+
50+ -- * Utilities
51+ , resizeArray
52+ , shrinkArray
53+ )
54+where
55+
56+#include "Streamly/Internal/Data/Array/Prim/MutTypesInclude.hs"
57+
58+-------------------------------------------------------------------------------
59+-- Allocation (Unpinned)
60+-------------------------------------------------------------------------------
61+
62+-- | Allocate an array that is unpinned and can hold 'count' items. The memory
63+-- of the array is uninitialized.
64+--
65+-- Note that this is internal routine, the reference to this array cannot be
66+-- given out until the array has been written to and frozen.
67+{-# INLINE newArray #-}
68+newArray ::
69+ forall m a. (MonadIO m, Prim a)
70+ => Int
71+ -> m (Array a)
72+newArray (I# n#) =
73+ liftIO $ do
74+ let bytes = n# *# sizeOf# (undefined :: a)
75+ primitive $ \s# ->
76+ case newByteArray# bytes s# of
77+ (# s1#, arr# #) -> (# s1#, Array arr# #)
78+
79+-- | Resize (unpinned) mutable byte array to new specified size (in elem
80+-- count). The returned array is either the original array resized in-place or,
81+-- if not possible, a newly allocated (unpinned) array (with the original
82+-- content copied over).
83+{-# INLINE resizeArray #-}
84+resizeArray ::
85+ forall m a. (MonadIO m, Prim a)
86+ => Array a
87+ -> Int -- ^ new size in elem count
88+ -> m (Array a)
89+resizeArray (Array arr#) (I# n#) =
90+ liftIO $ do
91+ let bytes = n# *# sizeOf# (undefined :: a)
92+ primitive $ \s# ->
93+ case resizeMutableByteArray# arr# bytes s# of
94+ (# s1#, arr1# #) -> (# s1#, Array arr1# #)
src/Streamly/Internal/Data/Array/Prim/MutTypesInclude.hsnew file+400
@@ -0,0 +1,400 @@
1+-- Copyright : (c) 2020 Composewell Technologies
2+-- License : BSD-3-Clause
3+-- Maintainer : streamly@composewell.com
4+-- Stability : experimental
5+-- Portability : GHC
6+
7+import Control.Monad (when)
8+import Control.Monad.IO.Class (MonadIO(..))
9+import Data.Primitive.Types (Prim(..), sizeOf)
10+import Streamly.Internal.Data.Fold.Types (Fold(..))
11+import Streamly.Internal.Data.SVar (adaptState)
12+import Streamly.Internal.Data.Strict (Tuple'(..), Tuple3'(..))
13+
14+import qualified Streamly.Internal.Data.Stream.StreamD.Type as D
15+
16+import GHC.Exts
17+import Control.Monad.Primitive
18+import Prelude hiding (length, unlines)
19+
20+-------------------------------------------------------------------------------
21+-- Array Data Type
22+-------------------------------------------------------------------------------
23+
24+data Array a = Array (MutableByteArray# RealWorld)
25+
26+-------------------------------------------------------------------------------
27+-- Utilities
28+-------------------------------------------------------------------------------
29+
30+-- | Copy a range of the first array to the specified region in the second
31+-- array. Both arrays must fully contain the specified ranges, but this is not
32+-- checked. The regions are allowed to overlap, although this is only possible
33+-- when the same array is provided as both the source and the destination.
34+{-# INLINE unsafeCopy #-}
35+unsafeCopy ::
36+ forall m a. (MonadIO m, Prim a)
37+ => Array a -- ^ destination array
38+ -> Int -- ^ offset into destination array
39+ -> Array a -- ^ source array
40+ -> Int -- ^ offset into source array
41+ -> Int -- ^ number of elements to copy
42+ -> m ()
43+unsafeCopy (Array dst#) (I# doff#) (Array src#) (I# soff#) (I# n#) =
44+ liftIO $ do
45+ let toBytes cnt# = cnt# *# (sizeOf# (undefined :: a))
46+ primitive_ $
47+ copyMutableByteArray#
48+ src#
49+ (toBytes soff#)
50+ dst#
51+ (toBytes doff#)
52+ (toBytes n#)
53+
54+-------------------------------------------------------------------------------
55+-- Length
56+-------------------------------------------------------------------------------
57+
58+-- XXX rename to byteCount?
59+{-# INLINE byteLength #-}
60+byteLength :: MonadIO m => Array a -> m Int
61+byteLength (Array arr#) =
62+ liftIO $
63+ primitive
64+ (\s0# ->
65+ case getSizeofMutableByteArray# arr# s0# of
66+ (# s1#, blen# #) -> (# s1#, I# blen# #))
67+
68+
69+-- XXX Rename length to elemCount so that there is no confusion bout what it
70+-- means.
71+--
72+-- XXX Since size of 'a' is statically known, we can replace `quot` with shift
73+-- when it is power of 2. Though it may not matter unless length is used too
74+-- often.
75+--
76+{-# INLINE length #-}
77+length ::
78+ forall m a. (MonadIO m, Prim a)
79+ => Array a
80+ -> m Int
81+length arr =
82+ liftIO $ do
83+ blen <- byteLength arr
84+ return $ blen `quot` (sizeOf (undefined :: a))
85+
86+-------------------------------------------------------------------------------
87+-- Random Access
88+-------------------------------------------------------------------------------
89+
90+{-# INLINE unsafeReadIndex #-}
91+unsafeReadIndex :: (MonadIO m, Prim a) => Array a -> Int -> m a
92+unsafeReadIndex (Array arr#) (I# i#) =
93+ liftIO $ primitive (readByteArray# arr# i#)
94+
95+{-# INLINE unsafeWriteIndex #-}
96+unsafeWriteIndex ::
97+ (MonadIO m, Prim a)
98+ => Array a -- ^ array
99+ -> Int -- ^ index
100+ -> a -- ^ element
101+ -> m ()
102+unsafeWriteIndex (Array arr#) (I# i#) x =
103+ liftIO $ primitive_ (writeByteArray# arr# i# x)
104+
105+-------------------------------------------------------------------------------
106+-- Construction
107+-------------------------------------------------------------------------------
108+
109+-- Note: We do not store the actual length of the array in the Array
110+-- constructor. Therefore, for "length" API to work correctly we need to match
111+-- the ByteArray length with the used length by shrinking it.
112+--
113+-- However, it may be expensive to always shrink the array. We may want to
114+-- shrink only if significant space is being wasted. If we want to do that then
115+-- we will have to store the used length separately. Or does GHC take care of
116+-- that?
117+-- Although the docs are not explicit about it, given how the signature is,
118+-- the shrinking must me inplace. "resizeMutableByteArray#" shrinks the
119+-- array inplace.
120+{-# INLINE shrinkArray #-}
121+shrinkArray ::
122+ forall m a. (MonadIO m, Prim a)
123+ => Array a
124+ -> Int -- ^ new size
125+ -> m ()
126+shrinkArray (Array arr#) (I# n#) =
127+ liftIO $ do
128+ let bytes = n# *# (sizeOf# (undefined :: a))
129+ primitive_ (shrinkMutableByteArray# arr# bytes)
130+
131+-- | Fold the whole input to a single array.
132+--
133+-- /Caution! Do not use this on infinite streams./
134+--
135+-- /Internal/
136+{-# INLINE_NORMAL write #-}
137+write :: (MonadIO m, Prim a) => Fold m a (Array a)
138+write = Fold step initial extract
139+
140+ where
141+
142+ initial = do
143+ marr <- newArray 0
144+ return $ Tuple3' marr 0 0
145+
146+ step (Tuple3' marr i capacity) x
147+ | i == capacity = do
148+ let newCapacity = max (capacity * 2) 1
149+ newMarr <- resizeArray marr newCapacity
150+ unsafeWriteIndex newMarr i x
151+ return $ Tuple3' newMarr (i + 1) newCapacity
152+ | otherwise = do
153+ unsafeWriteIndex marr i x
154+ return $ Tuple3' marr (i + 1) capacity
155+
156+ extract (Tuple3' marr len _) = shrinkArray marr len >> return marr
157+
158+-- | @writeN n@ folds a maximum of @n@ elements from the input stream to an
159+-- 'Array'.
160+--
161+-- /Internal/
162+{-# INLINE_NORMAL writeN #-}
163+writeN :: (MonadIO m, Prim a) => Int -> Fold m a (Array a)
164+writeN limit = Fold step initial extract
165+
166+ where
167+
168+ initial = do
169+ marr <- newArray limit
170+ return $ Tuple' marr 0
171+
172+ step (Tuple' marr i) x
173+ | i == limit = return $ Tuple' marr i
174+ | otherwise = do
175+ unsafeWriteIndex marr i x
176+ return $ Tuple' marr (i + 1)
177+
178+ extract (Tuple' marr len) = shrinkArray marr len >> return marr
179+
180+-- Use Tuple' instead?
181+data ArrayUnsafe a = ArrayUnsafe
182+ {-# UNPACK #-} !(Array a)
183+ {-# UNPACK #-} !Int
184+
185+-- | Like 'writeN' but does not check the array bounds when writing. The fold
186+-- driver must not call the step function more than 'n' times otherwise it will
187+-- corrupt the memory and crash. This function exists mainly because any
188+-- conditional in the step function blocks fusion causing 10x performance
189+-- slowdown.
190+--
191+-- /Internal/
192+{-# INLINE_NORMAL writeNUnsafe #-}
193+writeNUnsafe :: (MonadIO m, Prim a) => Int -> Fold m a (Array a)
194+writeNUnsafe n = Fold step initial extract
195+
196+ where
197+
198+ initial = do
199+ arr <- newArray (max n 0)
200+ return $ ArrayUnsafe arr 0
201+ step (ArrayUnsafe marr i) x = do
202+ unsafeWriteIndex marr i x
203+ return $ ArrayUnsafe marr (i + 1)
204+ extract (ArrayUnsafe marr i) = shrinkArray marr i >> return marr
205+
206+{-# INLINE_NORMAL fromStreamDN #-}
207+fromStreamDN :: (MonadIO m, Prim a) => Int -> D.Stream m a -> m (Array a)
208+fromStreamDN limit str = do
209+ marr <- newArray (max limit 0)
210+ let step i x = i `seq` (unsafeWriteIndex marr i x) >> return (i + 1)
211+ n <- D.foldlM' step (return 0) $ D.take limit str
212+ shrinkArray marr n
213+ return marr
214+
215+{-# INLINE runFold #-}
216+runFold :: (Monad m) => Fold m a b -> D.Stream m a -> m b
217+runFold (Fold step begin done) = D.foldlMx' step begin done
218+
219+{-# INLINE fromStreamD #-}
220+fromStreamD :: (MonadIO m, Prim a) => D.Stream m a -> m (Array a)
221+fromStreamD str = runFold write str
222+
223+{-# INLINABLE fromListNM #-}
224+fromListNM :: (MonadIO m, Prim a) => Int -> [a] -> m (Array a)
225+fromListNM n xs = fromStreamDN n $ D.fromList xs
226+
227+{-# INLINABLE fromListM #-}
228+fromListM :: (MonadIO m, Prim a) => [a] -> m (Array a)
229+fromListM xs = fromStreamD $ D.fromList xs
230+
231+-------------------------------------------------------------------------------
232+-- Combining
233+-------------------------------------------------------------------------------
234+
235+-- Splice two mutable arrays creating a new array.
236+{-# INLINE spliceTwo #-}
237+spliceTwo :: (MonadIO m, Prim a) => Array a -> Array a -> m (Array a)
238+spliceTwo a1 a2 = do
239+ l1 <- length a1
240+ l2 <- length a2
241+ a3 <- resizeArray a1 (l1 + l2)
242+ unsafeCopy a2 0 a3 l1 l2
243+ return a3
244+
245+-------------------------------------------------------------------------------
246+-- Stream of Arrays
247+-------------------------------------------------------------------------------
248+
249+data GroupState s a
250+ = GroupStart s
251+ | GroupBuffer s (Array a) Int
252+ | GroupYield (Array a) s
253+ | GroupLastYield (Array a) Int
254+ | GroupFinish
255+
256+-- | @fromStreamArraysOf n stream@ groups the input stream into a stream of
257+-- arrays of size n.
258+{-# INLINE_NORMAL fromStreamDArraysOf #-}
259+fromStreamDArraysOf ::
260+ (MonadIO m, Prim a) => Int -> D.Stream m a -> D.Stream m (Array a)
261+-- fromStreamDArraysOf n str = D.groupsOf n (writeN n) str
262+fromStreamDArraysOf n (D.Stream step state) = D.Stream step' (GroupStart state)
263+
264+ where
265+
266+ {-# INLINE_LATE step' #-}
267+ step' _ (GroupStart st) = do
268+ when (n <= 0) $
269+ -- XXX we can pass the module string from the higher level API
270+ error $
271+ "Streamly.Internal.Memory.Mutable.Array.Types.fromStreamDArraysOf: the size of " ++
272+ "arrays [" ++ show n ++ "] must be a natural number"
273+ arr <- newArray n
274+ return $ D.Skip (GroupBuffer st arr 0)
275+ step' gst (GroupBuffer st arr i)
276+ | i < n = do
277+ r <- step (adaptState gst) st
278+ case r of
279+ D.Yield x s -> do
280+ unsafeWriteIndex arr i x
281+ return $ D.Skip (GroupBuffer s arr (i + 1))
282+ D.Skip s -> return $ D.Skip (GroupBuffer s arr i)
283+ D.Stop -> return $ D.Skip (GroupLastYield arr i)
284+ | otherwise = return $ D.Skip (GroupYield arr st)
285+ step' _ (GroupYield arr st) = do
286+ nArr <- newArray n
287+ return $ D.Yield arr (GroupBuffer st nArr 0)
288+ step' _ (GroupLastYield arr i)
289+ | i == 0 = return D.Stop
290+ | otherwise = do
291+ shrinkArray arr i
292+ return $ D.Yield arr GroupFinish
293+ step' _ GroupFinish = return D.Stop
294+
295+data SpliceState s arr
296+ = SpliceInitial s
297+ | SpliceBuffering s arr
298+ | SpliceYielding arr (SpliceState s arr)
299+ | SpliceFinish
300+
301+-- XXX can use general grouping combinators to achieve this?
302+-- | Coalesce adjacent arrays in incoming stream to form bigger arrays of a
303+-- maximum specified size in bytes. Note that if a single array is bigger than
304+-- the specified size we do not split it to fit. When we coalesce multiple
305+-- arrays if the size would exceed the specified size we do not coalesce
306+-- therefore the actual array size may be less than the specified chunk size.
307+--
308+-- /Internal/
309+{-# INLINE_NORMAL packArraysChunksOf #-}
310+packArraysChunksOf ::
311+ (MonadIO m, Prim a)
312+ => Int
313+ -> D.Stream m (Array a)
314+ -> D.Stream m (Array a)
315+packArraysChunksOf n (D.Stream step state) =
316+ D.Stream step' (SpliceInitial state)
317+
318+ where
319+
320+ {-# INLINE_LATE step' #-}
321+ step' gst (SpliceInitial st) = do
322+ when (n <= 0) $
323+ -- XXX we can pass the module string from the higher level API
324+ error $ "Streamly.Internal.Memory.Mutable.Array.Types.packArraysChunksOf: the size of "
325+ ++ "arrays [" ++ show n ++ "] must be a natural number"
326+ r <- step gst st
327+ case r of
328+ D.Yield arr s -> do
329+ len <- byteLength arr
330+ return $
331+ if len >= n
332+ then D.Skip $ SpliceYielding arr (SpliceInitial s)
333+ else D.Skip $ SpliceBuffering s arr
334+ D.Skip s -> return $ D.Skip (SpliceInitial s)
335+ D.Stop -> return $ D.Stop
336+
337+ step' gst (SpliceBuffering st buf) = do
338+ r <- step gst st
339+ case r of
340+ D.Yield arr s -> do
341+ blen <- byteLength buf
342+ alen <- byteLength arr
343+ let len = blen + alen
344+ if len > n
345+ then return $
346+ D.Skip (SpliceYielding buf (SpliceBuffering s arr))
347+ else do
348+ buf' <- spliceTwo buf arr
349+ return $ D.Skip (SpliceBuffering s buf')
350+ D.Skip s -> return $ D.Skip (SpliceBuffering s buf)
351+ D.Stop -> return $ D.Skip (SpliceYielding buf SpliceFinish)
352+
353+ step' _ SpliceFinish = return D.Stop
354+
355+ step' _ (SpliceYielding arr next) = return $ D.Yield arr next
356+
357+{-# INLINE_NORMAL lpackArraysChunksOf #-}
358+lpackArraysChunksOf ::
359+ (MonadIO m, Prim a) => Int -> Fold m (Array a) () -> Fold m (Array a) ()
360+lpackArraysChunksOf n (Fold step1 initial1 extract1) =
361+ Fold step initial extract
362+
363+ where
364+
365+ initial = do
366+ when (n <= 0) $
367+ -- XXX we can pass the module string from the higher level API
368+ error $ "Streamly.Internal.Memory.Mutable.Array.Types.packArraysChunksOf: the size of "
369+ ++ "arrays [" ++ show n ++ "] must be a natural number"
370+ r1 <- initial1
371+ return (Tuple' Nothing r1)
372+
373+ extract (Tuple' Nothing r1) = extract1 r1
374+ extract (Tuple' (Just buf) r1) = do
375+ r <- step1 r1 buf
376+ extract1 r
377+
378+ step (Tuple' Nothing r1) arr = do
379+ len <- byteLength arr
380+ if len >= n
381+ then do
382+ r <- step1 r1 arr
383+ extract1 r
384+ r1' <- initial1
385+ return (Tuple' Nothing r1')
386+ else return (Tuple' (Just arr) r1)
387+
388+ step (Tuple' (Just buf) r1) arr = do
389+ blen <- byteLength buf
390+ alen <- byteLength arr
391+ let len = blen + alen
392+ buf' <- spliceTwo buf arr
393+
394+ if len >= n
395+ then do
396+ r <- step1 r1 buf'
397+ extract1 r
398+ r1' <- initial1
399+ return (Tuple' Nothing r1')
400+ else return (Tuple' (Just buf') r1)
src/Streamly/Internal/Data/Array/Prim/Pinned.hsnew file+108
@@ -0,0 +1,108 @@
1+#include "inline.hs"
2+
3+-- |
4+-- Module : Streamly.Internal.Data.Array.Prim.Pinned
5+-- Copyright : (c) 2020 Composewell Technologies
6+--
7+-- License : BSD-3-Clause
8+-- Maintainer : streamly@composewell.com
9+-- Stability : experimental
10+-- Portability : GHC
11+--
12+module Streamly.Internal.Data.Array.Prim.Pinned
13+ (
14+ Array
15+
16+ -- * Construction
17+
18+ -- Pure List APIs
19+ , A.fromListN
20+ , A.fromList
21+
22+ -- Stream Folds
23+ , fromStreamN
24+ , fromStream
25+
26+ -- Monadic APIs
27+ -- , newArray
28+ , A.writeN -- drop new
29+ , A.write -- full buffer
30+
31+ -- * Elimination
32+
33+ , A.toList
34+ , toStream
35+ , toStreamRev
36+ , read
37+ , unsafeRead
38+
39+ -- * Random Access
40+ , length
41+ , null
42+ , last
43+ -- , (!!)
44+ , readIndex
45+ , A.unsafeIndex
46+ -- , readIndices
47+ -- , readRanges
48+
49+ -- , readFrom -- read from a given position to the end of file
50+ -- , readFromRev -- read from a given position to the beginning of file
51+ -- , readTo -- read from beginning up to the given position
52+ -- , readToRev -- read from end to the given position in file
53+ -- , readFromTo
54+ -- , readFromThenTo
55+
56+ -- , readChunksOfFrom
57+ -- , ...
58+
59+ -- , writeIndex
60+ -- , writeFrom -- start writing at the given position
61+ -- , writeFromRev
62+ -- , writeTo -- write from beginning up to the given position
63+ -- , writeToRev
64+ -- , writeFromTo
65+ -- , writeFromThenTo
66+ --
67+ -- , writeChunksOfFrom
68+ -- , ...
69+
70+ -- , writeIndex
71+ -- , writeIndices
72+ -- , writeRanges
73+
74+ -- -- * Search
75+ -- , bsearch
76+ -- , bsearchIndex
77+ -- , find
78+ -- , findIndex
79+ -- , findIndices
80+
81+ -- -- * In-pace mutation (for Mutable Array type)
82+ -- , partitionBy
83+ -- , shuffleBy
84+ -- , foldtWith
85+ -- , foldbWith
86+
87+ -- * Immutable Transformations
88+-- , streamTransform
89+
90+ -- * Folding Arrays
91+ , streamFold
92+ , fold
93+
94+ -- * Folds with Array as the container
95+-- , D.lastN
96+
97+ -- * Streaming array operations
98+
99+ , concat
100+ , compact
101+
102+ )
103+where
104+
105+import Streamly.Internal.Data.Array.Prim.Pinned.Types (Array(..), length)
106+import qualified Streamly.Internal.Data.Array.Prim.Pinned.Types as A
107+
108+#include "Streamly/Internal/Data/Array/PrimInclude.hs"
src/Streamly/Internal/Data/Array/Prim/Pinned/Mut/Types.hsnew file+176
@@ -0,0 +1,176 @@
1+{-# LANGUAGE UnboxedTuples #-}
2+
3+#include "inline.hs"
4+
5+-- |
6+-- Module : Streamly.Internal.Data.Array.Prim.Pinned.Mut.Types
7+-- Copyright : (c) 2020 Composewell Technologies
8+--
9+-- License : BSD-3-Clause
10+-- Maintainer : streamly@composewell.com
11+-- Stability : experimental
12+-- Portability : GHC
13+--
14+module Streamly.Internal.Data.Array.Prim.Pinned.Mut.Types
15+ (
16+ Array (..)
17+
18+ -- * Construction
19+ , newArray
20+ , newAlignedArray
21+ , unsafeWriteIndex
22+
23+ , spliceTwo
24+ , unsafeCopy
25+
26+ , fromListM
27+ , fromListNM
28+ , fromStreamDN
29+ , fromStreamD
30+
31+ -- * Streams of arrays
32+ , fromStreamDArraysOf
33+
34+ , packArraysChunksOf
35+ , lpackArraysChunksOf
36+
37+#if !defined(mingw32_HOST_OS)
38+-- , groupIOVecsOf
39+#endif
40+
41+ -- * Elimination
42+ , unsafeReadIndex
43+ , length
44+ , byteLength
45+
46+ , writeN
47+ , ArrayUnsafe(..)
48+ , writeNUnsafe
49+ , writeNAligned
50+ , write
51+
52+ -- * Utilities
53+ , resizeArray
54+ , shrinkArray
55+
56+ , touchArray
57+ , withArrayAsPtr
58+ )
59+where
60+
61+import GHC.IO (IO(..))
62+
63+#include "Streamly/Internal/Data/Array/Prim/MutTypesInclude.hs"
64+
65+-------------------------------------------------------------------------------
66+-- Allocation (Pinned)
67+-------------------------------------------------------------------------------
68+
69+-- XXX we can use a single newArray routine which accepts an allocation
70+-- function which could be newByteArray#, newPinnedByteArray# or
71+-- newAlignedPinnedByteArray#. That function can go in the common include file.
72+--
73+-- | Allocate an array that is pinned and can hold 'count' items. The memory of
74+-- the array is uninitialized.
75+--
76+-- Note that this is internal routine, the reference to this array cannot be
77+-- given out until the array has been written to and frozen.
78+{-# INLINE newArray #-}
79+newArray ::
80+ forall m a. (MonadIO m, Prim a)
81+ => Int
82+ -> m (Array a)
83+newArray (I# n#) =
84+ liftIO $ do
85+ let bytes = n# *# sizeOf# (undefined :: a)
86+ primitive $ \s# ->
87+ case newPinnedByteArray# bytes s# of
88+ (# s1#, arr# #) -> (# s1#, Array arr# #)
89+
90+-- Change order of args?
91+-- | Allocate a new array aligned to the specified alignment and using pinned
92+-- memory.
93+{-# INLINE newAlignedArray #-}
94+newAlignedArray ::
95+ forall m a. (MonadIO m, Prim a)
96+ => Int -- size
97+ -> Int -- Alignment
98+ -> m (Array a)
99+newAlignedArray (I# n#) (I# a#) =
100+ liftIO $ do
101+ let bytes = n# *# sizeOf# (undefined :: a)
102+ primitive $ \s# ->
103+ case newAlignedPinnedByteArray# bytes a# s# of
104+ (# s1#, arr# #) -> (# s1#, Array arr# #)
105+
106+-- | Resize (pinned) mutable byte array to new specified size (in elem
107+-- count). The returned array is either the original array resized in-place or,
108+-- if not possible, a newly allocated (pinned) array (with the original content
109+-- copied over).
110+{-# INLINE resizeArray #-}
111+resizeArray ::
112+ (MonadIO m, Prim a)
113+ => Array a
114+ -> Int -- ^ new size
115+ -> m (Array a)
116+resizeArray arr i = do
117+ len <- length arr
118+ if len == i
119+ then return arr
120+ else if i < len
121+ then shrinkArray arr i >> return arr
122+ else do
123+ nArr <- newArray i
124+ unsafeCopy nArr 0 arr 0 len
125+ return nArr
126+
127+-------------------------------------------------------------------------------
128+-- Aligned Construction
129+-------------------------------------------------------------------------------
130+
131+-- XXX we can also factor out common code in writeN and writeNAligned in the
132+-- same way as suggested above.
133+--
134+{-# INLINE_NORMAL writeNAligned #-}
135+writeNAligned ::
136+ (MonadIO m, Prim a)
137+ => Int
138+ -> Int
139+ -> Fold m a (Array a)
140+writeNAligned align limit = Fold step initial extract
141+
142+ where
143+
144+ initial = do
145+ marr <- newAlignedArray limit align
146+ return (marr, 0)
147+
148+ step (marr, i) x
149+ | i == limit = return (marr, i)
150+ | otherwise = do
151+ unsafeWriteIndex marr i x
152+ return (marr, i + 1)
153+
154+ extract (marr, len) = shrinkArray marr len >> return marr
155+
156+-------------------------------------------------------------------------------
157+-- Mutation with pointers
158+-------------------------------------------------------------------------------
159+
160+-- XXX This section can probably go in a common include file for pinned arrays.
161+
162+-- Change name later.
163+{-# INLINE toPtr #-}
164+toPtr :: Array a -> Ptr a
165+toPtr (Array arr#) = Ptr (byteArrayContents# (unsafeCoerce# arr#))
166+
167+{-# INLINE touchArray #-}
168+touchArray :: Array a -> IO ()
169+touchArray arr = IO $ \s -> case touch# arr s of s' -> (# s', () #)
170+
171+{-# INLINE withArrayAsPtr #-}
172+withArrayAsPtr :: Array a -> (Ptr a -> IO b) -> IO b
173+withArrayAsPtr arr f = do
174+ r <- f (toPtr arr)
175+ touchArray arr
176+ return r
src/Streamly/Internal/Data/Array/Prim/Pinned/Types.hsnew file+127
@@ -0,0 +1,127 @@
1+{-# LANGUAGE UnboxedTuples #-}
2+
3+#include "inline.hs"
4+
5+-- |
6+-- Module : Streamly.Internal.Data.Array.Prim.Pinned.Types
7+-- Copyright : (c) 2020 Composewell Technologies
8+--
9+-- License : BSD-3-Clause
10+-- Maintainer : streamly@composewell.com
11+-- Stability : experimental
12+-- Portability : GHC
13+--
14+module Streamly.Internal.Data.Array.Prim.Pinned.Types
15+ (
16+ Array (..)
17+ , unsafeFreeze
18+ , unsafeFreezeWithShrink
19+-- , unsafeThaw
20+ , defaultChunkSize
21+ , nil
22+
23+ -- * Construction
24+ , spliceTwo
25+
26+ , fromList
27+ , fromListN
28+ , fromStreamDN
29+ , fromStreamD
30+
31+ -- * Streams of arrays
32+ , fromStreamDArraysOf
33+ , FlattenState (..) -- for inspection testing
34+ , flattenArrays
35+ , flattenArraysRev
36+ , SpliceState (..) -- for inspection testing
37+ , packArraysChunksOf
38+ , lpackArraysChunksOf
39+#if !defined(mingw32_HOST_OS)
40+-- , groupIOVecsOf
41+#endif
42+ , splitOn
43+ , breakOn
44+
45+ -- * Elimination
46+ , unsafeIndex
47+ , byteLength
48+ , length
49+
50+ , foldl'
51+ , foldr
52+ , foldr'
53+ , foldlM'
54+ , splitAt
55+
56+ , toStreamD
57+ , toStreamDRev
58+ , toStreamK
59+ , toStreamKRev
60+ , toList
61+-- , toArrayMinChunk
62+ , writeN
63+ , MA.ArrayUnsafe(..)
64+ , writeNUnsafe
65+ , write
66+
67+ , unlines
68+
69+ , toPtr
70+
71+ , touchArray
72+ , withArrayAsPtr
73+ )
74+where
75+
76+import Foreign.C.Types (CSize(..))
77+import GHC.IO (IO(..))
78+import Foreign.Ptr (minusPtr, nullPtr, plusPtr)
79+
80+import qualified Streamly.Internal.Data.Array.Prim.Pinned.Mut.Types as MA
81+
82+#include "Streamly/Internal/Data/Array/Prim/TypesInclude.hs"
83+
84+-------------------------------------------------------------------------------
85+-- Utility functions
86+-------------------------------------------------------------------------------
87+
88+foreign import ccall unsafe "string.h memchr" c_memchr
89+ :: Ptr Word8 -> Word8 -> CSize -> IO (Ptr Word8)
90+
91+-------------------------------------------------------------------------------
92+-- Using as a Pointer
93+-------------------------------------------------------------------------------
94+
95+-- Change name later.
96+{-# INLINE toPtr #-}
97+toPtr :: Array a -> Ptr a
98+toPtr (Array arr# off _) = Ptr (byteArrayContents# arr#) `plusPtr` off
99+
100+{-# INLINE touchArray #-}
101+touchArray :: Array a -> IO ()
102+touchArray (Array arr# _ _) = IO $ \s -> case touch# arr# s of s1 -> (# s1, () #)
103+
104+{-# INLINE withArrayAsPtr #-}
105+withArrayAsPtr :: Array a -> (Ptr a -> IO b) -> IO b
106+withArrayAsPtr arr f = do
107+ r <- f (toPtr arr)
108+ touchArray arr
109+ return r
110+
111+-- Drops the separator byte
112+{-# INLINE breakOn #-}
113+breakOn ::
114+ MonadIO m
115+ => Word8
116+ -> Array Word8
117+ -> m (Array Word8, Maybe (Array Word8))
118+breakOn sep arr@(Array arr# off len) = do
119+ let p = toPtr arr
120+ loc = unsafePerformIO $ c_memchr p sep (fromIntegral (byteLength arr))
121+ len1 = loc `minusPtr` p
122+ len2 = len - len1 - 1
123+ return $
124+ if loc == nullPtr
125+ then (arr, Nothing)
126+ else ( Array arr# off len1
127+ , Just $ Array arr# (off + len1 + 1) len2)
src/Streamly/Internal/Data/Array/Prim/Types.hsnew file+98
@@ -0,0 +1,98 @@
1+{-# LANGUAGE UnboxedTuples #-}
2+
3+#include "inline.hs"
4+
5+-- |
6+-- Module : Streamly.Internal.Data.Array.Prim.Types
7+-- Copyright : (c) 2020 Composewell Technologies
8+--
9+-- License : BSD-3-Clause
10+-- Maintainer : streamly@composewell.com
11+-- Stability : experimental
12+-- Portability : GHC
13+--
14+module Streamly.Internal.Data.Array.Prim.Types
15+ (
16+ Array (..)
17+ , unsafeFreeze
18+ , unsafeFreezeWithShrink
19+-- , unsafeThaw
20+ , defaultChunkSize
21+ , nil
22+
23+ -- * Construction
24+ , spliceTwo
25+
26+ , fromList
27+ , fromListN
28+ , fromStreamDN
29+ , fromStreamD
30+
31+ -- * Streams of arrays
32+ , fromStreamDArraysOf
33+ , FlattenState (..) -- for inspection testing
34+ , flattenArrays
35+ , flattenArraysRev
36+ , SpliceState (..) -- for inspection testing
37+ , packArraysChunksOf
38+ , lpackArraysChunksOf
39+#if !defined(mingw32_HOST_OS)
40+-- , groupIOVecsOf
41+#endif
42+ , splitOn
43+ , breakOn
44+
45+ -- * Elimination
46+ , unsafeIndex
47+ , byteLength
48+ , length
49+
50+ , foldl'
51+ , foldr
52+ , foldr'
53+ , foldlM'
54+ , splitAt
55+
56+ , toStreamD
57+ , toStreamDRev
58+ , toStreamK
59+ , toStreamKRev
60+ , toList
61+-- , toArrayMinChunk
62+ , writeN
63+ , MA.ArrayUnsafe(..)
64+ , writeNUnsafe
65+ , write
66+
67+ , unlines
68+ )
69+where
70+
71+import qualified Streamly.Internal.Data.Array.Prim.Mut.Types as MA
72+
73+#include "Streamly/Internal/Data/Array/Prim/TypesInclude.hs"
74+
75+-- Drops the separator byte
76+-- Inefficient compared to Memory Array
77+{-# INLINE breakOn #-}
78+breakOn ::
79+ MonadIO m
80+ => Word8
81+ -> Array Word8
82+ -> m (Array Word8, Maybe (Array Word8))
83+breakOn sep arr@(Array arr# off len) =
84+ case loc of
85+ Left _ -> return (arr, Nothing)
86+ Right len1 -> do
87+ let len2 = len - len1 - 1
88+ return (Array arr# off len1, Just $ Array arr# (off + len1 + 1) len2)
89+
90+ where
91+
92+ loc = foldl' chk (Left 0) arr
93+
94+ chk (Left i) a =
95+ if a == sep
96+ then Right i
97+ else Left (i + 1)
98+ chk r _ = r
src/Streamly/Internal/Data/Array/Prim/TypesInclude.hsnew file+694
@@ -0,0 +1,694 @@
1+-- Copyright : (c) 2020 Composewell Technologies
2+-- License : BSD-3-Clause
3+-- Maintainer : streamly@composewell.com
4+-- Stability : experimental
5+-- Portability : GHC
6+
7+import Control.DeepSeq (NFData(..))
8+import Control.Monad (when)
9+import Control.Monad.IO.Class (MonadIO(..))
10+import Control.Monad.Primitive (PrimMonad(..), primitive_)
11+import Data.Primitive.Types (Prim(..), sizeOf)
12+#if __GLASGOW_HASKELL__ < 808
13+import Data.Semigroup (Semigroup(..))
14+#endif
15+import Data.Word (Word8)
16+import Streamly.Internal.Data.Strict (Tuple3'(..), Maybe'(..))
17+import Streamly.Internal.Data.Fold.Types (Fold(..))
18+import Streamly.Internal.Data.SVar (adaptState)
19+import System.IO.Unsafe (unsafePerformIO)
20+import Text.Read (readPrec, readListPrec, readListPrecDefault)
21+
22+import qualified GHC.Exts as Exts
23+import qualified Prelude as P
24+import qualified Streamly.Internal.Data.Fold as FL
25+import qualified Streamly.Internal.Data.Stream.StreamD.Type as D
26+import qualified Streamly.Internal.Data.Stream.StreamK as K
27+
28+import GHC.Exts hiding (fromListN, fromList, toList)
29+import Prelude hiding (length, unlines, foldr)
30+
31+-------------------------------------------------------------------------------
32+-- Array Data Type
33+-------------------------------------------------------------------------------
34+
35+data Array a = Array ByteArray# Int Int
36+
37+-------------------------------------------------------------------------------
38+-- Utilities
39+-------------------------------------------------------------------------------
40+
41+-- | Both arrays must fully contain the specified ranges, but this is not
42+-- checked. The two arrays must not be the same array in different states, but
43+-- this is not checked either.
44+{-# INLINE unsafeCopy #-}
45+unsafeCopy ::
46+ forall m a. (MonadIO m, Prim a)
47+ => MA.Array a -- ^ destination array
48+ -> Int -- ^ offset into destination array
49+ -> Array a -- ^ source array
50+ -> Int -- ^ offset into source array
51+ -> Int -- ^ number of elements to copy
52+ -> m ()
53+unsafeCopy (MA.Array dst#) (I# doff#) (Array src# (I# off#) _) (I# soff#) (I# n#) =
54+ liftIO $ do
55+ let toBytes cnt# = cnt# *# (sizeOf# (undefined :: a))
56+ primitive_ $
57+ copyByteArray#
58+ src#
59+ (toBytes (off# +# soff#))
60+ dst#
61+ (toBytes doff#)
62+ (toBytes n#)
63+
64+-------------------------------------------------------------------------------
65+-- Basic Byte Array Operations
66+-------------------------------------------------------------------------------
67+
68+{-# INLINE unsafeFreeze #-}
69+unsafeFreeze :: (Prim a, MonadIO m) => MA.Array a -> m (Array a)
70+unsafeFreeze marr@(MA.Array arr#) =
71+ liftIO $ do
72+ len <- MA.length marr
73+ primitive $ \s# ->
74+ case unsafeFreezeByteArray# arr# s# of
75+ (# s1#, arr1# #) -> (# s1#, Array arr1# 0 len #)
76+
77+{-# INLINE unsafeFreezeWithShrink #-}
78+unsafeFreezeWithShrink ::
79+ (Prim a, MonadIO m) => MA.Array a -> Int -> m (Array a)
80+unsafeFreezeWithShrink marr@(MA.Array arr#) n =
81+ liftIO $ do
82+ MA.shrinkArray marr n
83+ len <- MA.length marr
84+ primitive $ \s# ->
85+ case unsafeFreezeByteArray# arr# s# of
86+ (# s1#, arr1# #) -> (# s1#, Array arr1# 0 len #)
87+
88+{-
89+-- Should never be used in general
90+{-# INLINE unsafeThaw #-}
91+unsafeThaw :: MonadIO m => Array a -> m (MA.Array a)
92+unsafeThaw (Array arr#) =
93+ primitive $ \s# -> (# s#, MA.Array (unsafeCoerce# arr#) #)
94+-}
95+
96+-- Unsafe because the index bounds are not checked
97+{-# INLINE unsafeIndex #-}
98+unsafeIndex :: Prim a => Array a -> Int -> a
99+unsafeIndex (Array arr# (I# off#) _) (I# i#) = indexByteArray# arr# (off# +# i#)
100+
101+-- unsafe
102+sameByteArray :: ByteArray# -> ByteArray# -> Bool
103+sameByteArray ba1 ba2 =
104+ case reallyUnsafePtrEquality#
105+ (unsafeCoerce# ba1 :: ())
106+ (unsafeCoerce# ba2 :: ()) of
107+ r -> isTrue# r
108+
109+-------------------------------------------------------------------------------
110+-- Chunk Size
111+-------------------------------------------------------------------------------
112+
113+-- XXX move this section to mutable array module?
114+
115+mkChunkSizeKB :: Int -> Int
116+mkChunkSizeKB n = n * k
117+ where k = 1024
118+
119+-- | Default maximum buffer size in bytes, for reading from and writing to IO
120+-- devices, the value is 32KB minus GHC allocation overhead, which is a few
121+-- bytes, so that the actual allocation is 32KB.
122+defaultChunkSize :: Int
123+defaultChunkSize = mkChunkSizeKB 32
124+
125+-------------------------------------------------------------------------------
126+-- Length
127+-------------------------------------------------------------------------------
128+
129+-- XXX rename to byteCount?
130+{-# INLINE byteLength #-}
131+byteLength :: forall a. Prim a => Array a -> Int
132+byteLength (Array _ _ len) = len * sizeOf (undefined :: a)
133+
134+-- XXX Also, rename to elemCount
135+-- XXX I would prefer length to keep the API consistent
136+-- XXX Also, re-export sizeOf from Primitive
137+{-# INLINE length #-}
138+length :: Array a -> Int
139+length (Array _ _ len) = len
140+
141+-------------------------------------------------------------------------------
142+-- Construction
143+-------------------------------------------------------------------------------
144+
145+-- | Use a slice of an array as another array. Note that this is unsafe and does
146+-- not check the bounds
147+slice :: Array a -> Int -> Int -> Array a
148+slice (Array arr# off _) off1 len1 = Array arr# (off + off1) len1
149+
150+nil :: Prim a => Array a
151+nil =
152+ unsafePerformIO $ do
153+ arr <- MA.newArray 0
154+ unsafeFreeze arr
155+
156+-- | Fold the whole input to a single array.
157+--
158+-- /Caution! Do not use this on infinite streams./
159+--
160+-- /Internal/
161+{-# INLINE_NORMAL write #-}
162+write :: (MonadIO m, Prim a) => Fold m a (Array a)
163+write = FL.mapM unsafeFreeze MA.write
164+
165+-- | @writeN n@ folds a maximum of @n@ elements from the input stream to an
166+-- 'Array'.
167+--
168+-- /Internal/
169+{-# INLINE_NORMAL writeN #-}
170+writeN :: (MonadIO m, Prim a) => Int -> Fold m a (Array a)
171+writeN limit = FL.mapM unsafeFreeze (MA.writeN limit)
172+
173+-- | Like 'writeN' but does not check the array bounds when writing. The fold
174+-- driver must not call the step function more than 'n' times otherwise it will
175+-- corrupt the memory and crash. This function exists mainly because any
176+-- conditional in the step function blocks fusion causing 10x performance
177+-- slowdown.
178+--
179+-- /Internal/
180+{-# INLINE_NORMAL writeNUnsafe #-}
181+writeNUnsafe :: (MonadIO m, Prim a) => Int -> Fold m a (Array a)
182+writeNUnsafe limit = FL.mapM unsafeFreeze (MA.writeNUnsafe limit)
183+
184+{-# INLINE_NORMAL fromStreamDN #-}
185+fromStreamDN :: (MonadIO m, Prim a) => Int -> D.Stream m a -> m (Array a)
186+fromStreamDN limit str = MA.fromStreamDN limit str >>= unsafeFreeze
187+
188+{-# INLINE fromStreamD #-}
189+fromStreamD :: (MonadIO m, Prim a) => D.Stream m a -> m (Array a)
190+fromStreamD str = MA.fromStreamD str >>= unsafeFreeze
191+
192+-- | @fromStreamArraysOf n stream@ groups the input stream into a stream of
193+-- arrays of size n.
194+{-# INLINE_NORMAL fromStreamDArraysOf #-}
195+fromStreamDArraysOf ::
196+ (MonadIO m, Prim a) => Int -> D.Stream m a -> D.Stream m (Array a)
197+fromStreamDArraysOf n str = D.mapM unsafeFreeze (MA.fromStreamDArraysOf n str)
198+
199+-- XXX derive from MA.fromListN?
200+{-# INLINE fromListN #-}
201+fromListN :: Prim a => Int -> [a] -> Array a
202+fromListN len xs = unsafePerformIO $ MA.fromListNM len xs >>= unsafeFreeze
203+
204+-- XXX derive from MA.fromList?
205+{-# INLINE fromList #-}
206+fromList :: Prim a => [a] -> Array a
207+fromList xs = fromListN (P.length xs) xs
208+
209+-------------------------------------------------------------------------------
210+-- Combining
211+-------------------------------------------------------------------------------
212+
213+-- | Splice two immutable arrays creating a new immutable array.
214+{-# INLINE spliceTwo #-}
215+spliceTwo :: (MonadIO m, Prim a) => Array a -> Array a -> m (Array a)
216+spliceTwo a1 a2 = do
217+ let l1 = length a1
218+ l2 = length a2
219+ a3 <- MA.newArray (l1 + l2)
220+ unsafeCopy a3 0 a1 0 l1
221+ unsafeCopy a3 l1 a2 0 l2
222+ unsafeFreeze a3 -- Use `unsafeFreezeWith off len`?
223+
224+-------------------------------------------------------------------------------
225+-- Elimination
226+-------------------------------------------------------------------------------
227+
228+{-# INLINE_LATE toListFB #-}
229+toListFB :: Prim a => (a -> b -> b) -> b -> Array a -> b
230+toListFB c n arr = go 0
231+ where
232+ len = length arr
233+ go p | p == len = n
234+ go p =
235+ let !x = unsafeIndex arr p
236+ in c x (go (p + 1))
237+
238+-- | Convert an 'Array' into a list.
239+--
240+-- /Internal/
241+{-# INLINE toList #-}
242+toList :: Prim a => Array a -> [a]
243+toList s = build (\c n -> toListFB c n s)
244+
245+-------------------------------------------------------------------------------
246+-- Instances
247+-------------------------------------------------------------------------------
248+
249+instance (Eq a, Prim a) => Eq (Array a) where
250+ {-# INLINE (==) #-}
251+ a1@(Array ba1# _ len1) == a2@(Array ba2# _ len2)
252+ | sameByteArray ba1# ba2# = True
253+ | len1 /= len2 = False
254+ | otherwise = loop (len1 - 1)
255+
256+ where
257+
258+ loop !i
259+ | i < 0 = True
260+ | otherwise = unsafeIndex a1 i == unsafeIndex a2 i && loop (i - 1)
261+
262+-- | Lexicographic ordering. Subject to change between major versions.
263+instance (Ord a, Prim a) => Ord (Array a) where
264+ {-# INLINE compare #-}
265+ compare a1@(Array ba1# _ len1) a2@(Array ba2# _ len2)
266+ | sameByteArray ba1# ba2# = EQ
267+ | otherwise = loop 0
268+
269+ where
270+
271+ sz = min len1 len2
272+
273+ loop !i
274+ | i < sz =
275+ compare (unsafeIndex a1 i) (unsafeIndex a2 i) <> loop (i + 1)
276+ | otherwise = compare len1 len2
277+
278+instance Prim a => Semigroup (Array a) where
279+ -- XXX can't we use runST instead of inlineIO?
280+ -- XXX I plan to remove MonadIO and replace it with IO
281+ a <> b = unsafePerformIO (spliceTwo a b :: IO (Array a))
282+
283+instance Prim a => Monoid (Array a) where
284+ mempty = nil
285+ mappend = (<>)
286+
287+instance NFData (Array a) where
288+ {-# INLINE rnf #-}
289+ rnf _ = ()
290+
291+
292+-- XXX check if this is compatible with Memory.Array?
293+-- XXX It isn't. I might prefer this Show instance though
294+-- XXX Memory.Array: showsPrec _ = shows . toList
295+instance (Show a, Prim a) => Show (Array a) where
296+ showsPrec p a =
297+ showParen (p > 10) $
298+ showString "fromListN "
299+ . shows (length a)
300+ . showString " "
301+ . shows (toList a)
302+
303+instance (a ~ Char) => IsString (Array a) where
304+ {-# INLINE fromString #-}
305+ fromString = fromList
306+
307+-- GHC versions 8.0 and below cannot derive IsList
308+instance Prim a => IsList (Array a) where
309+ type (Item (Array a)) = a
310+
311+ {-# INLINE fromList #-}
312+ fromList = fromList
313+
314+ {-# INLINE fromListN #-}
315+ fromListN = fromListN
316+
317+ {-# INLINE toList #-}
318+ toList = toList
319+
320+instance (Prim a, Read a, Show a) => Read (Array a) where
321+ {-# INLINE readPrec #-}
322+ readPrec = fromList <$> readPrec
323+ readListPrec = readListPrecDefault
324+
325+-- XXX these folds can be made common with mutable arrays by defining a
326+-- unsafeIndex in the specific module?
327+
328+-------------------------------------------------------------------------------
329+-- Folds
330+-------------------------------------------------------------------------------
331+
332+{-# INLINE foldr #-}
333+foldr :: Prim a => (a -> b -> b) -> b -> Array a -> b
334+foldr f z arr = go 0
335+
336+ where
337+
338+ !len = length arr
339+
340+ go !i
341+ | len > i = f (unsafeIndex arr i) (go (i + 1))
342+ | otherwise = z
343+
344+-- | Strict right-associated fold over the elements of an 'Array'.
345+{-# INLINE foldr' #-}
346+foldr' :: Prim a => (a -> b -> b) -> b -> Array a -> b
347+foldr' f z0 arr = go (length arr - 1) z0
348+
349+ where
350+
351+ go !i !acc
352+ | i < 0 = acc
353+ | otherwise = go (i - 1) (f (unsafeIndex arr i) acc)
354+
355+-- | Strict left-associated fold over the elements of an 'Array'.
356+{-# INLINE foldl' #-}
357+foldl' :: Prim a => (b -> a -> b) -> b -> Array a -> b
358+foldl' f z0 arr = go 0 z0
359+
360+ where
361+
362+ !len = length arr
363+
364+ go !i !acc
365+ | i < len = go (i + 1) (f acc (unsafeIndex arr i))
366+ | otherwise = acc
367+
368+-- | Strict left-associated fold over the elements of an 'Array'.
369+{-# INLINE foldlM' #-}
370+foldlM' :: (Prim a, Monad m) => (b -> a -> m b) -> b -> Array a -> m b
371+foldlM' f z0 arr = go 0 z0
372+
373+ where
374+
375+ !len = length arr
376+
377+ go !i !acc1
378+ | i < len = do
379+ acc2 <- f acc1 (unsafeIndex arr i)
380+ go (i + 1) acc2
381+ | otherwise = return acc1
382+
383+-------------------------------------------------------------------------------
384+-- Converting to streams
385+-------------------------------------------------------------------------------
386+
387+{-# INLINE_NORMAL toStreamD #-}
388+toStreamD :: (Prim a, Monad m) => Array a -> D.Stream m a
389+toStreamD arr = D.Stream step 0
390+
391+ where
392+
393+ {-# INLINE_LATE step #-}
394+ step _ i
395+ | i == length arr = return D.Stop
396+ step _ i = return $ D.Yield (unsafeIndex arr i) (i + 1)
397+
398+{-# INLINE toStreamK #-}
399+toStreamK :: (K.IsStream t, Prim a) => Array a -> t m a
400+toStreamK arr = go 0
401+
402+ where
403+
404+ len = length arr
405+
406+ go p
407+ | p == len = K.nil
408+ | otherwise =
409+ let !x = unsafeIndex arr p
410+ in x `K.cons` go (p + 1)
411+
412+{-# INLINE_NORMAL toStreamDRev #-}
413+toStreamDRev :: (Prim a, Monad m) => Array a -> D.Stream m a
414+toStreamDRev arr = D.Stream step (length arr - 1)
415+
416+ where
417+
418+ {-# INLINE_LATE step #-}
419+ step _ i
420+ | i < 0 = return D.Stop
421+ step _ i = return $ D.Yield (unsafeIndex arr i) (i - 1)
422+
423+{-# INLINE toStreamKRev #-}
424+toStreamKRev :: (K.IsStream t, Prim a) => Array a -> t m a
425+toStreamKRev arr = go (length arr - 1)
426+
427+ where
428+
429+ go p | p == -1 = K.nil
430+ | otherwise =
431+ let !x = unsafeIndex arr p
432+ in x `K.cons` go (p - 1)
433+
434+-------------------------------------------------------------------------------
435+-- Stream of Arrays
436+-------------------------------------------------------------------------------
437+
438+data FlattenState s a =
439+ OuterLoop s
440+ | InnerLoop s !(Array a) !Int !Int
441+
442+{-# INLINE_NORMAL flattenArrays #-}
443+flattenArrays :: (MonadIO m, Prim a) => D.Stream m (Array a) -> D.Stream m a
444+flattenArrays (D.Stream step state) = D.Stream step' (OuterLoop state)
445+
446+ where
447+
448+ {-# INLINE_LATE step' #-}
449+ step' gst (OuterLoop st) = do
450+ r <- step (adaptState gst) st
451+ return $ case r of
452+ D.Yield arr s ->
453+ let len = length arr
454+ in if len == 0
455+ then D.Skip (OuterLoop s)
456+ else D.Skip (InnerLoop s arr len 0)
457+ D.Skip s -> D.Skip (OuterLoop s)
458+ D.Stop -> D.Stop
459+
460+ step' _ (InnerLoop st _ len i) | i == len =
461+ return $ D.Skip $ OuterLoop st
462+
463+ step' _ (InnerLoop st arr len i) = do
464+ let x = unsafeIndex arr i
465+ return $ D.Yield x (InnerLoop st arr len (i + 1))
466+
467+{-# INLINE_NORMAL flattenArraysRev #-}
468+flattenArraysRev :: (MonadIO m, Prim a) => D.Stream m (Array a) -> D.Stream m a
469+flattenArraysRev (D.Stream step state) = D.Stream step' (OuterLoop state)
470+
471+ where
472+
473+ {-# INLINE_LATE step' #-}
474+ step' gst (OuterLoop st) = do
475+ r <- step (adaptState gst) st
476+ return $ case r of
477+ D.Yield arr s ->
478+ let len = length arr
479+ in if len == 0
480+ then D.Skip (OuterLoop s)
481+ else D.Skip (InnerLoop s arr len (len - 1))
482+ D.Skip s -> D.Skip (OuterLoop s)
483+ D.Stop -> D.Stop
484+
485+ step' _ (InnerLoop st _ _ i) | i == -1 =
486+ return $ D.Skip $ OuterLoop st
487+
488+ step' _ (InnerLoop st arr len i) = do
489+ let x = unsafeIndex arr i
490+ return $ D.Yield x (InnerLoop st arr len (i - 1))
491+
492+{-# INLINE_NORMAL unlines #-}
493+unlines :: (MonadIO m, Prim a) => a -> D.Stream m (Array a) -> D.Stream m a
494+unlines sep (D.Stream step state) = D.Stream step' (OuterLoop state)
495+
496+ where
497+
498+ {-# INLINE_LATE step' #-}
499+ step' gst (OuterLoop st) = do
500+ r <- step (adaptState gst) st
501+ return $ case r of
502+ D.Yield arr s ->
503+ let len = length arr
504+ in D.Skip (InnerLoop s arr len 0)
505+ D.Skip s -> D.Skip (OuterLoop s)
506+ D.Stop -> D.Stop
507+
508+ step' _ (InnerLoop st _ len i) | i == len =
509+ return $ D.Yield sep $ OuterLoop st
510+
511+ step' _ (InnerLoop st arr len i) = do
512+ let x = unsafeIndex arr i
513+ return $ D.Yield x (InnerLoop st arr len (i + 1))
514+
515+-- Splice an array into a pre-reserved mutable array. The user must ensure
516+-- that there is enough space in the mutable array.
517+{-# INLINE spliceInto #-}
518+spliceInto :: (MonadIO m, Prim a) => MA.Array a -> Int -> Array a -> m Int
519+spliceInto dst doff src@(Array _ _ len) = do
520+ unsafeCopy dst doff src 0 len
521+ return $ doff + len
522+
523+data SpliceState s arr1 arr2
524+ = SpliceInitial s
525+ | SpliceBuffering s arr2
526+ | SpliceYielding arr1 (SpliceState s arr1 arr2)
527+ | SpliceFinish
528+
529+-- | Coalesce adjacent arrays in incoming stream to form bigger arrays of a
530+-- maximum specified size in bytes. Note that if a single array is bigger than
531+-- the specified size we do not split it to fit. When we coalesce multiple
532+-- arrays if the size would exceed the specified size we do not coalesce
533+-- therefore the actual array size may be less than the specified chunk size.
534+--
535+-- /Internal/
536+{-# INLINE_NORMAL packArraysChunksOf #-}
537+packArraysChunksOf ::
538+ forall m a. (MonadIO m, Prim a)
539+ => Int
540+ -> D.Stream m (Array a)
541+ -> D.Stream m (Array a)
542+packArraysChunksOf n (D.Stream step state) =
543+ D.Stream step' (SpliceInitial state)
544+
545+ where
546+
547+ nElem = n `quot` sizeOf (undefined :: a)
548+
549+ {-# INLINE_LATE step' #-}
550+ step' gst (SpliceInitial st) = do
551+ when (n <= 0) $
552+ -- XXX we can pass the module string from the higher level API
553+ error $ "Streamly.Internal.Memory.Array.Types.packArraysChunksOf: the size of "
554+ ++ "arrays [" ++ show n ++ "] must be a natural number"
555+ r <- step gst st
556+ case r of
557+ D.Yield arr s ->
558+ if length arr >= nElem
559+ then return $ D.Skip (SpliceYielding arr (SpliceInitial s))
560+ else do
561+ buf <- MA.newArray nElem
562+ noff <- spliceInto buf 0 arr
563+ return $ D.Skip (SpliceBuffering s (buf, noff))
564+ D.Skip s -> return $ D.Skip (SpliceInitial s)
565+ D.Stop -> return $ D.Stop
566+
567+ step' gst (SpliceBuffering st arr2@(buf, boff)) = do
568+ r <- step gst st
569+ case r of
570+ D.Yield arr s -> do
571+ if boff + length arr > nElem
572+ then do
573+ nArr <- unsafeFreeze buf
574+ return $ D.Skip (SpliceYielding (slice nArr 0 boff) (SpliceBuffering s arr2))
575+ else do
576+ noff <- spliceInto buf boff arr
577+ return $ D.Skip (SpliceBuffering s (buf, noff))
578+ D.Skip s -> return $ D.Skip (SpliceBuffering s arr2)
579+ D.Stop -> do
580+ nArr <- unsafeFreeze buf
581+ return $ D.Skip (SpliceYielding (slice nArr 0 boff) SpliceFinish)
582+
583+ step' _ SpliceFinish = return D.Stop
584+
585+ step' _ (SpliceYielding arr next) = return $ D.Yield arr next
586+
587+{-# INLINE_NORMAL lpackArraysChunksOf #-}
588+lpackArraysChunksOf ::
589+ forall m a. (MonadIO m, Prim a)
590+ => Int
591+ -> Fold m (Array a) ()
592+ -> Fold m (Array a) ()
593+lpackArraysChunksOf n (Fold step1 initial1 extract1) =
594+ Fold step initial extract
595+
596+ where
597+
598+ nElem = n `quot` sizeOf (undefined :: a)
599+
600+ initial = do
601+ when (n <= 0) $
602+ -- XXX we can pass the module string from the higher level API
603+ error $ "Streamly.Internal.Memory.Array.Types.packArraysChunksOf: the size of "
604+ ++ "arrays [" ++ show n ++ "] must be a natural number"
605+ r1 <- initial1
606+ return (Tuple3' Nothing' 0 r1)
607+
608+ extract (Tuple3' Nothing' _ r1) = extract1 r1
609+ extract (Tuple3' (Just' buf) boff r1) = do
610+ nArr <- unsafeFreeze buf
611+ r <- step1 r1 (slice nArr 0 boff)
612+ extract1 r
613+
614+ step (Tuple3' Nothing' _ r1) arr =
615+
616+ if length arr >= nElem
617+ then do
618+ r <- step1 r1 arr
619+ extract1 r
620+ r1' <- initial1
621+ return (Tuple3' Nothing' 0 r1')
622+ else do
623+ buf <- MA.newArray nElem
624+ noff <- spliceInto buf 0 arr
625+ return (Tuple3' (Just' buf) noff r1)
626+
627+ step (Tuple3' (Just' buf) boff r1) arr = do
628+ noff <- spliceInto buf boff arr
629+
630+ if noff >= nElem
631+ then do
632+ nArr <- unsafeFreeze buf
633+ r <- step1 r1 (slice nArr 0 noff)
634+ extract1 r
635+ r1' <- initial1
636+ return (Tuple3' Nothing' 0 r1')
637+ else return (Tuple3' (Just' buf) noff r1)
638+
639+data SplitState s arr
640+ = Initial s
641+ | Buffering s arr
642+ | Splitting s arr
643+ | Yielding arr (SplitState s arr)
644+ | Finishing
645+
646+-- | Split a stream of arrays on a given separator byte, dropping the separator
647+-- and coalescing all the arrays between two separators into a single array.
648+--
649+-- /Internal/
650+{-# INLINE_NORMAL splitOn #-}
651+splitOn ::
652+ MonadIO m
653+ => Word8
654+ -> D.Stream m (Array Word8)
655+ -> D.Stream m (Array Word8)
656+splitOn byte (D.Stream step state) = D.Stream step' (Initial state)
657+
658+ where
659+
660+ {-# INLINE_LATE step' #-}
661+ step' gst (Initial st) = do
662+ r <- step gst st
663+ case r of
664+ D.Yield arr s -> do
665+ (arr1, marr2) <- breakOn byte arr
666+ return $ case marr2 of
667+ Nothing -> D.Skip (Buffering s arr1)
668+ Just arr2 -> D.Skip (Yielding arr1 (Splitting s arr2))
669+ D.Skip s -> return $ D.Skip (Initial s)
670+ D.Stop -> return $ D.Stop
671+
672+ step' gst (Buffering st buf) = do
673+ r <- step gst st
674+ case r of
675+ D.Yield arr s -> do
676+ (arr1, marr2) <- breakOn byte arr
677+ buf' <- spliceTwo buf arr1
678+ return $ case marr2 of
679+ Nothing -> D.Skip (Buffering s buf')
680+ Just x -> D.Skip (Yielding buf' (Splitting s x))
681+ D.Skip s -> return $ D.Skip (Buffering s buf)
682+ D.Stop -> return $
683+ if byteLength buf == 0
684+ then D.Stop
685+ else D.Skip (Yielding buf Finishing)
686+
687+ step' _ (Splitting st buf) = do
688+ (arr1, marr2) <- breakOn byte buf
689+ return $ case marr2 of
690+ Nothing -> D.Skip $ Buffering st arr1
691+ Just arr2 -> D.Skip $ Yielding arr1 (Splitting st arr2)
692+
693+ step' _ (Yielding arr next) = return $ D.Yield arr next
694+ step' _ Finishing = return $ D.Stop
src/Streamly/Internal/Data/Array/PrimInclude.hsnew file+185
@@ -0,0 +1,185 @@
1+-- Copyright : (c) 2020 Composewell Technologies
2+-- License : BSD-3-Clause
3+-- Maintainer : streamly@composewell.com
4+-- Stability : experimental
5+-- Portability : GHC
6+
7+import Control.Monad (when)
8+import Control.Monad.IO.Class (MonadIO(..))
9+import Data.Primitive.Types (Prim(..))
10+import Streamly.Internal.Data.Fold.Types (Fold(..))
11+import Streamly.Internal.Data.Unfold.Types (Unfold(..))
12+import Streamly.Internal.Data.Stream.Serial (SerialT)
13+import Streamly.Internal.Data.Stream.StreamK.Type (IsStream)
14+
15+import qualified Streamly.Internal.Data.Stream.Prelude as P
16+import qualified Streamly.Internal.Data.Stream.Serial as Serial
17+import qualified Streamly.Internal.Data.Stream.StreamD as D
18+import qualified Streamly.Internal.Data.Stream.StreamK as K
19+
20+import Prelude hiding (length, null, last, map, (!!), read, concat)
21+
22+-------------------------------------------------------------------------------
23+-- Construction
24+-------------------------------------------------------------------------------
25+
26+-- | Create an 'Array' from the first N elements of a stream. The array is
27+-- allocated to size N, if the stream terminates before N elements then the
28+-- array may hold less than N elements.
29+--
30+-- /Internal/
31+{-# INLINE fromStreamN #-}
32+fromStreamN :: (MonadIO m, Prim a) => Int -> SerialT m a -> m (Array a)
33+fromStreamN n m = do
34+ when (n < 0) $ error "writeN: negative write count specified"
35+ A.fromStreamDN n $ D.toStreamD m
36+
37+-- | Create an 'Array' from a stream. This is useful when we want to create a
38+-- single array from a stream of unknown size. 'writeN' is at least twice
39+-- as efficient when the size is already known.
40+--
41+-- Note that if the input stream is too large memory allocation for the array
42+-- may fail. When the stream size is not known, `arraysOf` followed by
43+-- processing of indvidual arrays in the resulting stream should be preferred.
44+--
45+-- /Internal/
46+{-# INLINE fromStream #-}
47+fromStream :: (MonadIO m, Prim a) => SerialT m a -> m (Array a)
48+fromStream = P.runFold A.write
49+-- write m = A.fromStreamD $ D.toStreamD m
50+
51+-------------------------------------------------------------------------------
52+-- Elimination
53+-------------------------------------------------------------------------------
54+
55+-- | Convert an 'Array' into a stream.
56+--
57+-- /Internal/
58+{-# INLINE_EARLY toStream #-}
59+toStream :: (MonadIO m, K.IsStream t, Prim a) => Array a -> t m a
60+toStream = D.fromStreamD . A.toStreamD
61+-- XXX add fallback to StreamK rule
62+-- {-# RULES "Streamly.Array.read fallback to StreamK" [1]
63+-- forall a. S.readK (read a) = K.fromArray a #-}
64+
65+-- | Convert an 'Array' into a stream in reverse order.
66+--
67+-- /Internal/
68+{-# INLINE_EARLY toStreamRev #-}
69+toStreamRev :: (MonadIO m, IsStream t, Prim a) => Array a -> t m a
70+toStreamRev = D.fromStreamD . A.toStreamDRev
71+-- XXX add fallback to StreamK rule
72+-- {-# RULES "Streamly.Array.readRev fallback to StreamK" [1]
73+-- forall a. S.toStreamK (readRev a) = K.revFromArray a #-}
74+
75+-- | Unfold an array into a stream.
76+--
77+-- @since 0.7.0
78+{-# INLINE_NORMAL read #-}
79+read :: (MonadIO m, Prim a) => Unfold m (Array a) a
80+read = Unfold step inject
81+ where
82+
83+ inject = return
84+
85+ {-# INLINE_LATE step #-}
86+ step (Array _ _ len) | len == 0 = return D.Stop
87+ step arr@(Array arr# off len) =
88+ let !x = A.unsafeIndex arr 0
89+ in return $ D.Yield x (Array arr# (off + 1) (len - 1))
90+
91+-- | Unfold an array into a stream, does not check the end of the array, the
92+-- user is responsible for terminating the stream within the array bounds. For
93+-- high performance application where the end condition can be determined by
94+-- a terminating fold.
95+--
96+-- The following might not be true, not that the representation changed.
97+-- Written in the hope that it may be faster than "read", however, in the case
98+-- for which this was written, "read" proves to be faster even though the core
99+-- generated with unsafeRead looks simpler.
100+--
101+-- /Internal/
102+--
103+{-# INLINE_NORMAL unsafeRead #-}
104+unsafeRead :: (MonadIO m, Prim a) => Unfold m (Array a) a
105+unsafeRead = Unfold step inject
106+ where
107+
108+ inject = return
109+
110+ {-# INLINE_LATE step #-}
111+ step arr@(Array arr# off len) =
112+ let !x = A.unsafeIndex arr 0
113+ in return $ D.Yield x (Array arr# (off + 1) (len - 1))
114+
115+-- | > null arr = length arr == 0
116+--
117+-- /Internal/
118+{-# INLINE null #-}
119+null :: Array a -> Bool
120+null arr = length arr == 0
121+
122+-------------------------------------------------------------------------------
123+-- Folds
124+-------------------------------------------------------------------------------
125+
126+-- | Fold an array using a 'Fold'.
127+--
128+-- /Internal/
129+{-# INLINE fold #-}
130+fold :: forall m a b. (MonadIO m, Prim a) => Fold m a b -> Array a -> m b
131+fold f arr = P.runFold f (toStream arr :: Serial.SerialT m a)
132+
133+-- | Fold an array using a stream fold operation.
134+--
135+-- /Internal/
136+{-# INLINE streamFold #-}
137+streamFold :: (MonadIO m, Prim a) => (SerialT m a -> m b) -> Array a -> m b
138+streamFold f arr = f (toStream arr)
139+
140+-------------------------------------------------------------------------------
141+-- Random reads
142+-------------------------------------------------------------------------------
143+
144+-- | /O(1)/ Lookup the element at the given index, starting from 0.
145+--
146+-- /Internal/
147+{-# INLINE readIndex #-}
148+readIndex :: Prim a => Array a -> Int -> Maybe a
149+readIndex arr i =
150+ if i < 0 || i > length arr - 1
151+ then Nothing
152+ else Just $ A.unsafeIndex arr i
153+
154+-- | > last arr = readIndex arr (length arr - 1)
155+--
156+-- /Internal/
157+{-# INLINE last #-}
158+last :: Prim a => Array a -> Maybe a
159+last arr = readIndex arr (length arr - 1)
160+
161+-------------------------------------------------------------------------------
162+-- Array stream operations
163+-------------------------------------------------------------------------------
164+
165+-- | Convert a stream of arrays into a stream of their elements.
166+--
167+-- Same as the following but more efficient:
168+--
169+-- > concat = S.concatMap A.read
170+--
171+-- /Internal/
172+{-# INLINE concat #-}
173+concat :: (IsStream t, MonadIO m, Prim a) => t m (Array a) -> t m a
174+-- concat m = D.fromStreamD $ A.flattenArrays (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)
177+
178+-- | Coalesce adjacent arrays in incoming stream to form bigger arrays of a
179+-- maximum specified size in bytes.
180+--
181+-- /Internal/
182+{-# INLINE compact #-}
183+compact ::
184+ (MonadIO m, Prim a) => Int -> SerialT m (Array a) -> SerialT m (Array a)
185+compact n xs = D.fromStreamD $ A.packArraysChunksOf n (D.toStreamD xs)
src/Streamly/Internal/Data/Prim/Array.hsdeleted−201
@@ -1,201 +0,0 @@
1−{-# OPTIONS_GHC -fno-warn-orphans #-}
2−
3−#include "inline.hs"
4−
5−-- |
6−-- Module : Streamly.Internal.Data.Prim.Array
7−-- Copyright : (c) 2019 Composewell Technologies
8−--
9−-- License : BSD-3-Clause
10−-- Maintainer : streamly@composewell.com
11−-- Stability : experimental
12−-- Portability : GHC
13−--
14−module Streamly.Internal.Data.Prim.Array
15− (
16−
17− -- XXX should it be just Array instead? We should be able to replace one
18− -- array type with another easily.
19− PrimArray(..)
20−
21− -- XXX Prim should be exported from Data.Prim module?
22− , Prim(..)
23−
24− , foldl'
25− , foldr
26−
27− , length
28−
29− , writeN
30− , write
31−
32− , toStreamD
33− , toStreamDRev
34−
35− , toStream
36− , toStreamRev
37− , read
38− , readSlice
39−
40− , fromListN
41− , fromList
42− , fromStreamDN
43− , fromStreamD
44−
45− , fromStreamN
46− , fromStream
47−
48− , streamFold
49− , fold
50− )
51−where
52−
53−import Prelude hiding (foldr, length, read)
54−import Control.DeepSeq (NFData(..))
55−import Control.Monad (when)
56−import Control.Monad.IO.Class (liftIO, MonadIO)
57−import GHC.IO (unsafePerformIO)
58−import Data.Primitive.Types (Prim(..))
59−
60−import Streamly.Internal.Data.Prim.Array.Types
61−import Streamly.Internal.Data.Unfold.Types (Unfold(..))
62−import Streamly.Internal.Data.Fold.Types (Fold(..))
63−import Streamly.Internal.Data.Stream.StreamK.Type (IsStream)
64−import Streamly.Internal.Data.Stream.Serial (SerialT)
65−
66−import qualified Streamly.Internal.Data.Stream.StreamD as D
67−
68−{-# INLINE_NORMAL toStreamD #-}
69−toStreamD :: (Prim a, Monad m) => PrimArray a -> D.Stream m a
70−toStreamD arr = D.Stream step 0
71− where
72− {-# INLINE_LATE step #-}
73− step _ i
74− | i == sizeofPrimArray arr = return D.Stop
75− step _ i = return $ D.Yield (indexPrimArray arr i) (i + 1)
76−
77−{-# INLINE length #-}
78−length :: Prim a => PrimArray a -> Int
79−length = sizeofPrimArray
80−
81−{-# INLINE_NORMAL toStreamDRev #-}
82−toStreamDRev :: (Prim a, Monad m) => PrimArray a -> D.Stream m a
83−toStreamDRev arr = D.Stream step (sizeofPrimArray arr - 1)
84− where
85− {-# INLINE_LATE step #-}
86− step _ i
87− | i < 0 = return D.Stop
88− step _ i = return $ D.Yield (indexPrimArray arr i) (i - 1)
89−
90−{-# INLINE_NORMAL foldl' #-}
91−foldl' :: Prim a => (b -> a -> b) -> b -> PrimArray a -> b
92−foldl' = foldlPrimArray'
93−
94−{-# INLINE_NORMAL foldr #-}
95−foldr :: Prim a => (a -> b -> b) -> b -> PrimArray a -> b
96−foldr = foldrPrimArray
97−
98−-- writeN n = S.evertM (fromStreamDN n)
99−{-# INLINE_NORMAL writeN #-}
100−writeN :: (MonadIO m, Prim a) => Int -> Fold m a (PrimArray a)
101−writeN limit = Fold step initial extract
102− where
103− initial = do
104− marr <- liftIO $ newPrimArray limit
105− return (marr, 0)
106− step (marr, i) x
107− | i == limit = return (marr, i)
108− | otherwise = do
109− liftIO $ writePrimArray marr i x
110− return (marr, i + 1)
111− extract (marr, _) = liftIO $ unsafeFreezePrimArray marr
112−
113−{-# INLINE_NORMAL write #-}
114−write :: (MonadIO m, Prim a) => Fold m a (PrimArray a)
115−write = Fold step initial extract
116− where
117− initial = do
118− marr <- liftIO $ newPrimArray 0
119− return (marr, 0, 0)
120− step (marr, i, capacity) x
121− | i == capacity =
122− let newCapacity = max (capacity * 2) 1
123− in do newMarr <- liftIO $ resizeMutablePrimArray marr newCapacity
124− liftIO $ writePrimArray newMarr i x
125− return (newMarr, i + 1, newCapacity)
126− | otherwise = do
127− liftIO $ writePrimArray marr i x
128− return (marr, i + 1, capacity)
129− extract (marr, len, _) = do liftIO $ shrinkMutablePrimArray marr len
130− liftIO $ unsafeFreezePrimArray marr
131−
132−{-# INLINE_NORMAL fromStreamDN #-}
133−fromStreamDN :: (MonadIO m, Prim a) => Int -> D.Stream m a -> m (PrimArray a)
134−fromStreamDN limit str = do
135− marr <- liftIO $ newPrimArray (max limit 0)
136− _ <-
137− D.foldlM'
138− (\i x -> i `seq` liftIO (writePrimArray marr i x) >> return (i + 1))
139− (return 0) $
140− D.take limit str
141− liftIO $ unsafeFreezePrimArray marr
142−
143−{-# INLINE fromStreamD #-}
144−fromStreamD :: (MonadIO m, Prim a) => D.Stream m a -> m (PrimArray a)
145−fromStreamD = D.runFold write
146−
147−{-# INLINABLE fromListN #-}
148−fromListN :: Prim a => Int -> [a] -> PrimArray a
149−fromListN n xs = unsafePerformIO $ fromStreamDN n $ D.fromList xs
150−
151−{-# INLINABLE fromList #-}
152−fromList :: Prim a => [a] -> PrimArray a
153−fromList xs = unsafePerformIO $ fromStreamD $ D.fromList xs
154−
155−instance Prim a => NFData (PrimArray a) where
156− {-# INLINE rnf #-}
157− rnf = foldl' (\_ _ -> ()) ()
158−
159−{-# INLINE fromStreamN #-}
160−fromStreamN :: (MonadIO m, Prim a) => Int -> SerialT m a -> m (PrimArray a)
161−fromStreamN n m = do
162− when (n < 0) $ error "fromStreamN: negative write count specified"
163− fromStreamDN n $ D.toStreamD m
164−
165−{-# INLINE fromStream #-}
166−fromStream :: (MonadIO m, Prim a) => SerialT m a -> m (PrimArray a)
167−fromStream m = fromStreamD $ D.toStreamD m
168−
169−{-# INLINE_EARLY toStream #-}
170−toStream :: (Prim a, Monad m, IsStream t) => PrimArray a -> t m a
171−toStream = D.fromStreamD . toStreamD
172−
173−{-# INLINE_EARLY toStreamRev #-}
174−toStreamRev :: (Prim a, Monad m, IsStream t) => PrimArray a -> t m a
175−toStreamRev = D.fromStreamD . toStreamDRev
176−
177−{-# INLINE fold #-}
178−fold :: (Prim a, Monad m) => Fold m a b -> PrimArray a -> m b
179−fold f arr = D.runFold f (toStreamD arr)
180−
181−{-# INLINE streamFold #-}
182−streamFold :: (Prim a, Monad m) => (SerialT m a -> m b) -> PrimArray a -> m b
183−streamFold f arr = f (toStream arr)
184−
185−{-# INLINE_NORMAL read #-}
186−read :: (Prim a, Monad m) => Unfold m (PrimArray a) a
187−read = Unfold step inject
188− where
189− inject arr = return (arr, 0)
190− step (arr, i)
191− | i == length arr = return D.Stop
192− step (arr, i) = return $ D.Yield (indexPrimArray arr i) (arr, i + 1)
193−
194−{-# INLINE_NORMAL readSlice #-}
195−readSlice :: (Prim a, Monad m) => Int -> Int -> Unfold m (PrimArray a) a
196−readSlice off len = Unfold step inject
197− where
198− inject arr = return (arr, off)
199− step (arr, i)
200− | i == min (off + len) (length arr) = return D.Stop
201− step (arr, i) = return $ D.Yield (indexPrimArray arr i) (arr, i + 1)
src/Streamly/Internal/Data/Prim/Array/Types.hsdeleted−207
@@ -1,207 +0,0 @@
1−{-# LANGUAGE UnboxedTuples #-}
2−
3−-- |
4−-- Module : Streamly.Internal.Data.Prim.Array.Types
5−-- Copyright : (c) Roman Leshchinskiy 2009-2012
6−-- License : BSD-style
7−--
8−-- Maintainer : streamly@composewell.com
9−-- Portability : non-portable
10−--
11−-- Arrays of unboxed primitive types. The function provided by this module
12−-- match the behavior of those provided by @Data.Primitive.ByteArray@, and
13−-- the underlying types and primops that back them are the same.
14−-- However, the type constructors 'PrimArray' and 'MutablePrimArray' take one additional
15−-- argument than their respective counterparts 'ByteArray' and 'MutableByteArray'.
16−-- This argument is used to designate the type of element in the array.
17−-- Consequently, all function this modules accepts length and incides in
18−-- terms of elements, not bytes.
19−--
20−-- @since 0.6.4.0
21−module Streamly.Internal.Data.Prim.Array.Types
22− ( -- * Types
23− PrimArray(..)
24− , MutablePrimArray(..)
25− -- * Allocation
26− , newPrimArray
27− , resizeMutablePrimArray
28− , shrinkMutablePrimArray
29− -- * Element Access
30− , writePrimArray
31− , indexPrimArray
32− -- * Freezing and Thawing
33− , unsafeFreezePrimArray
34− -- * Information
35− , sizeofPrimArray
36− -- * Folding
37− , foldrPrimArray
38− , foldlPrimArray'
39− ) where
40−
41−import GHC.Exts
42−
43−import Data.Primitive.Types
44−import Data.Primitive.ByteArray (ByteArray(..))
45−import Control.Monad.Primitive
46−import qualified Data.Primitive.ByteArray as PB
47−
48−-- | Arrays of unboxed elements. This accepts types like 'Double', 'Char',
49−-- 'Int', and 'Word', as well as their fixed-length variants ('Word8',
50−-- 'Word16', etc.). Since the elements are unboxed, a 'PrimArray' is strict
51−-- in its elements. This differs from the behavior of 'Array', which is lazy
52−-- in its elements.
53−data PrimArray a = PrimArray ByteArray#
54−
55−-- | Mutable primitive arrays associated with a primitive state token.
56−-- These can be written to and read from in a monadic context that supports
57−-- sequencing such as 'IO' or 'ST'. Typically, a mutable primitive array will
58−-- be built and then convert to an immutable primitive array using
59−-- 'unsafeFreezePrimArray'. However, it is also acceptable to simply discard
60−-- a mutable primitive array since it lives in managed memory and will be
61−-- garbage collected when no longer referenced.
62−data MutablePrimArray s a = MutablePrimArray (MutableByteArray# s)
63−
64−sameByteArray :: ByteArray# -> ByteArray# -> Bool
65−sameByteArray ba1 ba2 =
66− case reallyUnsafePtrEquality# (unsafeCoerce# ba1 :: ()) (unsafeCoerce# ba2 :: ()) of
67− r -> isTrue# r
68−
69−-- | @since 0.6.4.0
70−instance (Eq a, Prim a) => Eq (PrimArray a) where
71− a1@(PrimArray ba1#) == a2@(PrimArray ba2#)
72− | sameByteArray ba1# ba2# = True
73− | sz1 /= sz2 = False
74− | otherwise = loop (quot sz1 (sizeOf (undefined :: a)) - 1)
75− where
76− -- Here, we take the size in bytes, not in elements. We do this
77− -- since it allows us to defer performing the division to
78− -- calculate the size in elements.
79− sz1 = PB.sizeofByteArray (ByteArray ba1#)
80− sz2 = PB.sizeofByteArray (ByteArray ba2#)
81− loop !i
82− | i < 0 = True
83− | otherwise = indexPrimArray a1 i == indexPrimArray a2 i && loop (i-1)
84− {-# INLINE (==) #-}
85−
86−-- | Lexicographic ordering. Subject to change between major versions.
87−--
88−-- @since 0.6.4.0
89−instance (Ord a, Prim a) => Ord (PrimArray a) where
90− compare a1@(PrimArray ba1#) a2@(PrimArray ba2#)
91− | sameByteArray ba1# ba2# = EQ
92− | otherwise = loop 0
93− where
94− cmp LT _ = LT
95− cmp EQ y = y
96− cmp GT _ = GT
97− sz1 = PB.sizeofByteArray (ByteArray ba1#)
98− sz2 = PB.sizeofByteArray (ByteArray ba2#)
99− sz = quot (min sz1 sz2) (sizeOf (undefined :: a))
100− loop !i
101− | i < sz = compare (indexPrimArray a1 i) (indexPrimArray a2 i) `cmp` loop (i+1)
102− | otherwise = compare sz1 sz2
103− {-# INLINE compare #-}
104−
105−-- | @since 0.6.4.0
106−instance (Show a, Prim a) => Show (PrimArray a) where
107− showsPrec p a = showParen (p > 10) $
108− showString "fromListN " . shows (sizeofPrimArray a) . showString " "
109− . shows (primArrayToList a)
110−
111−-- | Convert the primitive array to a list.
112−{-# INLINE primArrayToList #-}
113−primArrayToList :: forall a. Prim a => PrimArray a -> [a]
114−primArrayToList xs = build (\c n -> foldrPrimArray c n xs)
115−
116−-- | Create a new mutable primitive array of the given length. The
117−-- underlying memory is left uninitialized.
118−newPrimArray :: forall m a. (PrimMonad m, Prim a) => Int -> m (MutablePrimArray (PrimState m) a)
119−{-# INLINE newPrimArray #-}
120−newPrimArray (I# n#)
121− = primitive (\s# ->
122− case newByteArray# (n# *# sizeOf# (undefined :: a)) s# of
123− (# s'#, arr# #) -> (# s'#, MutablePrimArray arr# #)
124− )
125−
126−-- | Resize a mutable primitive array. The new size is given in elements.
127−--
128−-- This will either resize the array in-place or, if not possible, allocate the
129−-- contents into a new, unpinned array and copy the original array\'s contents.
130−--
131−-- To avoid undefined behaviour, the original 'MutablePrimArray' shall not be
132−-- accessed anymore after a 'resizeMutablePrimArray' has been performed.
133−-- Moreover, no reference to the old one should be kept in order to allow
134−-- garbage collection of the original 'MutablePrimArray' in case a new
135−-- 'MutablePrimArray' had to be allocated.
136−resizeMutablePrimArray :: forall m a. (PrimMonad m, Prim a)
137− => MutablePrimArray (PrimState m) a
138− -> Int -- ^ new size
139− -> m (MutablePrimArray (PrimState m) a)
140−{-# INLINE resizeMutablePrimArray #-}
141−resizeMutablePrimArray (MutablePrimArray arr#) (I# n#)
142− = primitive (\s# -> case resizeMutableByteArray# arr# (n# *# sizeOf# (undefined :: a)) s# of
143− (# s'#, arr'# #) -> (# s'#, MutablePrimArray arr'# #))
144−
145−-- Although it is possible to shim resizeMutableByteArray for old GHCs, this
146−-- is not the case with shrinkMutablePrimArray.
147−
148−-- | Shrink a mutable primitive array. The new size is given in elements.
149−-- It must be smaller than the old size. The array will be resized in place.
150−-- This function is only available when compiling with GHC 7.10 or newer.
151−shrinkMutablePrimArray :: forall m a. (PrimMonad m, Prim a)
152− => MutablePrimArray (PrimState m) a
153− -> Int -- ^ new size
154− -> m ()
155−{-# INLINE shrinkMutablePrimArray #-}
156−shrinkMutablePrimArray (MutablePrimArray arr#) (I# n#)
157− = primitive_ (shrinkMutableByteArray# arr# (n# *# sizeOf# (undefined :: a)))
158−
159−-- | Write an element to the given index.
160−writePrimArray ::
161− (Prim a, PrimMonad m)
162− => MutablePrimArray (PrimState m) a -- ^ array
163− -> Int -- ^ index
164− -> a -- ^ element
165− -> m ()
166−{-# INLINE writePrimArray #-}
167−writePrimArray (MutablePrimArray arr#) (I# i#) x
168− = primitive_ (writeByteArray# arr# i# x)
169−
170−-- | Convert a mutable byte array to an immutable one without copying. The
171−-- array should not be modified after the conversion.
172−unsafeFreezePrimArray
173− :: PrimMonad m => MutablePrimArray (PrimState m) a -> m (PrimArray a)
174−{-# INLINE unsafeFreezePrimArray #-}
175−unsafeFreezePrimArray (MutablePrimArray arr#)
176− = primitive (\s# -> case unsafeFreezeByteArray# arr# s# of
177− (# s'#, arr'# #) -> (# s'#, PrimArray arr'# #))
178−
179−-- | Read a primitive value from the primitive array.
180−indexPrimArray :: forall a. Prim a => PrimArray a -> Int -> a
181−{-# INLINE indexPrimArray #-}
182−indexPrimArray (PrimArray arr#) (I# i#) = indexByteArray# arr# i#
183−
184−-- | Get the size, in elements, of the primitive array.
185−sizeofPrimArray :: forall a. Prim a => PrimArray a -> Int
186−{-# INLINE sizeofPrimArray #-}
187−sizeofPrimArray (PrimArray arr#) = I# (quotInt# (sizeofByteArray# arr#) (sizeOf# (undefined :: a)))
188−
189−-- | Lazy right-associated fold over the elements of a 'PrimArray'.
190−{-# INLINE foldrPrimArray #-}
191−foldrPrimArray :: forall a b. Prim a => (a -> b -> b) -> b -> PrimArray a -> b
192−foldrPrimArray f z arr = go 0
193− where
194− !sz = sizeofPrimArray arr
195− go !i
196− | sz > i = f (indexPrimArray arr i) (go (i+1))
197− | otherwise = z
198−
199−-- | Strict left-associated fold over the elements of a 'PrimArray'.
200−{-# INLINE foldlPrimArray' #-}
201−foldlPrimArray' :: forall a b. Prim a => (b -> a -> b) -> b -> PrimArray a -> b
202−foldlPrimArray' f z0 arr = go 0 z0
203− where
204− !sz = sizeofPrimArray arr
205− go !i !acc
206− | i < sz = go (i + 1) (f acc (indexPrimArray arr i))
207− | otherwise = acc
src/Streamly/Internal/Unicode/Array/Prim/Pinned.hsnew file+84
@@ -0,0 +1,84 @@
1+-- |
2+-- Module : Streamly.Internal.Unicode.Array.Prim.Pinned
3+-- Copyright : (c) 2020 Composewell Technologies
4+--
5+-- License : BSD-3-Clause
6+-- Maintainer : streamly@composewell.com
7+-- Stability : experimental
8+-- Portability : GHC
9+--
10+module Streamly.Internal.Unicode.Array.Prim.Pinned
11+ (
12+ -- * Streams of Strings
13+ lines
14+ , words
15+ , unlines
16+ , unwords
17+ )
18+where
19+
20+import Control.Monad.IO.Class (MonadIO(..))
21+import Streamly (IsStream, MonadAsync)
22+import Prelude hiding (String, lines, words, unlines, unwords)
23+import Streamly.Internal.Data.Array.Prim.Pinned (Array)
24+
25+import qualified Streamly.Internal.Data.Unicode.Stream as S
26+import qualified Streamly.Internal.Data.Array.Prim.Pinned as A
27+
28+-- | Break a string up into a stream of strings at newline characters.
29+-- The resulting strings do not contain newlines.
30+--
31+-- > lines = S.lines A.write
32+--
33+-- >>> S.toList $ lines $ S.fromList "lines\nthis\nstring\n\n\n"
34+-- ["lines","this","string","",""]
35+--
36+{-# INLINE lines #-}
37+lines :: (MonadIO m, IsStream t) => t m Char -> t m (Array Char)
38+lines = S.lines A.write
39+
40+-- | Break a string up into a stream of strings, which were delimited
41+-- by characters representing white space.
42+--
43+-- > words = S.words A.write
44+--
45+-- >>> S.toList $ words $ S.fromList "A newline\nis considered white space?"
46+-- ["A", "newline", "is", "considered", "white", "space?"]
47+--
48+{-# INLINE words #-}
49+words :: (MonadIO m, IsStream t) => t m Char -> t m (Array Char)
50+words = S.words A.write
51+
52+-- | Flattens the stream of @Array Char@, after appending a terminating
53+-- newline to each string.
54+--
55+-- 'unlines' is an inverse operation to 'lines'.
56+--
57+-- >>> S.toList $ unlines $ S.fromList ["lines", "this", "string"]
58+-- "lines\nthis\nstring\n"
59+--
60+-- > unlines = S.unlines A.read
61+--
62+-- Note that, in general
63+--
64+-- > unlines . lines /= id
65+{-# INLINE unlines #-}
66+unlines :: (MonadAsync m, IsStream t) => t m (Array Char) -> t m Char
67+unlines = S.unlines A.read
68+
69+-- | Flattens the stream of @Array Char@, after appending a separating
70+-- space to each string.
71+--
72+-- 'unwords' is an inverse operation to 'words'.
73+--
74+-- >>> S.toList $ unwords $ S.fromList ["unwords", "this", "string"]
75+-- "unwords this string"
76+--
77+-- > unwords = S.unwords A.read
78+--
79+-- Note that, in general
80+--
81+-- > unwords . words /= id
82+{-# INLINE unwords #-}
83+unwords :: (MonadAsync m, IsStream t) => t m (Array Char) -> t m Char
84+unwords = S.unwords A.read
streamly.cabal+28−3
@@ -143,6 +143,9 @@ extra-source-files:
143143 examples/README.md
144144 src/Streamly/Internal/Data/Stream/Instances.hs
145145 src/Streamly/Internal/Data/Time/config-clock.h
146+ src/Streamly/Internal/Data/Array/PrimInclude.hs
147+ src/Streamly/Internal/Data/Array/Prim/TypesInclude.hs
148+ src/Streamly/Internal/Data/Array/Prim/MutTypesInclude.hs
146149 src/Streamly/Internal/Data/Time/config.h.in
147150 src/inline.hs
148151 stack.yaml
@@ -378,8 +381,12 @@ library
378381
379382 -- Arrays
380383 , Streamly.Internal.Data.Array
381− , Streamly.Internal.Data.Prim.Array.Types
382− , Streamly.Internal.Data.Prim.Array
384+ , Streamly.Internal.Data.Array.Prim.Types
385+ , Streamly.Internal.Data.Array.Prim
386+ , Streamly.Internal.Data.Array.Prim.Mut.Types
387+ , Streamly.Internal.Data.Array.Prim.Pinned.Types
388+ , Streamly.Internal.Data.Array.Prim.Pinned
389+ , Streamly.Internal.Data.Array.Prim.Pinned.Mut.Types
383390 , Streamly.Internal.Data.SmallArray.Types
384391 , Streamly.Internal.Data.SmallArray
385392 , Streamly.Internal.Memory.Mutable.Array.Types
@@ -439,6 +446,7 @@ library
439446 , Streamly.Internal.Data.Unicode.Stream
440447 , Streamly.Internal.Data.Unicode.Char
441448 , Streamly.Internal.Memory.Unicode.Array
449+ , Streamly.Internal.Unicode.Array.Prim.Pinned
442450
443451 if !impl(ghcjs)
444452 exposed-modules:
@@ -660,7 +668,24 @@ test-suite primarray-test
660668 main-is: Streamly/Test/Array.hs
661669 js-sources: jsbits/clock.js
662670 hs-source-dirs: test
663− cpp-options: -DTEST_PRIM_ARRAY
671+ cpp-options: -DDATA_ARRAY_PRIM
672+ build-depends:
673+ streamly
674+ , base >= 4.8 && < 5
675+ , QuickCheck >= 2.10 && < 2.15
676+ , hspec >= 2.0 && < 3
677+ if impl(ghc < 8.0)
678+ build-depends:
679+ transformers >= 0.4 && < 0.6
680+ default-language: Haskell2010
681+
682+test-suite prim-pinned-array-test
683+ import: test-options
684+ type: exitcode-stdio-1.0
685+ main-is: Streamly/Test/Array.hs
686+ js-sources: jsbits/clock.js
687+ hs-source-dirs: test
688+ cpp-options: -DDATA_ARRAY_PRIM_PINNED
664689 build-depends:
665690 streamly
666691 , base >= 4.8 && < 5
test/Streamly/Test/Array.hs+31−9
@@ -25,13 +25,20 @@ import qualified Streamly.Prelude as S
2525import qualified Streamly.Internal.Data.SmallArray as A
2626type Array = A.SmallArray
2727#elif defined(TEST_ARRAY)
28−import qualified Streamly.Memory.Array as A
2928import qualified Streamly.Internal.Memory.Array as A
29+import qualified Streamly.Internal.Memory.Array.Types as A
30+import qualified Streamly.Internal.Prelude as IP
31+type Array = A.Array
32+#elif defined(DATA_ARRAY_PRIM_PINNED)
33+import qualified Streamly.Internal.Data.Array.Prim.Pinned as A
34+import qualified Streamly.Internal.Data.Array.Prim.Pinned.Types as A
35+import qualified Streamly.Internal.Prelude as IP
36+type Array = A.Array
37+#elif defined(DATA_ARRAY_PRIM)
38+import qualified Streamly.Internal.Data.Array.Prim as A
39+import qualified Streamly.Internal.Data.Array.Prim.Types as A
3040import qualified Streamly.Internal.Prelude as IP
3141type Array = A.Array
32−#elif defined(TEST_PRIM_ARRAY)
33−import qualified Streamly.Internal.Data.Prim.Array as A
34−type Array = A.PrimArray
3542#else
3643import qualified Streamly.Internal.Data.Array as A
3744type Array = A.Array
@@ -122,17 +129,27 @@ testFoldUnfold :: Property
122129testFoldUnfold = genericTestFromTo (const (S.fold A.write)) (S.unfold A.read) (==)
123130#endif
124131
125−#ifdef TEST_ARRAY
132+#if defined(TEST_ARRAY) ||\
133+ defined(DATA_ARRAY_PRIM) ||\
134+ defined(DATA_ARRAY_PRIM_PINNED)
135+
126136testArraysOf :: Property
127137testArraysOf =
128138 forAll (choose (0, maxArrLen)) $ \len ->
129139 forAll (vectorOf len (arbitrary :: Gen Int)) $ \list ->
130140 monadicIO $ do
131− xs <- S.toList
141+ xs <- run
142+ $ S.toList
132143 $ S.concatUnfold A.read
133− $ IP.arraysOf 240
144+ $ arraysOf 240
134145 $ S.fromList list
135146 assert (xs == list)
147+ where
148+ arraysOf n = IP.chunksOf n (A.writeNUnsafe n)
149+
150+#endif
151+
152+#ifdef TEST_ARRAY
136153
137154lastN :: Int -> [a] -> [a]
138155lastN n l = drop (length l - n) l
@@ -143,7 +160,8 @@ testLastN =
143160 forAll (choose (0, len)) $ \n ->
144161 forAll (vectorOf len (arbitrary :: Gen Int)) $ \list ->
145162 monadicIO $ do
146− xs <- fmap A.toList
163+ xs <- run
164+ $ fmap A.toList
147165 $ S.fold (A.lastN n)
148166 $ S.fromList list
149167 assert (xs == lastN n list)
@@ -174,9 +192,13 @@ main =
174192 prop "toStream . fromStream === id" testFromStreamToStream
175193 prop "read . write === id" testFoldUnfold
176194#endif
177−#ifdef TEST_ARRAY
195+
196+#if defined(TEST_ARRAY) ||\
197+ defined(DATA_ARRAY_PRIM) ||\
198+ defined(DATA_ARRAY_PRIM_PINNED)
178199 prop "arraysOf concats to original" testArraysOf
179200#endif
201+
180202#ifdef TEST_ARRAY
181203 describe "Fold" $ do
182204 prop "lastN : 0 <= n <= len" $ testLastN