on master help sign in
58 files changed+1969−824
since splitsnapshots
unifiedsplit
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+1−1 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+30−5 core/src/Streamly/Internal/Data/Array/Generic.hs+22−6 core/src/Streamly/Internal/Data/Array/Type.hs+143−46 core/src/Streamly/Internal/Data/IsMap.hs+4 core/src/Streamly/Internal/Data/MutArray.hs+20−3 core/src/Streamly/Internal/Data/MutArray/Generic.hs+59−24 core/src/Streamly/Internal/Data/MutArray/Type.hs+453−230 core/src/Streamly/Internal/Data/MutByteArray/Type.hs+37−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+52−26 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+2−2 core/src/Streamly/Internal/FileSystem/Handle.hs+17−16 core/src/Streamly/Internal/FileSystem/Path.hs+27−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/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+10−8 streamly.cabal+11−11 test/Streamly/Test/Data/Array.hs+2−2
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+1−1
@@ -1,6 +1,6 @@
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
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+30−5
@@ -74,6 +74,8 @@ module Streamly.Internal.Data.Array
7474 , interposeSuffix
7575 , intercalateSuffix
7676
77+ , compactLE
78+ , pinnedCompactLE
7779 , compactOnByte
7880 , compactOnByteSuffix
7981
@@ -203,6 +205,8 @@ last = getIndexRev 0
203205-- Folds with Array as the container
204206-------------------------------------------------------------------------------
205207
208+-- XXX We should generate this from Ring.
209+
206210-- | @writeLastN n@ folds a maximum of @n@ elements from the end of the input
207211-- stream to an 'Array'.
208212--
@@ -324,6 +328,7 @@ sliceIndexerFromLen from len =
324328 Unfold.lmap unsafeThaw (MA.sliceIndexerFromLen from len)
325329
326330{-# DEPRECATED genSlicesFromLen "Please use sliceIndexerFromLen instead." #-}
331+{-# INLINE genSlicesFromLen #-}
327332genSlicesFromLen :: forall m a. (Monad m, Unbox a)
328333 => Int -- ^ from index
329334 -> Int -- ^ length of the slice
@@ -345,6 +350,7 @@ slicerFromLen from len =
345350 $ Unfold.lmap unsafeThaw (MA.slicerFromLen from len)
346351
347352{-# DEPRECATED getSlicesFromLen "Please use slicerFromLen instead." #-}
353+{-# INLINE getSlicesFromLen #-}
348354getSlicesFromLen :: forall m a. (Monad m, Unbox a)
349355 => Int -- ^ from index
350356 -> Int -- ^ length of the slice
@@ -395,6 +401,7 @@ indexReader m =
395401
396402-- XXX DO NOT REMOVE, change the signature to use Stream instead of unfold
397403{-# DEPRECATED getIndices "Please use getIndices instead." #-}
404+{-# INLINE getIndices #-}
398405getIndices :: (Monad m, Unbox a) => Stream m Int -> Unfold m (Array a) a
399406getIndices = indexReader
400407
@@ -445,7 +452,7 @@ runPipe f arr = P.runPipe (toArrayMinChunk (length arr)) $ f (read arr)
445452streamTransform :: forall m a b. (MonadIO m, Unbox a, Unbox b)
446453 => (Stream m a -> Stream m b) -> Array a -> m (Array b)
447454streamTransform f arr =
448− Stream.fold (writeWith (length arr)) $ f (read arr)
455+ Stream.fold (createWith (length arr)) $ f (read arr)
449456
450457-------------------------------------------------------------------------------
451458-- Casts
@@ -498,8 +505,8 @@ cast arr =
498505asCStringUnsafe :: Array a -> (CString -> IO b) -> IO b
499506asCStringUnsafe arr act = do
500507 let arr1 = asBytes arr <> fromList [0]
501− -- asPtrUnsafe makes sure the array is pinned
502− asPtrUnsafe arr1 $ \ptr -> act (castPtr ptr)
508+ -- unsafePinnedAsPtr makes sure the array is pinned
509+ unsafePinnedAsPtr arr1 $ \ptr -> act (castPtr ptr)
503510
504511-------------------------------------------------------------------------------
505512-- Folds
@@ -591,7 +598,7 @@ data FlattenState s =
591598-- >>> interposeSuffix x = Stream.interposeSuffix x Array.reader
592599--
593600{-# INLINE_NORMAL interposeSuffix #-}
594−interposeSuffix :: forall m a. (MonadIO m, Unbox a)
601+interposeSuffix :: forall m a. (Monad m, Unbox a)
595602 => a -> Stream m (Array a) -> Stream m a
596603-- This does not require MonadIO constraint.
597604-- interposeSuffix x = D.interposeSuffix x reader
@@ -612,7 +619,7 @@ interposeSuffix sep (D.Stream step state) = D.Stream step' (OuterLoop state)
612619 return $ D.Yield sep $ OuterLoop st
613620
614621 step' _ (InnerLoop st contents p end) = do
615− x <- liftIO $ peekAt p contents
622+ x <- pure $ unsafeInlineIO $ peekAt p contents
616623 return $ D.Yield x (InnerLoop st contents (INDEX_NEXT(p,a)) end)
617624
618625-- | Insert the given array after each array and flatten.
@@ -624,6 +631,24 @@ intercalateSuffix :: (Monad m, Unbox a)
624631 => Array a -> Stream m (Array a) -> Stream m a
625632intercalateSuffix = D.intercalateSuffix reader
626633
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+
627652-- | Split a stream of arrays on a given separator byte, dropping the separator
628653-- and coalescing all the arrays between two separators into a single array.
629654--
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/Type.hs+143−46
@@ -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-- |
37-- Module : Streamly.Internal.Data.Array.Type
48-- Copyright : (c) 2020 Composewell Technologies
@@ -28,10 +32,10 @@ module Streamly.Internal.Data.Array.Type
2832 , isPinned
2933
3034 -- *** Casting
31− , asPtrUnsafe
35+ , unsafePinnedAsPtr
3236
3337 -- ** Construction
34− , nil
38+ , empty
3539
3640 -- *** Cloning
3741 -- XXX Why would we clone an immutable array?
@@ -45,15 +49,13 @@ module Streamly.Internal.Data.Array.Type
4549
4650 -- *** Stream Folds
4751 , unsafeMakePure
48− , writeWith
49− , writeN
50− , pinnedWriteN
51− , writeNUnsafe
52− , pinnedWriteNUnsafe
53− -- , MA.ArrayUnsafe (..)
54− , pinnedWriteNAligned
55− , write
56− , pinnedWrite
52+ , createOf
53+ , pinnedCreateOf
54+ , unsafeCreateOf
55+ , unsafePinnedCreateOf
56+ , create
57+ , pinnedCreate
58+ , createWith
5759
5860 -- *** From containers
5961 , fromListN
@@ -75,8 +77,8 @@ module Streamly.Internal.Data.Array.Type
7577 -- ** Reading
7678
7779 -- *** Indexing
78− , unsafeIndexIO -- XXX rename to getIndexUnsafeIO
79− , getIndexUnsafe
80+ , unsafeIndexIO -- XXX unsafeGetIndexIO
81+ , getIndexUnsafe -- XXX unsafeGetIndex
8082
8183 -- *** To Streams
8284 , read
@@ -123,12 +125,14 @@ module Streamly.Internal.Data.Array.Type
123125
124126 -- *** Compact
125127 -- | Append the arrays in a stream to form a stream of larger arrays.
126− , compactLE
127128 , fCompactGE
129+ , fPinnedCompactGE
128130 , lCompactGE
131+ , lPinnedCompactGE
129132 , compactGE
130133
131134 -- ** Deprecated
135+ , asPtrUnsafe
132136 , unsafeIndex
133137 , bufferChunks
134138 , flattenArrays
@@ -140,6 +144,15 @@ module Streamly.Internal.Data.Array.Type
140144 , toStreamDRev
141145 , toStream
142146 , toStreamRev
147+ , nil
148+ , writeWith
149+ , writeN
150+ , pinnedWriteN
151+ , writeNUnsafe
152+ , pinnedWriteNUnsafe
153+ , pinnedWriteNAligned
154+ , write
155+ , pinnedWrite
143156 )
144157where
145158
@@ -230,15 +243,20 @@ data Array a =
230243
231244-- | Use an @Array a@ as @Ptr a@.
232245--
233−-- See 'MA.asPtrUnsafe' in the Mutable array module for more details.
246+-- See 'MA.unsafePinnedAsPtr' in the Mutable array module for more details.
234247--
235248-- /Unsafe/
236249--
237250-- /Pre-release/
238251--
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." #-}
239257{-# INLINE asPtrUnsafe #-}
240258asPtrUnsafe :: MonadIO m => Array a -> (Ptr a -> m b) -> m b
241−asPtrUnsafe arr = MA.asPtrUnsafe (unsafeThaw arr)
259+asPtrUnsafe = unsafePinnedAsPtr
242260
243261-------------------------------------------------------------------------------
244262-- Freezing and Thawing
@@ -459,12 +477,13 @@ pinnedChunksOf n str = D.map unsafeFreeze $ MA.pinnedChunksOf n str
459477-- >>> concat = Stream.unfoldMany Array.reader
460478--
461479{-# INLINE_NORMAL concat #-}
462−concat :: (MonadIO m, Unbox a) => Stream m (Array a) -> Stream m a
480+concat :: (Monad m, Unbox a) => Stream m (Array a) -> Stream m a
463481-- XXX this requires MonadIO whereas the unfoldMany version does not
464−concat = MA.concat . D.map unsafeThaw
482+concat = MA.concatWith (pure . unsafeInlineIO) . D.map unsafeThaw
465483-- concat = D.unfoldMany reader
466484
467485{-# DEPRECATED flattenArrays "Please use \"unfoldMany reader\" instead." #-}
486+{-# INLINE flattenArrays #-}
468487flattenArrays :: forall m a. (MonadIO m, Unbox a)
469488 => D.Stream m (Array a) -> D.Stream m a
470489flattenArrays = concat
@@ -475,13 +494,14 @@ flattenArrays = concat
475494-- >>> concatRev = Stream.unfoldMany Array.readerRev
476495--
477496{-# INLINE_NORMAL concatRev #-}
478−concatRev :: forall m a. (MonadIO m, Unbox a)
497+concatRev :: forall m a. (Monad m, Unbox a)
479498 => D.Stream m (Array a) -> D.Stream m a
480499-- XXX this requires MonadIO whereas the unfoldMany version does not
481−concatRev = MA.concatRev . D.map unsafeThaw
500+concatRev = MA.concatRevWith (pure . unsafeInlineIO) . D.map unsafeThaw
482501-- concatRev = D.unfoldMany readerRev
483502
484503{-# DEPRECATED flattenArraysRev "Please use \"unfoldMany readerRev\" instead." #-}
504+{-# INLINE flattenArraysRev #-}
485505flattenArraysRev :: forall m a. (MonadIO m, Unbox a)
486506 => D.Stream m (Array a) -> D.Stream m a
487507flattenArraysRev = concatRev
@@ -495,26 +515,28 @@ flattenArraysRev = concatRev
495515-- arrays would have no capacity to append, therefore, a copy will be forced
496516-- anyway.
497517
498−-- | Scan @compactLE n@ coalesces adjacent arrays in the input stream
499−-- only if the combined size would be less than or equal to n.
500−{-# INLINE_NORMAL compactLE #-}
501−compactLE :: (MonadIO m, Unbox a)
502− => Int -> Stream m (Array a) -> Stream m (Array a)
503−compactLE n stream =
504− D.map unsafeFreeze $ MA.rCompactLE n $ D.map unsafeThaw stream
505−
506518-- | Fold @fCompactGE n@ coalesces adjacent arrays in the input stream
507519-- until the size becomes greater than or equal to n.
508520--
521+-- Generates unpinned arrays irrespective of the pinning status of input
522+-- arrays.
509523{-# INLINE_NORMAL fCompactGE #-}
510524fCompactGE :: (MonadIO m, Unbox a) => Int -> Fold m (Array a) (Array a)
511525fCompactGE n = fmap unsafeFreeze $ Fold.lmap unsafeThaw $ MA.fCompactGE n
512526
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+
513533-- | @compactGE n stream@ coalesces adjacent arrays in the @stream@ until
514534-- the size becomes greater than or equal to @n@.
515535--
516536-- >>> compactGE n = Stream.foldMany (Array.fCompactGE n)
517537--
538+-- Generates unpinned arrays irrespective of the pinning status of input
539+-- arrays.
518540{-# INLINE compactGE #-}
519541compactGE ::
520542 (MonadIO m, Unbox a)
@@ -526,12 +548,21 @@ compactGE n stream =
526548--
527549-- >>> lCompactGE n = Fold.many (Array.fCompactGE n)
528550--
551+-- Generates unpinned arrays irrespective of the pinning status of input
552+-- arrays.
529553{-# INLINE_NORMAL lCompactGE #-}
530554lCompactGE :: (MonadIO m, Unbox a)
531555 => Int -> Fold m (Array a) () -> Fold m (Array a) ()
532556lCompactGE n fld =
533557 Fold.lmap unsafeThaw $ MA.lCompactGE n (Fold.lmap unsafeFreeze fld)
534558
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+
535566-------------------------------------------------------------------------------
536567-- Splitting
537568-------------------------------------------------------------------------------
@@ -726,17 +757,28 @@ toList s = build (\c n -> toListFB c n s)
726757-- Folds
727758-------------------------------------------------------------------------------
728759
729−-- | @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
730761-- 'Array'.
731762--
732−{-# 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 #-}
733770writeN :: forall m a. (MonadIO m, Unbox a) => Int -> Fold m a (Array a)
734−writeN = fmap unsafeFreeze . MA.writeN
771+writeN = createOf
735772
736−-- | Like 'fromListN' but creates a pinned array.
737−{-# 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 #-}
738780pinnedWriteN :: forall m a. (MonadIO m, Unbox a) => Int -> Fold m a (Array a)
739−pinnedWriteN = fmap unsafeFreeze . MA.pinnedWriteN
781+pinnedWriteN = pinnedCreateOf
740782
741783-- | @pinnedWriteNAligned alignment n@ folds a maximum of @n@ elements from the input
742784-- stream to an 'Array' aligned to the given size.
@@ -744,44 +786,74 @@ pinnedWriteN = fmap unsafeFreeze . MA.pinnedWriteN
744786-- /Pre-release/
745787--
746788{-# INLINE_NORMAL pinnedWriteNAligned #-}
789+{-# DEPRECATED pinnedWriteNAligned "To be removed." #-}
747790pinnedWriteNAligned :: forall m a. (MonadIO m, Unbox a)
748791 => Int -> Int -> Fold m a (Array a)
749792pinnedWriteNAligned alignSize = fmap unsafeFreeze . MA.pinnedWriteNAligned alignSize
750793
751−-- | 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
752795-- driver must not call the step function more than 'n' times otherwise it will
753796-- corrupt the memory and crash. This function exists mainly because any
754797-- conditional in the step function blocks fusion causing 10x performance
755798-- slowdown.
756799--
757−{-# 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 #-}
758807writeNUnsafe :: forall m a. (MonadIO m, Unbox a)
759808 => Int -> Fold m a (Array a)
760−writeNUnsafe n = unsafeFreeze <$> MA.writeNUnsafe n
809+writeNUnsafe = unsafeCreateOf
810+
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
761815
762−{-# INLINE_NORMAL pinnedWriteNUnsafe #-}
816+{-# DEPRECATED pinnedWriteNUnsafe "Please use unsafePinnedCreateOf instead." #-}
817+{-# INLINE pinnedWriteNUnsafe #-}
763818pinnedWriteNUnsafe :: forall m a. (MonadIO m, Unbox a)
764819 => Int -> Fold m a (Array a)
765−pinnedWriteNUnsafe n = unsafeFreeze <$> MA.pinnedWriteNUnsafe n
820+pinnedWriteNUnsafe = unsafePinnedCreateOf
766821
767−{-# INLINE_NORMAL writeWith #-}
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
827+
828+{-# DEPRECATED writeWith "Please use createWith instead." #-}
829+{-# INLINE writeWith #-}
768830writeWith :: forall m a. (MonadIO m, Unbox a)
769831 => Int -> Fold m a (Array a)
770−-- writeWith n = FL.rmapM spliceArrays $ toArraysOf n
771−writeWith elemCount = unsafeFreeze <$> MA.writeWith elemCount
832+writeWith = createWith
772833
773834-- | Fold the whole input to a single array.
774835--
775836-- /Caution! Do not use this on infinite streams./
776837--
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." #-}
777844{-# INLINE write #-}
778845write :: forall m a. (MonadIO m, Unbox a) => Fold m a (Array a)
779−write = fmap unsafeFreeze MA.write
846+write = create
780847
781−-- | Like 'write' but creates a pinned array.
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
852+
853+{-# DEPRECATED pinnedWrite "Please use pinnedCreate instead." #-}
782854{-# INLINE pinnedWrite #-}
783855pinnedWrite :: forall m a. (MonadIO m, Unbox a) => Fold m a (Array a)
784−pinnedWrite = fmap unsafeFreeze MA.pinnedWrite
856+pinnedWrite = pinnedCreate
785857
786858-- | Fold "step" has a dependency on "initial", and each step is dependent on
787859-- the previous invocation of step due to state passing, finally extract
@@ -829,13 +901,19 @@ fromPureStream x = unsafePerformIO $ fmap (unsafeFreeze) (MA.fromPureStream x)
829901-- fromPureStream = runIdentity . D.fold (unsafeMakePure write)
830902-- fromPureStream = fromList . runIdentity . D.toList
831903
904+-- XXX This should be monadic.
905+
832906-- | Copy an immutable 'Ptr Word8' sequence into an array.
833907--
834908-- /Unsafe:/ The caller is responsible for safe addressing.
835909--
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.
836912fromPtrN :: Int -> Ptr Word8 -> Array Word8
837913fromPtrN n addr = unsafePerformIO $ fmap unsafeFreeze (MA.fromPtrN n addr)
838914
915+-- XXX This should be monadic.
916+
839917-- | Copy a null terminated immutable 'Addr#' Word8 sequence into an array.
840918--
841919-- /Unsafe:/ The caller is responsible for safe addressing.
@@ -846,9 +924,15 @@ fromPtrN n addr = unsafePerformIO $ fmap unsafeFreeze (MA.fromPtrN n addr)
846924-- >>> Array.toList $ Array.fromByteStr# "\1\2\3\0"#
847925-- [1,2,3]
848926--
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.
849929fromByteStr# :: Addr# -> Array Word8
850930fromByteStr# addr = unsafePerformIO $ fmap unsafeFreeze (MA.fromByteStr# addr)
851931
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.
852936fromByteStr :: Ptr Word8 -> Array Word8
853937fromByteStr (Ptr addr#) = fromByteStr# addr#
854938
@@ -945,11 +1029,16 @@ MK_EQ_INSTANCE(Char)
9451029MK_EQ_INSTANCE(Word8)
9461030MK_EQ_INSTANCE(Word16)
9471031MK_EQ_INSTANCE(Word32)
1032+
1033+-- XXX The Word64 default instance should be as fast because we are comparing
1034+-- 64-bit at a time.
9481035MK_EQ_INSTANCE(Word64)
9491036MK_EQ_INSTANCE(Int)
9501037MK_EQ_INSTANCE(Int8)
9511038MK_EQ_INSTANCE(Int16)
9521039MK_EQ_INSTANCE(Int32)
1040+
1041+-- XXX The Int64 default instance should be as fast.
9531042MK_EQ_INSTANCE(Int64)
9541043
9551044-- | If the type allows a byte-by-byte comparison this instance can be
@@ -1039,12 +1128,20 @@ instance Foldable Array where
10391128instance Unbox a => Semigroup (Array a) where
10401129 arr1 <> arr2 = unsafePerformIO $ splice arr1 arr2
10411130
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." #-}
10421139nil ::
10431140#ifdef DEVBUILD
10441141 Unbox a =>
10451142#endif
10461143 Array a
1047−nil = Array Unboxed.nil 0 0
1144+nil = empty
10481145
10491146instance Unbox a => Monoid (Array a) where
10501147 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+20−3
@@ -14,6 +14,7 @@ module Streamly.Internal.Data.MutArray
1414 , sliceIndexerFromLen
1515 , slicerFromLen
1616 , compactLE
17+ , pinnedCompactLE
1718 , compactOnByte
1819 , compactOnByteSuffix
1920 -- * Unboxed IORef
@@ -29,6 +30,7 @@ where
2930
3031import Control.Monad.IO.Class (MonadIO(..))
3132import Data.Word (Word8)
33+import Streamly.Internal.Data.MutByteArray.Type (PinnedState(..))
3234import Streamly.Internal.Data.Stream.Type (Stream)
3335import Streamly.Internal.Data.Unbox (Unbox)
3436import Streamly.Internal.Data.Unfold.Type (Unfold(..))
@@ -91,15 +93,30 @@ getSlicesFromLen = slicerFromLen
9193-- Compacting Streams of Arrays
9294-------------------------------------------------------------------------------
9395
94−-- | Scan @compactLE n@ coalesces adjacent arrays in the input stream
95−-- only if the combined size would be less than or equal to n.
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.
96105{-# INLINE compactLE #-}
97106compactLE :: (MonadIO m, Unbox a) =>
98107 Int -> Stream m (MutArray a) -> Stream m (MutArray a)
99108-- XXX compactLE can be moved to MutArray/Type if we are not using the parser
100109-- to implement it.
110+compactLE = compactLeAs Unpinned
111+-- The parser version turns out to be a little bit slower.
101112-- compactLE n = Stream.catRights . Stream.parseManyD (pCompactLE n)
102−compactLE = rCompactLE
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)
103120
104121data SplitState s arr
105122 = Initial s
core/src/Streamly/Internal/Data/MutArray/Generic.hs+59−24
@@ -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-- |
@@ -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,36 +619,47 @@ 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"
662+ when (elemCount < 0) $ error "createWith: elemCount is negative"
639663 new elemCount
640664
641665 step arr@(MutArray _ start end bound) x
@@ -649,16 +673,27 @@ writeWith elemCount = FL.rmapM extract $ FL.foldlM' step initial
649673 -- 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@
core/src/Streamly/Internal/Data/MutArray/Type.hs+453−230
@@ -40,22 +40,22 @@ module Streamly.Internal.Data.MutArray.Type
4040
4141 -- ** Casting
4242 , cast
43− , castUnsafe
43+ , castUnsafe -- XXX unsafeCast
4444 , asBytes
45− , asPtrUnsafe
46− , asUnpinnedPtrUnsafe
45+ , unsafePinnedAsPtr
46+ , unsafeAsPtr
4747
4848 -- ** Construction
49− , nil
49+ , empty
5050
5151 -- *** New
5252 -- | New arrays are always empty arrays with some reserve capacity to
5353 -- extend the length without reallocating.
54− , new
55− , newArrayWith
56− , pinnedNew
57− , pinnedNewBytes
58− , pinnedNewAligned
54+ , emptyOf
55+ , newArrayWith -- emptyAlignedWith
56+ , pinnedEmptyOf
57+ , pinnedNewAligned -- XXX not required
58+ -- , new -- uninitialized array of specified length
5959
6060 -- *** Cloning
6161 , clone
@@ -63,27 +63,25 @@ module Streamly.Internal.Data.MutArray.Type
6363
6464 -- *** Slicing
6565 -- | Get a subarray without copying
66− , getSliceUnsafe
66+ , getSliceUnsafe -- XXX unsafeGetSlice
6767 , getSlice
6868 , splitAt -- XXX should be able to express using getSlice
6969 , breakOn
7070
7171 -- *** Stream Folds
7272 , ArrayUnsafe (..)
73− , writeNWithUnsafe
74− , writeNWith
75− , writeNUnsafe
76− , pinnedWriteNUnsafe
77− , writeN
78− , pinnedWriteN
79− , pinnedWriteNAligned
80−
81− , writeWith
82− , write
83− , pinnedWrite
84−
85− , writeRevN
86− -- , writeRev
73+ , unsafeCreateOfWith
74+ , unsafeCreateOf
75+ , unsafePinnedCreateOf
76+ , pinnedCreateOf
77+ , createOfWith
78+ , createOf
79+ , revCreateOf
80+
81+ , pinnedCreate
82+ , createWith
83+ , create
84+ -- , revCreate
8785
8886 -- *** From containers
8987 , fromListN
@@ -99,11 +97,11 @@ module Streamly.Internal.Data.MutArray.Type
9997 , fromByteStr#
10098 , fromPtrN
10199 , fromChunksK
102− , fromChunksRealloced
100+ , fromChunksRealloced -- fromSmallChunks
103101
104102 -- ** Random writes
105103 , putIndex
106− , putIndexUnsafe
104+ , putIndexUnsafe -- XXX unsafePutIndex
107105 , putIndices
108106 -- , putFromThenTo
109107 -- , putFrom -- start writing at the given position
@@ -111,7 +109,7 @@ module Streamly.Internal.Data.MutArray.Type
111109 -- , putFromTo
112110 -- , putFromRev
113111 -- , putUptoRev
114− , modifyIndexUnsafe
112+ , modifyIndexUnsafe -- XXX unsafeModifyIndex
115113 , modifyIndex
116114 , modifyIndices
117115 , modify
@@ -122,9 +120,9 @@ module Streamly.Internal.Data.MutArray.Type
122120
123121 -- *** Indexing
124122 , getIndex
125− , getIndexUnsafe
123+ , getIndexUnsafe -- XXX unsafeGetIndex
126124 -- , getFromThenTo
127− , getIndexRev
125+ , getIndexRev -- getRevIndex?
128126 , indexReader
129127 , indexReaderWith
130128
@@ -195,20 +193,20 @@ module Streamly.Internal.Data.MutArray.Type
195193 , snoc
196194 , snocLinear
197195 , snocMay
198− , snocUnsafe
196+ , snocUnsafe -- XXX unsafeSnoc
199197
200198 -- *** Appending streams
201− , writeAppendNUnsafe
202− , writeAppendN
203− , writeAppendWith
204− , writeAppend
199+ , unsafeAppendN
200+ , appendN
201+ , appendWith
202+ , append
205203
206204 -- *** Appending arrays
207205 , spliceCopy
208206 , spliceWith
209207 , splice
210208 , spliceExp
211− , spliceUnsafe
209+ , spliceUnsafe -- XXX unsafeSplice
212210 -- , putSlice
213211 -- , appendSlice
214212 -- , appendSliceFrom
@@ -216,12 +214,12 @@ module Streamly.Internal.Data.MutArray.Type
216214 -- ** Serialization using Unbox
217215 , pokeAppend
218216 , pokeAppendMay
219− , pokeSkipUnsafe
217+ , pokeSkipUnsafe -- unsafePokeSkip
220218
221219 -- ** Deserialization using Unbox
222220 , peekUncons
223− , peekUnconsUnsafe
224− , peekSkipUnsafe
221+ , peekUnconsUnsafe -- unsafePeekUncons
222+ , peekSkipUnsafe -- unsafePeekSkip
225223
226224 -- Arrays of arrays
227225 -- We can add dimensionality parameter to the array type to get
@@ -244,6 +242,8 @@ module Streamly.Internal.Data.MutArray.Type
244242
245243 -- *** Concat
246244 -- | Append the arrays in a stream to form a stream of elements.
245+ , concatWith
246+ , concatRevWith
247247 , concat
248248 , concatRev
249249
@@ -251,9 +251,12 @@ module Streamly.Internal.Data.MutArray.Type
251251 -- | Append the arrays in a stream to form a stream of larger arrays.
252252 , SpliceState (..)
253253 , pCompactLE
254− , rCompactLE
254+ , pPinnedCompactLE
255+ , compactLeAs
255256 , fCompactGE
257+ , fPinnedCompactGE
256258 , lCompactGE
259+ , lPinnedCompactGE
257260 , compactGE
258261 , compactEQ
259262
@@ -264,6 +267,7 @@ module Streamly.Internal.Data.MutArray.Type
264267 , c_memchr
265268
266269 -- * Deprecated
270+ , asPtrUnsafe
267271 , writeChunks
268272 , flattenArrays
269273 , flattenArraysRev
@@ -275,6 +279,25 @@ module Streamly.Internal.Data.MutArray.Type
275279 , getIndicesWith
276280 , resize
277281 , 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
278301 )
279302where
280303
@@ -456,6 +479,8 @@ isPinned MutArray{..} = Unboxed.isPinned arrContents
456479-- we need more than that. See stg_pinnedNewAlignedByteArrayzh and
457480-- allocatePinned in GHC source.
458481
482+-- XXX Rename to emptyAlignedWith, alignSize should be first arg.
483+
459484-- | @newArrayWith allocator alignment count@ allocates a new array of zero
460485-- length and with a capacity to hold @count@ elements, using @allocator
461486-- size alignment@ as the memory allocator function.
@@ -479,12 +504,25 @@ newArrayWith alloc alignSize count = do
479504 , arrBound = size
480505 }
481506
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." #-}
482520nil ::
483521#ifdef DEVBUILD
484522 Unbox a =>
485523#endif
486524 MutArray a
487−nil = MutArray Unboxed.nil 0 0 0
525+nil = empty
488526
489527{-# INLINE newBytesAs #-}
490528newBytesAs :: MonadIO m =>
@@ -507,6 +545,7 @@ newBytesAs ps bytes = do
507545--
508546-- /Pre-release/
509547{-# INLINE pinnedNewBytes #-}
548+{-# DEPRECATED pinnedNewBytes "Please use pinnedEmptyOf with appropriate calculation" #-}
510549pinnedNewBytes :: MonadIO m =>
511550#ifdef DEVBUILD
512551 Unbox a =>
@@ -531,21 +570,31 @@ newAs ps =
531570 (error "new: alignment is not used in unpinned arrays.")
532571
533572-- XXX can unaligned allocation be more efficient when alignment is not needed?
534−--
535−-- | Allocates an empty pinned array that can hold 'count' items. The memory of
536−-- the array is uninitialized and the allocation is aligned as per the 'Unboxed'
537−-- instance of the type.
538−--
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." #-}
539582{-# INLINE pinnedNew #-}
540583pinnedNew :: forall m a. (MonadIO m, Unbox a) => Int -> m (MutArray a)
541−pinnedNew = newAs Pinned
584+pinnedNew = pinnedEmptyOf
542585
543−-- | Allocates an empty unpinned array that can hold 'count' items. The memory
544−-- of the array is uninitialized.
586+-- | Allocates an unpinned array of zero length but growable to the specified
587+-- capacity without reallocation.
545588--
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." #-}
546595{-# INLINE new #-}
547596new :: (MonadIO m, Unbox a) => Int -> m (MutArray a)
548−new = newAs Unpinned
597+new = emptyOf
549598
550599-------------------------------------------------------------------------------
551600-- Random writes
@@ -793,15 +842,15 @@ roundDownTo elemSize size = size - (size `mod` elemSize)
793842-- Since this is not inlined Unboxed consrraint leads to dictionary passing
794843-- which complicates some inspection tests.
795844--
796−{-# NOINLINE reallocExplicit #-}
797−reallocExplicit :: Int -> Int -> MutArray a -> IO (MutArray a)
798−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
799848 assertM(arrEnd <= arrBound)
800849
801850 -- Allocate new array
802851 let newCapMaxInBytes = roundUpLargeArray newCapacityInBytes
803852 contents <-
804− if Unboxed.isPinned arrContents
853+ if ps == Pinned
805854 then Unboxed.pinnedNew newCapMaxInBytes
806855 else Unboxed.new newCapMaxInBytes
807856 let !(MutByteArray mbarrFrom#) = arrContents
@@ -841,7 +890,12 @@ reallocExplicit elemSize newCapacityInBytes MutArray{..} = do
841890-- If the original array is pinned, the newly allocated array is also pinned.
842891{-# INLINABLE realloc #-}
843892realloc :: forall m a. (MonadIO m, Unbox a) => Int -> MutArray a -> m (MutArray a)
844−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
845899
846900-- | @reallocWith label capSizer minIncrBytes array@. The label is used
847901-- in error messages and the capSizer is used to determine the capacity of the
@@ -890,6 +944,7 @@ grow nElems arr@MutArray{..} = do
890944 else realloc req arr
891945
892946{-# DEPRECATED resize "Please use grow instead." #-}
947+{-# INLINE resize #-}
893948resize :: forall m a. (MonadIO m, Unbox a) =>
894949 Int -> MutArray a -> m (MutArray a)
895950resize = grow
@@ -913,6 +968,7 @@ growExp nElems arr@MutArray{..} = do
913968 else realloc req1 arr
914969
915970{-# DEPRECATED resizeExp "Please use growExp instead." #-}
971+{-# INLINE resizeExp #-}
916972resizeExp :: forall m a. (MonadIO m, Unbox a) =>
917973 Int -> MutArray a -> m (MutArray a)
918974resizeExp = growExp
@@ -1246,6 +1302,7 @@ indexReaderWith liftio (D.Stream stepi sti) = Unfold step inject
12461302 D.Stop -> return D.Stop
12471303
12481304{-# DEPRECATED getIndicesWith "Please use indexReaderWith instead." #-}
1305+{-# INLINE getIndicesWith #-}
12491306getIndicesWith :: (Monad m, Unbox a) =>
12501307 (forall b. IO b -> m b) -> D.Stream m Int -> Unfold m (MutArray a) a
12511308getIndicesWith = indexReaderWith
@@ -1261,6 +1318,7 @@ indexReader = indexReaderWith liftIO
12611318
12621319-- XXX DO NOT REMOVE, change the signature to use Stream instead of unfold
12631320{-# DEPRECATED getIndices "Please use indexReader instead." #-}
1321+{-# INLINE getIndices #-}
12641322getIndices :: (MonadIO m, Unbox a) => Stream m Int -> Unfold m (MutArray a) a
12651323getIndices = indexReader
12661324
@@ -1557,7 +1615,7 @@ chunksOfAs ps n (D.Stream step state) =
15571615--
15581616-- Same as the following but may be more efficient:
15591617--
1560−-- >>> chunksOf n = Stream.foldMany (MutArray.writeN n)
1618+-- >>> chunksOf n = Stream.foldMany (MutArray.createOf n)
15611619--
15621620-- /Pre-release/
15631621{-# INLINE_NORMAL chunksOf #-}
@@ -1566,14 +1624,14 @@ chunksOf :: forall m a. (MonadIO m, Unbox a)
15661624-- XXX the idiomatic implementation leads to large regression in the D.reverse'
15671625-- benchmark. It seems it has difficulty producing optimized code when
15681626-- converting to StreamK. Investigate GHC optimizations.
1569−-- chunksOf n = D.foldMany (writeN n)
1627+-- chunksOf n = D.foldMany (createOf n)
15701628chunksOf = chunksOfAs Unpinned
15711629
15721630-- | Like 'chunksOf' but creates pinned arrays.
15731631{-# INLINE_NORMAL pinnedChunksOf #-}
15741632pinnedChunksOf :: forall m a. (MonadIO m, Unbox a)
15751633 => Int -> D.Stream m a -> D.Stream m (MutArray a)
1576−-- pinnedChunksOf n = D.foldMany (pinnedWriteN n)
1634+-- pinnedChunksOf n = D.foldMany (pinnedCreateOf n)
15771635pinnedChunksOf = chunksOfAs Pinned
15781636
15791637-- | When we are buffering a stream of unknown size into an array we do not
@@ -1606,16 +1664,10 @@ data FlattenState s contents a =
16061664 OuterLoop s
16071665 | InnerLoop s contents !Int !Int
16081666
1609−-- | Use the "reader" unfold instead.
1610−--
1611−-- @concat = unfoldMany reader@
1612−--
1613−-- We can try this if there are any fusion issues in the unfold.
1614−--
1615−{-# INLINE_NORMAL concat #-}
1616−concat :: forall m a. (MonadIO m, Unbox a)
1617− => D.Stream m (MutArray a) -> D.Stream m a
1618−concat (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)
16191671
16201672 where
16211673
@@ -1632,24 +1684,30 @@ concat (D.Stream step state) = D.Stream step' (OuterLoop state)
16321684 return $ D.Skip $ OuterLoop st
16331685
16341686 step' _ (InnerLoop st contents p end) = do
1635− x <- liftIO $ peekAt p contents
1687+ x <- liftio $ peekAt p contents
16361688 return $ D.Yield x (InnerLoop st contents (INDEX_NEXT(p,a)) end)
16371689
1638−{-# DEPRECATED flattenArrays "Please use \"unfoldMany reader\" instead." #-}
1639−flattenArrays :: forall m a. (MonadIO m, Unbox a)
1640− => D.Stream m (MutArray a) -> D.Stream m a
1641−flattenArrays = concat
1642−
1643−-- | Use the "readerRev" unfold instead.
1690+-- | Use the "reader" unfold instead.
16441691--
1645−-- @concat = unfoldMany readerRev@
1692+-- @concat = unfoldMany reader@
16461693--
16471694-- We can try this if there are any fusion issues in the unfold.
16481695--
1649−{-# INLINE_NORMAL concatRev #-}
1650−concatRev :: 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)
16511704 => D.Stream m (MutArray a) -> D.Stream m a
1652−concatRev (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)
16531711
16541712 where
16551713
@@ -1667,11 +1725,23 @@ concatRev (D.Stream step state) = D.Stream step' (OuterLoop state)
16671725 return $ D.Skip $ OuterLoop st
16681726
16691727 step' _ (InnerLoop st contents p start) = do
1670− x <- liftIO $ peekAt p contents
1728+ x <- liftio $ peekAt p contents
16711729 let cur = INDEX_PREV(p,a)
16721730 return $ D.Yield x (InnerLoop st contents cur start)
16731731
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+
16741743{-# DEPRECATED flattenArraysRev "Please use \"unfoldMany readerRev\" instead." #-}
1744+{-# INLINE flattenArraysRev #-}
16751745flattenArraysRev :: forall m a. (MonadIO m, Unbox a)
16761746 => D.Stream m (MutArray a) -> D.Stream m a
16771747flattenArraysRev = concatRev
@@ -1903,7 +1973,7 @@ foldr f z arr = D.foldr f z $ read arr
19031973
19041974-- XXX Keep the bound intact to not lose any free space? Perf impact?
19051975
1906−-- | @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
19071977-- array.
19081978--
19091979-- Unsafe: Do not drive the fold beyond @n@ elements, it will lead to memory
@@ -1912,13 +1982,12 @@ foldr f z arr = D.foldr f z $ read arr
19121982-- Any free space left in the array after appending @n@ elements is lost.
19131983--
19141984-- /Internal/
1915−{-# INLINE_NORMAL writeAppendNUnsafe #-}
1916−writeAppendNUnsafe :: forall m a. (MonadIO m, Unbox a) =>
1985+{-# INLINE_NORMAL unsafeAppendN #-}
1986+unsafeAppendN :: forall m a. (MonadIO m, Unbox a) =>
19171987 Int
19181988 -> m (MutArray a)
19191989 -> Fold m a (MutArray a)
1920−writeAppendNUnsafe n action =
1921− fmap fromArrayUnsafe $ FL.foldlM' step initial
1990+unsafeAppendN n action = fmap fromArrayUnsafe $ FL.foldlM' step initial
19221991
19231992 where
19241993
@@ -1931,7 +2000,7 @@ writeAppendNUnsafe n action =
19312000 -- otherwise we lose that space.
19322001 arr1 <-
19332002 if free < needed
1934− then noinline reallocWith "writeAppendNUnsafeWith" (+ needed) needed arr
2003+ then noinline reallocWith "unsafeAppendN" (+ needed) needed arr
19352004 else return arr
19362005 return $ toArrayUnsafe arr1
19372006
@@ -1939,30 +2008,51 @@ writeAppendNUnsafe n action =
19392008 liftIO $ pokeAt end contents x
19402009 return $ ArrayUnsafe contents start (INDEX_NEXT(end,a))
19412010
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+
19422019-- | Append @n@ elements to an existing array. Any free space left in the array
19432020-- after appending @n@ elements is lost.
19442021--
1945−-- >>> writeAppendN n initial = Fold.take n (MutArray.writeAppendNUnsafe n initial)
2022+-- >>> appendN n initial = Fold.take n (MutArray.unsafeAppendN n initial)
19462023--
1947−{-# 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 #-}
19482032writeAppendN :: forall m a. (MonadIO m, Unbox a) =>
19492033 Int -> m (MutArray a) -> Fold m a (MutArray a)
1950−writeAppendN n initial = FL.take n (writeAppendNUnsafe n initial)
2034+writeAppendN = appendN
19512035
1952−-- | @writeAppendWith realloc action@ mutates the array generated by @action@ to
2036+-- | @appendWith realloc action@ mutates the array generated by @action@ to
19532037-- append the input stream. If there is no reserved space available in the
19542038-- array it is reallocated to a size in bytes determined by @realloc oldSize@,
19552039-- where @oldSize@ is the current size of the array in bytes.
19562040--
19572041-- Note that the returned array may be a mutated version of original array.
19582042--
1959−-- >>> writeAppendWith sizer = Fold.foldlM' (MutArray.snocWith sizer)
2043+-- >>> appendWith sizer = Fold.foldlM' (MutArray.snocWith sizer)
19602044--
19612045-- /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." #-}
19622052{-# INLINE writeAppendWith #-}
19632053writeAppendWith :: forall m a. (MonadIO m, Unbox a) =>
19642054 (Int -> Int) -> m (MutArray a) -> Fold m a (MutArray a)
1965−writeAppendWith sizer = FL.foldlM' (snocWith sizer)
2055+writeAppendWith = appendWith
19662056
19672057-- | @append action@ mutates the array generated by @action@ to append the
19682058-- input stream. If there is no reserved space available in the array it is
@@ -1970,26 +2060,33 @@ writeAppendWith sizer = FL.foldlM' (snocWith sizer)
19702060--
19712061-- Note that the returned array may be a mutated version of original array.
19722062--
1973−-- >>> writeAppend = MutArray.writeAppendWith (* 2)
2063+-- >>> append = MutArray.appendWith (* 2)
19742064--
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." #-}
19752072{-# INLINE writeAppend #-}
19762073writeAppend :: forall m a. (MonadIO m, Unbox a) =>
19772074 m (MutArray a) -> Fold m a (MutArray a)
1978−writeAppend = writeAppendWith (* 2)
2075+writeAppend = append
19792076
19802077-- XXX We can carry bound as well in the state to make sure we do not lose the
19812078-- remaining capacity. Need to check perf impact.
19822079--
1983−-- | Like 'writeNUnsafe' but takes a new array allocator @alloc size@ function
1984−-- as argument.
2080+-- | Like 'unsafeCreateOf' but takes a new array allocator @alloc size@
2081+-- function as argument.
19852082--
1986−-- >>> writeNWithUnsafe alloc n = MutArray.writeAppendNUnsafe (alloc n) n
2083+-- >>> unsafeCreateOfWith alloc n = MutArray.unsafeAppendN (alloc n) n
19872084--
19882085-- /Pre-release/
1989−{-# INLINE_NORMAL writeNWithUnsafe #-}
1990−writeNWithUnsafe :: forall m a. (MonadIO m, Unbox a)
2086+{-# INLINE_NORMAL unsafeCreateOfWith #-}
2087+unsafeCreateOfWith :: forall m a. (MonadIO m, Unbox a)
19912088 => (Int -> m (MutArray a)) -> Int -> Fold m a (MutArray a)
1992−writeNWithUnsafe alloc n = fromArrayUnsafe <$> FL.foldlM' step initial
2089+unsafeCreateOfWith alloc n = fromArrayUnsafe <$> FL.foldlM' step initial
19932090
19942091 where
19952092
@@ -2000,40 +2097,64 @@ writeNWithUnsafe alloc n = fromArrayUnsafe <$> FL.foldlM' step initial
20002097 return
20012098 $ ArrayUnsafe contents start (INDEX_NEXT(end,a))
20022099
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+
20032106{-# INLINE_NORMAL writeNUnsafeAs #-}
20042107writeNUnsafeAs :: forall m a. (MonadIO m, Unbox a)
20052108 => PinnedState -> Int -> Fold m a (MutArray a)
2006−writeNUnsafeAs ps = writeNWithUnsafe (newAs ps)
2109+writeNUnsafeAs ps = unsafeCreateOfWith (newAs ps)
20072110
2008−-- | 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
20092112-- driver must not call the step function more than 'n' times otherwise it will
20102113-- corrupt the memory and crash. This function exists mainly because any
20112114-- conditional in the step function blocks fusion causing 10x performance
20122115-- slowdown.
20132116--
2014−-- >>> writeNUnsafe = MutArray.writeNWithUnsafe MutArray.new
2117+-- >>> unsafeCreateOf = MutArray.unsafeCreateOfWith MutArray.emptyOf
20152118--
2016−{-# 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 #-}
20172126writeNUnsafe :: forall m a. (MonadIO m, Unbox a)
20182127 => Int -> Fold m a (MutArray a)
2019−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
20202135
2021−-- | Like 'writeNUnsafe' but creates a pinned array.
2022−{-# INLINE_NORMAL pinnedWriteNUnsafe #-}
2136+{-# DEPRECATED pinnedWriteNUnsafe "Please use unsafePinnedCreateOf instead." #-}
2137+{-# INLINE pinnedWriteNUnsafe #-}
20232138pinnedWriteNUnsafe :: forall m a. (MonadIO m, Unbox a)
20242139 => Int -> Fold m a (MutArray a)
2025−pinnedWriteNUnsafe = writeNUnsafeAs Pinned
2140+pinnedWriteNUnsafe = unsafePinnedCreateOf
20262141
2027−-- | @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
20282143-- allocated using the @alloc@ function.
20292144--
2030−-- >>> writeNWith alloc n = Fold.take n (MutArray.writeNWithUnsafe alloc n)
2031−-- >>> 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
20322147--
2033−{-# 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 #-}
20342155writeNWith :: forall m a. (MonadIO m, Unbox a)
20352156 => (Int -> m (MutArray a)) -> Int -> Fold m a (MutArray a)
2036−writeNWith alloc n = FL.take n (writeNWithUnsafe alloc n)
2157+writeNWith = createOfWith
20372158
20382159{-# INLINE_NORMAL writeNAs #-}
20392160writeNAs ::
@@ -2041,28 +2162,42 @@ writeNAs ::
20412162 => PinnedState
20422163 -> Int
20432164 -> Fold m a (MutArray a)
2044−writeNAs ps = writeNWith (newAs ps)
2165+writeNAs ps = createOfWith (newAs ps)
20452166
2046−-- | @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
20472168-- 'MutArray'.
20482169--
2049−-- >>> writeN = MutArray.writeNWith MutArray.new
2050−-- >>> writeN n = Fold.take n (MutArray.writeNUnsafe n)
2051−-- >>> 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)
20522173--
2053−{-# 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 #-}
20542181writeN :: forall m a. (MonadIO m, Unbox a) => Int -> Fold m a (MutArray a)
2055−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
20562191
2057−-- | Like 'writeN' but creates a pinned array.
2058−{-# INLINE_NORMAL pinnedWriteN #-}
2192+{-# DEPRECATED pinnedWriteN "Please use pinnedCreateOf instead." #-}
2193+{-# INLINE pinnedWriteN #-}
20592194pinnedWriteN ::
20602195 forall m a. (MonadIO m, Unbox a)
20612196 => Int
20622197 -> Fold m a (MutArray a)
2063−pinnedWriteN = writeNAs Pinned
2198+pinnedWriteN = pinnedCreateOf
20642199
2065−-- | Like writeNWithUnsafe but writes the array in reverse order.
2200+-- | Like unsafeCreateOfWith but writes the array in reverse order.
20662201--
20672202-- /Internal/
20682203{-# INLINE_NORMAL writeRevNWithUnsafe #-}
@@ -2083,7 +2218,7 @@ writeRevNWithUnsafe alloc n = fromArrayUnsafe <$> FL.foldlM' step initial
20832218 return
20842219 $ ArrayUnsafe contents ptr end
20852220
2086−-- | Like writeNWith but writes the array in reverse order.
2221+-- | Like createOfWith but writes the array in reverse order.
20872222--
20882223-- /Internal/
20892224{-# INLINE_NORMAL writeRevNWith #-}
@@ -2091,31 +2226,36 @@ writeRevNWith :: forall m a. (MonadIO m, Unbox a)
20912226 => (Int -> m (MutArray a)) -> Int -> Fold m a (MutArray a)
20922227writeRevNWith alloc n = FL.take n (writeRevNWithUnsafe alloc n)
20932228
2094−-- | Like writeN but writes the array in reverse order.
2229+-- | Like 'createOf' but writes the array in reverse order.
20952230--
20962231-- /Pre-release/
2097−{-# 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 #-}
20982238writeRevN :: forall m a. (MonadIO m, Unbox a) => Int -> Fold m a (MutArray a)
2099−writeRevN = writeRevNWith new
2239+writeRevN = revCreateOf
21002240
21012241-- | @pinnedWriteNAligned align n@ folds a maximum of @n@ elements from the
21022242-- input stream to a 'MutArray' aligned to the given size.
21032243--
2104−-- >>> pinnedWriteNAligned align = MutArray.writeNWith (MutArray.pinnedNewAligned align)
2105−-- >>> 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)
21062246--
21072247-- /Pre-release/
21082248--
21092249{-# INLINE_NORMAL pinnedWriteNAligned #-}
21102250pinnedWriteNAligned :: forall m a. (MonadIO m, Unbox a)
21112251 => Int -> Int -> Fold m a (MutArray a)
2112−pinnedWriteNAligned align = writeNWith (pinnedNewAligned align)
2252+pinnedWriteNAligned align = createOfWith (pinnedNewAligned align)
21132253
21142254-- XXX Buffer to a list instead?
21152255
21162256-- | Buffer a stream into a stream of arrays.
21172257--
2118−-- >>> buildChunks n = Fold.many (MutArray.writeN n) Fold.toStreamK
2258+-- >>> buildChunks n = Fold.many (MutArray.createOf n) Fold.toStreamK
21192259--
21202260-- Breaking an array into an array stream can be useful to consume a large
21212261-- array sequentially such that memory of the array is released incrementatlly.
@@ -2127,9 +2267,10 @@ pinnedWriteNAligned align = writeNWith (pinnedNewAligned align)
21272267{-# INLINE_NORMAL buildChunks #-}
21282268buildChunks :: (MonadIO m, Unbox a) =>
21292269 Int -> Fold m a (StreamK n (MutArray a))
2130−buildChunks n = FL.many (writeN n) FL.toStreamK
2270+buildChunks n = FL.many (createOf n) FL.toStreamK
21312271
21322272{-# DEPRECATED writeChunks "Please use buildChunks instead." #-}
2273+{-# INLINE writeChunks #-}
21332274writeChunks :: (MonadIO m, Unbox a) =>
21342275 Int -> Fold m a (StreamK n (MutArray a))
21352276writeChunks = buildChunks
@@ -2137,29 +2278,29 @@ writeChunks = buildChunks
21372278{-# INLINE_NORMAL writeWithAs #-}
21382279writeWithAs :: forall m a. (MonadIO m, Unbox a)
21392280 => PinnedState -> Int -> Fold m a (MutArray a)
2140−-- writeWithAs ps n = FL.rmapM rightSize $ writeAppendWith (* 2) (newAs ps n)
2281+-- writeWithAs ps n = FL.rmapM rightSize $ appendWith (* 2) (newAs ps n)
21412282writeWithAs ps elemCount =
21422283 FL.rmapM extract $ FL.foldlM' step initial
21432284
21442285 where
21452286
21462287 initial = do
2147− when (elemCount < 0) $ error "writeWith: elemCount is negative"
2288+ when (elemCount < 0) $ error "createWith: elemCount is negative"
21482289 newAs ps elemCount
21492290
21502291 step arr@(MutArray _ start end bound) x
21512292 | INDEX_NEXT(end,a) > bound = do
21522293 let oldSize = end - start
21532294 newSize = max (oldSize * 2) 1
2154− arr1 <- liftIO $ reallocExplicit (SIZE_OF(a)) newSize arr
2295+ arr1 <- liftIO $ reallocExplicitAs ps (SIZE_OF(a)) newSize arr
21552296 snocUnsafe arr1 x
21562297 step arr x = snocUnsafe arr x
21572298
21582299 extract = liftIO . rightSize
21592300
2160−-- XXX Compare writeWith with fromStreamD which uses an array of streams
2301+-- XXX Compare createWith with fromStreamD which uses an array of streams
21612302-- implementation. We can write this using buildChunks above if that is faster.
2162−-- If writeWith is faster then we should use that to implement
2303+-- If createWith is faster then we should use that to implement
21632304-- fromStreamD.
21642305--
21652306-- XXX The realloc based implementation needs to make one extra copy if we use
@@ -2174,39 +2315,56 @@ writeWithAs ps elemCount =
21742315-- XXX check if GHC's memory allocator is efficient enough. We can try the C
21752316-- malloc to compare against.
21762317
2177−-- | @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
21782319-- starts at a size big enough to hold minCount elements, the size is doubled
21792320-- every time the array needs to be grown.
21802321--
21812322-- /Caution! Do not use this on infinite streams./
21822323--
2183−-- >>> f n = MutArray.writeAppendWith (* 2) (MutArray.new n)
2184−-- >>> writeWith n = Fold.rmapM MutArray.rightSize (f n)
2185−-- >>> 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)
21862327--
21872328-- /Pre-release/
2188−{-# 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 #-}
21892337writeWith :: forall m a. (MonadIO m, Unbox a)
21902338 => Int -> Fold m a (MutArray a)
2191−-- writeWith n = FL.rmapM rightSize $ writeAppendWith (* 2) (new n)
2192−writeWith = writeWithAs Unpinned
2339+writeWith = createWith
21932340
21942341-- | Fold the whole input to a single array.
21952342--
2196−-- Same as 'writeWith' using an initial array size of 'arrayChunkBytes' bytes
2343+-- Same as 'createWith' using an initial array size of 'arrayChunkBytes' bytes
21972344-- rounded up to the element size.
21982345--
21992346-- /Caution! Do not use this on infinite streams./
22002347--
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." #-}
22012354{-# INLINE write #-}
22022355write :: forall m a. (MonadIO m, Unbox a) => Fold m a (MutArray a)
2203−write = writeWith (allocBytesToElemCount (undefined :: a) arrayChunkBytes)
2356+write = create
22042357
2205−-- | 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." #-}
22062365{-# INLINE pinnedWrite #-}
22072366pinnedWrite :: forall m a. (MonadIO m, Unbox a) => Fold m a (MutArray a)
2208−pinnedWrite =
2209− writeWithAs Pinned (allocBytesToElemCount (undefined :: a) arrayChunkBytes)
2367+pinnedWrite = pinnedCreate
22102368
22112369-------------------------------------------------------------------------------
22122370-- construct from streams, known size
@@ -2226,17 +2384,18 @@ fromStreamDNAs ps limit str = do
22262384 liftIO $ pokeAt ptr arrContents x
22272385 return $ INDEX_NEXT(ptr,a)
22282386
2229−-- | Use the 'writeN' fold instead.
2387+-- | Use the 'createOf' fold instead.
22302388--
2231−-- >>> fromStreamN n = Stream.fold (MutArray.writeN n)
2389+-- >>> fromStreamN n = Stream.fold (MutArray.createOf n)
22322390--
22332391{-# INLINE_NORMAL fromStreamN #-}
22342392fromStreamN :: forall m a. (MonadIO m, Unbox a)
22352393 => Int -> D.Stream m a -> m (MutArray a)
2236−-- fromStreamDN n = D.fold (writeN n)
2394+-- fromStreamDN n = D.fold (createOf n)
22372395fromStreamN = fromStreamDNAs Unpinned
22382396
22392397{-# DEPRECATED fromStreamDN "Please use fromStreamN instead." #-}
2398+{-# INLINE fromStreamDN #-}
22402399fromStreamDN :: forall m a. (MonadIO m, Unbox a)
22412400 => Int -> D.Stream m a -> m (MutArray a)
22422401fromStreamDN = fromStreamN
@@ -2259,20 +2418,20 @@ pinnedFromListN n xs = fromStreamDNAs Pinned n $ D.fromList xs
22592418-- /Pre-release/
22602419{-# INLINE fromListRevN #-}
22612420fromListRevN :: (MonadIO m, Unbox a) => Int -> [a] -> m (MutArray a)
2262−fromListRevN n xs = D.fold (writeRevN n) $ D.fromList xs
2421+fromListRevN n xs = D.fold (revCreateOf n) $ D.fromList xs
22632422
22642423-- | Convert a pure stream in Identity monad to a mutable array.
22652424{-# INLINABLE fromPureStreamN #-}
22662425fromPureStreamN :: (MonadIO m, Unbox a) =>
22672426 Int -> Stream Identity a -> m (MutArray a)
22682427fromPureStreamN n xs =
2269− D.fold (writeN n) $ D.morphInner (return . runIdentity) xs
2428+ D.fold (createOf n) $ D.morphInner (return . runIdentity) xs
22702429
22712430-- | Convert a pure stream in Identity monad to a mutable array.
22722431{-# INLINABLE fromPureStream #-}
22732432fromPureStream :: (MonadIO m, Unbox a) => Stream Identity a -> m (MutArray a)
22742433fromPureStream xs =
2275− D.fold write $ D.morphInner (return . runIdentity) xs
2434+ D.fold create $ D.morphInner (return . runIdentity) xs
22762435
22772436{-# INLINABLE fromPtrN #-}
22782437fromPtrN :: MonadIO m => Int -> Ptr Word8 -> m (MutArray Word8)
@@ -2281,7 +2440,7 @@ fromPtrN len addr = do
22812440 -- XXX We can implement a stream copy in a similar way by streaming Word64
22822441 -- first and then remaining Word8.
22832442 arr <- new len
2284− _ <- asUnpinnedPtrUnsafe arr
2443+ _ <- unsafeAsPtr arr
22852444 (\ptr -> liftIO $ c_memcpy ptr addr (fromIntegral len))
22862445 return (arr {arrEnd = len})
22872446
@@ -2297,7 +2456,7 @@ fromByteStr# addr = do
22972456 len <- liftIO $ c_strlen (Ptr addr)
22982457 let lenInt = fromIntegral len
22992458 arr <- new lenInt
2300− _ <- asUnpinnedPtrUnsafe arr (\ptr -> liftIO $ c_memcpy ptr (Ptr addr) len)
2459+ _ <- unsafeAsPtr arr (\ptr -> liftIO $ c_memcpy ptr (Ptr addr) len)
23012460 return (arr {arrEnd = lenInt})
23022461
23032462-------------------------------------------------------------------------------
@@ -2356,6 +2515,7 @@ fromChunksK :: (Unbox a, MonadIO m) =>
23562515fromChunksK = fromChunkskAs Unpinned
23572516
23582517{-# DEPRECATED fromArrayStreamK "Please use fromChunksK instead." #-}
2518+{-# INLINE fromArrayStreamK #-}
23592519fromArrayStreamK :: (Unbox a, MonadIO m) =>
23602520 StreamK m (MutArray a) -> m (MutArray a)
23612521fromArrayStreamK = fromChunksK
@@ -2367,7 +2527,7 @@ fromStreamDAs ps m =
23672527 arrayStreamKFromStreamDAs Unpinned m >>= fromChunkskAs ps
23682528
23692529-- | Create an 'Array' from a stream. This is useful when we want to create a
2370−-- 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
23712531-- as efficient when the size is already known.
23722532--
23732533-- Note that if the input stream is too large memory allocation for the array
@@ -2379,19 +2539,19 @@ fromStreamDAs ps m =
23792539fromStream :: (MonadIO m, Unbox a) => Stream m a -> m (MutArray a)
23802540fromStream = fromStreamDAs Unpinned
23812541
2382−-- fromStream (Stream m) = P.fold write m
2542+-- fromStream (Stream m) = P.fold create m
23832543-- CAUTION: a very large number (millions) of arrays can degrade performance
23842544-- due to GC overhead because we need to buffer the arrays before we flatten
23852545-- all the arrays.
23862546--
2387−-- XXX Compare if this is faster or "fold write".
2547+-- XXX Compare if this is faster or "fold create".
23882548--
23892549-- | We could take the approach of doubling the memory allocation on each
23902550-- overflow. This would result in more or less the same amount of copying as in
23912551-- the chunking approach. However, if we have to shrink in the end then it may
23922552-- result in an extra copy of the entire data.
23932553--
2394−-- >>> fromStreamD = StreamD.fold MutArray.write
2554+-- >>> fromStreamD = StreamD.fold MutArray.create
23952555--
23962556{-# INLINE fromStreamD #-}
23972557{-# DEPRECATED fromStreamD "Please use fromStream instead." #-}
@@ -2421,6 +2581,10 @@ fromListRev xs = fromListRevN (Prelude.length xs) xs
24212581-- Cloning
24222582-------------------------------------------------------------------------------
24232583
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+
24242588{-# INLINE cloneAs #-}
24252589cloneAs ::
24262590 ( MonadIO m
@@ -2512,7 +2676,7 @@ spliceWith :: forall m a. (MonadIO m, Unbox a) =>
25122676 (Int -> Int -> Int) -> MutArray a -> MutArray a -> m (MutArray a)
25132677spliceWith sizer dst@(MutArray _ start end bound) src = do
25142678{-
2515− let f = writeAppendWith (`sizer` byteLength src) (return dst)
2679+ let f = appendWith (`sizer` byteLength src) (return dst)
25162680 in D.fold f (toStreamD src)
25172681-}
25182682 assert (end <= bound) (return ())
@@ -2580,7 +2744,7 @@ splitOn predicate arr =
25802744{-# INLINE breakOn #-}
25812745breakOn :: MonadIO m
25822746 => Word8 -> MutArray Word8 -> m (MutArray Word8, Maybe (MutArray Word8))
2583−breakOn sep arr@MutArray{..} = asUnpinnedPtrUnsafe arr $ \p -> liftIO $ do
2747+breakOn sep arr@MutArray{..} = unsafeAsPtr arr $ \p -> liftIO $ do
25842748 -- XXX We do not need memchr here, we can use a Haskell equivalent.
25852749 -- Need efficient stream based primitives that work on Word64.
25862750 loc <- c_memchr p sep (fromIntegral $ byteLength arr)
@@ -2603,33 +2767,41 @@ breakOn sep arr@MutArray{..} = asUnpinnedPtrUnsafe arr $ \p -> liftIO $ do
26032767 }
26042768 )
26052769
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+
26062792-- | Create two slices of an array without copying the original array. The
26072793-- specified index @i@ is the first index of the second slice.
26082794--
26092795{-# INLINE splitAt #-}
26102796splitAt :: forall a. Unbox a => Int -> MutArray a -> (MutArray a, MutArray a)
2611−splitAt i arr@MutArray{..} =
2797+splitAt i arr =
26122798 let maxIndex = length arr - 1
26132799 in if i < 0
26142800 then error "sliceAt: negative array index"
26152801 else if i > maxIndex
26162802 then error $ "sliceAt: specified array index " ++ show i
26172803 ++ " is beyond the maximum index " ++ show maxIndex
2618− else let off = i * SIZE_OF(a)
2619− p = arrStart + off
2620− in ( MutArray
2621− { arrContents = arrContents
2622− , arrStart = arrStart
2623− , arrEnd = p
2624− , arrBound = p
2625− }
2626− , MutArray
2627− { arrContents = arrContents
2628− , arrStart = p
2629− , arrEnd = arrEnd
2630− , arrBound = arrBound
2631− }
2632− )
2804+ else unsafeSplitAt i arr
26332805
26342806-------------------------------------------------------------------------------
26352807-- Casting
@@ -2679,9 +2851,13 @@ cast arr =
26792851-- guarantees that unsafe calls will be performed in the calling thread. Making
26802852-- it safe to pass heap-allocated objects to unsafe functions.
26812853
2682−-- 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
26832855-- operations. And we are safe from Haskell perspective because we will be
26842856-- 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.
26852861
26862862-- | Use a @MutArray a@ as @Ptr a@. This is useful when we want to pass an
26872863-- array as a pointer to some operating system call or to a "safe" FFI call.
@@ -2699,16 +2875,21 @@ cast arr =
26992875--
27002876-- /Pre-release/
27012877--
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." #-}
27022885{-# INLINE asPtrUnsafe #-}
27032886asPtrUnsafe :: MonadIO m => MutArray a -> (Ptr a -> m b) -> m b
2704−asPtrUnsafe arr f =
2705− Unboxed.asPtrUnsafe
2706− (arrContents arr) (\ptr -> f (ptr `plusPtr` arrStart arr))
2887+asPtrUnsafe = unsafePinnedAsPtr
27072888
2708−{-# INLINE asUnpinnedPtrUnsafe #-}
2709−asUnpinnedPtrUnsafe :: MonadIO m => MutArray a -> (Ptr a -> m b) -> m b
2710−asUnpinnedPtrUnsafe arr f =
2711− Unboxed.asUnpinnedPtrUnsafe
2889+{-# INLINE unsafeAsPtr #-}
2890+unsafeAsPtr :: MonadIO m => MutArray a -> (Ptr a -> m b) -> m b
2891+unsafeAsPtr arr f =
2892+ Unboxed.unsafeAsPtr
27122893 (arrContents arr) (\ptr -> f (ptr `plusPtr` arrStart arr))
27132894
27142895-------------------------------------------------------------------------------
@@ -2770,19 +2951,11 @@ byteEq arr1 arr2 = fmap (EQ ==) $ byteCmp arr1 arr2
27702951-- can leave some memory unused. They can split the last array to fit it
27712952-- exactly in the space.
27722953
2773−-- | Parser @pCompactLE maxElems@ coalesces adjacent arrays in the input stream
2774−-- only if the combined size would be less than or equal to @maxElems@
2775−-- elements. Note that it won't split an array if the original array is already
2776−-- larger than maxElems.
2777−--
2778−-- @maxElems@ must be greater than 0.
2779−--
2780−-- /Internal/
2781−{-# INLINE_NORMAL pCompactLE #-}
2782−pCompactLE ::
2954+{-# INLINE_NORMAL pCompactLeAs #-}
2955+pCompactLeAs ::
27832956 forall m a. (MonadIO m, Unbox a)
2784− => Int -> Parser (MutArray a) m (MutArray a)
2785−pCompactLE maxElems = Parser step initial extract
2957+ => PinnedState -> Int -> Parser (MutArray a) m (MutArray a)
2958+pCompactLeAs ps maxElems = Parser step initial extract
27862959
27872960 where
27882961
@@ -2813,7 +2986,8 @@ pCompactLE maxElems = Parser step initial extract
28132986 else do
28142987 buf1 <-
28152988 if byteCapacity buf < maxBytes
2816− then realloc maxBytes buf
2989+ then liftIO $ reallocExplicitAs
2990+ ps (SIZE_OF(a)) maxBytes buf
28172991 else return buf
28182992 buf2 <- spliceUnsafe buf1 arr
28192993 return $ Parser.Partial 0 (Just buf2)
@@ -2821,6 +2995,30 @@ pCompactLE maxElems = Parser step initial extract
28212995 extract Nothing = return $ Parser.Done 0 nil
28222996 extract (Just buf) = return $ Parser.Done 0 buf
28232997
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+
28243022data SpliceState s arr
28253023 = SpliceInitial s
28263024 | SpliceBuffering s arr
@@ -2832,18 +3030,10 @@ data SpliceState s arr
28323030-- immutable array never has additional space so a new array is allocated
28333031-- instead of mutating it.
28343032
2835−-- | Scan @rCompactLE maxElems@ coalesces adjacent arrays in the input stream
2836−-- only if the combined size would be less than or equal to @maxElems@
2837−-- elements. Note that it won't split an array if the original array is already
2838−-- larger than maxElems.
2839−--
2840−-- @maxElems@ must be greater than 0.
2841−--
2842−-- /Internal/
2843−{-# INLINE_NORMAL rCompactLE #-}
2844−rCompactLE :: forall m a. (MonadIO m, Unbox a)
2845− => Int -> D.Stream m (MutArray a) -> D.Stream m (MutArray a)
2846−rCompactLE maxElems (D.Stream step state) =
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) =
28473037 D.Stream step' (SpliceInitial state)
28483038
28493039 where
@@ -2879,7 +3069,8 @@ rCompactLE maxElems (D.Stream step state) =
28793069 D.Skip (SpliceYielding buf (SpliceBuffering s arr))
28803070 else do
28813071 buf1 <- if byteCapacity buf < maxBytes
2882− then realloc maxBytes buf
3072+ then liftIO $ reallocExplicitAs
3073+ ps (SIZE_OF(a)) maxBytes buf
28833074 else return buf
28843075 buf2 <- spliceUnsafe buf1 arr
28853076 return $ D.Skip (SpliceBuffering s buf2)
@@ -2890,14 +3081,12 @@ rCompactLE maxElems (D.Stream step state) =
28903081
28913082 step' _ (SpliceYielding arr next) = return $ D.Yield arr next
28923083
2893−-- | Fold @fCompactGE minElems@ coalesces adjacent arrays in the input stream
2894−-- until the size becomes greater than or equal to @minElems@.
2895−--
2896−{-# INLINE_NORMAL fCompactGE #-}
2897−fCompactGE ::
3084+
3085+{-# INLINE_NORMAL fCompactGeAs #-}
3086+fCompactGeAs ::
28983087 forall m a. (MonadIO m, Unbox a)
2899− => Int -> FL.Fold m (MutArray a) (MutArray a)
2900−fCompactGE minElems = Fold step initial extract extract
3088+ => PinnedState -> Int -> FL.Fold m (MutArray a) (MutArray a)
3089+fCompactGeAs ps minElems = Fold step initial extract extract
29013090
29023091 where
29033092
@@ -2925,7 +3114,8 @@ fCompactGE minElems = Fold step initial extract extract
29253114 let len = byteLength buf + byteLength arr
29263115 buf1 <-
29273116 if byteCapacity buf < len
2928− then realloc (max len minBytes) buf
3117+ then liftIO $ reallocExplicitAs
3118+ ps (SIZE_OF(a)) (max minBytes len) buf
29293119 else return buf
29303120 buf2 <- spliceUnsafe buf1 arr
29313121 if len >= minBytes
@@ -2935,15 +3125,30 @@ fCompactGE minElems = Fold step initial extract extract
29353125 extract Nothing = return nil
29363126 extract (Just buf) = return buf
29373127
2938−-- | Like 'compactGE' but for transforming folds instead of stream.
2939−--
2940−-- >>> lCompactGE n = Fold.many (MutArray.fCompactGE n)
3128+-- | Fold @fCompactGE minElems@ coalesces adjacent arrays in the input stream
3129+-- until the size becomes greater than or equal to @minElems@.
29413130--
2942−{-# INLINE_NORMAL lCompactGE #-}
2943−lCompactGE :: forall m a. (MonadIO m, Unbox a)
2944− => Int -> Fold m (MutArray a) () -> Fold m (MutArray a) ()
2945−-- lCompactGE n = Fold.many (fCompactGE n)
2946−lCompactGE minElems (Fold step1 initial1 _ final1) =
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) =
29473152 Fold step initial extract final
29483153
29493154 where
@@ -2981,7 +3186,8 @@ lCompactGE minElems (Fold step1 initial1 _ final1) =
29813186 step (Tuple' (Just buf) r1) arr = do
29823187 let len = byteLength buf + byteLength arr
29833188 buf1 <- if byteCapacity buf < len
2984− then realloc (max minBytes len) buf
3189+ then liftIO $ reallocExplicitAs
3190+ ps (SIZE_OF(a)) (max minBytes len) buf
29853191 else return buf
29863192 buf2 <- spliceUnsafe buf1 arr
29873193 runInner len r1 buf2
@@ -3001,6 +3207,23 @@ lCompactGE minElems (Fold step1 initial1 _ final1) =
30013207 FL.Partial rr -> final1 rr
30023208 FL.Done _ -> return ()
30033209
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+
30043227-- | @compactGE n stream@ coalesces adjacent arrays in the @stream@ until
30053228-- the size becomes greater than or equal to @n@.
30063229--
core/src/Streamly/Internal/Data/MutByteArray/Type.hs+37−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
@@ -114,9 +118,9 @@ touch (MutByteArray contents) =
114118--
115119-- /Pre-release/
116120--
117−{-# INLINE asPtrUnsafe #-}
118−asPtrUnsafe :: MonadIO m => MutByteArray -> (Ptr a -> m b) -> m b
119−asPtrUnsafe arr f = do
121+{-# INLINE unsafePinnedAsPtr #-}
122+unsafePinnedAsPtr :: MonadIO m => MutByteArray -> (Ptr a -> m b) -> m b
123+unsafePinnedAsPtr arr f = do
120124 contents <- liftIO $ pin arr
121125 let !ptr = Ptr (byteArrayContents#
122126 (unsafeCoerce# (getMutableByteArray# contents)))
@@ -124,10 +128,15 @@ asPtrUnsafe arr f = do
124128 liftIO $ touch contents
125129 return r
126130
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+
127136-- | For use with unsafe FFI functions. Does not force pin the array memory.
128−{-# INLINE asUnpinnedPtrUnsafe #-}
129−asUnpinnedPtrUnsafe :: MonadIO m => MutByteArray -> (Ptr a -> m b) -> m b
130−asUnpinnedPtrUnsafe arr f = do
137+{-# INLINE unsafeAsPtr #-}
138+unsafeAsPtr :: MonadIO m => MutByteArray -> (Ptr a -> m b) -> m b
139+unsafeAsPtr arr f = do
131140 let !ptr = Ptr (byteArrayContents#
132141 (unsafeCoerce# (getMutableByteArray# arr)))
133142 r <- f ptr
@@ -138,10 +147,16 @@ asUnpinnedPtrUnsafe arr f = do
138147-- Creation
139148--------------------------------------------------------------------------------
140149
141−{-# NOINLINE nil #-}
150+{-# NOINLINE empty #-}
151+empty :: MutByteArray
152+empty = unsafePerformIO $ new 0
153+
154+{-# DEPRECATED nil "Please use empty instead" #-}
142155nil :: MutByteArray
143−nil = unsafePerformIO $ new 0
156+nil = empty
144157
158+-- XXX add "newRounded" to round up the large size to the next page boundary
159+-- and return the allocated size.
145160{-# INLINE new #-}
146161new :: Int -> IO MutByteArray
147162new nbytes | nbytes < 0 =
@@ -155,7 +170,7 @@ new (I# nbytes) = IO $ \s ->
155170{-# INLINE pinnedNew #-}
156171pinnedNew :: Int -> IO MutByteArray
157172pinnedNew nbytes | nbytes < 0 =
158− errorWithoutStackTrace "pinnedNewByteArray: size must be >= 0"
173+ errorWithoutStackTrace "pinnedNew: size must be >= 0"
159174pinnedNew (I# nbytes) = IO $ \s ->
160175 case newPinnedByteArray# nbytes s of
161176 (# s', mbarr# #) ->
@@ -181,6 +196,12 @@ newBytesAs Pinned = pinnedNew
181196-- Copying
182197-------------------------------------------------------------------------------
183198
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+
184205-- | Put a sub range of a source array into a subrange of a destination array.
185206-- This is not safe as it does not check the bounds of neither the src array
186207-- 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+52−26
@@ -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--
@@ -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+2−2
@@ -103,7 +103,7 @@ 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)
109109import Streamly.Internal.Data.Unfold.Type (Unfold(..))
@@ -462,7 +462,7 @@ writeChunks path = Fold step initial extract final
462462writeWith :: (MonadIO m, MonadCatch m)
463463 => Int -> FilePath -> Fold m Word8 ()
464464writeWith n path =
465− groupsOf n (pinnedWriteNUnsafe n) (writeChunks path)
465+ groupsOf n (A.unsafePinnedCreateOf n) (writeChunks path)
466466
467467{-# DEPRECATED writeWithBufferOf "Please use 'writeWith' instead" #-}
468468{-# INLINE writeWithBufferOf #-}
core/src/Streamly/Internal/FileSystem/Handle.hs+17−16
@@ -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,14 +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)
136136-- import Streamly.String (encodeUtf8, decodeUtf8, foldLines)
137137import Streamly.Internal.System.IO (defaultChunkSize)
138138
139139import qualified Streamly.Data.Fold as FL
140−import qualified Streamly.Data.Array as A
141−import qualified Streamly.Internal.Data.Array.Type as A
140+import qualified Streamly.Internal.Data.Array as A
142141import qualified Streamly.Internal.Data.MutArray.Type as MArray
143142import qualified Streamly.Internal.Data.Refold.Type as Refold
144143import qualified Streamly.Internal.Data.Fold.Type as FL(refoldMany)
@@ -167,6 +166,8 @@ import qualified Streamly.Internal.Data.StreamK.Type as K (mkStream)
167166-- Array IO (Input)
168167-------------------------------------------------------------------------------
169168
169+-- XXX add an API that compacts the arrays to an exact size.
170+
170171-- | Read a 'ByteArray' consisting of one or more bytes from a file handle. If
171172-- no data is available on the handle it blocks until at least one byte becomes
172173-- available. If any data is available then it immediately returns that data
@@ -176,9 +177,9 @@ import qualified Streamly.Internal.Data.StreamK.Type as K (mkStream)
176177{-# INLINABLE getChunk #-}
177178getChunk :: MonadIO m => Int -> Handle -> m (Array Word8)
178179getChunk size h = liftIO $ do
179− arr <- MArray.pinnedNewBytes size
180+ arr :: MArray.MutArray Word8 <- MArray.pinnedEmptyOf size
180181 -- ptr <- mallocPlainForeignPtrAlignedBytes size (alignment (undefined :: Word8))
181− MArray.asPtrUnsafe arr $ \p -> do
182+ MArray.unsafePinnedAsPtr arr $ \p -> do
182183 n <- hGetBufSome h p size
183184 -- XXX shrink only if the diff is significant
184185 return $
@@ -378,12 +379,12 @@ read = A.concat . readChunks
378379{-# INLINABLE putChunk #-}
379380putChunk :: MonadIO m => Handle -> Array a -> m ()
380381putChunk _ arr | byteLength arr == 0 = return ()
381−putChunk h arr = A.asPtrUnsafe arr $ \ptr ->
382+putChunk h arr = A.unsafePinnedAsPtr arr $ \ptr ->
382383 liftIO $ hPutBuf h ptr aLen
383384
384385 where
385386
386− -- XXX We should have the length passed by asPtrUnsafe itself.
387+ -- XXX We should have the length passed by unsafePinnedAsPtr itself.
387388 aLen = A.byteLength arr
388389
389390-------------------------------------------------------------------------------
@@ -488,11 +489,11 @@ writeChunksWithBufferOf = writeChunksWith
488489-- Bytes in the input stream are collected into a buffer until we have a chunk
489490-- of @reqSize@ and then written to the IO device.
490491--
491−-- >>> 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)
492493--
493494{-# INLINE writeWith #-}
494495writeWith :: MonadIO m => Int -> Handle -> Fold m Word8 ()
495−writeWith n h = FL.groupsOf n (pinnedWriteNUnsafe n) (writeChunks h)
496+writeWith n h = FL.groupsOf n (A.unsafePinnedCreateOf n) (writeChunks h)
496497
497498-- | Same as 'writeWith'
498499--
@@ -510,7 +511,7 @@ writeWithBufferOf = writeWith
510511writeMaybesWith :: (MonadIO m )
511512 => Int -> Handle -> Fold m (Maybe Word8) ()
512513writeMaybesWith n h =
513− let writeNJusts = FL.lmap fromJust $ A.pinnedWriteN n
514+ let writeNJusts = FL.lmap fromJust $ A.pinnedCreateOf n
514515 writeOnNothing = FL.takeEndBy_ isNothing writeNJusts
515516 in FL.many writeOnNothing (writeChunks h)
516517
@@ -520,7 +521,7 @@ writeMaybesWith n h =
520521{-# INLINE writerWith #-}
521522writerWith :: MonadIO m => Int -> Refold m Handle Word8 ()
522523writerWith n =
523− FL.refoldMany (FL.take n $ pinnedWriteNUnsafe n) chunkWriter
524+ FL.refoldMany (FL.take n $ A.unsafePinnedCreateOf n) chunkWriter
524525
525526-- | Write a byte stream to a file handle. Accumulates the input in chunks of
526527-- up to 'Streamly.Internal.Data.Array.Type.defaultChunkSize' before writing
core/src/Streamly/Internal/FileSystem/Path.hs+27−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__)
@@ -447,8 +453,25 @@ mkRelFile = undefined
447453-- Operations
448454------------------------------------------------------------------------------
449455
450−separator :: WORD_TYPE
451−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
452475
453476-- If we append an absolute path it may fail with an error if the 'Path'
454477-- implementation stores absolute path information (a leading separator char).
@@ -474,7 +497,7 @@ extendPath (Path a) (Path b) =
474497 newArr = unsafePerformIO $ do
475498 arr <- MutArray.new len
476499 arr1 <- MutArray.spliceUnsafe arr (Array.unsafeThaw a)
477− arr2 <- MutArray.snocUnsafe arr1 separator
500+ arr2 <- MutArray.snocUnsafe arr1 separatorWord
478501 arr3 <- MutArray.spliceUnsafe arr2 (Array.unsafeThaw b)
479502 return (Array.unsafeFreeze arr3)
480503 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/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+10−8
@@ -98,10 +98,10 @@ import qualified Streamly.Data.Fold as FL
9898import qualified Streamly.Data.Stream as S
9999import qualified Streamly.Data.Unfold as UF
100100import qualified Streamly.Internal.Data.Array as A
101− ( unsafeFreeze, asPtrUnsafe, byteLength, pinnedChunksOf,
102− pinnedWriteN, pinnedWriteNUnsafe, lCompactGE )
101+ ( unsafeFreeze, unsafePinnedAsPtr, byteLength, pinnedChunksOf,
102+ pinnedCreateOf, unsafePinnedCreateOf, lCompactGE )
103103import qualified Streamly.Internal.Data.MutArray as MArray
104− (MutArray(..), asPtrUnsafe, pinnedNewBytes)
104+ (MutArray(..), unsafePinnedAsPtr, pinnedEmptyOf)
105105import qualified Streamly.Internal.Data.Stream as S (fromStreamK, Stream(..), Step(..))
106106import qualified Streamly.Internal.Data.StreamK as K (mkStream)
107107
@@ -252,6 +252,8 @@ accept tcpListenQ spec addr =
252252-- Array IO (Input)
253253-------------------------------------------------------------------------------
254254
255+-- XXX add an API that compacts the arrays to an exact size.
256+
255257{-# INLINABLE readArrayUptoWith #-}
256258readArrayUptoWith
257259 :: (h -> Ptr Word8 -> Int -> IO Int)
@@ -259,9 +261,9 @@ readArrayUptoWith
259261 -> h
260262 -> IO (Array Word8)
261263readArrayUptoWith f size h = do
262− arr <- MArray.pinnedNewBytes size
264+ arr <- MArray.pinnedEmptyOf size
263265 -- ptr <- mallocPlainForeignPtrAlignedBytes size (alignment (undefined :: Word8))
264− MArray.asPtrUnsafe arr $ \p -> do
266+ MArray.unsafePinnedAsPtr arr $ \p -> do
265267 n <- f h p size
266268 let v = A.unsafeFreeze
267269 $ arr { MArray.arrEnd = n, MArray.arrBound = size }
@@ -309,7 +311,7 @@ writeArrayWith :: Unbox a
309311 -> Array a
310312 -> IO ()
311313writeArrayWith _ _ arr | A.length arr == 0 = return ()
312−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
313315
314316 where
315317
@@ -503,7 +505,7 @@ putBytesWith n h m = putChunks h $ A.pinnedChunksOf n m
503505--
504506{-# INLINE writeWith #-}
505507writeWith :: MonadIO m => Int -> Socket -> Fold m Word8 ()
506−writeWith n h = FL.groupsOf n (A.pinnedWriteNUnsafe n) (writeChunks h)
508+writeWith n h = FL.groupsOf n (A.unsafePinnedCreateOf n) (writeChunks h)
507509
508510-- | Same as 'writeWith'
509511--
@@ -521,7 +523,7 @@ writeWithBufferOf = writeWith
521523writeMaybesWith :: (MonadIO m )
522524 => Int -> Socket -> Fold m (Maybe Word8) ()
523525writeMaybesWith n h =
524− let writeNJusts = FL.lmap fromJust $ A.pinnedWriteN n
526+ let writeNJusts = FL.lmap fromJust $ A.pinnedCreateOf n
525527 writeOnNothing = FL.takeEndBy_ isNothing writeNJusts
526528 in FL.many writeOnNothing (writeChunks h)
527529
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]