on master help sign in
65 files changed+3373−1759
since splitsnapshots
unifiedsplit
benchmark/Streamly/Benchmark/FileSystem/Handle/Read.hs+7−8 benchmark/Streamly/Benchmark/FileSystem/Handle/ReadWrite.hs+8−9 benchmark/Streamly/Benchmark/Unicode/Stream.hs+4−3 configure+9−9 configure.ac+1−1 core/configure+9−9 core/configure.ac+1−1 core/docs/ApiChangelogs/0.2.0-0.2.2.txt+306 core/docs/Changelog.md+3−1 core/src/DocTestDataArray.hs+3 core/src/DocTestDataStream.hs+2 core/src/DocTestFileSystemHandle.hs+1−1 core/src/Streamly/Data/Array.hs+30−27 core/src/Streamly/Data/Array/Generic.hs+16−13 core/src/Streamly/Data/MutArray.hs+12−6 core/src/Streamly/Data/MutArray/Generic.hs+8−3 core/src/Streamly/Data/Stream.hs+129−20 core/src/Streamly/Internal/Data/Array.hs+530−91 core/src/Streamly/Internal/Data/Array/Generic.hs+22−6 core/src/Streamly/Internal/Data/Array/Stream.hs+15−475 core/src/Streamly/Internal/Data/Array/Type.hs+328−86 core/src/Streamly/Internal/Data/IsMap.hs+4 core/src/Streamly/Internal/Data/MutArray.hs+128 core/src/Streamly/Internal/Data/MutArray/Generic.hs+65−30 core/src/Streamly/Internal/Data/MutArray/Stream.hs+11−195 core/src/Streamly/Internal/Data/MutArray/Type.hs+987−323 core/src/Streamly/Internal/Data/MutByteArray/Type.hs+40−16 core/src/Streamly/Internal/Data/Serialize/TH.hs+5−1 core/src/Streamly/Internal/Data/Stream/Container.hs+56−48 core/src/Streamly/Internal/Data/Stream/Eliminate.hs+33−25 core/src/Streamly/Internal/Data/Stream/Exception.hs+9−6 core/src/Streamly/Internal/Data/Stream/Generate.hs−12 core/src/Streamly/Internal/Data/Stream/Nesting.hs+10−64 core/src/Streamly/Internal/Data/Stream/Top.hs+243−115 core/src/Streamly/Internal/Data/Stream/Transform.hs+53−27 core/src/Streamly/Internal/Data/Stream/Transformer.hs+2−1 core/src/Streamly/Internal/Data/Stream/Type.hs+37−3 core/src/Streamly/Internal/Data/StreamK.hs+19−8 core/src/Streamly/Internal/Data/StreamK/Type.hs+60−11 core/src/Streamly/Internal/Data/Time/Units.hs−4 core/src/Streamly/Internal/Data/Unbox.hs+4−1 core/src/Streamly/Internal/Data/Unfold.hs+6−4 core/src/Streamly/Internal/FileSystem/File.hs+4−9 core/src/Streamly/Internal/FileSystem/Handle.hs+21−28 core/src/Streamly/Internal/FileSystem/Path.hs+36−4 core/src/Streamly/Internal/Unicode/Stream.hs+1−1 core/src/Streamly/Internal/Unicode/String.hs+7−7 core/streamly-core.cabal+14−10 docs/Developer/optimization-guidelines.md+12 docs/User/Project/Changelog.md+1−1 docs/User/Project/README.md+1 src/Streamly/Internal/Data/IsMap/HashMap.hs+5 src/Streamly/Internal/Data/Stream/IsStream/Common.hs+1−1 src/Streamly/Internal/Data/Stream/IsStream/Expand.hs+5 src/Streamly/Internal/Data/Stream/IsStream/Top.hs+1−1 src/Streamly/Internal/Data/Stream/Serial.hs+5 src/Streamly/Internal/Data/Stream/Zip.hs+5 src/Streamly/Internal/FileSystem/Event.hs+3 src/Streamly/Internal/FileSystem/Event/Darwin.hs+1−1 src/Streamly/Internal/FileSystem/Event/Linux.hs+2−2 src/Streamly/Internal/FileSystem/FD.hs+5−5 src/Streamly/Internal/Network/Inet/TCP.hs+3−3 src/Streamly/Internal/Network/Socket.hs+11−10 streamly.cabal+11−11 test/Streamly/Test/Data/Array.hs+2−2
benchmark/Streamly/Benchmark/FileSystem/Handle/Read.hs+7−8
@@ -33,7 +33,6 @@ import qualified Streamly.Data.Stream as Stream
3333import qualified Streamly.Data.Fold as Fold
3434import qualified Streamly.FileSystem.Handle as FH
3535import qualified Streamly.Internal.Data.Array as A
36−import qualified Streamly.Internal.Data.Array as AT
3736import qualified Streamly.Internal.Data.Fold as FL
3837import qualified Streamly.Internal.Data.Stream as IP
3938import qualified Streamly.Internal.FileSystem.Handle as IFH
@@ -48,7 +47,7 @@ import Streamly.Benchmark.Common.Handle
4847#ifdef INSPECTION
4948import Streamly.Internal.Data.Stream (Step(..), FoldMany)
5049
51−import qualified Streamly.Internal.Data.MutArray as MA
50+import qualified Streamly.Internal.Data.MutArray as MutArray
5251import qualified Streamly.Internal.Data.Stream as D
5352import qualified Streamly.Internal.Data.Unfold as IUF
5453
@@ -69,7 +68,7 @@ readLast = S.fold Fold.last . S.unfold FH.reader
6968inspect $ hasNoTypeClasses 'readLast
7069inspect $ 'readLast `hasNoType` ''Step -- S.unfold
7170inspect $ 'readLast `hasNoType` ''IUF.ConcatState -- FH.read/UF.many
72−inspect $ 'readLast `hasNoType` ''MA.ArrayUnsafe -- FH.read/A.read
71+inspect $ 'readLast `hasNoType` ''MutArray.ArrayUnsafe -- FH.read/A.read
7372#endif
7473
7574-- assert that flattenArrays constructors are not present
@@ -81,7 +80,7 @@ readCountBytes = S.fold Fold.length . S.unfold FH.reader
8180inspect $ hasNoTypeClasses 'readCountBytes
8281inspect $ 'readCountBytes `hasNoType` ''Step -- S.unfold
8382inspect $ 'readCountBytes `hasNoType` ''IUF.ConcatState -- FH.read/UF.many
84−inspect $ 'readCountBytes `hasNoType` ''MA.ArrayUnsafe -- FH.read/A.read
83+inspect $ 'readCountBytes `hasNoType` ''MutArray.ArrayUnsafe -- FH.read/A.read
8584#endif
8685
8786-- | Count the number of lines in a file.
@@ -96,7 +95,7 @@ readCountLines =
9695inspect $ hasNoTypeClasses 'readCountLines
9796inspect $ 'readCountLines `hasNoType` ''Step
9897inspect $ 'readCountLines `hasNoType` ''IUF.ConcatState -- FH.read/UF.many
99−inspect $ 'readCountLines `hasNoType` ''MA.ArrayUnsafe -- FH.read/A.read
98+inspect $ 'readCountLines `hasNoType` ''MutArray.ArrayUnsafe -- FH.read/A.read
10099#endif
101100
102101-- | Count the number of words in a file.
@@ -120,7 +119,7 @@ readSumBytes = S.fold Fold.sum . S.unfold FH.reader
120119inspect $ hasNoTypeClasses 'readSumBytes
121120inspect $ 'readSumBytes `hasNoType` ''Step
122121inspect $ 'readSumBytes `hasNoType` ''IUF.ConcatState -- FH.read/UF.many
123−inspect $ 'readSumBytes `hasNoType` ''MA.ArrayUnsafe -- FH.read/A.read
122+inspect $ 'readSumBytes `hasNoType` ''MutArray.ArrayUnsafe -- FH.read/A.read
124123#endif
125124
126125-- XXX When we mark this with INLINE and we have two benchmarks using S.drain
@@ -236,13 +235,13 @@ groupsOf :: Int -> Handle -> IO Int
236235groupsOf n inh =
237236 -- writeNUnsafe gives 2.5x boost here over writeN.
238237 S.fold Fold.length
239− $ IP.groupsOf n (AT.writeNUnsafe n) (S.unfold FH.reader inh)
238+ $ IP.groupsOf n (A.writeNUnsafe n) (S.unfold FH.reader inh)
240239
241240#ifdef INSPECTION
242241inspect $ hasNoTypeClasses 'groupsOf
243242inspect $ 'groupsOf `hasNoType` ''Step
244243inspect $ 'groupsOf `hasNoType` ''FoldMany
245−inspect $ 'groupsOf `hasNoType` ''AT.ArrayUnsafe -- AT.writeNUnsafe
244+inspect $ 'groupsOf `hasNoType` ''MutArray.ArrayUnsafe -- AT.writeNUnsafe
246245 -- FH.read/A.read
247246inspect $ 'groupsOf `hasNoType` ''IUF.ConcatState -- FH.read/UF.many
248247#endif
benchmark/Streamly/Benchmark/FileSystem/Handle/ReadWrite.hs+8−9
@@ -42,8 +42,7 @@ import Streamly.Internal.Data.Stream (Step(..))
4242
4343import qualified Streamly.Internal.Data.Stream as D
4444import qualified Streamly.Internal.Data.Tuple.Strict as Strict
45−import qualified Streamly.Internal.Data.MutArray.Stream as MAS
46−import qualified Streamly.Internal.Data.Array as AT
45+import qualified Streamly.Internal.Data.MutArray as MutArray
4746
4847import Test.Inspection
4948#endif
@@ -83,7 +82,7 @@ copyStream inh outh = S.fold (FH.write outh) (S.unfold FH.reader inh)
8382inspect $ hasNoTypeClasses 'copyStream
8483inspect $ 'copyStream `hasNoType` ''Step -- S.unfold
8584inspect $ 'copyStream `hasNoType` ''IUF.ConcatState -- FH.read/UF.many
86−inspect $ 'copyStream `hasNoType` ''AT.ArrayUnsafe -- FH.write/writeNUnsafe
85+inspect $ 'copyStream `hasNoType` ''MutArray.ArrayUnsafe -- FH.write/writeNUnsafe
8786 -- FH.read/A.read
8887inspect $ 'copyStream `hasNoType` ''Strict.Tuple3' -- FH.write/chunksOf
8988#endif
@@ -109,8 +108,8 @@ readFromBytesNull inh devNull = IFH.putBytes devNull $ S.unfold FH.reader inh
109108#ifdef INSPECTION
110109inspect $ hasNoTypeClasses 'readFromBytesNull
111110inspect $ 'readFromBytesNull `hasNoType` ''Step
112−inspect $ 'readFromBytesNull `hasNoType` ''MAS.SpliceState
113−inspect $ 'readFromBytesNull `hasNoType` ''AT.ArrayUnsafe -- FH.fromBytes/S.chunksOf
111+inspect $ 'readFromBytesNull `hasNoType` ''MutArray.SpliceState
112+inspect $ 'readFromBytesNull `hasNoType` ''MutArray.ArrayUnsafe -- FH.fromBytes/S.chunksOf
114113inspect $ 'readFromBytesNull `hasNoType` ''D.FoldMany
115114#endif
116115
@@ -123,8 +122,8 @@ readWithFromBytesNull inh devNull =
123122#ifdef INSPECTION
124123inspect $ hasNoTypeClasses 'readWithFromBytesNull
125124inspect $ 'readWithFromBytesNull `hasNoType` ''Step
126−inspect $ 'readWithFromBytesNull `hasNoType` ''MAS.SpliceState
127−inspect $ 'readWithFromBytesNull `hasNoType` ''AT.ArrayUnsafe -- FH.fromBytes/S.chunksOf
125+inspect $ 'readWithFromBytesNull `hasNoType` ''MutArray.SpliceState
126+inspect $ 'readWithFromBytesNull `hasNoType` ''MutArray.ArrayUnsafe -- FH.fromBytes/S.chunksOf
128127inspect $ 'readWithFromBytesNull `hasNoType` ''D.FoldMany
129128#endif
130129
@@ -171,7 +170,7 @@ writeReadWith inh devNull = IUF.fold fld unf (defaultChunkSize, inh)
171170inspect $ hasNoTypeClasses 'writeReadWith
172171inspect $ 'writeReadWith `hasNoType` ''Step
173172inspect $ 'writeReadWith `hasNoType` ''IUF.ConcatState -- FH.read/UF.many
174−inspect $ 'writeReadWith `hasNoType` ''AT.ArrayUnsafe -- FH.write/writeNUnsafe
173+inspect $ 'writeReadWith `hasNoType` ''MutArray.ArrayUnsafe -- FH.write/writeNUnsafe
175174 -- FH.read/A.read
176175#endif
177176
@@ -188,7 +187,7 @@ writeRead inh devNull = IUF.fold fld unf inh
188187inspect $ hasNoTypeClasses 'writeRead
189188inspect $ 'writeRead `hasNoType` ''Step
190189inspect $ 'writeRead `hasNoType` ''IUF.ConcatState -- FH.read/UF.many
191−inspect $ 'writeRead `hasNoType` ''AT.ArrayUnsafe -- FH.write/writeNUnsafe
190+inspect $ 'writeRead `hasNoType` ''MutArray.ArrayUnsafe -- FH.write/writeNUnsafe
192191 -- FH.read/A.read
193192#endif
194193
benchmark/Streamly/Benchmark/Unicode/Stream.hs+4−3
@@ -45,9 +45,10 @@ import Streamly.Benchmark.Common.Handle
4545#ifdef INSPECTION
4646import Streamly.Internal.Data.MutByteArray (Unbox)
4747import Streamly.Internal.Data.Stream (Step(..))
48+import qualified Streamly.Internal.Data.Array as Array
49+import qualified Streamly.Internal.Data.MutArray as MutArray
4850import qualified Streamly.Internal.Data.Fold as Fold
4951import qualified Streamly.Internal.Data.Tuple.Strict as Strict
50−import qualified Streamly.Internal.Data.Array as Array
5152
5253import Test.Inspection
5354#endif
@@ -223,7 +224,7 @@ inspect $ 'copyStreamLatin1' `hasNoType` ''Step
223224inspect $ 'copyStreamLatin1' `hasNoType` ''Unfold.ConcatState -- Handle.read/UF.many
224225
225226inspect $ 'copyStreamLatin1' `hasNoType` ''Fold.Step
226−inspect $ 'copyStreamLatin1' `hasNoType` ''Array.ArrayUnsafe -- Handle.write/writeNUnsafe
227+inspect $ 'copyStreamLatin1' `hasNoType` ''MutArray.ArrayUnsafe -- Handle.write/writeNUnsafe
227228 -- Handle.read/Array.read
228229inspect $ 'copyStreamLatin1' `hasNoType` ''Strict.Tuple3' -- Handle.write/chunksOf
229230#endif
@@ -244,7 +245,7 @@ inspect $ 'copyStreamLatin1 `hasNoType` ''Unfold.ConcatState -- Handle.read/UF.m
244245
245246inspect $ 'copyStreamLatin1 `hasNoType` ''Fold.ManyState
246247inspect $ 'copyStreamLatin1 `hasNoType` ''Fold.Step
247−inspect $ 'copyStreamLatin1 `hasNoType` ''Array.ArrayUnsafe -- Handle.write/writeNUnsafe
248+inspect $ 'copyStreamLatin1 `hasNoType` ''MutArray.ArrayUnsafe -- Handle.write/writeNUnsafe
248249 -- Handle.read/Array.read
249250inspect $ 'copyStreamLatin1 `hasNoType` ''Strict.Tuple3' -- Handle.write/chunksOf
250251#endif
configure+9−9
@@ -1,6 +1,6 @@
11#! /bin/sh
22# Guess values for system-dependent variables and create Makefiles.
3−# Generated by GNU Autoconf 2.71 for streamly 0.10.0.
3+# Generated by GNU Autoconf 2.71 for streamly 0.10.1.
44#
55# Report bugs to <streamly@composewell.com>.
66#
@@ -610,8 +610,8 @@ MAKEFLAGS=
610610# Identity of this package.
611611PACKAGE_NAME='streamly'
612612PACKAGE_TARNAME='streamly'
613−PACKAGE_VERSION='0.10.0'
614−PACKAGE_STRING='streamly 0.10.0'
613+PACKAGE_VERSION='0.10.1'
614+PACKAGE_STRING='streamly 0.10.1'
615615PACKAGE_BUGREPORT='streamly@composewell.com'
616616PACKAGE_URL='https://streamly.composewell.com'
617617
@@ -1224,7 +1224,7 @@ if test "$ac_init_help" = "long"; then
12241224 # Omit some internal or obsolete options to make the list less imposing.
12251225 # This message is too long to be a string in the A/UX 3.1 sh.
12261226 cat <<_ACEOF
1227−\`configure' configures streamly 0.10.0 to adapt to many kinds of systems.
1227+\`configure' configures streamly 0.10.1 to adapt to many kinds of systems.
12281228
12291229Usage: $0 [OPTION]... [VAR=VALUE]...
12301230
@@ -1286,7 +1286,7 @@ fi
12861286
12871287if test -n "$ac_init_help"; then
12881288 case $ac_init_help in
1289− short | recursive ) echo "Configuration of streamly 0.10.0:";;
1289+ short | recursive ) echo "Configuration of streamly 0.10.1:";;
12901290 esac
12911291 cat <<\_ACEOF
12921292
@@ -1372,7 +1372,7 @@ fi
13721372test -n "$ac_init_help" && exit $ac_status
13731373if $ac_init_version; then
13741374 cat <<\_ACEOF
1375−streamly configure 0.10.0
1375+streamly configure 0.10.1
13761376generated by GNU Autoconf 2.71
13771377
13781378Copyright (C) 2021 Free Software Foundation, Inc.
@@ -1500,7 +1500,7 @@ cat >config.log <<_ACEOF
15001500This file contains any messages produced by compilers while
15011501running configure, to aid debugging if configure makes a mistake.
15021502
1503−It was created by streamly $as_me 0.10.0, which was
1503+It was created by streamly $as_me 0.10.1, which was
15041504generated by GNU Autoconf 2.71. Invocation command line was
15051505
15061506 $ $0$ac_configure_args_raw
@@ -3800,7 +3800,7 @@ cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1
38003800# report actual input values of CONFIG_FILES etc. instead of their
38013801# values after options handling.
38023802ac_log="
3803−This file was extended by streamly $as_me 0.10.0, which was
3803+This file was extended by streamly $as_me 0.10.1, which was
38043804generated by GNU Autoconf 2.71. Invocation command line was
38053805
38063806 CONFIG_FILES = $CONFIG_FILES
@@ -3856,7 +3856,7 @@ ac_cs_config_escaped=`printf "%s\n" "$ac_cs_config" | sed "s/^ //; s/'/'\\\\\\\\
38563856cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1
38573857ac_cs_config='$ac_cs_config_escaped'
38583858ac_cs_version="\\
3859−streamly config.status 0.10.0
3859+streamly config.status 0.10.1
38603860configured by $0, generated by GNU Autoconf 2.71,
38613861 with options \\"\$ac_cs_config\\"
38623862
configure.ac+1−1
@@ -3,7 +3,7 @@
33# See https://www.gnu.org/software/autoconf/manual/autoconf.html for help on
44# the macros used in this file.
55
6−AC_INIT([streamly], [0.10.0], [streamly@composewell.com], [streamly], [https://streamly.composewell.com])
6+AC_INIT([streamly], [0.10.1], [streamly@composewell.com], [streamly], [https://streamly.composewell.com])
77
88# To suppress "WARNING: unrecognized options: --with-compiler"
99AC_ARG_WITH([compiler], [GHC])
core/configure+9−9
@@ -1,6 +1,6 @@
11#! /bin/sh
22# Guess values for system-dependent variables and create Makefiles.
3−# Generated by GNU Autoconf 2.71 for streamly-core 0.2.1.
3+# Generated by GNU Autoconf 2.71 for streamly-core 0.2.2.
44#
55# Report bugs to <streamly@composewell.com>.
66#
@@ -610,8 +610,8 @@ MAKEFLAGS=
610610# Identity of this package.
611611PACKAGE_NAME='streamly-core'
612612PACKAGE_TARNAME='streamly-core'
613−PACKAGE_VERSION='0.2.1'
614−PACKAGE_STRING='streamly-core 0.2.1'
613+PACKAGE_VERSION='0.2.2'
614+PACKAGE_STRING='streamly-core 0.2.2'
615615PACKAGE_BUGREPORT='streamly@composewell.com'
616616PACKAGE_URL='https://streamly.composewell.com'
617617
@@ -1256,7 +1256,7 @@ if test "$ac_init_help" = "long"; then
12561256 # Omit some internal or obsolete options to make the list less imposing.
12571257 # This message is too long to be a string in the A/UX 3.1 sh.
12581258 cat <<_ACEOF
1259−\`configure' configures streamly-core 0.2.1 to adapt to many kinds of systems.
1259+\`configure' configures streamly-core 0.2.2 to adapt to many kinds of systems.
12601260
12611261Usage: $0 [OPTION]... [VAR=VALUE]...
12621262
@@ -1318,7 +1318,7 @@ fi
13181318
13191319if test -n "$ac_init_help"; then
13201320 case $ac_init_help in
1321− short | recursive ) echo "Configuration of streamly-core 0.2.1:";;
1321+ short | recursive ) echo "Configuration of streamly-core 0.2.2:";;
13221322 esac
13231323 cat <<\_ACEOF
13241324
@@ -1404,7 +1404,7 @@ fi
14041404test -n "$ac_init_help" && exit $ac_status
14051405if $ac_init_version; then
14061406 cat <<\_ACEOF
1407−streamly-core configure 0.2.1
1407+streamly-core configure 0.2.2
14081408generated by GNU Autoconf 2.71
14091409
14101410Copyright (C) 2021 Free Software Foundation, Inc.
@@ -1622,7 +1622,7 @@ cat >config.log <<_ACEOF
16221622This file contains any messages produced by compilers while
16231623running configure, to aid debugging if configure makes a mistake.
16241624
1625−It was created by streamly-core $as_me 0.2.1, which was
1625+It was created by streamly-core $as_me 0.2.2, which was
16261626generated by GNU Autoconf 2.71. Invocation command line was
16271627
16281628 $ $0$ac_configure_args_raw
@@ -3832,7 +3832,7 @@ cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1
38323832# report actual input values of CONFIG_FILES etc. instead of their
38333833# values after options handling.
38343834ac_log="
3835−This file was extended by streamly-core $as_me 0.2.1, which was
3835+This file was extended by streamly-core $as_me 0.2.2, which was
38363836generated by GNU Autoconf 2.71. Invocation command line was
38373837
38383838 CONFIG_FILES = $CONFIG_FILES
@@ -3888,7 +3888,7 @@ ac_cs_config_escaped=`printf "%s\n" "$ac_cs_config" | sed "s/^ //; s/'/'\\\\\\\\
38883888cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1
38893889ac_cs_config='$ac_cs_config_escaped'
38903890ac_cs_version="\\
3891−streamly-core config.status 0.2.1
3891+streamly-core config.status 0.2.2
38923892configured by $0, generated by GNU Autoconf 2.71,
38933893 with options \\"\$ac_cs_config\\"
38943894
core/configure.ac+1−1
@@ -3,7 +3,7 @@
33# See https://www.gnu.org/software/autoconf/manual/autoconf.html for help on
44# the macros used in this file.
55
6−AC_INIT([streamly-core], [0.2.1], [streamly@composewell.com], [streamly-core], [https://streamly.composewell.com])
6+AC_INIT([streamly-core], [0.2.2], [streamly@composewell.com], [streamly-core], [https://streamly.composewell.com])
77
88# To suppress "WARNING: unrecognized options: --with-compiler"
99AC_ARG_WITH([compiler], [GHC])
core/docs/ApiChangelogs/0.2.0-0.2.2.txtnew file+306
@@ -0,0 +1,306 @@
1+---------------------------------
2+API Annotations
3+---------------------------------
4+
5+[A] : Added
6+[R] : Removed
7+[C] : Changed
8+[O] : Old definition
9+[N] : New definition
10+[D] : Deprecated
11+
12+---------------------------------
13+API diff
14+---------------------------------
15+
16+[C] Streamly.Data.Stream
17+ [A] (FixityR,5)
18+ [A] (FixityR,5)
19+[C] Streamly.Data.MutArray.Generic
20+ [A] emptyOf :: MonadIO m => Int -> m (MutArray a)
21+ [A] createOf :: MonadIO m => Int -> Fold m a (MutArray a)
22+ [A] create :: MonadIO m => Fold m a (MutArray a)
23+[C] Streamly.Data.MutArray
24+ [A] pinnedEmptyOf :: forall m a. (MonadIO m, Unbox a) => Int -> m (MutArray a)
25+ [A] emptyOf :: (MonadIO m, Unbox a) => Int -> m (MutArray a)
26+ [A] createOf :: forall m a. (MonadIO m, Unbox a) => Int -> Fold m a (MutArray a)
27+ [A] create :: forall m a. (MonadIO m, Unbox a) => Fold m a (MutArray a)
28+ [A] appendN :: forall m a. (MonadIO m, Unbox a) => Int -> m (MutArray a) -> Fold m a (MutArray a)
29+ [A] append :: forall m a. (MonadIO m, Unbox a) => m (MutArray a) -> Fold m a (MutArray a)
30+[C] Streamly.Data.Array.Generic
31+ [A] createOf :: MonadIO m => Int -> Fold m a (Array a)
32+ [A] create :: MonadIO m => Fold m a (Array a)
33+[C] Streamly.Data.Array
34+ [A] class Serialize a
35+ [A] serializeAt :: Serialize a => Int -> MutByteArray -> a -> IO Int
36+ [A] pinnedSerialize :: Serialize a => a -> Array Word8
37+ [A] deserializeAt :: Serialize a => Int -> MutByteArray -> Int -> IO (Int, a)
38+ [A] deserialize :: Serialize a => Array Word8 -> a
39+ [A] createOf :: forall m a. (MonadIO m, Unbox a) => Int -> Fold m a (Array a)
40+ [A] create :: forall m a. (MonadIO m, Unbox a) => Fold m a (Array a)
41+ [A] addSizeTo :: Serialize a => Int -> a -> Int
42+
43+---------------------------------
44+Internal API diff
45+---------------------------------
46+
47+[C] Streamly.Internal.Unicode.Stream
48+ [A] encodeUtf16le' :: Stream m Char -> Stream m Word16
49+ [A] decodeUtf16le' :: Stream m Word16 -> Stream m Char
50+[A] Streamly.Internal.FileSystem.Path
51+ [A] class IsPath a
52+ [A] Rel
53+ [A] File
54+ [A] Dir
55+ [A] Abs
56+ [A] Streamly.Internal.FileSystem.Path.IsPath
57+ [A] instance Streamly.Internal.FileSystem.Path.IsPath Streamly.Internal.FileSystem.Path.Path
58+ [A] instance Streamly.Internal.FileSystem.Path.IsPath (Streamly.Internal.FileSystem.Path.Rel Streamly.Internal.FileSystem.Path.Path)
59+ [A] instance Streamly.Internal.FileSystem.Path.IsPath (Streamly.Internal.FileSystem.Path.Rel (Streamly.Internal.FileSystem.Path.File Streamly.Internal.FileSystem.Path.Path))
60+ [A] instance Streamly.Internal.FileSystem.Path.IsPath (Streamly.Internal.FileSystem.Path.Rel (Streamly.Internal.FileSystem.Path.Dir Streamly.Internal.FileSystem.Path.Path))
61+ [A] instance Streamly.Internal.FileSystem.Path.IsPath (Streamly.Internal.FileSystem.Path.File Streamly.Internal.FileSystem.Path.Path)
62+ [A] instance Streamly.Internal.FileSystem.Path.IsPath (Streamly.Internal.FileSystem.Path.Dir Streamly.Internal.FileSystem.Path.Path)
63+ [A] instance Streamly.Internal.FileSystem.Path.IsPath (Streamly.Internal.FileSystem.Path.Abs Streamly.Internal.FileSystem.Path.Path)
64+ [A] instance Streamly.Internal.FileSystem.Path.IsPath (Streamly.Internal.FileSystem.Path.Abs (Streamly.Internal.FileSystem.Path.File Streamly.Internal.FileSystem.Path.Path))
65+ [A] instance Streamly.Internal.FileSystem.Path.IsPath (Streamly.Internal.FileSystem.Path.Abs (Streamly.Internal.FileSystem.Path.Dir Streamly.Internal.FileSystem.Path.Path))
66+ [A] GHC.Show.Show
67+ [A] instance GHC.Show.Show Streamly.Internal.FileSystem.Path.PathException
68+ [A] GHC.Exception.Type.Exception
69+ [A] instance GHC.Exception.Type.Exception Streamly.Internal.FileSystem.Path.PathException
70+ [A] GHC.Classes.Eq
71+ [A] instance GHC.Classes.Eq Streamly.Internal.FileSystem.Path.PathException
72+ [A] Path
73+ [A] Path :: Array Word8 -> Path
74+ [A] toString :: Path -> [Char]
75+ [A] toPath :: IsPath a => a -> Path
76+ [A] toChunk :: Path -> Array Word8
77+ [A] toChars :: Monad m => Path -> Stream m Char
78+ [A] relfile :: QuasiQuoter
79+ [A] reldir :: QuasiQuoter
80+ [A] rel :: QuasiQuoter
81+ [A] primarySeparator :: Char
82+ [A] path :: QuasiQuoter
83+ [A] mkRelFile :: String -> Q Exp
84+ [A] mkRelDir :: String -> Q Exp
85+ [A] mkRel :: String -> Q Exp
86+ [A] mkPath :: String -> Q Exp
87+ [A] mkFile :: String -> Q Exp
88+ [A] mkDir :: String -> Q Exp
89+ [A] mkAbsFile :: String -> Q Exp
90+ [A] mkAbsDir :: String -> Q Exp
91+ [A] mkAbs :: String -> Q Exp
92+ [A] isSeparator :: Char -> Bool
93+ [A] fromString :: MonadThrow m => [Char] -> m Path
94+ [A] fromPathUnsafe :: IsPath a => Path -> a
95+ [A] fromPath :: (IsPath a, MonadThrow m) => Path -> m a
96+ [A] fromChunkUnsafe :: Array Word8 -> Path
97+ [A] fromChunk :: MonadThrow m => Array Word8 -> m Path
98+ [A] fromChars :: MonadThrow m => Stream Identity Char -> m Path
99+ [A] file :: QuasiQuoter
100+ [A] extendPath :: Path -> Path -> Path
101+ [A] extendDir :: (IsPath (a (Dir Path)), IsPath b, IsPath (a b)) => a (Dir Path) -> Rel b -> a b
102+ [A] dir :: QuasiQuoter
103+ [A] adaptPath :: (MonadThrow m, IsPath a, IsPath b) => a -> m b
104+ [A] absfile :: QuasiQuoter
105+ [A] absdir :: QuasiQuoter
106+ [A] abs :: QuasiQuoter
107+[C] Streamly.Internal.Data.Stream
108+ [A] (FixityR,5)
109+ [A] (FixityR,5)
110+ [C] splitInnerBySuffix
111+ [O] splitInnerBySuffix :: (Monad m, Eq (f a), Monoid (f a)) => (f a -> m (f a, Maybe (f a))) -> (f a -> f a -> m (f a)) -> Stream m (f a) -> Stream m (f a)
112+ [N] splitInnerBySuffix :: Monad m => (f a -> Bool) -> (f a -> m (f a, Maybe (f a))) -> (f a -> f a -> m (f a)) -> Stream m (f a) -> Stream m (f a)
113+ [D] sliceOnSuffix :: Monad m => (a -> Bool) -> Stream m a -> Stream m (Int, Int)
114+ [A] indexOnSuffix :: Monad m => (a -> Bool) -> Stream m a -> Stream m (Int, Int)
115+[C] Streamly.Internal.Data.MutByteArray
116+ [A] unsafePinnedAsPtr :: MonadIO m => MutByteArray -> (Ptr a -> m b) -> m b
117+ [A] unsafeAsPtr :: MonadIO m => MutByteArray -> (Ptr a -> m b) -> m b
118+ [D] nil :: MutByteArray
119+ [A] empty :: MutByteArray
120+ [D] asPtrUnsafe :: MonadIO m => MutByteArray -> (Ptr a -> m b) -> m b
121+[D] Streamly.Internal.Data.MutArray.Stream
122+ [D] writeChunks :: (MonadIO m, Unbox a) => Int -> Fold m a (StreamK n (MutArray a))
123+ [D] fromArrayStreamK :: (Unbox a, MonadIO m) => StreamK m (MutArray a) -> m (MutArray a)
124+ [D] flattenArraysRev :: forall m a. (MonadIO m, Unbox a) => Stream m (MutArray a) -> Stream m a
125+ [D] flattenArrays :: forall m a. (MonadIO m, Unbox a) => Stream m (MutArray a) -> Stream m a
126+[C] Streamly.Internal.Data.MutArray.Generic
127+ [D] writeWith :: MonadIO m => Int -> Fold m a (MutArray a)
128+ [D] writeNUnsafe :: MonadIO m => Int -> Fold m a (MutArray a)
129+ [A] unsafeCreateOf :: MonadIO m => Int -> Fold m a (MutArray a)
130+ [A] emptyOf :: MonadIO m => Int -> m (MutArray a)
131+ [A] createWith :: MonadIO m => Int -> Fold m a (MutArray a)
132+ [A] createOf :: MonadIO m => Int -> Fold m a (MutArray a)
133+ [A] create :: MonadIO m => Fold m a (MutArray a)
134+[C] Streamly.Internal.Data.MutArray
135+ [A] SpliceState
136+ [A] SpliceYielding :: arr -> SpliceState s arr -> SpliceState s arr
137+ [A] SpliceInitial :: s -> SpliceState s arr
138+ [A] SpliceFinish :: SpliceState s arr
139+ [A] SpliceBuffering :: s -> arr -> SpliceState s arr
140+ [R] MutByteArray
141+ [D] writeWith :: forall m a. (MonadIO m, Unbox a) => Int -> Fold m a (MutArray a)
142+ [D] writeRevN :: forall m a. (MonadIO m, Unbox a) => Int -> Fold m a (MutArray a)
143+ [D] writeNWithUnsafe :: forall m a. (MonadIO m, Unbox a) => (Int -> m (MutArray a)) -> Int -> Fold m a (MutArray a)
144+ [D] writeNUnsafe :: forall m a. (MonadIO m, Unbox a) => Int -> Fold m a (MutArray a)
145+ [D] writeChunks :: (MonadIO m, Unbox a) => Int -> Fold m a (StreamK n (MutArray a))
146+ [D] writeAppendWith :: forall m a. (MonadIO m, Unbox a) => (Int -> Int) -> m (MutArray a) -> Fold m a (MutArray a)
147+ [D] writeAppendNUnsafe :: forall m a. (MonadIO m, Unbox a) => Int -> m (MutArray a) -> Fold m a (MutArray a)
148+ [A] unsafePinnedCreateOf :: forall m a. (MonadIO m, Unbox a) => Int -> Fold m a (MutArray a)
149+ [A] unsafePinnedAsPtr :: MonadIO m => MutArray a -> (Ptr a -> m b) -> m b
150+ [A] unsafeCreateOfWith :: forall m a. (MonadIO m, Unbox a) => (Int -> m (MutArray a)) -> Int -> Fold m a (MutArray a)
151+ [A] unsafeCreateOf :: forall m a. (MonadIO m, Unbox a) => Int -> Fold m a (MutArray a)
152+ [A] unsafeAsPtr :: MonadIO m => MutArray a -> (Ptr a -> m b) -> m b
153+ [A] unsafeAppendN :: forall m a. (MonadIO m, Unbox a) => Int -> m (MutArray a) -> Fold m a (MutArray a)
154+ [A] toStreamWith :: forall m a. (Monad m, Unbox a) => (forall b. IO b -> m b) -> MutArray a -> Stream m a
155+ [A] toStreamRevWith :: forall m a. (Monad m, Unbox a) => (forall b. IO b -> m b) -> MutArray a -> Stream m a
156+ [R] toStreamDWith :: forall m a. (Monad m, Unbox a) => (forall b. IO b -> m b) -> MutArray a -> Stream m a
157+ [R] toStreamDRevWith :: forall m a. (Monad m, Unbox a) => (forall b. IO b -> m b) -> MutArray a -> Stream m a
158+ [A] slicerFromLen :: forall m a. (Monad m, Unbox a) => Int -> Int -> Unfold m (MutArray a) (MutArray a)
159+ [A] sliceIndexerFromLen :: forall m a. (Monad m, Unbox a) => Int -> Int -> Unfold m (MutArray a) (Int, Int)
160+ [A] revCreateOf :: forall m a. (MonadIO m, Unbox a) => Int -> Fold m a (MutArray a)
161+ [D] resizeExp :: forall m a. (MonadIO m, Unbox a) => Int -> MutArray a -> m (MutArray a)
162+ [D] resize :: forall m a. (MonadIO m, Unbox a) => Int -> MutArray a -> m (MutArray a)
163+ [A] pokeSkipUnsafe :: Int -> MutArray Word8 -> MutArray Word8
164+ [A] pokeAppendMay :: forall m a. (MonadIO m, Unbox a) => MutArray Word8 -> a -> m (Maybe (MutArray Word8))
165+ [A] pokeAppend :: forall m a. (MonadIO m, Unbox a) => MutArray Word8 -> a -> m (MutArray Word8)
166+ [D] pinnedWriteNUnsafe :: forall m a. (MonadIO m, Unbox a) => Int -> Fold m a (MutArray a)
167+ [D] pinnedWriteN :: forall m a. (MonadIO m, Unbox a) => Int -> Fold m a (MutArray a)
168+ [D] pinnedWrite :: forall m a. (MonadIO m, Unbox a) => Fold m a (MutArray a)
169+ [D] pinnedNewBytes :: MonadIO m => Int -> m (MutArray a)
170+ [A] pinnedEmptyOf :: forall m a. (MonadIO m, Unbox a) => Int -> m (MutArray a)
171+ [A] pinnedCreateOf :: forall m a. (MonadIO m, Unbox a) => Int -> Fold m a (MutArray a)
172+ [A] pinnedCreate :: forall m a. (MonadIO m, Unbox a) => Fold m a (MutArray a)
173+ [A] pinnedCompactLE :: forall m a. (MonadIO m, Unbox a) => Int -> Stream m (MutArray a) -> Stream m (MutArray a)
174+ [A] peekUnconsUnsafe :: forall m a. (MonadIO m, Unbox a) => MutArray Word8 -> m (a, MutArray Word8)
175+ [A] peekUncons :: forall m a. (MonadIO m, Unbox a) => MutArray Word8 -> m (Maybe a, MutArray Word8)
176+ [A] peekSkipUnsafe :: Int -> MutArray Word8 -> MutArray Word8
177+ [A] pPinnedCompactLE :: forall m a. (MonadIO m, Unbox a) => Int -> Parser (MutArray a) m (MutArray a)
178+ [A] pCompactLE :: forall m a. (MonadIO m, Unbox a) => Int -> Parser (MutArray a) m (MutArray a)
179+ [D] nil :: MutArray a
180+ [A] lPinnedCompactGE :: forall m a. (MonadIO m, Unbox a) => Int -> Fold m (MutArray a) () -> Fold m (MutArray a) ()
181+ [A] lCompactGE :: forall m a. (MonadIO m, Unbox a) => Int -> Fold m (MutArray a) () -> Fold m (MutArray a) ()
182+ [A] indexReaderWith :: (Monad m, Unbox a) => (forall b. IO b -> m b) -> Stream m Int -> Unfold m (MutArray a) a
183+ [A] indexReader :: (MonadIO m, Unbox a) => Stream m Int -> Unfold m (MutArray a) a
184+ [A] growExp :: forall m a. (MonadIO m, Unbox a) => Int -> MutArray a -> m (MutArray a)
185+ [A] grow :: forall m a. (MonadIO m, Unbox a) => Int -> MutArray a -> m (MutArray a)
186+ [D] getSlicesFromLen :: forall m a. (Monad m, Unbox a) => Int -> Int -> Unfold m (MutArray a) (MutArray a)
187+ [R] getIndicesD :: (Monad m, Unbox a) => (forall b. IO b -> m b) -> Stream m Int -> Unfold m (MutArray a) a
188+ [D] getIndices :: (MonadIO m, Unbox a) => Stream m Int -> Unfold m (MutArray a) a
189+ [D] genSlicesFromLen :: forall m a. (Monad m, Unbox a) => Int -> Int -> Unfold m (MutArray a) (Int, Int)
190+ [A] fromStreamN :: forall m a. (MonadIO m, Unbox a) => Int -> Stream m a -> m (MutArray a)
191+ [D] fromStreamDN :: forall m a. (MonadIO m, Unbox a) => Int -> Stream m a -> m (MutArray a)
192+ [D] fromStreamD :: (MonadIO m, Unbox a) => Stream m a -> m (MutArray a)
193+ [A] fromPureStreamN :: (MonadIO m, Unbox a) => Int -> Stream Identity a -> m (MutArray a)
194+ [A] fromPtrN :: MonadIO m => Int -> Ptr Word8 -> m (MutArray Word8)
195+ [A] fromChunksRealloced :: forall m a. (MonadIO m, Unbox a) => Stream m (MutArray a) -> m (MutArray a)
196+ [A] fromChunksK :: (Unbox a, MonadIO m) => StreamK m (MutArray a) -> m (MutArray a)
197+ [A] fromByteStr# :: MonadIO m => Addr# -> m (MutArray Word8)
198+ [D] fromArrayStreamK :: (Unbox a, MonadIO m) => StreamK m (MutArray a) -> m (MutArray a)
199+ [D] flattenArraysRev :: forall m a. (MonadIO m, Unbox a) => Stream m (MutArray a) -> Stream m a
200+ [D] flattenArrays :: forall m a. (MonadIO m, Unbox a) => Stream m (MutArray a) -> Stream m a
201+ [A] fPinnedCompactGE :: forall m a. (MonadIO m, Unbox a) => Int -> Fold m (MutArray a) (MutArray a)
202+ [A] fCompactGE :: forall m a. (MonadIO m, Unbox a) => Int -> Fold m (MutArray a) (MutArray a)
203+ [A] emptyOf :: (MonadIO m, Unbox a) => Int -> m (MutArray a)
204+ [A] empty :: MutArray a
205+ [A] createWith :: forall m a. (MonadIO m, Unbox a) => Int -> Fold m a (MutArray a)
206+ [A] createOfWith :: forall m a. (MonadIO m, Unbox a) => (Int -> m (MutArray a)) -> Int -> Fold m a (MutArray a)
207+ [A] createOf :: forall m a. (MonadIO m, Unbox a) => Int -> Fold m a (MutArray a)
208+ [A] create :: forall m a. (MonadIO m, Unbox a) => Fold m a (MutArray a)
209+ [A] concatWith :: forall m a. (Monad m, Unbox a) => (forall b. IO b -> m b) -> Stream m (MutArray a) -> Stream m a
210+ [A] concatRevWith :: forall m a. (Monad m, Unbox a) => (forall b. IO b -> m b) -> Stream m (MutArray a) -> Stream m a
211+ [A] concatRev :: forall m a. (MonadIO m, Unbox a) => Stream m (MutArray a) -> Stream m a
212+ [A] concat :: forall m a. (MonadIO m, Unbox a) => Stream m (MutArray a) -> Stream m a
213+ [A] compactOnByteSuffix :: MonadIO m => Word8 -> Stream m (MutArray Word8) -> Stream m (MutArray Word8)
214+ [A] compactOnByte :: MonadIO m => Word8 -> Stream m (MutArray Word8) -> Stream m (MutArray Word8)
215+ [A] compactLeAs :: forall m a. (MonadIO m, Unbox a) => PinnedState -> Int -> Stream m (MutArray a) -> Stream m (MutArray a)
216+ [A] compactLE :: (MonadIO m, Unbox a) => Int -> Stream m (MutArray a) -> Stream m (MutArray a)
217+ [A] compactGE :: (MonadIO m, Unbox a) => Int -> Stream m (MutArray a) -> Stream m (MutArray a)
218+ [A] compactEQ :: Int -> Stream m (MutArray a) -> Stream m (MutArray a)
219+ [D] cmp :: MonadIO m => MutArray a -> MutArray a -> m Ordering
220+ [A] byteEq :: MonadIO m => MutArray a -> MutArray a -> m Bool
221+ [A] byteCmp :: MonadIO m => MutArray a -> MutArray a -> m Ordering
222+ [A] buildChunks :: (MonadIO m, Unbox a) => Int -> Fold m a (StreamK n (MutArray a))
223+ [D] asPtrUnsafe :: MonadIO m => MutArray a -> (Ptr a -> m b) -> m b
224+ [A] appendWith :: forall m a. (MonadIO m, Unbox a) => (Int -> Int) -> m (MutArray a) -> Fold m a (MutArray a)
225+ [A] appendN :: forall m a. (MonadIO m, Unbox a) => Int -> m (MutArray a) -> Fold m a (MutArray a)
226+ [A] append :: forall m a. (MonadIO m, Unbox a) => m (MutArray a) -> Fold m a (MutArray a)
227+[D] Streamly.Internal.Data.Array.Stream
228+ [C] interposeSuffix
229+ [O] interposeSuffix :: (Monad m, Unbox a) => a -> Stream m (Array a) -> Stream m a
230+ [N] interposeSuffix :: forall m a. (Monad m, Unbox a) => a -> Stream m (Array a) -> Stream m a
231+ [D] flattenArraysRev :: forall m a. (MonadIO m, Unbox a) => Stream m (Array a) -> Stream m a
232+ [D] flattenArrays :: forall m a. (MonadIO m, Unbox a) => Stream m (Array a) -> Stream m a
233+ [C] concatRev
234+ [O] concatRev :: (Monad m, Unbox a) => Stream m (Array a) -> Stream m a
235+ [N] concatRev :: forall m a. (Monad m, Unbox a) => Stream m (Array a) -> Stream m a
236+ [D] bufferChunks :: (MonadIO m, Unbox a) => Stream m a -> m (StreamK m (Array a))
237+[C] Streamly.Internal.Data.Array.Generic
238+ [A] createOf :: MonadIO m => Int -> Fold m a (Array a)
239+ [A] create :: MonadIO m => Fold m a (Array a)
240+[C] Streamly.Internal.Data.Array
241+ [R] ArrayUnsafe
242+ [D] writeWith :: forall m a. (MonadIO m, Unbox a) => Int -> Fold m a (Array a)
243+ [D] writeNUnsafe :: forall m a. (MonadIO m, Unbox a) => Int -> Fold m a (Array a)
244+ [A] unsafePinnedCreateOf :: forall m a. (MonadIO m, Unbox a) => Int -> Fold m a (Array a)
245+ [A] unsafePinnedAsPtr :: MonadIO m => Array a -> (Ptr a -> m b) -> m b
246+ [A] unsafeCreateOf :: forall m a. (MonadIO m, Unbox a) => Int -> Fold m a (Array a)
247+ [D] toStreamDRev :: forall m a. (Monad m, Unbox a) => Array a -> Stream m a
248+ [D] toStreamD :: forall m a. (Monad m, Unbox a) => Array a -> Stream m a
249+ [C] splice
250+ [O] splice :: (MonadIO m, Unbox a) => Array a -> Array a -> m (Array a)
251+ [N] splice :: MonadIO m => Array a -> Array a -> m (Array a)
252+ [A] slicerFromLen :: forall m a. (Monad m, Unbox a) => Int -> Int -> Unfold m (Array a) (Array a)
253+ [A] sliceIndexerFromLen :: forall m a. (Monad m, Unbox a) => Int -> Int -> Unfold m (Array a) (Int, Int)
254+ [D] pinnedWriteNUnsafe :: forall m a. (MonadIO m, Unbox a) => Int -> Fold m a (Array a)
255+ [D] pinnedWriteNAligned :: forall m a. (MonadIO m, Unbox a) => Int -> Int -> Fold m a (Array a)
256+ [D] pinnedWriteN :: forall m a. (MonadIO m, Unbox a) => Int -> Fold m a (Array a)
257+ [D] pinnedWrite :: forall m a. (MonadIO m, Unbox a) => Fold m a (Array a)
258+ [A] pinnedCreateOf :: forall m a. (MonadIO m, Unbox a) => Int -> Fold m a (Array a)
259+ [A] pinnedCreate :: forall m a. (MonadIO m, Unbox a) => Fold m a (Array a)
260+ [A] pinnedCompactLE :: (MonadIO m, Unbox a) => Int -> Stream m (Array a) -> Stream m (Array a)
261+ [A] parseBreakChunksK :: forall m a b. (MonadIO m, Unbox a) => Parser a m b -> StreamK m (Array a) -> m (Either ParseError b, StreamK m (Array a))
262+ [D] nil :: Array a
263+ [A] lPinnedCompactGE :: (MonadIO m, Unbox a) => Int -> Fold m (Array a) () -> Fold m (Array a) ()
264+ [A] lCompactGE :: (MonadIO m, Unbox a) => Int -> Fold m (Array a) () -> Fold m (Array a) ()
265+ [A] interposeSuffix :: forall m a. (Monad m, Unbox a) => a -> Stream m (Array a) -> Stream m a
266+ [A] interpose :: (Monad m, Unbox a) => a -> Stream m (Array a) -> Stream m a
267+ [A] intercalateSuffix :: (Monad m, Unbox a) => Array a -> Stream m (Array a) -> Stream m a
268+ [A] indexReaderFromThenTo :: Unfold m (Int, Int, Int, Array a) a
269+ [A] indexReader :: (Monad m, Unbox a) => Stream m Int -> Unfold m (Array a) a
270+ [A] indexFinder :: (a -> Bool) -> Unfold Identity (Array a) Int
271+ [D] getSlicesFromLen :: forall m a. (Monad m, Unbox a) => Int -> Int -> Unfold m (Array a) (Array a)
272+ [R] getIndicesFromThenTo :: Unfold m (Int, Int, Int, Array a) a
273+ [D] getIndices :: (Monad m, Unbox a) => Stream m Int -> Unfold m (Array a) a
274+ [D] genSlicesFromLen :: forall m a. (Monad m, Unbox a) => Int -> Int -> Unfold m (Array a) (Int, Int)
275+ [D] fromStreamDN :: forall m a. (MonadIO m, Unbox a) => Int -> Stream m a -> m (Array a)
276+ [D] fromStreamD :: forall m a. (MonadIO m, Unbox a) => Stream m a -> m (Array a)
277+ [A] fromPureStreamN :: Unbox a => Int -> Stream Identity a -> Array a
278+ [A] fromPtrN :: Int -> Ptr Word8 -> Array Word8
279+ [A] fromChunksK :: (MonadIO m, Unbox a) => StreamK m (Array a) -> m (Array a)
280+ [A] fromChunks :: (MonadIO m, Unbox a) => Stream m (Array a) -> m (Array a)
281+ [A] fromByteStr :: Ptr Word8 -> Array Word8
282+ [A] foldChunks :: (MonadIO m, Unbox a) => Fold m a b -> Stream m (Array a) -> m b
283+ [A] foldBreakChunksK :: forall m a b. (MonadIO m, Unbox a) => Fold m a b -> StreamK m (Array a) -> m (b, StreamK m (Array a))
284+ [A] foldBreakChunks :: forall m a b. (MonadIO m, Unbox a) => Fold m a b -> Stream m (Array a) -> m (b, Stream m (Array a))
285+ [D] flattenArraysRev :: forall m a. (MonadIO m, Unbox a) => Stream m (Array a) -> Stream m a
286+ [D] flattenArrays :: forall m a. (MonadIO m, Unbox a) => Stream m (Array a) -> Stream m a
287+ [C] findIndicesOf
288+ [O] findIndicesOf :: (a -> Bool) -> Unfold Identity (Array a) Int
289+ [N] findIndicesOf :: (a -> Bool) -> Array a -> Stream Identity Int
290+ [A] fPinnedCompactGE :: (MonadIO m, Unbox a) => Int -> Fold m (Array a) (Array a)
291+ [A] fCompactGE :: (MonadIO m, Unbox a) => Int -> Fold m (Array a) (Array a)
292+ [A] empty :: Array a
293+ [A] createWith :: forall m a. (MonadIO m, Unbox a) => Int -> Fold m a (Array a)
294+ [A] createOf :: forall m a. (MonadIO m, Unbox a) => Int -> Fold m a (Array a)
295+ [A] create :: forall m a. (MonadIO m, Unbox a) => Fold m a (Array a)
296+ [A] concatRev :: forall m a. (Monad m, Unbox a) => Stream m (Array a) -> Stream m a
297+ [A] concat :: (Monad m, Unbox a) => Stream m (Array a) -> Stream m a
298+ [A] compactOnByteSuffix :: MonadIO m => Word8 -> Stream m (Array Word8) -> Stream m (Array Word8)
299+ [A] compactOnByte :: MonadIO m => Word8 -> Stream m (Array Word8) -> Stream m (Array Word8)
300+ [A] compactLE :: (MonadIO m, Unbox a) => Int -> Stream m (Array a) -> Stream m (Array a)
301+ [A] compactGE :: (MonadIO m, Unbox a) => Int -> Stream m (Array a) -> Stream m (Array a)
302+ [A] byteEq :: Array a -> Array a -> Bool
303+ [A] byteCmp :: Array a -> Array a -> Ordering
304+ [A] buildChunks :: (MonadIO m, Unbox a) => Stream m a -> m (StreamK m (Array a))
305+ [D] bufferChunks :: (MonadIO m, Unbox a) => Stream m a -> m (StreamK m (Array a))
306+ [D] asPtrUnsafe :: MonadIO m => Array a -> (Ptr a -> m b) -> m b
core/docs/Changelog.md+3−1
@@ -1,10 +1,12 @@
11# Changelog
22
3−## Unreleased
3+## 0.2.2 (Jan 2024)
44
55* Add fixities `infixr 5` for `cons` and `consM` functions.
66* Fix a bug in Array `Eq` instance when the type is a sum type with
77 differently sized constructors.
8+* lpackArraysChunksOf, compact, writeChunksWith, putChunksWith now take the
9+ buffer size in number of array elements instead of bytes.
810
911## 0.2.1 (Dec 2023)
1012
core/src/DocTestDataArray.hs+3
@@ -11,9 +11,12 @@
1111
1212>>> import qualified Streamly.Data.Array as Array
1313>>> import qualified Streamly.Data.Fold as Fold
14+>>> import qualified Streamly.Data.ParserK as ParserK
1415>>> import qualified Streamly.Data.Stream as Stream
16+>>> import qualified Streamly.Data.StreamK as StreamK
1517
1618For APIs that have not been released yet.
1719
1820>>> import qualified Streamly.Internal.Data.Array as Array
21+>>> import qualified Streamly.Internal.Data.Stream as Stream
1922-}
core/src/DocTestDataStream.hs+2
@@ -6,6 +6,7 @@
66>>> import Control.Monad.IO.Class (MonadIO (liftIO))
77>>> import Control.Monad.Trans.Class (lift)
88>>> import Control.Monad.Trans.Identity (runIdentityT)
9+>>> import Data.Char (isSpace)
910>>> import Data.Either (fromLeft, fromRight, isLeft, isRight, either)
1011>>> import Data.Maybe (fromJust, isJust)
1112>>> import Data.Function (fix, (&))
@@ -32,6 +33,7 @@ For APIs that have not been released yet.
3233>>> import qualified Streamly.Internal.Data.Fold as Fold
3334>>> import qualified Streamly.Internal.Data.Parser as Parser
3435>>> import qualified Streamly.Internal.Data.Stream as Stream
36+>>> import qualified Streamly.Internal.Data.StreamK as StreamK
3537>>> import qualified Streamly.Internal.Data.Unfold as Unfold
3638>>> import qualified Streamly.Internal.FileSystem.Dir as Dir
3739-}
core/src/DocTestFileSystemHandle.hs+1−1
@@ -8,7 +8,7 @@
88
99For APIs that have not been released yet.
1010
11−>>> import qualified Streamly.Internal.Data.Array as Array (writeNUnsafe)
11+>>> import qualified Streamly.Internal.Data.Array as Array (unsafeCreateOf)
1212>>> import qualified Streamly.Internal.Data.Unfold as Unfold (first)
1313>>> import qualified Streamly.Internal.FileSystem.Handle as Handle
1414>>> import qualified Streamly.Internal.System.IO as IO (defaultChunkSize)
core/src/Streamly/Data/Array.hs+30−27
@@ -31,42 +31,42 @@ module Streamly.Data.Array
3131 -- * The Array Type
3232 Array
3333
34+ -- * Pinning & Unpinning
35+ -- | Arrays are created unpinned by default unless pinned versions of
36+ -- creation APIs are used. Look for APIs with @pinned@ prefix in
37+ -- "Streamly.Internal.Data.Array" for some unreleased pinned creation APIs.
38+ -- If an array is to be sent to the OS without any further modification
39+ -- then it should be created pinned in the first place instead of pinning
40+ -- it later. Pinning an unpinned array has a copy overhead. OS interfacing
41+ -- APIs create a pinned array directly or convert an unpinned array to
42+ -- pinned array before sending it to the OS.
43+ , pin
44+ , unpin
45+ , isPinned
46+
3447 -- * Construction
3548 -- | When performance matters, the fastest way to generate an array is
36− -- 'writeN'. 'IsList' and 'IsString' instances can be
49+ -- 'createOf'. 'IsList' and 'IsString' instances can be
3750 -- used to conveniently construct arrays from literal values.
3851 -- 'OverloadedLists' extension or 'fromList' can be used to construct an
3952 -- array from a list literal. Similarly, 'OverloadedStrings' extension or
4053 -- 'fromList' can be used to construct an array from a string literal.
4154
42− -- Pure List APIs
55+ -- ** From Stream
56+ , createOf
57+ , create
58+ , writeLastN -- drop old (ring buffer)
59+
60+ -- ** From List
4361 , fromListN
4462 , fromList
4563
46− -- Monadic APIs
47− , writeN -- drop new
48− , write -- full buffer
49− , writeLastN -- drop old (ring buffer)
50−
51− -- * Conversion
64+ -- * To List
5265 -- 'GHC.Exts.toList' from "GHC.Exts" can be used to convert an array to a
5366 -- list.
5467 , toList
5568
56− -- * Pinning & Unpinning
57− -- | Arrays are created unpinned by default unless pinned versions of
58− -- creation APIs are used. Look for APIs with @pinned@ prefix in
59− -- "Streamly.Internal.Data.Array" for some unreleased pinned creation APIs.
60− -- If an array is to be sent to the OS without any further modification
61− -- then it should be created pinned in the first place instead of pinning
62− -- it later. Pinning an unpinned array has a copy overhead. OS interfacing
63− -- APIs create a pinned array directly or convert an unpinned array to
64− -- pinned array before sending it to the OS.
65− , pin
66− , unpin
67− , isPinned
68−
69− -- * Streams
69+ -- * To Stream
7070 , read
7171 , readRev
7272
@@ -90,6 +90,10 @@ module Streamly.Data.Array
9090 -- * Re-exports
9191 , Unbox (..)
9292 , Serialize(..)
93+
94+ -- * Deprecated
95+ , writeN -- drop new
96+ , write -- full buffer
9397 )
9498where
9599
@@ -122,7 +126,7 @@ import Prelude hiding (read, length)
122126--
123127-- Convert array to stream, transform, and fold back to array:
124128--
125−-- >>> amap f arr = Array.read arr & fmap f & Stream.fold Array.write
129+-- >>> amap f arr = Array.read arr & fmap f & Stream.fold (Array.createOf (Array.length arr))
126130-- >>> amap (+1) (Array.fromList [1,2,3::Int])
127131-- fromList [2,3,4]
128132--
@@ -149,12 +153,11 @@ import Prelude hiding (read, length)
149153-- See the 'fromPureStream' unreleased API to generate an array from an
150154-- Identity stream safely without using MonadIO constraint.
151155--
152−--
153156-- Note that 'Identity' streams can be generalized to IO streams:
154157--
155−-- >>> pure = Stream.fromList [1,2,3] :: Stream Identity Int
156−-- >>> generally = Stream.morphInner (return . runIdentity)
157−-- >>> Stream.fold Array.write (generally pure :: Stream IO Int)
158+-- >>> stream = Stream.fromList [1,2,3] :: Stream Identity Int
159+-- >>> pure2IO s = Stream.morphInner (return . runIdentity) s :: Stream IO Int
160+-- >>> Stream.fold Array.create (pure2IO stream)
158161-- fromList [1,2,3]
159162--
160163-- == Programming Tips
core/src/Streamly/Data/Array/Generic.hs+16−13
@@ -15,34 +15,37 @@ module Streamly.Data.Array.Generic
1515 ( Array
1616
1717 -- * Construction
18− , A.fromListN
19− , A.fromList
18+ , fromListN
19+ , fromList
2020
2121 -- MonadicAPIs
22− , A.writeN
23− , A.write
22+ , createOf
23+ , create
2424
2525 -- * Conversion
26− , A.toList
26+ , toList
2727
2828 -- * Streams
29− , A.read
30− , A.readRev
29+ , read
30+ , readRev
3131
3232 -- * Unfolds
33− , A.reader
33+ , reader
3434 -- , A.readerRev
3535
3636 -- * Random Access
37− , A.length
38− , A.getIndex
37+ , length
38+ , getIndex
3939
4040 -- -- * Folding Arrays
4141 -- , A.streamFold
4242 -- , A.fold
43+
44+ -- * Deprecated
45+ , writeN
46+ , write
4347 )
4448where
4549
46−import Streamly.Internal.Data.Array.Generic (Array)
47−
48−import qualified Streamly.Internal.Data.Array.Generic as A
50+import Streamly.Internal.Data.Array.Generic
51+import Prelude hiding (length, read)
core/src/Streamly/Data/MutArray.hs+12−6
@@ -32,14 +32,14 @@ module Streamly.Data.MutArray
3232 -- * Construction
3333
3434 -- Uninitialized Arrays
35− , new
36− , pinnedNew
35+ , emptyOf
36+ , pinnedEmptyOf
3737
3838 -- From containers
3939 , fromListN
4040 , fromList
41− , writeN -- drop new
42− , write -- full buffer
41+ , createOf
42+ , create
4343 -- writeLastN
4444
4545 -- * Pinning & Unpinning
@@ -51,8 +51,8 @@ module Streamly.Data.MutArray
5151 , snoc
5252
5353 -- * Appending streams
54− , writeAppendN -- XXX Rename to writeSnocN?
55− , writeAppend -- XXX Rename to writeSnoc?
54+ , appendN
55+ , append
5656
5757 -- * Inplace mutation
5858 , putIndex
@@ -88,6 +88,12 @@ module Streamly.Data.MutArray
8888
8989 -- * Deprecated
9090 , newPinned
91+ , new
92+ , pinnedNew
93+ , writeN
94+ , write
95+ , writeAppendN
96+ , writeAppend
9197 )
9298where
9399
core/src/Streamly/Data/MutArray/Generic.hs+8−3
@@ -23,11 +23,11 @@ module Streamly.Data.MutArray.Generic
2323 MutArray
2424
2525 -- * Construction
26− , new
26+ , emptyOf
2727 , fromListN
2828 , fromList
29− , writeN
30− , write
29+ , createOf
30+ , create
3131
3232 -- * Appending elements
3333 , snoc
@@ -56,6 +56,11 @@ module Streamly.Data.MutArray.Generic
5656
5757 -- * Size
5858 , length
59+
60+ -- * Deprecated
61+ , new
62+ , writeN
63+ , write
5964 )
6065where
6166
core/src/Streamly/Data/Stream.hs+129−20
@@ -70,6 +70,15 @@ module Streamly.Data.Stream
7070
7171 -- ** From Values
7272 -- | Generate a monadic stream from a seed value or values.
73+ --
74+ -- Non-empty pure streams can be cycled using repeat and unfold:
75+ --
76+ -- >>> cycle = Stream.unfoldMany Unfold.fromList . Stream.repeat
77+ --
78+ -- Non-empty StreamK can be cycled using append:
79+ --
80+ -- >>> cycle xs = let ys = xs `StreamK.append` ys in ys
81+ --
7382 , fromPure
7483 , fromEffect
7584 , repeat
@@ -212,21 +221,57 @@ module Streamly.Data.Stream
212221 -- operations like mapping a function over the stream.
213222 , foldrM
214223 , foldr
224+ -- foldr1
215225
216226 -- ** Specific Folds
217− -- | Usually you can use the folds in "Streamly.Data.Fold". However, some
218− -- folds that may be commonly used or may have an edge in performance in
219− -- some cases are provided here.
220− --
221− -- Useful idioms:
227+ -- | Streams are folded using folds in "Streamly.Data.Fold". Here are some
228+ -- idioms and equivalents of Data.List APIs using folds:
222229 --
223230 -- >>> foldlM' f a = Stream.fold (Fold.foldlM' f a)
224− -- >>> foldl1 f = Stream.fold (Fold.foldl1' f)
231+ -- >>> foldl1' f = Stream.fold (Fold.foldl1' f)
225232 -- >>> foldl' f a = Stream.fold (Fold.foldl' f a)
226233 -- >>> drain = Stream.fold Fold.drain
227234 -- >>> mapM_ f = Stream.fold (Fold.drainMapM f)
228235 -- >>> length = Stream.fold Fold.length
236+ -- >>> genericLength = Stream.fold Fold.lengthGeneric
229237 -- >>> head = Stream.fold Fold.one
238+ -- >>> last = Stream.fold Fold.latest
239+ -- >>> null = Stream.fold Fold.null
240+ -- >>> and = Stream.fold Fold.and
241+ -- >>> or = Stream.fold Fold.or
242+ -- >>> any p = Stream.fold (Fold.any p)
243+ -- >>> all p = Stream.fold (Fold.all p)
244+ -- >>> sum = Stream.fold Fold.sum
245+ -- >>> product = Stream.fold Fold.product
246+ -- >>> maximum = Stream.fold Fold.maximum
247+ -- >>> maximumBy cmp = Stream.fold (Fold.maximumBy cmp)
248+ -- >>> minimum = Stream.fold Fold.minimum
249+ -- >>> minimumBy cmp = Stream.fold (Fold.minimumBy cmp)
250+ -- >>> elem x = Stream.fold (Fold.elem x)
251+ -- >>> notElem x = Stream.fold (Fold.notElem x)
252+ -- >>> lookup x = Stream.fold (Fold.lookup x)
253+ -- >>> find p = Stream.fold (Fold.find p)
254+ -- >>> (!?) i = Stream.fold (Fold.index i)
255+ -- >>> genericIndex i = Stream.fold (Fold.indexGeneric i)
256+ -- >>> elemIndex x = Stream.fold (Fold.elemIndex x)
257+ -- >>> findIndex p = Stream.fold (Fold.findIndex p)
258+ --
259+ -- Some equivalents of Data.List APIs from the Stream module:
260+ --
261+ -- >>> head = fmap (fmap fst) . Stream.uncons
262+ -- >>> tail = fmap (fmap snd) . Stream.uncons
263+ -- >>> tail = Stream.tail -- unreleased API
264+ -- >>> init = Stream.init -- unreleased API
265+ --
266+ -- A Stream based toList fold implementation is provided below because it
267+ -- has a better performance compared to the fold.
268+
269+ -- Functions in Data.List, missing here:
270+ -- unsnoc = Stream.parseBreak (Parser.init Fold.toList)
271+ -- genericTake
272+ -- genericDrop
273+ -- genericSplitAt
274+ -- genericReplicate
230275 , toList
231276
232277 -- * Mapping
@@ -280,6 +325,15 @@ module Streamly.Data.Stream
280325 , scan
281326 , postscan
282327 -- XXX postscan1 can be implemented using Monoids or Refolds.
328+ -- The following scans from Data.List are not provided.
329+ -- XXX scanl
330+ -- XXX scanl1
331+ -- XXX scanr
332+ -- XXX scanr1
333+ -- XXX mapAccumL
334+ -- XXX mapAccumR
335+ -- XXX inits
336+ -- XXX tails
283337
284338 -- ** Specific scans
285339 -- Indexing can be considered as a special type of zipping where we zip a
@@ -288,6 +342,8 @@ module Streamly.Data.Stream
288342
289343 -- * Insertion
290344 -- | Add elements to the stream.
345+ --
346+ -- >>> insert = Stream.insertBy compare
291347
292348 -- Inserting elements is a special case of interleaving/merging streams.
293349 , insertBy
@@ -323,12 +379,15 @@ module Streamly.Data.Stream
323379 -- "Streamly.Internal.Data.Fold" can be used along with 'scanMaybe' to
324380 -- perform stateful filtering operations in general.
325381 --
326− -- Useful idioms:
382+ -- Idioms and equivalents of Data.List APIs:
327383 --
328384 -- >>> deleteBy cmp x = Stream.scanMaybe (Fold.deleteBy cmp x)
385+ -- >>> deleteBy = Stream.deleteBy -- unreleased API
386+ -- >>> delete = deleteBy (==)
329387 -- >>> findIndices p = Stream.scanMaybe (Fold.findIndices p)
330388 -- >>> elemIndices a = findIndices (== a)
331389 -- >>> uniq = Stream.scanMaybe (Fold.uniqBy (==))
390+ -- >>> partition p = Stream.fold (Fold.partition Fold.toList Fold.toList) . fmap (if p then Left else Right)
332391 , scanMaybe
333392 , take
334393 , takeWhile
@@ -336,6 +395,8 @@ module Streamly.Data.Stream
336395 , drop
337396 , dropWhile
338397 , dropWhileM
398+ -- XXX write to an array in reverse and then read in reverse
399+ -- > dropWhileEnd = reverse . dropWhile p . reverse
339400
340401 -- XXX These are available as scans in folds. We need to check the
341402 -- performance though. If these are common and we need convenient stream
@@ -372,6 +433,9 @@ module Streamly.Data.Stream
372433 -- >>> s = s1 `Stream.append` s2
373434
374435 -- ** Appending
436+ -- | Equivalent of Data.List append:
437+ --
438+ -- >>> (++) = Stream.append
375439 , append
376440
377441 -- ** Interleaving
@@ -382,8 +446,14 @@ module Streamly.Data.Stream
382446 , mergeByM
383447
384448 -- ** Zipping
449+ -- | Idioms and equivalents of Data.List APIs:
450+ --
451+ -- >>> zip = Stream.zipWith (,)
452+ -- >>> unzip = Stream.fold (Fold.unzip Fold.toList Fold.toList)
385453 , zipWith
386454 , zipWithM
455+ -- XXX zipWith3,4,5,6,7
456+ -- XXX unzip3,4,5,6,7
387457 -- , ZipStream (..)
388458
389459 -- ** Cross Product
@@ -416,48 +486,89 @@ module Streamly.Data.Stream
416486 -- One dimension loops are just a special case of nested loops. For
417487 -- example, 'concatMap' can degenerate to a simple map operation:
418488 --
419− -- > map f m = S.concatMap (\x -> S.fromPure (f x)) m
489+ -- >>> map f m = Stream.concatMap (\x -> Stream.fromPure (f x)) m
420490 --
421491 -- Similarly, 'concatMap' can perform filtering by mapping an element to a
422492 -- 'nil' stream:
423493 --
424− -- > filter p m = S.concatMap (\x -> if p x then S.fromPure x else S.nil) m
494+ -- >>> filter p m = Stream.concatMap (\x -> if p x then Stream.fromPure x else Stream.nil) m
495+ --
496+ -- Idioms and equivalents of Data.List APIs:
425497 --
498+ -- >>> concat = Stream.concatMap id
499+ -- >>> unlines = Stream.interposeSuffix '\n'
500+ -- >>> unlines = Stream.intercalateSuffix Unfold.fromList "\n"
501+ -- >>> unwords = Stream.interpose ' '
502+ -- >>> unwords = Stream.intercalate Unfold.fromList " "
426503
427504 , concatEffect
428505 , concatMap
429506 , concatMapM
430507
431508 -- * Repeated Fold
432− -- | Useful idioms:
509+ -- | Idioms and equivalents of Data.List APIs:
433510 --
434− -- >>> splitWithSuffix p f = Stream.foldMany (Fold.takeEndBy p f)
435− -- >>> splitOnSuffix p f = Stream.foldMany (Fold.takeEndBy_ p f)
436− -- >>> groupsBy eq f = Stream.parseMany (Parser.groupBy eq f)
437− -- >>> groupsByRolling eq f = Stream.parseMany (Parser.groupByRolling eq f)
438− -- >>> groupsOf n f = Stream.foldMany (Fold.take n f)
511+ -- >>> groupsOf n = Stream.foldMany (Fold.take n Fold.toList)
512+ -- >>> groupBy eq = Stream.groupsWhile eq Fold.toList
513+ -- >>> groupBy eq = Stream.parseMany (Parser.groupBy eq Fold.toList)
514+ -- >>> groupsByRolling eq = Stream.parseMany (Parser.groupByRolling eq Fold.toList)
515+ -- >>> groups = groupBy (==)
439516 , foldMany -- XXX Rename to foldRepeat
440517 , groupsOf
441518 , parseMany
442519
443520 -- * Splitting
521+ -- | Idioms and equivalents of Data.List APIs:
522+ --
523+ -- >>> splitWithSuffix p f = Stream.foldMany (Fold.takeEndBy p f)
524+ -- >>> splitOnSuffix p f = Stream.foldMany (Fold.takeEndBy_ p f)
525+ -- >>> lines = splitOnSuffix (== '\n')
526+ -- >>> words = Stream.wordsBy isSpace
527+ -- >>> splitAt n = Stream.fold (Fold.splitAt n Fold.toList Fold.toList)
528+ -- >>> span p = Parser.splitWith (,) (Parser.takeWhile p Fold.toList) (Parser.fromFold Fold.toList)
529+ -- >>> break p = span (not . p)
444530 , splitOn
445531 , wordsBy
446532
447533 -- * Buffered Operations
448534 -- | Operations that require buffering of the stream.
449535 -- Reverse is essentially a left fold followed by an unfold.
536+ --
537+ -- Idioms and equivalents of Data.List APIs:
538+ --
539+ -- >>> nub = Stream.fold Fold.toList . Stream.scanMaybe Fold.nub
540+ -- >>> nub = Stream.ordNub -- unreleased API
541+ -- >>> sortBy = StreamK.sortBy
542+ -- >>> sortOn f = StreamK.sortOn -- unreleased API
543+ -- >>> deleteFirstsBy = Stream.deleteFirstsBy -- unreleased
544+ -- >>> (\\) = Stream.deleteFirstsBy (==) -- unreleased
545+ -- >>> intersectBy = Stream.intersectBy -- unreleased
546+ -- >>> intersect = Stream.intersectBy (==) -- unreleased
547+ -- >>> unionBy = Stream.unionBy -- unreleased
548+ -- >>> union = Stream.unionBy (==) -- unreleased
549+ --
450550 , reverse
551+ -- XXX transpose: write the streams to arrays and then stream transposed.
552+ -- XXX subsequences
553+ -- XXX permutations
554+ -- , nub
555+ -- , ordNub
556+ -- , nubBy
451557
452558 -- * Multi-Stream folds
453559 -- | Operations that consume multiple streams at the same time.
454560 , eqBy
455561 , cmpBy
456562 , isPrefixOf
563+ , isInfixOf
564+ -- , isSuffixOf
565+ -- , isSuffixOfUnbox
457566 , isSubsequenceOf
458567
459568 -- trimming sequences
460− , stripPrefix
569+ -- , stripPrefix
570+ -- , stripSuffix
571+ -- , stripSuffixUnbox
461572
462573 -- Exceptions and resource management depend on the "exceptions" package
463574 -- XXX We can have IO Stream operations not depending on "exceptions"
@@ -521,10 +632,8 @@ where
521632import Streamly.Internal.Data.Stream
522633import Prelude
523634 hiding (filter, drop, dropWhile, take, takeWhile, zipWith, foldr,
524− foldl, map, mapM, mapM_, sequence, all, any, sum, product, elem,
525− notElem, maximum, minimum, head, last, tail, length, null,
526− reverse, iterate, init, and, or, lookup, foldr1, (!!),
527− scanl, scanl1, repeat, replicate, concatMap, span)
635+ mapM, sequence, reverse, iterate, foldr1, repeat, replicate,
636+ concatMap)
528637
529638import qualified Streamly.Internal.Data.Array.Type as Array
530639
core/src/Streamly/Internal/Data/Array.hs+530−91
@@ -2,7 +2,6 @@
22-- |
33-- Module : Streamly.Internal.Data.Array
44-- Copyright : (c) 2019 Composewell Technologies
5−--
65-- License : BSD3
76-- Maintainer : streamly@composewell.com
87-- Stability : experimental
@@ -23,30 +22,27 @@ module Streamly.Internal.Data.Array
2322 -- Monadic Folds
2423 , writeLastN
2524
26− -- * Unfolds
27− , reader
28− , readerUnsafe
29− , producer -- experimental
30−
3125 -- * Random Access
3226 -- , (!!)
3327 , getIndex
3428 , getIndexRev
35− , last -- XXX getIndexLast?
36− , getIndices
37− , getIndicesFromThenTo
29+ , last -- XXX getLastIndex?
3830 -- , getIndicesFrom -- read from a given position to the end of file
3931 -- , getIndicesUpto -- read from beginning up to the given position
4032 -- , getIndicesFromTo
4133 -- , getIndicesFromRev -- read from a given position to the beginning of file
4234 -- , getIndicesUptoRev -- read from end to the given position in file
35+ , indexReader
36+ , indexReaderFromThenTo
4337
4438 -- * Size
4539 , null
4640
4741 -- * Search
4842 , binarySearch
49− , findIndicesOf -- see splitOn, rename to indicesOf
43+ , findIndicesOf
44+ -- getIndicesOf
45+ , indexFinder -- see splitOn
5046 -- , findIndexOf
5147 -- , find
5248
@@ -70,6 +66,24 @@ module Streamly.Internal.Data.Array
7066 , streamFold
7167 , fold
7268
69+ -- * Stream of Arrays
70+
71+ -- XXX these are probably not very useful to have in this module as we can
72+ -- express these idiomatically using streams.
73+ , interpose
74+ , interposeSuffix
75+ , intercalateSuffix
76+
77+ , compactLE
78+ , pinnedCompactLE
79+ , compactOnByte
80+ , compactOnByteSuffix
81+
82+ , foldBreakChunks
83+ , foldChunks
84+ , foldBreakChunksK
85+ , parseBreakChunksK
86+
7387 -- * Serialization
7488 , encodeAs
7589 , serialize
@@ -79,6 +93,7 @@ module Streamly.Internal.Data.Array
7993 -- * Deprecated
8094 , genSlicesFromLen
8195 , getSlicesFromLen
96+ , getIndices
8297 )
8398where
8499
@@ -87,35 +102,42 @@ where
87102#include "ArrayMacros.h"
88103
89104import Control.Monad.IO.Class (MonadIO(..))
105+-- import Data.Bifunctor (first)
106+-- import Data.Either (fromRight)
90107import Data.Functor.Identity (Identity)
91108import Data.Proxy (Proxy(..))
92109import Data.Word (Word8)
93110import Foreign.C.String (CString)
94111import Foreign.Ptr (castPtr)
95112import Foreign.Storable (Storable)
113+import GHC.Types (SPEC(..))
96114import Streamly.Internal.Data.Unbox (Unbox(..))
97115import Prelude hiding (length, null, last, map, (!!), read, concat)
98116
99−import Streamly.Internal.Data.MutByteArray.Type (PinnedState(..))
117+import Streamly.Internal.Data.MutByteArray.Type (PinnedState(..), MutByteArray)
100118import Streamly.Internal.Data.Serialize.Type (Serialize)
101119import Streamly.Internal.Data.Fold.Type (Fold(..))
102−import Streamly.Internal.Data.Producer.Type (Producer(..))
103−import Streamly.Internal.Data.Stream (Stream)
104−import Streamly.Internal.Data.Tuple.Strict (Tuple3Fused'(..))
120+import Streamly.Internal.Data.Parser (Parser(..), Initial(..), ParseError(..))
121+import Streamly.Internal.Data.Stream (Stream(..))
122+import Streamly.Internal.Data.StreamK (StreamK)
123+import Streamly.Internal.Data.SVar.Type (adaptState, defState)
124+import Streamly.Internal.Data.Tuple.Strict (Tuple'(..), Tuple3Fused'(..))
105125import Streamly.Internal.Data.Unfold.Type (Unfold(..))
106126import Streamly.Internal.System.IO (unsafeInlineIO)
107127
128+import qualified Streamly.Internal.Data.Fold.Type as Fold
108129import qualified Streamly.Internal.Data.Serialize.Type as Serialize
109130import qualified Streamly.Internal.Data.MutByteArray.Type as MBA
110131import qualified Streamly.Internal.Data.MutArray as MA
111−import qualified Streamly.Internal.Data.Array.Type as A
112132import qualified Streamly.Internal.Data.Fold as FL
113−import qualified Streamly.Internal.Data.Producer.Type as Producer
114−import qualified Streamly.Internal.Data.Producer as Producer
115133import qualified Streamly.Internal.Data.Ring as RB
134+import qualified Streamly.Internal.Data.Parser as Parser
135+-- import qualified Streamly.Internal.Data.ParserK as ParserK
116136import qualified Streamly.Internal.Data.Stream as D
117137import qualified Streamly.Internal.Data.Stream as Stream
138+import qualified Streamly.Internal.Data.StreamK as StreamK
118139import qualified Streamly.Internal.Data.Unfold as Unfold
140+import qualified Prelude
119141
120142import Streamly.Internal.Data.Array.Type
121143
@@ -148,49 +170,6 @@ import Streamly.Internal.Data.Array.Type
148170-- Elimination
149171-------------------------------------------------------------------------------
150172
151−{-# INLINE_NORMAL producer #-}
152−producer :: forall m a. (Monad m, Unbox a) => Producer m (Array a) a
153−producer =
154− Producer.translate A.unsafeThaw A.unsafeFreeze
155− $ MA.producerWith (return . unsafeInlineIO)
156−
157−-- | Unfold an array into a stream.
158−--
159−{-# INLINE_NORMAL reader #-}
160−reader :: forall m a. (Monad m, Unbox a) => Unfold m (Array a) a
161−reader = Producer.simplify producer
162−
163−-- | Unfold an array into a stream, does not check the end of the array, the
164−-- user is responsible for terminating the stream within the array bounds. For
165−-- high performance application where the end condition can be determined by
166−-- a terminating fold.
167−--
168−-- Written in the hope that it may be faster than "read", however, in the case
169−-- for which this was written, "read" proves to be faster even though the core
170−-- generated with unsafeRead looks simpler.
171−--
172−-- /Pre-release/
173−--
174−{-# INLINE_NORMAL readerUnsafe #-}
175−readerUnsafe :: forall m a. (Monad m, Unbox a) => Unfold m (Array a) a
176−readerUnsafe = Unfold step inject
177− where
178−
179− inject (Array contents start end) =
180− return (ArrayUnsafe contents end start)
181−
182− {-# INLINE_LATE step #-}
183− step (ArrayUnsafe contents end p) = do
184− -- unsafeInlineIO allows us to run this in Identity monad for pure
185− -- toList/foldr case which makes them much faster due to not
186− -- accumulating the list and fusing better with the pure consumers.
187− --
188− -- This should be safe as the array contents are guaranteed to be
189− -- evaluated/written to before we peek at them.
190− let !x = unsafeInlineIO $ peekAt p contents
191− let !p1 = INDEX_NEXT(p,a)
192− return $ D.Yield x (ArrayUnsafe contents end p1)
193−
194173-- |
195174--
196175-- >>> null arr = Array.byteLength arr == 0
@@ -198,7 +177,7 @@ readerUnsafe = Unfold step inject
198177-- /Pre-release/
199178{-# INLINE null #-}
200179null :: Array a -> Bool
201−null arr = A.byteLength arr == 0
180+null arr = byteLength arr == 0
202181
203182-- | Like 'getIndex' but indexes the array in reverse from the end.
204183--
@@ -226,6 +205,8 @@ last = getIndexRev 0
226205-- Folds with Array as the container
227206-------------------------------------------------------------------------------
228207
208+-- XXX We should generate this from Ring.
209+
229210-- | @writeLastN n@ folds a maximum of @n@ elements from the end of the input
230211-- stream to an 'Array'.
231212--
@@ -234,7 +215,7 @@ writeLastN ::
234215 (Storable a, Unbox a, MonadIO m) => Int -> Fold m a (Array a)
235216writeLastN n
236217 | n <= 0 = fmap (const mempty) FL.drain
237− | otherwise = A.unsafeFreeze <$> Fold step initial done done
218+ | otherwise = unsafeFreeze <$> Fold step initial done done
238219
239220 where
240221
@@ -247,11 +228,9 @@ writeLastN n
247228 in fmap f $ liftIO $ RB.new n
248229
249230 done (Tuple3Fused' rb rh i) = do
250− arr <- liftIO $ MA.new n
251− foldFunc i rh snoc' arr rb
252−
253− -- XXX We should write a read unfold for ring.
254− snoc' b a = liftIO $ MA.snocUnsafe b a
231+ arr <- MA.new n
232+ -- XXX We should write a read unfold for ring.
233+ foldFunc i rh MA.snocUnsafe arr rb
255234
256235 foldFunc i
257236 | i < n = RB.unsafeFoldRingM
@@ -277,19 +256,26 @@ binarySearch = undefined
277256-- compared to streams by using SIMD instructions.
278257-- We can also return a bit array instead.
279258
259+-- Can use SIMD.
260+
280261-- | Perform a linear search to find all the indices where a given element is
281262-- present in an array.
282263--
283264-- /Unimplemented/
284−findIndicesOf :: (a -> Bool) -> Unfold Identity (Array a) Int
285−findIndicesOf = undefined
265+indexFinder :: (a -> Bool) -> Unfold Identity (Array a) Int
266+indexFinder = undefined
267+
268+-- |
269+-- /Unimplemented/
270+findIndicesOf :: (a -> Bool) -> Array a -> Stream Identity Int
271+findIndicesOf p = Stream.unfold (indexFinder p)
286272
287273{-
288274findIndexOf :: (a -> Bool) -> Array a -> Maybe Int
289−findIndexOf p = Unfold.fold Fold.one . Stream.unfold (findIndicesOf p)
275+findIndexOf p = Unfold.fold Fold.one . Stream.unfold (indexFinder p)
290276
291277find :: (a -> Bool) -> Array a -> Bool
292−find = Unfold.fold Fold.null . Stream.unfold (findIndicesOf p)
278+find = Unfold.fold Fold.null . Stream.unfold (indexFinder p)
293279-}
294280
295281-------------------------------------------------------------------------------
@@ -331,7 +317,7 @@ splitOn :: (Monad m, Unbox a) =>
331317 (a -> Bool) -> Array a -> Stream m (Array a)
332318splitOn predicate arr =
333319 fmap (\(i, len) -> getSliceUnsafe i len arr)
334− $ D.indexOnSuffix predicate (A.read arr)
320+ $ D.indexOnSuffix predicate (read arr)
335321
336322{-# INLINE sliceIndexerFromLen #-}
337323sliceIndexerFromLen :: forall m a. (Monad m, Unbox a)
@@ -339,9 +325,10 @@ sliceIndexerFromLen :: forall m a. (Monad m, Unbox a)
339325 -> Int -- ^ length of the slice
340326 -> Unfold m (Array a) (Int, Int)
341327sliceIndexerFromLen from len =
342− Unfold.lmap A.unsafeThaw (MA.sliceIndexerFromLen from len)
328+ Unfold.lmap unsafeThaw (MA.sliceIndexerFromLen from len)
343329
344330{-# DEPRECATED genSlicesFromLen "Please use sliceIndexerFromLen instead." #-}
331+{-# INLINE genSlicesFromLen #-}
345332genSlicesFromLen :: forall m a. (Monad m, Unbox a)
346333 => Int -- ^ from index
347334 -> Int -- ^ length of the slice
@@ -359,10 +346,11 @@ slicerFromLen :: forall m a. (Monad m, Unbox a)
359346 -> Int -- ^ length of the slice
360347 -> Unfold m (Array a) (Array a)
361348slicerFromLen from len =
362− fmap A.unsafeFreeze
363− $ Unfold.lmap A.unsafeThaw (MA.slicerFromLen from len)
349+ fmap unsafeFreeze
350+ $ Unfold.lmap unsafeThaw (MA.slicerFromLen from len)
364351
365352{-# DEPRECATED getSlicesFromLen "Please use slicerFromLen instead." #-}
353+{-# INLINE getSlicesFromLen #-}
366354getSlicesFromLen :: forall m a. (Monad m, Unbox a)
367355 => Int -- ^ from index
368356 -> Int -- ^ length of the slice
@@ -397,19 +385,25 @@ getIndex i arr =
397385-- @
398386-- read =
399387-- let u = lmap (\arr -> (0, length arr - 1)) Unfold.enumerateFromTo
400−-- in Unfold.lmap f (getIndices arr)
388+-- in Unfold.lmap f (indexReader arr)
401389--
402390-- readRev =
403391-- let i = length arr - 1
404−-- in Unfold.lmap f (getIndicesFromThenTo i (i - 1) 0)
392+-- in Unfold.lmap f (indexReaderFromThenTo i (i - 1) 0)
405393-- @
406394--
407395-- /Pre-release/
396+{-# INLINE indexReader #-}
397+indexReader :: (Monad m, Unbox a) => Stream m Int -> Unfold m (Array a) a
398+indexReader m =
399+ let unf = MA.indexReaderWith (return . unsafeInlineIO) m
400+ in Unfold.lmap unsafeThaw unf
401+
402+-- XXX DO NOT REMOVE, change the signature to use Stream instead of unfold
403+{-# DEPRECATED getIndices "Please use getIndices instead." #-}
408404{-# INLINE getIndices #-}
409405getIndices :: (Monad m, Unbox a) => Stream m Int -> Unfold m (Array a) a
410−getIndices m =
411− let unf = MA.getIndicesWith (return . unsafeInlineIO) m
412− in Unfold.lmap A.unsafeThaw unf
406+getIndices = indexReader
413407
414408-- | Unfolds @(from, then, to, array)@ generating a finite stream whose first
415409-- element is the array value from the index @from@ and the successive elements
@@ -425,9 +419,9 @@ getIndices m =
425419-- @
426420--
427421-- /Unimplemented/
428−{-# INLINE getIndicesFromThenTo #-}
429−getIndicesFromThenTo :: Unfold m (Int, Int, Int, Array a) a
430−getIndicesFromThenTo = undefined
422+{-# INLINE indexReaderFromThenTo #-}
423+indexReaderFromThenTo :: Unfold m (Int, Int, Int, Array a) a
424+indexReaderFromThenTo = undefined
431425
432426-------------------------------------------------------------------------------
433427-- Transform via stream operations
@@ -444,7 +438,7 @@ getIndicesFromThenTo = undefined
444438{-# INLINE runPipe #-}
445439runPipe :: (MonadIO m, Unbox a, Unbox b)
446440 => Pipe m a b -> Array a -> m (Array b)
447−runPipe f arr = P.runPipe (toArrayMinChunk (length arr)) $ f (A.read arr)
441+runPipe f arr = P.runPipe (toArrayMinChunk (length arr)) $ f (read arr)
448442-}
449443
450444-- XXX For transformations that cannot change the number of elements e.g. "map"
@@ -458,7 +452,7 @@ runPipe f arr = P.runPipe (toArrayMinChunk (length arr)) $ f (A.read arr)
458452streamTransform :: forall m a b. (MonadIO m, Unbox a, Unbox b)
459453 => (Stream m a -> Stream m b) -> Array a -> m (Array b)
460454streamTransform f arr =
461− Stream.fold (A.writeWith (length arr)) $ f (A.read arr)
455+ Stream.fold (createWith (length arr)) $ f (read arr)
462456
463457-------------------------------------------------------------------------------
464458-- Casts
@@ -492,7 +486,7 @@ asBytes = castUnsafe
492486--
493487cast :: forall a b. (Unbox b) => Array a -> Maybe (Array b)
494488cast arr =
495− let len = A.byteLength arr
489+ let len = byteLength arr
496490 r = len `mod` SIZE_OF(b)
497491 in if r /= 0
498492 then Nothing
@@ -510,9 +504,9 @@ cast arr =
510504--
511505asCStringUnsafe :: Array a -> (CString -> IO b) -> IO b
512506asCStringUnsafe arr act = do
513− let arr1 = asBytes arr <> A.fromList [0]
514− -- asPtrUnsafe makes sure the array is pinned
515− asPtrUnsafe arr1 $ \ptr -> act (castPtr ptr)
507+ let arr1 = asBytes arr <> fromList [0]
508+ -- unsafePinnedAsPtr makes sure the array is pinned
509+ unsafePinnedAsPtr arr1 $ \ptr -> act (castPtr ptr)
516510
517511-------------------------------------------------------------------------------
518512-- Folds
@@ -525,14 +519,14 @@ asCStringUnsafe arr act = do
525519-- /Pre-release/
526520{-# INLINE fold #-}
527521fold :: forall m a b. (Monad m, Unbox a) => Fold m a b -> Array a -> m b
528−fold f arr = Stream.fold f (A.read arr)
522+fold f arr = Stream.fold f (read arr)
529523
530524-- | Fold an array using a stream fold operation.
531525--
532526-- /Pre-release/
533527{-# INLINE streamFold #-}
534528streamFold :: (Monad m, Unbox a) => (Stream m a -> m b) -> Array a -> m b
535−streamFold f arr = f (A.read arr)
529+streamFold f arr = f (read arr)
536530
537531--------------------------------------------------------------------------------
538532-- Serialization
@@ -577,3 +571,448 @@ deserialize arr@(Array {..}) = unsafeInlineIO $ do
577571 Serialize.deserializeAt arrStart arrContents (arrStart + lenArr)
578572 assertM(off == arrStart + lenArr)
579573 pure val
574+
575+-------------------------------------------------------------------------------
576+-- Streams of Arrays
577+-------------------------------------------------------------------------------
578+
579+-- TODO: efficiently compare two streams of arrays. Two streams can have chunks
580+-- of different sizes, we can handle that in the stream comparison abstraction.
581+-- This could be useful e.g. to fast compare whether two files differ.
582+
583+-- | Insert the given element between arrays and flatten.
584+--
585+-- >>> interpose x = Stream.interpose x Array.reader
586+--
587+{-# INLINE interpose #-}
588+interpose :: (Monad m, Unbox a) => a -> Stream m (Array a) -> Stream m a
589+interpose x = D.interpose x reader
590+
591+data FlattenState s =
592+ OuterLoop s
593+ | InnerLoop s !MutByteArray !Int !Int
594+
595+-- | Insert the given element after each array and flatten. This is similar to
596+-- unlines.
597+--
598+-- >>> interposeSuffix x = Stream.interposeSuffix x Array.reader
599+--
600+{-# INLINE_NORMAL interposeSuffix #-}
601+interposeSuffix :: forall m a. (Monad m, Unbox a)
602+ => a -> Stream m (Array a) -> Stream m a
603+-- This does not require MonadIO constraint.
604+-- interposeSuffix x = D.interposeSuffix x reader
605+interposeSuffix sep (D.Stream step state) = D.Stream step' (OuterLoop state)
606+
607+ where
608+
609+ {-# INLINE_LATE step' #-}
610+ step' gst (OuterLoop st) = do
611+ r <- step (adaptState gst) st
612+ return $ case r of
613+ D.Yield Array{..} s ->
614+ D.Skip (InnerLoop s arrContents arrStart arrEnd)
615+ D.Skip s -> D.Skip (OuterLoop s)
616+ D.Stop -> D.Stop
617+
618+ step' _ (InnerLoop st _ p end) | p == end =
619+ return $ D.Yield sep $ OuterLoop st
620+
621+ step' _ (InnerLoop st contents p end) = do
622+ x <- pure $ unsafeInlineIO $ peekAt p contents
623+ return $ D.Yield x (InnerLoop st contents (INDEX_NEXT(p,a)) end)
624+
625+-- | Insert the given array after each array and flatten.
626+--
627+-- >>> intercalateSuffix = Stream.intercalateSuffix Array.reader
628+--
629+{-# INLINE intercalateSuffix #-}
630+intercalateSuffix :: (Monad m, Unbox a)
631+ => Array a -> Stream m (Array a) -> Stream m a
632+intercalateSuffix = D.intercalateSuffix reader
633+
634+-- | @compactLE n@ coalesces adjacent arrays in the input stream
635+-- only if the combined size would be less than or equal to n.
636+--
637+-- Generates unpinned arrays irrespective of the pinning status of input
638+-- arrays.
639+{-# INLINE_NORMAL compactLE #-}
640+compactLE :: (MonadIO m, Unbox a)
641+ => Int -> Stream m (Array a) -> Stream m (Array a)
642+compactLE n stream =
643+ D.map unsafeFreeze $ MA.compactLE n $ D.map unsafeThaw stream
644+
645+-- | Pinned version of 'compactLE'.
646+{-# INLINE_NORMAL pinnedCompactLE #-}
647+pinnedCompactLE :: (MonadIO m, Unbox a)
648+ => Int -> Stream m (Array a) -> Stream m (Array a)
649+pinnedCompactLE n stream =
650+ D.map unsafeFreeze $ MA.pinnedCompactLE n $ D.map unsafeThaw stream
651+
652+-- | Split a stream of arrays on a given separator byte, dropping the separator
653+-- and coalescing all the arrays between two separators into a single array.
654+--
655+{-# INLINE compactOnByte #-}
656+compactOnByte
657+ :: (MonadIO m)
658+ => Word8
659+ -> Stream m (Array Word8)
660+ -> Stream m (Array Word8)
661+compactOnByte byte =
662+ fmap unsafeFreeze . MA.compactOnByte byte . fmap unsafeThaw
663+
664+-- | Like 'compactOnByte' considers the separator in suffix position instead of
665+-- infix position.
666+{-# INLINE compactOnByteSuffix #-}
667+compactOnByteSuffix
668+ :: (MonadIO m)
669+ => Word8
670+ -> Stream m (Array Word8)
671+ -> Stream m (Array Word8)
672+compactOnByteSuffix byte =
673+ fmap unsafeFreeze . MA.compactOnByteSuffix byte . fmap unsafeThaw
674+
675+-------------------------------------------------------------------------------
676+-- Folding Streams of Arrays
677+-------------------------------------------------------------------------------
678+
679+-- XXX This should not be used for breaking a stream as the D.cons used in
680+-- reconstructing the stream could be very bad for performance. This can only
681+-- be useful in folding without breaking.
682+{-# INLINE_NORMAL foldBreakChunks #-}
683+foldBreakChunks :: forall m a b. (MonadIO m, Unbox a) =>
684+ Fold m a b -> Stream m (Array a) -> m (b, Stream m (Array a))
685+foldBreakChunks (Fold fstep initial _ final) stream@(Stream step state) = do
686+ res <- initial
687+ case res of
688+ Fold.Partial fs -> go SPEC state fs
689+ Fold.Done fb -> return $! (fb, stream)
690+
691+ where
692+
693+ {-# INLINE go #-}
694+ go !_ st !fs = do
695+ r <- step defState st
696+ case r of
697+ Stream.Yield (Array contents start end) s ->
698+ let fp = Tuple' end contents
699+ in goArray SPEC s fp start fs
700+ Stream.Skip s -> go SPEC s fs
701+ Stream.Stop -> do
702+ b <- final fs
703+ return (b, D.nil)
704+
705+ goArray !_ s (Tuple' end _) !cur !fs
706+ | cur == end = do
707+ go SPEC s fs
708+ goArray !_ st fp@(Tuple' end contents) !cur !fs = do
709+ x <- liftIO $ peekAt cur contents
710+ res <- fstep fs x
711+ let next = INDEX_NEXT(cur,a)
712+ case res of
713+ Fold.Done b -> do
714+ let arr = Array contents next end
715+ return $! (b, D.cons arr (D.Stream step st))
716+ Fold.Partial fs1 -> goArray SPEC st fp next fs1
717+
718+-- This may be more robust wrt fusion compared to unfoldMany?
719+
720+-- | Fold a stream of arrays using a 'Fold'. This is equivalent to the
721+-- following:
722+--
723+-- >>> foldChunks f = Stream.fold f . Stream.unfoldMany Array.reader
724+--
725+foldChunks :: (MonadIO m, Unbox a) => Fold m a b -> Stream m (Array a) -> m b
726+foldChunks f s = fmap fst (foldBreakChunks f s)
727+-- foldStream f = Stream.fold f . Stream.unfoldMany reader
728+
729+-- | Fold a stream of arrays using a 'Fold' and return the remaining stream.
730+--
731+-- The following alternative to this function allows composing the fold using
732+-- the parser Monad:
733+--
734+-- @
735+-- foldBreakStreamK f s =
736+-- fmap (first (fromRight undefined))
737+-- $ StreamK.parseBreakChunks (ParserK.adaptC (Parser.fromFold f)) s
738+-- @
739+--
740+-- We can compare perf and remove this one or define it in terms of that.
741+--
742+foldBreakChunksK :: forall m a b. (MonadIO m, Unbox a) =>
743+ Fold m a b -> StreamK m (Array a) -> m (b, StreamK m (Array a))
744+{-
745+foldBreakChunksK f s =
746+ fmap (first (fromRight undefined))
747+ $ StreamK.parseBreakChunks (ParserK.adaptC (Parser.fromFold f)) s
748+-}
749+foldBreakChunksK (Fold fstep initial _ final) stream = do
750+ res <- initial
751+ case res of
752+ Fold.Partial fs -> go fs stream
753+ Fold.Done fb -> return (fb, stream)
754+
755+ where
756+
757+ {-# INLINE go #-}
758+ go !fs st = do
759+ let stop = (, StreamK.nil) <$> final fs
760+ single a = yieldk a StreamK.nil
761+ yieldk (Array contents start end) r =
762+ let fp = Tuple' end contents
763+ in goArray fs r fp start
764+ in StreamK.foldStream defState yieldk single stop st
765+
766+ goArray !fs st (Tuple' end _) !cur
767+ | cur == end = do
768+ go fs st
769+ goArray !fs st fp@(Tuple' end contents) !cur = do
770+ x <- liftIO $ peekAt cur contents
771+ res <- fstep fs x
772+ let next = INDEX_NEXT(cur,a)
773+ case res of
774+ Fold.Done b -> do
775+ let arr = Array contents next end
776+ return $! (b, StreamK.cons arr st)
777+ Fold.Partial fs1 -> goArray fs1 st fp next
778+
779+{-
780+-- This can be generalized to any type provided it can be unfolded to a stream
781+-- and it can be combined using a semigroup operation.
782+--
783+{-# INLINE_NORMAL parseBreakD #-}
784+parseBreakD ::
785+ forall m a b. (MonadIO m, MonadThrow m, Unbox a)
786+ => PRD.Parser a m b
787+ -> D.Stream m (Array.Array a)
788+ -> m (b, D.Stream m (Array.Array a))
789+parseBreakD
790+ (PRD.Parser pstep initial extract) stream@(D.Stream step state) = do
791+
792+ res <- initial
793+ case res of
794+ PRD.IPartial s -> go SPEC state (List []) s
795+ PRD.IDone b -> return (b, stream)
796+ PRD.IError err -> throwM $ ParseError err
797+
798+ where
799+
800+ -- "backBuf" contains last few items in the stream that we may have to
801+ -- backtrack to.
802+ --
803+ -- XXX currently we are using a dumb list based approach for backtracking
804+ -- buffer. This can be replaced by a sliding/ring buffer using Data.Array.
805+ -- That will allow us more efficient random back and forth movement.
806+ go !_ st backBuf !pst = do
807+ r <- step defState st
808+ case r of
809+ D.Yield (Array contents start end) s ->
810+ gobuf SPEC s backBuf
811+ (Tuple' end contents) start pst
812+ D.Skip s -> go SPEC s backBuf pst
813+ D.Stop -> do
814+ b <- extract pst
815+ return (b, D.nil)
816+
817+ -- Use strictness on "cur" to keep it unboxed
818+ gobuf !_ s backBuf (Tuple' end _) !cur !pst
819+ | cur == end = do
820+ go SPEC s backBuf pst
821+ gobuf !_ s backBuf fp@(Tuple' end contents) !cur !pst = do
822+ x <- liftIO $ peekByteIndex contents cur
823+ pRes <- pstep pst x
824+ let next = INDEX_NEXT(cur,a)
825+ case pRes of
826+ PR.Partial 0 pst1 ->
827+ gobuf SPEC s (List []) fp next pst1
828+ PR.Partial n pst1 -> do
829+ assert (n <= Prelude.length (x:getList backBuf)) (return ())
830+ let src0 = Prelude.take n (x:getList backBuf)
831+ arr0 = A.fromListN n (Prelude.reverse src0)
832+ arr1 = Array contents next end
833+ src = arr0 <> arr1
834+ let !(Array cont1 start end1) = src
835+ fp1 = Tuple' end1 cont1
836+ gobuf SPEC s (List []) fp1 start pst1
837+ PR.Continue 0 pst1 ->
838+ gobuf SPEC s (List (x:getList backBuf)) fp next pst1
839+ PR.Continue n pst1 -> do
840+ assert (n <= Prelude.length (x:getList backBuf)) (return ())
841+ let (src0, buf1) = splitAt n (x:getList backBuf)
842+ arr0 = A.fromListN n (Prelude.reverse src0)
843+ arr1 = Array contents next end
844+ src = arr0 <> arr1
845+ let !(Array cont1 start end1) = src
846+ fp1 = Tuple' end1 cont1
847+ gobuf SPEC s (List buf1) fp1 start pst1
848+ PR.Done 0 b -> do
849+ let arr = Array contents next end
850+ return (b, D.cons arr (D.Stream step s))
851+ PR.Done n b -> do
852+ assert (n <= Prelude.length (x:getList backBuf)) (return ())
853+ let src0 = Prelude.take n (x:getList backBuf)
854+ -- XXX create the array in reverse instead
855+ arr0 = A.fromListN n (Prelude.reverse src0)
856+ arr1 = Array contents next end
857+ -- XXX Use StreamK to avoid adding arbitrary layers of
858+ -- constructors every time.
859+ str = D.cons arr0 (D.cons arr1 (D.Stream step s))
860+ return (b, str)
861+ PR.Error err -> throwM $ ParseError err
862+-}
863+
864+-- | Parse an array stream using the supplied 'Parser'. Returns the parse
865+-- result and the unconsumed stream. Throws 'ParseError' if the parse fails.
866+--
867+-- The following alternative to this function allows composing the parser using
868+-- the parser Monad:
869+--
870+-- >>> parseBreakStreamK p = StreamK.parseBreakChunks (ParserK.adaptC p)
871+--
872+-- We can compare perf and remove this one or define it in terms of that.
873+--
874+-- /Internal/
875+--
876+{-# INLINE_NORMAL parseBreakChunksK #-}
877+parseBreakChunksK ::
878+ forall m a b. (MonadIO m, Unbox a)
879+ => Parser a m b
880+ -> StreamK m (Array a)
881+ -> m (Either ParseError b, StreamK m (Array a))
882+-- parseBreakStreamK p = StreamK.parseBreakChunks (ParserK.adaptC p)
883+parseBreakChunksK (Parser pstep initial extract) stream = do
884+ res <- initial
885+ case res of
886+ IPartial s -> go s stream []
887+ IDone b -> return (Right b, stream)
888+ IError err -> return (Left (ParseError err), stream)
889+
890+ where
891+
892+ -- "backBuf" contains last few items in the stream that we may have to
893+ -- backtrack to.
894+ --
895+ -- XXX currently we are using a dumb list based approach for backtracking
896+ -- buffer. This can be replaced by a sliding/ring buffer using Data.Array.
897+ -- That will allow us more efficient random back and forth movement.
898+ go !pst st backBuf = do
899+ let stop = goStop pst backBuf -- (, K.nil) <$> extract pst
900+ single a = yieldk a StreamK.nil
901+ yieldk arr r = goArray pst backBuf r arr
902+ in StreamK.foldStream defState yieldk single stop st
903+
904+ -- Use strictness on "cur" to keep it unboxed
905+ goArray !pst backBuf st (Array _ cur end) | cur == end = go pst st backBuf
906+ goArray !pst backBuf st (Array contents cur end) = do
907+ x <- liftIO $ peekAt cur contents
908+ pRes <- pstep pst x
909+ let next = INDEX_NEXT(cur,a)
910+ case pRes of
911+ Parser.Partial 0 s ->
912+ goArray s [] st (Array contents next end)
913+ Parser.Partial n s -> do
914+ assert (n <= Prelude.length (x:backBuf)) (return ())
915+ let src0 = Prelude.take n (x:backBuf)
916+ arr0 = fromListN n (Prelude.reverse src0)
917+ arr1 = Array contents next end
918+ src = arr0 <> arr1
919+ goArray s [] st src
920+ Parser.Continue 0 s ->
921+ goArray s (x:backBuf) st (Array contents next end)
922+ Parser.Continue n s -> do
923+ assert (n <= Prelude.length (x:backBuf)) (return ())
924+ let (src0, buf1) = Prelude.splitAt n (x:backBuf)
925+ arr0 = fromListN n (Prelude.reverse src0)
926+ arr1 = Array contents next end
927+ src = arr0 <> arr1
928+ goArray s buf1 st src
929+ Parser.Done 0 b -> do
930+ let arr = Array contents next end
931+ return (Right b, StreamK.cons arr st)
932+ Parser.Done n b -> do
933+ assert (n <= Prelude.length (x:backBuf)) (return ())
934+ let src0 = Prelude.take n (x:backBuf)
935+ -- XXX Use fromListRevN once implemented
936+ -- arr0 = A.fromListRevN n src0
937+ arr0 = fromListN n (Prelude.reverse src0)
938+ arr1 = Array contents next end
939+ str = StreamK.cons arr0 (StreamK.cons arr1 st)
940+ return (Right b, str)
941+ Parser.Error err -> do
942+ let n = Prelude.length backBuf
943+ arr0 = fromListN n (Prelude.reverse backBuf)
944+ arr1 = Array contents cur end
945+ str = StreamK.cons arr0 (StreamK.cons arr1 stream)
946+ return (Left (ParseError err), str)
947+
948+ -- This is a simplified goArray
949+ goExtract !pst backBuf (Array _ cur end)
950+ | cur == end = goStop pst backBuf
951+ goExtract !pst backBuf (Array contents cur end) = do
952+ x <- liftIO $ peekAt cur contents
953+ pRes <- pstep pst x
954+ let next = INDEX_NEXT(cur,a)
955+ case pRes of
956+ Parser.Partial 0 s ->
957+ goExtract s [] (Array contents next end)
958+ Parser.Partial n s -> do
959+ assert (n <= Prelude.length (x:backBuf)) (return ())
960+ let src0 = Prelude.take n (x:backBuf)
961+ arr0 = fromListN n (Prelude.reverse src0)
962+ arr1 = Array contents next end
963+ src = arr0 <> arr1
964+ goExtract s [] src
965+ Parser.Continue 0 s ->
966+ goExtract s backBuf (Array contents next end)
967+ Parser.Continue n s -> do
968+ assert (n <= Prelude.length (x:backBuf)) (return ())
969+ let (src0, buf1) = Prelude.splitAt n (x:backBuf)
970+ arr0 = fromListN n (Prelude.reverse src0)
971+ arr1 = Array contents next end
972+ src = arr0 <> arr1
973+ goExtract s buf1 src
974+ Parser.Done 0 b -> do
975+ let arr = Array contents next end
976+ return (Right b, StreamK.fromPure arr)
977+ Parser.Done n b -> do
978+ assert (n <= Prelude.length backBuf) (return ())
979+ let src0 = Prelude.take n backBuf
980+ -- XXX Use fromListRevN once implemented
981+ -- arr0 = A.fromListRevN n src0
982+ arr0 = fromListN n (Prelude.reverse src0)
983+ arr1 = Array contents next end
984+ str = StreamK.cons arr0 (StreamK.fromPure arr1)
985+ return (Right b, str)
986+ Parser.Error err -> do
987+ let n = Prelude.length backBuf
988+ arr0 = fromListN n (Prelude.reverse backBuf)
989+ arr1 = Array contents cur end
990+ str = StreamK.cons arr0 (StreamK.cons arr1 stream)
991+ return (Left (ParseError err), str)
992+
993+ -- This is a simplified goExtract
994+ {-# INLINE goStop #-}
995+ goStop !pst backBuf = do
996+ pRes <- extract pst
997+ case pRes of
998+ Parser.Partial _ _ -> error "Bug: parseBreak: Partial in extract"
999+ Parser.Continue 0 s ->
1000+ goStop s backBuf
1001+ Parser.Continue n s -> do
1002+ assert (n <= Prelude.length backBuf) (return ())
1003+ let (src0, buf1) = Prelude.splitAt n backBuf
1004+ arr = fromListN n (Prelude.reverse src0)
1005+ goExtract s buf1 arr
1006+ Parser.Done 0 b ->
1007+ return (Right b, StreamK.nil)
1008+ Parser.Done n b -> do
1009+ assert (n <= Prelude.length backBuf) (return ())
1010+ let src0 = Prelude.take n backBuf
1011+ -- XXX Use fromListRevN once implemented
1012+ -- arr0 = A.fromListRevN n src0
1013+ arr0 = fromListN n (Prelude.reverse src0)
1014+ return (Right b, StreamK.fromPure arr0)
1015+ Parser.Error err -> do
1016+ let n = Prelude.length backBuf
1017+ arr0 = fromListN n (Prelude.reverse backBuf)
1018+ return (Left (ParseError err), StreamK.fromPure arr0)
core/src/Streamly/Internal/Data/Array/Generic.hs+22−6
@@ -11,8 +11,8 @@ module Streamly.Internal.Data.Array.Generic
1111
1212 -- * Construction
1313 , nil
14− , writeN
15− , write
14+ , createOf
15+ , create
1616 , writeWith
1717 , writeLastN
1818
@@ -44,6 +44,10 @@ module Streamly.Internal.Data.Array.Generic
4444 , getIndex
4545 , getSliceUnsafe
4646 , strip
47+
48+ -- * Deprecated
49+ , writeN
50+ , write
4751 )
4852where
4953
@@ -104,21 +108,33 @@ nil = unsafePerformIO $ unsafeFreeze <$> MArray.nil
104108-- Construction - Folds
105109-------------------------------------------------------------------------------
106110
107−{-# INLINE_NORMAL writeN #-}
111+{-# INLINE_NORMAL createOf #-}
112+createOf :: MonadIO m => Int -> Fold m a (Array a)
113+createOf = fmap unsafeFreeze <$> MArray.createOf
114+
115+-- XXX Deprecate in major
116+-- {-# DEPRECATED writeN "Please use createOf instead." #-}
117+{-# INLINE writeN #-}
108118writeN :: MonadIO m => Int -> Fold m a (Array a)
109−writeN = fmap unsafeFreeze <$> MArray.writeN
119+writeN = createOf
110120
111121{-# INLINE_NORMAL writeWith #-}
112122writeWith :: MonadIO m => Int -> Fold m a (Array a)
113−writeWith elemCount = unsafeFreeze <$> MArray.writeWith elemCount
123+writeWith elemCount = unsafeFreeze <$> MArray.createWith elemCount
114124
115125-- | Fold the whole input to a single array.
116126--
117127-- /Caution! Do not use this on infinite streams./
118128--
129+{-# INLINE create #-}
130+create :: MonadIO m => Fold m a (Array a)
131+create = fmap unsafeFreeze MArray.create
132+
133+-- XXX Deprecate in major
134+-- {-# DEPRECATED write "Please use create instead." #-}
119135{-# INLINE write #-}
120136write :: MonadIO m => Fold m a (Array a)
121−write = fmap unsafeFreeze MArray.write
137+write = create
122138
123139fromPureStream :: Stream Identity a -> Array a
124140fromPureStream x =
core/src/Streamly/Internal/Data/Array/Stream.hs+15−475
@@ -12,6 +12,7 @@
1212-- keep this as a separate module and release it.
1313--
1414module Streamly.Internal.Data.Array.Stream
15+{-# DEPRECATED "Please use \"Streamly.Internal.Data.Array\" instead." #-}
1516 (
1617 -- * Creation
1718 Array.chunksOf
@@ -19,13 +20,13 @@ module Streamly.Internal.Data.Array.Stream
1920 , Array.bufferChunks
2021
2122 -- * Flattening to elements
22− , concat
23+ , Array.concat
2324 , Array.flattenArrays
24− , concatRev
25+ , Array.concatRev
2526 , Array.flattenArraysRev
26− , interpose
27− , interposeSuffix
28− , intercalateSuffix
27+ , Array.interpose
28+ , Array.interposeSuffix
29+ , Array.intercalateSuffix
2930 , unlines
3031
3132 -- * Elimination
@@ -90,20 +91,14 @@ import Prelude hiding (null, last, (!!), read, concat, unlines)
9091
9192import Streamly.Data.Fold (Fold)
9293import Streamly.Internal.Data.Array.Type (Array(..))
93−import Streamly.Internal.Data.MutByteArray.Type (MutByteArray)
9494import Streamly.Internal.Data.Fold.Chunked (ChunkFold(..))
9595import Streamly.Internal.Data.Parser (ParseError(..))
9696import Streamly.Internal.Data.Stream (Stream)
9797import Streamly.Internal.Data.StreamK (StreamK, fromStream, toStream)
9898import Streamly.Internal.Data.SVar.Type (adaptState, defState)
99−import Streamly.Internal.Data.Tuple.Strict (Tuple'(..))
10099
101−import qualified Streamly.Data.Fold as FL
102100import qualified Streamly.Internal.Data.Array as A
103101import qualified Streamly.Internal.Data.Array as Array
104−import qualified Streamly.Internal.Data.MutArray.Type as MA
105−import qualified Streamly.Internal.Data.MutArray.Stream as AS
106−import qualified Streamly.Internal.Data.Fold.Type as FL (Fold(..), Step(..))
107102import qualified Streamly.Internal.Data.Parser as PR
108103import qualified Streamly.Internal.Data.Parser as PRD
109104 (Parser(..), Initial(..))
@@ -114,110 +109,19 @@ import qualified Streamly.Internal.Data.StreamK as K
114109-- XXX Since these are immutable arrays MonadIO constraint can be removed from
115110-- most places.
116111
117−-------------------------------------------------------------------------------
118−-- Append
119−-------------------------------------------------------------------------------
120−
121−-- XXX efficiently compare two streams of arrays. Two streams can have chunks
122−-- of different sizes, we can handle that in the stream comparison abstraction.
123−-- This could be useful e.g. to fast compare whether two files differ.
124−
125−-- | Convert a stream of arrays into a stream of their elements.
126−--
127−-- Same as the following:
128−--
129−-- > concat = Stream.unfoldMany Array.read
130−--
131−-- @since 0.7.0
132−{-# INLINE concat #-}
133−concat :: (Monad m, Unbox a) => Stream m (Array a) -> Stream m a
134−-- concat m = fromStreamD $ A.flattenArrays (toStreamD m)
135−-- concat m = fromStreamD $ D.concatMap A.toStreamD (toStreamD m)
136−concat = D.unfoldMany A.reader
137−
138−-- | Convert a stream of arrays into a stream of their elements reversing the
139−-- contents of each array before flattening.
140−--
141−-- > concatRev = Stream.unfoldMany Array.readerRev
142−--
143−-- @since 0.7.0
144−{-# INLINE concatRev #-}
145−concatRev :: (Monad m, Unbox a) => Stream m (Array a) -> Stream m a
146−-- concatRev m = fromStreamD $ A.flattenArraysRev (toStreamD m)
147−concatRev = D.unfoldMany A.readerRev
148−
149112-------------------------------------------------------------------------------
150113-- Intersperse and append
151114-------------------------------------------------------------------------------
152115
153−-- | Flatten a stream of arrays after inserting the given element between
154−-- arrays.
155−--
156−-- /Pre-release/
157−{-# INLINE interpose #-}
158−interpose :: (Monad m, Unbox a) => a -> Stream m (Array a) -> Stream m a
159−interpose x = D.interpose x A.reader
160−
161−{-# INLINE intercalateSuffix #-}
162−intercalateSuffix :: (Monad m, Unbox a)
163− => Array a -> Stream m (Array a) -> Stream m a
164−intercalateSuffix = D.intercalateSuffix A.reader
165−
166−-- | Flatten a stream of arrays appending the given element after each
167−-- array.
168−--
169−-- @since 0.7.0
170−{-# INLINE interposeSuffix #-}
171−interposeSuffix :: (Monad m, Unbox a)
172− => a -> Stream m (Array a) -> Stream m a
173−-- interposeSuffix x = fromStreamD . A.unlines x . toStreamD
174−interposeSuffix x = D.interposeSuffix x A.reader
175−
176−data FlattenState s =
177− OuterLoop s
178− | InnerLoop s !MutByteArray !Int !Int
179−
180−-- XXX This is a special case of interposeSuffix, can be removed.
181−-- XXX Remove monadIO constraint
182116{-# INLINE_NORMAL unlines #-}
183117unlines :: forall m a. (MonadIO m, Unbox a)
184118 => a -> D.Stream m (Array a) -> D.Stream m a
185−unlines sep (D.Stream step state) = D.Stream step' (OuterLoop state)
186− where
187− {-# INLINE_LATE step' #-}
188− step' gst (OuterLoop st) = do
189− r <- step (adaptState gst) st
190− return $ case r of
191− D.Yield Array{..} s ->
192− D.Skip (InnerLoop s arrContents arrStart arrEnd)
193− D.Skip s -> D.Skip (OuterLoop s)
194− D.Stop -> D.Stop
195−
196− step' _ (InnerLoop st _ p end) | p == end =
197− return $ D.Yield sep $ OuterLoop st
198−
199− step' _ (InnerLoop st contents p end) = do
200− x <- liftIO $ peekByteIndex p contents
201− return $ D.Yield x (InnerLoop st contents (INDEX_NEXT(p,a)) end)
119+unlines = Array.interposeSuffix
202120
203121-------------------------------------------------------------------------------
204122-- Compact
205123-------------------------------------------------------------------------------
206124
207−-- XXX These would not be needed once we implement compactLEFold, see
208−-- module Streamly.Internal.Data.Stream.MutChunked
209−--
210−-- XXX Note that this thaws immutable arrays for appending, that may be
211−-- problematic if multiple users do the same thing, however, immutable arrays
212−-- would usually have no capacity to append, therefore, a copy will be forced
213−-- anyway. Confirm this. We can forcefully trim the array capacity before thaw
214−-- to ensure this.
215−{-# INLINE_NORMAL packArraysChunksOf #-}
216−packArraysChunksOf :: (MonadIO m, Unbox a)
217− => Int -> D.Stream m (Array a) -> D.Stream m (Array a)
218−packArraysChunksOf n str =
219− D.map A.unsafeFreeze $ AS.packArraysChunksOf n $ D.map A.unsafeThaw str
220−
221125-- XXX instead of writing two different versions of this operation, we should
222126-- write it as a pipe.
223127--
@@ -225,8 +129,7 @@ packArraysChunksOf n str =
225129{-# INLINE_NORMAL lpackArraysChunksOf #-}
226130lpackArraysChunksOf :: (MonadIO m, Unbox a)
227131 => Int -> Fold m (Array a) () -> Fold m (Array a) ()
228−lpackArraysChunksOf n fld =
229− FL.lmap A.unsafeThaw $ AS.lpackArraysChunksOf n (FL.lmap A.unsafeFreeze fld)
132+lpackArraysChunksOf = Array.lCompactGE
230133
231134-- | Coalesce adjacent arrays in incoming stream to form bigger arrays of a
232135-- maximum specified size in bytes.
@@ -235,7 +138,7 @@ lpackArraysChunksOf n fld =
235138{-# INLINE compact #-}
236139compact :: (MonadIO m, Unbox a)
237140 => Int -> Stream m (Array a) -> Stream m (Array a)
238−compact = packArraysChunksOf
141+compact = Array.compactLE
239142
240143-- | Given a stream of arrays, splice them all together to generate a single
241144-- array. The stream must be /finite/.
@@ -243,71 +146,12 @@ compact = packArraysChunksOf
243146-- @since 0.7.0
244147{-# INLINE toArray #-}
245148toArray :: (MonadIO m, Unbox a) => Stream m (Array a) -> m (Array a)
246−toArray s =
247− fmap A.unsafeFreeze $ MA.fromChunksRealloced (fmap A.unsafeThaw s)
149+toArray = Array.fromChunks
248150
249151-------------------------------------------------------------------------------
250152-- Split
251153-------------------------------------------------------------------------------
252154
253−data SplitState s arr
254− = Initial s
255− | Buffering s arr
256− | Splitting s arr
257− | Yielding arr (SplitState s arr)
258− | Finishing
259−
260−-- | Split a stream of arrays on a given separator byte, dropping the separator
261−-- and coalescing all the arrays between two separators into a single array.
262−--
263−-- @since 0.7.0
264−{-# INLINE_NORMAL _splitOn #-}
265−_splitOn
266− :: MonadIO m
267− => Word8
268− -> D.Stream m (Array Word8)
269− -> D.Stream m (Array Word8)
270−_splitOn byte (D.Stream step state) = D.Stream step' (Initial state)
271−
272− where
273−
274− {-# INLINE_LATE step' #-}
275− step' gst (Initial st) = do
276− r <- step gst st
277− case r of
278− D.Yield arr s -> do
279− (arr1, marr2) <- A.breakOn byte arr
280− return $ case marr2 of
281− Nothing -> D.Skip (Buffering s arr1)
282− Just arr2 -> D.Skip (Yielding arr1 (Splitting s arr2))
283− D.Skip s -> return $ D.Skip (Initial s)
284− D.Stop -> return D.Stop
285−
286− step' gst (Buffering st buf) = do
287− r <- step gst st
288− case r of
289− D.Yield arr s -> do
290− (arr1, marr2) <- A.breakOn byte arr
291− -- XXX Should use MutArray.splice
292− buf' <- A.splice buf arr1
293− return $ case marr2 of
294− Nothing -> D.Skip (Buffering s buf')
295− Just x -> D.Skip (Yielding buf' (Splitting s x))
296− D.Skip s -> return $ D.Skip (Buffering s buf)
297− D.Stop -> return $
298− if A.byteLength buf == 0
299− then D.Stop
300− else D.Skip (Yielding buf Finishing)
301−
302− step' _ (Splitting st buf) = do
303− (arr1, marr2) <- A.breakOn byte buf
304− return $ case marr2 of
305− Nothing -> D.Skip $ Buffering st arr1
306− Just arr2 -> D.Skip $ Yielding arr1 (Splitting st arr2)
307−
308− step' _ (Yielding arr next) = return $ D.Yield arr next
309− step' _ Finishing = return D.Stop
310−
311155-- XXX Remove MonadIO constraint.
312156-- | Split a stream of arrays on a given separator byte, dropping the separator
313157-- and coalescing all the arrays between two separators into a single array.
@@ -319,11 +163,7 @@ splitOn
319163 => Word8
320164 -> Stream m (Array Word8)
321165 -> Stream m (Array Word8)
322−splitOn byte =
323− fmap A.unsafeFreeze
324− -- XXX use spliceExp and rightSize?
325− . D.splitInnerBy (MA.breakOn byte) MA.splice
326− . fmap A.unsafeThaw
166+splitOn = Array.compactOnByte
327167
328168{-# INLINE splitOnSuffix #-}
329169splitOnSuffix
@@ -331,13 +171,7 @@ splitOnSuffix
331171 => Word8
332172 -> Stream m (Array Word8)
333173 -> Stream m (Array Word8)
334−-- splitOn byte s = fromStreamD $ A.splitOn byte $ toStreamD s
335−splitOnSuffix byte =
336− fmap A.unsafeFreeze
337− -- XXX use spliceExp and rightSize?
338− . D.splitInnerBySuffix
339− (\arr -> MA.byteLength arr == 0) (MA.breakOn byte) MA.splice
340− . fmap A.unsafeThaw
174+splitOnSuffix = Array.compactOnByteSuffix
341175
342176-------------------------------------------------------------------------------
343177-- Elimination - Running folds
@@ -346,71 +180,7 @@ splitOnSuffix byte =
346180{-# INLINE_NORMAL foldBreakD #-}
347181foldBreakD :: forall m a b. (MonadIO m, Unbox a) =>
348182 Fold m a b -> D.Stream m (Array a) -> m (b, D.Stream m (Array a))
349−foldBreakD (FL.Fold fstep initial _ final) stream@(D.Stream step state) = do
350− res <- initial
351− case res of
352− FL.Partial fs -> go SPEC state fs
353− FL.Done fb -> return $! (fb, stream)
354−
355− where
356−
357− {-# INLINE go #-}
358− go !_ st !fs = do
359− r <- step defState st
360− case r of
361− D.Yield (Array contents start end) s ->
362− let fp = Tuple' end contents
363− in goArray SPEC s fp start fs
364− D.Skip s -> go SPEC s fs
365− D.Stop -> do
366− b <- final fs
367− return (b, D.nil)
368−
369− goArray !_ s (Tuple' end _) !cur !fs
370− | cur == end = do
371− go SPEC s fs
372− goArray !_ st fp@(Tuple' end contents) !cur !fs = do
373− x <- liftIO $ peekByteIndex cur contents
374− res <- fstep fs x
375− let next = INDEX_NEXT(cur,a)
376− case res of
377− FL.Done b -> do
378− let arr = Array contents next end
379− return $! (b, D.cons arr (D.Stream step st))
380− FL.Partial fs1 -> goArray SPEC st fp next fs1
381−
382−{-# INLINE_NORMAL foldBreakK #-}
383−foldBreakK :: forall m a b. (MonadIO m, Unbox a) =>
384− Fold m a b -> K.StreamK m (Array a) -> m (b, K.StreamK m (Array a))
385−foldBreakK (FL.Fold fstep initial _ final) stream = do
386− res <- initial
387− case res of
388− FL.Partial fs -> go fs stream
389− FL.Done fb -> return (fb, stream)
390−
391− where
392−
393− {-# INLINE go #-}
394− go !fs st = do
395− let stop = (, K.nil) <$> final fs
396− single a = yieldk a K.nil
397− yieldk (Array contents start end) r =
398− let fp = Tuple' end contents
399− in goArray fs r fp start
400− in K.foldStream defState yieldk single stop st
401−
402− goArray !fs st (Tuple' end _) !cur
403− | cur == end = do
404− go fs st
405− goArray !fs st fp@(Tuple' end contents) !cur = do
406− x <- liftIO $ peekByteIndex cur contents
407− res <- fstep fs x
408− let next = INDEX_NEXT(cur,a)
409− case res of
410− FL.Done b -> do
411− let arr = Array contents next end
412− return $! (b, K.cons arr st)
413− FL.Partial fs1 -> goArray fs1 st fp next
183+foldBreakD = Array.foldBreakChunks
414184
415185-- | Fold an array stream using the supplied 'Fold'. Returns the fold result
416186-- and the unconsumed stream.
@@ -436,7 +206,7 @@ foldBreak ::
436206 => Fold m a b
437207 -> StreamK m (A.Array a)
438208 -> m (b, StreamK m (A.Array a))
439−foldBreak = foldBreakK
209+foldBreak = Array.foldBreakChunksK
440210--
441211-- foldBreak f s = fmap fromStreamD <$> foldBreakD f (toStreamD s)
442212--
@@ -500,236 +270,6 @@ splitAtArrayListRev n ls
500270{-# ANN type List NoSpecConstr #-}
501271newtype List a = List {getList :: [a]}
502272
503−{-
504−-- This can be generalized to any type provided it can be unfolded to a stream
505−-- and it can be combined using a semigroup operation.
506−--
507−-- XXX This should be written using CPS (as parseK) if we want it to scale wrt
508−-- to the number of times it can be called on the same stream.
509−{-# INLINE_NORMAL parseBreakD #-}
510−parseBreakD ::
511− forall m a b. (MonadIO m, MonadThrow m, Unbox a)
512− => PRD.Parser a m b
513− -> D.Stream m (Array.Array a)
514− -> m (b, D.Stream m (Array.Array a))
515−parseBreakD
516− (PRD.Parser pstep initial extract) stream@(D.Stream step state) = do
517−
518− res <- initial
519− case res of
520− PRD.IPartial s -> go SPEC state (List []) s
521− PRD.IDone b -> return (b, stream)
522− PRD.IError err -> throwM $ ParseError err
523−
524− where
525−
526− -- "backBuf" contains last few items in the stream that we may have to
527− -- backtrack to.
528− --
529− -- XXX currently we are using a dumb list based approach for backtracking
530− -- buffer. This can be replaced by a sliding/ring buffer using Data.Array.
531− -- That will allow us more efficient random back and forth movement.
532− go !_ st backBuf !pst = do
533− r <- step defState st
534− case r of
535− D.Yield (Array contents start end) s ->
536− gobuf SPEC s backBuf
537− (Tuple' end contents) start pst
538− D.Skip s -> go SPEC s backBuf pst
539− D.Stop -> do
540− b <- extract pst
541− return (b, D.nil)
542−
543− -- Use strictness on "cur" to keep it unboxed
544− gobuf !_ s backBuf (Tuple' end _) !cur !pst
545− | cur == end = do
546− go SPEC s backBuf pst
547− gobuf !_ s backBuf fp@(Tuple' end contents) !cur !pst = do
548− x <- liftIO $ peekByteIndex contents cur
549− pRes <- pstep pst x
550− let next = INDEX_NEXT(cur,a)
551− case pRes of
552− PR.Partial 0 pst1 ->
553− gobuf SPEC s (List []) fp next pst1
554− PR.Partial n pst1 -> do
555− assert (n <= Prelude.length (x:getList backBuf)) (return ())
556− let src0 = Prelude.take n (x:getList backBuf)
557− arr0 = A.fromListN n (Prelude.reverse src0)
558− arr1 = Array contents next end
559− src = arr0 <> arr1
560− let !(Array cont1 start end1) = src
561− fp1 = Tuple' end1 cont1
562− gobuf SPEC s (List []) fp1 start pst1
563− PR.Continue 0 pst1 ->
564− gobuf SPEC s (List (x:getList backBuf)) fp next pst1
565− PR.Continue n pst1 -> do
566− assert (n <= Prelude.length (x:getList backBuf)) (return ())
567− let (src0, buf1) = splitAt n (x:getList backBuf)
568− arr0 = A.fromListN n (Prelude.reverse src0)
569− arr1 = Array contents next end
570− src = arr0 <> arr1
571− let !(Array cont1 start end1) = src
572− fp1 = Tuple' end1 cont1
573− gobuf SPEC s (List buf1) fp1 start pst1
574− PR.Done 0 b -> do
575− let arr = Array contents next end
576− return (b, D.cons arr (D.Stream step s))
577− PR.Done n b -> do
578− assert (n <= Prelude.length (x:getList backBuf)) (return ())
579− let src0 = Prelude.take n (x:getList backBuf)
580− -- XXX create the array in reverse instead
581− arr0 = A.fromListN n (Prelude.reverse src0)
582− arr1 = Array contents next end
583− -- XXX Use StreamK to avoid adding arbitrary layers of
584− -- constructors every time.
585− str = D.cons arr0 (D.cons arr1 (D.Stream step s))
586− return (b, str)
587− PR.Error err -> throwM $ ParseError err
588−-}
589−
590−{-# INLINE_NORMAL parseBreakK #-}
591−parseBreakK ::
592− forall m a b. (MonadIO m, Unbox a)
593− => PRD.Parser a m b
594− -> K.StreamK m (Array.Array a)
595− -> m (Either ParseError b, K.StreamK m (Array.Array a))
596−parseBreakK (PRD.Parser pstep initial extract) stream = do
597− res <- initial
598− case res of
599− PRD.IPartial s -> go s stream []
600− PRD.IDone b -> return (Right b, stream)
601− PRD.IError err -> return (Left (ParseError err), stream)
602−
603− where
604−
605− -- "backBuf" contains last few items in the stream that we may have to
606− -- backtrack to.
607− --
608− -- XXX currently we are using a dumb list based approach for backtracking
609− -- buffer. This can be replaced by a sliding/ring buffer using Data.Array.
610− -- That will allow us more efficient random back and forth movement.
611− go !pst st backBuf = do
612− let stop = goStop pst backBuf -- (, K.nil) <$> extract pst
613− single a = yieldk a K.nil
614− yieldk arr r = goArray pst backBuf r arr
615− in K.foldStream defState yieldk single stop st
616−
617− -- Use strictness on "cur" to keep it unboxed
618− goArray !pst backBuf st (Array _ cur end) | cur == end = go pst st backBuf
619− goArray !pst backBuf st (Array contents cur end) = do
620− x <- liftIO $ peekByteIndex cur contents
621− pRes <- pstep pst x
622− let next = INDEX_NEXT(cur,a)
623− case pRes of
624− PR.Partial 0 s ->
625− goArray s [] st (Array contents next end)
626− PR.Partial n s -> do
627− assert (n <= Prelude.length (x:backBuf)) (return ())
628− let src0 = Prelude.take n (x:backBuf)
629− arr0 = A.fromListN n (Prelude.reverse src0)
630− arr1 = Array contents next end
631− src = arr0 <> arr1
632− goArray s [] st src
633− PR.Continue 0 s ->
634− goArray s (x:backBuf) st (Array contents next end)
635− PR.Continue n s -> do
636− assert (n <= Prelude.length (x:backBuf)) (return ())
637− let (src0, buf1) = splitAt n (x:backBuf)
638− arr0 = A.fromListN n (Prelude.reverse src0)
639− arr1 = Array contents next end
640− src = arr0 <> arr1
641− goArray s buf1 st src
642− PR.Done 0 b -> do
643− let arr = Array contents next end
644− return (Right b, K.cons arr st)
645− PR.Done n b -> do
646− assert (n <= Prelude.length (x:backBuf)) (return ())
647− let src0 = Prelude.take n (x:backBuf)
648− -- XXX Use fromListRevN once implemented
649− -- arr0 = A.fromListRevN n src0
650− arr0 = A.fromListN n (Prelude.reverse src0)
651− arr1 = Array contents next end
652− str = K.cons arr0 (K.cons arr1 st)
653− return (Right b, str)
654− PR.Error err -> do
655− let n = Prelude.length backBuf
656− arr0 = A.fromListN n (Prelude.reverse backBuf)
657− arr1 = Array contents cur end
658− str = K.cons arr0 (K.cons arr1 stream)
659− return (Left (ParseError err), str)
660−
661− -- This is a simplified goArray
662− goExtract !pst backBuf (Array _ cur end)
663− | cur == end = goStop pst backBuf
664− goExtract !pst backBuf (Array contents cur end) = do
665− x <- liftIO $ peekByteIndex cur contents
666− pRes <- pstep pst x
667− let next = INDEX_NEXT(cur,a)
668− case pRes of
669− PR.Partial 0 s ->
670− goExtract s [] (Array contents next end)
671− PR.Partial n s -> do
672− assert (n <= Prelude.length (x:backBuf)) (return ())
673− let src0 = Prelude.take n (x:backBuf)
674− arr0 = A.fromListN n (Prelude.reverse src0)
675− arr1 = Array contents next end
676− src = arr0 <> arr1
677− goExtract s [] src
678− PR.Continue 0 s ->
679− goExtract s backBuf (Array contents next end)
680− PR.Continue n s -> do
681− assert (n <= Prelude.length (x:backBuf)) (return ())
682− let (src0, buf1) = splitAt n (x:backBuf)
683− arr0 = A.fromListN n (Prelude.reverse src0)
684− arr1 = Array contents next end
685− src = arr0 <> arr1
686− goExtract s buf1 src
687− PR.Done 0 b -> do
688− let arr = Array contents next end
689− return (Right b, K.fromPure arr)
690− PR.Done n b -> do
691− assert (n <= Prelude.length backBuf) (return ())
692− let src0 = Prelude.take n backBuf
693− -- XXX Use fromListRevN once implemented
694− -- arr0 = A.fromListRevN n src0
695− arr0 = A.fromListN n (Prelude.reverse src0)
696− arr1 = Array contents next end
697− str = K.cons arr0 (K.fromPure arr1)
698− return (Right b, str)
699− PR.Error err -> do
700− let n = Prelude.length backBuf
701− arr0 = A.fromListN n (Prelude.reverse backBuf)
702− arr1 = Array contents cur end
703− str = K.cons arr0 (K.cons arr1 stream)
704− return (Left (ParseError err), str)
705−
706− -- This is a simplified goExtract
707− {-# INLINE goStop #-}
708− goStop !pst backBuf = do
709− pRes <- extract pst
710− case pRes of
711− PR.Partial _ _ -> error "Bug: parseBreak: Partial in extract"
712− PR.Continue 0 s ->
713− goStop s backBuf
714− PR.Continue n s -> do
715− assert (n <= Prelude.length backBuf) (return ())
716− let (src0, buf1) = splitAt n backBuf
717− arr = A.fromListN n (Prelude.reverse src0)
718− goExtract s buf1 arr
719− PR.Done 0 b ->
720− return (Right b, K.nil)
721− PR.Done n b -> do
722− assert (n <= Prelude.length backBuf) (return ())
723− let src0 = Prelude.take n backBuf
724− -- XXX Use fromListRevN once implemented
725− -- arr0 = A.fromListRevN n src0
726− arr0 = A.fromListN n (Prelude.reverse src0)
727− return (Right b, K.fromPure arr0)
728− PR.Error err -> do
729− let n = Prelude.length backBuf
730− arr0 = A.fromListN n (Prelude.reverse backBuf)
731− return (Left (ParseError err), K.fromPure arr0)
732−
733273-- | Parse an array stream using the supplied 'Parser'. Returns the parse
734274-- result and the unconsumed stream. Throws 'ParseError' if the parse fails.
735275--
@@ -753,7 +293,7 @@ parseBreak ::
753293parseBreak p s =
754294 fmap fromStreamD <$> parseBreakD (PRD.fromParserK p) (toStreamD s)
755295-}
756−parseBreak = parseBreakK
296+parseBreak = Array.parseBreakChunksK
757297-- parseBreak p = K.parseBreakChunks (ParserK.adaptC p)
758298
759299-------------------------------------------------------------------------------
core/src/Streamly/Internal/Data/Array/Type.hs+328−86
@@ -1,8 +1,11 @@
11{-# LANGUAGE CPP #-}
2+{-# LANGUAGE TypeFamilies #-}
3+-- Must come after TypeFamilies, otherwise it is re-enabled.
4+-- MonoLocalBinds enabled by TypeFamilies causes perf regressions in general.
5+{-# LANGUAGE NoMonoLocalBinds #-}
26-- |
37-- Module : Streamly.Internal.Data.Array.Type
48-- Copyright : (c) 2020 Composewell Technologies
5−--
69-- License : BSD3-3-Clause
710-- Maintainer : streamly@composewell.com
811-- Stability : experimental
@@ -10,33 +13,57 @@
1013--
1114-- See notes in "Streamly.Internal.Data.MutArray.Type"
1215--
16+
1317module Streamly.Internal.Data.Array.Type
1418 (
1519 -- ** Type
1620 -- $arrayNotes
1721 Array (..)
18− , asPtrUnsafe
19− , nil
2022
21− -- ** Freezing and Thawing
23+ -- ** Conversion
24+ -- *** Mutable and Immutable
2225 , unsafeFreeze
2326 , unsafeFreezeWithShrink
2427 , unsafeThaw
2528
26− -- ** Pinning and Unpinning
29+ -- *** Pinned and Unpinned
2730 , pin
2831 , unpin
2932 , isPinned
3033
34+ -- *** Casting
35+ , unsafePinnedAsPtr
36+
3137 -- ** Construction
32− , splice
38+ , empty
3339
34− , fromList
35− , pinnedFromList
40+ -- *** Cloning
41+ -- XXX Why would we clone an immutable array?
42+ , clone
43+ , pinnedClone
44+
45+ -- *** Slicing
46+ -- | Get a subarray without copying
47+ , splitAt
48+ , breakOn -- XXX requires MonadIO
49+
50+ -- *** Stream Folds
51+ , unsafeMakePure
52+ , createOf
53+ , pinnedCreateOf
54+ , unsafeCreateOf
55+ , unsafePinnedCreateOf
56+ , create
57+ , pinnedCreate
58+ , createWith
59+
60+ -- *** From containers
3661 , fromListN
3762 , pinnedFromListN
38− , fromListRev
63+ , fromList
64+ , pinnedFromList
3965 , fromListRevN
66+ , fromListRev
4067 , fromStreamN
4168 , fromStream
4269 , fromPureStreamN
@@ -44,54 +71,68 @@ module Streamly.Internal.Data.Array.Type
4471 , fromByteStr#
4572 , fromByteStr
4673 , fromPtrN
74+ , fromChunks
4775 , fromChunksK
4876
49− -- ** Split
50− , breakOn
51− , splitAt
52−
53− -- ** Cloning arrays
54− , clone
55− , pinnedClone
56−
57− -- ** Elimination
58− , unsafeIndexIO
59− , getIndexUnsafe
60− , byteLength
61− , length
77+ -- ** Reading
6278
63− , foldl'
64− , foldr
79+ -- *** Indexing
80+ , unsafeIndexIO -- XXX unsafeGetIndexIO
81+ , getIndexUnsafe -- XXX unsafeGetIndex
6582
66− , toStreamK
67− , toStreamKRev
83+ -- *** To Streams
6884 , read
6985 , readRev
70− , readerRev
86+ , toStreamK
87+ , toStreamKRev
88+
89+ -- *** To Containers
7190 , toList
7291
73− -- ** Folds
74− , writeWith
75− , writeN
76− , pinnedWriteN
77− , writeNUnsafe
78− , pinnedWriteNUnsafe
79− , MA.ArrayUnsafe (..)
80− , pinnedWriteNAligned
81− , write
82− , pinnedWrite
83− , unsafeMakePure
92+ -- *** Unfolds
93+ , producer -- experimental
94+ , readerUnsafe
95+ , reader
96+ , readerRev
97+
98+ -- *** Size
99+ , length
100+ , byteLength
101+
102+ -- ** Folding
103+ , foldl'
104+ , foldr
84105 , byteCmp
85106 , byteEq
86107
108+ -- ** Appending
109+ , splice -- XXX requires MonadIO
110+
87111 -- ** Streams of arrays
112+ -- *** Chunk
113+ -- | Group a stream into arrays.
88114 , chunksOf
89115 , pinnedChunksOf
90116 , buildChunks
91− , concatChunks
92− , concatChunksRev
117+
118+ -- *** Split
119+ -- | Split an array into slices.
120+
121+ -- *** Concat
122+ -- | Append the arrays in a stream to form a stream of elements.
123+ , concat
124+ , concatRev
125+
126+ -- *** Compact
127+ -- | Append the arrays in a stream to form a stream of larger arrays.
128+ , fCompactGE
129+ , fPinnedCompactGE
130+ , lCompactGE
131+ , lPinnedCompactGE
132+ , compactGE
93133
94134 -- ** Deprecated
135+ , asPtrUnsafe
95136 , unsafeIndex
96137 , bufferChunks
97138 , flattenArrays
@@ -103,6 +144,15 @@ module Streamly.Internal.Data.Array.Type
103144 , toStreamDRev
104145 , toStream
105146 , toStreamRev
147+ , nil
148+ , writeWith
149+ , writeN
150+ , pinnedWriteN
151+ , writeNUnsafe
152+ , pinnedWriteNUnsafe
153+ , pinnedWriteNAligned
154+ , write
155+ , pinnedWrite
106156 )
107157where
108158
@@ -121,6 +171,7 @@ import GHC.Exts (IsList, IsString(..), Addr#)
121171
122172import GHC.IO (unsafePerformIO)
123173import GHC.Ptr (Ptr(..))
174+import Streamly.Internal.Data.Producer.Type (Producer(..))
124175import Streamly.Internal.Data.MutArray.Type (MutArray(..))
125176import Streamly.Internal.Data.MutByteArray.Type (MutByteArray)
126177import Streamly.Internal.Data.Fold.Type (Fold(..))
@@ -130,13 +181,15 @@ import Streamly.Internal.Data.Unbox (Unbox(..))
130181import Streamly.Internal.Data.Unfold.Type (Unfold(..))
131182import Text.Read (readPrec)
132183
133−import Prelude hiding (Foldable(..), read, unlines, splitAt)
184+import Prelude hiding (Foldable(..), concat, read, unlines, splitAt)
134185
135186import qualified GHC.Exts as Exts
187+import qualified Streamly.Internal.Data.Fold.Type as Fold
136188import qualified Streamly.Internal.Data.MutArray.Type as MA
137189import qualified Streamly.Internal.Data.Stream.Type as D
138190import qualified Streamly.Internal.Data.StreamK.Type as K
139191import qualified Streamly.Internal.Data.MutByteArray.Type as Unboxed
192+import qualified Streamly.Internal.Data.Producer as Producer
140193import qualified Streamly.Internal.Data.Unfold.Type as Unfold
141194import qualified Text.ParserCombinators.ReadPrec as ReadPrec
142195
@@ -190,15 +243,20 @@ data Array a =
190243
191244-- | Use an @Array a@ as @Ptr a@.
192245--
193−-- See 'MA.asPtrUnsafe' in the Mutable array module for more details.
246+-- See 'MA.unsafePinnedAsPtr' in the Mutable array module for more details.
194247--
195248-- /Unsafe/
196249--
197250-- /Pre-release/
198251--
252+{-# INLINE unsafePinnedAsPtr #-}
253+unsafePinnedAsPtr :: MonadIO m => Array a -> (Ptr a -> m b) -> m b
254+unsafePinnedAsPtr arr = MA.unsafePinnedAsPtr (unsafeThaw arr)
255+
256+{-# DEPRECATED asPtrUnsafe "Please use unsafePinnedAsPtr instead." #-}
199257{-# INLINE asPtrUnsafe #-}
200258asPtrUnsafe :: MonadIO m => Array a -> (Ptr a -> m b) -> m b
201−asPtrUnsafe arr = MA.asPtrUnsafe (unsafeThaw arr)
259+asPtrUnsafe = unsafePinnedAsPtr
202260
203261-------------------------------------------------------------------------------
204262-- Freezing and Thawing
@@ -414,37 +472,100 @@ pinnedChunksOf :: forall m a. (MonadIO m, Unbox a)
414472 => Int -> D.Stream m a -> D.Stream m (Array a)
415473pinnedChunksOf n str = D.map unsafeFreeze $ MA.pinnedChunksOf n str
416474
417−-- | Use the "reader" unfold instead.
418−--
419−-- @concatChunks = unfoldMany reader@
475+-- | Convert a stream of arrays into a stream of their elements.
420476--
421−-- We can try this if there are any fusion issues in the unfold.
477+-- >>> concat = Stream.unfoldMany Array.reader
422478--
423−{-# INLINE_NORMAL concatChunks #-}
424−concatChunks :: forall m a. (MonadIO m, Unbox a)
425− => D.Stream m (Array a) -> D.Stream m a
426−concatChunks = MA.concatChunks . D.map unsafeThaw
479+{-# INLINE_NORMAL concat #-}
480+concat :: (Monad m, Unbox a) => Stream m (Array a) -> Stream m a
481+-- XXX this requires MonadIO whereas the unfoldMany version does not
482+concat = MA.concatWith (pure . unsafeInlineIO) . D.map unsafeThaw
483+-- concat = D.unfoldMany reader
427484
428485{-# DEPRECATED flattenArrays "Please use \"unfoldMany reader\" instead." #-}
486+{-# INLINE flattenArrays #-}
429487flattenArrays :: forall m a. (MonadIO m, Unbox a)
430488 => D.Stream m (Array a) -> D.Stream m a
431−flattenArrays = concatChunks
489+flattenArrays = concat
432490
433−-- | Use the "readerRev" unfold instead.
434−--
435−-- @concatChunksRev = unfoldMany readerRev@
491+-- | Convert a stream of arrays into a stream of their elements reversing the
492+-- contents of each array before flattening.
436493--
437−-- We can try this if there are any fusion issues in the unfold.
494+-- >>> concatRev = Stream.unfoldMany Array.readerRev
438495--
439−{-# INLINE_NORMAL concatChunksRev #-}
440−concatChunksRev :: forall m a. (MonadIO m, Unbox a)
496+{-# INLINE_NORMAL concatRev #-}
497+concatRev :: forall m a. (Monad m, Unbox a)
441498 => D.Stream m (Array a) -> D.Stream m a
442−concatChunksRev = MA.concatChunksRev . D.map unsafeThaw
499+-- XXX this requires MonadIO whereas the unfoldMany version does not
500+concatRev = MA.concatRevWith (pure . unsafeInlineIO) . D.map unsafeThaw
501+-- concatRev = D.unfoldMany readerRev
443502
444503{-# DEPRECATED flattenArraysRev "Please use \"unfoldMany readerRev\" instead." #-}
504+{-# INLINE flattenArraysRev #-}
445505flattenArraysRev :: forall m a. (MonadIO m, Unbox a)
446506 => D.Stream m (Array a) -> D.Stream m a
447−flattenArraysRev = concatChunksRev
507+flattenArraysRev = concatRev
508+
509+-------------------------------------------------------------------------------
510+-- Compact
511+-------------------------------------------------------------------------------
512+
513+-- XXX Note that this thaws immutable arrays for appending, that may be
514+-- problematic if multiple users do the same thing, however, thawed immutable
515+-- arrays would have no capacity to append, therefore, a copy will be forced
516+-- anyway.
517+
518+-- | Fold @fCompactGE n@ coalesces adjacent arrays in the input stream
519+-- until the size becomes greater than or equal to n.
520+--
521+-- Generates unpinned arrays irrespective of the pinning status of input
522+-- arrays.
523+{-# INLINE_NORMAL fCompactGE #-}
524+fCompactGE :: (MonadIO m, Unbox a) => Int -> Fold m (Array a) (Array a)
525+fCompactGE n = fmap unsafeFreeze $ Fold.lmap unsafeThaw $ MA.fCompactGE n
526+
527+-- | PInned version of 'fCompactGE'.
528+{-# INLINE_NORMAL fPinnedCompactGE #-}
529+fPinnedCompactGE :: (MonadIO m, Unbox a) => Int -> Fold m (Array a) (Array a)
530+fPinnedCompactGE n =
531+ fmap unsafeFreeze $ Fold.lmap unsafeThaw $ MA.fPinnedCompactGE n
532+
533+-- | @compactGE n stream@ coalesces adjacent arrays in the @stream@ until
534+-- the size becomes greater than or equal to @n@.
535+--
536+-- >>> compactGE n = Stream.foldMany (Array.fCompactGE n)
537+--
538+-- Generates unpinned arrays irrespective of the pinning status of input
539+-- arrays.
540+{-# INLINE compactGE #-}
541+compactGE ::
542+ (MonadIO m, Unbox a)
543+ => Int -> Stream m (Array a) -> Stream m (Array a)
544+compactGE n stream =
545+ D.map unsafeFreeze $ MA.compactGE n $ D.map unsafeThaw stream
546+
547+-- | Like 'compactGE' but for transforming folds instead of stream.
548+--
549+-- >>> lCompactGE n = Fold.many (Array.fCompactGE n)
550+--
551+-- Generates unpinned arrays irrespective of the pinning status of input
552+-- arrays.
553+{-# INLINE_NORMAL lCompactGE #-}
554+lCompactGE :: (MonadIO m, Unbox a)
555+ => Int -> Fold m (Array a) () -> Fold m (Array a) ()
556+lCompactGE n fld =
557+ Fold.lmap unsafeThaw $ MA.lCompactGE n (Fold.lmap unsafeFreeze fld)
558+
559+-- | Pinned version of 'lCompactGE'.
560+{-# INLINE_NORMAL lPinnedCompactGE #-}
561+lPinnedCompactGE :: (MonadIO m, Unbox a)
562+ => Int -> Fold m (Array a) () -> Fold m (Array a) ()
563+lPinnedCompactGE n fld =
564+ Fold.lmap unsafeThaw $ MA.lPinnedCompactGE n (Fold.lmap unsafeFreeze fld)
565+
566+-------------------------------------------------------------------------------
567+-- Splitting
568+-------------------------------------------------------------------------------
448569
449570-- Drops the separator byte
450571{-# INLINE breakOn #-}
@@ -488,6 +609,49 @@ byteLength = MA.byteLength . unsafeThaw
488609length :: Unbox a => Array a -> Int
489610length arr = MA.length (unsafeThaw arr)
490611
612+{-# INLINE_NORMAL producer #-}
613+producer :: forall m a. (Monad m, Unbox a) => Producer m (Array a) a
614+producer =
615+ Producer.translate unsafeThaw unsafeFreeze
616+ $ MA.producerWith (return . unsafeInlineIO)
617+
618+-- | Unfold an array into a stream.
619+--
620+{-# INLINE_NORMAL reader #-}
621+reader :: forall m a. (Monad m, Unbox a) => Unfold m (Array a) a
622+reader = Producer.simplify producer
623+
624+-- | Unfold an array into a stream, does not check the end of the array, the
625+-- user is responsible for terminating the stream within the array bounds. For
626+-- high performance application where the end condition can be determined by
627+-- a terminating fold.
628+--
629+-- Written in the hope that it may be faster than "read", however, in the case
630+-- for which this was written, "read" proves to be faster even though the core
631+-- generated with unsafeRead looks simpler.
632+--
633+-- /Pre-release/
634+--
635+{-# INLINE_NORMAL readerUnsafe #-}
636+readerUnsafe :: forall m a. (Monad m, Unbox a) => Unfold m (Array a) a
637+readerUnsafe = Unfold step inject
638+ where
639+
640+ inject (Array contents start end) =
641+ return (MA.ArrayUnsafe contents end start)
642+
643+ {-# INLINE_LATE step #-}
644+ step (MA.ArrayUnsafe contents end p) = do
645+ -- unsafeInlineIO allows us to run this in Identity monad for pure
646+ -- toList/foldr case which makes them much faster due to not
647+ -- accumulating the list and fusing better with the pure consumers.
648+ --
649+ -- This should be safe as the array contents are guaranteed to be
650+ -- evaluated/written to before we peek at them.
651+ let !x = unsafeInlineIO $ peekAt p contents
652+ let !p1 = INDEX_NEXT(p,a)
653+ return $ D.Yield x (MA.ArrayUnsafe contents end p1)
654+
491655-- | Unfold an array into a stream in reverse order.
492656--
493657{-# INLINE_NORMAL readerRev #-}
@@ -593,17 +757,28 @@ toList s = build (\c n -> toListFB c n s)
593757-- Folds
594758-------------------------------------------------------------------------------
595759
596−-- | @writeN n@ folds a maximum of @n@ elements from the input stream to an
760+-- | @createOf n@ folds a maximum of @n@ elements from the input stream to an
597761-- 'Array'.
598762--
599−{-# INLINE_NORMAL writeN #-}
763+{-# INLINE_NORMAL createOf #-}
764+createOf :: forall m a. (MonadIO m, Unbox a) => Int -> Fold m a (Array a)
765+createOf = fmap unsafeFreeze . MA.createOf
766+
767+-- XXX Deprecate in major
768+-- {-# DEPRECATED writeN "Please use createOf instead." #-}
769+{-# INLINE writeN #-}
600770writeN :: forall m a. (MonadIO m, Unbox a) => Int -> Fold m a (Array a)
601−writeN = fmap unsafeFreeze . MA.writeN
771+writeN = createOf
602772
603−-- | Like 'fromListN' but creates a pinned array.
604−{-# INLINE_NORMAL pinnedWriteN #-}
773+-- | Like 'createOf' but creates a pinned array.
774+{-# INLINE_NORMAL pinnedCreateOf #-}
775+pinnedCreateOf :: forall m a. (MonadIO m, Unbox a) => Int -> Fold m a (Array a)
776+pinnedCreateOf = fmap unsafeFreeze . MA.pinnedCreateOf
777+
778+{-# DEPRECATED pinnedWriteN "Please use pinnedCreateOf instead." #-}
779+{-# INLINE pinnedWriteN #-}
605780pinnedWriteN :: forall m a. (MonadIO m, Unbox a) => Int -> Fold m a (Array a)
606−pinnedWriteN = fmap unsafeFreeze . MA.pinnedWriteN
781+pinnedWriteN = pinnedCreateOf
607782
608783-- | @pinnedWriteNAligned alignment n@ folds a maximum of @n@ elements from the input
609784-- stream to an 'Array' aligned to the given size.
@@ -611,44 +786,74 @@ pinnedWriteN = fmap unsafeFreeze . MA.pinnedWriteN
611786-- /Pre-release/
612787--
613788{-# INLINE_NORMAL pinnedWriteNAligned #-}
789+{-# DEPRECATED pinnedWriteNAligned "To be removed." #-}
614790pinnedWriteNAligned :: forall m a. (MonadIO m, Unbox a)
615791 => Int -> Int -> Fold m a (Array a)
616792pinnedWriteNAligned alignSize = fmap unsafeFreeze . MA.pinnedWriteNAligned alignSize
617793
618−-- | Like 'writeN' but does not check the array bounds when writing. The fold
794+-- | Like 'createOf' but does not check the array bounds when writing. The fold
619795-- driver must not call the step function more than 'n' times otherwise it will
620796-- corrupt the memory and crash. This function exists mainly because any
621797-- conditional in the step function blocks fusion causing 10x performance
622798-- slowdown.
623799--
624−{-# INLINE_NORMAL writeNUnsafe #-}
800+{-# INLINE_NORMAL unsafeCreateOf #-}
801+unsafeCreateOf :: forall m a. (MonadIO m, Unbox a)
802+ => Int -> Fold m a (Array a)
803+unsafeCreateOf n = unsafeFreeze <$> MA.unsafeCreateOf n
804+
805+{-# DEPRECATED writeNUnsafe "Please use unsafeCreateOf instead." #-}
806+{-# INLINE writeNUnsafe #-}
625807writeNUnsafe :: forall m a. (MonadIO m, Unbox a)
626808 => Int -> Fold m a (Array a)
627−writeNUnsafe n = unsafeFreeze <$> MA.writeNUnsafe n
809+writeNUnsafe = unsafeCreateOf
628810
629−{-# INLINE_NORMAL pinnedWriteNUnsafe #-}
811+{-# INLINE_NORMAL unsafePinnedCreateOf #-}
812+unsafePinnedCreateOf :: forall m a. (MonadIO m, Unbox a)
813+ => Int -> Fold m a (Array a)
814+unsafePinnedCreateOf n = unsafeFreeze <$> MA.unsafePinnedCreateOf n
815+
816+{-# DEPRECATED pinnedWriteNUnsafe "Please use unsafePinnedCreateOf instead." #-}
817+{-# INLINE pinnedWriteNUnsafe #-}
630818pinnedWriteNUnsafe :: forall m a. (MonadIO m, Unbox a)
631819 => Int -> Fold m a (Array a)
632−pinnedWriteNUnsafe n = unsafeFreeze <$> MA.pinnedWriteNUnsafe n
820+pinnedWriteNUnsafe = unsafePinnedCreateOf
821+
822+{-# INLINE_NORMAL createWith #-}
823+createWith :: forall m a. (MonadIO m, Unbox a)
824+ => Int -> Fold m a (Array a)
825+-- createWith n = FL.rmapM spliceArrays $ toArraysOf n
826+createWith elemCount = unsafeFreeze <$> MA.createWith elemCount
633827
634−{-# INLINE_NORMAL writeWith #-}
828+{-# DEPRECATED writeWith "Please use createWith instead." #-}
829+{-# INLINE writeWith #-}
635830writeWith :: forall m a. (MonadIO m, Unbox a)
636831 => Int -> Fold m a (Array a)
637−-- writeWith n = FL.rmapM spliceArrays $ toArraysOf n
638−writeWith elemCount = unsafeFreeze <$> MA.writeWith elemCount
832+writeWith = createWith
639833
640834-- | Fold the whole input to a single array.
641835--
642836-- /Caution! Do not use this on infinite streams./
643837--
838+{-# INLINE create #-}
839+create :: forall m a. (MonadIO m, Unbox a) => Fold m a (Array a)
840+create = fmap unsafeFreeze MA.create
841+
842+-- XXX Deprecate in major
843+-- {-# DEPRECATED write "Please use create instead." #-}
644844{-# INLINE write #-}
645845write :: forall m a. (MonadIO m, Unbox a) => Fold m a (Array a)
646−write = fmap unsafeFreeze MA.write
846+write = create
847+
848+-- | Like 'create' but creates a pinned array.
849+{-# INLINE pinnedCreate #-}
850+pinnedCreate :: forall m a. (MonadIO m, Unbox a) => Fold m a (Array a)
851+pinnedCreate = fmap unsafeFreeze MA.pinnedCreate
647852
648−-- | Like 'write' but creates a pinned array.
853+{-# DEPRECATED pinnedWrite "Please use pinnedCreate instead." #-}
649854{-# INLINE pinnedWrite #-}
650855pinnedWrite :: forall m a. (MonadIO m, Unbox a) => Fold m a (Array a)
651−pinnedWrite = fmap unsafeFreeze MA.pinnedWrite
856+pinnedWrite = pinnedCreate
652857
653858-- | Fold "step" has a dependency on "initial", and each step is dependent on
654859-- the previous invocation of step due to state passing, finally extract
@@ -696,13 +901,19 @@ fromPureStream x = unsafePerformIO $ fmap (unsafeFreeze) (MA.fromPureStream x)
696901-- fromPureStream = runIdentity . D.fold (unsafeMakePure write)
697902-- fromPureStream = fromList . runIdentity . D.toList
698903
904+-- XXX This should be monadic.
905+
699906-- | Copy an immutable 'Ptr Word8' sequence into an array.
700907--
701908-- /Unsafe:/ The caller is responsible for safe addressing.
702909--
910+-- Note that this should be evaluated strictly to ensure that we do not hold
911+-- the reference to the pointer in a lazy thunk.
703912fromPtrN :: Int -> Ptr Word8 -> Array Word8
704913fromPtrN n addr = unsafePerformIO $ fmap unsafeFreeze (MA.fromPtrN n addr)
705914
915+-- XXX This should be monadic.
916+
706917-- | Copy a null terminated immutable 'Addr#' Word8 sequence into an array.
707918--
708919-- /Unsafe:/ The caller is responsible for safe addressing.
@@ -713,9 +924,15 @@ fromPtrN n addr = unsafePerformIO $ fmap unsafeFreeze (MA.fromPtrN n addr)
713924-- >>> Array.toList $ Array.fromByteStr# "\1\2\3\0"#
714925-- [1,2,3]
715926--
927+-- Note that this should be evaluated strictly to ensure that we do not hold
928+-- the reference to the pointer in a lazy thunk.
716929fromByteStr# :: Addr# -> Array Word8
717930fromByteStr# addr = unsafePerformIO $ fmap unsafeFreeze (MA.fromByteStr# addr)
718931
932+-- XXX This should be monadic.
933+
934+-- | Note that this should be evaluated strictly to ensure that we do not hold
935+-- the reference to the pointer in a lazy thunk.
719936fromByteStr :: Ptr Word8 -> Array Word8
720937fromByteStr (Ptr addr#) = fromByteStr# addr#
721938
@@ -725,14 +942,29 @@ fromByteStr (Ptr addr#) = fromByteStr# addr#
725942-- that is double the size of the array stream.
726943--
727944{-# INLINE fromChunksK #-}
728−fromChunksK :: (Unbox a, MonadIO m) => StreamK m (Array a) -> m (Array a)
945+fromChunksK :: (MonadIO m, Unbox a) => StreamK m (Array a) -> m (Array a)
729946fromChunksK stream =
947+ -- We buffer the entire stream and then allocate the target array of the
948+ -- same size, thus requiring double the memory.
730949 fmap unsafeFreeze $ MA.fromChunksK $ fmap unsafeThaw stream
731950
732951{-# DEPRECATED fromArrayStreamK "Please use fromChunksK instead." #-}
733952fromArrayStreamK :: (Unbox a, MonadIO m) => StreamK m (Array a) -> m (Array a)
734953fromArrayStreamK = fromChunksK
735954
955+-- | Given a stream of arrays, splice them all together to generate a single
956+-- array. The stream must be /finite/.
957+--
958+{-# INLINE fromChunks #-}
959+fromChunks :: (MonadIO m, Unbox a) => Stream m (Array a) -> m (Array a)
960+fromChunks s =
961+ -- XXX Check which implementation is better
962+ -- This may also require double the memory as we double the space every
963+ -- time, when copying the last array we may have reallocated almost double
964+ -- the space required before we right size it.
965+ fmap unsafeFreeze $ MA.fromChunksRealloced (fmap unsafeThaw s)
966+ -- fromChunkStreamK $ D.toStreamK s
967+
736968-------------------------------------------------------------------------------
737969-- Instances
738970-------------------------------------------------------------------------------
@@ -775,6 +1007,7 @@ instance Unbox a => IsList (Array a) where
7751007{-# INLINE byteCmp #-}
7761008byteCmp :: Array a -> Array a -> Ordering
7771009byteCmp arr1 arr2 =
1010+ -- unsafePerformIO?
7781011 unsafeInlineIO $! unsafeThaw arr1 `MA.byteCmp` unsafeThaw arr2
7791012
7801013-- | Byte equality of two arrays.
@@ -796,20 +1029,21 @@ MK_EQ_INSTANCE(Char)
7961029MK_EQ_INSTANCE(Word8)
7971030MK_EQ_INSTANCE(Word16)
7981031MK_EQ_INSTANCE(Word32)
1032+
1033+-- XXX The Word64 default instance should be as fast because we are comparing
1034+-- 64-bit at a time.
7991035MK_EQ_INSTANCE(Word64)
8001036MK_EQ_INSTANCE(Int)
8011037MK_EQ_INSTANCE(Int8)
8021038MK_EQ_INSTANCE(Int16)
8031039MK_EQ_INSTANCE(Int32)
1040+
1041+-- XXX The Int64 default instance should be as fast.
8041042MK_EQ_INSTANCE(Int64)
8051043
806−-- | If the type allows a byte-by-byte comparison (see 'byteCmp') this instance
807−-- can be overlapped by a more specific instance that uses a byte comparison.
808−-- Byte comparison can be significantly faster.
809−--
810−-- For example:
811−--
812−-- >>> (==) = byteCmp
1044+-- | If the type allows a byte-by-byte comparison this instance can be
1045+-- overlapped by a more specific instance that uses 'byteCmp'. Byte comparison
1046+-- can be significantly faster.
8131047--
8141048instance {-# OVERLAPPABLE #-} (Unbox a, Eq a) => Eq (Array a) where
8151049 {-# INLINE (==) #-}
@@ -894,12 +1128,20 @@ instance Foldable Array where
8941128instance Unbox a => Semigroup (Array a) where
8951129 arr1 <> arr2 = unsafePerformIO $ splice arr1 arr2
8961130
1131+empty ::
1132+#ifdef DEVBUILD
1133+ Unbox a =>
1134+#endif
1135+ Array a
1136+empty = Array Unboxed.empty 0 0
1137+
1138+{-# DEPRECATED nil "Please use empty instead." #-}
8971139nil ::
8981140#ifdef DEVBUILD
8991141 Unbox a =>
9001142#endif
9011143 Array a
902−nil = Array Unboxed.nil 0 0
1144+nil = empty
9031145
9041146instance Unbox a => Monoid (Array a) where
9051147 mempty = nil
core/src/Streamly/Internal/Data/IsMap.hs+4
@@ -1,3 +1,7 @@
1+{-# LANGUAGE TypeFamilies #-}
2+-- Must come after TypeFamilies, otherwise it is re-enabled.
3+-- MonoLocalBinds enabled by TypeFamilies causes perf regressions in general.
4+{-# LANGUAGE NoMonoLocalBinds #-}
15-- |
26-- Module : Streamly.Internal.Data.IsMap
37-- Copyright : (c) 2022 Composewell Technologies
core/src/Streamly/Internal/Data/MutArray.hs+128
@@ -13,6 +13,10 @@ module Streamly.Internal.Data.MutArray
1313 -- * MutArray module
1414 , sliceIndexerFromLen
1515 , slicerFromLen
16+ , compactLE
17+ , pinnedCompactLE
18+ , compactOnByte
19+ , compactOnByteSuffix
1620 -- * Unboxed IORef
1721 , module Streamly.Internal.Data.IORef.Unboxed
1822
@@ -24,9 +28,16 @@ where
2428
2529#include "inline.hs"
2630
31+import Control.Monad.IO.Class (MonadIO(..))
32+import Data.Word (Word8)
33+import Streamly.Internal.Data.MutByteArray.Type (PinnedState(..))
34+import Streamly.Internal.Data.Stream.Type (Stream)
2735import Streamly.Internal.Data.Unbox (Unbox)
2836import Streamly.Internal.Data.Unfold.Type (Unfold(..))
2937
38+import qualified Streamly.Internal.Data.Stream.Type as Stream
39+import qualified Streamly.Internal.Data.Stream.Nesting as Stream
40+-- import qualified Streamly.Internal.Data.Stream.Transform as Stream
3041import qualified Streamly.Internal.Data.Unfold as Unfold
3142
3243import Prelude hiding (foldr, length, read, splitAt)
@@ -77,3 +88,120 @@ getSlicesFromLen :: forall m a. (Monad m, Unbox a)
7788 -> Int -- ^ length of the slice
7889 -> Unfold m (MutArray a) (MutArray a)
7990getSlicesFromLen = slicerFromLen
91+
92+-------------------------------------------------------------------------------
93+-- Compacting Streams of Arrays
94+-------------------------------------------------------------------------------
95+
96+-- | @compactLE maxElems@ coalesces adjacent arrays in the input stream
97+-- only if the combined size would be less than or equal to @maxElems@
98+-- elements. Note that it won't split an array if the original array is already
99+-- larger than maxElems.
100+--
101+-- @maxElems@ must be greater than 0.
102+--
103+-- Generates unpinned arrays irrespective of the pinning status of input
104+-- arrays.
105+{-# INLINE compactLE #-}
106+compactLE :: (MonadIO m, Unbox a) =>
107+ Int -> Stream m (MutArray a) -> Stream m (MutArray a)
108+-- XXX compactLE can be moved to MutArray/Type if we are not using the parser
109+-- to implement it.
110+compactLE = compactLeAs Unpinned
111+-- The parser version turns out to be a little bit slower.
112+-- compactLE n = Stream.catRights . Stream.parseManyD (pCompactLE n)
113+
114+-- | Pinned version of 'compactLE'.
115+{-# INLINE pinnedCompactLE #-}
116+pinnedCompactLE :: forall m a. (MonadIO m, Unbox a)
117+ => Int -> Stream m (MutArray a) -> Stream m (MutArray a)
118+pinnedCompactLE = compactLeAs Pinned
119+-- pinnedCompactLE n = Stream.catRights . Stream.parseManyD (pPinnedCompactLE n)
120+
121+data SplitState s arr
122+ = Initial s
123+ | Buffering s arr
124+ | Splitting s arr
125+ | Yielding arr (SplitState s arr)
126+ | Finishing
127+
128+-- | Split a stream of arrays on a given separator byte, dropping the separator
129+-- and coalescing all the arrays between two separators into a single array.
130+--
131+{-# INLINE_NORMAL _compactOnByteCustom #-}
132+_compactOnByteCustom
133+ :: MonadIO m
134+ => Word8
135+ -> Stream m (MutArray Word8)
136+ -> Stream m (MutArray Word8)
137+_compactOnByteCustom byte (Stream.Stream step state) =
138+ Stream.Stream step' (Initial state)
139+
140+ where
141+
142+ {-# INLINE_LATE step' #-}
143+ step' gst (Initial st) = do
144+ r <- step gst st
145+ case r of
146+ Stream.Yield arr s -> do
147+ (arr1, marr2) <- breakOn byte arr
148+ return $ case marr2 of
149+ Nothing -> Stream.Skip (Buffering s arr1)
150+ Just arr2 -> Stream.Skip (Yielding arr1 (Splitting s arr2))
151+ Stream.Skip s -> return $ Stream.Skip (Initial s)
152+ Stream.Stop -> return Stream.Stop
153+
154+ step' gst (Buffering st buf) = do
155+ r <- step gst st
156+ case r of
157+ Stream.Yield arr s -> do
158+ (arr1, marr2) <- breakOn byte arr
159+ -- XXX Use spliceExp instead and then rightSize?
160+ buf1 <- splice buf arr1
161+ return $ case marr2 of
162+ Nothing -> Stream.Skip (Buffering s buf1)
163+ Just x -> Stream.Skip (Yielding buf1 (Splitting s x))
164+ Stream.Skip s -> return $ Stream.Skip (Buffering s buf)
165+ Stream.Stop -> return $
166+ if byteLength buf == 0
167+ then Stream.Stop
168+ else Stream.Skip (Yielding buf Finishing)
169+
170+ step' _ (Splitting st buf) = do
171+ (arr1, marr2) <- breakOn byte buf
172+ return $ case marr2 of
173+ Nothing -> Stream.Skip $ Buffering st arr1
174+ Just arr2 -> Stream.Skip $ Yielding arr1 (Splitting st arr2)
175+
176+ step' _ (Yielding arr next) = return $ Stream.Yield arr next
177+ step' _ Finishing = return Stream.Stop
178+
179+-- XXX implement predicate based version of this
180+-- XXX Naming of predicate based vs custom version
181+
182+-- | Split a stream of arrays on a given separator byte, dropping the separator
183+-- and coalescing all the arrays between two separators into a single array.
184+--
185+{-# INLINE compactOnByte #-}
186+compactOnByte
187+ :: (MonadIO m)
188+ => Word8
189+ -> Stream m (MutArray Word8)
190+ -> Stream m (MutArray Word8)
191+-- XXX compare perf of custom vs idiomatic version
192+-- compactOnByte = _compactOnByteCustom
193+-- XXX use spliceExp and rightSize?
194+compactOnByte byte = Stream.splitInnerBy (breakOn byte) splice
195+
196+-- | Like 'compactOnByte' considers the separator in suffix position instead of
197+-- infix position.
198+{-# INLINE compactOnByteSuffix #-}
199+compactOnByteSuffix
200+ :: (MonadIO m)
201+ => Word8
202+ -> Stream m (MutArray Word8)
203+ -> Stream m (MutArray Word8)
204+compactOnByteSuffix byte =
205+ -- XXX use spliceExp and rightSize?
206+ Stream.splitInnerBySuffix
207+ (\arr -> byteLength arr == 0) (breakOn byte) splice
core/src/Streamly/Internal/Data/MutArray/Generic.hs+65−30
@@ -19,14 +19,14 @@ module Streamly.Internal.Data.MutArray.Generic
1919 , nil
2020
2121 -- *** Uninitialized Arrays
22− , new
22+ , emptyOf
2323 -- , newArrayWith
2424
2525 -- *** From streams
26− , writeNUnsafe
27− , writeN
28− , writeWith
29− , write
26+ , unsafeCreateOf
27+ , createOf
28+ , createWith -- createOfMin/createMin/createGE?
29+ , create
3030 , fromStreamN
3131 , fromStream
3232 , fromPureStream
@@ -160,6 +160,13 @@ module Streamly.Internal.Data.MutArray.Generic
160160 -- , appendSliceFrom
161161
162162 , clone
163+
164+ -- * Deprecated
165+ , new
166+ , writeNUnsafe
167+ , writeN
168+ , writeWith
169+ , write
163170 )
164171where
165172
@@ -236,13 +243,13 @@ bottomElement =
236243-- XXX Would be nice if GHC can provide something like newUninitializedArray# so
237244-- that we do not have to write undefined or error in the whole array.
238245
239−-- | @new count@ allocates a zero length array that can be extended to hold
246+-- | @emptyOf count@ allocates a zero length array that can be extended to hold
240247-- up to 'count' items without reallocating.
241248--
242249-- /Pre-release/
243−{-# INLINE new #-}
244−new :: MonadIO m => Int -> m (MutArray a)
245−new n@(I# n#) =
250+{-# INLINE emptyOf #-}
251+emptyOf :: MonadIO m => Int -> m (MutArray a)
252+emptyOf n@(I# n#) =
246253 liftIO
247254 $ IO
248255 $ \s# ->
@@ -251,6 +258,12 @@ new n@(I# n#) =
251258 let ma = MutArray arr# 0 0 n
252259 in (# s1#, ma #)
253260
261+-- XXX Deprecate in major
262+-- {-# DEPRECATED new "Please use emptyOf instead." #-}
263+{-# INLINE new #-}
264+new :: MonadIO m => Int -> m (MutArray a)
265+new = emptyOf
266+
254267-- XXX This could be pure?
255268
256269-- |
@@ -318,7 +331,7 @@ putIndices arr = FL.foldlM' step (return ())
318331
319332 where
320333
321− step () (i, x) = liftIO (putIndex i arr x)
334+ step () (i, x) = putIndex i arr x
322335
323336-- | Modify a given index of an array using a modifier function without checking
324337-- the bounds.
@@ -589,16 +602,16 @@ readRev arr@MutArray{..} =
589602arrayChunkSize :: Int
590603arrayChunkSize = 1024
591604
592−-- | Like 'writeN' but does not check the array bounds when writing. The fold
605+-- | Like 'createOf' but does not check the array bounds when writing. The fold
593606-- driver must not call the step function more than 'n' times otherwise it will
594607-- corrupt the memory and crash. This function exists mainly because any
595608-- conditional in the step function blocks fusion causing 10x performance
596609-- slowdown.
597610--
598611-- /Pre-release/
599−{-# INLINE_NORMAL writeNUnsafe #-}
600−writeNUnsafe :: MonadIO m => Int -> Fold m a (MutArray a)
601−writeNUnsafe n = Fold step initial return return
612+{-# INLINE_NORMAL unsafeCreateOf #-}
613+unsafeCreateOf :: MonadIO m => Int -> Fold m a (MutArray a)
614+unsafeCreateOf n = Fold step initial return return
602615
603616 where
604617
@@ -606,59 +619,81 @@ writeNUnsafe n = Fold step initial return return
606619
607620 step arr x = FL.Partial <$> snocUnsafe arr x
608621
609−-- | @writeN n@ folds a maximum of @n@ elements from the input stream to an
622+{-# DEPRECATED writeNUnsafe "Please use unsafeCreateOf instead." #-}
623+{-# INLINE writeNUnsafe #-}
624+writeNUnsafe :: MonadIO m => Int -> Fold m a (MutArray a)
625+writeNUnsafe = unsafeCreateOf
626+
627+-- | @createOf n@ folds a maximum of @n@ elements from the input stream to an
610628-- 'Array'.
611629--
612−-- >>> writeN n = Fold.take n (MutArray.writeNUnsafe n)
630+-- >>> createOf n = Fold.take n (MutArray.unsafeCreateOf n)
613631--
614632-- /Pre-release/
615−{-# INLINE_NORMAL writeN #-}
633+{-# INLINE_NORMAL createOf #-}
634+createOf :: MonadIO m => Int -> Fold m a (MutArray a)
635+createOf n = FL.take n $ unsafeCreateOf n
636+
637+-- XXX Deprecate in major
638+-- {-# DEPRECATED writeN "Please use createOf instead." #-}
639+{-# INLINE writeN #-}
616640writeN :: MonadIO m => Int -> Fold m a (MutArray a)
617−writeN n = FL.take n $ writeNUnsafe n
641+writeN = createOf
618642
619643-- >>> f n = MutArray.writeAppendWith (* 2) (MutArray.pinnedNew n)
620644-- >>> writeWith n = Fold.rmapM MutArray.rightSize (f n)
621645-- >>> writeWith n = Fold.rmapM MutArray.fromArrayStreamK (MutArray.writeChunks n)
622646
623−-- | @writeWith minCount@ folds the whole input to a single array. The array
647+-- | @createWith minCount@ folds the whole input to a single array. The array
624648-- starts at a size big enough to hold minCount elements, the size is doubled
625649-- every time the array needs to be grown.
626650--
627651-- /Caution! Do not use this on infinite streams./
628652--
629653-- /Pre-release/
630−{-# INLINE_NORMAL writeWith #-}
631−writeWith :: MonadIO m => Int -> Fold m a (MutArray a)
654+{-# INLINE_NORMAL createWith #-}
655+createWith :: MonadIO m => Int -> Fold m a (MutArray a)
632656-- writeWith n = FL.rmapM rightSize $ writeAppendWith (* 2) (pinnedNew n)
633−writeWith elemCount = FL.rmapM extract $ FL.foldlM' step initial
657+createWith elemCount = FL.rmapM extract $ FL.foldlM' step initial
634658
635659 where
636660
637661 initial = do
638− when (elemCount < 0) $ error "writeWith: elemCount is negative"
639− liftIO $ new elemCount
662+ when (elemCount < 0) $ error "createWith: elemCount is negative"
663+ new elemCount
640664
641665 step arr@(MutArray _ start end bound) x
642666 | end == bound = do
643667 let oldSize = end - start
644668 newSize = max (oldSize * 2) 1
645− arr1 <- liftIO $ realloc newSize arr
669+ arr1 <- realloc newSize arr
646670 snocUnsafe arr1 x
647671 step arr x = snocUnsafe arr x
648672
649− -- extract = liftIO . rightSize
673+ -- extract = rightSize
650674 extract = return
651675
676+{-# DEPRECATED writeWith "Please use createWith instead." #-}
677+{-# INLINE writeWith #-}
678+writeWith :: MonadIO m => Int -> Fold m a (MutArray a)
679+writeWith = createWith
680+
652681-- | Fold the whole input to a single array.
653682--
654−-- Same as 'writeWith' using an initial array size of 'arrayChunkSize' bytes
683+-- Same as 'createWith' using an initial array size of 'arrayChunkSize' bytes
655684-- rounded up to the element size.
656685--
657686-- /Caution! Do not use this on infinite streams./
658687--
688+{-# INLINE create #-}
689+create :: MonadIO m => Fold m a (MutArray a)
690+create = writeWith arrayChunkSize
691+
692+-- XXX Deprecate in major
693+-- {-# DEPRECATED write "Please use create instead." #-}
659694{-# INLINE write #-}
660695write :: MonadIO m => Fold m a (MutArray a)
661−write = writeWith arrayChunkSize
696+write = create
662697
663698-- | Create a 'MutArray' from the first @n@ elements of a stream. The
664699-- array is allocated to size @n@, if the stream terminates before @n@
@@ -683,7 +718,7 @@ fromList xs = fromStream $ D.fromList xs
683718{-# INLINABLE fromPureStream #-}
684719fromPureStream :: MonadIO m => Stream Identity a -> m (MutArray a)
685720fromPureStream xs =
686− liftIO $ D.fold write $ D.morphInner (return . runIdentity) xs
721+ D.fold write $ D.morphInner (return . runIdentity) xs
687722
688723-------------------------------------------------------------------------------
689724-- Chunking
@@ -815,7 +850,7 @@ putSliceUnsafe src srcStart dst dstStart len = liftIO $ do
815850
816851{-# INLINE clone #-}
817852clone :: MonadIO m => MutArray a -> m (MutArray a)
818−clone src = liftIO $ do
853+clone src = do
819854 let len = arrLen src
820855 dst <- new len
821856 putSliceUnsafe src 0 dst 0 len
core/src/Streamly/Internal/Data/MutArray/Stream.hs+11−195
@@ -12,6 +12,7 @@
1212-- keep this as a separate module and release it.
1313--
1414module Streamly.Internal.Data.MutArray.Stream
15+{-# DEPRECATED "Please use \"Streamly.Internal.Data.MutArray\" instead." #-}
1516 (
1617 -- * Generation
1718 MArray.chunksOf
@@ -21,7 +22,7 @@ module Streamly.Internal.Data.MutArray.Stream
2122
2223 -- * Compaction
2324 , packArraysChunksOf
24− , SpliceState (..)
25+ , MArray.SpliceState (..)
2526 , lpackArraysChunksOf
2627 , compact -- chunksCompact
2728 , compactLE
@@ -35,19 +36,12 @@ module Streamly.Internal.Data.MutArray.Stream
3536 )
3637where
3738
38−#include "inline.hs"
39−#include "ArrayMacros.h"
40−
4139import Control.Monad.IO.Class (MonadIO(..))
42−import Control.Monad (when)
43−import Data.Bifunctor (first)
44−import Data.Proxy (Proxy(..))
45−import Streamly.Internal.Data.Unbox (Unbox, sizeOf)
40+import Streamly.Internal.Data.Unbox (Unbox)
4641import Streamly.Internal.Data.MutArray.Type (MutArray(..))
4742import Streamly.Internal.Data.Fold.Type (Fold(..))
4843import Streamly.Internal.Data.Parser (ParseError)
4944import Streamly.Internal.Data.Stream.Type (Stream)
50−import Streamly.Internal.Data.Tuple.Strict (Tuple'(..))
5145
5246import qualified Streamly.Internal.Data.MutArray as MArray
5347import qualified Streamly.Internal.Data.Fold.Type as FL
@@ -58,12 +52,6 @@ import qualified Streamly.Internal.Data.Stream as D
5852-- Compact
5953-------------------------------------------------------------------------------
6054
61−data SpliceState s arr
62− = SpliceInitial s
63− | SpliceBuffering s arr
64− | SpliceYielding arr (SpliceState s arr)
65− | SpliceFinish
66−
6755-- XXX This can be removed once compactLEFold/compactLE are implemented.
6856--
6957-- | This mutates the first array (if it has space) to append values from the
@@ -78,117 +66,18 @@ data SpliceState s arr
7866-- actual array size may be less than the specified chunk size.
7967--
8068-- @since 0.7.0
81−{-# INLINE_NORMAL packArraysChunksOf #-}
69+{-# INLINE packArraysChunksOf #-}
8270packArraysChunksOf :: (MonadIO m, Unbox a)
8371 => Int -> D.Stream m (MutArray a) -> D.Stream m (MutArray a)
84−packArraysChunksOf n (D.Stream step state) =
85− D.Stream step' (SpliceInitial state)
86−
87− where
88−
89− {-# INLINE_LATE step' #-}
90− step' gst (SpliceInitial st) = do
91− when (n <= 0) $
92− -- XXX we can pass the module string from the higher level API
93− error $ "Streamly.Internal.Data.MutArray.Type.packArraysChunksOf: the size of "
94− ++ "arrays [" ++ show n ++ "] must be a natural number"
95− r <- step gst st
96− case r of
97− D.Yield arr s -> return $
98− let len = MArray.byteLength arr
99− in if len >= n
100− then D.Skip (SpliceYielding arr (SpliceInitial s))
101− else D.Skip (SpliceBuffering s arr)
102− D.Skip s -> return $ D.Skip (SpliceInitial s)
103− D.Stop -> return D.Stop
104−
105− step' gst (SpliceBuffering st buf) = do
106− r <- step gst st
107− case r of
108− D.Yield arr s -> do
109− let len = MArray.byteLength buf + MArray.byteLength arr
110− if len > n
111− then return $
112− D.Skip (SpliceYielding buf (SpliceBuffering s arr))
113− else do
114− buf' <- if MArray.byteCapacity buf < n
115− then liftIO $ MArray.realloc n buf
116− else return buf
117− buf'' <- MArray.spliceUnsafe buf' arr
118− return $ D.Skip (SpliceBuffering s buf'')
119− D.Skip s -> return $ D.Skip (SpliceBuffering s buf)
120− D.Stop -> return $ D.Skip (SpliceYielding buf SpliceFinish)
121−
122− step' _ SpliceFinish = return D.Stop
123−
124− step' _ (SpliceYielding arr next) = return $ D.Yield arr next
72+packArraysChunksOf = MArray.compactLE
12573
12674-- XXX Remove this once compactLEFold is implemented
12775-- lpackArraysChunksOf = Fold.many compactLEFold
12876--
129−{-# INLINE_NORMAL lpackArraysChunksOf #-}
77+{-# INLINE lpackArraysChunksOf #-}
13078lpackArraysChunksOf :: (MonadIO m, Unbox a)
13179 => Int -> Fold m (MutArray a) () -> Fold m (MutArray a) ()
132−lpackArraysChunksOf n (Fold step1 initial1 _ final1) =
133− Fold step initial extract final
134−
135− where
136−
137− initial = do
138− when (n <= 0) $
139− -- XXX we can pass the module string from the higher level API
140− error $ "Streamly.Internal.Data.MutArray.Type.packArraysChunksOf: the size of "
141− ++ "arrays [" ++ show n ++ "] must be a natural number"
142−
143− r <- initial1
144− return $ first (Tuple' Nothing) r
145−
146− step (Tuple' Nothing r1) arr =
147− let len = MArray.byteLength arr
148− in if len >= n
149− then do
150− r <- step1 r1 arr
151− case r of
152− FL.Done _ -> return $ FL.Done ()
153− FL.Partial s -> do
154− _ <- final1 s
155− res <- initial1
156− return $ first (Tuple' Nothing) res
157− else return $ FL.Partial $ Tuple' (Just arr) r1
158−
159− step (Tuple' (Just buf) r1) arr = do
160− let len = MArray.byteLength buf + MArray.byteLength arr
161− buf' <- if MArray.byteCapacity buf < len
162− then liftIO $ MArray.realloc (max n len) buf
163− else return buf
164− buf'' <- MArray.spliceUnsafe buf' arr
165−
166− -- XXX this is common in both the equations of step
167− if len >= n
168− then do
169− r <- step1 r1 buf''
170− case r of
171− FL.Done _ -> return $ FL.Done ()
172− FL.Partial s -> do
173− _ <- final1 s
174− res <- initial1
175− return $ first (Tuple' Nothing) res
176− else return $ FL.Partial $ Tuple' (Just buf'') r1
177−
178− -- XXX Several folds do extract >=> final, therefore, we need to make final
179− -- return "m b" rather than using extract post it if we want extract to be
180− -- partial.
181− --
182− -- extract forces the pending buffer to be sent to the fold which is not
183− -- what we want.
184− extract _ = error "lpackArraysChunksOf: not designed for scanning"
185−
186− final (Tuple' Nothing r1) = final1 r1
187− final (Tuple' (Just buf) r1) = do
188− r <- step1 r1 buf
189− case r of
190− FL.Partial rr -> final1 rr
191− FL.Done _ -> return ()
80+lpackArraysChunksOf = MArray.lCompactGE
19281
19382-- XXX Same as compactLE, to be removed once that is implemented.
19483--
@@ -208,48 +97,11 @@ compact = packArraysChunksOf
20897-- actual array size may be less than the specified chunk size.
20998--
21099-- /Internal/
211−{-# INLINE_NORMAL compactLEParserD #-}
100+{-# INLINE compactLEParserD #-}
212101compactLEParserD ::
213102 forall m a. (MonadIO m, Unbox a)
214103 => Int -> ParserD.Parser (MutArray a) m (MutArray a)
215−compactLEParserD n = ParserD.Parser step initial extract
216−
217− where
218−
219− nBytes = n * SIZE_OF(a)
220−
221− initial =
222− return
223− $ if n <= 0
224− then error
225− $ functionPath
226− ++ ": the size of arrays ["
227− ++ show n ++ "] must be a natural number"
228− else ParserD.IPartial Nothing
229−
230− step Nothing arr =
231− return
232− $ let len = MArray.byteLength arr
233− in if len >= nBytes
234− then ParserD.Done 0 arr
235− else ParserD.Partial 0 (Just arr)
236− step (Just buf) arr =
237− let len = MArray.byteLength buf + MArray.byteLength arr
238− in if len > nBytes
239− then return $ ParserD.Done 1 buf
240− else do
241− buf1 <-
242− if MArray.byteCapacity buf < nBytes
243− then liftIO $ MArray.realloc nBytes buf
244− else return buf
245− buf2 <- MArray.spliceUnsafe buf1 arr
246− return $ ParserD.Partial 0 (Just buf2)
247−
248− extract Nothing = return $ ParserD.Done 0 MArray.nil
249− extract (Just buf) = return $ ParserD.Done 0 buf
250−
251− functionPath =
252− "Streamly.Internal.Data.Stream.MutChunked.compactLEParserD"
104+compactLEParserD = MArray.pCompactLE
253105
254106-- | Coalesce adjacent arrays in incoming stream to form bigger arrays of a
255107-- minimum specified size. Note that if all the arrays in the stream together
@@ -258,47 +110,11 @@ compactLEParserD n = ParserD.Parser step initial extract
258110-- the specified size we stop coalescing further.
259111--
260112-- /Internal/
261−{-# INLINE_NORMAL compactGEFold #-}
113+{-# INLINE compactGEFold #-}
262114compactGEFold ::
263115 forall m a. (MonadIO m, Unbox a)
264116 => Int -> FL.Fold m (MutArray a) (MutArray a)
265−compactGEFold n = Fold step initial extract extract
266−
267− where
268−
269− nBytes = n * SIZE_OF(a)
270−
271− initial =
272− return
273− $ if n < 0
274− then error
275− $ functionPath
276− ++ ": the size of arrays ["
277− ++ show n ++ "] must be a natural number"
278− else FL.Partial Nothing
279−
280− step Nothing arr =
281− return
282− $ let len = MArray.byteLength arr
283− in if len >= nBytes
284− then FL.Done arr
285− else FL.Partial (Just arr)
286− step (Just buf) arr = do
287− let len = MArray.byteLength buf + MArray.byteLength arr
288− buf1 <-
289− if MArray.byteCapacity buf < len
290− then liftIO $ MArray.realloc (max len nBytes) buf
291− else return buf
292− buf2 <- MArray.spliceUnsafe buf1 arr
293− if len >= n
294− then return $ FL.Done buf2
295− else return $ FL.Partial (Just buf2)
296−
297− extract Nothing = return MArray.nil
298− extract (Just buf) = return buf
299−
300− functionPath =
301− "Streamly.Internal.Data.Stream.MutChunked.compactGEFold"
117+compactGEFold = MArray.fCompactGE
302118
303119-- | Coalesce adjacent arrays in incoming stream to form bigger arrays of a
304120-- maximum specified size in bytes.
core/src/Streamly/Internal/Data/MutArray/Type.hs+987−323
@@ -31,37 +31,57 @@ module Streamly.Internal.Data.MutArray.Type
3131 -- ** Type
3232 -- $arrayNotes
3333 MutArray (..)
34+
35+ -- ** Conversion
36+ -- *** Pinned and Unpinned
3437 , pin
3538 , unpin
3639 , isPinned
3740
38− -- -- * Constructing and Writing
41+ -- ** Casting
42+ , cast
43+ , castUnsafe -- XXX unsafeCast
44+ , asBytes
45+ , unsafePinnedAsPtr
46+ , unsafeAsPtr
47+
3948 -- ** Construction
40− , nil
49+ , empty
50+
51+ -- *** New
52+ -- | New arrays are always empty arrays with some reserve capacity to
53+ -- extend the length without reallocating.
54+ , emptyOf
55+ , newArrayWith -- emptyAlignedWith
56+ , pinnedEmptyOf
57+ , pinnedNewAligned -- XXX not required
58+ -- , new -- uninitialized array of specified length
59+
60+ -- *** Cloning
61+ , clone
62+ , pinnedClone
4163
42− -- *** Uninitialized Arrays
43− , pinnedNew
44− , pinnedNewBytes
45− , pinnedNewAligned
46− , new
47− , newArrayWith
64+ -- *** Slicing
65+ -- | Get a subarray without copying
66+ , getSliceUnsafe -- XXX unsafeGetSlice
67+ , getSlice
68+ , splitAt -- XXX should be able to express using getSlice
69+ , breakOn
4870
49− -- *** From streams
71+ -- *** Stream Folds
5072 , ArrayUnsafe (..)
51− , writeNWithUnsafe
52− , writeNWith
53− , writeNUnsafe
54− , pinnedWriteNUnsafe
55− , writeN
56− , pinnedWriteN
57− , pinnedWriteNAligned
58−
59− , writeWith
60− , write
61− , pinnedWrite
62−
63− , writeRevN
64− -- , writeRev
73+ , unsafeCreateOfWith
74+ , unsafeCreateOf
75+ , unsafePinnedCreateOf
76+ , pinnedCreateOf
77+ , createOfWith
78+ , createOf
79+ , revCreateOf
80+
81+ , pinnedCreate
82+ , createWith
83+ , create
84+ -- , revCreate
6585
6686 -- *** From containers
6787 , fromListN
@@ -77,11 +97,11 @@ module Streamly.Internal.Data.MutArray.Type
7797 , fromByteStr#
7898 , fromPtrN
7999 , fromChunksK
80− , fromChunksRealloced
100+ , fromChunksRealloced -- fromSmallChunks
81101
82102 -- ** Random writes
83103 , putIndex
84− , putIndexUnsafe
104+ , putIndexUnsafe -- XXX unsafePutIndex
85105 , putIndices
86106 -- , putFromThenTo
87107 -- , putFrom -- start writing at the given position
@@ -89,77 +109,70 @@ module Streamly.Internal.Data.MutArray.Type
89109 -- , putFromTo
90110 -- , putFromRev
91111 -- , putUptoRev
92− , modifyIndexUnsafe
112+ , modifyIndexUnsafe -- XXX unsafeModifyIndex
93113 , modifyIndex
94114 , modifyIndices
95115 , modify
96116 , swapIndices
97117 , unsafeSwapIndices
98118
99− -- ** Growing and Shrinking
100− -- | Arrays grow only at the end, though it is possible to grow on both sides
101− -- and therefore have a cons as well as snoc. But that will require two
102− -- bounds in the array representation.
103−
104− -- *** Appending elements
105− , snocWith
106− , snoc
107− , snocLinear
108− , snocMay
109− , snocUnsafe
110−
111− -- *** Appending streams
112− , writeAppendNUnsafe
113− , writeAppendN
114− , writeAppendWith
115− , writeAppend
119+ -- ** Reading
116120
117− -- ** Eliminating and Reading
118−
119− -- *** To streams
120− , reader
121− , readerRevWith
122− , readerRev
121+ -- *** Indexing
122+ , getIndex
123+ , getIndexUnsafe -- XXX unsafeGetIndex
124+ -- , getFromThenTo
125+ , getIndexRev -- getRevIndex?
126+ , indexReader
127+ , indexReaderWith
123128
124− -- *** To containers
125− , toStreamWith
126− , toStreamRevWith
127− , toStreamKWith
128− , toStreamKRevWith
129+ -- *** To Streams
129130 , read
130131 , readRev
132+ , toStreamWith
133+ , toStreamRevWith
131134 , toStreamK
135+ , toStreamKWith
132136 , toStreamKRev
137+ , toStreamKRevWith
138+
139+ -- *** To Containers
133140 , toList
134141
142+ -- *** Unfolds
135143 -- experimental
136144 , producerWith
137145 , producer
138146
139− -- *** Random reads
140− , getIndex
141− , getIndexUnsafe
142− , getIndices
143− , getIndicesWith
144− -- , getFromThenTo
145− , getIndexRev
146−
147− -- ** Memory Management
148− , blockSize
149− , arrayChunkBytes
150− , allocBytesToElemCount
151− , realloc
152− , resize
153− , resizeExp
154− , rightSize
147+ , reader
148+ , readerRevWith
149+ , readerRev
155150
156− -- ** Size
151+ -- ** Size and Capacity
152+ -- *** Size
157153 , length
158154 , byteLength
155+
156+ -- *** Capacity
159157 -- , capacity
160158 , byteCapacity
161159 , bytesFree
162160
161+ -- *** Capacity Management
162+ , blockSize
163+ , arrayChunkBytes
164+ , allocBytesToElemCount
165+ , realloc -- this is "resize"
166+ , grow
167+ , growExp
168+ , rightSize
169+
170+ -- ** Folding
171+ , foldl'
172+ , foldr
173+ , byteCmp
174+ , byteEq
175+
163176 -- ** In-place Mutation Algorithms
164177 , strip
165178 , reverse
@@ -170,59 +183,82 @@ module Streamly.Internal.Data.MutArray.Type
170183 , mergeBy
171184 , bubble
172185
173− -- ** Casting
174− , cast
175− , castUnsafe
176− , asBytes
177− , asPtrUnsafe
178− , asUnpinnedPtrUnsafe
186+ -- ** Growing and Shrinking
187+ -- | Arrays grow only at the end, though it is possible to grow on both
188+ -- sides and therefore have a cons as well as snoc. But that will require
189+ -- both lower and upper bound in the array representation.
179190
180− -- ** Folding
181− , foldl'
182− , foldr
183− , cmp
184− , byteCmp
185− , byteEq
191+ -- *** Appending elements
192+ , snocWith
193+ , snoc
194+ , snocLinear
195+ , snocMay
196+ , snocUnsafe -- XXX unsafeSnoc
197+
198+ -- *** Appending streams
199+ , unsafeAppendN
200+ , appendN
201+ , appendWith
202+ , append
203+
204+ -- *** Appending arrays
205+ , spliceCopy
206+ , spliceWith
207+ , splice
208+ , spliceExp
209+ , spliceUnsafe -- XXX unsafeSplice
210+ -- , putSlice
211+ -- , appendSlice
212+ -- , appendSliceFrom
213+
214+ -- ** Serialization using Unbox
215+ , pokeAppend
216+ , pokeAppendMay
217+ , pokeSkipUnsafe -- unsafePokeSkip
218+
219+ -- ** Deserialization using Unbox
220+ , peekUncons
221+ , peekUnconsUnsafe -- unsafePeekUncons
222+ , peekSkipUnsafe -- unsafePeekSkip
186223
187− -- ** Arrays of arrays
224+ -- Arrays of arrays
188225 -- We can add dimensionality parameter to the array type to get
189226 -- multidimensional arrays. Multidimensional arrays would just be a
190227 -- convenience wrapper on top of single dimensional arrays.
191228
192− -- | Operations dealing with multiple arrays, streams of arrays or
193− -- multidimensional array representations.
194−
195− -- *** Construct from streams
229+ -- ** Streams of Arrays
230+ -- *** Chunk
231+ -- | Group a stream into arrays.
196232 , chunksOf
197233 , pinnedChunksOf
198234 , buildChunks
199235
200− -- *** Eliminate to streams
201− , concatChunks
202− , concatChunksRev
236+ -- *** Split
237+ -- | Split an array into slices.
203238
204− -- *** Construct from arrays
205− -- get chunks without copying
206− , getSliceUnsafe
207− , getSlice
208239 -- , getSlicesFromLenN
209− , splitAt -- XXX should be able to express using getSlice
210240 , splitOn
211− , breakOn
212−
213− -- ** Cloning arrays
214− , clone
215− , pinnedClone
216−
217− -- ** Appending arrays
218− , spliceCopy
219− , spliceWith
220− , splice
221− , spliceExp
222− , spliceUnsafe
223− -- , putSlice
224− -- , appendSlice
225− -- , appendSliceFrom
241+ -- , slicesOf
242+
243+ -- *** Concat
244+ -- | Append the arrays in a stream to form a stream of elements.
245+ , concatWith
246+ , concatRevWith
247+ , concat
248+ , concatRev
249+
250+ -- *** Compact
251+ -- | Append the arrays in a stream to form a stream of larger arrays.
252+ , SpliceState (..)
253+ , pCompactLE
254+ , pPinnedCompactLE
255+ , compactLeAs
256+ , fCompactGE
257+ , fPinnedCompactGE
258+ , lCompactGE
259+ , lPinnedCompactGE
260+ , compactGE
261+ , compactEQ
226262
227263 -- ** Utilities
228264 , roundUpToPower2
@@ -231,12 +267,37 @@ module Streamly.Internal.Data.MutArray.Type
231267 , c_memchr
232268
233269 -- * Deprecated
270+ , asPtrUnsafe
234271 , writeChunks
235272 , flattenArrays
236273 , flattenArraysRev
237274 , fromArrayStreamK
238275 , fromStreamDN
239276 , fromStreamD
277+ , cmp
278+ , getIndices
279+ , getIndicesWith
280+ , resize
281+ , resizeExp
282+ , nil
283+ , new
284+ , pinnedNew
285+ , pinnedNewBytes
286+ , writeAppendNUnsafe
287+ , writeAppendN
288+ , writeAppendWith
289+ , writeAppend
290+ , writeNWithUnsafe
291+ , writeNWith
292+ , writeNUnsafe
293+ , pinnedWriteNUnsafe
294+ , writeN
295+ , pinnedWriteN
296+ , pinnedWriteNAligned -- XXX not required
297+ , writeWith
298+ , write
299+ , pinnedWrite
300+ , writeRevN
240301 )
241302where
242303
@@ -247,6 +308,7 @@ where
247308
248309import Control.Monad (when, void)
249310import Control.Monad.IO.Class (MonadIO(..))
311+import Data.Bifunctor (first)
250312import Data.Bits (shiftR, (.|.), (.&.))
251313import Data.Functor.Identity (Identity(..))
252314import Data.Proxy (Proxy(..))
@@ -273,13 +335,17 @@ import GHC.Ptr (Ptr(..))
273335import Streamly.Internal.Data.Fold.Type (Fold(..))
274336import Streamly.Internal.Data.Producer.Type (Producer (..))
275337import Streamly.Internal.Data.Stream.Type (Stream)
338+import Streamly.Internal.Data.Parser.Type (Parser (..))
276339import Streamly.Internal.Data.StreamK.Type (StreamK)
277340import Streamly.Internal.Data.SVar.Type (adaptState, defState)
341+import Streamly.Internal.Data.Tuple.Strict (Tuple'(..))
278342import Streamly.Internal.Data.Unfold.Type (Unfold(..))
279343import Streamly.Internal.System.IO (arrayPayloadSize, defaultChunkSize)
280344
281345import qualified Streamly.Internal.Data.Fold.Type as FL
282346import qualified Streamly.Internal.Data.MutByteArray.Type as Unboxed
347+import qualified Streamly.Internal.Data.Parser.Type as Parser
348+-- import qualified Streamly.Internal.Data.Fold.Type as Fold
283349import qualified Streamly.Internal.Data.Producer as Producer
284350import qualified Streamly.Internal.Data.Stream.Type as D
285351import qualified Streamly.Internal.Data.Stream.Lift as D
@@ -287,7 +353,7 @@ import qualified Streamly.Internal.Data.StreamK.Type as K
287353import qualified Prelude
288354
289355import Prelude hiding
290− (Foldable(..), read, unlines, splitAt, reverse, truncate)
356+ (Foldable(..), concat, read, unlines, splitAt, reverse, truncate)
291357
292358#include "DocTestDataMutArray.hs"
293359
@@ -413,6 +479,8 @@ isPinned MutArray{..} = Unboxed.isPinned arrContents
413479-- we need more than that. See stg_pinnedNewAlignedByteArrayzh and
414480-- allocatePinned in GHC source.
415481
482+-- XXX Rename to emptyAlignedWith, alignSize should be first arg.
483+
416484-- | @newArrayWith allocator alignment count@ allocates a new array of zero
417485-- length and with a capacity to hold @count@ elements, using @allocator
418486-- size alignment@ as the memory allocator function.
@@ -436,12 +504,25 @@ newArrayWith alloc alignSize count = do
436504 , arrBound = size
437505 }
438506
507+-- For arrays "nil" sounds a bit odd. empty is better. The only problem with
508+-- empty is that it is also used by the Alternative type class. But assuming we
509+-- will mostly import the Array module qualified this should be fine.
510+
511+-- | Create an empty array.
512+empty ::
513+#ifdef DEVBUILD
514+ Unbox a =>
515+#endif
516+ MutArray a
517+empty = MutArray Unboxed.empty 0 0 0
518+
519+{-# DEPRECATED nil "Please use empty instead." #-}
439520nil ::
440521#ifdef DEVBUILD
441522 Unbox a =>
442523#endif
443524 MutArray a
444−nil = MutArray Unboxed.nil 0 0 0
525+nil = empty
445526
446527{-# INLINE newBytesAs #-}
447528newBytesAs :: MonadIO m =>
@@ -458,12 +539,13 @@ newBytesAs ps bytes = do
458539 , arrBound = bytes
459540 }
460541
461−-- | Allocates a pinned empty array that can hold 'count' items. The memory of
462−-- the array is uninitialized and the allocation is aligned as per the
463−-- 'Unboxed' instance of the type.
542+-- | Allocates a pinned empty array that with a reserved capacity of bytes.
543+-- The memory of the array is uninitialized and the allocation is aligned as
544+-- per the 'Unboxed' instance of the type.
464545--
465546-- /Pre-release/
466547{-# INLINE pinnedNewBytes #-}
548+{-# DEPRECATED pinnedNewBytes "Please use pinnedEmptyOf with appropriate calculation" #-}
467549pinnedNewBytes :: MonadIO m =>
468550#ifdef DEVBUILD
469551 Unbox a =>
@@ -488,21 +570,31 @@ newAs ps =
488570 (error "new: alignment is not used in unpinned arrays.")
489571
490572-- XXX can unaligned allocation be more efficient when alignment is not needed?
491−--
492−-- | Allocates an empty pinned array that can hold 'count' items. The memory of
493−-- the array is uninitialized and the allocation is aligned as per the 'Unboxed'
494−-- instance of the type.
495−--
573+
574+-- | Allocates a pinned array of zero length but growable to the specified
575+-- capacity without reallocation.
576+{-# INLINE pinnedEmptyOf #-}
577+pinnedEmptyOf :: forall m a. (MonadIO m, Unbox a) => Int -> m (MutArray a)
578+pinnedEmptyOf = newAs Pinned
579+
580+-- XXX Deprecate in major
581+-- {-# DEPRECATED pinnedNew "Please use pinnedEmptyOf instead." #-}
496582{-# INLINE pinnedNew #-}
497583pinnedNew :: forall m a. (MonadIO m, Unbox a) => Int -> m (MutArray a)
498−pinnedNew = newAs Pinned
584+pinnedNew = pinnedEmptyOf
499585
500−-- | Allocates an empty unpinned array that can hold 'count' items. The memory
501−-- of the array is uninitialized.
586+-- | Allocates an unpinned array of zero length but growable to the specified
587+-- capacity without reallocation.
502588--
589+{-# INLINE emptyOf #-}
590+emptyOf :: (MonadIO m, Unbox a) => Int -> m (MutArray a)
591+emptyOf = newAs Unpinned
592+
593+-- XXX Deprecate in major
594+-- {-# DEPRECATED new "Please use emptyOf instead." #-}
503595{-# INLINE new #-}
504596new :: (MonadIO m, Unbox a) => Int -> m (MutArray a)
505−new = newAs Unpinned
597+new = emptyOf
506598
507599-------------------------------------------------------------------------------
508600-- Random writes
@@ -550,7 +642,7 @@ putIndices arr = FL.foldlM' step (return ())
550642
551643 where
552644
553− step () (i, x) = liftIO (putIndex i arr x)
645+ step () (i, x) = putIndex i arr x
554646
555647-- | Modify a given index of an array using a modifier function.
556648--
@@ -746,22 +838,19 @@ arrayChunkBytes = 1024
746838roundDownTo :: Int -> Int -> Int
747839roundDownTo elemSize size = size - (size `mod` elemSize)
748840
749−-- XXX See if resizing can be implemented by reading the old array as a stream
750−-- and then using writeN to the new array.
751−--
752841-- NOTE: we are passing elemSize explicitly to avoid an Unboxed constraint.
753842-- Since this is not inlined Unboxed consrraint leads to dictionary passing
754843-- which complicates some inspection tests.
755844--
756−{-# NOINLINE reallocExplicit #-}
757−reallocExplicit :: Int -> Int -> MutArray a -> IO (MutArray a)
758−reallocExplicit elemSize newCapacityInBytes MutArray{..} = do
845+{-# NOINLINE reallocExplicitAs #-}
846+reallocExplicitAs :: PinnedState -> Int -> Int -> MutArray a -> IO (MutArray a)
847+reallocExplicitAs ps elemSize newCapacityInBytes MutArray{..} = do
759848 assertM(arrEnd <= arrBound)
760849
761850 -- Allocate new array
762851 let newCapMaxInBytes = roundUpLargeArray newCapacityInBytes
763852 contents <-
764− if Unboxed.isPinned arrContents
853+ if ps == Pinned
765854 then Unboxed.pinnedNew newCapMaxInBytes
766855 else Unboxed.new newCapMaxInBytes
767856 let !(MutByteArray mbarrFrom#) = arrContents
@@ -786,6 +875,10 @@ reallocExplicit elemSize newCapacityInBytes MutArray{..} = do
786875 , arrBound = newCapInBytes
787876 }
788877
878+-- XXX Should these be called reallocBytes etc?
879+-- XXX We may also need reallocAs to allocate as pinned/unpinned explicitly. In
880+-- fact clone/pinnedClone can be implemented using reallocAs.
881+
789882-- | @realloc newCapacity array@ reallocates the array to the specified
790883-- capacity in bytes.
791884--
@@ -797,7 +890,12 @@ reallocExplicit elemSize newCapacityInBytes MutArray{..} = do
797890-- If the original array is pinned, the newly allocated array is also pinned.
798891{-# INLINABLE realloc #-}
799892realloc :: forall m a. (MonadIO m, Unbox a) => Int -> MutArray a -> m (MutArray a)
800−realloc bytes arr = liftIO $ reallocExplicit (SIZE_OF(a)) bytes arr
893+realloc bytes arr =
894+ let ps =
895+ if isPinned arr
896+ then Pinned
897+ else Unpinned
898+ in liftIO $ reallocExplicitAs ps (SIZE_OF(a)) bytes arr
801899
802900-- | @reallocWith label capSizer minIncrBytes array@. The label is used
803901-- in error messages and the capSizer is used to determine the capacity of the
@@ -820,14 +918,14 @@ reallocWith label capSizer minIncrBytes arr = do
820918 where
821919
822920 badSize newSize =
823− concat
921+ Prelude.concat
824922 [ label
825923 , ": new array size (in bytes) is less than required size "
826924 , show newSize
827925 , ". Please check the sizing function passed."
828926 ]
829927
830−-- | @resize newCapacity array@ changes the total capacity of the array so that
928+-- | @grow newCapacity array@ changes the total capacity of the array so that
831929-- it is enough to hold the specified number of elements. Nothing is done if
832930-- the specified capacity is less than the length of the array.
833931--
@@ -835,34 +933,46 @@ reallocWith label capSizer minIncrBytes arr = do
835933-- the block size (4K).
836934--
837935-- /Pre-release/
838−{-# INLINE resize #-}
839−resize :: forall m a. (MonadIO m, Unbox a) =>
936+{-# INLINE grow #-}
937+grow :: forall m a. (MonadIO m, Unbox a) =>
840938 Int -> MutArray a -> m (MutArray a)
841−resize nElems arr@MutArray{..} = do
939+grow nElems arr@MutArray{..} = do
842940 let req = SIZE_OF(a) * nElems
843− len = arrEnd - arrStart
844− if req < len
941+ cap = arrBound - arrStart
942+ if req < cap
845943 then return arr
846944 else realloc req arr
847945
848−-- | Like 'resize' but if the byte capacity is more than 'largeObjectThreshold'
849−-- then it is rounded up to the closest power of 2.
946+{-# DEPRECATED resize "Please use grow instead." #-}
947+{-# INLINE resize #-}
948+resize :: forall m a. (MonadIO m, Unbox a) =>
949+ Int -> MutArray a -> m (MutArray a)
950+resize = grow
951+
952+-- | Like 'grow' but if the requested byte capacity is more than
953+-- 'largeObjectThreshold' then it is rounded up to the closest power of 2.
850954--
851955-- /Pre-release/
852−{-# INLINE resizeExp #-}
853−resizeExp :: forall m a. (MonadIO m, Unbox a) =>
956+{-# INLINE growExp #-}
957+growExp :: forall m a. (MonadIO m, Unbox a) =>
854958 Int -> MutArray a -> m (MutArray a)
855−resizeExp nElems arr@MutArray{..} = do
959+growExp nElems arr@MutArray{..} = do
856960 let req = roundUpLargeArray (SIZE_OF(a) * nElems)
857961 req1 =
858962 if req > largeObjectThreshold
859963 then roundUpToPower2 req
860964 else req
861− len = arrEnd - arrStart
862− if req1 < len
965+ cap = arrBound - arrStart
966+ if req1 < cap
863967 then return arr
864968 else realloc req1 arr
865969
970+{-# DEPRECATED resizeExp "Please use growExp instead." #-}
971+{-# INLINE resizeExp #-}
972+resizeExp :: forall m a. (MonadIO m, Unbox a) =>
973+ Int -> MutArray a -> m (MutArray a)
974+resizeExp = growExp
975+
866976-- | Resize the allocated memory to drop any reserved free space at the end of
867977-- the array and reallocate it to reduce wastage.
868978--
@@ -927,7 +1037,7 @@ snocUnsafe arr@MutArray{..} = snocNewEnd (INDEX_NEXT(arrEnd,a)) arr
9271037{-# INLINE snocMay #-}
9281038snocMay :: forall m a. (MonadIO m, Unbox a) =>
9291039 MutArray a -> a -> m (Maybe (MutArray a))
930−snocMay arr@MutArray{..} x = liftIO $ do
1040+snocMay arr@MutArray{..} x = do
9311041 let newEnd = INDEX_NEXT(arrEnd,a)
9321042 if newEnd <= arrBound
9331043 then Just <$> snocNewEnd newEnd arr x
@@ -941,7 +1051,7 @@ snocWithRealloc :: forall m a. (MonadIO m, Unbox a) =>
9411051 -> a
9421052 -> m (MutArray a)
9431053snocWithRealloc sizer arr x = do
944− arr1 <- liftIO $ reallocWith "snocWith" sizer (SIZE_OF(a)) arr
1054+ arr1 <- reallocWith "snocWith" sizer (SIZE_OF(a)) arr
9451055 snocUnsafe arr1 x
9461056
9471057-- | @snocWith sizer arr elem@ mutates @arr@ to append @elem@. The length of
@@ -963,7 +1073,7 @@ snocWith :: forall m a. (MonadIO m, Unbox a) =>
9631073 -> MutArray a
9641074 -> a
9651075 -> m (MutArray a)
966−snocWith allocSize arr x = liftIO $ do
1076+snocWith allocSize arr x = do
9671077 let newEnd = INDEX_NEXT(arrEnd arr,a)
9681078 if newEnd <= arrBound arr
9691079 then snocNewEnd newEnd arr x
@@ -1007,6 +1117,126 @@ snoc = snocWith f
10071117 then oldSize * 2
10081118 else roundUpToPower2 oldSize * 2
10091119
1120+-------------------------------------------------------------------------------
1121+-- Serialization/Deserialization
1122+-------------------------------------------------------------------------------
1123+
1124+{-# INLINE pokeNewEnd #-}
1125+pokeNewEnd :: (MonadIO m, Unbox a) =>
1126+ Int -> MutArray Word8 -> a -> m (MutArray Word8)
1127+pokeNewEnd newEnd arr@MutArray{..} x = liftIO $ do
1128+ assert (newEnd <= arrBound) (return ())
1129+ liftIO $ pokeAt arrEnd arrContents x
1130+ return $ arr {arrEnd = newEnd}
1131+
1132+-- | Really really unsafe, unboxes a Haskell type and appends the resulting
1133+-- bytes to the byte array, may cause silent data corruption or if you are
1134+-- lucky a segfault if the array does not have enough space to append the
1135+-- element.
1136+--
1137+-- /Internal/
1138+{-# INLINE pokeAppendUnsafe #-}
1139+pokeAppendUnsafe :: forall m a. (MonadIO m, Unbox a) =>
1140+ MutArray Word8 -> a -> m (MutArray Word8)
1141+pokeAppendUnsafe arr@MutArray{..} = pokeNewEnd (arrEnd + SIZE_OF(a)) arr
1142+
1143+-- | Skip the specified number of bytes in the array. The data in the skipped
1144+-- region remains uninitialzed.
1145+{-# INLINE pokeSkipUnsafe #-}
1146+pokeSkipUnsafe :: Int -> MutArray Word8 -> MutArray Word8
1147+pokeSkipUnsafe n arr@MutArray{..} = do
1148+ let newEnd = arrEnd + n
1149+ in assert (newEnd <= arrBound) (arr {arrEnd = newEnd})
1150+
1151+-- | Like 'pokeAppend' but does not grow the array when pre-allocated array
1152+-- capacity becomes full.
1153+--
1154+-- /Internal/
1155+{-# INLINE pokeAppendMay #-}
1156+pokeAppendMay :: forall m a. (MonadIO m, Unbox a) =>
1157+ MutArray Word8 -> a -> m (Maybe (MutArray Word8))
1158+pokeAppendMay arr@MutArray{..} x = liftIO $ do
1159+ let newEnd = arrEnd + SIZE_OF(a)
1160+ if newEnd <= arrBound
1161+ then Just <$> pokeNewEnd newEnd arr x
1162+ else return Nothing
1163+
1164+{-# NOINLINE pokeWithRealloc #-}
1165+pokeWithRealloc :: forall m a. (MonadIO m, Unbox a) =>
1166+ (Int -> Int)
1167+ -> MutArray Word8
1168+ -> a
1169+ -> m (MutArray Word8)
1170+pokeWithRealloc sizer arr x = do
1171+ arr1 <- liftIO $ reallocWith "pokeWithRealloc" sizer (SIZE_OF(a)) arr
1172+ pokeAppendUnsafe arr1 x
1173+
1174+{-# INLINE pokeAppendWith #-}
1175+pokeAppendWith :: forall m a. (MonadIO m, Unbox a) =>
1176+ (Int -> Int)
1177+ -> MutArray Word8
1178+ -> a
1179+ -> m (MutArray Word8)
1180+pokeAppendWith allocSize arr x = liftIO $ do
1181+ let newEnd = arrEnd arr + SIZE_OF(a)
1182+ if newEnd <= arrBound arr
1183+ then pokeNewEnd newEnd arr x
1184+ else pokeWithRealloc allocSize arr x
1185+
1186+-- | Unbox a Haskell type and append the resulting bytes to a mutable byte
1187+-- array. The array is grown exponentially when more space is needed.
1188+--
1189+-- Definition:
1190+--
1191+-- >>> pokeAppend arr x = MutArray.castUnsafe <$> MutArray.snoc (MutArray.castUnsafe arr) x
1192+--
1193+{-# INLINE pokeAppend #-}
1194+pokeAppend :: forall m a. (MonadIO m, Unbox a) =>
1195+ MutArray Word8 -> a -> m (MutArray Word8)
1196+pokeAppend = pokeAppendWith f
1197+
1198+ where
1199+
1200+ f oldSize =
1201+ if isPower2 oldSize
1202+ then oldSize * 2
1203+ else roundUpToPower2 oldSize * 2
1204+
1205+-- | Really really unsafe, create a Haskell value from an unboxed byte array,
1206+-- does not check if the array is big enough, may return garbage or if you are
1207+-- lucky may cause a segfault.
1208+--
1209+-- /Internal/
1210+{-# INLINE peekUnconsUnsafe #-}
1211+peekUnconsUnsafe :: forall m a. (MonadIO m, Unbox a) =>
1212+ MutArray Word8 -> m (a, MutArray Word8)
1213+peekUnconsUnsafe MutArray{..} = do
1214+ let start1 = arrStart + SIZE_OF(a)
1215+ assert (start1 <= arrEnd) (return ())
1216+ liftIO $ do
1217+ r <- peekAt arrStart arrContents
1218+ return (r, MutArray arrContents start1 arrEnd arrBound)
1219+
1220+-- | Discard the specified number of bytes in the array.
1221+{-# INLINE peekSkipUnsafe #-}
1222+peekSkipUnsafe :: Int -> MutArray Word8 -> MutArray Word8
1223+peekSkipUnsafe n MutArray{..} =
1224+ let start1 = arrStart + n
1225+ in assert (start1 <= arrEnd) (MutArray arrContents start1 arrEnd arrBound)
1226+
1227+-- | Create a Haskell value from its unboxed representation from the head of a
1228+-- byte array, return the value and the remaining array.
1229+{-# INLINE peekUncons #-}
1230+peekUncons :: forall m a. (MonadIO m, Unbox a) =>
1231+ MutArray Word8 -> m (Maybe a, MutArray Word8)
1232+peekUncons arr@MutArray{..} = do
1233+ let start1 = arrStart + SIZE_OF(a)
1234+ if start1 > arrEnd
1235+ then return (Nothing, arr)
1236+ else liftIO $ do
1237+ r <- peekAt arrStart arrContents
1238+ return (Just r, MutArray arrContents start1 arrEnd arrBound)
1239+
10101240-------------------------------------------------------------------------------
10111241-- Random reads
10121242-------------------------------------------------------------------------------
@@ -1049,10 +1279,10 @@ getIndexRev i MutArray{..} = do
10491279data GetIndicesState contents start end st =
10501280 GetIndicesState contents start end st
10511281
1052−{-# INLINE getIndicesWith #-}
1053−getIndicesWith :: (Monad m, Unbox a) =>
1282+{-# INLINE indexReaderWith #-}
1283+indexReaderWith :: (Monad m, Unbox a) =>
10541284 (forall b. IO b -> m b) -> D.Stream m Int -> Unfold m (MutArray a) a
1055−getIndicesWith liftio (D.Stream stepi sti) = Unfold step inject
1285+indexReaderWith liftio (D.Stream stepi sti) = Unfold step inject
10561286
10571287 where
10581288
@@ -1071,14 +1301,26 @@ getIndicesWith liftio (D.Stream stepi sti) = Unfold step inject
10711301 D.Skip s -> return $ D.Skip (GetIndicesState contents start end s)
10721302 D.Stop -> return D.Stop
10731303
1304+{-# DEPRECATED getIndicesWith "Please use indexReaderWith instead." #-}
1305+{-# INLINE getIndicesWith #-}
1306+getIndicesWith :: (Monad m, Unbox a) =>
1307+ (forall b. IO b -> m b) -> D.Stream m Int -> Unfold m (MutArray a) a
1308+getIndicesWith = indexReaderWith
1309+
10741310-- | Given an unfold that generates array indices, read the elements on those
10751311-- indices from the supplied MutArray. An error is thrown if an index is out of
10761312-- bounds.
10771313--
10781314-- /Pre-release/
1315+{-# INLINE indexReader #-}
1316+indexReader :: (MonadIO m, Unbox a) => Stream m Int -> Unfold m (MutArray a) a
1317+indexReader = indexReaderWith liftIO
1318+
1319+-- XXX DO NOT REMOVE, change the signature to use Stream instead of unfold
1320+{-# DEPRECATED getIndices "Please use indexReader instead." #-}
10791321{-# INLINE getIndices #-}
10801322getIndices :: (MonadIO m, Unbox a) => Stream m Int -> Unfold m (MutArray a) a
1081−getIndices = getIndicesWith liftIO
1323+getIndices = indexReader
10821324
10831325-------------------------------------------------------------------------------
10841326-- Subarrays
@@ -1373,7 +1615,7 @@ chunksOfAs ps n (D.Stream step state) =
13731615--
13741616-- Same as the following but may be more efficient:
13751617--
1376−-- >>> chunksOf n = Stream.foldMany (MutArray.writeN n)
1618+-- >>> chunksOf n = Stream.foldMany (MutArray.createOf n)
13771619--
13781620-- /Pre-release/
13791621{-# INLINE_NORMAL chunksOf #-}
@@ -1382,14 +1624,14 @@ chunksOf :: forall m a. (MonadIO m, Unbox a)
13821624-- XXX the idiomatic implementation leads to large regression in the D.reverse'
13831625-- benchmark. It seems it has difficulty producing optimized code when
13841626-- converting to StreamK. Investigate GHC optimizations.
1385−-- chunksOf n = D.foldMany (writeN n)
1627+-- chunksOf n = D.foldMany (createOf n)
13861628chunksOf = chunksOfAs Unpinned
13871629
13881630-- | Like 'chunksOf' but creates pinned arrays.
13891631{-# INLINE_NORMAL pinnedChunksOf #-}
13901632pinnedChunksOf :: forall m a. (MonadIO m, Unbox a)
13911633 => Int -> D.Stream m a -> D.Stream m (MutArray a)
1392−-- pinnedChunksOf n = D.foldMany (pinnedWriteN n)
1634+-- pinnedChunksOf n = D.foldMany (pinnedCreateOf n)
13931635pinnedChunksOf = chunksOfAs Pinned
13941636
13951637-- | When we are buffering a stream of unknown size into an array we do not
@@ -1422,16 +1664,10 @@ data FlattenState s contents a =
14221664 OuterLoop s
14231665 | InnerLoop s contents !Int !Int
14241666
1425−-- | Use the "reader" unfold instead.
1426−--
1427−-- @concatChunks = unfoldMany reader@
1428−--
1429−-- We can try this if there are any fusion issues in the unfold.
1430−--
1431−{-# INLINE_NORMAL concatChunks #-}
1432−concatChunks :: forall m a. (MonadIO m, Unbox a)
1433− => D.Stream m (MutArray a) -> D.Stream m a
1434−concatChunks (D.Stream step state) = D.Stream step' (OuterLoop state)
1667+{-# INLINE_NORMAL concatWith #-}
1668+concatWith :: forall m a. (Monad m, Unbox a)
1669+ => (forall b. IO b -> m b) -> D.Stream m (MutArray a) -> D.Stream m a
1670+concatWith liftio (D.Stream step state) = D.Stream step' (OuterLoop state)
14351671
14361672 where
14371673
@@ -1448,24 +1684,30 @@ concatChunks (D.Stream step state) = D.Stream step' (OuterLoop state)
14481684 return $ D.Skip $ OuterLoop st
14491685
14501686 step' _ (InnerLoop st contents p end) = do
1451− x <- liftIO $ peekAt p contents
1687+ x <- liftio $ peekAt p contents
14521688 return $ D.Yield x (InnerLoop st contents (INDEX_NEXT(p,a)) end)
14531689
1454−{-# DEPRECATED flattenArrays "Please use \"unfoldMany reader\" instead." #-}
1455−flattenArrays :: forall m a. (MonadIO m, Unbox a)
1456− => D.Stream m (MutArray a) -> D.Stream m a
1457−flattenArrays = concatChunks
1458−
1459−-- | Use the "readerRev" unfold instead.
1690+-- | Use the "reader" unfold instead.
14601691--
1461−-- @concatChunks = unfoldMany readerRev@
1692+-- @concat = unfoldMany reader@
14621693--
14631694-- We can try this if there are any fusion issues in the unfold.
14641695--
1465−{-# INLINE_NORMAL concatChunksRev #-}
1466−concatChunksRev :: forall m a. (MonadIO m, Unbox a)
1696+{-# INLINE_NORMAL concat #-}
1697+concat :: forall m a. (MonadIO m, Unbox a)
1698+ => D.Stream m (MutArray a) -> D.Stream m a
1699+concat = concatWith liftIO
1700+
1701+{-# DEPRECATED flattenArrays "Please use \"unfoldMany reader\" instead." #-}
1702+{-# INLINE flattenArrays #-}
1703+flattenArrays :: forall m a. (MonadIO m, Unbox a)
14671704 => D.Stream m (MutArray a) -> D.Stream m a
1468−concatChunksRev (D.Stream step state) = D.Stream step' (OuterLoop state)
1705+flattenArrays = concat
1706+
1707+{-# INLINE_NORMAL concatRevWith #-}
1708+concatRevWith :: forall m a. (Monad m, Unbox a)
1709+ => (forall b. IO b -> m b) -> D.Stream m (MutArray a) -> D.Stream m a
1710+concatRevWith liftio (D.Stream step state) = D.Stream step' (OuterLoop state)
14691711
14701712 where
14711713
@@ -1483,14 +1725,26 @@ concatChunksRev (D.Stream step state) = D.Stream step' (OuterLoop state)
14831725 return $ D.Skip $ OuterLoop st
14841726
14851727 step' _ (InnerLoop st contents p start) = do
1486− x <- liftIO $ peekAt p contents
1728+ x <- liftio $ peekAt p contents
14871729 let cur = INDEX_PREV(p,a)
14881730 return $ D.Yield x (InnerLoop st contents cur start)
14891731
1732+-- | Use the "readerRev" unfold instead.
1733+--
1734+-- @concat = unfoldMany readerRev@
1735+--
1736+-- We can try this if there are any fusion issues in the unfold.
1737+--
1738+{-# INLINE_NORMAL concatRev #-}
1739+concatRev :: forall m a. (MonadIO m, Unbox a)
1740+ => D.Stream m (MutArray a) -> D.Stream m a
1741+concatRev = concatRevWith liftIO
1742+
14901743{-# DEPRECATED flattenArraysRev "Please use \"unfoldMany readerRev\" instead." #-}
1744+{-# INLINE flattenArraysRev #-}
14911745flattenArraysRev :: forall m a. (MonadIO m, Unbox a)
14921746 => D.Stream m (MutArray a) -> D.Stream m a
1493−flattenArraysRev = concatChunksRev
1747+flattenArraysRev = concatRev
14941748
14951749-------------------------------------------------------------------------------
14961750-- Unfolds
@@ -1719,7 +1973,7 @@ foldr f z arr = D.foldr f z $ read arr
17191973
17201974-- XXX Keep the bound intact to not lose any free space? Perf impact?
17211975
1722−-- | @writeAppendNUnsafe n arr@ appends up to @n@ input items to the supplied
1976+-- | @unsafeAppendN n arr@ appends up to @n@ input items to the supplied
17231977-- array.
17241978--
17251979-- Unsafe: Do not drive the fold beyond @n@ elements, it will lead to memory
@@ -1728,13 +1982,12 @@ foldr f z arr = D.foldr f z $ read arr
17281982-- Any free space left in the array after appending @n@ elements is lost.
17291983--
17301984-- /Internal/
1731−{-# INLINE_NORMAL writeAppendNUnsafe #-}
1732−writeAppendNUnsafe :: forall m a. (MonadIO m, Unbox a) =>
1985+{-# INLINE_NORMAL unsafeAppendN #-}
1986+unsafeAppendN :: forall m a. (MonadIO m, Unbox a) =>
17331987 Int
17341988 -> m (MutArray a)
17351989 -> Fold m a (MutArray a)
1736−writeAppendNUnsafe n action =
1737− fmap fromArrayUnsafe $ FL.foldlM' step initial
1990+unsafeAppendN n action = fmap fromArrayUnsafe $ FL.foldlM' step initial
17381991
17391992 where
17401993
@@ -1747,7 +2000,7 @@ writeAppendNUnsafe n action =
17472000 -- otherwise we lose that space.
17482001 arr1 <-
17492002 if free < needed
1750− then noinline reallocWith "writeAppendNUnsafeWith" (+ needed) needed arr
2003+ then noinline reallocWith "unsafeAppendN" (+ needed) needed arr
17512004 else return arr
17522005 return $ toArrayUnsafe arr1
17532006
@@ -1755,30 +2008,51 @@ writeAppendNUnsafe n action =
17552008 liftIO $ pokeAt end contents x
17562009 return $ ArrayUnsafe contents start (INDEX_NEXT(end,a))
17572010
2011+{-# DEPRECATED writeAppendNUnsafe "Please use unsafeAppendN instead." #-}
2012+{-# INLINE writeAppendNUnsafe #-}
2013+writeAppendNUnsafe :: forall m a. (MonadIO m, Unbox a) =>
2014+ Int
2015+ -> m (MutArray a)
2016+ -> Fold m a (MutArray a)
2017+writeAppendNUnsafe = unsafeAppendN
2018+
17582019-- | Append @n@ elements to an existing array. Any free space left in the array
17592020-- after appending @n@ elements is lost.
17602021--
1761−-- >>> writeAppendN n initial = Fold.take n (MutArray.writeAppendNUnsafe n initial)
2022+-- >>> appendN n initial = Fold.take n (MutArray.unsafeAppendN n initial)
17622023--
1763−{-# INLINE_NORMAL writeAppendN #-}
2024+{-# INLINE_NORMAL appendN #-}
2025+appendN :: forall m a. (MonadIO m, Unbox a) =>
2026+ Int -> m (MutArray a) -> Fold m a (MutArray a)
2027+appendN n initial = FL.take n (unsafeAppendN n initial)
2028+
2029+-- XXX Deprecate in major
2030+-- {-# DEPRECATED writeAppendN "Please use appendN instead." #-}
2031+{-# INLINE writeAppendN #-}
17642032writeAppendN :: forall m a. (MonadIO m, Unbox a) =>
17652033 Int -> m (MutArray a) -> Fold m a (MutArray a)
1766−writeAppendN n initial = FL.take n (writeAppendNUnsafe n initial)
2034+writeAppendN = appendN
17672035
1768−-- | @writeAppendWith realloc action@ mutates the array generated by @action@ to
2036+-- | @appendWith realloc action@ mutates the array generated by @action@ to
17692037-- append the input stream. If there is no reserved space available in the
17702038-- array it is reallocated to a size in bytes determined by @realloc oldSize@,
17712039-- where @oldSize@ is the current size of the array in bytes.
17722040--
17732041-- Note that the returned array may be a mutated version of original array.
17742042--
1775−-- >>> writeAppendWith sizer = Fold.foldlM' (MutArray.snocWith sizer)
2043+-- >>> appendWith sizer = Fold.foldlM' (MutArray.snocWith sizer)
17762044--
17772045-- /Pre-release/
2046+{-# INLINE appendWith #-}
2047+appendWith :: forall m a. (MonadIO m, Unbox a) =>
2048+ (Int -> Int) -> m (MutArray a) -> Fold m a (MutArray a)
2049+appendWith sizer = FL.foldlM' (snocWith sizer)
2050+
2051+{-# DEPRECATED writeAppendWith "Please use appendWith instead." #-}
17782052{-# INLINE writeAppendWith #-}
17792053writeAppendWith :: forall m a. (MonadIO m, Unbox a) =>
17802054 (Int -> Int) -> m (MutArray a) -> Fold m a (MutArray a)
1781−writeAppendWith sizer = FL.foldlM' (snocWith sizer)
2055+writeAppendWith = appendWith
17822056
17832057-- | @append action@ mutates the array generated by @action@ to append the
17842058-- input stream. If there is no reserved space available in the array it is
@@ -1786,26 +2060,33 @@ writeAppendWith sizer = FL.foldlM' (snocWith sizer)
17862060--
17872061-- Note that the returned array may be a mutated version of original array.
17882062--
1789−-- >>> writeAppend = MutArray.writeAppendWith (* 2)
2063+-- >>> append = MutArray.appendWith (* 2)
17902064--
2065+{-# INLINE append #-}
2066+append :: forall m a. (MonadIO m, Unbox a) =>
2067+ m (MutArray a) -> Fold m a (MutArray a)
2068+append = appendWith (* 2)
2069+
2070+-- XXX Deprecate in major
2071+-- {-# DEPRECATED writeAppend "Please use append instead." #-}
17912072{-# INLINE writeAppend #-}
17922073writeAppend :: forall m a. (MonadIO m, Unbox a) =>
17932074 m (MutArray a) -> Fold m a (MutArray a)
1794−writeAppend = writeAppendWith (* 2)
2075+writeAppend = append
17952076
17962077-- XXX We can carry bound as well in the state to make sure we do not lose the
17972078-- remaining capacity. Need to check perf impact.
17982079--
1799−-- | Like 'writeNUnsafe' but takes a new array allocator @alloc size@ function
1800−-- as argument.
2080+-- | Like 'unsafeCreateOf' but takes a new array allocator @alloc size@
2081+-- function as argument.
18012082--
1802−-- >>> writeNWithUnsafe alloc n = MutArray.writeAppendNUnsafe (alloc n) n
2083+-- >>> unsafeCreateOfWith alloc n = MutArray.unsafeAppendN (alloc n) n
18032084--
18042085-- /Pre-release/
1805−{-# INLINE_NORMAL writeNWithUnsafe #-}
1806−writeNWithUnsafe :: forall m a. (MonadIO m, Unbox a)
2086+{-# INLINE_NORMAL unsafeCreateOfWith #-}
2087+unsafeCreateOfWith :: forall m a. (MonadIO m, Unbox a)
18072088 => (Int -> m (MutArray a)) -> Int -> Fold m a (MutArray a)
1808−writeNWithUnsafe alloc n = fromArrayUnsafe <$> FL.foldlM' step initial
2089+unsafeCreateOfWith alloc n = fromArrayUnsafe <$> FL.foldlM' step initial
18092090
18102091 where
18112092
@@ -1816,40 +2097,64 @@ writeNWithUnsafe alloc n = fromArrayUnsafe <$> FL.foldlM' step initial
18162097 return
18172098 $ ArrayUnsafe contents start (INDEX_NEXT(end,a))
18182099
2100+{-# DEPRECATED writeNWithUnsafe "Please use unsafeCreateOfWith instead." #-}
2101+{-# INLINE writeNWithUnsafe #-}
2102+writeNWithUnsafe :: forall m a. (MonadIO m, Unbox a)
2103+ => (Int -> m (MutArray a)) -> Int -> Fold m a (MutArray a)
2104+writeNWithUnsafe = unsafeCreateOfWith
2105+
18192106{-# INLINE_NORMAL writeNUnsafeAs #-}
18202107writeNUnsafeAs :: forall m a. (MonadIO m, Unbox a)
18212108 => PinnedState -> Int -> Fold m a (MutArray a)
1822−writeNUnsafeAs ps = writeNWithUnsafe (newAs ps)
2109+writeNUnsafeAs ps = unsafeCreateOfWith (newAs ps)
18232110
1824−-- | Like 'writeN' but does not check the array bounds when writing. The fold
2111+-- | Like 'createOf' but does not check the array bounds when writing. The fold
18252112-- driver must not call the step function more than 'n' times otherwise it will
18262113-- corrupt the memory and crash. This function exists mainly because any
18272114-- conditional in the step function blocks fusion causing 10x performance
18282115-- slowdown.
18292116--
1830−-- >>> writeNUnsafe = MutArray.writeNWithUnsafe MutArray.new
2117+-- >>> unsafeCreateOf = MutArray.unsafeCreateOfWith MutArray.emptyOf
18312118--
1832−{-# INLINE_NORMAL writeNUnsafe #-}
2119+{-# INLINE_NORMAL unsafeCreateOf #-}
2120+unsafeCreateOf :: forall m a. (MonadIO m, Unbox a)
2121+ => Int -> Fold m a (MutArray a)
2122+unsafeCreateOf = writeNUnsafeAs Unpinned
2123+
2124+{-# DEPRECATED writeNUnsafe "Please use unsafeCreateOf instead." #-}
2125+{-# INLINE writeNUnsafe #-}
18332126writeNUnsafe :: forall m a. (MonadIO m, Unbox a)
18342127 => Int -> Fold m a (MutArray a)
1835−writeNUnsafe = writeNUnsafeAs Unpinned
2128+writeNUnsafe = unsafeCreateOf
2129+
2130+-- | Like 'unsafeCreateOf' but creates a pinned array.
2131+{-# INLINE_NORMAL unsafePinnedCreateOf #-}
2132+unsafePinnedCreateOf :: forall m a. (MonadIO m, Unbox a)
2133+ => Int -> Fold m a (MutArray a)
2134+unsafePinnedCreateOf = writeNUnsafeAs Pinned
18362135
1837−-- | Like 'writeNUnsafe' but creates a pinned array.
1838−{-# INLINE_NORMAL pinnedWriteNUnsafe #-}
2136+{-# DEPRECATED pinnedWriteNUnsafe "Please use unsafePinnedCreateOf instead." #-}
2137+{-# INLINE pinnedWriteNUnsafe #-}
18392138pinnedWriteNUnsafe :: forall m a. (MonadIO m, Unbox a)
18402139 => Int -> Fold m a (MutArray a)
1841−pinnedWriteNUnsafe = writeNUnsafeAs Pinned
2140+pinnedWriteNUnsafe = unsafePinnedCreateOf
18422141
1843−-- | @writeNWith alloc n@ folds a maximum of @n@ elements into an array
2142+-- | @createOfWith alloc n@ folds a maximum of @n@ elements into an array
18442143-- allocated using the @alloc@ function.
18452144--
1846−-- >>> writeNWith alloc n = Fold.take n (MutArray.writeNWithUnsafe alloc n)
1847−-- >>> writeNWith alloc n = MutArray.writeAppendN (alloc n) n
2145+-- >>> createOfWith alloc n = Fold.take n (MutArray.unsafeCreateOfWith alloc n)
2146+-- >>> createOfWith alloc n = MutArray.appendN (alloc n) n
18482147--
1849−{-# INLINE_NORMAL writeNWith #-}
2148+{-# INLINE_NORMAL createOfWith #-}
2149+createOfWith :: forall m a. (MonadIO m, Unbox a)
2150+ => (Int -> m (MutArray a)) -> Int -> Fold m a (MutArray a)
2151+createOfWith alloc n = FL.take n (unsafeCreateOfWith alloc n)
2152+
2153+-- {-# DEPRECATED writeNWith "Please use createOfWith instead." #-}
2154+{-# INLINE writeNWith #-}
18502155writeNWith :: forall m a. (MonadIO m, Unbox a)
18512156 => (Int -> m (MutArray a)) -> Int -> Fold m a (MutArray a)
1852−writeNWith alloc n = FL.take n (writeNWithUnsafe alloc n)
2157+writeNWith = createOfWith
18532158
18542159{-# INLINE_NORMAL writeNAs #-}
18552160writeNAs ::
@@ -1857,28 +2162,42 @@ writeNAs ::
18572162 => PinnedState
18582163 -> Int
18592164 -> Fold m a (MutArray a)
1860−writeNAs ps = writeNWith (newAs ps)
2165+writeNAs ps = createOfWith (newAs ps)
18612166
1862−-- | @writeN n@ folds a maximum of @n@ elements from the input stream to an
2167+-- | @createOf n@ folds a maximum of @n@ elements from the input stream to an
18632168-- 'MutArray'.
18642169--
1865−-- >>> writeN = MutArray.writeNWith MutArray.new
1866−-- >>> writeN n = Fold.take n (MutArray.writeNUnsafe n)
1867−-- >>> writeN n = MutArray.writeAppendN n (MutArray.new n)
2170+-- >>> createOf = MutArray.createOfWith MutArray.new
2171+-- >>> createOf n = Fold.take n (MutArray.unsafeCreateOf n)
2172+-- >>> createOf n = MutArray.appendN n (MutArray.emptyOf n)
18682173--
1869−{-# INLINE_NORMAL writeN #-}
2174+{-# INLINE_NORMAL createOf #-}
2175+createOf :: forall m a. (MonadIO m, Unbox a) => Int -> Fold m a (MutArray a)
2176+createOf = writeNAs Unpinned
2177+
2178+-- XXX Deprecate in major
2179+-- {-# DEPRECATED writeN "Please use createOf instead." #-}
2180+{-# INLINE writeN #-}
18702181writeN :: forall m a. (MonadIO m, Unbox a) => Int -> Fold m a (MutArray a)
1871−writeN = writeNAs Unpinned
2182+writeN = createOf
2183+
2184+-- | Like 'createOf' but creates a pinned array.
2185+{-# INLINE_NORMAL pinnedCreateOf #-}
2186+pinnedCreateOf ::
2187+ forall m a. (MonadIO m, Unbox a)
2188+ => Int
2189+ -> Fold m a (MutArray a)
2190+pinnedCreateOf = writeNAs Pinned
18722191
1873−-- | Like 'writeN' but creates a pinned array.
1874−{-# INLINE_NORMAL pinnedWriteN #-}
2192+{-# DEPRECATED pinnedWriteN "Please use pinnedCreateOf instead." #-}
2193+{-# INLINE pinnedWriteN #-}
18752194pinnedWriteN ::
18762195 forall m a. (MonadIO m, Unbox a)
18772196 => Int
18782197 -> Fold m a (MutArray a)
1879−pinnedWriteN = writeNAs Pinned
2198+pinnedWriteN = pinnedCreateOf
18802199
1881−-- | Like writeNWithUnsafe but writes the array in reverse order.
2200+-- | Like unsafeCreateOfWith but writes the array in reverse order.
18822201--
18832202-- /Internal/
18842203{-# INLINE_NORMAL writeRevNWithUnsafe #-}
@@ -1899,7 +2218,7 @@ writeRevNWithUnsafe alloc n = fromArrayUnsafe <$> FL.foldlM' step initial
18992218 return
19002219 $ ArrayUnsafe contents ptr end
19012220
1902−-- | Like writeNWith but writes the array in reverse order.
2221+-- | Like createOfWith but writes the array in reverse order.
19032222--
19042223-- /Internal/
19052224{-# INLINE_NORMAL writeRevNWith #-}
@@ -1907,31 +2226,36 @@ writeRevNWith :: forall m a. (MonadIO m, Unbox a)
19072226 => (Int -> m (MutArray a)) -> Int -> Fold m a (MutArray a)
19082227writeRevNWith alloc n = FL.take n (writeRevNWithUnsafe alloc n)
19092228
1910−-- | Like writeN but writes the array in reverse order.
2229+-- | Like 'createOf' but writes the array in reverse order.
19112230--
19122231-- /Pre-release/
1913−{-# INLINE_NORMAL writeRevN #-}
2232+{-# INLINE_NORMAL revCreateOf #-}
2233+revCreateOf :: forall m a. (MonadIO m, Unbox a) => Int -> Fold m a (MutArray a)
2234+revCreateOf = writeRevNWith new
2235+
2236+{-# DEPRECATED writeRevN "Please use revCreateOf instead." #-}
2237+{-# INLINE writeRevN #-}
19142238writeRevN :: forall m a. (MonadIO m, Unbox a) => Int -> Fold m a (MutArray a)
1915−writeRevN = writeRevNWith new
2239+writeRevN = revCreateOf
19162240
19172241-- | @pinnedWriteNAligned align n@ folds a maximum of @n@ elements from the
19182242-- input stream to a 'MutArray' aligned to the given size.
19192243--
1920−-- >>> pinnedWriteNAligned align = MutArray.writeNWith (MutArray.pinnedNewAligned align)
1921−-- >>> pinnedWriteNAligned align n = MutArray.writeAppendN n (MutArray.pinnedNewAligned align n)
2244+-- >>> pinnedWriteNAligned align = MutArray.createOfWith (MutArray.pinnedNewAligned align)
2245+-- >>> pinnedWriteNAligned align n = MutArray.appendN n (MutArray.pinnedNewAligned align n)
19222246--
19232247-- /Pre-release/
19242248--
19252249{-# INLINE_NORMAL pinnedWriteNAligned #-}
19262250pinnedWriteNAligned :: forall m a. (MonadIO m, Unbox a)
19272251 => Int -> Int -> Fold m a (MutArray a)
1928−pinnedWriteNAligned align = writeNWith (pinnedNewAligned align)
2252+pinnedWriteNAligned align = createOfWith (pinnedNewAligned align)
19292253
19302254-- XXX Buffer to a list instead?
19312255
19322256-- | Buffer a stream into a stream of arrays.
19332257--
1934−-- >>> buildChunks n = Fold.many (MutArray.writeN n) Fold.toStreamK
2258+-- >>> buildChunks n = Fold.many (MutArray.createOf n) Fold.toStreamK
19352259--
19362260-- Breaking an array into an array stream can be useful to consume a large
19372261-- array sequentially such that memory of the array is released incrementatlly.
@@ -1943,9 +2267,10 @@ pinnedWriteNAligned align = writeNWith (pinnedNewAligned align)
19432267{-# INLINE_NORMAL buildChunks #-}
19442268buildChunks :: (MonadIO m, Unbox a) =>
19452269 Int -> Fold m a (StreamK n (MutArray a))
1946−buildChunks n = FL.many (writeN n) FL.toStreamK
2270+buildChunks n = FL.many (createOf n) FL.toStreamK
19472271
19482272{-# DEPRECATED writeChunks "Please use buildChunks instead." #-}
2273+{-# INLINE writeChunks #-}
19492274writeChunks :: (MonadIO m, Unbox a) =>
19502275 Int -> Fold m a (StreamK n (MutArray a))
19512276writeChunks = buildChunks
@@ -1953,29 +2278,29 @@ writeChunks = buildChunks
19532278{-# INLINE_NORMAL writeWithAs #-}
19542279writeWithAs :: forall m a. (MonadIO m, Unbox a)
19552280 => PinnedState -> Int -> Fold m a (MutArray a)
1956−-- writeWithAs ps n = FL.rmapM rightSize $ writeAppendWith (* 2) (newAs ps n)
2281+-- writeWithAs ps n = FL.rmapM rightSize $ appendWith (* 2) (newAs ps n)
19572282writeWithAs ps elemCount =
19582283 FL.rmapM extract $ FL.foldlM' step initial
19592284
19602285 where
19612286
19622287 initial = do
1963− when (elemCount < 0) $ error "writeWith: elemCount is negative"
1964− liftIO $ newAs ps elemCount
2288+ when (elemCount < 0) $ error "createWith: elemCount is negative"
2289+ newAs ps elemCount
19652290
19662291 step arr@(MutArray _ start end bound) x
19672292 | INDEX_NEXT(end,a) > bound = do
19682293 let oldSize = end - start
19692294 newSize = max (oldSize * 2) 1
1970− arr1 <- liftIO $ reallocExplicit (SIZE_OF(a)) newSize arr
2295+ arr1 <- liftIO $ reallocExplicitAs ps (SIZE_OF(a)) newSize arr
19712296 snocUnsafe arr1 x
19722297 step arr x = snocUnsafe arr x
19732298
19742299 extract = liftIO . rightSize
19752300
1976−-- XXX Compare writeWith with fromStreamD which uses an array of streams
2301+-- XXX Compare createWith with fromStreamD which uses an array of streams
19772302-- implementation. We can write this using buildChunks above if that is faster.
1978−-- If writeWith is faster then we should use that to implement
2303+-- If createWith is faster then we should use that to implement
19792304-- fromStreamD.
19802305--
19812306-- XXX The realloc based implementation needs to make one extra copy if we use
@@ -1990,39 +2315,56 @@ writeWithAs ps elemCount =
19902315-- XXX check if GHC's memory allocator is efficient enough. We can try the C
19912316-- malloc to compare against.
19922317
1993−-- | @writeWith minCount@ folds the whole input to a single array. The array
2318+-- | @createWith minCount@ folds the whole input to a single array. The array
19942319-- starts at a size big enough to hold minCount elements, the size is doubled
19952320-- every time the array needs to be grown.
19962321--
19972322-- /Caution! Do not use this on infinite streams./
19982323--
1999−-- >>> f n = MutArray.writeAppendWith (* 2) (MutArray.new n)
2000−-- >>> writeWith n = Fold.rmapM MutArray.rightSize (f n)
2001−-- >>> writeWith n = Fold.rmapM MutArray.fromChunksK (MutArray.buildChunks n)
2324+-- >>> f n = MutArray.appendWith (* 2) (MutArray.emptyOf n)
2325+-- >>> createWith n = Fold.rmapM MutArray.rightSize (f n)
2326+-- >>> createWith n = Fold.rmapM MutArray.fromChunksK (MutArray.buildChunks n)
20022327--
20032328-- /Pre-release/
2004−{-# INLINE_NORMAL writeWith #-}
2329+{-# INLINE_NORMAL createWith #-}
2330+createWith :: forall m a. (MonadIO m, Unbox a)
2331+ => Int -> Fold m a (MutArray a)
2332+-- createWith n = FL.rmapM rightSize $ appendWith (* 2) (emptyOf n)
2333+createWith = writeWithAs Unpinned
2334+
2335+{-# DEPRECATED writeWith "Please use createWith instead." #-}
2336+{-# INLINE writeWith #-}
20052337writeWith :: forall m a. (MonadIO m, Unbox a)
20062338 => Int -> Fold m a (MutArray a)
2007−-- writeWith n = FL.rmapM rightSize $ writeAppendWith (* 2) (new n)
2008−writeWith = writeWithAs Unpinned
2339+writeWith = createWith
20092340
20102341-- | Fold the whole input to a single array.
20112342--
2012−-- Same as 'writeWith' using an initial array size of 'arrayChunkBytes' bytes
2343+-- Same as 'createWith' using an initial array size of 'arrayChunkBytes' bytes
20132344-- rounded up to the element size.
20142345--
20152346-- /Caution! Do not use this on infinite streams./
20162347--
2348+{-# INLINE create #-}
2349+create :: forall m a. (MonadIO m, Unbox a) => Fold m a (MutArray a)
2350+create = createWith (allocBytesToElemCount (undefined :: a) arrayChunkBytes)
2351+
2352+-- XXX Deprecate in major
2353+-- {-# DEPRECATED write "Please use create instead." #-}
20172354{-# INLINE write #-}
20182355write :: forall m a. (MonadIO m, Unbox a) => Fold m a (MutArray a)
2019−write = writeWith (allocBytesToElemCount (undefined :: a) arrayChunkBytes)
2356+write = create
20202357
2021−-- | Like 'write' but creates a pinned array.
2358+-- | Like 'create' but creates a pinned array.
2359+{-# INLINE pinnedCreate #-}
2360+pinnedCreate :: forall m a. (MonadIO m, Unbox a) => Fold m a (MutArray a)
2361+pinnedCreate =
2362+ writeWithAs Pinned (allocBytesToElemCount (undefined :: a) arrayChunkBytes)
2363+
2364+{-# DEPRECATED pinnedWrite "Please use pinnedCreate instead." #-}
20222365{-# INLINE pinnedWrite #-}
20232366pinnedWrite :: forall m a. (MonadIO m, Unbox a) => Fold m a (MutArray a)
2024−pinnedWrite =
2025− writeWithAs Pinned (allocBytesToElemCount (undefined :: a) arrayChunkBytes)
2367+pinnedWrite = pinnedCreate
20262368
20272369-------------------------------------------------------------------------------
20282370-- construct from streams, known size
@@ -2032,7 +2374,7 @@ pinnedWrite =
20322374fromStreamDNAs :: forall m a. (MonadIO m, Unbox a)
20332375 => PinnedState -> Int -> D.Stream m a -> m (MutArray a)
20342376fromStreamDNAs ps limit str = do
2035− (arr :: MutArray a) <- liftIO $ newAs ps limit
2377+ (arr :: MutArray a) <- newAs ps limit
20362378 end <- D.foldlM' (fwrite (arrContents arr)) (return $ arrEnd arr) $ D.take limit str
20372379 return $ arr {arrEnd = end}
20382380
@@ -2042,17 +2384,18 @@ fromStreamDNAs ps limit str = do
20422384 liftIO $ pokeAt ptr arrContents x
20432385 return $ INDEX_NEXT(ptr,a)
20442386
2045−-- | Use the 'writeN' fold instead.
2387+-- | Use the 'createOf' fold instead.
20462388--
2047−-- >>> fromStreamN n = Stream.fold (MutArray.writeN n)
2389+-- >>> fromStreamN n = Stream.fold (MutArray.createOf n)
20482390--
20492391{-# INLINE_NORMAL fromStreamN #-}
20502392fromStreamN :: forall m a. (MonadIO m, Unbox a)
20512393 => Int -> D.Stream m a -> m (MutArray a)
2052−-- fromStreamDN n = D.fold (writeN n)
2394+-- fromStreamDN n = D.fold (createOf n)
20532395fromStreamN = fromStreamDNAs Unpinned
20542396
20552397{-# DEPRECATED fromStreamDN "Please use fromStreamN instead." #-}
2398+{-# INLINE fromStreamDN #-}
20562399fromStreamDN :: forall m a. (MonadIO m, Unbox a)
20572400 => Int -> D.Stream m a -> m (MutArray a)
20582401fromStreamDN = fromStreamN
@@ -2075,20 +2418,20 @@ pinnedFromListN n xs = fromStreamDNAs Pinned n $ D.fromList xs
20752418-- /Pre-release/
20762419{-# INLINE fromListRevN #-}
20772420fromListRevN :: (MonadIO m, Unbox a) => Int -> [a] -> m (MutArray a)
2078−fromListRevN n xs = D.fold (writeRevN n) $ D.fromList xs
2421+fromListRevN n xs = D.fold (revCreateOf n) $ D.fromList xs
20792422
20802423-- | Convert a pure stream in Identity monad to a mutable array.
20812424{-# INLINABLE fromPureStreamN #-}
20822425fromPureStreamN :: (MonadIO m, Unbox a) =>
20832426 Int -> Stream Identity a -> m (MutArray a)
20842427fromPureStreamN n xs =
2085− liftIO $ D.fold (writeN n) $ D.morphInner (return . runIdentity) xs
2428+ D.fold (createOf n) $ D.morphInner (return . runIdentity) xs
20862429
20872430-- | Convert a pure stream in Identity monad to a mutable array.
20882431{-# INLINABLE fromPureStream #-}
20892432fromPureStream :: (MonadIO m, Unbox a) => Stream Identity a -> m (MutArray a)
20902433fromPureStream xs =
2091− liftIO $ D.fold write $ D.morphInner (return . runIdentity) xs
2434+ D.fold create $ D.morphInner (return . runIdentity) xs
20922435
20932436{-# INLINABLE fromPtrN #-}
20942437fromPtrN :: MonadIO m => Int -> Ptr Word8 -> m (MutArray Word8)
@@ -2097,7 +2440,7 @@ fromPtrN len addr = do
20972440 -- XXX We can implement a stream copy in a similar way by streaming Word64
20982441 -- first and then remaining Word8.
20992442 arr <- new len
2100− _ <- asUnpinnedPtrUnsafe arr
2443+ _ <- unsafeAsPtr arr
21012444 (\ptr -> liftIO $ c_memcpy ptr addr (fromIntegral len))
21022445 return (arr {arrEnd = len})
21032446
@@ -2113,7 +2456,7 @@ fromByteStr# addr = do
21132456 len <- liftIO $ c_strlen (Ptr addr)
21142457 let lenInt = fromIntegral len
21152458 arr <- new lenInt
2116− _ <- asUnpinnedPtrUnsafe arr (\ptr -> liftIO $ c_memcpy ptr (Ptr addr) len)
2459+ _ <- unsafeAsPtr arr (\ptr -> liftIO $ c_memcpy ptr (Ptr addr) len)
21172460 return (arr {arrEnd = lenInt})
21182461
21192462-------------------------------------------------------------------------------
@@ -2134,7 +2477,7 @@ fromChunksRealloced s = do
21342477 arr <- D.foldlM' spliceExp (pure a) strm
21352478 -- Reallocation is exponential so there may be 50% empty space in
21362479 -- worst case. One more reallocation to reclaim the space.
2137− liftIO $ rightSize arr
2480+ rightSize arr
21382481 Nothing -> pure nil
21392482
21402483-------------------------------------------------------------------------------
@@ -2172,6 +2515,7 @@ fromChunksK :: (Unbox a, MonadIO m) =>
21722515fromChunksK = fromChunkskAs Unpinned
21732516
21742517{-# DEPRECATED fromArrayStreamK "Please use fromChunksK instead." #-}
2518+{-# INLINE fromArrayStreamK #-}
21752519fromArrayStreamK :: (Unbox a, MonadIO m) =>
21762520 StreamK m (MutArray a) -> m (MutArray a)
21772521fromArrayStreamK = fromChunksK
@@ -2183,7 +2527,7 @@ fromStreamDAs ps m =
21832527 arrayStreamKFromStreamDAs Unpinned m >>= fromChunkskAs ps
21842528
21852529-- | Create an 'Array' from a stream. This is useful when we want to create a
2186−-- single array from a stream of unknown size. 'writeN' is at least twice
2530+-- single array from a stream of unknown size. 'createOf' is at least twice
21872531-- as efficient when the size is already known.
21882532--
21892533-- Note that if the input stream is too large memory allocation for the array
@@ -2195,19 +2539,19 @@ fromStreamDAs ps m =
21952539fromStream :: (MonadIO m, Unbox a) => Stream m a -> m (MutArray a)
21962540fromStream = fromStreamDAs Unpinned
21972541
2198−-- fromStream (Stream m) = P.fold write m
2542+-- fromStream (Stream m) = P.fold create m
21992543-- CAUTION: a very large number (millions) of arrays can degrade performance
22002544-- due to GC overhead because we need to buffer the arrays before we flatten
22012545-- all the arrays.
22022546--
2203−-- XXX Compare if this is faster or "fold write".
2547+-- XXX Compare if this is faster or "fold create".
22042548--
22052549-- | We could take the approach of doubling the memory allocation on each
22062550-- overflow. This would result in more or less the same amount of copying as in
22072551-- the chunking approach. However, if we have to shrink in the end then it may
22082552-- result in an extra copy of the entire data.
22092553--
2210−-- >>> fromStreamD = StreamD.fold MutArray.write
2554+-- >>> fromStreamD = StreamD.fold MutArray.create
22112555--
22122556{-# INLINE fromStreamD #-}
22132557{-# DEPRECATED fromStreamD "Please use fromStream instead." #-}
@@ -2237,6 +2581,10 @@ fromListRev xs = fromListRevN (Prelude.length xs) xs
22372581-- Cloning
22382582-------------------------------------------------------------------------------
22392583
2584+-- Arrays are aligned on 64-bit boundaries. The fastest way to copy an array is
2585+-- to unsafeCast it to Word64, read it, write it to Word64 array and unsafeCast
2586+-- it again. We can use SIMD read/write as well.
2587+
22402588{-# INLINE cloneAs #-}
22412589cloneAs ::
22422590 ( MonadIO m
@@ -2246,7 +2594,7 @@ cloneAs ::
22462594 )
22472595 => PinnedState -> MutArray a -> m (MutArray a)
22482596cloneAs ps src =
2249− liftIO $ do
2597+ do
22502598 let startSrc = arrStart src
22512599 srcLen = arrEnd src - startSrc
22522600 newArrContents <-
@@ -2285,17 +2633,16 @@ spliceCopy :: forall m a. MonadIO m =>
22852633 Unbox a =>
22862634#endif
22872635 MutArray a -> MutArray a -> m (MutArray a)
2288−spliceCopy arr1 arr2 = liftIO $ do
2636+spliceCopy arr1 arr2 = do
22892637 let start1 = arrStart arr1
22902638 start2 = arrStart arr2
22912639 len1 = arrEnd arr1 - start1
22922640 len2 = arrEnd arr2 - start2
2293− let newLen = len1 + len2
2641+ let len = len1 + len2
22942642 newArrContents <-
22952643 if Unboxed.isPinned (arrContents arr1)
2296− then Unboxed.pinnedNew newLen
2297− else Unboxed.new newLen
2298− let len = len1 + len2
2644+ then liftIO $ Unboxed.pinnedNew len
2645+ else liftIO $ Unboxed.new len
22992646 putSliceUnsafe (arrContents arr1) start1 newArrContents 0 len1
23002647 putSliceUnsafe (arrContents arr2) start2 newArrContents len1 len2
23012648 return $ MutArray newArrContents 0 len len
@@ -2307,7 +2654,7 @@ spliceCopy arr1 arr2 = liftIO $ do
23072654spliceUnsafe :: MonadIO m =>
23082655 MutArray a -> MutArray a -> m (MutArray a)
23092656spliceUnsafe dst src =
2310− liftIO $ do
2657+ do
23112658 let startSrc = arrStart src
23122659 srcLen = arrEnd src - startSrc
23132660 endDst = arrEnd dst
@@ -2329,7 +2676,7 @@ spliceWith :: forall m a. (MonadIO m, Unbox a) =>
23292676 (Int -> Int -> Int) -> MutArray a -> MutArray a -> m (MutArray a)
23302677spliceWith sizer dst@(MutArray _ start end bound) src = do
23312678{-
2332− let f = writeAppendWith (`sizer` byteLength src) (return dst)
2679+ let f = appendWith (`sizer` byteLength src) (return dst)
23332680 in D.fold f (toStreamD src)
23342681-}
23352682 assert (end <= bound) (return ())
@@ -2345,7 +2692,7 @@ spliceWith sizer dst@(MutArray _ start end bound) src = do
23452692 $ "splice: newSize is less than the total size "
23462693 ++ "of arrays being appended. Please check the "
23472694 ++ "sizer function passed."
2348− liftIO $ realloc newSizeInBytes dst
2695+ realloc newSizeInBytes dst
23492696 else return dst
23502697 spliceUnsafe dst1 src
23512698
@@ -2397,7 +2744,7 @@ splitOn predicate arr =
23972744{-# INLINE breakOn #-}
23982745breakOn :: MonadIO m
23992746 => Word8 -> MutArray Word8 -> m (MutArray Word8, Maybe (MutArray Word8))
2400−breakOn sep arr@MutArray{..} = asUnpinnedPtrUnsafe arr $ \p -> liftIO $ do
2747+breakOn sep arr@MutArray{..} = unsafeAsPtr arr $ \p -> liftIO $ do
24012748 -- XXX We do not need memchr here, we can use a Haskell equivalent.
24022749 -- Need efficient stream based primitives that work on Word64.
24032750 loc <- c_memchr p sep (fromIntegral $ byteLength arr)
@@ -2420,33 +2767,41 @@ breakOn sep arr@MutArray{..} = asUnpinnedPtrUnsafe arr $ \p -> liftIO $ do
24202767 }
24212768 )
24222769
2770+-- | Like 'splitAt' but does not check whether the index is valid.
2771+--
2772+{-# INLINE unsafeSplitAt #-}
2773+unsafeSplitAt :: forall a. Unbox a =>
2774+ Int -> MutArray a -> (MutArray a, MutArray a)
2775+unsafeSplitAt i MutArray{..} =
2776+ let off = i * SIZE_OF(a)
2777+ p = arrStart + off
2778+ in ( MutArray
2779+ { arrContents = arrContents
2780+ , arrStart = arrStart
2781+ , arrEnd = p
2782+ , arrBound = p
2783+ }
2784+ , MutArray
2785+ { arrContents = arrContents
2786+ , arrStart = p
2787+ , arrEnd = arrEnd
2788+ , arrBound = arrBound
2789+ }
2790+ )
2791+
24232792-- | Create two slices of an array without copying the original array. The
24242793-- specified index @i@ is the first index of the second slice.
24252794--
24262795{-# INLINE splitAt #-}
24272796splitAt :: forall a. Unbox a => Int -> MutArray a -> (MutArray a, MutArray a)
2428−splitAt i arr@MutArray{..} =
2797+splitAt i arr =
24292798 let maxIndex = length arr - 1
24302799 in if i < 0
24312800 then error "sliceAt: negative array index"
24322801 else if i > maxIndex
24332802 then error $ "sliceAt: specified array index " ++ show i
24342803 ++ " is beyond the maximum index " ++ show maxIndex
2435− else let off = i * SIZE_OF(a)
2436− p = arrStart + off
2437− in ( MutArray
2438− { arrContents = arrContents
2439− , arrStart = arrStart
2440− , arrEnd = p
2441− , arrBound = p
2442− }
2443− , MutArray
2444− { arrContents = arrContents
2445− , arrStart = p
2446− , arrEnd = arrEnd
2447− , arrBound = arrBound
2448− }
2449− )
2804+ else unsafeSplitAt i arr
24502805
24512806-------------------------------------------------------------------------------
24522807-- Casting
@@ -2496,9 +2851,13 @@ cast arr =
24962851-- guarantees that unsafe calls will be performed in the calling thread. Making
24972852-- it safe to pass heap-allocated objects to unsafe functions.
24982853
2499−-- Should we just name it asPtr, the unsafety is implicit for any pointer
2854+-- XXX Should we just name it asPtr, the unsafety is implicit for any pointer
25002855-- operations. And we are safe from Haskell perspective because we will be
25012856-- pinning the memory.
2857+--
2858+-- XXX we cannot pass the length of the ptr here as in some cases it may not be
2859+-- available e.g. a null terminated C string. However, we can create another
2860+-- flavor of the API e.g. asPtrN.
25022861
25032862-- | Use a @MutArray a@ as @Ptr a@. This is useful when we want to pass an
25042863-- array as a pointer to some operating system call or to a "safe" FFI call.
@@ -2516,16 +2875,21 @@ cast arr =
25162875--
25172876-- /Pre-release/
25182877--
2878+{-# INLINE unsafePinnedAsPtr #-}
2879+unsafePinnedAsPtr :: MonadIO m => MutArray a -> (Ptr a -> m b) -> m b
2880+unsafePinnedAsPtr arr f =
2881+ Unboxed.unsafePinnedAsPtr
2882+ (arrContents arr) (\ptr -> f (ptr `plusPtr` arrStart arr))
2883+
2884+{-# DEPRECATED asPtrUnsafe "Please use unsafePinnedAsPtr instead." #-}
25192885{-# INLINE asPtrUnsafe #-}
25202886asPtrUnsafe :: MonadIO m => MutArray a -> (Ptr a -> m b) -> m b
2521−asPtrUnsafe arr f =
2522− Unboxed.asPtrUnsafe
2523− (arrContents arr) (\ptr -> f (ptr `plusPtr` arrStart arr))
2887+asPtrUnsafe = unsafePinnedAsPtr
25242888
2525−{-# INLINE asUnpinnedPtrUnsafe #-}
2526−asUnpinnedPtrUnsafe :: MonadIO m => MutArray a -> (Ptr a -> m b) -> m b
2527−asUnpinnedPtrUnsafe arr f =
2528− Unboxed.asUnpinnedPtrUnsafe
2889+{-# INLINE unsafeAsPtr #-}
2890+unsafeAsPtr :: MonadIO m => MutArray a -> (Ptr a -> m b) -> m b
2891+unsafeAsPtr arr f =
2892+ Unboxed.unsafeAsPtr
25292893 (arrContents arr) (\ptr -> f (ptr `plusPtr` arrStart arr))
25302894
25312895-------------------------------------------------------------------------------
@@ -2543,28 +2907,26 @@ asUnpinnedPtrUnsafe arr f =
25432907-- /Pre-release/
25442908{-# INLINE byteCmp #-}
25452909byteCmp :: MonadIO m => MutArray a -> MutArray a -> m Ordering
2546−byteCmp arr1 arr2 =
2547− liftIO
2548− $ do
2549− let marr1 = getMutableByteArray# (arrContents arr1)
2550− marr2 = getMutableByteArray# (arrContents arr2)
2551− !(I# st1#) = arrStart arr1
2552− !(I# st2#) = arrStart arr2
2553− !(I# len#) = byteLength arr1
2554− case compare (byteLength arr1) (byteLength arr2) of
2555− EQ -> do
2556− r <- liftIO $ IO $ \s# ->
2557− let res =
2558− I#
2559− (compareByteArrays#
2560− (unsafeCoerce# marr1)
2561− st1#
2562− (unsafeCoerce# marr2)
2563− st2#
2564− len#)
2565− in (# s#, res #)
2566− return $ compare r 0
2567− x -> return x
2910+byteCmp arr1 arr2 = do
2911+ let marr1 = getMutableByteArray# (arrContents arr1)
2912+ marr2 = getMutableByteArray# (arrContents arr2)
2913+ !(I# st1#) = arrStart arr1
2914+ !(I# st2#) = arrStart arr2
2915+ !(I# len#) = byteLength arr1
2916+ case compare (byteLength arr1) (byteLength arr2) of
2917+ EQ -> do
2918+ r <- liftIO $ IO $ \s# ->
2919+ let res =
2920+ I#
2921+ (compareByteArrays#
2922+ (unsafeCoerce# marr1)
2923+ st1#
2924+ (unsafeCoerce# marr2)
2925+ st2#
2926+ len#)
2927+ in (# s#, res #)
2928+ return $ compare r 0
2929+ x -> return x
25682930
25692931{-# INLINE cmp #-}
25702932{-# DEPRECATED cmp "Please use byteCmp instead." #-}
@@ -2581,7 +2943,309 @@ byteEq :: MonadIO m => MutArray a -> MutArray a -> m Bool
25812943byteEq arr1 arr2 = fmap (EQ ==) $ byteCmp arr1 arr2
25822944
25832945-------------------------------------------------------------------------------
2584−-- NFData
2946+-- Compact
2947+-------------------------------------------------------------------------------
2948+
2949+-- Note: LE versions avoid an extra copy compared to GE. LE parser trades
2950+-- backtracking one array in lieu of avoiding a copy. However, LE and GE both
2951+-- can leave some memory unused. They can split the last array to fit it
2952+-- exactly in the space.
2953+
2954+{-# INLINE_NORMAL pCompactLeAs #-}
2955+pCompactLeAs ::
2956+ forall m a. (MonadIO m, Unbox a)
2957+ => PinnedState -> Int -> Parser (MutArray a) m (MutArray a)
2958+pCompactLeAs ps maxElems = Parser step initial extract
2959+
2960+ where
2961+
2962+ maxBytes = maxElems * SIZE_OF(a)
2963+
2964+ functionName = "Streamly.Internal.Data.MutArray.pCompactLE"
2965+
2966+ initial =
2967+ return
2968+ $ if maxElems <= 0
2969+ then error
2970+ $ functionName
2971+ ++ ": the size of arrays ["
2972+ ++ show maxElems ++ "] must be a natural number"
2973+ else Parser.IPartial Nothing
2974+
2975+ step Nothing arr =
2976+ return
2977+ $ let len = byteLength arr
2978+ in if len >= maxBytes
2979+ then Parser.Done 0 arr
2980+ else Parser.Partial 0 (Just arr)
2981+ -- XXX Split the last array to use the space more compactly.
2982+ step (Just buf) arr =
2983+ let len = byteLength buf + byteLength arr
2984+ in if len > maxBytes
2985+ then return $ Parser.Done 1 buf
2986+ else do
2987+ buf1 <-
2988+ if byteCapacity buf < maxBytes
2989+ then liftIO $ reallocExplicitAs
2990+ ps (SIZE_OF(a)) maxBytes buf
2991+ else return buf
2992+ buf2 <- spliceUnsafe buf1 arr
2993+ return $ Parser.Partial 0 (Just buf2)
2994+
2995+ extract Nothing = return $ Parser.Done 0 nil
2996+ extract (Just buf) = return $ Parser.Done 0 buf
2997+
2998+-- | Parser @pCompactLE maxElems@ coalesces adjacent arrays in the input stream
2999+-- only if the combined size would be less than or equal to @maxElems@
3000+-- elements. Note that it won't split an array if the original array is already
3001+-- larger than maxElems.
3002+--
3003+-- @maxElems@ must be greater than 0.
3004+--
3005+-- Generates unpinned arrays irrespective of the pinning status of input
3006+-- arrays.
3007+--
3008+-- /Internal/
3009+{-# INLINE pCompactLE #-}
3010+pCompactLE ::
3011+ forall m a. (MonadIO m, Unbox a)
3012+ => Int -> Parser (MutArray a) m (MutArray a)
3013+pCompactLE = pCompactLeAs Unpinned
3014+
3015+-- | Pinned version of 'pCompactLE'.
3016+{-# INLINE pPinnedCompactLE #-}
3017+pPinnedCompactLE ::
3018+ forall m a. (MonadIO m, Unbox a)
3019+ => Int -> Parser (MutArray a) m (MutArray a)
3020+pPinnedCompactLE = pCompactLeAs Pinned
3021+
3022+data SpliceState s arr
3023+ = SpliceInitial s
3024+ | SpliceBuffering s arr
3025+ | SpliceYielding arr (SpliceState s arr)
3026+ | SpliceFinish
3027+
3028+-- This mutates the first array (if it has space) to append values from the
3029+-- second one. This would work for immutable arrays as well because an
3030+-- immutable array never has additional space so a new array is allocated
3031+-- instead of mutating it.
3032+
3033+{-# INLINE_NORMAL compactLeAs #-}
3034+compactLeAs :: forall m a. (MonadIO m, Unbox a)
3035+ => PinnedState -> Int -> D.Stream m (MutArray a) -> D.Stream m (MutArray a)
3036+compactLeAs ps maxElems (D.Stream step state) =
3037+ D.Stream step' (SpliceInitial state)
3038+
3039+ where
3040+
3041+ maxBytes = maxElems * SIZE_OF(a)
3042+
3043+ functionName = "Streamly.Internal.Data.MutArray.rCompactLE"
3044+
3045+ {-# INLINE_LATE step' #-}
3046+ step' gst (SpliceInitial st) = do
3047+ when (maxElems <= 0) $
3048+ -- XXX we can pass the module string from the higher level API
3049+ error $ functionName ++ ": the size of arrays [" ++ show maxElems
3050+ ++ "] must be a natural number"
3051+ r <- step gst st
3052+ case r of
3053+ D.Yield arr s -> return $
3054+ let len = byteLength arr
3055+ in if len >= maxBytes
3056+ then D.Skip (SpliceYielding arr (SpliceInitial s))
3057+ else D.Skip (SpliceBuffering s arr)
3058+ D.Skip s -> return $ D.Skip (SpliceInitial s)
3059+ D.Stop -> return D.Stop
3060+
3061+ -- XXX Split the last array to use the space more compactly.
3062+ step' gst (SpliceBuffering st buf) = do
3063+ r <- step gst st
3064+ case r of
3065+ D.Yield arr s -> do
3066+ let len = byteLength buf + byteLength arr
3067+ if len > maxBytes
3068+ then return $
3069+ D.Skip (SpliceYielding buf (SpliceBuffering s arr))
3070+ else do
3071+ buf1 <- if byteCapacity buf < maxBytes
3072+ then liftIO $ reallocExplicitAs
3073+ ps (SIZE_OF(a)) maxBytes buf
3074+ else return buf
3075+ buf2 <- spliceUnsafe buf1 arr
3076+ return $ D.Skip (SpliceBuffering s buf2)
3077+ D.Skip s -> return $ D.Skip (SpliceBuffering s buf)
3078+ D.Stop -> return $ D.Skip (SpliceYielding buf SpliceFinish)
3079+
3080+ step' _ SpliceFinish = return D.Stop
3081+
3082+ step' _ (SpliceYielding arr next) = return $ D.Yield arr next
3083+
3084+
3085+{-# INLINE_NORMAL fCompactGeAs #-}
3086+fCompactGeAs ::
3087+ forall m a. (MonadIO m, Unbox a)
3088+ => PinnedState -> Int -> FL.Fold m (MutArray a) (MutArray a)
3089+fCompactGeAs ps minElems = Fold step initial extract extract
3090+
3091+ where
3092+
3093+ minBytes = minElems * SIZE_OF(a)
3094+
3095+ functionName = "Streamly.Internal.Data.MutArray.fCompactGE"
3096+
3097+ initial =
3098+ return
3099+ $ if minElems < 0
3100+ then error
3101+ $ functionName
3102+ ++ ": the size of arrays ["
3103+ ++ show minElems ++ "] must be a natural number"
3104+ else FL.Partial Nothing
3105+
3106+ step Nothing arr =
3107+ return
3108+ $ let len = byteLength arr
3109+ in if len >= minBytes
3110+ then FL.Done arr
3111+ else FL.Partial (Just arr)
3112+ -- XXX Buffer arrays as a list to avoid copy and reallocations
3113+ step (Just buf) arr = do
3114+ let len = byteLength buf + byteLength arr
3115+ buf1 <-
3116+ if byteCapacity buf < len
3117+ then liftIO $ reallocExplicitAs
3118+ ps (SIZE_OF(a)) (max minBytes len) buf
3119+ else return buf
3120+ buf2 <- spliceUnsafe buf1 arr
3121+ if len >= minBytes
3122+ then return $ FL.Done buf2
3123+ else return $ FL.Partial (Just buf2)
3124+
3125+ extract Nothing = return nil
3126+ extract (Just buf) = return buf
3127+
3128+-- | Fold @fCompactGE minElems@ coalesces adjacent arrays in the input stream
3129+-- until the size becomes greater than or equal to @minElems@.
3130+--
3131+-- Generates unpinned arrays irrespective of the pinning status of input
3132+-- arrays.
3133+{-# INLINE fCompactGE #-}
3134+fCompactGE ::
3135+ forall m a. (MonadIO m, Unbox a)
3136+ => Int -> FL.Fold m (MutArray a) (MutArray a)
3137+fCompactGE = fCompactGeAs Unpinned
3138+
3139+-- | Pinned version of 'fCompactGE'.
3140+{-# INLINE fPinnedCompactGE #-}
3141+fPinnedCompactGE ::
3142+ forall m a. (MonadIO m, Unbox a)
3143+ => Int -> FL.Fold m (MutArray a) (MutArray a)
3144+fPinnedCompactGE = fCompactGeAs Pinned
3145+
3146+{-# INLINE_NORMAL lCompactGeAs #-}
3147+lCompactGeAs :: forall m a. (MonadIO m, Unbox a)
3148+ => PinnedState -> Int -> Fold m (MutArray a) () -> Fold m (MutArray a) ()
3149+-- The fold version turns out to be a little bit slower.
3150+-- lCompactGeAs ps n = FL.many (fCompactGeAs ps n)
3151+lCompactGeAs ps minElems (Fold step1 initial1 _ final1) =
3152+ Fold step initial extract final
3153+
3154+ where
3155+
3156+ minBytes = minElems * SIZE_OF(a)
3157+
3158+ functionName = "Streamly.Internal.Data.MutArray.lCompactGE"
3159+
3160+ initial = do
3161+ when (minElems <= 0) $
3162+ -- XXX we can pass the module string from the higher level API
3163+ error $ functionName ++ ": the size of arrays ["
3164+ ++ show minElems ++ "] must be a natural number"
3165+
3166+ r <- initial1
3167+ return $ first (Tuple' Nothing) r
3168+
3169+ {-# INLINE runInner #-}
3170+ runInner len acc buf =
3171+ if len >= minBytes
3172+ then do
3173+ r <- step1 acc buf
3174+ case r of
3175+ FL.Done _ -> return $ FL.Done ()
3176+ FL.Partial s -> do
3177+ _ <- final1 s
3178+ res <- initial1
3179+ return $ first (Tuple' Nothing) res
3180+ else return $ FL.Partial $ Tuple' (Just buf) acc
3181+
3182+ step (Tuple' Nothing r1) arr =
3183+ runInner (byteLength arr) r1 arr
3184+
3185+ -- XXX Buffer arrays as a list to avoid copy and reallocations
3186+ step (Tuple' (Just buf) r1) arr = do
3187+ let len = byteLength buf + byteLength arr
3188+ buf1 <- if byteCapacity buf < len
3189+ then liftIO $ reallocExplicitAs
3190+ ps (SIZE_OF(a)) (max minBytes len) buf
3191+ else return buf
3192+ buf2 <- spliceUnsafe buf1 arr
3193+ runInner len r1 buf2
3194+
3195+ -- XXX Several folds do extract >=> final, therefore, we need to make final
3196+ -- return "m b" rather than using extract post it if we want extract to be
3197+ -- partial.
3198+ --
3199+ -- extract forces the pending buffer to be sent to the fold which is not
3200+ -- what we want.
3201+ extract _ = error "lCompactGE: not designed for scanning"
3202+
3203+ final (Tuple' Nothing r1) = final1 r1
3204+ final (Tuple' (Just buf) r1) = do
3205+ r <- step1 r1 buf
3206+ case r of
3207+ FL.Partial rr -> final1 rr
3208+ FL.Done _ -> return ()
3209+
3210+-- | Like 'compactGE' but for transforming folds instead of stream.
3211+--
3212+-- >>> lCompactGE n = Fold.many (MutArray.fCompactGE n)
3213+--
3214+-- Generates unpinned arrays irrespective of the pinning status of input
3215+-- arrays.
3216+{-# INLINE lCompactGE #-}
3217+lCompactGE :: forall m a. (MonadIO m, Unbox a)
3218+ => Int -> Fold m (MutArray a) () -> Fold m (MutArray a) ()
3219+lCompactGE = lCompactGeAs Unpinned
3220+
3221+-- | Pinned version of 'lCompactGE'.
3222+{-# INLINE lPinnedCompactGE #-}
3223+lPinnedCompactGE :: forall m a. (MonadIO m, Unbox a)
3224+ => Int -> Fold m (MutArray a) () -> Fold m (MutArray a) ()
3225+lPinnedCompactGE = lCompactGeAs Pinned
3226+
3227+-- | @compactGE n stream@ coalesces adjacent arrays in the @stream@ until
3228+-- the size becomes greater than or equal to @n@.
3229+--
3230+-- >>> compactGE n = Stream.foldMany (MutArray.fCompactGE n)
3231+--
3232+{-# INLINE compactGE #-}
3233+compactGE ::
3234+ (MonadIO m, Unbox a)
3235+ => Int -> Stream m (MutArray a) -> Stream m (MutArray a)
3236+compactGE n = D.foldMany (fCompactGE n)
3237+
3238+-- | 'compactEQ n' coalesces adajacent arrays in the input stream to
3239+-- arrays of exact size @n@.
3240+--
3241+-- /Unimplemented/
3242+{-# INLINE compactEQ #-}
3243+compactEQ :: -- (MonadIO m, Unbox a) =>
3244+ Int -> Stream m (MutArray a) -> Stream m (MutArray a)
3245+compactEQ _n = undefined -- D.parseManyD (pCompactEQ n)
3246+
3247+-------------------------------------------------------------------------------
3248+-- In-place mutation algorithms
25853249-------------------------------------------------------------------------------
25863250
25873251-- | Strip elements which match with predicate from both ends.
core/src/Streamly/Internal/Data/MutByteArray/Type.hs+40−16
@@ -13,7 +13,7 @@ module Streamly.Internal.Data.MutByteArray.Type
1313 -- ** MutByteArray
1414 MutByteArray(..)
1515 , MutableByteArray
16− , getMutableByteArray#
16+ , getMutableByteArray# -- XXX getMutByteArray#
1717
1818 -- ** Pinning
1919 , PinnedState(..)
@@ -22,20 +22,24 @@ module Streamly.Internal.Data.MutByteArray.Type
2222 , unpin
2323
2424 -- ** Allocation
25− , nil
26− , newBytesAs
25+ , empty
26+ , newBytesAs -- XXX should be removed
2727 , new
2828 , pinnedNew
29− , pinnedNewAlignedBytes
29+ , pinnedNewAlignedBytes -- XXX should be removed
3030
3131 -- ** Access
32− , sizeOfMutableByteArray
32+ , sizeOfMutableByteArray -- XXX length
3333 , putSliceUnsafe
3434 , cloneSliceUnsafeAs
3535 , cloneSliceUnsafe
3636 , pinnedCloneSliceUnsafe
37+ , unsafePinnedAsPtr
38+ , unsafeAsPtr
39+
40+ -- ** Deprecated
3741 , asPtrUnsafe
38− , asUnpinnedPtrUnsafe
42+ , nil
3943 ) where
4044
4145import Control.Monad.IO.Class (MonadIO(..))
@@ -54,7 +58,7 @@ import GHC.Exts
5458
5559data PinnedState
5660 = Pinned
57− | Unpinned
61+ | Unpinned deriving (Show, Eq)
5862
5963-- XXX can use UnliftedNewtypes
6064
@@ -83,6 +87,9 @@ touch :: MutByteArray -> IO ()
8387touch (MutByteArray contents) =
8488 IO $ \s -> case touch# contents s of s' -> (# s', () #)
8589
90+-- XXX Some functions in this module are "IO" and others are "m", we need to
91+-- make it consistent.
92+
8693-- XXX We can provide another API for "unsafe" FFI calls passing an unlifted
8794-- pointer to the FFI call. For unsafe calls we do not need to pin the array.
8895-- We can pass an unlifted pointer to the FFI routine to avoid GC kicking in
@@ -111,9 +118,9 @@ touch (MutByteArray contents) =
111118--
112119-- /Pre-release/
113120--
114−{-# INLINE asPtrUnsafe #-}
115−asPtrUnsafe :: MonadIO m => MutByteArray -> (Ptr a -> m b) -> m b
116−asPtrUnsafe arr f = do
121+{-# INLINE unsafePinnedAsPtr #-}
122+unsafePinnedAsPtr :: MonadIO m => MutByteArray -> (Ptr a -> m b) -> m b
123+unsafePinnedAsPtr arr f = do
117124 contents <- liftIO $ pin arr
118125 let !ptr = Ptr (byteArrayContents#
119126 (unsafeCoerce# (getMutableByteArray# contents)))
@@ -121,10 +128,15 @@ asPtrUnsafe arr f = do
121128 liftIO $ touch contents
122129 return r
123130
131+{-# DEPRECATED asPtrUnsafe "Please use unsafePinnedAsPtr instead." #-}
132+{-# INLINE asPtrUnsafe #-}
133+asPtrUnsafe :: MonadIO m => MutByteArray -> (Ptr a -> m b) -> m b
134+asPtrUnsafe = unsafePinnedAsPtr
135+
124136-- | For use with unsafe FFI functions. Does not force pin the array memory.
125−{-# INLINE asUnpinnedPtrUnsafe #-}
126−asUnpinnedPtrUnsafe :: MonadIO m => MutByteArray -> (Ptr a -> m b) -> m b
127−asUnpinnedPtrUnsafe arr f = do
137+{-# INLINE unsafeAsPtr #-}
138+unsafeAsPtr :: MonadIO m => MutByteArray -> (Ptr a -> m b) -> m b
139+unsafeAsPtr arr f = do
128140 let !ptr = Ptr (byteArrayContents#
129141 (unsafeCoerce# (getMutableByteArray# arr)))
130142 r <- f ptr
@@ -135,10 +147,16 @@ asUnpinnedPtrUnsafe arr f = do
135147-- Creation
136148--------------------------------------------------------------------------------
137149
138−{-# NOINLINE nil #-}
150+{-# NOINLINE empty #-}
151+empty :: MutByteArray
152+empty = unsafePerformIO $ new 0
153+
154+{-# DEPRECATED nil "Please use empty instead" #-}
139155nil :: MutByteArray
140−nil = unsafePerformIO $ new 0
156+nil = empty
141157
158+-- XXX add "newRounded" to round up the large size to the next page boundary
159+-- and return the allocated size.
142160{-# INLINE new #-}
143161new :: Int -> IO MutByteArray
144162new nbytes | nbytes < 0 =
@@ -152,7 +170,7 @@ new (I# nbytes) = IO $ \s ->
152170{-# INLINE pinnedNew #-}
153171pinnedNew :: Int -> IO MutByteArray
154172pinnedNew nbytes | nbytes < 0 =
155− errorWithoutStackTrace "pinnedNewByteArray: size must be >= 0"
173+ errorWithoutStackTrace "pinnedNew: size must be >= 0"
156174pinnedNew (I# nbytes) = IO $ \s ->
157175 case newPinnedByteArray# nbytes s of
158176 (# s', mbarr# #) ->
@@ -178,6 +196,12 @@ newBytesAs Pinned = pinnedNew
178196-- Copying
179197-------------------------------------------------------------------------------
180198
199+-- Note: Array copy is more efficient than streaming copy.
200+-- CopyMutableByteArray# translates to genMemcpy in GHC/CmmToAsm/X86/CodeGen.hs
201+-- glibc memcpy copies bytes/words/pages - unrolls the loops:
202+-- https://github.com/bminor/glibc/blob/4290aed05135ae4c0272006442d147f2155e70d7/string/memcpy.c
203+-- https://github.com/bminor/glibc/blob/4290aed05135ae4c0272006442d147f2155e70d7/string/wordcopy.c
204+
181205-- | Put a sub range of a source array into a subrange of a destination array.
182206-- This is not safe as it does not check the bounds of neither the src array
183207-- nor the destination array.
core/src/Streamly/Internal/Data/Serialize/TH.hs+5−1
@@ -7,7 +7,11 @@
77-- Maintainer : streamly@composewell.com
88-- Stability : experimental
99-- Portability : GHC
10−--
10+
11+-- XXX Instead of applying the product constructor in one go can we apply it
12+-- one at a time in case of too many arguments, compiler may not have to save
13+-- them in local vars.
14+
1115module Streamly.Internal.Data.Serialize.TH
1216 (
1317 -- Deriving
core/src/Streamly/Internal/Data/Stream/Container.hs+56−48
@@ -11,16 +11,17 @@
1111
1212module Streamly.Internal.Data.Stream.Container
1313 (
14− nub
14+ -- * Deduplication
15+ ordNub
1516
16− -- * Joins for unconstrained types
17− , joinLeftGeneric
18− , joinOuterGeneric
17+ -- * Joins
18+ , leftJoin
19+ , outerJoin
1920
20− -- * Joins with Ord constraint
21− , joinInner
22− , joinLeft
23− , joinOuter
21+ -- * Ord Joins
22+ , innerOrdJoin
23+ , leftOrdJoin
24+ , outerOrdJoin
2425 )
2526where
2627
@@ -47,12 +48,15 @@ import qualified Streamly.Internal.Data.Stream.Transformer as Stream
4748
4849#include "DocTestDataStream.hs"
4950
50−-- | The memory used is proportional to the number of unique elements in the
51−-- stream. If we want to limit the memory we can just use "take" to limit the
52−-- uniq elements in the stream.
53−{-# INLINE_NORMAL nub #-}
54−nub :: (Monad m, Ord a) => Stream m a -> Stream m a
55−nub (Stream step1 state1) = Stream step (Set.empty, state1)
51+-- | @nub@ specialized to 'Ord' types for better performance. Returns a
52+-- subsequence of the stream removing any duplicate elements.
53+--
54+-- The memory used is proportional to the number of unique elements in the
55+-- stream. One way to limit the memory is to use @take@ on the resulting
56+-- stream to limit the unique elements in the stream.
57+{-# INLINE_NORMAL ordNub #-}
58+ordNub :: (Monad m, Ord a) => Stream m a -> Stream m a
59+ordNub (Stream step1 state1) = Stream step (Set.empty, state1)
5660
5761 where
5862
@@ -78,8 +82,8 @@ toMap =
7882--
7983-- XXX An IntMap may be faster when the keys are Int.
8084-- XXX Use hashmap instead of map?
81−--
82−-- | Like 'joinInner' but uses a 'Map' for efficiency.
85+
86+-- | 'innerJoin' specialized to 'Ord' types for better performance.
8387--
8488-- If the input streams have duplicate keys, the behavior is undefined.
8589--
@@ -90,10 +94,10 @@ toMap =
9094-- Time: O(m + n)
9195--
9296-- /Pre-release/
93−{-# INLINE joinInner #-}
94−joinInner :: (Monad m, Ord k) =>
97+{-# INLINE innerOrdJoin #-}
98+innerOrdJoin :: (Monad m, Ord k) =>
9599 Stream m (k, a) -> Stream m (k, b) -> Stream m (k, a, b)
96−joinInner s1 s2 =
100+innerOrdJoin s1 s2 =
97101 Stream.concatEffect $ do
98102 km <- toMap s2
99103 pure $ Stream.mapMaybe (joinAB km) s1
@@ -106,32 +110,33 @@ joinInner s1 s2 =
106110 Nothing -> Nothing
107111
108112-- XXX We can do this concurrently.
113+-- XXX Check performance of StreamD vs StreamK
109114-- XXX If the second stream is sorted and passed as an Array or a seek capable
110115-- stream then we could use binary search if we have an Ord instance or
111116-- Ordering returning function. The time complexity would then become (m x log
112117-- n).
113118
114−-- XXX Check performance of StreamD vs StreamK
115−
116−-- | Like 'joinInner' but emit @(a, Just b)@, and additionally, for those @a@'s
117−-- that are not equal to any @b@ emit @(a, Nothing)@.
119+-- | Like 'innerJoin' but emits @(a, Just b)@ whenever a and b are equal, for
120+-- those @a@'s that are not equal to any @b@ emits @(a, Nothing)@.
118121--
119−-- The second stream is evaluated multiple times. If the stream is a
120−-- consume-once stream then the caller should cache it in an 'Data.Array.Array'
121−-- before calling this function. Caching may also improve performance if the
122−-- stream is expensive to evaluate.
122+-- This is a generalization of 'innerJoin' to include all elements from the
123+-- left stream and not just those which have an equal in the right stream. This
124+-- is not a commutative operation, the order of the stream arguments matters.
123125--
124−-- >>> joinRightGeneric eq = flip (Stream.joinLeftGeneric eq)
126+-- All the caveats mentioned in 'innerJoin' apply here as well. Right join is
127+-- not provided because it is just a flipped left join:
128+--
129+-- >>> rightJoin eq = flip (Stream.leftJoin eq)
125130--
126131-- Space: O(n) assuming the second stream is cached in memory.
127132--
128133-- Time: O(m x n)
129134--
130135-- /Unimplemented/
131−{-# INLINE joinLeftGeneric #-}
132−joinLeftGeneric :: Monad m =>
136+{-# INLINE leftJoin #-}
137+leftJoin :: Monad m =>
133138 (a -> b -> Bool) -> Stream m a -> Stream m b -> Stream m (a, Maybe b)
134−joinLeftGeneric eq s1 s2 = Stream.evalStateT (return False) $ unCross $ do
139+leftJoin eq s1 s2 = Stream.evalStateT (return False) $ unCross $ do
135140 a <- mkCross (Stream.liftInner s1)
136141 -- XXX should we use StreamD monad here?
137142 -- XXX Is there a better way to perform some action at the end of a loop
@@ -152,19 +157,17 @@ joinLeftGeneric eq s1 s2 = Stream.evalStateT (return False) $ unCross $ do
152157 else mkCross Stream.nil
153158 Nothing -> return (a, Nothing)
154159
155−-- XXX rename to joinLeftOrd?
156−
157−-- | A more efficient 'joinLeft' using a hashmap for efficiency.
160+-- | 'leftJoin' specialized to 'Ord' types for better performance.
158161--
159162-- Space: O(n)
160163--
161164-- Time: O(m + n)
162165--
163166-- /Pre-release/
164−{-# INLINE joinLeft #-}
165−joinLeft :: (Ord k, Monad m) =>
167+{-# INLINE leftOrdJoin #-}
168+leftOrdJoin :: (Ord k, Monad m) =>
166169 Stream m (k, a) -> Stream m (k, b) -> Stream m (k, a, Maybe b)
167−joinLeft s1 s2 =
170+leftOrdJoin s1 s2 =
168171 Stream.concatEffect $ do
169172 km <- toMap s2
170173 return $ fmap (joinAB km) s1
@@ -177,14 +180,19 @@ joinLeft s1 s2 =
177180 Nothing -> (k, a, Nothing)
178181
179182-- XXX We can do this concurrently.
183+-- XXX Check performance of StreamD vs StreamK cross operation.
180184
181−-- XXX Check performance of StreamD vs StreamK
182−
183−-- | Like 'joinLeft' but emits a @(Just a, Just b)@. Like 'joinLeft', for those
185+-- | Like 'leftJoin' but emits a @(Just a, Just b)@. Like 'leftJoin', for those
184186-- @a@'s that are not equal to any @b@ emit @(Just a, Nothing)@, but
185187-- additionally, for those @b@'s that are not equal to any @a@ emit @(Nothing,
186188-- Just b)@.
187189--
190+-- This is a generalization of left join to include all the elements from the
191+-- right stream as well, in other words it is a combination of left and right
192+-- joins. This is a commutative operation. The order of stream arguments can be
193+-- changed without affecting results, except for the ordering of elements in
194+-- the resulting tuple.
195+--
188196-- For space efficiency use the smaller stream as the second stream.
189197--
190198-- Space: O(n)
@@ -192,15 +200,15 @@ joinLeft s1 s2 =
192200-- Time: O(m x n)
193201--
194202-- /Pre-release/
195−{-# INLINE joinOuterGeneric #-}
196−joinOuterGeneric :: MonadIO m =>
203+{-# INLINE outerJoin #-}
204+outerJoin :: MonadIO m =>
197205 (a -> b -> Bool)
198206 -> Stream m a
199207 -> Stream m b
200208 -> Stream m (Maybe a, Maybe b)
201−joinOuterGeneric eq s1 s =
209+outerJoin eq s1 s2 =
202210 Stream.concatEffect $ do
203− inputArr <- Array.fromStream s
211+ inputArr <- Array.fromStream s2
204212 let len = Array.length inputArr
205213 foundArr <-
206214 Stream.fold
@@ -254,18 +262,18 @@ joinOuterGeneric eq s1 s =
254262-- a flag. At the end go through @Stream m b@ and find those that are not in that
255263-- hash to return (Nothing, b).
256264
257−-- | Like 'joinOuter' but uses a 'Map' for efficiency.
265+-- | 'outerJoin' specialized to 'Ord' types for better performance.
258266--
259267-- Space: O(m + n)
260268--
261269-- Time: O(m + n)
262270--
263271-- /Pre-release/
264−{-# INLINE joinOuter #-}
265−joinOuter ::
272+{-# INLINE outerOrdJoin #-}
273+outerOrdJoin ::
266274 (Ord k, MonadIO m) =>
267275 Stream m (k, a) -> Stream m (k, b) -> Stream m (k, Maybe a, Maybe b)
268−joinOuter s1 s2 =
276+outerOrdJoin s1 s2 =
269277 Stream.concatEffect $ do
270278 km1 <- kvFold s1
271279 km2 <- kvFold s2
core/src/Streamly/Internal/Data/Stream/Eliminate.hs+33−25
@@ -13,36 +13,24 @@
1313--
1414module Streamly.Internal.Data.Stream.Eliminate
1515 (
16− -- * Running a 'Fold'
17− fold
18−
19− -- -- * Running a 'Parser'
20− , parse
16+ -- * Running a Parser
17+ parse
2118 , parseD
2219 , parseBreak
2320 , parseBreakD
2421
25− -- * Stream Deconstruction
22+ -- * Deconstruction
2623 , uncons
2724
2825 -- * Right Folds
29− , foldrM
30− , foldr
31− , foldrMx
3226 , foldr1
3327
34− -- * Left Folds
35− , foldlM'
36− , foldl'
37− , foldlMx'
38− , foldlx'
39−
4028 -- * Specific Fold Functions
41− , drain
4229 , mapM_ -- Map and Fold
4330 , null
4431 , head
4532 , headElse
33+ , init
4634 , tail
4735 , last
4836 , elem
@@ -60,16 +48,9 @@ module Streamly.Internal.Data.Stream.Eliminate
6048 , the
6149
6250 -- * To containers
63− , toList
6451 , toListRev
6552
6653 -- * Multi-Stream Folds
67− -- ** Comparisons
68− -- | These should probably be expressed using zipping operations.
69− , eqBy
70− , cmpBy
71−
72− -- ** Substreams
7354 -- | These should probably be expressed using parsers.
7455 , isPrefixOf
7556 , isInfixOf
@@ -90,7 +71,7 @@ import Foreign.Storable (Storable)
9071import GHC.Exts (SpecConstrAnnotation(..))
9172import GHC.Types (SPEC(..))
9273import Streamly.Internal.Data.Parser (ParseError(..))
93−import Streamly.Internal.Data.SVar.Type (defState)
74+import Streamly.Internal.Data.SVar.Type (adaptState, defState)
9475import Streamly.Internal.Data.Unbox (Unbox)
9576
9677import Streamly.Internal.Data.Maybe.Strict (Maybe'(..))
@@ -105,7 +86,7 @@ import qualified Streamly.Internal.Data.Stream.Transform as StreamD
10586
10687import Prelude hiding
10788 ( Foldable(..), all, any, head, last, lookup, mapM, mapM_
108− , notElem, splitAt, tail, (!!))
89+ , notElem, splitAt, init, tail, (!!))
10990import Data.Foldable (length)
11091import Streamly.Internal.Data.Stream.Type
11192
@@ -391,9 +372,35 @@ head = foldrM (\x _ -> return (Just x)) (return Nothing)
391372headElse :: Monad m => a -> Stream m a -> m a
392373headElse a = foldrM (\x _ -> return x) (return a)
393374
375+{-# INLINE_NORMAL init #-}
376+init :: Monad m => Stream m a -> m (Maybe (Stream m a))
377+init stream = do
378+ r <- uncons stream
379+ case r of
380+ Nothing -> return Nothing
381+ Just (h, Stream step1 state1) ->
382+ return $ Just $ Stream step (h, state1)
383+
384+ where
385+
386+ step gst (a, s1) = do
387+ res <- step1 (adaptState gst) s1
388+ return $
389+ case res of
390+ Yield x s -> Yield a (x, s)
391+ Skip s -> Skip (a, s)
392+ Stop -> Stop
393+
394+-- | Same as:
395+--
396+-- >>> tail = fmap (fmap snd) . Stream.uncons
397+--
394398-- Does not fuse, has the same performance as the StreamK version.
399+--
395400{-# INLINE_NORMAL tail #-}
396401tail :: Monad m => Stream m a -> m (Maybe (Stream m a))
402+tail = fmap (fmap snd) . uncons
403+{-
397404tail (UnStream step state) = go SPEC state
398405 where
399406 go !_ st = do
@@ -402,6 +409,7 @@ tail (UnStream step state) = go SPEC state
402409 Yield _ s -> return (Just $ Stream step s)
403410 Skip s -> go SPEC s
404411 Stop -> return Nothing
412+-}
405413
406414-- XXX will it fuse? need custom impl?
407415{-# INLINE_NORMAL last #-}
core/src/Streamly/Internal/Data/Stream/Exception.hs+9−6
@@ -9,17 +9,20 @@
99
1010module Streamly.Internal.Data.Stream.Exception
1111 (
12− gbracket_
13− , gbracket
14− , before
15− , afterUnsafe
12+ -- * Resources
13+ before
1614 , afterIO
15+ , afterUnsafe
16+ , finallyIO
17+ , finallyUnsafe
18+ , gbracket_
19+ , gbracket
1720 , bracketUnsafe
1821 , bracketIO3
1922 , bracketIO
23+
24+ -- * Exceptions
2025 , onException
21− , finallyUnsafe
22− , finallyIO
2326 , ghandle
2427 , handle
2528 )
core/src/Streamly/Internal/Data/Stream/Generate.hs−12
@@ -16,20 +16,13 @@ module Streamly.Internal.Data.Stream.Generate
1616 (
1717 -- * Primitives
1818 nil
19− , nilM
2019 , cons
21− , consM
22−
23− -- * From 'Unfold'
24− , unfold
2520
2621 -- * Unfolding
2722 , unfoldr
2823 , unfoldrM
2924
3025 -- * From Values
31− , fromPure
32− , fromEffect
3326 , repeat
3427 , repeatM
3528 , replicate
@@ -95,7 +88,6 @@ module Streamly.Internal.Data.Stream.Generate
9588 -- * From Containers
9689 -- | Transform an input structure into a stream.
9790
98− , fromList
9991 , fromListM
10092 , fromFoldable
10193 , fromFoldableM
@@ -104,10 +96,6 @@ module Streamly.Internal.Data.Stream.Generate
10496 , fromPtr
10597 , fromPtrN
10698 , fromByteStr#
107−
108− -- * Conversions
109− , fromStreamK
110− , toStreamK
11199 )
112100where
113101
core/src/Streamly/Internal/Data/Stream/Nesting.hs+10−64
@@ -55,11 +55,6 @@ module Streamly.Internal.Data.Stream.Nesting
5555 -- yields an element. A special case of unfoldRoundRobin.
5656 , roundRobin -- interleaveFair?/ParallelFair
5757
58− -- *** Zipping
59− -- | Zip corresponding elements of two streams.
60− , zipWith
61− , zipWithM
62−
6358 -- *** Merging
6459 -- | Interleave elements from two streams based on a condition.
6560 , mergeBy
@@ -76,17 +71,10 @@ module Streamly.Internal.Data.Stream.Nesting
7671 -- unfoldMany: Unfold m a b -> t m a -> t m b
7772 -- @
7873
79− -- *** ConcatMap
80− -- | Generate streams by mapping a stream generator on each element of an
81− -- input stream, append the resulting streams and flatten.
82− , concatMap
83− , concatMapM
84−
8574 -- *** ConcatUnfold
8675 -- | Generate streams by using an unfold on each element of an input
8776 -- stream, append the resulting streams and flatten. A special case of
8877 -- gintercalate.
89− , unfoldMany
9078 , ConcatUnfoldInterleaveState (..)
9179 , unfoldInterleave
9280 , unfoldRoundRobin
@@ -118,11 +106,8 @@ module Streamly.Internal.Data.Stream.Nesting
118106
119107 -- ** Folding
120108 -- | Apply folds on a stream.
121− , foldMany
122− , refoldMany
123109 , foldSequence
124110 , foldIterateM
125− , refoldIterateM
126111
127112 -- ** Parsing
128113 -- | Parsing is opposite to flattening. 'parseMany' is dual to concatMap or
@@ -139,7 +124,6 @@ module Streamly.Internal.Data.Stream.Nesting
139124
140125 -- ** Grouping
141126 -- | Group segments of a stream and fold. Special case of parsing.
142− , groupsOf
143127 , groupsBy
144128 , groupsWhile
145129 , groupsRollingBy
@@ -147,8 +131,8 @@ module Streamly.Internal.Data.Stream.Nesting
147131 -- ** Splitting
148132 -- | A special case of parsing.
149133 , wordsBy
150− , splitOnSeq
151− , splitOnSuffixSeq
134+ , splitOnSeq -- XXX splitOnSeg
135+ , splitOnSuffixSeq -- XXX splitOnSegSuffix, splitOnTrailer
152136
153137 -- XXX Implement these as folds or parsers instead.
154138 , splitOnSuffixSeqAny
@@ -157,9 +141,8 @@ module Streamly.Internal.Data.Stream.Nesting
157141
158142 -- * Transform (Nested Containers)
159143 -- | Opposite to compact in ArrayStream
160− , splitInnerBy
161− , splitInnerBySuffix
162− , intersectBySorted
144+ , splitInnerBy -- XXX innerSplitOn
145+ , splitInnerBySuffix -- XXX innerSplitOnSuffix
163146
164147 -- * Reduce By Streams
165148 , dropPrefix
@@ -609,49 +592,6 @@ mergeFstBy :: -- Monad m =>
609592mergeFstBy _f _m1 _m2 = undefined
610593 -- fromStreamK $ D.mergeFstBy f (toStreamD m1) (toStreamD m2)
611594
612−-------------------------------------------------------------------------------
613−-- Intersection of sorted streams
614−-------------------------------------------------------------------------------
615−
616−-- Assuming the streams are sorted in ascending order
617−{-# INLINE_NORMAL intersectBySorted #-}
618−intersectBySorted :: Monad m
619− => (a -> a -> Ordering) -> Stream m a -> Stream m a -> Stream m a
620−intersectBySorted cmp (Stream stepa ta) (Stream stepb tb) =
621− Stream step
622− ( ta -- left stream state
623− , tb -- right stream state
624− , Nothing -- left value
625− , Nothing -- right value
626− )
627−
628− where
629−
630− {-# INLINE_LATE step #-}
631− -- step 1, fetch the first value
632− step gst (sa, sb, Nothing, b) = do
633− r <- stepa gst sa
634− return $ case r of
635− Yield a sa' -> Skip (sa', sb, Just a, b) -- step 2/3
636− Skip sa' -> Skip (sa', sb, Nothing, b)
637− Stop -> Stop
638−
639− -- step 2, fetch the second value
640− step gst (sa, sb, a@(Just _), Nothing) = do
641− r <- stepb gst sb
642− return $ case r of
643− Yield b sb' -> Skip (sa, sb', a, Just b) -- step 3
644− Skip sb' -> Skip (sa, sb', a, Nothing)
645− Stop -> Stop
646−
647− -- step 3, compare the two values
648− step _ (sa, sb, Just a, Just b) = do
649− let res = cmp a b
650− return $ case res of
651− GT -> Skip (sa, sb, Just a, Nothing) -- step 2
652− LT -> Skip (sa, sb, Nothing, Just b) -- step 1
653− EQ -> Yield a (sa, sb, Nothing, Just b) -- step 1
654−
655595------------------------------------------------------------------------------
656596-- Combine N Streams - unfoldMany
657597------------------------------------------------------------------------------
@@ -3046,6 +2986,8 @@ splitInnerBySuffix isEmpty splitter joiner (Stream step1 state1) =
30462986--
30472987-- Space: @O(1)@
30482988--
2989+-- See also stripPrefix.
2990+--
30492991-- /Unimplemented/
30502992{-# INLINE dropPrefix #-}
30512993dropPrefix ::
@@ -3058,6 +3000,8 @@ dropPrefix = error "Not implemented yet!"
30583000--
30593001-- Space: @O(n)@ where n is the length of the infix.
30603002--
3003+-- See also stripInfix.
3004+--
30613005-- /Unimplemented/
30623006{-# INLINE dropInfix #-}
30633007dropInfix ::
@@ -3070,6 +3014,8 @@ dropInfix = error "Not implemented yet!"
30703014--
30713015-- Space: @O(n)@ where n is the length of the suffix.
30723016--
3017+-- See also stripSuffix.
3018+--
30733019-- /Unimplemented/
30743020{-# INLINE dropSuffix #-}
30753021dropSuffix ::
core/src/Streamly/Internal/Data/Stream/Top.hs+243−115
@@ -8,38 +8,73 @@
88-- Portability : GHC
99--
1010-- Top level module that can depend on all other lower level Stream modules.
11+--
12+-- Design notes:
13+--
14+-- The order of arguments in the join operations should ideally be opposite. It
15+-- should be such that the infinite stream is the last one. The transformation
16+-- should be on the last argument, so if you curry the functions with all other
17+-- arguments we get a @Stream -> Stream@ function. The first stream argument
18+-- may be considered as a config or modifier for the operation.
19+--
20+-- Benefit of changing the order is that we get a more intuitive Stream ->
21+-- Stream transformation after currying all other arguments. The inner loop
22+-- streams become arguments for the transformation, more like local modifiers
23+-- for the global outer stream as the last argument. Thus we can continue using
24+-- transformations on the outer stream in a composed pipeline. Otherwise we can
25+-- use flip to flip the order.
26+--
27+-- The fact that the inner stream can be used in the loop multiple times also
28+-- tells that this is not the real effectful stream, it is more like a pure
29+-- stream or an array. In fact we may consider using an Identity streams as
30+-- inner streams in which case these functions will not look nice.
31+--
32+-- Downsides:
33+--
34+-- * Maybe less intuitive to think about, because we usually think the first
35+-- stream as the outer loop and second as the inner.
36+-- * Zip and merge operations will continue using the opposite order.
37+-- * Need to change the order of cross, crossWith operations as well
38+-- * It will be inconsistent with Data.List. The functions cannot be used as
39+-- intuitive operators.
40+--
41+-- The choice is similar to concatMap vs bind. concatMap is pipeline
42+-- composition friendly but bind is user intuition friendly. Another option is
43+-- to have other functions with a different argument order e.g. flippedCross
44+-- instead of cross.
45+--
46+-- If we change the order we have to make sure that we have a consistent
47+-- convention for set-like and the cross join operations.
1148
1249module Streamly.Internal.Data.Stream.Top
1350 (
14− -- * Transformation
15− -- ** Sampling
51+ -- * Sampling
1652 -- | Value agnostic filtering.
1753 strideFromThen
1854
19− -- * Nesting
20− -- ** Set like operations
21− -- | These are not exactly set operations because streams are not
22− -- necessarily sets, they may have duplicated elements. These operations
23− -- are generic i.e. they work on streams of unconstrained types, therefore,
24− -- they have quadratic performance characterstics. For better performance
25− -- using Set structures see the Streamly.Internal.Data.Stream.Container
26− -- module.
27− , filterInStreamGenericBy
28− , deleteInStreamGenericBy
29− , unionWithStreamGenericBy
30−
31− -- ** Set like operations on sorted streams
32− , filterInStreamAscBy
33− , deleteInStreamAscBy
34− , unionWithStreamAscBy
35−
36− -- ** Join operations
37− , joinInnerGeneric
38−
39− -- * Joins on sorted stream
40− , joinInnerAscBy
41− , joinLeftAscBy
42− , joinOuterAscBy
55+ -- * Straight Joins
56+ -- | These are set-like operations but not exactly set operations because
57+ -- streams are not necessarily sets, they may have duplicated elements.
58+ -- These operations are generic i.e. they work on streams of unconstrained
59+ -- types, therefore, they have quadratic performance characterstics. For
60+ -- better performance using Set or Map structures see the
61+ -- Streamly.Internal.Data.Stream.Container module.
62+ , intersectBy
63+ , deleteFirstsBy
64+ , unionBy
65+
66+ -- Set like operations on sorted streams
67+ , sortedIntersectBy
68+ , sortedDeleteFirstsBy
69+ , sortedUnionBy
70+
71+ -- * Cross Joins
72+ , innerJoin
73+
74+ -- Joins on sorted stream
75+ , innerSortedJoin
76+ , leftSortedJoin
77+ , outerSortedJoin
4378 )
4479where
4580
@@ -48,7 +83,7 @@ where
4883import Control.Monad.IO.Class (MonadIO(..))
4984import Data.IORef (newIORef, readIORef, modifyIORef')
5085import Streamly.Internal.Data.Fold.Type (Fold)
51−import Streamly.Internal.Data.Stream.Type (Stream, cross)
86+import Streamly.Internal.Data.Stream.Type (Stream(..), Step(..), cross)
5287
5388import qualified Data.List as List
5489import qualified Streamly.Internal.Data.Fold as Fold
@@ -110,29 +145,40 @@ strideFromThen offset stride =
110145-- binary search if we have an Ord instance or Ordering returning function. The
111146-- time complexity would then become (m x log n).
112147
113−-- | Like 'cross' but emits only those tuples where @a == b@ using the
114−-- supplied equality predicate.
148+-- | Like 'cross' but emits only those tuples where @a == b@ using the supplied
149+-- equality predicate. This is essentially a @cross intersection@ of two
150+-- streams.
115151--
116152-- Definition:
117153--
118−-- >>> joinInnerGeneric eq s1 s2 = Stream.filter (\(a, b) -> a `eq` b) $ Stream.cross s1 s2
154+-- >>> innerJoin eq s1 s2 = Stream.filter (\(a, b) -> a `eq` b) $ Stream.cross s1 s2
155+--
156+-- The second (inner) stream must be finite. Moreover, it must be either pure
157+-- or capable of multiple evaluations. If not then the caller should cache it
158+-- in an 'Data.Array.Array', if the type does not have an 'Unbox' instance then
159+-- use the Generic 'Data.Array.Generic.Array'. Convert the array to stream
160+-- before calling this function. Caching may also improve performance if the
161+-- stream is expensive to evaluate.
119162--
120−-- You should almost always prefer @joinInnerOrd@ over 'joinInnerGeneric' if
121−-- possible. @joinInnerOrd@ is an order of magnitude faster but may take more
122−-- space for caching the second stream.
163+-- If you care about performance this function should be your last choice among
164+-- all inner joins. 'Streamly.Internal.Data.Unfold.innerJoin' is a much faster
165+-- fused alternative. 'innerSortedJoin' is a faster alternative when streams
166+-- are sorted. 'innerOrdJoin' is an order of magnitude faster alternative when
167+-- the type has an 'Ord' instance.
123168--
124−-- See 'Streamly.Internal.Data.Unfold.joinInnerGeneric' for a much faster fused
125−-- alternative.
169+-- Note: Conceptually, this is a commutative operation. Result includes all the
170+-- elements from the left and the right stream. The order of streams can be
171+-- changed without affecting results, except for the ordering within the tuple.
126172--
127173-- Time: O(m x n)
128174--
129175-- /Pre-release/
130−{-# INLINE joinInnerGeneric #-}
131−joinInnerGeneric :: Monad m =>
176+{-# INLINE innerJoin #-}
177+innerJoin :: Monad m =>
132178 (a -> b -> Bool) -> Stream m a -> Stream m b -> Stream m (a, b)
133−joinInnerGeneric eq s1 s2 = Stream.filter (\(a, b) -> a `eq` b) $ cross s1 s2
179+innerJoin eq s1 s2 = Stream.filter (\(a, b) -> a `eq` b) $ cross s1 s2
134180{-
135−joinInnerGeneric eq s1 s2 = do
181+innerJoin eq s1 s2 = do
136182 -- ConcatMap works faster than bind
137183 Stream.concatMap (\a ->
138184 Stream.concatMap (\b ->
@@ -143,44 +189,44 @@ joinInnerGeneric eq s1 s2 = do
143189 ) s1
144190-}
145191
146−-- | A more efficient 'joinInner' for sorted streams.
192+-- | A more efficient 'innerJoin' for sorted streams.
147193--
148194-- Space: O(1)
149195--
150196-- Time: O(m + n)
151197--
152198-- /Unimplemented/
153−{-# INLINE joinInnerAscBy #-}
154−joinInnerAscBy ::
199+{-# INLINE innerSortedJoin #-}
200+innerSortedJoin ::
155201 (a -> b -> Ordering) -> Stream m a -> Stream m b -> Stream m (a, b)
156−joinInnerAscBy = undefined
202+innerSortedJoin = undefined
157203
158−-- | A more efficient 'joinLeft' for sorted streams.
204+-- | A more efficient 'leftJoin' for sorted streams.
159205--
160206-- Space: O(1)
161207--
162208-- Time: O(m + n)
163209--
164210-- /Unimplemented/
165−{-# INLINE joinLeftAscBy #-}
166−joinLeftAscBy :: -- Monad m =>
211+{-# INLINE leftSortedJoin #-}
212+leftSortedJoin :: -- Monad m =>
167213 (a -> b -> Ordering) -> Stream m a -> Stream m b -> Stream m (a, Maybe b)
168−joinLeftAscBy _eq _s1 _s2 = undefined
214+leftSortedJoin _eq _s1 _s2 = undefined
169215
170−-- | A more efficient 'joinOuter' for sorted streams.
216+-- | A more efficient 'outerJoin' for sorted streams.
171217--
172218-- Space: O(1)
173219--
174220-- Time: O(m + n)
175221--
176222-- /Unimplemented/
177−{-# INLINE joinOuterAscBy #-}
178−joinOuterAscBy :: -- Monad m =>
223+{-# INLINE outerSortedJoin #-}
224+outerSortedJoin :: -- Monad m =>
179225 (a -> b -> Ordering)
180226 -> Stream m a
181227 -> Stream m b
182228 -> Stream m (Maybe a, Maybe b)
183−joinOuterAscBy _eq _s1 _s2 = undefined
229+outerSortedJoin _eq _s1 _s2 = undefined
184230
185231------------------------------------------------------------------------------
186232-- Set operations (special joins)
@@ -190,8 +236,8 @@ joinOuterAscBy _eq _s1 _s2 = undefined
190236-- the best would be to use an Array with linear search. If the second stream
191237-- is sorted we can also use a binary search, using Ord constraint.
192238
193−-- | Keep only those elements in the second stream that are present in the
194−-- first stream too. The first stream is folded to a container using the
239+-- | Keep only those elements in the first stream that are present in the
240+-- second stream too. The second stream is folded to a container using the
195241-- supplied fold and then the elements in the container are looked up using the
196242-- supplied lookup function.
197243--
@@ -206,23 +252,30 @@ filterStreamWith :: Monad m =>
206252filterStreamWith fld member s1 s2 =
207253 Stream.concatEffect
208254 $ do
209− xs <- Stream.fold fld s1
210− return $ Stream.filter (`member` xs) s2
255+ xs <- Stream.fold fld s2
256+ return $ Stream.filter (`member` xs) s1
211257
212−-- | 'filterInStreamGenericBy' retains only those elements in the second stream that
213−-- are present in the first stream.
214−--
215−-- >>> Stream.fold Fold.toList $ Stream.filterInStreamGenericBy (==) (Stream.fromList [1,2,2,4]) (Stream.fromList [2,1,1,3])
216−-- [2,1,1]
217−--
218−-- >>> Stream.fold Fold.toList $ Stream.filterInStreamGenericBy (==) (Stream.fromList [2,1,1,3]) (Stream.fromList [1,2,2,4])
219−-- [1,2,2]
258+-- XXX instead of folding the second stream to a list we could use it directly.
259+-- If the user wants they can generate the stream from an array and also call
260+-- uniq or nub on it. We can provide a convenience Stream -> Stream to cache
261+-- a finite stream in an array and serve it from the cache. The user can decide
262+-- what is best based on the context. They can also choose to use a boxed or
263+-- unboxed array for caching. To force caching we can make the second stream
264+-- monad type Identity. But that may be less flexible. One option is to use
265+-- cachedIntersectBy etc for automatic caching.
266+
267+-- | 'intersectBy' returns a subsequence of the first stream which intersects
268+-- with the second stream. Note that this is not a commutative operation unlike
269+-- a set intersection, because of duplicate elements in the stream the order of
270+-- the streams matters. This is similar to 'Data.List.intersectBy'. Note that
271+-- intersectBy is a special case of 'innerJoin'.
220272--
221−-- Similar to the list intersectBy operation but with the stream argument order
222−-- flipped.
273+-- >>> f s1 s2 = Stream.fold Fold.toList $ Stream.intersectBy (==) (Stream.fromList s1) (Stream.fromList s2)
274+-- >>> f [1,3,4,4,5]) [2,3,4,5,5]
275+-- [3,4,4,5]
223276--
224−-- The first stream must be finite and must not block. Second stream is
225−-- processed only after the first stream is fully realized.
277+-- First stream can be infinite, the second stream must be finite and must be
278+-- capable of multiple evaluations.
226279--
227280-- Space: O(n) where @n@ is the number of elements in the second stream.
228281--
@@ -230,46 +283,87 @@ filterStreamWith fld member s1 s2 =
230283-- @n@ is the number of elements in the second stream.
231284--
232285-- /Pre-release/
233−{-# INLINE filterInStreamGenericBy #-}
234−filterInStreamGenericBy :: Monad m =>
286+{-# INLINE intersectBy #-}
287+intersectBy :: Monad m =>
235288 (a -> a -> Bool) -> Stream m a -> Stream m a -> Stream m a
236−filterInStreamGenericBy eq =
289+intersectBy eq =
237290 -- XXX Use an (unboxed) array instead.
238291 filterStreamWith
239292 (Fold.scanMaybe (Fold.uniqBy eq) Fold.toListRev)
240293 (List.any . eq)
241294
242−-- | Like 'filterInStreamGenericBy' but assumes that the input streams are sorted in
295+-------------------------------------------------------------------------------
296+-- Intersection of sorted streams
297+-------------------------------------------------------------------------------
298+
299+-- XXX The sort order is not important as long both the streams have the same
300+-- sort order. We need to move only in one direction in each stream.
301+-- XXX Fix the argument order to use the same behavior as intersectBy.
302+
303+-- | Like 'intersectBy' but assumes that the input streams are sorted in
243304-- ascending order. To use it on streams sorted in descending order pass an
244305-- inverted comparison function returning GT for less than and LT for greater
245306-- than.
246307--
308+-- Both streams can be infinite.
309+--
247310-- Space: O(1)
248311--
249312-- Time: O(m+n)
250313--
251314-- /Pre-release/
252−{-# INLINE filterInStreamAscBy #-}
253−filterInStreamAscBy :: Monad m =>
315+{-# INLINE_NORMAL sortedIntersectBy #-}
316+sortedIntersectBy :: Monad m =>
254317 (a -> a -> Ordering) -> Stream m a -> Stream m a -> Stream m a
255−filterInStreamAscBy eq s1 s2 = Stream.intersectBySorted eq s2 s1
318+sortedIntersectBy cmp (Stream stepa ta) (Stream stepb tb) =
319+ Stream step
320+ ( ta -- left stream state
321+ , tb -- right stream state
322+ , Nothing -- left value
323+ , Nothing -- right value
324+ )
256325
257−-- | Delete all elements of the first stream from the seconds stream. If an
258−-- element occurs multiple times in the first stream as many occurrences of it
259−-- are deleted from the second stream.
260−--
261−-- >>> Stream.fold Fold.toList $ Stream.deleteInStreamGenericBy (==) (Stream.fromList [1,2,3]) (Stream.fromList [1,2,2])
262−-- [2]
326+ where
327+
328+ {-# INLINE_LATE step #-}
329+ -- step 1, fetch the first value
330+ step gst (sa, sb, Nothing, b) = do
331+ r <- stepa gst sa
332+ return $ case r of
333+ Yield a sa' -> Skip (sa', sb, Just a, b) -- step 2/3
334+ Skip sa' -> Skip (sa', sb, Nothing, b)
335+ Stop -> Stop
336+
337+ -- step 2, fetch the second value
338+ step gst (sa, sb, a@(Just _), Nothing) = do
339+ r <- stepb gst sb
340+ return $ case r of
341+ Yield b sb' -> Skip (sa, sb', a, Just b) -- step 3
342+ Skip sb' -> Skip (sa, sb', a, Nothing)
343+ Stop -> Stop
344+
345+ -- step 3, compare the two values
346+ step _ (sa, sb, Just a, Just b) = do
347+ let res = cmp a b
348+ return $ case res of
349+ GT -> Skip (sa, sb, Just a, Nothing) -- step 2
350+ LT -> Skip (sa, sb, Nothing, Just b) -- step 1
351+ EQ -> Yield a (sa, sb, Nothing, Just b) -- step 1
352+
353+-- | Returns a subsequence of the first stream, deleting first occurrences of
354+-- those elements that are present in the second stream. Note that this is not
355+-- a commutative operation. This is similar to the 'Data.List.deleteFirstsBy'.
263356--
264−-- The following laws hold:
357+-- >>> f xs ys = Stream.fold Fold.toList $ Stream.deleteFirstsBy (==) (Stream.fromList xs) (Stream.fromList ys)
358+-- >>> f [1,2,2,3,3,5] [1,2,2,3,4]
359+-- [2,3,5]
265360--
266−-- > deleteInStreamGenericBy (==) s1 (s1 `append` s2) === s2
267−-- > deleteInStreamGenericBy (==) s1 (s1 `interleave` s2) === s2
361+-- The following holds:
268362--
269−-- Same as the list 'Data.List.//' operation but with argument order flipped.
363+-- > deleteFirstsBy (==) (Stream.nub s2 `append` s1) s2 === s1
364+-- > deleteFirstsBy (==) (Stream.nub s2 `interleave` s1) s2 === s1
270365--
271−-- The first stream must be finite and must not block. Second stream is
272−-- processed only after the first stream is fully realized.
366+-- First stream can be infinite, second stream must be finite.
273367--
274368-- Space: O(m) where @m@ is the number of elements in the first stream.
275369--
@@ -277,58 +371,83 @@ filterInStreamAscBy eq s1 s2 = Stream.intersectBySorted eq s2 s1
277371-- @n@ is the number of elements in the second stream.
278372--
279373-- /Pre-release/
280−{-# INLINE deleteInStreamGenericBy #-}
281−deleteInStreamGenericBy :: Monad m =>
374+{-# INLINE deleteFirstsBy #-}
375+deleteFirstsBy :: Monad m =>
282376 (a -> a -> Bool) -> Stream m a -> Stream m a -> Stream m a
283−deleteInStreamGenericBy eq s1 s2 =
284− Stream.concatEffect
285− $ do
286− -- This may work well if s1 is small
287− -- If s1 is big we can go through s1, deleting elements from s2 and
288− -- not emitting an element if it was successfully deleted from s2.
289− -- we will need a deleteBy that can return whether the element was
290− -- deleted or not.
291− xs <- Stream.fold Fold.toList s2
292− let f = Fold.foldl' (flip (List.deleteBy eq)) xs
293− fmap Stream.fromList $ Stream.fold f s1
377+deleteFirstsBy eq s2 s1 =
378+ -- XXX s2 can be a sorted mutable array and we can use binary
379+ -- search to find. Mark the element deleted, count the deletions
380+ -- and reconsolidate the array when a min number of elements is
381+ -- deleted.
294382
295−-- | A more efficient 'deleteInStreamGenericBy' for streams sorted in ascending order.
383+ -- XXX Use StreamK or list as second argument instead of Stream to avoid
384+ -- concatEffect?
385+ Stream.concatEffect $ do
386+ xs <- Stream.toList s1
387+ -- It reverses the list but that is fine.
388+ let del x =
389+ List.foldl' (\(ys,res) y ->
390+ if x `eq` y
391+ then (ys, True)
392+ else (x:ys, res)) ([], False)
393+ g (ys,_) x =
394+ let (ys1, deleted) = del x ys
395+ in if deleted
396+ then (ys1, Nothing)
397+ else (ys1, Just x)
398+ in return
399+ $ Stream.catMaybes
400+ $ fmap snd
401+ $ Stream.postscanl' g (xs, Nothing) s2
402+
403+-- | A more efficient 'deleteFirstsBy' for streams sorted in ascending order.
404+--
405+-- Both streams can be infinite.
296406--
297407-- Space: O(1)
298408--
299409-- /Unimplemented/
300−{-# INLINE deleteInStreamAscBy #-}
301−deleteInStreamAscBy :: -- (Monad m) =>
410+{-# INLINE sortedDeleteFirstsBy #-}
411+sortedDeleteFirstsBy :: -- (Monad m) =>
302412 (a -> a -> Ordering) -> Stream m a -> Stream m a -> Stream m a
303−deleteInStreamAscBy _eq _s1 _s2 = undefined
413+sortedDeleteFirstsBy _eq _s1 _s2 = undefined
304414
305415-- XXX Remove the MonadIO constraint. We can just cache one stream and then
306416-- implement using differenceEqBy.
307417
308−-- | This essentially appends to the second stream all the occurrences of
309−-- elements in the first stream that are not already present in the second
310−-- stream.
418+-- | Returns the first stream appended with those unique elements from the
419+-- second stream that are not already present in the first stream. Note that
420+-- this is not a commutative operation unlike a set union, argument order
421+-- matters. The behavior is similar to the 'Data.List.unionBy'.
311422--
312423-- Equivalent to the following except that @s2@ is evaluated only once:
313424--
314−-- >>> unionWithStreamGenericBy eq s1 s2 = s2 `Stream.append` (Stream.deleteInStreamGenericBy eq s2 s1)
425+-- >>> unionBy eq s1 s2 = s1 `Stream.append` Stream.deleteFirstsBy eq s1 (Stream.nub s2)
315426--
316427-- Example:
317428--
318−-- >>> Stream.fold Fold.toList $ Stream.unionWithStreamGenericBy (==) (Stream.fromList [1,1,2,3]) (Stream.fromList [1,2,2,4])
429+-- >>> f s1 s2 = Stream.fold Fold.toList $ Stream.unionBy (==) (Stream.fromList s1) (Stream.fromList s2)
430+-- >>> f [1,2,2,4] [1,1,2,3,3]
319431-- [1,2,2,4,3]
320432--
433+-- First stream can be infinite, but second stream must be finite. Note that if
434+-- the first stream is infinite the union means just the first stream. Thus
435+-- union is useful only when both streams are finite. See 'sortedUnionBy' where
436+-- union can work on infinite streams if they are sorted.
437+--
321438-- Space: O(n)
322439--
323440-- Time: O(m x n)
324441--
325442-- /Pre-release/
326−{-# INLINE unionWithStreamGenericBy #-}
327−unionWithStreamGenericBy :: MonadIO m =>
443+{-# INLINE unionBy #-}
444+unionBy :: MonadIO m =>
328445 (a -> a -> Bool) -> Stream m a -> Stream m a -> Stream m a
329−unionWithStreamGenericBy eq s1 s2 =
446+unionBy eq s2 s1 =
330447 Stream.concatEffect
331448 $ do
449+ -- XXX use a rewrite rule such that if a list converted to stream
450+ -- is passed to unionBy then this becomes an identity operation.
332451 xs <- Stream.fold Fold.toList s1
333452 -- XXX we can use postscanlMAfter' instead of IORef
334453 ref <- liftIO $ newIORef $! List.nubBy eq xs
@@ -341,12 +460,21 @@ unionWithStreamGenericBy eq s1 s2 =
341460 return $ Stream.fromList xs1
342461 return $ Stream.mapM f s2 `Stream.append` s3
343462
344−-- | A more efficient 'unionWithStreamGenericBy' for sorted streams.
463+-- | A more efficient 'unionBy' for sorted streams.
464+--
465+-- Note that the behavior is different from 'unionBy'. In 'unionBy' we append
466+-- the unique elements from second stream only after exhausting the first one
467+-- whereas in sorted streams we can determine unique elements early even when
468+-- we are going through the first stream. Thus the result is an interleaving of
469+-- the two streams, merging those elements from the second stream that are not
470+-- present in the first.
345471--
346472-- Space: O(1)
347473--
474+-- Both streams can be infinite.
475+--
348476-- /Unimplemented/
349−{-# INLINE unionWithStreamAscBy #-}
350−unionWithStreamAscBy :: -- (Monad m) =>
477+{-# INLINE sortedUnionBy #-}
478+sortedUnionBy :: -- (Monad m) =>
351479 (a -> a -> Ordering) -> Stream m a -> Stream m a -> Stream m a
352−unionWithStreamAscBy _eq _s1 _s2 = undefined
480+sortedUnionBy _eq _s1 _s2 = undefined
core/src/Streamly/Internal/Data/Stream/Transform.hs+53−27
@@ -21,8 +21,6 @@ module Streamly.Internal.Data.Stream.Transform
2121
2222 -- * Mapping
2323 -- | Stateless one-to-one maps.
24− , map
25− , mapM
2624 , sequence
2725
2826 -- * Mapping Effects
@@ -75,7 +73,7 @@ module Streamly.Internal.Data.Stream.Transform
7573 , scanMaybe
7674 , filter
7775 , filterM
78− , deleteBy
76+ , deleteBy -- deleteOnceBy?
7977 , uniqBy
8078 , uniq
8179 , prune
@@ -83,9 +81,8 @@ module Streamly.Internal.Data.Stream.Transform
8381
8482 -- * Trimming
8583 -- | Produce a subset of the stream trimmed at ends.
86− , take
87− , takeWhile
88− , takeWhileM
84+ , initNonEmpty
85+ , tailNonEmpty
8986 , takeWhileLast
9087 , takeWhileAround
9188 , drop
@@ -132,7 +129,6 @@ module Streamly.Internal.Data.Stream.Transform
132129 -- * Searching
133130 , findIndices
134131 , elemIndices
135− , slicesBy
136132
137133 -- * Rolling map
138134 -- | Map using the previous element.
@@ -1074,6 +1070,55 @@ repeated = undefined
10741070-- Trimming
10751071------------------------------------------------------------------------------
10761072
1073+-- | init for non-empty streams, fails for empty stream case.
1074+--
1075+{-# INLINE initNonEmpty #-}
1076+initNonEmpty :: Monad m => Stream m a -> Stream m a
1077+initNonEmpty (Stream step1 state1) = Stream step (Nothing, state1)
1078+
1079+ where
1080+
1081+ step gst (Nothing, s1) = do
1082+ r <- step1 (adaptState gst) s1
1083+ return $
1084+ case r of
1085+ Yield x s -> Skip (Just x, s)
1086+ Skip s -> Skip (Nothing, s)
1087+ Stop -> error "initNonEmpty: empty Stream"
1088+
1089+ step gst (Just a, s1) = do
1090+ r <- step1 (adaptState gst) s1
1091+ return $
1092+ case r of
1093+ Yield x s -> Yield a (Just x, s)
1094+ Skip s -> Skip (Just a, s)
1095+ Stop -> Stop
1096+
1097+-- | tail for non-empty streams, fails for empty stream case.
1098+--
1099+-- See also 'tail' for a non-partial version of this function..
1100+{-# INLINE tailNonEmpty #-}
1101+tailNonEmpty :: Monad m => Stream m a -> Stream m a
1102+tailNonEmpty (Stream step1 state1) = Stream step (Nothing, state1)
1103+
1104+ where
1105+
1106+ step gst (Nothing, s1) = do
1107+ r <- step1 (adaptState gst) s1
1108+ return $
1109+ case r of
1110+ Yield x s -> Skip (Just x, s)
1111+ Skip s -> Skip (Nothing, s)
1112+ Stop -> error "tailNonEmpty: empty Stream"
1113+
1114+ step gst (Just a, s1) = do
1115+ r <- step1 (adaptState gst) s1
1116+ return $
1117+ case r of
1118+ Yield x s -> Yield x (Just x, s)
1119+ Skip s -> Skip (Just a, s)
1120+ Stop -> Stop
1121+
10771122-- | Take all consecutive elements at the end of the stream for which the
10781123-- predicate is true.
10791124--
@@ -1537,7 +1582,7 @@ reverse m = Stream step Nothing
15371582{-# INLINE reverseUnbox #-}
15381583reverseUnbox :: (MonadIO m, Unbox a) => Stream m a -> Stream m a
15391584reverseUnbox =
1540− A.concatChunksRev -- unfoldMany A.readerRev
1585+ A.concatRev -- unfoldMany A.readerRev
15411586 . fromStreamK
15421587 . K.reverse
15431588 . toStreamK
@@ -1709,25 +1754,6 @@ findIndices p (Stream step state) = Stream step' (state, 0)
17091754elemIndices :: (Monad m, Eq a) => a -> Stream m a -> Stream m Int
17101755elemIndices a = findIndices (== a)
17111756
1712−{-# INLINE_NORMAL slicesBy #-}
1713−slicesBy :: Monad m => (a -> Bool) -> Stream m a -> Stream m (Int, Int)
1714−slicesBy p (Stream step1 state1) = Stream step (Just (state1, 0, 0))
1715−
1716− where
1717−
1718− {-# INLINE_LATE step #-}
1719− step gst (Just (st, i, len)) = i `seq` len `seq` do
1720− r <- step1 (adaptState gst) st
1721− return
1722− $ case r of
1723− Yield x s ->
1724− if p x
1725− then Yield (i, len + 1) (Just (s, i + len + 1, 0))
1726− else Skip (Just (s, i, len + 1))
1727− Skip s -> Skip (Just (s, i, len))
1728− Stop -> if len == 0 then Stop else Yield (i, len) Nothing
1729− step _ Nothing = return Stop
1730−
17311757------------------------------------------------------------------------------
17321758-- Rolling map
17331759------------------------------------------------------------------------------
core/src/Streamly/Internal/Data/Stream/Transformer.hs+2−1
@@ -11,10 +11,11 @@
1111
1212module Streamly.Internal.Data.Stream.Transformer
1313 (
14+ -- * Fold to Transformer Monad
1415 foldlT
1516 , foldrT
1617
17− -- * Transform Inner Monad
18+ -- * Inner Monad Operations
1819 , liftInner
1920 , runReaderT
2021 , usingReaderT
core/src/Streamly/Internal/Data/Stream/Type.hs+37−3
@@ -1,5 +1,11 @@
11{-# LANGUAGE CPP #-}
2+{-# LANGUAGE PatternSynonyms #-}
3+{-# LANGUAGE TypeFamilies #-}
4+-- Must come after TypeFamilies, otherwise it is re-enabled.
5+-- MonoLocalBinds enabled by TypeFamilies causes perf regressions in general.
6+{-# LANGUAGE NoMonoLocalBinds #-}
27{-# LANGUAGE UndecidableInstances #-}
8+{-# LANGUAGE ViewPatterns #-}
39
410-- |
511-- Module : Streamly.Internal.Data.Stream.Type
@@ -16,18 +22,18 @@
1622
1723module Streamly.Internal.Data.Stream.Type
1824 (
19− -- * The stream type
25+ -- * Type
2026 Step (..)
2127 -- XXX UnStream is exported to avoid a performance issue in some
2228 -- combinators if we use the pattern synonym "Stream".
2329 , Stream (Stream, UnStream)
2430
25− -- * CrossStream type wrapper
31+ -- * CrossStream
2632 , CrossStream
2733 , unCross
2834 , mkCross
2935
30− -- * Conversion to StreamK
36+ -- * To StreamK
3137 , fromStreamK
3238 , toStreamK
3339
@@ -85,6 +91,7 @@ module Streamly.Internal.Data.Stream.Type
8591
8692 -- * Combining Two Streams
8793 -- ** Zipping
94+ -- | Zip corresponding elements of two streams.
8895 , zipWithM
8996 , zipWith
9097
@@ -100,6 +107,8 @@ module Streamly.Internal.Data.Stream.Type
100107 , unfoldMany
101108
102109 -- * Concat
110+ -- | Generate streams by mapping a stream generator on each element of an
111+ -- input stream, append the resulting streams and flatten.
103112 , concatEffect
104113 , concatMap
105114 , concatMapM
@@ -133,6 +142,7 @@ module Streamly.Internal.Data.Stream.Type
133142 , indexOnSuffix
134143
135144 -- * Multi-stream folds
145+ -- | These should probably be expressed using zipping operations.
136146 , eqBy
137147 , cmpBy
138148
@@ -562,8 +572,12 @@ foldAdd f =
562572-- >>> Stream.foldrM step (return False) s
563573-- True
564574--
575+-- >>> import Control.Monad (join)
576+-- >>> foldrM f z = join $ Stream.foldr f z
577+--
565578{-# INLINE_NORMAL foldrM #-}
566579foldrM :: Monad m => (a -> m b -> m b) -> m b -> Stream m a -> m b
580+-- foldrM f z = join . Streamly.Internal.Data.Stream.StreamD.Type.foldr f z
567581foldrM f z (Stream step state) = go SPEC state
568582 where
569583 {-# INLINE_LATE go #-}
@@ -2062,6 +2076,26 @@ indexOnSuffix predicate =
20622076 (indexerBy (FL.takeEndBy_ predicate FL.length) 1)
20632077 (return (-1, 0))
20642078
2079+-- Alternate implementation
2080+{-# INLINE_NORMAL _indexOnSuffix #-}
2081+_indexOnSuffix :: Monad m => (a -> Bool) -> Stream m a -> Stream m (Int, Int)
2082+_indexOnSuffix p (Stream step1 state1) = Stream step (Just (state1, 0, 0))
2083+
2084+ where
2085+
2086+ {-# INLINE_LATE step #-}
2087+ step gst (Just (st, i, len)) = i `seq` len `seq` do
2088+ r <- step1 (adaptState gst) st
2089+ return
2090+ $ case r of
2091+ Yield x s ->
2092+ if p x
2093+ then Yield (i, len + 1) (Just (s, i + len + 1, 0))
2094+ else Skip (Just (s, i, len + 1))
2095+ Skip s -> Skip (Just (s, i, len))
2096+ Stop -> if len == 0 then Stop else Yield (i, len) Nothing
2097+ step _ Nothing = return Stop
2098+
20652099{-# DEPRECATED sliceOnSuffix "Please use indexOnSuffix instead." #-}
20662100sliceOnSuffix :: Monad m => (a -> Bool) -> Stream m a -> Stream m (Int, Int)
20672101sliceOnSuffix = indexOnSuffix
core/src/Streamly/Internal/Data/StreamK.hs+19−8
@@ -16,16 +16,15 @@ module Streamly.Internal.Data.StreamK
1616 --
1717 -- $setup
1818
19− -- * The stream type
2019 module Streamly.Internal.Data.StreamK.Type
20+ -- * Transformer
2121 , module Streamly.Internal.Data.StreamK.Transformer
2222
23− , StreamK(..)
23+ -- * From containers
2424 , fromList
2525 , fromStream
26− , toStream
2726
28− -- ** Specialized Generation
27+ -- * Specialized Generation
2928 , repeatM
3029 , replicate
3130 , replicateM
@@ -67,13 +66,13 @@ module Streamly.Internal.Data.StreamK
6766 , find
6867 , (!!)
6968
69+ -- ** To Containers
70+ , toList
71+ , toStream
72+
7073 -- ** Map and Fold
7174 , mapM_
7275
73− -- ** Conversions
74− , toList
75− , hoist
76−
7776 -- * Transformation
7877 -- ** By folding (scans)
7978 , scanl'
@@ -100,6 +99,7 @@ module Streamly.Internal.Data.StreamK
10099
101100 -- ** Reordering
102101 , sortBy
102+ , sortOn
103103
104104 -- ** Map and Filter
105105 , mapMaybe
@@ -115,6 +115,9 @@ module Streamly.Internal.Data.StreamK
115115 -- ** Transformation comprehensions
116116 , the
117117
118+ -- ** Transforming Inner Monad
119+ , hoist
120+
118121 -- * Exceptions
119122 , handle
120123
@@ -131,6 +134,7 @@ import Control.Exception (mask_, Exception)
131134import Control.Monad (void, join)
132135import Control.Monad.Catch (MonadCatch)
133136import Control.Monad.IO.Class (MonadIO(..))
137+import Data.Ord (comparing)
134138import Data.Proxy (Proxy(..))
135139import GHC.Types (SPEC(..))
136140import Streamly.Internal.Data.Array.Type (Array(..))
@@ -1717,3 +1721,10 @@ sortBy cmp =
17171721 . Stream.catRights -- its a non-failing backtracking parser
17181722 . Stream.parseMany (fmap (either id id) p)
17191723 . Stream.fromStreamK
1724+
1725+{-# INLINE sortOn #-}
1726+sortOn :: (Monad m, Ord b) => (a -> b) -> StreamK m a -> StreamK m a
1727+sortOn f =
1728+ fmap snd
1729+ . sortBy (comparing fst)
1730+ . fmap (\x -> let y = f x in y `seq` (y, x))
core/src/Streamly/Internal/Data/StreamK/Type.hs+60−11
@@ -1,4 +1,8 @@
11{-# LANGUAGE CPP #-}
2+{-# LANGUAGE TypeFamilies #-}
3+-- Must come after TypeFamilies, otherwise it is re-enabled.
4+-- MonoLocalBinds enabled by TypeFamilies causes perf regressions in general.
5+{-# LANGUAGE NoMonoLocalBinds #-}
26{-# LANGUAGE UndecidableInstances #-}
37-- |
48-- Module : Streamly.Internal.Data.StreamK.Type
@@ -93,8 +97,11 @@ module Streamly.Internal.Data.StreamK.Type
9397 -- ** Specific Folds
9498 , drain
9599 , null
100+ , headNonEmpty
96101 , tail
102+ , tailNonEmpty
97103 , init
104+ , initNonEmpty
98105
99106 -- * Mapping
100107 , map
@@ -1807,14 +1814,42 @@ iterateMWith cns step = go
18071814 !next <- s
18081815 foldStreamShared st stp sng yld (return next `cns` go (step next))
18091816
1810−{-# INLINE headPartial #-}
1811−headPartial :: Monad m => StreamK m a -> m a
1812−headPartial = foldrM (\x _ -> return x) (error "head of nil")
1817+-- | head for non-empty streams, fails for empty stream case.
1818+--
1819+{-# INLINE headNonEmpty #-}
1820+headNonEmpty :: Monad m => StreamK m a -> m a
1821+headNonEmpty = foldrM (\x _ -> return x) (error "headNonEmpty: empty stream")
1822+
1823+-- | init for non-empty streams, fails for empty stream case.
1824+--
1825+-- See also 'init' for a non-partial version of this function..
1826+{-# INLINE initNonEmpty #-}
1827+initNonEmpty :: Stream m a -> Stream m a
1828+initNonEmpty = go0
1829+
1830+ where
1831+
1832+ go0 m = mkStream $ \st yld sng stp ->
1833+ let stop = error "initNonEmpty: Empty Stream."
1834+ single _ = stp
1835+ yieldk a r = foldStream st yld sng stp (go1 a r)
1836+ in foldStream st yieldk single stop m
1837+
1838+ go1 a r = mkStream $ \st yld sng stp ->
1839+ let stop = stp
1840+ single _ = sng a
1841+ yieldk a1 r1 = yld a (go1 a1 r1)
1842+ in foldStream st yieldk single stop r
18131843
1814−{-# INLINE tailPartial #-}
1815−tailPartial :: StreamK m a -> StreamK m a
1816−tailPartial m = mkStream $ \st yld sng stp ->
1817− let stop = error "tail of nil"
1844+-- | tail for non-empty streams, fails for empty stream case.
1845+--
1846+-- See also 'tail' for a non-partial version of this function..
1847+--
1848+-- Note: this is same as "drop 1" with error on empty stream.
1849+{-# INLINE tailNonEmpty #-}
1850+tailNonEmpty :: StreamK m a -> StreamK m a
1851+tailNonEmpty m = mkStream $ \st yld sng stp ->
1852+ let stop = error "tailNonEmpty: empty stream"
18181853 single _ = stp
18191854 yieldk _ r = foldStream st yld sng stp r
18201855 in foldStream st yieldk single stop m
@@ -1879,10 +1914,10 @@ mfix f = mkStream $ \st yld sng stp ->
18791914 where
18801915
18811916 -- fix the head element of the stream
1882− xs = fix (f . headPartial)
1917+ xs = fix (f . headNonEmpty)
18831918
18841919 -- now fix the tail recursively
1885− ys = mfix (tailPartial . f)
1920+ ys = mfix (tailNonEmpty . f)
18861921
18871922-------------------------------------------------------------------------------
18881923-- Conversions
@@ -1913,6 +1948,15 @@ uncons m =
19131948 yieldk a r = pure (Just (a, r))
19141949 in foldStream defState yieldk single stop m
19151950
1951+-- Note that this is not a StreamK -> StreamK because then we cannot handle the
1952+-- empty stream case without making this a partial function.
1953+--
1954+-- See tailNonEmpty as well above.
1955+
1956+-- | Same as:
1957+--
1958+-- >>> tail = fmap (fmap snd) StreamK.uncons
1959+--
19161960{-# INLINE tail #-}
19171961tail :: Applicative m => StreamK m a -> m (Maybe (StreamK m a))
19181962tail =
@@ -1921,9 +1965,14 @@ tail =
19211965 yieldk _ r = pure $ Just r
19221966 in foldStream defState yieldk single stop
19231967
1924−-- | Extract all but the last element of the stream, if any.
1968+-- Note that this is not a StreamK -> StreamK because then we cannot handle the
1969+-- empty stream case without making this a partial function.
19251970--
1926−-- Note: This will end up buffering the entire stream.
1971+-- XXX How do we implement unsnoc? Make StreamK a monad and return the
1972+-- remaining stream as a result value in the monad?
1973+
1974+-- | Extract all but the last element of the stream, if any. This will end up
1975+-- evaluating the last element as well to find out that it is last.
19271976--
19281977-- /Pre-release/
19291978{-# INLINE init #-}
core/src/Streamly/Internal/Data/Time/Units.hs−4
@@ -1,7 +1,3 @@
1−{-# LANGUAGE DataKinds #-}
2−{-# LANGUAGE PolyKinds #-}
3−{-# LANGUAGE UnboxedTuples #-}
4−
51-- |
62-- Module : Streamly.Internal.Data.Time.Units
73-- Copyright : (c) 2019 Composewell Technologies
core/src/Streamly/Internal/Data/Unbox.hs+4−1
@@ -1,5 +1,8 @@
11{-# LANGUAGE DataKinds #-}
2−{-# LANGUAGE DefaultSignatures #-}
2+{-# LANGUAGE TypeFamilies #-}
3+-- Must come after TypeFamilies, otherwise it is re-enabled.
4+-- MonoLocalBinds enabled by TypeFamilies causes perf regressions in general.
5+{-# LANGUAGE NoMonoLocalBinds #-}
36{-# LANGUAGE UnboxedTuples #-}
47{-# LANGUAGE UndecidableInstances #-}
58
core/src/Streamly/Internal/Data/Unfold.hs+6−4
@@ -82,7 +82,7 @@ module Streamly.Internal.Data.Unfold
8282 , dropWhileM
8383
8484 -- ** Cross product
85− , joinInnerGeneric
85+ , innerJoin
8686
8787 -- ** Resource Management
8888 -- | 'bracket' is the most general resource management operation, all other
@@ -677,10 +677,12 @@ dropWhileM f (Unfold step inject) = Unfold step' inject'
677677dropWhile :: Monad m => (b -> Bool) -> Unfold m a b -> Unfold m a b
678678dropWhile f = dropWhileM (return . f)
679679
680−{-# INLINE_NORMAL joinInnerGeneric #-}
681−joinInnerGeneric :: Monad m =>
680+-- | Cross intersection of two unfolds. See
681+-- 'Streamly.Internal.Data.Stream.innerJoin' for more details.
682+{-# INLINE_NORMAL innerJoin #-}
683+innerJoin :: Monad m =>
682684 (b -> c -> Bool) -> Unfold m a b -> Unfold m a c -> Unfold m a (b, c)
683−joinInnerGeneric eq s1 s2 = filter (\(a, b) -> a `eq` b) $ cross s1 s2
685+innerJoin eq s1 s2 = filter (\(a, b) -> a `eq` b) $ cross s1 s2
684686
685687------------------------------------------------------------------------------
686688-- Exceptions
core/src/Streamly/Internal/FileSystem/File.hs+4−9
@@ -103,15 +103,14 @@ import qualified Control.Monad.Catch as MC
103103import qualified System.IO as SIO
104104
105105import Streamly.Data.Fold (groupsOf, drain)
106−import Streamly.Internal.Data.Array.Type (Array(..), pinnedWriteNUnsafe)
106+import Streamly.Internal.Data.Array.Type (Array(..))
107107import Streamly.Internal.Data.Fold.Type (Fold(..))
108108import Streamly.Data.Stream (Stream)
109−import Streamly.Internal.Data.Unbox (Unbox)
110109import Streamly.Internal.Data.Unfold.Type (Unfold(..))
111110-- import Streamly.String (encodeUtf8, decodeUtf8, foldLines)
112111import Streamly.Internal.System.IO (defaultChunkSize)
113112
114−import qualified Streamly.Data.Array as A
113+import qualified Streamly.Internal.Data.Array as A
115114import qualified Streamly.Data.Stream as S
116115import qualified Streamly.Data.Unfold as UF
117116import qualified Streamly.Internal.Data.Array.Type as IA (pinnedChunksOf)
@@ -339,10 +338,6 @@ readWithBufferOf = readerWith
339338reader :: (MonadIO m, MonadCatch m) => Unfold m FilePath Word8
340339reader = UF.many A.reader (usingFile FH.chunkReader)
341340
342−{-# INLINE concatChunks #-}
343−concatChunks :: (Monad m, Unbox a) => Stream m (Array a) -> Stream m a
344−concatChunks = S.unfoldMany A.reader
345−
346341-- | Generate a stream of bytes from a file specified by path. The stream ends
347342-- when EOF is encountered. File is locked using multiple reader and single
348343-- writer locking mode.
@@ -351,7 +346,7 @@ concatChunks = S.unfoldMany A.reader
351346--
352347{-# INLINE read #-}
353348read :: (MonadIO m, MonadCatch m) => FilePath -> Stream m Word8
354−read file = concatChunks $ withFile file ReadMode FH.readChunks
349+read file = A.concat $ withFile file ReadMode FH.readChunks
355350
356351{-# DEPRECATED toBytes "Please use 'read' instead" #-}
357352{-# INLINE toBytes #-}
@@ -467,7 +462,7 @@ writeChunks path = Fold step initial extract final
467462writeWith :: (MonadIO m, MonadCatch m)
468463 => Int -> FilePath -> Fold m Word8 ()
469464writeWith n path =
470− groupsOf n (pinnedWriteNUnsafe n) (writeChunks path)
465+ groupsOf n (A.unsafePinnedCreateOf n) (writeChunks path)
471466
472467{-# DEPRECATED writeWithBufferOf "Please use 'writeWith' instead" #-}
473468{-# INLINE writeWithBufferOf #-}
core/src/Streamly/Internal/FileSystem/Handle.hs+21−28
@@ -40,8 +40,8 @@ module Streamly.Internal.FileSystem.Handle
4040
4141 -- * Streams
4242 , read
43− , readWith
44− , readChunksWith
43+ , readWith -- readConcatChunksOf
44+ , readChunksWith -- readChunksOf
4545 , readChunks
4646
4747 -- * Unfolds
@@ -59,10 +59,10 @@ module Streamly.Internal.FileSystem.Handle
5959 -- , writeUtf8ByLines
6060 -- , writeByFrames
6161 -- , writeLines
62− , writeWith
62+ , writeWith -- writeChunksOf
6363 , writeChunks
64− , writeChunksWith
65− , writeMaybesWith
64+ , writeChunksWith -- writeCompactChunksOf
65+ , writeMaybesWith -- writeCompactJustsOf
6666
6767 -- * Refolds
6868 , writer
@@ -131,16 +131,13 @@ import Streamly.Internal.Data.Fold (Fold)
131131import Streamly.Internal.Data.Refold.Type (Refold(..))
132132import Streamly.Internal.Data.Unfold.Type (Unfold(..))
133133import Streamly.Internal.Data.Array.Type
134− (Array(..), pinnedWriteNUnsafe, unsafeFreezeWithShrink, byteLength)
134+ (Array(..), unsafeFreezeWithShrink, byteLength)
135135import Streamly.Internal.Data.Stream.Type (Stream)
136−import Streamly.Internal.Data.Array.Stream (lpackArraysChunksOf)
137136-- import Streamly.String (encodeUtf8, decodeUtf8, foldLines)
138137import Streamly.Internal.System.IO (defaultChunkSize)
139138
140139import qualified Streamly.Data.Fold as FL
141−import qualified Streamly.Data.Array as A
142−import qualified Streamly.Internal.Data.Array.Type as A
143−import qualified Streamly.Internal.Data.Array.Stream as AS
140+import qualified Streamly.Internal.Data.Array as A
144141import qualified Streamly.Internal.Data.MutArray.Type as MArray
145142import qualified Streamly.Internal.Data.Refold.Type as Refold
146143import qualified Streamly.Internal.Data.Fold.Type as FL(refoldMany)
@@ -169,6 +166,8 @@ import qualified Streamly.Internal.Data.StreamK.Type as K (mkStream)
169166-- Array IO (Input)
170167-------------------------------------------------------------------------------
171168
169+-- XXX add an API that compacts the arrays to an exact size.
170+
172171-- | Read a 'ByteArray' consisting of one or more bytes from a file handle. If
173172-- no data is available on the handle it blocks until at least one byte becomes
174173-- available. If any data is available then it immediately returns that data
@@ -178,9 +177,9 @@ import qualified Streamly.Internal.Data.StreamK.Type as K (mkStream)
178177{-# INLINABLE getChunk #-}
179178getChunk :: MonadIO m => Int -> Handle -> m (Array Word8)
180179getChunk size h = liftIO $ do
181− arr <- MArray.pinnedNewBytes size
180+ arr :: MArray.MutArray Word8 <- MArray.pinnedEmptyOf size
182181 -- ptr <- mallocPlainForeignPtrAlignedBytes size (alignment (undefined :: Word8))
183− MArray.asPtrUnsafe arr $ \p -> do
182+ MArray.unsafePinnedAsPtr arr $ \p -> do
184183 n <- hGetBufSome h p size
185184 -- XXX shrink only if the diff is significant
186185 return $
@@ -338,10 +337,6 @@ readerWith = UF.many A.reader chunkReaderWith
338337readWithBufferOf :: MonadIO m => Unfold m (Int, Handle) Word8
339338readWithBufferOf = readerWith
340339
341−{-# INLINE concatChunks #-}
342−concatChunks :: (Monad m, Unbox a) => Stream m (Array a) -> Stream m a
343−concatChunks = S.unfoldMany A.reader
344−
345340-- | @readWith bufsize handle@ reads a byte stream from a file
346341-- handle, reads are performed in chunks of up to @bufsize@.
347342--
@@ -350,7 +345,7 @@ concatChunks = S.unfoldMany A.reader
350345-- /Pre-release/
351346{-# INLINE readWith #-}
352347readWith :: MonadIO m => Int -> Handle -> Stream m Word8
353−readWith size h = concatChunks $ readChunksWith size h
348+readWith size h = A.concat $ readChunksWith size h
354349
355350-- | Unfolds a file handle into a byte stream. IO requests to the device are
356351-- performed in sizes of
@@ -369,7 +364,7 @@ reader = UF.many A.reader chunkReader
369364-- /Pre-release/
370365{-# INLINE read #-}
371366read :: MonadIO m => Handle -> Stream m Word8
372−read = concatChunks . readChunks
367+read = A.concat . readChunks
373368
374369-------------------------------------------------------------------------------
375370-- Writing
@@ -384,12 +379,12 @@ read = concatChunks . readChunks
384379{-# INLINABLE putChunk #-}
385380putChunk :: MonadIO m => Handle -> Array a -> m ()
386381putChunk _ arr | byteLength arr == 0 = return ()
387−putChunk h arr = A.asPtrUnsafe arr $ \ptr ->
382+putChunk h arr = A.unsafePinnedAsPtr arr $ \ptr ->
388383 liftIO $ hPutBuf h ptr aLen
389384
390385 where
391386
392− -- XXX We should have the length passed by asPtrUnsafe itself.
387+ -- XXX We should have the length passed by unsafePinnedAsPtr itself.
393388 aLen = A.byteLength arr
394389
395390-------------------------------------------------------------------------------
@@ -421,7 +416,7 @@ putChunks h = S.fold (FL.drainMapM (putChunk h))
421416{-# INLINE putChunksWith #-}
422417putChunksWith :: (MonadIO m, Unbox a)
423418 => Int -> Handle -> Stream m (Array a) -> m ()
424−putChunksWith n h xs = putChunks h $ AS.compact n xs
419+putChunksWith n h xs = putChunks h $ A.compactLE n xs
425420
426421-- > putBytesWith n h m = Handle.putChunks h $ A.pinnedChunksOf n m
427422
@@ -461,8 +456,6 @@ writeChunks h = FL.drainMapM (putChunk h)
461456chunkWriter :: MonadIO m => Refold m Handle (Array a) ()
462457chunkWriter = Refold.drainBy putChunk
463458
464−-- XXX lpackArraysChunksOf should be written idiomatically
465−
466459-- | @writeChunksWith bufsize handle@ writes a stream of arrays
467460-- to @handle@ after coalescing the adjacent arrays in chunks of @bufsize@.
468461-- We never split an array, if a single array is bigger than the specified size
@@ -472,7 +465,7 @@ chunkWriter = Refold.drainBy putChunk
472465{-# INLINE writeChunksWith #-}
473466writeChunksWith :: (MonadIO m, Unbox a)
474467 => Int -> Handle -> Fold m (Array a) ()
475−writeChunksWith n h = lpackArraysChunksOf n (writeChunks h)
468+writeChunksWith n h = A.lCompactGE n (writeChunks h)
476469
477470-- | Same as 'writeChunksWith'
478471--
@@ -496,11 +489,11 @@ writeChunksWithBufferOf = writeChunksWith
496489-- Bytes in the input stream are collected into a buffer until we have a chunk
497490-- of @reqSize@ and then written to the IO device.
498491--
499−-- >>> writeWith n h = Fold.groupsOf n (Array.writeNUnsafe n) (Handle.writeChunks h)
492+-- >>> writeWith n h = Fold.groupsOf n (Array.unsafeCreateOf n) (Handle.writeChunks h)
500493--
501494{-# INLINE writeWith #-}
502495writeWith :: MonadIO m => Int -> Handle -> Fold m Word8 ()
503−writeWith n h = FL.groupsOf n (pinnedWriteNUnsafe n) (writeChunks h)
496+writeWith n h = FL.groupsOf n (A.unsafePinnedCreateOf n) (writeChunks h)
504497
505498-- | Same as 'writeWith'
506499--
@@ -518,7 +511,7 @@ writeWithBufferOf = writeWith
518511writeMaybesWith :: (MonadIO m )
519512 => Int -> Handle -> Fold m (Maybe Word8) ()
520513writeMaybesWith n h =
521− let writeNJusts = FL.lmap fromJust $ A.pinnedWriteN n
514+ let writeNJusts = FL.lmap fromJust $ A.pinnedCreateOf n
522515 writeOnNothing = FL.takeEndBy_ isNothing writeNJusts
523516 in FL.many writeOnNothing (writeChunks h)
524517
@@ -528,7 +521,7 @@ writeMaybesWith n h =
528521{-# INLINE writerWith #-}
529522writerWith :: MonadIO m => Int -> Refold m Handle Word8 ()
530523writerWith n =
531− FL.refoldMany (FL.take n $ pinnedWriteNUnsafe n) chunkWriter
524+ FL.refoldMany (FL.take n $ A.unsafePinnedCreateOf n) chunkWriter
532525
533526-- | Write a byte stream to a file handle. Accumulates the input in chunks of
534527-- up to 'Streamly.Internal.Data.Array.Type.defaultChunkSize' before writing
core/src/Streamly/Internal/FileSystem/Path.hs+36−4
@@ -36,7 +36,7 @@
3636module Streamly.Internal.FileSystem.Path
3737 (
3838 -- * Path Types
39− Path
39+ Path (..)
4040 , File
4141 , Dir
4242 , Abs
@@ -82,6 +82,11 @@ module Streamly.Internal.FileSystem.Path
8282 , toChars
8383
8484 -- * Operations
85+ -- Do we need to export the separator functions? They are not essential if
86+ -- operations to split and combine paths are provided. If someone wants to
87+ -- work on paths at low level then they know what they are.
88+ , primarySeparator
89+ , isSeparator
8590 , extendPath
8691 , extendDir
8792 )
@@ -89,6 +94,7 @@ where
8994
9095import Control.Exception (Exception)
9196import Control.Monad.Catch (MonadThrow(..))
97+import Data.Char (chr)
9298import Data.Functor.Identity (Identity(..))
9399import Data.Word (Word8)
94100#if defined(mingw32_HOST_OS) || defined(__MINGW32__)
@@ -151,6 +157,15 @@ instance Exception PathException
151157-- | A type representing file system paths for directories or files.
152158newtype Path = Path (Array WORD_TYPE) -- deriving Eq
153159
160+-- Show instance prints raw bytes without any decoding for rountdtripping.
161+-- Should we print this as a string instead, may be useful for ascii chars but
162+-- utf8 encoded chars may be unprintable. Better use toString if you want to
163+-- pretty print the path.
164+{-
165+instance Show Path where
166+ show (Path x) = show x
167+-}
168+
154169-- XXX Do we need a type for file or dir Name as names cannot have the
155170-- separator char and there may be other restrictions on names? For example,
156171-- length restriction. A file name cannot be "." or "..". We can use the types
@@ -438,8 +453,25 @@ mkRelFile = undefined
438453-- Operations
439454------------------------------------------------------------------------------
440455
441−separator :: WORD_TYPE
442−separator = SEPARATOR
456+separatorWord :: WORD_TYPE
457+separatorWord = SEPARATOR
458+
459+-- Portable definition for exporting.
460+
461+-- | Primary path separator character, @/@ on Posix and @\\@ on Windows.
462+-- Windows supports @/@ too as a separator. Please use 'isSeparator' for
463+-- testing if a char is a separator char.
464+primarySeparator :: Char
465+primarySeparator = chr (SEPARATOR)
466+
467+-- | On Posix only @/@ is a path separator but in windows it could be either
468+-- @/@ or @\\@.
469+isSeparator :: Char -> Bool
470+#if defined(mingw32_HOST_OS) || defined(__MINGW32__)
471+isSeparator c = (c == '/') || (c == '\\')
472+#else
473+isSeparator = (== '/')
474+#endif
443475
444476-- If we append an absolute path it may fail with an error if the 'Path'
445477-- implementation stores absolute path information (a leading separator char).
@@ -465,7 +497,7 @@ extendPath (Path a) (Path b) =
465497 newArr = unsafePerformIO $ do
466498 arr <- MutArray.new len
467499 arr1 <- MutArray.spliceUnsafe arr (Array.unsafeThaw a)
468− arr2 <- MutArray.snocUnsafe arr1 separator
500+ arr2 <- MutArray.snocUnsafe arr1 separatorWord
469501 arr3 <- MutArray.spliceUnsafe arr2 (Array.unsafeThaw b)
470502 return (Array.unsafeFreeze arr3)
471503 in Path newArr
core/src/Streamly/Internal/Unicode/Stream.hs+1−1
@@ -1057,7 +1057,7 @@ stripHead = Stream.dropWhile isSpace
10571057-- >>> Stream.fold Fold.toList $ Unicode.lines Fold.toList (Stream.fromList "lines\nthis\nstring\n\n\n")
10581058-- ["lines","this","string","",""]
10591059--
1060−-- > lines = Stream.splitOnSuffix (== '\n')
1060+-- >>> lines f = Stream.foldMany (Fold.takeEndBy_ (== '\n') f)
10611061--
10621062-- /Pre-release/
10631063{-# INLINE lines #-}
core/src/Streamly/Internal/Unicode/String.hs+7−7
@@ -39,14 +39,14 @@ module Streamly.Internal.Unicode.String
3939 --
4040 -- $setup
4141
42− str
42+ str
4343 ) where
4444
4545
4646import Control.Applicative (Alternative(..))
4747import Control.Exception (displayException)
4848import Data.Functor.Identity (runIdentity)
49−import Streamly.Internal.Data.Parser (Parser)
49+import Streamly.Internal.Data.Parser (Parser, ParseError)
5050
5151import Language.Haskell.TH
5252import Language.Haskell.TH.Quote
@@ -106,9 +106,12 @@ strSegmentExp (StrVar name) = do
106106strExp :: [StrSegment] -> Q Exp
107107strExp xs = appE [| concat |] $ listE $ map strSegmentExp xs
108108
109+parseStr :: String -> Either ParseError [StrSegment]
110+parseStr = runIdentity . Stream.parse strParser . Stream.fromList
111+
109112expandVars :: String -> Q Exp
110−expandVars ln =
111− case runIdentity $ Stream.parse strParser (Stream.fromList ln) of
113+expandVars input =
114+ case parseStr input of
112115 Left e ->
113116 fail $ "str QuasiQuoter parse error: " ++ displayException e
114117 Right x ->
@@ -141,9 +144,6 @@ expandVars ln =
141144-- :}
142145-- "hello world!"
143146--
144−-- Bugs: because of a bug in parsers, a lone # at the end of input gets
145−-- removed.
146−--
147147str :: QuasiQuoter
148148str =
149149 QuasiQuoter
core/streamly-core.cabal+14−10
@@ -1,6 +1,6 @@
11cabal-version: 2.2
22name: streamly-core
3−version: 0.2.1
3+version: 0.2.2
44synopsis: Streaming, parsers, arrays, serialization and more
55description:
66 For upgrading to streamly-0.9.0+ please read the
@@ -116,6 +116,7 @@ extra-doc-files:
116116 docs/*.md
117117 docs/ApiChangelogs/0.1.0.txt
118118 docs/ApiChangelogs/0.1.0-0.2.0.txt
119+ docs/ApiChangelogs/0.2.0-0.2.2.txt
119120
120121source-repository head
121122 type: git
@@ -217,8 +218,6 @@ common compile-options
217218common default-extensions
218219 default-extensions:
219220 BangPatterns
220− CApiFFI
221− CPP
222221 ConstraintKinds
223222 DeriveDataTypeable
224223 DeriveGeneric
@@ -229,24 +228,29 @@ common default-extensions
229228 GeneralizedNewtypeDeriving
230229 InstanceSigs
231230 KindSignatures
232− LambdaCase
233− MagicHash
234231 MultiParamTypeClasses
235− PatternSynonyms
236232 RankNTypes
237− RecordWildCards
238233 ScopedTypeVariables
239234 StandaloneDeriving
240235 TupleSections
241236 TypeApplications
242− TypeFamilies
243237 TypeOperators
244− ViewPatterns
245238
239+ -- Not GHC2021
240+ CApiFFI
241+ CPP
242+ DefaultSignatures
243+ LambdaCase
244+ MagicHash
245+ RecordWildCards
246+
247+ -- TypeFamilies is required by IsList, IsMap type classes and
248+ -- Unbox generic deriving code.
249+ -- TypeFamilies
246250 -- MonoLocalBinds, enabled by TypeFamilies, causes performance
247251 -- regressions. Disable it. This must come after TypeFamilies,
248252 -- otherwise TypeFamilies will enable it again.
249− NoMonoLocalBinds
253+ -- NoMonoLocalBinds
250254
251255 -- UndecidableInstances -- Does not show any perf impact
252256 -- UnboxedTuples -- interferes with (#.)
docs/Developer/optimization-guidelines.md+12
@@ -191,6 +191,18 @@ the stages of a loop, it is often not useful to inline the whole loop
191191itself, in fact we may have to occasionally use a `NOINLINE` so that the
192192compiler does not inline it.
193193
194+We need to be careful about NOINLINE on polymorphic functions. When
195+NOINLINE is used, the function cannot be specialized causing huge
196+performance degradation. To avoid that we can either make the
197+function monomorphic or sometimes using `NOINLINE [0]` can do the
198+trick. `NOINLINE [0]` blocks inlining in earlier phases but lets it
199+specialize in the last phase, inlining in the last phase is left to
200+the compiler. `INLINE [0]` may sound the same as `NOINLINE [0]`,
201+however, it behaves differently because we ask the compiler to INLINE
202+it compulsorily and it may not give us the desired results. The
203+Data.Fold.writeLastN benchmark is one such case where `NOINLINE [0]`
204+provides much better performance than `INLINE [0]`.
205+
194206### NoSpecConstr
195207
196208It is not always useful to specialize function calls for all constructors, some
docs/User/Project/Changelog.md+1−1
@@ -2,7 +2,7 @@
22
33<!-- See rendered changelog at https://streamly.composewell.com -->
44
5−## Unreleased
5+## 0.10.1 (Jan 2024)
66
77* Fix TH macros in `Streamly.Data.Stream.MkType` for GHC 9.6 and above.
88
docs/User/Project/README.md+1
@@ -1,6 +1,7 @@
11# [Streamly][]: Idiomatic Haskell with C-Like Performance
22
33[![Gitter chat](https://badges.gitter.im/composewell/gitter.svg)](https://gitter.im/composewell/streamly)
4+[![Hackage](https://img.shields.io/hackage/v/streamly-core.svg?style=flat)](https://hackage.haskell.org/package/streamly-core)
45[![Hackage](https://img.shields.io/hackage/v/streamly.svg?style=flat)](https://hackage.haskell.org/package/streamly)
56
67## Upgrading to 0.9.0+
src/Streamly/Internal/Data/IsMap/HashMap.hs+5
@@ -1,3 +1,8 @@
1+{-# LANGUAGE TypeFamilies #-}
2+-- Must come after TypeFamilies, otherwise it is re-enabled.
3+-- MonoLocalBinds enabled by TypeFamilies causes perf regressions in general.
4+{-# LANGUAGE NoMonoLocalBinds #-}
5+
16{-# OPTIONS_GHC -Wno-orphans #-}
27
38-- |
src/Streamly/Internal/Data/Stream/IsStream/Common.hs+1−1
@@ -529,7 +529,7 @@ reverse' :: (IsStream t, MonadIO m, Unbox a) => t m a -> t m a
529529-- reverse' s = fromStreamD $ D.reverse' $ toStreamD s
530530reverse' =
531531 fromStreamD
532− . A.concatChunksRev -- unfoldMany A.readerRev
532+ . A.concatRev -- unfoldMany A.readerRev
533533 . D.fromStreamK
534534 . K.reverse
535535 . D.toStreamK
src/Streamly/Internal/Data/Stream/IsStream/Expand.hs+5
@@ -1,3 +1,8 @@
1+{-# LANGUAGE TypeFamilies #-}
2+-- Must come after TypeFamilies, otherwise it is re-enabled.
3+-- MonoLocalBinds enabled by TypeFamilies causes perf regressions in general.
4+{-# LANGUAGE NoMonoLocalBinds #-}
5+
16{-# OPTIONS_GHC -Wno-deprecations #-}
27
38-- |
src/Streamly/Internal/Data/Stream/IsStream/Top.hs+1−1
@@ -649,7 +649,7 @@ intersectBySorted :: (IsStream t, Monad m) =>
649649 (a -> a -> Ordering) -> t m a -> t m a -> t m a
650650intersectBySorted eq s1 =
651651 IsStream.fromStreamD
652− . StreamD.intersectBySorted eq (IsStream.toStreamD s1)
652+ . StreamD.sortedIntersectBy eq (IsStream.toStreamD s1)
653653 . IsStream.toStreamD
654654
655655-- Roughly joinLeft s1 s2 = s1 `difference` s2 + s1 `intersection` s2
src/Streamly/Internal/Data/Stream/Serial.hs+5
@@ -1,4 +1,9 @@
1+{-# LANGUAGE TypeFamilies #-}
2+-- Must come after TypeFamilies, otherwise it is re-enabled.
3+-- MonoLocalBinds enabled by TypeFamilies causes perf regressions in general.
4+{-# LANGUAGE NoMonoLocalBinds #-}
15{-# LANGUAGE UndecidableInstances #-}
6+
27{-# OPTIONS_GHC -Wno-deprecations #-}
38{-# OPTIONS_GHC -Wno-redundant-constraints #-}
49
src/Streamly/Internal/Data/Stream/Zip.hs+5
@@ -1,4 +1,9 @@
1+{-# LANGUAGE TypeFamilies #-}
2+-- Must come after TypeFamilies, otherwise it is re-enabled.
3+-- MonoLocalBinds enabled by TypeFamilies causes perf regressions in general.
4+{-# LANGUAGE NoMonoLocalBinds #-}
15{-# LANGUAGE UndecidableInstances #-}
6+
27{-# OPTIONS_GHC -Wno-deprecations #-}
38
49-- |
src/Streamly/Internal/FileSystem/Event.hs+3
@@ -20,6 +20,9 @@
2020-- * "Streamly.Internal.FileSystem.Event.Linux"
2121-- * "Streamly.Internal.FileSystem.Event.Windows"
2222
23+-- XXX Need to ensure that the signatures of the exported APIs are same on all
24+-- platforms.
25+
2326module Streamly.Internal.FileSystem.Event
2427 (
2528 -- * Creating a Watch
src/Streamly/Internal/FileSystem/Event/Darwin.hs+1−1
@@ -451,7 +451,7 @@ openWatch Config{..} paths = do
451451
452452 withPathName :: Array Word8 -> (PathName -> IO a) -> IO a
453453 withPathName arr act = do
454− A.asPtrUnsafe arr $ \ptr ->
454+ A.unsafePinnedAsPtr arr $ \ptr ->
455455 let pname = PathName (castPtr ptr) (fromIntegral (A.length arr))
456456 in act pname
457457
src/Streamly/Internal/FileSystem/Event/Linux.hs+2−2
@@ -188,7 +188,7 @@ import qualified Streamly.FileSystem.Handle as FH
188188import qualified Streamly.Unicode.Stream as U
189189
190190import qualified Streamly.Internal.Data.Array as A
191− ( fromStream, asCStringUnsafe, asPtrUnsafe
191+ ( fromStream, asCStringUnsafe, unsafePinnedAsPtr
192192 , getSliceUnsafe, read
193193 )
194194import qualified Streamly.Internal.FileSystem.Dir as Dir (readDirs)
@@ -809,7 +809,7 @@ readOneEvent :: Config -> Watch -> Parser Word8 IO Event
809809readOneEvent cfg wt@(Watch _ wdMap) = do
810810 let headerLen = sizeOf (undefined :: CInt) + 12
811811 arr <- PR.takeEQ headerLen (A.writeN headerLen)
812− (ewd, eflags, cookie, pathLen) <- PR.fromEffect $ A.asPtrUnsafe arr readHeader
812+ (ewd, eflags, cookie, pathLen) <- PR.fromEffect $ A.unsafePinnedAsPtr arr readHeader
813813 -- XXX need the "initial" in parsers to return a step type so that "take 0"
814814 -- can return without an input. otherwise if pathLen is 0 we will keep
815815 -- waiting to read one more char before we return this event.
src/Streamly/Internal/FileSystem/FD.hs+5−5
@@ -130,7 +130,7 @@ import qualified GHC.IO.Device as RawIO
130130import Streamly.Data.Array (Array, Unbox)
131131import Streamly.Data.Stream (Stream)
132132
133−import Streamly.Internal.Data.Array (byteLength, unsafeFreeze, asPtrUnsafe)
133+import Streamly.Internal.Data.Array (byteLength, unsafeFreeze, unsafePinnedAsPtr)
134134import Streamly.Internal.System.IO (defaultChunkSize)
135135
136136#if !defined(mingw32_HOST_OS)
@@ -147,7 +147,7 @@ import qualified Streamly.Internal.System.IOVec.Type as RawIO
147147import qualified Streamly.Data.Array as A
148148import qualified Streamly.Data.Fold as FL
149149import qualified Streamly.Internal.Data.MutArray as MArray
150− (MutArray(..), asPtrUnsafe, pinnedNewBytes)
150+ (MutArray(..), unsafePinnedAsPtr, pinnedNewBytes)
151151import qualified Streamly.Internal.Data.Array.Stream as AS
152152import qualified Streamly.Internal.Data.Stream as S
153153import qualified Streamly.Internal.Data.Stream as D
@@ -220,7 +220,7 @@ readArrayUpto :: Int -> Handle -> IO (Array Word8)
220220readArrayUpto size (Handle fd) = do
221221 arr <- MArray.pinnedNewBytes size
222222 -- ptr <- mallocPlainForeignPtrAlignedBytes size (alignment (undefined :: Word8))
223− MArray.asPtrUnsafe arr $ \p -> do
223+ MArray.unsafePinnedAsPtr arr $ \p -> do
224224 -- n <- hGetBufSome h p size
225225#if MIN_VERSION_base(4,15,0)
226226 n <- RawIO.read fd p 0 size
@@ -244,7 +244,7 @@ readArrayUpto size (Handle fd) = do
244244writeArray :: Unbox a => Handle -> Array a -> IO ()
245245writeArray _ arr | A.length arr == 0 = return ()
246246writeArray (Handle fd) arr =
247− asPtrUnsafe arr $ \p ->
247+ unsafePinnedAsPtr arr $ \p ->
248248 -- RawIO.writeAll fd (castPtr p) aLen
249249#if MIN_VERSION_base(4,15,0)
250250 RawIO.write fd (castPtr p) 0 aLen
@@ -270,7 +270,7 @@ writeArray (Handle fd) arr =
270270writeIOVec :: Handle -> Array RawIO.IOVec -> IO ()
271271writeIOVec _ iov | A.length iov == 0 = return ()
272272writeIOVec (Handle fd) iov =
273− asPtrUnsafe iov $ \p ->
273+ unsafePinnedAsPtr iov $ \p ->
274274 RawIO.writevAll fd p (A.length iov)
275275-}
276276#endif
src/Streamly/Internal/Network/Inet/TCP.hs+3−3
@@ -116,7 +116,6 @@ import Prelude hiding (read)
116116import Streamly.Internal.Control.Concurrent (MonadAsync)
117117import Streamly.Internal.Control.ForkLifted (fork)
118118import Streamly.Data.Array (Array)
119−import Streamly.Internal.Data.Array (pinnedWriteNUnsafe)
120119import Streamly.Internal.Data.Fold ( Fold(..) )
121120import Streamly.Data.Stream (Stream)
122121import Streamly.Internal.Data.Tuple.Strict (Tuple'(..))
@@ -132,7 +131,8 @@ import qualified Streamly.Data.Array as A
132131import qualified Streamly.Data.Fold as FL
133132import qualified Streamly.Data.Stream as S
134133import qualified Streamly.Data.Unfold as UF
135−import qualified Streamly.Internal.Data.Array as A (pinnedChunksOf)
134+import qualified Streamly.Internal.Data.Array as A
135+ (pinnedChunksOf, unsafePinnedCreateOf)
136136import qualified Streamly.Internal.Data.Unfold as UF (bracketIO)
137137import qualified Streamly.Internal.Data.Fold as FL (Step(..), reduce)
138138
@@ -435,7 +435,7 @@ writeWithBufferOf
435435 -> PortNumber
436436 -> Fold m Word8 ()
437437writeWithBufferOf n addr port =
438− FL.groupsOf n (pinnedWriteNUnsafe n) (writeChunks addr port)
438+ FL.groupsOf n (A.unsafePinnedCreateOf n) (writeChunks addr port)
439439
440440-- | Write a stream to the supplied IPv4 host address and port number.
441441--
src/Streamly/Internal/Network/Socket.hs+11−10
@@ -87,7 +87,6 @@ import Prelude hiding (read)
8787import qualified Network.Socket as Net
8888
8989import Streamly.Internal.Data.Array (Array(..))
90−import Streamly.Internal.Data.Array.Stream (lpackArraysChunksOf)
9190import Streamly.Data.Fold (Fold)
9291import Streamly.Data.Stream (Stream)
9392import Streamly.Internal.Data.Unfold (Unfold(..))
@@ -99,10 +98,10 @@ import qualified Streamly.Data.Fold as FL
9998import qualified Streamly.Data.Stream as S
10099import qualified Streamly.Data.Unfold as UF
101100import qualified Streamly.Internal.Data.Array as A
102− ( unsafeFreeze, asPtrUnsafe, byteLength, pinnedChunksOf,
103− pinnedWriteN, pinnedWriteNUnsafe )
101+ ( unsafeFreeze, unsafePinnedAsPtr, byteLength, pinnedChunksOf,
102+ pinnedCreateOf, unsafePinnedCreateOf, lCompactGE )
104103import qualified Streamly.Internal.Data.MutArray as MArray
105− (MutArray(..), asPtrUnsafe, pinnedNewBytes)
104+ (MutArray(..), unsafePinnedAsPtr, pinnedEmptyOf)
106105import qualified Streamly.Internal.Data.Stream as S (fromStreamK, Stream(..), Step(..))
107106import qualified Streamly.Internal.Data.StreamK as K (mkStream)
108107
@@ -253,6 +252,8 @@ accept tcpListenQ spec addr =
253252-- Array IO (Input)
254253-------------------------------------------------------------------------------
255254
255+-- XXX add an API that compacts the arrays to an exact size.
256+
256257{-# INLINABLE readArrayUptoWith #-}
257258readArrayUptoWith
258259 :: (h -> Ptr Word8 -> Int -> IO Int)
@@ -260,9 +261,9 @@ readArrayUptoWith
260261 -> h
261262 -> IO (Array Word8)
262263readArrayUptoWith f size h = do
263− arr <- MArray.pinnedNewBytes size
264+ arr <- MArray.pinnedEmptyOf size
264265 -- ptr <- mallocPlainForeignPtrAlignedBytes size (alignment (undefined :: Word8))
265− MArray.asPtrUnsafe arr $ \p -> do
266+ MArray.unsafePinnedAsPtr arr $ \p -> do
266267 n <- f h p size
267268 let v = A.unsafeFreeze
268269 $ arr { MArray.arrEnd = n, MArray.arrBound = size }
@@ -310,7 +311,7 @@ writeArrayWith :: Unbox a
310311 -> Array a
311312 -> IO ()
312313writeArrayWith _ _ arr | A.length arr == 0 = return ()
313−writeArrayWith f h arr = A.asPtrUnsafe arr $ \ptr -> f h (castPtr ptr) aLen
314+writeArrayWith f h arr = A.unsafePinnedAsPtr arr $ \ptr -> f h (castPtr ptr) aLen
314315
315316 where
316317
@@ -473,7 +474,7 @@ writeChunks h = FL.drainMapM (liftIO . putChunk h)
473474{-# INLINE writeChunksWith #-}
474475writeChunksWith :: (MonadIO m, Unbox a)
475476 => Int -> Socket -> Fold m (Array a) ()
476−writeChunksWith n h = lpackArraysChunksOf n (writeChunks h)
477+writeChunksWith n h = A.lCompactGE n (writeChunks h)
477478
478479-- | Same as 'writeChunksWith'
479480--
@@ -504,7 +505,7 @@ putBytesWith n h m = putChunks h $ A.pinnedChunksOf n m
504505--
505506{-# INLINE writeWith #-}
506507writeWith :: MonadIO m => Int -> Socket -> Fold m Word8 ()
507−writeWith n h = FL.groupsOf n (A.pinnedWriteNUnsafe n) (writeChunks h)
508+writeWith n h = FL.groupsOf n (A.unsafePinnedCreateOf n) (writeChunks h)
508509
509510-- | Same as 'writeWith'
510511--
@@ -522,7 +523,7 @@ writeWithBufferOf = writeWith
522523writeMaybesWith :: (MonadIO m )
523524 => Int -> Socket -> Fold m (Maybe Word8) ()
524525writeMaybesWith n h =
525− let writeNJusts = FL.lmap fromJust $ A.pinnedWriteN n
526+ let writeNJusts = FL.lmap fromJust $ A.pinnedCreateOf n
526527 writeOnNothing = FL.takeEndBy_ isNothing writeNJusts
527528 in FL.many writeOnNothing (writeChunks h)
528529
streamly.cabal+11−11
@@ -1,6 +1,6 @@
11cabal-version: 2.2
22name: streamly
3−version: 0.10.0
3+version: 0.10.1
44synopsis: Streaming, dataflow programming and declarative concurrency
55description:
66 For upgrading to streamly-0.9.0+ please read the
@@ -294,8 +294,6 @@ common compile-options
294294common default-extensions
295295 default-extensions:
296296 BangPatterns
297− CApiFFI
298− CPP
299297 ConstraintKinds
300298 DeriveDataTypeable
301299 DeriveGeneric
@@ -306,23 +304,25 @@ common default-extensions
306304 GeneralizedNewtypeDeriving
307305 InstanceSigs
308306 KindSignatures
309− LambdaCase
310− MagicHash
311307 MultiParamTypeClasses
312− PatternSynonyms
313308 RankNTypes
314− RecordWildCards
315309 ScopedTypeVariables
316310 TupleSections
317311 TypeApplications
318− TypeFamilies
319312 TypeOperators
320− ViewPatterns
321313
314+ -- Not GHC2021
315+ CApiFFI
316+ CPP
317+ LambdaCase
318+ MagicHash
319+ RecordWildCards
320+
321+ -- TypeFamilies -- required by IsHashMap type class
322322 -- MonoLocalBinds, enabled by TypeFamilies, causes performance
323323 -- regressions. Disable it. This must come after TypeFamilies,
324324 -- otherwise TypeFamilies will enable it again.
325− NoMonoLocalBinds
325+ -- NoMonoLocalBinds
326326
327327 -- UndecidableInstances -- Does not show any perf impact
328328 -- UnboxedTuples -- interferes with (#.)
@@ -513,7 +513,7 @@ library
513513 , template-haskell >= 2.14 && < 2.22
514514
515515 -- The core streamly package
516− , streamly-core >= 0.2.0 && < 0.2.2
516+ , streamly-core == 0.2.2
517517
518518 , hashable >= 1.3 && < 1.5
519519 , unordered-containers >= 0.2 && < 0.3
test/Streamly/Test/Data/Array.hs+2−2
@@ -185,7 +185,7 @@ testUnsafeIndxedFromList inp =
185185testAsPtrUnsafeMA :: IO ()
186186testAsPtrUnsafeMA = do
187187 arr <- MA.fromList ([0 .. 99] :: [Int])
188− MA.asPtrUnsafe arr (getList (0 :: Int)) `shouldReturn` [0 .. 99]
188+ MA.unsafePinnedAsPtr arr (getList (0 :: Int)) `shouldReturn` [0 .. 99]
189189
190190 where
191191
@@ -229,7 +229,7 @@ main =
229229 prop "read . write === id" testFoldUnfold
230230 prop "fromList" testFromList
231231 prop "foldMany with writeNUnsafe concats to original"
232− (foldManyWith (\n -> Fold.take n (A.writeNUnsafe n)))
232+ (foldManyWith (\n -> Fold.take n (A.unsafeCreateOf n)))
233233 describe "unsafeSlice" $ do
234234 it "partial" $ unsafeSlice 2 4 [1..10]
235235 it "none" $ unsafeSlice 10 0 [1..10]