on master help sign in
58 files changed+2157−860
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+3−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+630−266 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+36−4 core/src/Streamly/Internal/Unicode/Stream.hs+1−1 core/src/Streamly/Internal/Unicode/String.hs+7−7 core/streamly-core.cabal+14−10 docs/Developer/optimization-guidelines.md+12 docs/User/Project/Changelog.md+1−1 docs/User/Project/README.md+1 src/Streamly/Internal/Data/IsMap/HashMap.hs+5 src/Streamly/Internal/Data/Stream/IsStream/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+3−1
@@ -1,10 +1,12 @@
11# Changelog
22
3−## Unreleased
3+## 0.2.2 (Jan 2024)
44
55* Add fixities `infixr 5` for `cons` and `consM` functions.
66* Fix a bug in Array `Eq` instance when the type is a sum type with
77 differently sized constructors.
8+* lpackArraysChunksOf, compact, writeChunksWith, putChunksWith now take the
9+ buffer size in number of array elements instead of bytes.
810
911## 0.2.1 (Dec 2023)
1012
core/src/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+630−266
@@ -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,24 +193,34 @@ 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
215213
214+ -- ** Serialization using Unbox
215+ , pokeAppend
216+ , pokeAppendMay
217+ , pokeSkipUnsafe -- unsafePokeSkip
218+
219+ -- ** Deserialization using Unbox
220+ , peekUncons
221+ , peekUnconsUnsafe -- unsafePeekUncons
222+ , peekSkipUnsafe -- unsafePeekSkip
223+
216224 -- Arrays of arrays
217225 -- We can add dimensionality parameter to the array type to get
218226 -- multidimensional arrays. Multidimensional arrays would just be a
@@ -234,6 +242,8 @@ module Streamly.Internal.Data.MutArray.Type
234242
235243 -- *** Concat
236244 -- | Append the arrays in a stream to form a stream of elements.
245+ , concatWith
246+ , concatRevWith
237247 , concat
238248 , concatRev
239249
@@ -241,9 +251,12 @@ module Streamly.Internal.Data.MutArray.Type
241251 -- | Append the arrays in a stream to form a stream of larger arrays.
242252 , SpliceState (..)
243253 , pCompactLE
244− , rCompactLE
254+ , pPinnedCompactLE
255+ , compactLeAs
245256 , fCompactGE
257+ , fPinnedCompactGE
246258 , lCompactGE
259+ , lPinnedCompactGE
247260 , compactGE
248261 , compactEQ
249262
@@ -254,6 +267,7 @@ module Streamly.Internal.Data.MutArray.Type
254267 , c_memchr
255268
256269 -- * Deprecated
270+ , asPtrUnsafe
257271 , writeChunks
258272 , flattenArrays
259273 , flattenArraysRev
@@ -265,6 +279,25 @@ module Streamly.Internal.Data.MutArray.Type
265279 , getIndicesWith
266280 , resize
267281 , 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
268301 )
269302where
270303
@@ -446,6 +479,8 @@ isPinned MutArray{..} = Unboxed.isPinned arrContents
446479-- we need more than that. See stg_pinnedNewAlignedByteArrayzh and
447480-- allocatePinned in GHC source.
448481
482+-- XXX Rename to emptyAlignedWith, alignSize should be first arg.
483+
449484-- | @newArrayWith allocator alignment count@ allocates a new array of zero
450485-- length and with a capacity to hold @count@ elements, using @allocator
451486-- size alignment@ as the memory allocator function.
@@ -469,12 +504,25 @@ newArrayWith alloc alignSize count = do
469504 , arrBound = size
470505 }
471506
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." #-}
472520nil ::
473521#ifdef DEVBUILD
474522 Unbox a =>
475523#endif
476524 MutArray a
477−nil = MutArray Unboxed.nil 0 0 0
525+nil = empty
478526
479527{-# INLINE newBytesAs #-}
480528newBytesAs :: MonadIO m =>
@@ -491,12 +539,13 @@ newBytesAs ps bytes = do
491539 , arrBound = bytes
492540 }
493541
494−-- | Allocates a pinned empty array that can hold 'count' items. The memory of
495−-- the array is uninitialized and the allocation is aligned as per the
496−-- 'Unboxed' instance of the type.
542+-- | Allocates a pinned empty array that with a reserved capacity of bytes.
543+-- The memory of the array is uninitialized and the allocation is aligned as
544+-- per the 'Unboxed' instance of the type.
497545--
498546-- /Pre-release/
499547{-# INLINE pinnedNewBytes #-}
548+{-# DEPRECATED pinnedNewBytes "Please use pinnedEmptyOf with appropriate calculation" #-}
500549pinnedNewBytes :: MonadIO m =>
501550#ifdef DEVBUILD
502551 Unbox a =>
@@ -521,21 +570,31 @@ newAs ps =
521570 (error "new: alignment is not used in unpinned arrays.")
522571
523572-- XXX can unaligned allocation be more efficient when alignment is not needed?
524−--
525−-- | Allocates an empty pinned array that can hold 'count' items. The memory of
526−-- the array is uninitialized and the allocation is aligned as per the 'Unboxed'
527−-- instance of the type.
528−--
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." #-}
529582{-# INLINE pinnedNew #-}
530583pinnedNew :: forall m a. (MonadIO m, Unbox a) => Int -> m (MutArray a)
531−pinnedNew = newAs Pinned
584+pinnedNew = pinnedEmptyOf
532585
533−-- | Allocates an empty unpinned array that can hold 'count' items. The memory
534−-- of the array is uninitialized.
586+-- | Allocates an unpinned array of zero length but growable to the specified
587+-- capacity without reallocation.
535588--
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." #-}
536595{-# INLINE new #-}
537596new :: (MonadIO m, Unbox a) => Int -> m (MutArray a)
538−new = newAs Unpinned
597+new = emptyOf
539598
540599-------------------------------------------------------------------------------
541600-- Random writes
@@ -783,15 +842,15 @@ roundDownTo elemSize size = size - (size `mod` elemSize)
783842-- Since this is not inlined Unboxed consrraint leads to dictionary passing
784843-- which complicates some inspection tests.
785844--
786−{-# NOINLINE reallocExplicit #-}
787−reallocExplicit :: Int -> Int -> MutArray a -> IO (MutArray a)
788−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
789848 assertM(arrEnd <= arrBound)
790849
791850 -- Allocate new array
792851 let newCapMaxInBytes = roundUpLargeArray newCapacityInBytes
793852 contents <-
794− if Unboxed.isPinned arrContents
853+ if ps == Pinned
795854 then Unboxed.pinnedNew newCapMaxInBytes
796855 else Unboxed.new newCapMaxInBytes
797856 let !(MutByteArray mbarrFrom#) = arrContents
@@ -831,7 +890,12 @@ reallocExplicit elemSize newCapacityInBytes MutArray{..} = do
831890-- If the original array is pinned, the newly allocated array is also pinned.
832891{-# INLINABLE realloc #-}
833892realloc :: forall m a. (MonadIO m, Unbox a) => Int -> MutArray a -> m (MutArray a)
834−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
835899
836900-- | @reallocWith label capSizer minIncrBytes array@. The label is used
837901-- in error messages and the capSizer is used to determine the capacity of the
@@ -880,6 +944,7 @@ grow nElems arr@MutArray{..} = do
880944 else realloc req arr
881945
882946{-# DEPRECATED resize "Please use grow instead." #-}
947+{-# INLINE resize #-}
883948resize :: forall m a. (MonadIO m, Unbox a) =>
884949 Int -> MutArray a -> m (MutArray a)
885950resize = grow
@@ -903,6 +968,7 @@ growExp nElems arr@MutArray{..} = do
903968 else realloc req1 arr
904969
905970{-# DEPRECATED resizeExp "Please use growExp instead." #-}
971+{-# INLINE resizeExp #-}
906972resizeExp :: forall m a. (MonadIO m, Unbox a) =>
907973 Int -> MutArray a -> m (MutArray a)
908974resizeExp = growExp
@@ -1051,6 +1117,126 @@ snoc = snocWith f
10511117 then oldSize * 2
10521118 else roundUpToPower2 oldSize * 2
10531119
1120+-------------------------------------------------------------------------------
1121+-- Serialization/Deserialization
1122+-------------------------------------------------------------------------------
1123+
1124+{-# INLINE pokeNewEnd #-}
1125+pokeNewEnd :: (MonadIO m, Unbox a) =>
1126+ Int -> MutArray Word8 -> a -> m (MutArray Word8)
1127+pokeNewEnd newEnd arr@MutArray{..} x = liftIO $ do
1128+ assert (newEnd <= arrBound) (return ())
1129+ liftIO $ pokeAt arrEnd arrContents x
1130+ return $ arr {arrEnd = newEnd}
1131+
1132+-- | Really really unsafe, unboxes a Haskell type and appends the resulting
1133+-- bytes to the byte array, may cause silent data corruption or if you are
1134+-- lucky a segfault if the array does not have enough space to append the
1135+-- element.
1136+--
1137+-- /Internal/
1138+{-# INLINE pokeAppendUnsafe #-}
1139+pokeAppendUnsafe :: forall m a. (MonadIO m, Unbox a) =>
1140+ MutArray Word8 -> a -> m (MutArray Word8)
1141+pokeAppendUnsafe arr@MutArray{..} = pokeNewEnd (arrEnd + SIZE_OF(a)) arr
1142+
1143+-- | Skip the specified number of bytes in the array. The data in the skipped
1144+-- region remains uninitialzed.
1145+{-# INLINE pokeSkipUnsafe #-}
1146+pokeSkipUnsafe :: Int -> MutArray Word8 -> MutArray Word8
1147+pokeSkipUnsafe n arr@MutArray{..} = do
1148+ let newEnd = arrEnd + n
1149+ in assert (newEnd <= arrBound) (arr {arrEnd = newEnd})
1150+
1151+-- | Like 'pokeAppend' but does not grow the array when pre-allocated array
1152+-- capacity becomes full.
1153+--
1154+-- /Internal/
1155+{-# INLINE pokeAppendMay #-}
1156+pokeAppendMay :: forall m a. (MonadIO m, Unbox a) =>
1157+ MutArray Word8 -> a -> m (Maybe (MutArray Word8))
1158+pokeAppendMay arr@MutArray{..} x = liftIO $ do
1159+ let newEnd = arrEnd + SIZE_OF(a)
1160+ if newEnd <= arrBound
1161+ then Just <$> pokeNewEnd newEnd arr x
1162+ else return Nothing
1163+
1164+{-# NOINLINE pokeWithRealloc #-}
1165+pokeWithRealloc :: forall m a. (MonadIO m, Unbox a) =>
1166+ (Int -> Int)
1167+ -> MutArray Word8
1168+ -> a
1169+ -> m (MutArray Word8)
1170+pokeWithRealloc sizer arr x = do
1171+ arr1 <- liftIO $ reallocWith "pokeWithRealloc" sizer (SIZE_OF(a)) arr
1172+ pokeAppendUnsafe arr1 x
1173+
1174+{-# INLINE pokeAppendWith #-}
1175+pokeAppendWith :: forall m a. (MonadIO m, Unbox a) =>
1176+ (Int -> Int)
1177+ -> MutArray Word8
1178+ -> a
1179+ -> m (MutArray Word8)
1180+pokeAppendWith allocSize arr x = liftIO $ do
1181+ let newEnd = arrEnd arr + SIZE_OF(a)
1182+ if newEnd <= arrBound arr
1183+ then pokeNewEnd newEnd arr x
1184+ else pokeWithRealloc allocSize arr x
1185+
1186+-- | Unbox a Haskell type and append the resulting bytes to a mutable byte
1187+-- array. The array is grown exponentially when more space is needed.
1188+--
1189+-- Definition:
1190+--
1191+-- >>> pokeAppend arr x = MutArray.castUnsafe <$> MutArray.snoc (MutArray.castUnsafe arr) x
1192+--
1193+{-# INLINE pokeAppend #-}
1194+pokeAppend :: forall m a. (MonadIO m, Unbox a) =>
1195+ MutArray Word8 -> a -> m (MutArray Word8)
1196+pokeAppend = pokeAppendWith f
1197+
1198+ where
1199+
1200+ f oldSize =
1201+ if isPower2 oldSize
1202+ then oldSize * 2
1203+ else roundUpToPower2 oldSize * 2
1204+
1205+-- | Really really unsafe, create a Haskell value from an unboxed byte array,
1206+-- does not check if the array is big enough, may return garbage or if you are
1207+-- lucky may cause a segfault.
1208+--
1209+-- /Internal/
1210+{-# INLINE peekUnconsUnsafe #-}
1211+peekUnconsUnsafe :: forall m a. (MonadIO m, Unbox a) =>
1212+ MutArray Word8 -> m (a, MutArray Word8)
1213+peekUnconsUnsafe MutArray{..} = do
1214+ let start1 = arrStart + SIZE_OF(a)
1215+ assert (start1 <= arrEnd) (return ())
1216+ liftIO $ do
1217+ r <- peekAt arrStart arrContents
1218+ return (r, MutArray arrContents start1 arrEnd arrBound)
1219+
1220+-- | Discard the specified number of bytes in the array.
1221+{-# INLINE peekSkipUnsafe #-}
1222+peekSkipUnsafe :: Int -> MutArray Word8 -> MutArray Word8
1223+peekSkipUnsafe n MutArray{..} =
1224+ let start1 = arrStart + n
1225+ in assert (start1 <= arrEnd) (MutArray arrContents start1 arrEnd arrBound)
1226+
1227+-- | Create a Haskell value from its unboxed representation from the head of a
1228+-- byte array, return the value and the remaining array.
1229+{-# INLINE peekUncons #-}
1230+peekUncons :: forall m a. (MonadIO m, Unbox a) =>
1231+ MutArray Word8 -> m (Maybe a, MutArray Word8)
1232+peekUncons arr@MutArray{..} = do
1233+ let start1 = arrStart + SIZE_OF(a)
1234+ if start1 > arrEnd
1235+ then return (Nothing, arr)
1236+ else liftIO $ do
1237+ r <- peekAt arrStart arrContents
1238+ return (Just r, MutArray arrContents start1 arrEnd arrBound)
1239+
10541240-------------------------------------------------------------------------------
10551241-- Random reads
10561242-------------------------------------------------------------------------------
@@ -1116,6 +1302,7 @@ indexReaderWith liftio (D.Stream stepi sti) = Unfold step inject
11161302 D.Stop -> return D.Stop
11171303
11181304{-# DEPRECATED getIndicesWith "Please use indexReaderWith instead." #-}
1305+{-# INLINE getIndicesWith #-}
11191306getIndicesWith :: (Monad m, Unbox a) =>
11201307 (forall b. IO b -> m b) -> D.Stream m Int -> Unfold m (MutArray a) a
11211308getIndicesWith = indexReaderWith
@@ -1131,6 +1318,7 @@ indexReader = indexReaderWith liftIO
11311318
11321319-- XXX DO NOT REMOVE, change the signature to use Stream instead of unfold
11331320{-# DEPRECATED getIndices "Please use indexReader instead." #-}
1321+{-# INLINE getIndices #-}
11341322getIndices :: (MonadIO m, Unbox a) => Stream m Int -> Unfold m (MutArray a) a
11351323getIndices = indexReader
11361324
@@ -1427,7 +1615,7 @@ chunksOfAs ps n (D.Stream step state) =
14271615--
14281616-- Same as the following but may be more efficient:
14291617--
1430−-- >>> chunksOf n = Stream.foldMany (MutArray.writeN n)
1618+-- >>> chunksOf n = Stream.foldMany (MutArray.createOf n)
14311619--
14321620-- /Pre-release/
14331621{-# INLINE_NORMAL chunksOf #-}
@@ -1436,14 +1624,14 @@ chunksOf :: forall m a. (MonadIO m, Unbox a)
14361624-- XXX the idiomatic implementation leads to large regression in the D.reverse'
14371625-- benchmark. It seems it has difficulty producing optimized code when
14381626-- converting to StreamK. Investigate GHC optimizations.
1439−-- chunksOf n = D.foldMany (writeN n)
1627+-- chunksOf n = D.foldMany (createOf n)
14401628chunksOf = chunksOfAs Unpinned
14411629
14421630-- | Like 'chunksOf' but creates pinned arrays.
14431631{-# INLINE_NORMAL pinnedChunksOf #-}
14441632pinnedChunksOf :: forall m a. (MonadIO m, Unbox a)
14451633 => Int -> D.Stream m a -> D.Stream m (MutArray a)
1446−-- pinnedChunksOf n = D.foldMany (pinnedWriteN n)
1634+-- pinnedChunksOf n = D.foldMany (pinnedCreateOf n)
14471635pinnedChunksOf = chunksOfAs Pinned
14481636
14491637-- | When we are buffering a stream of unknown size into an array we do not
@@ -1476,16 +1664,10 @@ data FlattenState s contents a =
14761664 OuterLoop s
14771665 | InnerLoop s contents !Int !Int
14781666
1479−-- | Use the "reader" unfold instead.
1480−--
1481−-- @concat = unfoldMany reader@
1482−--
1483−-- We can try this if there are any fusion issues in the unfold.
1484−--
1485−{-# INLINE_NORMAL concat #-}
1486−concat :: forall m a. (MonadIO m, Unbox a)
1487− => D.Stream m (MutArray a) -> D.Stream m a
1488−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)
14891671
14901672 where
14911673
@@ -1502,24 +1684,30 @@ concat (D.Stream step state) = D.Stream step' (OuterLoop state)
15021684 return $ D.Skip $ OuterLoop st
15031685
15041686 step' _ (InnerLoop st contents p end) = do
1505− x <- liftIO $ peekAt p contents
1687+ x <- liftio $ peekAt p contents
15061688 return $ D.Yield x (InnerLoop st contents (INDEX_NEXT(p,a)) end)
15071689
1508−{-# DEPRECATED flattenArrays "Please use \"unfoldMany reader\" instead." #-}
1509−flattenArrays :: forall m a. (MonadIO m, Unbox a)
1510− => D.Stream m (MutArray a) -> D.Stream m a
1511−flattenArrays = concat
1512−
1513−-- | Use the "readerRev" unfold instead.
1690+-- | Use the "reader" unfold instead.
15141691--
1515−-- @concat = unfoldMany readerRev@
1692+-- @concat = unfoldMany reader@
15161693--
15171694-- We can try this if there are any fusion issues in the unfold.
15181695--
1519−{-# INLINE_NORMAL concatRev #-}
1520−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)
15211704 => D.Stream m (MutArray a) -> D.Stream m a
1522−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)
15231711
15241712 where
15251713
@@ -1537,11 +1725,23 @@ concatRev (D.Stream step state) = D.Stream step' (OuterLoop state)
15371725 return $ D.Skip $ OuterLoop st
15381726
15391727 step' _ (InnerLoop st contents p start) = do
1540− x <- liftIO $ peekAt p contents
1728+ x <- liftio $ peekAt p contents
15411729 let cur = INDEX_PREV(p,a)
15421730 return $ D.Yield x (InnerLoop st contents cur start)
15431731
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+
15441743{-# DEPRECATED flattenArraysRev "Please use \"unfoldMany readerRev\" instead." #-}
1744+{-# INLINE flattenArraysRev #-}
15451745flattenArraysRev :: forall m a. (MonadIO m, Unbox a)
15461746 => D.Stream m (MutArray a) -> D.Stream m a
15471747flattenArraysRev = concatRev
@@ -1773,7 +1973,7 @@ foldr f z arr = D.foldr f z $ read arr
17731973
17741974-- XXX Keep the bound intact to not lose any free space? Perf impact?
17751975
1776−-- | @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
17771977-- array.
17781978--
17791979-- Unsafe: Do not drive the fold beyond @n@ elements, it will lead to memory
@@ -1782,13 +1982,12 @@ foldr f z arr = D.foldr f z $ read arr
17821982-- Any free space left in the array after appending @n@ elements is lost.
17831983--
17841984-- /Internal/
1785−{-# INLINE_NORMAL writeAppendNUnsafe #-}
1786−writeAppendNUnsafe :: forall m a. (MonadIO m, Unbox a) =>
1985+{-# INLINE_NORMAL unsafeAppendN #-}
1986+unsafeAppendN :: forall m a. (MonadIO m, Unbox a) =>
17871987 Int
17881988 -> m (MutArray a)
17891989 -> Fold m a (MutArray a)
1790−writeAppendNUnsafe n action =
1791− fmap fromArrayUnsafe $ FL.foldlM' step initial
1990+unsafeAppendN n action = fmap fromArrayUnsafe $ FL.foldlM' step initial
17921991
17931992 where
17941993
@@ -1801,7 +2000,7 @@ writeAppendNUnsafe n action =
18012000 -- otherwise we lose that space.
18022001 arr1 <-
18032002 if free < needed
1804− then noinline reallocWith "writeAppendNUnsafeWith" (+ needed) needed arr
2003+ then noinline reallocWith "unsafeAppendN" (+ needed) needed arr
18052004 else return arr
18062005 return $ toArrayUnsafe arr1
18072006
@@ -1809,30 +2008,51 @@ writeAppendNUnsafe n action =
18092008 liftIO $ pokeAt end contents x
18102009 return $ ArrayUnsafe contents start (INDEX_NEXT(end,a))
18112010
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+
18122019-- | Append @n@ elements to an existing array. Any free space left in the array
18132020-- after appending @n@ elements is lost.
18142021--
1815−-- >>> writeAppendN n initial = Fold.take n (MutArray.writeAppendNUnsafe n initial)
2022+-- >>> appendN n initial = Fold.take n (MutArray.unsafeAppendN n initial)
18162023--
1817−{-# 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 #-}
18182032writeAppendN :: forall m a. (MonadIO m, Unbox a) =>
18192033 Int -> m (MutArray a) -> Fold m a (MutArray a)
1820−writeAppendN n initial = FL.take n (writeAppendNUnsafe n initial)
2034+writeAppendN = appendN
18212035
1822−-- | @writeAppendWith realloc action@ mutates the array generated by @action@ to
2036+-- | @appendWith realloc action@ mutates the array generated by @action@ to
18232037-- append the input stream. If there is no reserved space available in the
18242038-- array it is reallocated to a size in bytes determined by @realloc oldSize@,
18252039-- where @oldSize@ is the current size of the array in bytes.
18262040--
18272041-- Note that the returned array may be a mutated version of original array.
18282042--
1829−-- >>> writeAppendWith sizer = Fold.foldlM' (MutArray.snocWith sizer)
2043+-- >>> appendWith sizer = Fold.foldlM' (MutArray.snocWith sizer)
18302044--
18312045-- /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." #-}
18322052{-# INLINE writeAppendWith #-}
18332053writeAppendWith :: forall m a. (MonadIO m, Unbox a) =>
18342054 (Int -> Int) -> m (MutArray a) -> Fold m a (MutArray a)
1835−writeAppendWith sizer = FL.foldlM' (snocWith sizer)
2055+writeAppendWith = appendWith
18362056
18372057-- | @append action@ mutates the array generated by @action@ to append the
18382058-- input stream. If there is no reserved space available in the array it is
@@ -1840,26 +2060,33 @@ writeAppendWith sizer = FL.foldlM' (snocWith sizer)
18402060--
18412061-- Note that the returned array may be a mutated version of original array.
18422062--
1843−-- >>> writeAppend = MutArray.writeAppendWith (* 2)
2063+-- >>> append = MutArray.appendWith (* 2)
18442064--
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." #-}
18452072{-# INLINE writeAppend #-}
18462073writeAppend :: forall m a. (MonadIO m, Unbox a) =>
18472074 m (MutArray a) -> Fold m a (MutArray a)
1848−writeAppend = writeAppendWith (* 2)
2075+writeAppend = append
18492076
18502077-- XXX We can carry bound as well in the state to make sure we do not lose the
18512078-- remaining capacity. Need to check perf impact.
18522079--
1853−-- | Like 'writeNUnsafe' but takes a new array allocator @alloc size@ function
1854−-- as argument.
2080+-- | Like 'unsafeCreateOf' but takes a new array allocator @alloc size@
2081+-- function as argument.
18552082--
1856−-- >>> writeNWithUnsafe alloc n = MutArray.writeAppendNUnsafe (alloc n) n
2083+-- >>> unsafeCreateOfWith alloc n = MutArray.unsafeAppendN (alloc n) n
18572084--
18582085-- /Pre-release/
1859−{-# INLINE_NORMAL writeNWithUnsafe #-}
1860−writeNWithUnsafe :: forall m a. (MonadIO m, Unbox a)
2086+{-# INLINE_NORMAL unsafeCreateOfWith #-}
2087+unsafeCreateOfWith :: forall m a. (MonadIO m, Unbox a)
18612088 => (Int -> m (MutArray a)) -> Int -> Fold m a (MutArray a)
1862−writeNWithUnsafe alloc n = fromArrayUnsafe <$> FL.foldlM' step initial
2089+unsafeCreateOfWith alloc n = fromArrayUnsafe <$> FL.foldlM' step initial
18632090
18642091 where
18652092
@@ -1870,40 +2097,64 @@ writeNWithUnsafe alloc n = fromArrayUnsafe <$> FL.foldlM' step initial
18702097 return
18712098 $ ArrayUnsafe contents start (INDEX_NEXT(end,a))
18722099
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+
18732106{-# INLINE_NORMAL writeNUnsafeAs #-}
18742107writeNUnsafeAs :: forall m a. (MonadIO m, Unbox a)
18752108 => PinnedState -> Int -> Fold m a (MutArray a)
1876−writeNUnsafeAs ps = writeNWithUnsafe (newAs ps)
2109+writeNUnsafeAs ps = unsafeCreateOfWith (newAs ps)
18772110
1878−-- | 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
18792112-- driver must not call the step function more than 'n' times otherwise it will
18802113-- corrupt the memory and crash. This function exists mainly because any
18812114-- conditional in the step function blocks fusion causing 10x performance
18822115-- slowdown.
18832116--
1884−-- >>> writeNUnsafe = MutArray.writeNWithUnsafe MutArray.new
2117+-- >>> unsafeCreateOf = MutArray.unsafeCreateOfWith MutArray.emptyOf
18852118--
1886−{-# 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 #-}
18872126writeNUnsafe :: forall m a. (MonadIO m, Unbox a)
18882127 => Int -> Fold m a (MutArray a)
1889−writeNUnsafe = writeNUnsafeAs Unpinned
2128+writeNUnsafe = unsafeCreateOf
18902129
1891−-- | Like 'writeNUnsafe' but creates a pinned array.
1892−{-# INLINE_NORMAL pinnedWriteNUnsafe #-}
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
2135+
2136+{-# DEPRECATED pinnedWriteNUnsafe "Please use unsafePinnedCreateOf instead." #-}
2137+{-# INLINE pinnedWriteNUnsafe #-}
18932138pinnedWriteNUnsafe :: forall m a. (MonadIO m, Unbox a)
18942139 => Int -> Fold m a (MutArray a)
1895−pinnedWriteNUnsafe = writeNUnsafeAs Pinned
2140+pinnedWriteNUnsafe = unsafePinnedCreateOf
18962141
1897−-- | @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
18982143-- allocated using the @alloc@ function.
18992144--
1900−-- >>> writeNWith alloc n = Fold.take n (MutArray.writeNWithUnsafe alloc n)
1901−-- >>> 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
19022147--
1903−{-# 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 #-}
19042155writeNWith :: forall m a. (MonadIO m, Unbox a)
19052156 => (Int -> m (MutArray a)) -> Int -> Fold m a (MutArray a)
1906−writeNWith alloc n = FL.take n (writeNWithUnsafe alloc n)
2157+writeNWith = createOfWith
19072158
19082159{-# INLINE_NORMAL writeNAs #-}
19092160writeNAs ::
@@ -1911,28 +2162,42 @@ writeNAs ::
19112162 => PinnedState
19122163 -> Int
19132164 -> Fold m a (MutArray a)
1914−writeNAs ps = writeNWith (newAs ps)
2165+writeNAs ps = createOfWith (newAs ps)
19152166
1916−-- | @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
19172168-- 'MutArray'.
19182169--
1919−-- >>> writeN = MutArray.writeNWith MutArray.new
1920−-- >>> writeN n = Fold.take n (MutArray.writeNUnsafe n)
1921−-- >>> 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)
19222173--
1923−{-# 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 #-}
19242181writeN :: forall m a. (MonadIO m, Unbox a) => Int -> Fold m a (MutArray a)
1925−writeN = writeNAs Unpinned
2182+writeN = createOf
19262183
1927−-- | Like 'writeN' but creates a pinned array.
1928−{-# INLINE_NORMAL pinnedWriteN #-}
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
2191+
2192+{-# DEPRECATED pinnedWriteN "Please use pinnedCreateOf instead." #-}
2193+{-# INLINE pinnedWriteN #-}
19292194pinnedWriteN ::
19302195 forall m a. (MonadIO m, Unbox a)
19312196 => Int
19322197 -> Fold m a (MutArray a)
1933−pinnedWriteN = writeNAs Pinned
2198+pinnedWriteN = pinnedCreateOf
19342199
1935−-- | Like writeNWithUnsafe but writes the array in reverse order.
2200+-- | Like unsafeCreateOfWith but writes the array in reverse order.
19362201--
19372202-- /Internal/
19382203{-# INLINE_NORMAL writeRevNWithUnsafe #-}
@@ -1953,7 +2218,7 @@ writeRevNWithUnsafe alloc n = fromArrayUnsafe <$> FL.foldlM' step initial
19532218 return
19542219 $ ArrayUnsafe contents ptr end
19552220
1956−-- | Like writeNWith but writes the array in reverse order.
2221+-- | Like createOfWith but writes the array in reverse order.
19572222--
19582223-- /Internal/
19592224{-# INLINE_NORMAL writeRevNWith #-}
@@ -1961,31 +2226,36 @@ writeRevNWith :: forall m a. (MonadIO m, Unbox a)
19612226 => (Int -> m (MutArray a)) -> Int -> Fold m a (MutArray a)
19622227writeRevNWith alloc n = FL.take n (writeRevNWithUnsafe alloc n)
19632228
1964−-- | Like writeN but writes the array in reverse order.
2229+-- | Like 'createOf' but writes the array in reverse order.
19652230--
19662231-- /Pre-release/
1967−{-# 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 #-}
19682238writeRevN :: forall m a. (MonadIO m, Unbox a) => Int -> Fold m a (MutArray a)
1969−writeRevN = writeRevNWith new
2239+writeRevN = revCreateOf
19702240
19712241-- | @pinnedWriteNAligned align n@ folds a maximum of @n@ elements from the
19722242-- input stream to a 'MutArray' aligned to the given size.
19732243--
1974−-- >>> pinnedWriteNAligned align = MutArray.writeNWith (MutArray.pinnedNewAligned align)
1975−-- >>> 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)
19762246--
19772247-- /Pre-release/
19782248--
19792249{-# INLINE_NORMAL pinnedWriteNAligned #-}
19802250pinnedWriteNAligned :: forall m a. (MonadIO m, Unbox a)
19812251 => Int -> Int -> Fold m a (MutArray a)
1982−pinnedWriteNAligned align = writeNWith (pinnedNewAligned align)
2252+pinnedWriteNAligned align = createOfWith (pinnedNewAligned align)
19832253
19842254-- XXX Buffer to a list instead?
19852255
19862256-- | Buffer a stream into a stream of arrays.
19872257--
1988−-- >>> buildChunks n = Fold.many (MutArray.writeN n) Fold.toStreamK
2258+-- >>> buildChunks n = Fold.many (MutArray.createOf n) Fold.toStreamK
19892259--
19902260-- Breaking an array into an array stream can be useful to consume a large
19912261-- array sequentially such that memory of the array is released incrementatlly.
@@ -1997,9 +2267,10 @@ pinnedWriteNAligned align = writeNWith (pinnedNewAligned align)
19972267{-# INLINE_NORMAL buildChunks #-}
19982268buildChunks :: (MonadIO m, Unbox a) =>
19992269 Int -> Fold m a (StreamK n (MutArray a))
2000−buildChunks n = FL.many (writeN n) FL.toStreamK
2270+buildChunks n = FL.many (createOf n) FL.toStreamK
20012271
20022272{-# DEPRECATED writeChunks "Please use buildChunks instead." #-}
2273+{-# INLINE writeChunks #-}
20032274writeChunks :: (MonadIO m, Unbox a) =>
20042275 Int -> Fold m a (StreamK n (MutArray a))
20052276writeChunks = buildChunks
@@ -2007,29 +2278,29 @@ writeChunks = buildChunks
20072278{-# INLINE_NORMAL writeWithAs #-}
20082279writeWithAs :: forall m a. (MonadIO m, Unbox a)
20092280 => PinnedState -> Int -> Fold m a (MutArray a)
2010−-- writeWithAs ps n = FL.rmapM rightSize $ writeAppendWith (* 2) (newAs ps n)
2281+-- writeWithAs ps n = FL.rmapM rightSize $ appendWith (* 2) (newAs ps n)
20112282writeWithAs ps elemCount =
20122283 FL.rmapM extract $ FL.foldlM' step initial
20132284
20142285 where
20152286
20162287 initial = do
2017− when (elemCount < 0) $ error "writeWith: elemCount is negative"
2288+ when (elemCount < 0) $ error "createWith: elemCount is negative"
20182289 newAs ps elemCount
20192290
20202291 step arr@(MutArray _ start end bound) x
20212292 | INDEX_NEXT(end,a) > bound = do
20222293 let oldSize = end - start
20232294 newSize = max (oldSize * 2) 1
2024− arr1 <- liftIO $ reallocExplicit (SIZE_OF(a)) newSize arr
2295+ arr1 <- liftIO $ reallocExplicitAs ps (SIZE_OF(a)) newSize arr
20252296 snocUnsafe arr1 x
20262297 step arr x = snocUnsafe arr x
20272298
20282299 extract = liftIO . rightSize
20292300
2030−-- XXX Compare writeWith with fromStreamD which uses an array of streams
2301+-- XXX Compare createWith with fromStreamD which uses an array of streams
20312302-- implementation. We can write this using buildChunks above if that is faster.
2032−-- If writeWith is faster then we should use that to implement
2303+-- If createWith is faster then we should use that to implement
20332304-- fromStreamD.
20342305--
20352306-- XXX The realloc based implementation needs to make one extra copy if we use
@@ -2044,39 +2315,56 @@ writeWithAs ps elemCount =
20442315-- XXX check if GHC's memory allocator is efficient enough. We can try the C
20452316-- malloc to compare against.
20462317
2047−-- | @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
20482319-- starts at a size big enough to hold minCount elements, the size is doubled
20492320-- every time the array needs to be grown.
20502321--
20512322-- /Caution! Do not use this on infinite streams./
20522323--
2053−-- >>> f n = MutArray.writeAppendWith (* 2) (MutArray.new n)
2054−-- >>> writeWith n = Fold.rmapM MutArray.rightSize (f n)
2055−-- >>> 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)
20562327--
20572328-- /Pre-release/
2058−{-# 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 #-}
20592337writeWith :: forall m a. (MonadIO m, Unbox a)
20602338 => Int -> Fold m a (MutArray a)
2061−-- writeWith n = FL.rmapM rightSize $ writeAppendWith (* 2) (new n)
2062−writeWith = writeWithAs Unpinned
2339+writeWith = createWith
20632340
20642341-- | Fold the whole input to a single array.
20652342--
2066−-- Same as 'writeWith' using an initial array size of 'arrayChunkBytes' bytes
2343+-- Same as 'createWith' using an initial array size of 'arrayChunkBytes' bytes
20672344-- rounded up to the element size.
20682345--
20692346-- /Caution! Do not use this on infinite streams./
20702347--
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." #-}
20712354{-# INLINE write #-}
20722355write :: forall m a. (MonadIO m, Unbox a) => Fold m a (MutArray a)
2073−write = writeWith (allocBytesToElemCount (undefined :: a) arrayChunkBytes)
2356+write = create
20742357
2075−-- | 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." #-}
20762365{-# INLINE pinnedWrite #-}
20772366pinnedWrite :: forall m a. (MonadIO m, Unbox a) => Fold m a (MutArray a)
2078−pinnedWrite =
2079− writeWithAs Pinned (allocBytesToElemCount (undefined :: a) arrayChunkBytes)
2367+pinnedWrite = pinnedCreate
20802368
20812369-------------------------------------------------------------------------------
20822370-- construct from streams, known size
@@ -2096,17 +2384,18 @@ fromStreamDNAs ps limit str = do
20962384 liftIO $ pokeAt ptr arrContents x
20972385 return $ INDEX_NEXT(ptr,a)
20982386
2099−-- | Use the 'writeN' fold instead.
2387+-- | Use the 'createOf' fold instead.
21002388--
2101−-- >>> fromStreamN n = Stream.fold (MutArray.writeN n)
2389+-- >>> fromStreamN n = Stream.fold (MutArray.createOf n)
21022390--
21032391{-# INLINE_NORMAL fromStreamN #-}
21042392fromStreamN :: forall m a. (MonadIO m, Unbox a)
21052393 => Int -> D.Stream m a -> m (MutArray a)
2106−-- fromStreamDN n = D.fold (writeN n)
2394+-- fromStreamDN n = D.fold (createOf n)
21072395fromStreamN = fromStreamDNAs Unpinned
21082396
21092397{-# DEPRECATED fromStreamDN "Please use fromStreamN instead." #-}
2398+{-# INLINE fromStreamDN #-}
21102399fromStreamDN :: forall m a. (MonadIO m, Unbox a)
21112400 => Int -> D.Stream m a -> m (MutArray a)
21122401fromStreamDN = fromStreamN
@@ -2129,20 +2418,20 @@ pinnedFromListN n xs = fromStreamDNAs Pinned n $ D.fromList xs
21292418-- /Pre-release/
21302419{-# INLINE fromListRevN #-}
21312420fromListRevN :: (MonadIO m, Unbox a) => Int -> [a] -> m (MutArray a)
2132−fromListRevN n xs = D.fold (writeRevN n) $ D.fromList xs
2421+fromListRevN n xs = D.fold (revCreateOf n) $ D.fromList xs
21332422
21342423-- | Convert a pure stream in Identity monad to a mutable array.
21352424{-# INLINABLE fromPureStreamN #-}
21362425fromPureStreamN :: (MonadIO m, Unbox a) =>
21372426 Int -> Stream Identity a -> m (MutArray a)
21382427fromPureStreamN n xs =
2139− D.fold (writeN n) $ D.morphInner (return . runIdentity) xs
2428+ D.fold (createOf n) $ D.morphInner (return . runIdentity) xs
21402429
21412430-- | Convert a pure stream in Identity monad to a mutable array.
21422431{-# INLINABLE fromPureStream #-}
21432432fromPureStream :: (MonadIO m, Unbox a) => Stream Identity a -> m (MutArray a)
21442433fromPureStream xs =
2145− D.fold write $ D.morphInner (return . runIdentity) xs
2434+ D.fold create $ D.morphInner (return . runIdentity) xs
21462435
21472436{-# INLINABLE fromPtrN #-}
21482437fromPtrN :: MonadIO m => Int -> Ptr Word8 -> m (MutArray Word8)
@@ -2151,7 +2440,7 @@ fromPtrN len addr = do
21512440 -- XXX We can implement a stream copy in a similar way by streaming Word64
21522441 -- first and then remaining Word8.
21532442 arr <- new len
2154− _ <- asUnpinnedPtrUnsafe arr
2443+ _ <- unsafeAsPtr arr
21552444 (\ptr -> liftIO $ c_memcpy ptr addr (fromIntegral len))
21562445 return (arr {arrEnd = len})
21572446
@@ -2167,7 +2456,7 @@ fromByteStr# addr = do
21672456 len <- liftIO $ c_strlen (Ptr addr)
21682457 let lenInt = fromIntegral len
21692458 arr <- new lenInt
2170− _ <- asUnpinnedPtrUnsafe arr (\ptr -> liftIO $ c_memcpy ptr (Ptr addr) len)
2459+ _ <- unsafeAsPtr arr (\ptr -> liftIO $ c_memcpy ptr (Ptr addr) len)
21712460 return (arr {arrEnd = lenInt})
21722461
21732462-------------------------------------------------------------------------------
@@ -2226,6 +2515,7 @@ fromChunksK :: (Unbox a, MonadIO m) =>
22262515fromChunksK = fromChunkskAs Unpinned
22272516
22282517{-# DEPRECATED fromArrayStreamK "Please use fromChunksK instead." #-}
2518+{-# INLINE fromArrayStreamK #-}
22292519fromArrayStreamK :: (Unbox a, MonadIO m) =>
22302520 StreamK m (MutArray a) -> m (MutArray a)
22312521fromArrayStreamK = fromChunksK
@@ -2237,7 +2527,7 @@ fromStreamDAs ps m =
22372527 arrayStreamKFromStreamDAs Unpinned m >>= fromChunkskAs ps
22382528
22392529-- | Create an 'Array' from a stream. This is useful when we want to create a
2240−-- 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
22412531-- as efficient when the size is already known.
22422532--
22432533-- Note that if the input stream is too large memory allocation for the array
@@ -2249,19 +2539,19 @@ fromStreamDAs ps m =
22492539fromStream :: (MonadIO m, Unbox a) => Stream m a -> m (MutArray a)
22502540fromStream = fromStreamDAs Unpinned
22512541
2252−-- fromStream (Stream m) = P.fold write m
2542+-- fromStream (Stream m) = P.fold create m
22532543-- CAUTION: a very large number (millions) of arrays can degrade performance
22542544-- due to GC overhead because we need to buffer the arrays before we flatten
22552545-- all the arrays.
22562546--
2257−-- XXX Compare if this is faster or "fold write".
2547+-- XXX Compare if this is faster or "fold create".
22582548--
22592549-- | We could take the approach of doubling the memory allocation on each
22602550-- overflow. This would result in more or less the same amount of copying as in
22612551-- the chunking approach. However, if we have to shrink in the end then it may
22622552-- result in an extra copy of the entire data.
22632553--
2264−-- >>> fromStreamD = StreamD.fold MutArray.write
2554+-- >>> fromStreamD = StreamD.fold MutArray.create
22652555--
22662556{-# INLINE fromStreamD #-}
22672557{-# DEPRECATED fromStreamD "Please use fromStream instead." #-}
@@ -2291,6 +2581,10 @@ fromListRev xs = fromListRevN (Prelude.length xs) xs
22912581-- Cloning
22922582-------------------------------------------------------------------------------
22932583
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+
22942588{-# INLINE cloneAs #-}
22952589cloneAs ::
22962590 ( MonadIO m
@@ -2382,7 +2676,7 @@ spliceWith :: forall m a. (MonadIO m, Unbox a) =>
23822676 (Int -> Int -> Int) -> MutArray a -> MutArray a -> m (MutArray a)
23832677spliceWith sizer dst@(MutArray _ start end bound) src = do
23842678{-
2385− let f = writeAppendWith (`sizer` byteLength src) (return dst)
2679+ let f = appendWith (`sizer` byteLength src) (return dst)
23862680 in D.fold f (toStreamD src)
23872681-}
23882682 assert (end <= bound) (return ())
@@ -2450,7 +2744,7 @@ splitOn predicate arr =
24502744{-# INLINE breakOn #-}
24512745breakOn :: MonadIO m
24522746 => Word8 -> MutArray Word8 -> m (MutArray Word8, Maybe (MutArray Word8))
2453−breakOn sep arr@MutArray{..} = asUnpinnedPtrUnsafe arr $ \p -> liftIO $ do
2747+breakOn sep arr@MutArray{..} = unsafeAsPtr arr $ \p -> liftIO $ do
24542748 -- XXX We do not need memchr here, we can use a Haskell equivalent.
24552749 -- Need efficient stream based primitives that work on Word64.
24562750 loc <- c_memchr p sep (fromIntegral $ byteLength arr)
@@ -2473,33 +2767,41 @@ breakOn sep arr@MutArray{..} = asUnpinnedPtrUnsafe arr $ \p -> liftIO $ do
24732767 }
24742768 )
24752769
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+
24762792-- | Create two slices of an array without copying the original array. The
24772793-- specified index @i@ is the first index of the second slice.
24782794--
24792795{-# INLINE splitAt #-}
24802796splitAt :: forall a. Unbox a => Int -> MutArray a -> (MutArray a, MutArray a)
2481−splitAt i arr@MutArray{..} =
2797+splitAt i arr =
24822798 let maxIndex = length arr - 1
24832799 in if i < 0
24842800 then error "sliceAt: negative array index"
24852801 else if i > maxIndex
24862802 then error $ "sliceAt: specified array index " ++ show i
24872803 ++ " is beyond the maximum index " ++ show maxIndex
2488− else let off = i * SIZE_OF(a)
2489− p = arrStart + off
2490− in ( MutArray
2491− { arrContents = arrContents
2492− , arrStart = arrStart
2493− , arrEnd = p
2494− , arrBound = p
2495− }
2496− , MutArray
2497− { arrContents = arrContents
2498− , arrStart = p
2499− , arrEnd = arrEnd
2500− , arrBound = arrBound
2501− }
2502− )
2804+ else unsafeSplitAt i arr
25032805
25042806-------------------------------------------------------------------------------
25052807-- Casting
@@ -2549,9 +2851,13 @@ cast arr =
25492851-- guarantees that unsafe calls will be performed in the calling thread. Making
25502852-- it safe to pass heap-allocated objects to unsafe functions.
25512853
2552−-- 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
25532855-- operations. And we are safe from Haskell perspective because we will be
25542856-- 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.
25552861
25562862-- | Use a @MutArray a@ as @Ptr a@. This is useful when we want to pass an
25572863-- array as a pointer to some operating system call or to a "safe" FFI call.
@@ -2569,16 +2875,21 @@ cast arr =
25692875--
25702876-- /Pre-release/
25712877--
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." #-}
25722885{-# INLINE asPtrUnsafe #-}
25732886asPtrUnsafe :: MonadIO m => MutArray a -> (Ptr a -> m b) -> m b
2574−asPtrUnsafe arr f =
2575− Unboxed.asPtrUnsafe
2576− (arrContents arr) (\ptr -> f (ptr `plusPtr` arrStart arr))
2887+asPtrUnsafe = unsafePinnedAsPtr
25772888
2578−{-# INLINE asUnpinnedPtrUnsafe #-}
2579−asUnpinnedPtrUnsafe :: MonadIO m => MutArray a -> (Ptr a -> m b) -> m b
2580−asUnpinnedPtrUnsafe arr f =
2581− Unboxed.asUnpinnedPtrUnsafe
2889+{-# INLINE unsafeAsPtr #-}
2890+unsafeAsPtr :: MonadIO m => MutArray a -> (Ptr a -> m b) -> m b
2891+unsafeAsPtr arr f =
2892+ Unboxed.unsafeAsPtr
25822893 (arrContents arr) (\ptr -> f (ptr `plusPtr` arrStart arr))
25832894
25842895-------------------------------------------------------------------------------
@@ -2635,46 +2946,48 @@ byteEq arr1 arr2 = fmap (EQ ==) $ byteCmp arr1 arr2
26352946-- Compact
26362947-------------------------------------------------------------------------------
26372948
2638−-- | Parser @pCompactLE n@ coalesces adjacent arrays in the input stream
2639−-- only if the combined size would be less than or equal to n.
2640−--
2641−-- /Internal/
2642−{-# INLINE_NORMAL pCompactLE #-}
2643−pCompactLE ::
2949+-- Note: LE versions avoid an extra copy compared to GE. LE parser trades
2950+-- backtracking one array in lieu of avoiding a copy. However, LE and GE both
2951+-- can leave some memory unused. They can split the last array to fit it
2952+-- exactly in the space.
2953+
2954+{-# INLINE_NORMAL pCompactLeAs #-}
2955+pCompactLeAs ::
26442956 forall m a. (MonadIO m, Unbox a)
2645− => Int -> Parser (MutArray a) m (MutArray a)
2646−pCompactLE n = Parser step initial extract
2957+ => PinnedState -> Int -> Parser (MutArray a) m (MutArray a)
2958+pCompactLeAs ps maxElems = Parser step initial extract
26472959
26482960 where
26492961
2650− nBytes = n * SIZE_OF(a)
2962+ maxBytes = maxElems * SIZE_OF(a)
26512963
26522964 functionName = "Streamly.Internal.Data.MutArray.pCompactLE"
26532965
26542966 initial =
26552967 return
2656− $ if n <= 0
2968+ $ if maxElems <= 0
26572969 then error
26582970 $ functionName
26592971 ++ ": the size of arrays ["
2660− ++ show n ++ "] must be a natural number"
2972+ ++ show maxElems ++ "] must be a natural number"
26612973 else Parser.IPartial Nothing
26622974
26632975 step Nothing arr =
26642976 return
26652977 $ let len = byteLength arr
2666− in if len >= nBytes
2978+ in if len >= maxBytes
26672979 then Parser.Done 0 arr
26682980 else Parser.Partial 0 (Just arr)
2981+ -- XXX Split the last array to use the space more compactly.
26692982 step (Just buf) arr =
26702983 let len = byteLength buf + byteLength arr
2671− in if len > nBytes
2984+ in if len > maxBytes
26722985 then return $ Parser.Done 1 buf
26732986 else do
2674− -- XXX check this in the Nothing case
26752987 buf1 <-
2676− if byteCapacity buf < nBytes
2677− then liftIO $ realloc nBytes buf
2988+ if byteCapacity buf < maxBytes
2989+ then liftIO $ reallocExplicitAs
2990+ ps (SIZE_OF(a)) maxBytes buf
26782991 else return buf
26792992 buf2 <- spliceUnsafe buf1 arr
26802993 return $ Parser.Partial 0 (Just buf2)
@@ -2682,6 +2995,30 @@ pCompactLE n = Parser step initial extract
26822995 extract Nothing = return $ Parser.Done 0 nil
26832996 extract (Just buf) = return $ Parser.Done 0 buf
26842997
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+
26853022data SpliceState s arr
26863023 = SpliceInitial s
26873024 | SpliceBuffering s arr
@@ -2693,48 +3030,47 @@ data SpliceState s arr
26933030-- immutable array never has additional space so a new array is allocated
26943031-- instead of mutating it.
26953032
2696−-- | Scan @rCompactLE n@ coalesces adjacent arrays in the input stream
2697−-- only if the combined size would be less than or equal to n.
2698−--
2699−-- /Internal/
2700−{-# INLINE_NORMAL rCompactLE #-}
2701−rCompactLE :: (MonadIO m, Unbox a)
2702− => Int -> D.Stream m (MutArray a) -> D.Stream m (MutArray a)
2703−rCompactLE n (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) =
27043037 D.Stream step' (SpliceInitial state)
27053038
27063039 where
27073040
3041+ maxBytes = maxElems * SIZE_OF(a)
3042+
27083043 functionName = "Streamly.Internal.Data.MutArray.rCompactLE"
27093044
27103045 {-# INLINE_LATE step' #-}
27113046 step' gst (SpliceInitial st) = do
2712− when (n <= 0) $
3047+ when (maxElems <= 0) $
27133048 -- XXX we can pass the module string from the higher level API
2714− error $ functionName ++ ": the size of arrays [" ++ show n
3049+ error $ functionName ++ ": the size of arrays [" ++ show maxElems
27153050 ++ "] must be a natural number"
27163051 r <- step gst st
27173052 case r of
27183053 D.Yield arr s -> return $
27193054 let len = byteLength arr
2720− in if len >= n
3055+ in if len >= maxBytes
27213056 then D.Skip (SpliceYielding arr (SpliceInitial s))
27223057 else D.Skip (SpliceBuffering s arr)
27233058 D.Skip s -> return $ D.Skip (SpliceInitial s)
27243059 D.Stop -> return D.Stop
27253060
3061+ -- XXX Split the last array to use the space more compactly.
27263062 step' gst (SpliceBuffering st buf) = do
27273063 r <- step gst st
27283064 case r of
27293065 D.Yield arr s -> do
27303066 let len = byteLength buf + byteLength arr
2731− if len > n
3067+ if len > maxBytes
27323068 then return $
27333069 D.Skip (SpliceYielding buf (SpliceBuffering s arr))
27343070 else do
2735− -- XXX check this in the SpliceInitial case
2736− buf1 <- if byteCapacity buf < n
2737− then liftIO $ realloc n buf
3071+ buf1 <- if byteCapacity buf < maxBytes
3072+ then liftIO $ reallocExplicitAs
3073+ ps (SIZE_OF(a)) maxBytes buf
27383074 else return buf
27393075 buf2 <- spliceUnsafe buf1 arr
27403076 return $ D.Skip (SpliceBuffering s buf2)
@@ -2745,108 +3081,119 @@ rCompactLE n (D.Stream step state) =
27453081
27463082 step' _ (SpliceYielding arr next) = return $ D.Yield arr next
27473083
2748−-- | Fold @fCompactGE n@ coalesces adjacent arrays in the input stream
2749−-- until the size becomes greater than or equal to n.
2750−--
2751−{-# INLINE_NORMAL fCompactGE #-}
2752−fCompactGE ::
3084+
3085+{-# INLINE_NORMAL fCompactGeAs #-}
3086+fCompactGeAs ::
27533087 forall m a. (MonadIO m, Unbox a)
2754− => Int -> FL.Fold m (MutArray a) (MutArray a)
2755−fCompactGE n = Fold step initial extract extract
3088+ => PinnedState -> Int -> FL.Fold m (MutArray a) (MutArray a)
3089+fCompactGeAs ps minElems = Fold step initial extract extract
27563090
27573091 where
27583092
2759− nBytes = n * SIZE_OF(a)
3093+ minBytes = minElems * SIZE_OF(a)
27603094
27613095 functionName = "Streamly.Internal.Data.MutArray.fCompactGE"
27623096
27633097 initial =
27643098 return
2765− $ if n < 0
3099+ $ if minElems < 0
27663100 then error
27673101 $ functionName
27683102 ++ ": the size of arrays ["
2769− ++ show n ++ "] must be a natural number"
3103+ ++ show minElems ++ "] must be a natural number"
27703104 else FL.Partial Nothing
27713105
27723106 step Nothing arr =
27733107 return
27743108 $ let len = byteLength arr
2775− in if len >= nBytes
3109+ in if len >= minBytes
27763110 then FL.Done arr
27773111 else FL.Partial (Just arr)
3112+ -- XXX Buffer arrays as a list to avoid copy and reallocations
27783113 step (Just buf) arr = do
27793114 let len = byteLength buf + byteLength arr
27803115 buf1 <-
27813116 if byteCapacity buf < len
2782− then liftIO $ realloc (max len nBytes) buf
3117+ then liftIO $ reallocExplicitAs
3118+ ps (SIZE_OF(a)) (max minBytes len) buf
27833119 else return buf
27843120 buf2 <- spliceUnsafe buf1 arr
2785− if len >= n
3121+ if len >= minBytes
27863122 then return $ FL.Done buf2
27873123 else return $ FL.Partial (Just buf2)
27883124
27893125 extract Nothing = return nil
27903126 extract (Just buf) = return buf
27913127
2792−-- | Like 'compactGE' but for transforming folds instead of stream.
3128+-- | Fold @fCompactGE minElems@ coalesces adjacent arrays in the input stream
3129+-- until the size becomes greater than or equal to @minElems@.
27933130--
2794−-- >>> lCompactGE n = Fold.many (MutArray.fCompactGE n)
2795−--
2796−{-# INLINE_NORMAL lCompactGE #-}
2797−lCompactGE :: (MonadIO m, Unbox a)
2798− => Int -> Fold m (MutArray a) () -> Fold m (MutArray a) ()
2799−-- lCompactGE n = Fold.many (fCompactGE n)
2800−lCompactGE n (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) =
28013152 Fold step initial extract final
28023153
28033154 where
28043155
3156+ minBytes = minElems * SIZE_OF(a)
3157+
28053158 functionName = "Streamly.Internal.Data.MutArray.lCompactGE"
28063159
28073160 initial = do
2808− when (n <= 0) $
3161+ when (minElems <= 0) $
28093162 -- XXX we can pass the module string from the higher level API
28103163 error $ functionName ++ ": the size of arrays ["
2811− ++ show n ++ "] must be a natural number"
3164+ ++ show minElems ++ "] must be a natural number"
28123165
28133166 r <- initial1
28143167 return $ first (Tuple' Nothing) r
28153168
2816− step (Tuple' Nothing r1) arr =
2817− let len = byteLength arr
2818− in if len >= n
2819− then do
2820− r <- step1 r1 arr
2821− case r of
2822− FL.Done _ -> return $ FL.Done ()
2823− FL.Partial s -> do
2824− _ <- final1 s
2825− res <- initial1
2826− return $ first (Tuple' Nothing) res
2827− else return $ FL.Partial $ Tuple' (Just arr) r1
2828−
2829− step (Tuple' (Just buf) r1) arr = do
2830− let len = byteLength buf + byteLength arr
2831− buf1 <- if byteCapacity buf < len
2832− then liftIO $ realloc (max n len) buf
2833− else return buf
2834− buf2 <- spliceUnsafe buf1 arr
2835−
2836− -- XXX this is common in both the equations of step
2837− if len >= n
3169+ {-# INLINE runInner #-}
3170+ runInner len acc buf =
3171+ if len >= minBytes
28383172 then do
2839− r <- step1 r1 buf2
3173+ r <- step1 acc buf
28403174 case r of
28413175 FL.Done _ -> return $ FL.Done ()
28423176 FL.Partial s -> do
28433177 _ <- final1 s
28443178 res <- initial1
28453179 return $ first (Tuple' Nothing) res
2846− else return $ FL.Partial $ Tuple' (Just buf2) r1
3180+ else return $ FL.Partial $ Tuple' (Just buf) acc
3181+
3182+ step (Tuple' Nothing r1) arr =
3183+ runInner (byteLength arr) r1 arr
3184+
3185+ -- XXX Buffer arrays as a list to avoid copy and reallocations
3186+ step (Tuple' (Just buf) r1) arr = do
3187+ let len = byteLength buf + byteLength arr
3188+ buf1 <- if byteCapacity buf < len
3189+ then liftIO $ reallocExplicitAs
3190+ ps (SIZE_OF(a)) (max minBytes len) buf
3191+ else return buf
3192+ buf2 <- spliceUnsafe buf1 arr
3193+ runInner len r1 buf2
28473194
28483195 -- XXX Several folds do extract >=> final, therefore, we need to make final
2849− -- return "m b" rather than using extract post it if we want extract to be
3196+ -- return "m b" rather than using extract post it if we want extract to be
28503197 -- partial.
28513198 --
28523199 -- extract forces the pending buffer to be sent to the fold which is not
@@ -2860,6 +3207,23 @@ lCompactGE n (Fold step1 initial1 _ final1) =
28603207 FL.Partial rr -> final1 rr
28613208 FL.Done _ -> return ()
28623209
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+
28633227-- | @compactGE n stream@ coalesces adjacent arrays in the @stream@ until
28643228-- the size becomes greater than or equal to @n@.
28653229--
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+36−4
@@ -36,7 +36,7 @@
3636module Streamly.Internal.FileSystem.Path
3737 (
3838 -- * Path Types
39− Path
39+ Path (..)
4040 , File
4141 , Dir
4242 , Abs
@@ -82,6 +82,11 @@ module Streamly.Internal.FileSystem.Path
8282 , toChars
8383
8484 -- * Operations
85+ -- Do we need to export the separator functions? They are not essential if
86+ -- operations to split and combine paths are provided. If someone wants to
87+ -- work on paths at low level then they know what they are.
88+ , primarySeparator
89+ , isSeparator
8590 , extendPath
8691 , extendDir
8792 )
@@ -89,6 +94,7 @@ where
8994
9095import Control.Exception (Exception)
9196import Control.Monad.Catch (MonadThrow(..))
97+import Data.Char (chr)
9298import Data.Functor.Identity (Identity(..))
9399import Data.Word (Word8)
94100#if defined(mingw32_HOST_OS) || defined(__MINGW32__)
@@ -151,6 +157,15 @@ instance Exception PathException
151157-- | A type representing file system paths for directories or files.
152158newtype Path = Path (Array WORD_TYPE) -- deriving Eq
153159
160+-- Show instance prints raw bytes without any decoding for rountdtripping.
161+-- Should we print this as a string instead, may be useful for ascii chars but
162+-- utf8 encoded chars may be unprintable. Better use toString if you want to
163+-- pretty print the path.
164+{-
165+instance Show Path where
166+ show (Path x) = show x
167+-}
168+
154169-- XXX Do we need a type for file or dir Name as names cannot have the
155170-- separator char and there may be other restrictions on names? For example,
156171-- length restriction. A file name cannot be "." or "..". We can use the types
@@ -438,8 +453,25 @@ mkRelFile = undefined
438453-- Operations
439454------------------------------------------------------------------------------
440455
441−separator :: WORD_TYPE
442−separator = SEPARATOR
456+separatorWord :: WORD_TYPE
457+separatorWord = SEPARATOR
458+
459+-- Portable definition for exporting.
460+
461+-- | Primary path separator character, @/@ on Posix and @\\@ on Windows.
462+-- Windows supports @/@ too as a separator. Please use 'isSeparator' for
463+-- testing if a char is a separator char.
464+primarySeparator :: Char
465+primarySeparator = chr (SEPARATOR)
466+
467+-- | On Posix only @/@ is a path separator but in windows it could be either
468+-- @/@ or @\\@.
469+isSeparator :: Char -> Bool
470+#if defined(mingw32_HOST_OS) || defined(__MINGW32__)
471+isSeparator c = (c == '/') || (c == '\\')
472+#else
473+isSeparator = (== '/')
474+#endif
443475
444476-- If we append an absolute path it may fail with an error if the 'Path'
445477-- implementation stores absolute path information (a leading separator char).
@@ -465,7 +497,7 @@ extendPath (Path a) (Path b) =
465497 newArr = unsafePerformIO $ do
466498 arr <- MutArray.new len
467499 arr1 <- MutArray.spliceUnsafe arr (Array.unsafeThaw a)
468− arr2 <- MutArray.snocUnsafe arr1 separator
500+ arr2 <- MutArray.snocUnsafe arr1 separatorWord
469501 arr3 <- MutArray.spliceUnsafe arr2 (Array.unsafeThaw b)
470502 return (Array.unsafeFreeze arr3)
471503 in Path newArr
core/src/Streamly/Internal/Unicode/Stream.hs+1−1
@@ -1057,7 +1057,7 @@ stripHead = Stream.dropWhile isSpace
10571057-- >>> Stream.fold Fold.toList $ Unicode.lines Fold.toList (Stream.fromList "lines\nthis\nstring\n\n\n")
10581058-- ["lines","this","string","",""]
10591059--
1060−-- > lines = Stream.splitOnSuffix (== '\n')
1060+-- >>> lines f = Stream.foldMany (Fold.takeEndBy_ (== '\n') f)
10611061--
10621062-- /Pre-release/
10631063{-# INLINE lines #-}
core/src/Streamly/Internal/Unicode/String.hs+7−7
@@ -39,14 +39,14 @@ module Streamly.Internal.Unicode.String
3939 --
4040 -- $setup
4141
42− str
42+ str
4343 ) where
4444
4545
4646import Control.Applicative (Alternative(..))
4747import Control.Exception (displayException)
4848import Data.Functor.Identity (runIdentity)
49−import Streamly.Internal.Data.Parser (Parser)
49+import Streamly.Internal.Data.Parser (Parser, ParseError)
5050
5151import Language.Haskell.TH
5252import Language.Haskell.TH.Quote
@@ -106,9 +106,12 @@ strSegmentExp (StrVar name) = do
106106strExp :: [StrSegment] -> Q Exp
107107strExp xs = appE [| concat |] $ listE $ map strSegmentExp xs
108108
109+parseStr :: String -> Either ParseError [StrSegment]
110+parseStr = runIdentity . Stream.parse strParser . Stream.fromList
111+
109112expandVars :: String -> Q Exp
110−expandVars ln =
111− case runIdentity $ Stream.parse strParser (Stream.fromList ln) of
113+expandVars input =
114+ case parseStr input of
112115 Left e ->
113116 fail $ "str QuasiQuoter parse error: " ++ displayException e
114117 Right x ->
@@ -141,9 +144,6 @@ expandVars ln =
141144-- :}
142145-- "hello world!"
143146--
144−-- Bugs: because of a bug in parsers, a lone # at the end of input gets
145−-- removed.
146−--
147147str :: QuasiQuoter
148148str =
149149 QuasiQuoter
core/streamly-core.cabal+14−10
@@ -1,6 +1,6 @@
11cabal-version: 2.2
22name: streamly-core
3−version: 0.2.1
3+version: 0.2.2
44synopsis: Streaming, parsers, arrays, serialization and more
55description:
66 For upgrading to streamly-0.9.0+ please read the
@@ -116,6 +116,7 @@ extra-doc-files:
116116 docs/*.md
117117 docs/ApiChangelogs/0.1.0.txt
118118 docs/ApiChangelogs/0.1.0-0.2.0.txt
119+ docs/ApiChangelogs/0.2.0-0.2.2.txt
119120
120121source-repository head
121122 type: git
@@ -217,8 +218,6 @@ common compile-options
217218common default-extensions
218219 default-extensions:
219220 BangPatterns
220− CApiFFI
221− CPP
222221 ConstraintKinds
223222 DeriveDataTypeable
224223 DeriveGeneric
@@ -229,24 +228,29 @@ common default-extensions
229228 GeneralizedNewtypeDeriving
230229 InstanceSigs
231230 KindSignatures
232− LambdaCase
233− MagicHash
234231 MultiParamTypeClasses
235− PatternSynonyms
236232 RankNTypes
237− RecordWildCards
238233 ScopedTypeVariables
239234 StandaloneDeriving
240235 TupleSections
241236 TypeApplications
242− TypeFamilies
243237 TypeOperators
244− ViewPatterns
245238
239+ -- Not GHC2021
240+ CApiFFI
241+ CPP
242+ DefaultSignatures
243+ LambdaCase
244+ MagicHash
245+ RecordWildCards
246+
247+ -- TypeFamilies is required by IsList, IsMap type classes and
248+ -- Unbox generic deriving code.
249+ -- TypeFamilies
246250 -- MonoLocalBinds, enabled by TypeFamilies, causes performance
247251 -- regressions. Disable it. This must come after TypeFamilies,
248252 -- otherwise TypeFamilies will enable it again.
249− NoMonoLocalBinds
253+ -- NoMonoLocalBinds
250254
251255 -- UndecidableInstances -- Does not show any perf impact
252256 -- UnboxedTuples -- interferes with (#.)
docs/Developer/optimization-guidelines.md+12
@@ -191,6 +191,18 @@ the stages of a loop, it is often not useful to inline the whole loop
191191itself, in fact we may have to occasionally use a `NOINLINE` so that the
192192compiler does not inline it.
193193
194+We need to be careful about NOINLINE on polymorphic functions. When
195+NOINLINE is used, the function cannot be specialized causing huge
196+performance degradation. To avoid that we can either make the
197+function monomorphic or sometimes using `NOINLINE [0]` can do the
198+trick. `NOINLINE [0]` blocks inlining in earlier phases but lets it
199+specialize in the last phase, inlining in the last phase is left to
200+the compiler. `INLINE [0]` may sound the same as `NOINLINE [0]`,
201+however, it behaves differently because we ask the compiler to INLINE
202+it compulsorily and it may not give us the desired results. The
203+Data.Fold.writeLastN benchmark is one such case where `NOINLINE [0]`
204+provides much better performance than `INLINE [0]`.
205+
194206### NoSpecConstr
195207
196208It is not always useful to specialize function calls for all constructors, some
docs/User/Project/Changelog.md+1−1
@@ -2,7 +2,7 @@
22
33<!-- See rendered changelog at https://streamly.composewell.com -->
44
5−## Unreleased
5+## 0.10.1 (Jan 2024)
66
77* Fix TH macros in `Streamly.Data.Stream.MkType` for GHC 9.6 and above.
88
docs/User/Project/README.md+1
@@ -1,6 +1,7 @@
11# [Streamly][]: Idiomatic Haskell with C-Like Performance
22
33[![Gitter chat](https://badges.gitter.im/composewell/gitter.svg)](https://gitter.im/composewell/streamly)
4+[![Hackage](https://img.shields.io/hackage/v/streamly-core.svg?style=flat)](https://hackage.haskell.org/package/streamly-core)
45[![Hackage](https://img.shields.io/hackage/v/streamly.svg?style=flat)](https://hackage.haskell.org/package/streamly)
56
67## Upgrading to 0.9.0+
src/Streamly/Internal/Data/IsMap/HashMap.hs+5
@@ -1,3 +1,8 @@
1+{-# LANGUAGE TypeFamilies #-}
2+-- Must come after TypeFamilies, otherwise it is re-enabled.
3+-- MonoLocalBinds enabled by TypeFamilies causes perf regressions in general.
4+{-# LANGUAGE NoMonoLocalBinds #-}
5+
16{-# OPTIONS_GHC -Wno-orphans #-}
27
38-- |
src/Streamly/Internal/Data/Stream/IsStream/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]