on master help sign in
87 files changed+4842−2566
since splitsnapshots
unifiedsplit
.circleci/config.yml+75−274 .github/workflows/haskell.yml+53−60 .hlint.ignore+5 benchmark/Streamly/Benchmark/Data/Fold.hs+2−2 benchmark/Streamly/Benchmark/Data/Serialize.hs+10−15 benchmark/Streamly/Benchmark/Data/Serialize/TH.hs+3−3 benchmark/Streamly/Benchmark/Data/Stream/Eliminate.hs+2 benchmark/Streamly/Benchmark/Data/Stream/StreamKAlt.hs+1−1 benchmark/Streamly/Benchmark/Data/Stream/ToStreamK.hs+1−1 benchmark/Streamly/Benchmark/FileSystem/Handle/Read.hs+7−8 benchmark/Streamly/Benchmark/FileSystem/Handle/ReadWrite.hs+8−9 benchmark/Streamly/Benchmark/Unicode/Stream.hs+4−3 configure+9−9 configure.ac+1−1 core/configure+9−9 core/configure.ac+1−1 core/docs/ApiChangelogs/0.2.0-0.2.2.txt+306 core/docs/Changelog.md+8 core/src/DocTestDataArray.hs+3 core/src/DocTestDataStream.hs+2 core/src/DocTestFileSystemHandle.hs+1−1 core/src/Streamly/Data/Array.hs+30−27 core/src/Streamly/Data/Array/Generic.hs+16−13 core/src/Streamly/Data/MutArray.hs+12−6 core/src/Streamly/Data/MutArray/Generic.hs+8−3 core/src/Streamly/Data/Stream.hs+129−20 core/src/Streamly/Internal/Data/Array.hs+551−128 core/src/Streamly/Internal/Data/Array/Generic.hs+22−6 core/src/Streamly/Internal/Data/Array/Stream.hs+57−564 core/src/Streamly/Internal/Data/Array/Type.hs+489−96 core/src/Streamly/Internal/Data/Fold/Combinators.hs−92 core/src/Streamly/Internal/Data/Fold/Type.hs+91 core/src/Streamly/Internal/Data/IsMap.hs+4 core/src/Streamly/Internal/Data/MutArray.hs+151−35 core/src/Streamly/Internal/Data/MutArray/Generic.hs+65−30 core/src/Streamly/Internal/Data/MutArray/Stream.hs+11−195 core/src/Streamly/Internal/Data/MutArray/Type.hs+1178−339 core/src/Streamly/Internal/Data/MutByteArray/Type.hs+47−12 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+2−12 core/src/Streamly/Internal/Data/Stream/Nesting.hs+19−146 core/src/Streamly/Internal/Data/Stream/Top.hs+243−115 core/src/Streamly/Internal/Data/Stream/Transform.hs+53−27 core/src/Streamly/Internal/Data/Stream/Transformer.hs+2−1 core/src/Streamly/Internal/Data/Stream/Type.hs+132−3 core/src/Streamly/Internal/Data/StreamK.hs+19−28 core/src/Streamly/Internal/Data/StreamK/Type.hs+80−11 core/src/Streamly/Internal/Data/Time/Units.hs−4 core/src/Streamly/Internal/Data/Unbox.hs+4−1 core/src/Streamly/Internal/Data/Unfold.hs+6−4 core/src/Streamly/Internal/FileSystem/File.hs+4−9 core/src/Streamly/Internal/FileSystem/Handle.hs+21−28 core/src/Streamly/Internal/FileSystem/Path.hs+515 core/src/Streamly/Internal/Unicode/Stream.hs+30−3 core/src/Streamly/Internal/Unicode/String.hs+7−7 core/streamly-core.cabal+15−10 docs/Developer/optimization-guidelines.md+12 docs/Developer/paths.rst+25−2 docs/User/Project/Changelog.md+4 docs/User/Project/README.md+1 src/Streamly/Data/Stream/Prelude.hs+25−15 src/Streamly/Internal/Data/IsMap/HashMap.hs+5 src/Streamly/Internal/Data/Stream/Concurrent.hs+19−11 src/Streamly/Internal/Data/Stream/IsStream/Common.hs+1−1 src/Streamly/Internal/Data/Stream/IsStream/Expand.hs+5 src/Streamly/Internal/Data/Stream/IsStream/Reduce.hs+1−1 src/Streamly/Internal/Data/Stream/IsStream/Top.hs+1−1 src/Streamly/Internal/Data/Stream/MkType.hs+33−34 src/Streamly/Internal/Data/Stream/Serial.hs+5 src/Streamly/Internal/Data/Stream/Zip.hs+5 src/Streamly/Internal/FileSystem/Event.hs+3 src/Streamly/Internal/FileSystem/Event/Darwin.hs+1−1 src/Streamly/Internal/FileSystem/Event/Linux.hs+2−2 src/Streamly/Internal/FileSystem/FD.hs+5−5 src/Streamly/Internal/Network/Inet/TCP.hs+3−3 src/Streamly/Internal/Network/Socket.hs+11−10 src/Streamly/Internal/Unicode/Char.hs+3−3 streamly.cabal+11−11 test/Streamly/Test/Data/Array.hs+2−2 test/Streamly/Test/Data/Fold.hs+7−6 test/Streamly/Test/Data/Serialize.hs+20−1 test/Streamly/Test/Data/Serialize/TH.hs+2−2 test/Streamly/Test/FileSystem/Handle.hs+2−2 test/Streamly/Test/Unicode/Char.hs+1−1
.circleci/config.yml+75−274
@@ -1,7 +1,7 @@
11version: 2.1
22
33#-----------------------------------------------------------------------------
4−# packcheck-0.4.2
4+# packcheck-0.7.0
55# Packcheck global environment variables
66#-----------------------------------------------------------------------------
77
@@ -11,6 +11,8 @@ env: &env
1111 # Common options
1212 # ------------------------------------------------------------------------
1313 # GHC_OPTIONS: "-Werror"
14+ # For updating see: https://downloads.haskell.org/~ghcup/
15+ GHCUP_VERSION: 0.1.20.0
1416 CABAL_REINIT_CONFIG: "y"
1517 LC_ALL: "C.UTF-8"
1618
@@ -22,7 +24,6 @@ env: &env
2224 # DISABLE_DOCS: "y"
2325 # DISABLE_SDIST_BUILD: "y"
2426 # DISABLE_DIST_CHECKS: "y"
25− ENABLE_INSTALL: "y"
2627
2728 # ------------------------------------------------------------------------
2829 # stack options
@@ -38,15 +39,12 @@ env: &env
3839 # cabal options
3940 # ------------------------------------------------------------------------
4041 CABAL_CHECK_RELAX: "y"
41− CABAL_NO_SANDBOX: "y"
42− CABAL_HACKAGE_MIRROR: "hackage.haskell.org:http://hackage.fpcomplete.com"
4342 CABAL_BUILD_OPTIONS: "-j1 --flag limit-build-mem"
4443
4544 # ------------------------------------------------------------------------
4645 # Where to find the required tools
4746 # ------------------------------------------------------------------------
48− PATH: /opt/ghc/bin:/opt/ghcjs/bin:/sbin:/usr/sbin:/bin:/usr/bin
49− TOOLS_DIR: /opt
47+ PATH: /sbin:/usr/sbin:/bin:/usr/bin
5048
5149 # ------------------------------------------------------------------------
5250 # Location of packcheck.sh (the shell script invoked to perform CI tests ).
@@ -59,15 +57,15 @@ env: &env
5957 # If you have not committed packcheck.sh in your repo at PACKCHECK
6058 # then it is automatically pulled from this URL.
6159 PACKCHECK_GITHUB_URL: "https://raw.githubusercontent.com/composewell/packcheck"
62− PACKCHECK_GITHUB_COMMIT: "a302772c8f3791c477edd1764b82f8d1f5b22fdf"
60+ PACKCHECK_GITHUB_COMMIT: "f6cfd7b7a33b4bf6fd63cce6a3fb3d26f5031403"
6361
6462executors:
6563 amd64-executor:
6664 docker:
67− - image: ubuntu:xenial
65+ - image: ubuntu:latest
6866 x86-executor:
6967 docker:
70− - image: i386/ubuntu:eoan
68+ - image: i386/ubuntu:focal
7169
7270#-----------------------------------------------------------------------------
7371# Common utility stuff, not to be modified usually
@@ -79,38 +77,50 @@ preinstall: &preinstall
7977 # required for https/cache save and restore
8078 apt-get install -y ca-certificates
8179
82− # required to (re)generate the configure script
83− apt-get install -y autoconf
84−
85− # For ghc and cabal-install packages from hvr's ppa
80+ # Alternative way of installing ghc and cabal, directly from
81+ # haskell.org instead of using ghcup. NOTE: this is for Debian
82+ # only and is debian release specific.
8683 # gnupg is required for apt-key to work
87− apt-get install -y gnupg
88− apt-get install -y apt-transport-https
89− apt-key adv --keyserver keyserver.ubuntu.com --recv-keys BA3CBA3FFE22B574
90− apt-get install -y software-properties-common
91− add-apt-repository -y ppa:hvr/ghc
92− # echo "deb http://downloads.haskell.org/debian stretch main" >> /etc/apt/sources.list
93− echo "deb-src http://ppa.launchpad.net/hvr/ghc/ubuntu xenial main" >> /etc/apt/sources.list
94− apt-get update
84+ #apt-get install -y gnupg
85+ #apt-key adv --keyserver keyserver.ubuntu.com --recv-keys BA3CBA3FFE22B574
86+ #echo "deb http://downloads.haskell.org/debian buster main" >> /etc/apt/sources.list
87+ #apt-get update
9588
96− # required for packcheck
97− apt-get install -y curl
89+ # required for outbound https for stack and for stack setup
90+ apt-get install -y netbase xz-utils make
91+ apt-get install -y zlib1g-dev
9892
99− # required for circleci.
100− apt-get install -y libcurl4-gnutls-dev
101− apt-get install -y git
93+ # For ghcup to install ghc
94+ if test -n "$GHCUP_VERSION"
95+ then
96+ apt-get install -y gcc
97+ apt-get install -y g++
98+ fi
99+
100+ # libgmp required by ghc for linking
101+ apt-get install -y libgmp-dev
102102 apt-get install -y libtinfo-dev
103103
104− # required for outbound https for stack and for stack setup
105− apt-get install -y netbase xz-utils make
104+ # Required by cabal when git URL is specified in project file
105+ apt-get install -y git
106+
107+ # Required for and by packcheck
108+ apt-get install -y curl
106109
107−project-preinstall: &project-preinstall
108− run: |
109110 # Get packcheck if needed
110− CURL=$(which curl)
111− PACKCHECK_URL=${PACKCHECK_GITHUB_URL}/${PACKCHECK_GITHUB_COMMIT}/packcheck.sh
112− if test ! -e "$PACKCHECK"; then $CURL -sL -o "$PACKCHECK" $PACKCHECK_URL; fi;
113− chmod +x $PACKCHECK
111+ if test ! -e "$PACKCHECK"
112+ then
113+ if test -z "$PACKCHECK_GITHUB_COMMIT"
114+ then
115+ die "PACKCHECK_GITHUB_COMMIT is not specified."
116+ fi
117+ PACKCHECK_URL=${PACKCHECK_GITHUB_URL}/${PACKCHECK_GITHUB_COMMIT}/packcheck.sh
118+ curl --fail -sL -o "$PACKCHECK" $PACKCHECK_URL || exit 1
119+ chmod +x $PACKCHECK
120+ elif test ! -x "$PACKCHECK"
121+ then
122+ chmod +x $PACKCHECK
123+ fi
114124
115125restore: &restore
116126 # Needs to happen after installing ca-certificates
@@ -126,266 +136,71 @@ save: &save
126136 save_cache:
127137 key: v1-{{ .Environment.CIRCLE_JOB }}-{{ .Branch }}
128138 paths:
129− - ~/.cabal
130− - ~/.ghc
131139 - ~/.local
140+ - ~/.cabal
132141 - ~/.stack
142+ - ~/.ghcup
133143
134144#-----------------------------------------------------------------------------
135145# Build matrix
136146#-----------------------------------------------------------------------------
137147
138148jobs:
139− cabal-ghc-8_10_4:
149+ cabal-ghc-8_6_5:
140150 <<: *env
141151 executor: amd64-executor
142152 steps:
143− - *preinstall
144153 - checkout
145− - *project-preinstall
146154 - *restore
155+ - *preinstall
147156 - run:
148157 environment:
158+ GHCVER: "8.6.5"
159+ CABAL_VERSION: "3.6.2.0"
149160 CABAL_PROJECT: "cabal.project"
150− DISABLE_TEST: "yes"
151− DISABLE_BENCH: "yes"
152161 DISABLE_SDIST_BUILD: "yes"
162+ CABAL_BUILD_OPTIONS: "--flag debug --flag -opt"
153163 command: |
154− apt-get install -y ghc-8.10.4
155− apt-get install -y cabal-install-3.4
156164 bash -c "$PACKCHECK cabal"
157165 - *save
158− stack-ghc-8_8:
159− <<: *env
160− executor: amd64-executor
161− steps:
162− - *preinstall
163− - checkout
164− - *project-preinstall
165− - *restore
166− - run:
167− name: GHC 8.8 + stack lts-16.12
168− environment:
169− BUILD: "stack"
170− RESOLVER: "lts-16.12"
171− SDIST_OPTIONS: "--ignore-check"
172− STACK_BUILD_OPTIONS: "--flag streamly-benchmarks:-opt"
173− command: |
174− apt-get build-dep -y ghc-8.8.4
175− apt-get install -y cabal-install-3.2
176− bash -c "$PACKCHECK $BUILD"
177− - *save
178− cabal-ghc-8_6_5:
179− <<: *env
180− executor: amd64-executor
181− steps:
182− - *preinstall
183− - checkout
184− - *project-preinstall
185− - *restore
186− - run: |
187− apt-get install -y ghc-8.6.3
188− apt-get install -y cabal-install-2.4
189− bash -c "$PACKCHECK cabal-v2"
190− - *save
191− cabal-ghc-8_4_4:
192− <<: *env
193− executor: x86-executor
194− steps:
195− - *preinstall
196− - checkout
197− - *project-preinstall
198− - *restore
199− - run:
200− name: GHC 8.4.4 + x86 + debug
201− environment:
202− BUILD: "cabal-v2"
203− GHCVER: "8.4.4"
204− CABAL_BUILD_OPTIONS: "--flag debug --flag limit-build-mem"
205− command: |
206− apt-get install -y ghc-8.4.4
207− apt-get install -y cabal-install-3.2
208− bash -c "$PACKCHECK $BUILD"
209− - *save
210− cabal-ghc-8_2_2:
166+ cabal-ghc-9_8_1-docspec:
211167 <<: *env
212168 executor: amd64-executor
213169 steps:
214− - *preinstall
215170 - checkout
216− - *project-preinstall
217171 - *restore
218− - run:
219− environment:
220− BUILD: "cabal-v2"
221− DISABLE_TEST: "yes"
222− DISABLE_BENCH: "yes"
223− DISABLE_DOCS: "yes"
224− DISABLE_SDIST_BUILD: "yes"
225− DISABLE_DIST_CHECKS: "yes"
226− command: |
227− apt-get install -y ghc-8.2.2
228− apt-get install -y cabal-install-3.2
229− bash -c "$PACKCHECK $BUILD"
230− - *save
231− cabal-ghc-8_0_2:
232− <<: *env
233− executor: amd64-executor
234− steps:
235172 - *preinstall
236− - checkout
237− - *project-preinstall
238− - *restore
239173 - run:
240174 environment:
241− BUILD: "cabal-v2"
242− DISABLE_TEST: "yes"
243− DISABLE_BENCH: "yes"
244− DISABLE_DOCS: "yes"
245− DISABLE_SDIST_BUILD: "yes"
246− DISABLE_DIST_CHECKS: "yes"
175+ GHCVER: "9.8.1"
176+ CABAL_VERSION: "3.10.1.0"
177+ CABAL_PROJECT: "cabal.project.doctest"
178+ DISABLE_SDIST_BUILD: "y"
179+ DISABLE_TEST: "y"
180+ DISABLE_BENCH: "y"
181+ DISABLE_DOCS: "y"
182+ ENABLE_DOCSPEC: "y"
183+ DOCSPEC_URL: https://github.com/phadej/cabal-extras/releases/download/cabal-docspec-0.0.0.20210111/cabal-docspec-0.0.0.20210111.xz
184+ DOCSPEC_OPTIONS: "--timeout 60 --check-properties --property-variables xs"
247185 command: |
248− apt-get install -y ghc-8.0.2
249− apt-get install -y cabal-install-3.2
250− bash -c "$PACKCHECK $BUILD"
251− - *save
252− cabal-ghc-7_10_3:
253− <<: *env
254− executor: amd64-executor
255− steps:
256− - *preinstall
257− - checkout
258− - *project-preinstall
259− - *restore
260− - run: |
261− apt-get install -y ghc-7.10.3
262− apt-get install -y cabal-install-2.4
263− bash -c "$PACKCHECK cabal-v2"
186+ bash -c "$PACKCHECK cabal"
264187 - *save
265− stack-ghc-8_4:
188+ hlint-trailing-spaces:
266189 <<: *env
267190 executor: amd64-executor
268191 steps:
269− - *preinstall
270192 - checkout
271− - *project-preinstall
272193 - *restore
273− - run: |
274− bash -c "$PACKCHECK stack RESOLVER=lts-12"
275− - *save
276− stack-ghc-8_2:
277− <<: *env
278− executor: amd64-executor
279− steps:
280194 - *preinstall
281− - checkout
282− - *project-preinstall
283− - *restore
284− - run: |
285− bash -c "$PACKCHECK stack RESOLVER=lts-11"
286− - *save
287− cabal-ghcjs-8_4:
288− <<: *env
289− executor: amd64-executor
290− steps:
291− - *preinstall
292− - checkout
293− - *project-preinstall
294− - *restore
295195 - run:
296− name: GHCJS 8.4+no-test+no-docs
196+ name: hlint and trailing spaces
297197 environment:
298− BUILD: cabal-v2
299− ENABLE_GHCJS: "yes"
300− DISABLE_TEST: "yes"
301− ENABLE_INSTALL: ""
302− DISABLE_DOCS: "yes"
303198 DISABLE_SDIST_BUILD: "yes"
199+ HLINT_VERSION: 3.6.1
200+ HLINT_OPTIONS: lint
201+ HLINT_TARGETS: core/src src test benchmark
304202 command: |
305− add-apt-repository -y ppa:hvr/ghcjs
306− add-apt-repository -y 'deb https://deb.nodesource.com/node_11.x xenial main'
307− curl -sSL https://deb.nodesource.com/gpgkey/nodesource.gpg.key | apt-key add -
308− apt-get update
309− apt-get install -y cabal-install-3.2
310− apt-get install -y nodejs
311− apt-get install -y ghcjs-8.4
312− cabal user-config update -a "jobs: 1"
313− bash -c "$PACKCHECK $BUILD"
314− - *save
315− coveralls-ghc-8_2_2:
316− <<: *env
317− executor: amd64-executor
318− steps:
319− - *preinstall
320− - checkout
321− - *project-preinstall
322− - *restore
323− - run:
324− environment:
325− BUILD: cabal
326− COVERALLS_OPTIONS: "--repo-token=\"$REPO_TOKEN\" --coverage-mode=StrictlyFullLines --exclude-dir=test test"
327− GHC_OPTIONS: "-DCOVERAGE_BUILD"
328− command: |
329− apt-get install -y ghc-8.2.2
330− apt-get install -y cabal-install-2.4
331− bash -c "$PACKCHECK cabal-v1"
332− - *save
333− coveralls-ghc-8_8_3:
334− <<: *env
335− executor: amd64-executor
336− steps:
337− - *preinstall
338− - checkout
339− - *project-preinstall
340− - *restore
341− - run:
342− environment:
343− BUILD: cabal-v2
344− COVERALLS_OPTIONS: "--repo-token=\"$REPO_TOKEN\" --coverage-mode=StrictlyFullLines --exclude-dir=test"
345− CABAL_PROJECT: "cabal.project.coverage"
346− CABAL_BUILD_TARGETS: ""
347− command: |
348− apt-get install -y ghc-8.8.3
349− apt-get install -y cabal-install-3.0
350− bash -c "$PACKCHECK cabal-v2"
351− no_output_timeout: 25m
352− - *save
353− coveralls-ghc-8_10_2:
354− <<: *env
355− executor: amd64-executor
356− steps:
357− - *preinstall
358− - checkout
359− - *project-preinstall
360− - *restore
361− - run:
362− name: Build and test with inspection, coverage, Werror
363− command: |
364− apt-get install -y ghc-8.10.2
365− apt-get install -y cabal-install-3.2
366− # Run tests with coverage
367− cabal update
368− cabal user-config update -a "jobs: 1"
369− bin/test.sh --coverage --raw
370− # Upload results to coveralls.io
371− PATH=$HOME/.cabal/bin:$PATH
372− export PATH
373− which hpc-coveralls 2>/dev/null || cabal install --project-file cabal.project.hpc-coveralls hpc-coveralls
374− hpc-coveralls --repo-token="$REPO_TOKEN" --coverage-mode=StrictlyFullLines
375− no_output_timeout: 25m
376− - *save
377− trailing-spaces:
378− <<: *env
379− executor: amd64-executor
380− steps:
381− - *preinstall
382− - checkout
383− - *project-preinstall
384− - *restore
385− - run:
386− name: Check trailing spaces
387− command: |
388− # bash -c "$PACKCHECK $BUILD" || exit 1
203+ bash -c "$PACKCHECK hlint" || exit 1
389204 echo "Checking trailing spaces..."
390205 count=$(find . -name "*.hs" -exec grep -H '\ $' {} \; | tee /dev/tty | wc -l)
391206 exit $count
@@ -395,23 +210,9 @@ workflows:
395210 version: 2
396211 build:
397212 jobs:
398− - cabal-ghc-8_10_4:
399− name: GHC 8.10.4 + sdist + no-test + no-bench
400− #- cabal-ghc-8.6.5
401− #- cabal-ghc-8_4_4:
402− # name: GHC 8.4.4 + x86 + debug
403− #- cabal-ghc-8_2_2:
404− # name: GHC 8.2.2 + no-test + no-bench + no-docs
405− #- cabal-ghc-8_0_2:
406− # name: GHC 8.0.2 + no-test + no-bench + no-docs
407− #- cabal-ghc-7.10.3
408− # - cabal-ghcjs-8_4:
409− # name: GHCJS 8.4 + no-test + no-docs
410− #- stack-ghc-8.4
411− #- stack-ghc-8.2
412− #- coveralls-ghc-8.2.2
413− #- coveralls-ghc-8.8.3
414− #- coveralls-ghc-8_10_2:
415− # name: GHC 8.10.2 + inspection + coverage + Werror
416− - trailing-spaces:
417− name: Trailing Spaces
213+ #- cabal-ghc-8_6_5:
214+ # name: 8.6.5-debug-unoptimized
215+ - cabal-ghc-9_8_1-docspec:
216+ name: ghc-9.8.1-docspec
217+ - hlint-trailing-spaces:
218+ name: hlint and trailing spaces
.github/workflows/haskell.yml+53−60
@@ -13,9 +13,15 @@ jobs:
1313 # packcheck environment variables
1414 LC_ALL: C.UTF-8
1515 BUILD: ${{ matrix.build }}
16+ # For updating see: https://downloads.haskell.org/~ghcup/
17+ GHCUP_VERSION: 0.1.20.0
18+ DOCSPEC_URL: https://github.com/phadej/cabal-extras/releases/download/cabal-docspec-0.0.0.20210111/cabal-docspec-0.0.0.20210111.xz
19+ DOCSPEC_OPTIONS: "--timeout 60 --check-properties --property-variables xs"
20+ GHCUP_GHC_OPTIONS: ${{ matrix.ghcup_ghc_options }}
1621 GHCVER: ${{ matrix.ghc_version }}
1722 CABALVER: ${{ matrix.cabal_version }}
1823 DISABLE_DOCS: ${{ matrix.disable_docs }}
24+ ENABLE_DOCSPEC: ${{ matrix.enable_docspec }}
1925 DISABLE_TEST: ${{ matrix.disable_test }}
2026 DISABLE_BENCH: ${{ matrix.disable_bench }}
2127 DISABLE_DIST_CHECKS: ${{ matrix.disable_dist_checks }}
@@ -39,16 +45,16 @@ jobs:
3945 STACK_BUILD_OPTIONS: ${{ matrix.stack_build_options }}
4046
4147 # packcheck location and revision
42− PACKCHECK_LOCAL_PATH: "./packcheck.sh"
48+ PACKCHECK: "./packcheck.sh"
4349 PACKCHECK_GITHUB_URL: "https://raw.githubusercontent.com/composewell/packcheck"
44− PACKCHECK_GITHUB_COMMIT: "a68b7b9c7c21eef8ed273e67030efb1d4fec027c"
50+ PACKCHECK_GITHUB_COMMIT: "v0.7.0"
4551
4652 # Pull token from "secrets" setting of the github repo
4753 COVERALLS_TOKEN: ${{ secrets.COVERALLS_TOKEN }}
4854 COVERAGE: ${{ matrix.coverage }}
49− DOCTEST: ${{ matrix.doctest }}
5055
5156 # hlint
57+ HLINT_VERSION: 3.6.1
5258 HLINT_OPTIONS: "lint"
5359 HLINT_TARGETS: "core/src src test benchmark"
5460
@@ -63,7 +69,7 @@ jobs:
6369 # The order is important to optimize fail-fast.
6470 name:
6571 - 9.8.1-Werror
66− - 9.2.8-docspec
72+ # - 9.8.1-docspec
6773 # - 8.10.7-coverage
6874
6975 # Note: if cabal.project is not specified benchmarks and tests won't
@@ -72,9 +78,19 @@ jobs:
7278 include:
7379 - name: head
7480 ghc_version: head
81+ # The URL may change, to find a working URL go to https://gitlab.haskell.org/ghc/ghc/-/jobs/
82+ # Find a debian10/11/12 job, click on a passed/failed status, at the
83+ # end of the output you will find the tar.xz name, put that tar
84+ # name after "raw/", and put the job name after "job=".
85+ # Also see https://github.com/mpickering/ghc-artefact-nix/blob/master/gitlab-artifact.nix
86+ #
87+ # May also use ghcup for installing ghc head version, use the
88+ # version "LatestNightly", and the following config:
89+ # ghcup config add-release-channel https://ghc.gitlab.haskell.org/ghcup-metadata/ghcup-nightlies-0.0.7.yaml
90+ ghcup_ghc_options: "-u https://gitlab.haskell.org/ghc/ghc/-/jobs/artifacts/master/raw/ghc-x86_64-linux-deb10-int_native-validate.tar.xz?job=x86_64-linux-deb10-int_native-validate"
7591 runner: ubuntu-latest
7692 build: cabal
77− cabal_version: 3.10.1.0
93+ cabal_version: 3.10.2.0
7894 cabal_project: cabal.project.ghc-head
7995 disable_sdist_build: "y"
8096 ignore_error: true
@@ -87,6 +103,18 @@ jobs:
87103 disable_sdist_build: "y"
88104 cabal_project: cabal.project.Werror
89105 ignore_error: false
106+ #- name: 9.8.1-docspec
107+ # ghc_version: 9.8.1
108+ # runner: ubuntu-latest
109+ # build: cabal
110+ # cabal_version: 3.10.1.0
111+ # cabal_project: cabal.project.doctest
112+ # disable_test: "y"
113+ # disable_bench: "y"
114+ # disable_docs: "y"
115+ # enable_docspec: "y"
116+ # disable_sdist_build: "y"
117+ # ignore_error: false
90118 - name: 9.8.1-fusion-inspection
91119 ghc_version: 9.8.1
92120 runner: ubuntu-latest
@@ -120,14 +148,6 @@ jobs:
120148 cabal_version: 3.6.2.0
121149 disable_sdist_build: "y"
122150 ignore_error: false
123− - name: 9.2.8-docspec
124− ghc_version: 9.2.8
125− runner: ubuntu-latest
126− doctest: "y"
127− cabal_version: 3.6.2.0
128− cabal_project: cabal.project
129− disable_sdist_build: "y"
130− ignore_error: false
131151 - name: 9.0.2-streamly-sdist
132152 ghc_version: 9.0.2
133153 runner: ubuntu-latest
@@ -183,61 +203,35 @@ jobs:
183203 - uses: actions/checkout@v2
184204
185205 - uses: actions/cache@v1
186− name: Cache ~/.cabal
206+ name: Cache common directories
187207 with:
188− path: ~/.cabal
208+ path: |
209+ ~/.local
210+ ~/.cabal
211+ ~/.stack
212+ ~/.ghcup
189213 # Bump the key version to clear the cache
190214 key: ${{ runner.os }}-${{ matrix.ghc_version }}-cabal-v2
191215
192216 - name: Download packcheck
193217 run: |
194− # Get packcheck if needed
195− CURL=$(which curl)
196− PACKCHECK_URL=${PACKCHECK_GITHUB_URL}/${PACKCHECK_GITHUB_COMMIT}/packcheck.sh
197− if test ! -e "$PACKCHECK_LOCAL_PATH"; then $CURL -sL -o "$PACKCHECK_LOCAL_PATH" $PACKCHECK_URL; fi;
198− chmod +x $PACKCHECK_LOCAL_PATH
199−
200− os=$(uname -s -m)
201− case "$os" in
202− "Linux x86_64") GHCUP_ARCH="x86_64-linux" ;;
203− "Darwin x86_64") GHCUP_ARCH="x86_64-apple-darwin" ;;
204− *) echo "Unknown OS/Arch: $os"; exit 1;;
205− esac
206−
207− # Check available versions here: https://downloads.haskell.org/~ghcup/
208− GHCUP_VER=0.1.18.0
209− $CURL -sL -o ./ghcup https://downloads.haskell.org/~ghcup/$GHCUP_VER/${GHCUP_ARCH}-ghcup-$GHCUP_VER
210− chmod +x ./ghcup
211− if test "$GHCVER" = "head"
218+ if test ! -e "$PACKCHECK"
212219 then
213− # The URL may change, to find a working URL go to https://gitlab.haskell.org/ghc/ghc/-/jobs/
214− # Find a debian10 job, click on a passed/failed job, at the
215− # end of the output you will find the tar.xz name, put that after
216− # "raw/", and put the job name after "job=".
217− # Also see https://github.com/mpickering/ghc-artefact-nix/blob/master/gitlab-artifact.nix
218− ./ghcup install ghc -u https://gitlab.haskell.org/ghc/ghc/-/jobs/artifacts/master/raw/ghc-x86_64-linux-deb10-int_native-validate.tar.xz?job=x86_64-linux-deb10-int_native-validate head
219− else
220− ./ghcup install ghc $GHCVER
220+ if test -z "$PACKCHECK_GITHUB_COMMIT"
221+ then
222+ die "PACKCHECK_GITHUB_COMMIT is not specified."
223+ fi
224+ PACKCHECK_URL=${PACKCHECK_GITHUB_URL}/${PACKCHECK_GITHUB_COMMIT}/packcheck.sh
225+ curl --fail -sL -o "$PACKCHECK" $PACKCHECK_URL || exit 1
226+ chmod +x $PACKCHECK
227+ elif test ! -x "$PACKCHECK"
228+ then
229+ chmod +x $PACKCHECK
221230 fi
222− ./ghcup install cabal $CABALVER
223− ./ghcup set ghc $GHCVER
224− ./ghcup set cabal $CABALVER
225231
226232 - name: Run tests
227233 run: |
228− if test -n "$DOCTEST"
229− then
230− mkdir -p $HOME/.cabal/bin
231− curl -sL https://github.com/phadej/cabal-extras/releases/download/cabal-docspec-0.0.0.20210111/cabal-docspec-0.0.0.20210111.xz > cabal-docspec.xz
232− echo '0829bd034fba901cbcfe491d98ed8b28fd54f9cb5c91fa8e1ac62dc4413c9562 cabal-docspec.xz' | sha256sum -c -
233− xz -d < cabal-docspec.xz > $HOME/.cabal/bin/cabal-docspec
234− rm -f cabal-docspec.xz
235− chmod a+x $HOME/.cabal/bin/cabal-docspec
236− cabal update
237− cabal build --project-file cabal.project.doctest all
238− $HOME/.cabal/bin/cabal-docspec --version
239− $HOME/.cabal/bin/cabal-docspec --timeout 60 --check-properties --property-variables xs
240− elif test -n "$COVERAGE"
234+ if test -n "$COVERAGE"
241235 then
242236 # Run tests with coverage
243237 cabal update
@@ -250,16 +244,15 @@ jobs:
250244 # XXX Uncomment this and fix it properly later
251245 # hpc-coveralls --repo-token="$COVERALLS_TOKEN" --coverage-mode=StrictlyFullLines
252246 else
253− export TOOLS_DIR=/opt
254247 # /usr/local/opt/curl/bin for macOS
255248 export PATH=$HOME/.local/bin:$HOME/.ghcup/bin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/local/opt/curl/bin
256249 if test -n "$SUBDIR"
257250 then
258− mv "$PACKCHECK_LOCAL_PATH" "$SUBDIR/packcheck.sh"
251+ mv "$PACKCHECK" "$SUBDIR/packcheck.sh"
259252 # This is required as dist-newstyle will be created on the top level
260253 # directory as it is considered the working dir.
261254 rm cabal.project
262255 cd "$SUBDIR"
263256 fi
264− bash -c "$PACKCHECK_LOCAL_PATH $BUILD"
257+ bash -c "$PACKCHECK $BUILD"
265258 fi
.hlint.ignore+5
@@ -2,6 +2,9 @@ core/src/Streamly/Internal/Data/Stream/StreamK/Type.hs
22core/src/Streamly/Internal/Data/Pipe/Type.hs
33core/src/Streamly/Internal/Unicode/Stream.hs
44src/Streamly/Internal/Data/SmallArray/Type.hs
5+src/Streamly/Internal/Data/Stream/IsStream/Eliminate.hs
6+src/Streamly/Internal/Data/Stream/Serial.hs
7+src/Streamly/Internal/Data/Stream/Zip.hs
58test/Streamly/Test/Data/Array.hs
69test/Streamly/Test/Data/Parser.hs
710test/Streamly/Test/Data/ParserK.hs
@@ -10,6 +13,7 @@ test/Streamly/Test/Data/Unfold.hs
1013test/Streamly/Test/FileSystem/Event.hs
1114test/Streamly/Test/Prelude/Concurrent.hs
1215test/Streamly/Test/Data/Stream/Concurrent.hs
16+test/lib/Streamly/Test/Prelude/Common.hs
1317test/Streamly/Test/Prelude/Fold.hs
1418test/Streamly/Test/Prelude/Rate.hs
1519test/Streamly/Test/Prelude/Serial.hs
@@ -27,6 +31,7 @@ benchmark/Streamly/Benchmark/Data/Stream/StreamK.hs
2731benchmark/Streamly/Benchmark/Data/Unfold.hs
2832benchmark/Streamly/Benchmark/FileSystem/Handle.hs
2933benchmark/Streamly/Benchmark/Prelude/Async.hs
34+benchmark/Streamly/Benchmark/Prelude/Concurrent.hs
3035benchmark/Streamly/Benchmark/Prelude/Merge.hs
3136benchmark/Streamly/Benchmark/Prelude/Parallel.hs
3237benchmark/Streamly/Benchmark/Prelude/Rate.hs
benchmark/Streamly/Benchmark/Data/Fold.hs+2−2
@@ -269,13 +269,13 @@ unzip = Stream.fold $ FL.lmap (\a -> (a, a)) (FL.unzip FL.sum FL.length)
269269{-# INLINE unzipWithFstM #-}
270270unzipWithFstM :: Monad m => Stream m Int -> m (Int, Int)
271271unzipWithFstM = do
272− let f = \a -> return (a + 1, a)
272+ let f a = return (a + 1, a)
273273 Stream.fold (FL.unzipWithFstM f FL.sum FL.length)
274274
275275{-# INLINE unzipWithMinM #-}
276276unzipWithMinM :: Monad m => Stream m Int -> m (Int, Int)
277277unzipWithMinM = do
278− let f = \a -> return (a + 1, a)
278+ let f a = return (a + 1, a)
279279 Stream.fold (FL.unzipWithMinM f FL.sum FL.length)
280280
281281-------------------------------------------------------------------------------
benchmark/Streamly/Benchmark/Data/Serialize.hs+10−15
@@ -1,4 +1,5 @@
11{-# LANGUAGE TemplateHaskell #-}
2+{- HLINT ignore -}
23
34#undef FUSION_CHECK
45#ifdef FUSION_CHECK
@@ -18,6 +19,7 @@ module Main (main) where
1819-------------------------------------------------------------------------------
1920
2021import Control.DeepSeq (NFData(..), deepseq)
22+import Control.Monad (when, void)
2123import GHC.Generics (Generic)
2224import System.Random (randomRIO)
2325#ifndef USE_UNBOX
@@ -264,13 +266,14 @@ $(deriveSerialize [d|instance Serialize a => Serialize (BinTree a)|])
264266instance NFData a => NFData (BinTree a) where
265267 {-# INLINE rnf #-}
266268 rnf (Leaf a) = rnf a `seq` ()
267− rnf (Tree l r) = rnf l `seq` rnf r `seq` ()
269+ rnf (Tree l r) = rnf l `seq` rnf r
268270
271+-- XXX This may not terminate, or could become really large.
269272instance Arbitrary a => Arbitrary (BinTree a) where
270273 arbitrary = oneof [Leaf <$> arbitrary, Tree <$> arbitrary <*> arbitrary]
271274
272275mkBinTree :: (Arbitrary a) => Int -> IO (BinTree a)
273−mkBinTree = go (generate $ arbitrary)
276+mkBinTree = go (generate arbitrary)
274277
275278 where
276279
@@ -295,16 +298,14 @@ getSize = addSizeTo 0
295298-- Common helpers
296299-------------------------------------------------------------------------------
297300
301+{- HLINT ignore "Eta reduce" -}
298302-- Parts of "f" that are dependent on val will not be optimized out.
299303{-# INLINE loop #-}
300304loop :: Int -> (a -> IO b) -> a -> IO ()
301305loop count f val = go count val
302306 where
303307
304− go n x = do
305− if n > 0
306− then f x >> go (n-1) x
307− else return ()
308+ go n x = when (n > 0) $ f x >> go (n-1) x
308309
309310-- The first arg of "f" is the environment which is not threaded around in the
310311-- loop.
@@ -313,10 +314,7 @@ loopWith :: Int -> (e -> a -> IO b) -> e -> a -> IO ()
313314loopWith count f e val = go count val
314315 where
315316
316− go n x = do
317− if n > 0
318− then f e x >> go (n-1) x
319− else return ()
317+ go n x = when (n > 0) $ f e x >> go (n-1) x
320318
321319benchSink :: NFData b => String -> Int -> (Int -> IO b) -> Benchmark
322320benchSink name times f = bench name (nfIO (randomRIO (times, times) >>= f))
@@ -342,7 +340,7 @@ pokeTimesWithSize val times = do
342340
343341{-# INLINE poke #-}
344342poke :: SERIALIZE_CLASS a => MutByteArray -> a -> IO ()
345−poke arr val = SERIALIZE_OP 0 arr val >> return ()
343+poke arr val = void (SERIALIZE_OP 0 arr val)
346344
347345{-# INLINE pokeTimes #-}
348346pokeTimes :: SERIALIZE_CLASS a => a -> Int -> IO ()
@@ -356,7 +354,7 @@ encode :: SERIALIZE_CLASS a => a -> IO ()
356354encode val = do
357355 let n = getSize val
358356 arr <- MBA.new n
359− SERIALIZE_OP 0 arr val >> return ()
357+ void (SERIALIZE_OP 0 arr val)
360358
361359{-# INLINE encodeTimes #-}
362360encodeTimes :: SERIALIZE_CLASS a => a -> Int -> IO ()
@@ -562,8 +560,6 @@ main = do
562560 let !len = length lInt -- evaluate the list
563561#endif
564562#ifndef FUSION_CHECK
565− -- This can take too much memory/CPU, need to restrict the test
566− -- runQC
567563#ifdef USE_UNBOX
568564 runWithCLIOpts defaultStreamSize allBenchmarks
569565#else
@@ -571,7 +567,6 @@ main = do
571567 `seq` runWithCLIOpts
572568 defaultStreamSize
573569 (allBenchmarks tInt lInt recL recR)
574−
575570#endif
576571#else
577572 -- Enable FUSION_CHECK macro at the beginning of the file
benchmark/Streamly/Benchmark/Data/Serialize/TH.hs+3−3
@@ -18,7 +18,7 @@ import Control.DeepSeq (NFData(..))
1818genLargeRecord :: String -> Int -> Q [Dec]
1919genLargeRecord tyName numFields =
2020 sequence
21− ([ dataD
21+ [ dataD
2222 (pure [])
2323 (mkName tyName)
2424 []
@@ -28,7 +28,7 @@ genLargeRecord tyName numFields =
2828 , mkValueSigDec
2929 , mkValueDec
3030 , nfDataInstance tyName
31− ])
31+ ]
3232
3333 where
3434
@@ -64,7 +64,7 @@ genLargeRecord tyName numFields =
6464 (foldl
6565 (\b a -> [|$(b) $(a)|])
6666 (conE (mkName tyName))
67− (const (conE '()) <$> [0 .. (numFields - 1)])))
67+ (conE '() <$ [0 .. (numFields - 1)])))
6868 []
6969 ]
7070 mkCon nm = recC (mkName nm) (mkField <$> [0 .. (numFields - 1)])
benchmark/Streamly/Benchmark/Data/Stream/Eliminate.hs+2
@@ -132,12 +132,14 @@ foldableAny :: Int -> Int -> Bool
132132foldableAny value n =
133133 Prelude.any (> (value + 1)) (sourceUnfoldr value n :: Stream Identity Int)
134134
135+{- HLINT ignore "Use all"-}
135136{-# INLINE foldableAnd #-}
136137foldableAnd :: Int -> Int -> Bool
137138foldableAnd value n =
138139 Prelude.and $ fmap
139140 (<= (value + 1)) (sourceUnfoldr value n :: Stream Identity Int)
140141
142+{- HLINT ignore "Use any"-}
141143{-# INLINE foldableOr #-}
142144foldableOr :: Int -> Int -> Bool
143145foldableOr value n =
benchmark/Streamly/Benchmark/Data/Stream/StreamKAlt.hs+1−1
@@ -452,4 +452,4 @@ o_1_space =
452452 ]
453453
454454main :: IO ()
455−main = defaultMain $ concat [o_1_space]
455+main = defaultMain [o_1_space]
benchmark/Streamly/Benchmark/Data/Stream/ToStreamK.hs+1−1
@@ -120,7 +120,7 @@ fromFoldable streamLen n = S.fromFoldable [n..n+streamLen]
120120{-# INLINE fromFoldableM #-}
121121fromFoldableM :: Monad m => Int -> Int -> Stream m Int
122122fromFoldableM streamLen n =
123− Prelude.foldr S.consM S.nil (Prelude.fmap return [n..n+streamLen])
123+ Prelude.foldr (S.consM . return) S.nil [n .. n + streamLen]
124124
125125{-
126126{-# INLINABLE concatMapFoldableWith #-}
benchmark/Streamly/Benchmark/FileSystem/Handle/Read.hs+7−8
@@ -33,7 +33,6 @@ import qualified Streamly.Data.Stream as Stream
3333import qualified Streamly.Data.Fold as Fold
3434import qualified Streamly.FileSystem.Handle as FH
3535import qualified Streamly.Internal.Data.Array as A
36−import qualified Streamly.Internal.Data.Array as AT
3736import qualified Streamly.Internal.Data.Fold as FL
3837import qualified Streamly.Internal.Data.Stream as IP
3938import qualified Streamly.Internal.FileSystem.Handle as IFH
@@ -48,7 +47,7 @@ import Streamly.Benchmark.Common.Handle
4847#ifdef INSPECTION
4948import Streamly.Internal.Data.Stream (Step(..), FoldMany)
5049
51−import qualified Streamly.Internal.Data.MutArray as MA
50+import qualified Streamly.Internal.Data.MutArray as MutArray
5251import qualified Streamly.Internal.Data.Stream as D
5352import qualified Streamly.Internal.Data.Unfold as IUF
5453
@@ -69,7 +68,7 @@ readLast = S.fold Fold.last . S.unfold FH.reader
6968inspect $ hasNoTypeClasses 'readLast
7069inspect $ 'readLast `hasNoType` ''Step -- S.unfold
7170inspect $ 'readLast `hasNoType` ''IUF.ConcatState -- FH.read/UF.many
72−inspect $ 'readLast `hasNoType` ''MA.ArrayUnsafe -- FH.read/A.read
71+inspect $ 'readLast `hasNoType` ''MutArray.ArrayUnsafe -- FH.read/A.read
7372#endif
7473
7574-- assert that flattenArrays constructors are not present
@@ -81,7 +80,7 @@ readCountBytes = S.fold Fold.length . S.unfold FH.reader
8180inspect $ hasNoTypeClasses 'readCountBytes
8281inspect $ 'readCountBytes `hasNoType` ''Step -- S.unfold
8382inspect $ 'readCountBytes `hasNoType` ''IUF.ConcatState -- FH.read/UF.many
84−inspect $ 'readCountBytes `hasNoType` ''MA.ArrayUnsafe -- FH.read/A.read
83+inspect $ 'readCountBytes `hasNoType` ''MutArray.ArrayUnsafe -- FH.read/A.read
8584#endif
8685
8786-- | Count the number of lines in a file.
@@ -96,7 +95,7 @@ readCountLines =
9695inspect $ hasNoTypeClasses 'readCountLines
9796inspect $ 'readCountLines `hasNoType` ''Step
9897inspect $ 'readCountLines `hasNoType` ''IUF.ConcatState -- FH.read/UF.many
99−inspect $ 'readCountLines `hasNoType` ''MA.ArrayUnsafe -- FH.read/A.read
98+inspect $ 'readCountLines `hasNoType` ''MutArray.ArrayUnsafe -- FH.read/A.read
10099#endif
101100
102101-- | Count the number of words in a file.
@@ -120,7 +119,7 @@ readSumBytes = S.fold Fold.sum . S.unfold FH.reader
120119inspect $ hasNoTypeClasses 'readSumBytes
121120inspect $ 'readSumBytes `hasNoType` ''Step
122121inspect $ 'readSumBytes `hasNoType` ''IUF.ConcatState -- FH.read/UF.many
123−inspect $ 'readSumBytes `hasNoType` ''MA.ArrayUnsafe -- FH.read/A.read
122+inspect $ 'readSumBytes `hasNoType` ''MutArray.ArrayUnsafe -- FH.read/A.read
124123#endif
125124
126125-- XXX When we mark this with INLINE and we have two benchmarks using S.drain
@@ -236,13 +235,13 @@ groupsOf :: Int -> Handle -> IO Int
236235groupsOf n inh =
237236 -- writeNUnsafe gives 2.5x boost here over writeN.
238237 S.fold Fold.length
239− $ IP.groupsOf n (AT.writeNUnsafe n) (S.unfold FH.reader inh)
238+ $ IP.groupsOf n (A.writeNUnsafe n) (S.unfold FH.reader inh)
240239
241240#ifdef INSPECTION
242241inspect $ hasNoTypeClasses 'groupsOf
243242inspect $ 'groupsOf `hasNoType` ''Step
244243inspect $ 'groupsOf `hasNoType` ''FoldMany
245−inspect $ 'groupsOf `hasNoType` ''AT.ArrayUnsafe -- AT.writeNUnsafe
244+inspect $ 'groupsOf `hasNoType` ''MutArray.ArrayUnsafe -- AT.writeNUnsafe
246245 -- FH.read/A.read
247246inspect $ 'groupsOf `hasNoType` ''IUF.ConcatState -- FH.read/UF.many
248247#endif
benchmark/Streamly/Benchmark/FileSystem/Handle/ReadWrite.hs+8−9
@@ -42,8 +42,7 @@ import Streamly.Internal.Data.Stream (Step(..))
4242
4343import qualified Streamly.Internal.Data.Stream as D
4444import qualified Streamly.Internal.Data.Tuple.Strict as Strict
45−import qualified Streamly.Internal.Data.MutArray.Stream as MAS
46−import qualified Streamly.Internal.Data.Array as AT
45+import qualified Streamly.Internal.Data.MutArray as MutArray
4746
4847import Test.Inspection
4948#endif
@@ -83,7 +82,7 @@ copyStream inh outh = S.fold (FH.write outh) (S.unfold FH.reader inh)
8382inspect $ hasNoTypeClasses 'copyStream
8483inspect $ 'copyStream `hasNoType` ''Step -- S.unfold
8584inspect $ 'copyStream `hasNoType` ''IUF.ConcatState -- FH.read/UF.many
86−inspect $ 'copyStream `hasNoType` ''AT.ArrayUnsafe -- FH.write/writeNUnsafe
85+inspect $ 'copyStream `hasNoType` ''MutArray.ArrayUnsafe -- FH.write/writeNUnsafe
8786 -- FH.read/A.read
8887inspect $ 'copyStream `hasNoType` ''Strict.Tuple3' -- FH.write/chunksOf
8988#endif
@@ -109,8 +108,8 @@ readFromBytesNull inh devNull = IFH.putBytes devNull $ S.unfold FH.reader inh
109108#ifdef INSPECTION
110109inspect $ hasNoTypeClasses 'readFromBytesNull
111110inspect $ 'readFromBytesNull `hasNoType` ''Step
112−inspect $ 'readFromBytesNull `hasNoType` ''MAS.SpliceState
113−inspect $ 'readFromBytesNull `hasNoType` ''AT.ArrayUnsafe -- FH.fromBytes/S.chunksOf
111+inspect $ 'readFromBytesNull `hasNoType` ''MutArray.SpliceState
112+inspect $ 'readFromBytesNull `hasNoType` ''MutArray.ArrayUnsafe -- FH.fromBytes/S.chunksOf
114113inspect $ 'readFromBytesNull `hasNoType` ''D.FoldMany
115114#endif
116115
@@ -123,8 +122,8 @@ readWithFromBytesNull inh devNull =
123122#ifdef INSPECTION
124123inspect $ hasNoTypeClasses 'readWithFromBytesNull
125124inspect $ 'readWithFromBytesNull `hasNoType` ''Step
126−inspect $ 'readWithFromBytesNull `hasNoType` ''MAS.SpliceState
127−inspect $ 'readWithFromBytesNull `hasNoType` ''AT.ArrayUnsafe -- FH.fromBytes/S.chunksOf
125+inspect $ 'readWithFromBytesNull `hasNoType` ''MutArray.SpliceState
126+inspect $ 'readWithFromBytesNull `hasNoType` ''MutArray.ArrayUnsafe -- FH.fromBytes/S.chunksOf
128127inspect $ 'readWithFromBytesNull `hasNoType` ''D.FoldMany
129128#endif
130129
@@ -171,7 +170,7 @@ writeReadWith inh devNull = IUF.fold fld unf (defaultChunkSize, inh)
171170inspect $ hasNoTypeClasses 'writeReadWith
172171inspect $ 'writeReadWith `hasNoType` ''Step
173172inspect $ 'writeReadWith `hasNoType` ''IUF.ConcatState -- FH.read/UF.many
174−inspect $ 'writeReadWith `hasNoType` ''AT.ArrayUnsafe -- FH.write/writeNUnsafe
173+inspect $ 'writeReadWith `hasNoType` ''MutArray.ArrayUnsafe -- FH.write/writeNUnsafe
175174 -- FH.read/A.read
176175#endif
177176
@@ -188,7 +187,7 @@ writeRead inh devNull = IUF.fold fld unf inh
188187inspect $ hasNoTypeClasses 'writeRead
189188inspect $ 'writeRead `hasNoType` ''Step
190189inspect $ 'writeRead `hasNoType` ''IUF.ConcatState -- FH.read/UF.many
191−inspect $ 'writeRead `hasNoType` ''AT.ArrayUnsafe -- FH.write/writeNUnsafe
190+inspect $ 'writeRead `hasNoType` ''MutArray.ArrayUnsafe -- FH.write/writeNUnsafe
192191 -- FH.read/A.read
193192#endif
194193
benchmark/Streamly/Benchmark/Unicode/Stream.hs+4−3
@@ -45,9 +45,10 @@ import Streamly.Benchmark.Common.Handle
4545#ifdef INSPECTION
4646import Streamly.Internal.Data.MutByteArray (Unbox)
4747import Streamly.Internal.Data.Stream (Step(..))
48+import qualified Streamly.Internal.Data.Array as Array
49+import qualified Streamly.Internal.Data.MutArray as MutArray
4850import qualified Streamly.Internal.Data.Fold as Fold
4951import qualified Streamly.Internal.Data.Tuple.Strict as Strict
50−import qualified Streamly.Internal.Data.Array as Array
5152
5253import Test.Inspection
5354#endif
@@ -223,7 +224,7 @@ inspect $ 'copyStreamLatin1' `hasNoType` ''Step
223224inspect $ 'copyStreamLatin1' `hasNoType` ''Unfold.ConcatState -- Handle.read/UF.many
224225
225226inspect $ 'copyStreamLatin1' `hasNoType` ''Fold.Step
226−inspect $ 'copyStreamLatin1' `hasNoType` ''Array.ArrayUnsafe -- Handle.write/writeNUnsafe
227+inspect $ 'copyStreamLatin1' `hasNoType` ''MutArray.ArrayUnsafe -- Handle.write/writeNUnsafe
227228 -- Handle.read/Array.read
228229inspect $ 'copyStreamLatin1' `hasNoType` ''Strict.Tuple3' -- Handle.write/chunksOf
229230#endif
@@ -244,7 +245,7 @@ inspect $ 'copyStreamLatin1 `hasNoType` ''Unfold.ConcatState -- Handle.read/UF.m
244245
245246inspect $ 'copyStreamLatin1 `hasNoType` ''Fold.ManyState
246247inspect $ 'copyStreamLatin1 `hasNoType` ''Fold.Step
247−inspect $ 'copyStreamLatin1 `hasNoType` ''Array.ArrayUnsafe -- Handle.write/writeNUnsafe
248+inspect $ 'copyStreamLatin1 `hasNoType` ''MutArray.ArrayUnsafe -- Handle.write/writeNUnsafe
248249 -- Handle.read/Array.read
249250inspect $ 'copyStreamLatin1 `hasNoType` ''Strict.Tuple3' -- Handle.write/chunksOf
250251#endif
configure+9−9
@@ -1,6 +1,6 @@
11#! /bin/sh
22# Guess values for system-dependent variables and create Makefiles.
3−# Generated by GNU Autoconf 2.71 for streamly 0.10.0.
3+# Generated by GNU Autoconf 2.71 for streamly 0.10.1.
44#
55# Report bugs to <streamly@composewell.com>.
66#
@@ -610,8 +610,8 @@ MAKEFLAGS=
610610# Identity of this package.
611611PACKAGE_NAME='streamly'
612612PACKAGE_TARNAME='streamly'
613−PACKAGE_VERSION='0.10.0'
614−PACKAGE_STRING='streamly 0.10.0'
613+PACKAGE_VERSION='0.10.1'
614+PACKAGE_STRING='streamly 0.10.1'
615615PACKAGE_BUGREPORT='streamly@composewell.com'
616616PACKAGE_URL='https://streamly.composewell.com'
617617
@@ -1224,7 +1224,7 @@ if test "$ac_init_help" = "long"; then
12241224 # Omit some internal or obsolete options to make the list less imposing.
12251225 # This message is too long to be a string in the A/UX 3.1 sh.
12261226 cat <<_ACEOF
1227−\`configure' configures streamly 0.10.0 to adapt to many kinds of systems.
1227+\`configure' configures streamly 0.10.1 to adapt to many kinds of systems.
12281228
12291229Usage: $0 [OPTION]... [VAR=VALUE]...
12301230
@@ -1286,7 +1286,7 @@ fi
12861286
12871287if test -n "$ac_init_help"; then
12881288 case $ac_init_help in
1289− short | recursive ) echo "Configuration of streamly 0.10.0:";;
1289+ short | recursive ) echo "Configuration of streamly 0.10.1:";;
12901290 esac
12911291 cat <<\_ACEOF
12921292
@@ -1372,7 +1372,7 @@ fi
13721372test -n "$ac_init_help" && exit $ac_status
13731373if $ac_init_version; then
13741374 cat <<\_ACEOF
1375−streamly configure 0.10.0
1375+streamly configure 0.10.1
13761376generated by GNU Autoconf 2.71
13771377
13781378Copyright (C) 2021 Free Software Foundation, Inc.
@@ -1500,7 +1500,7 @@ cat >config.log <<_ACEOF
15001500This file contains any messages produced by compilers while
15011501running configure, to aid debugging if configure makes a mistake.
15021502
1503−It was created by streamly $as_me 0.10.0, which was
1503+It was created by streamly $as_me 0.10.1, which was
15041504generated by GNU Autoconf 2.71. Invocation command line was
15051505
15061506 $ $0$ac_configure_args_raw
@@ -3800,7 +3800,7 @@ cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1
38003800# report actual input values of CONFIG_FILES etc. instead of their
38013801# values after options handling.
38023802ac_log="
3803−This file was extended by streamly $as_me 0.10.0, which was
3803+This file was extended by streamly $as_me 0.10.1, which was
38043804generated by GNU Autoconf 2.71. Invocation command line was
38053805
38063806 CONFIG_FILES = $CONFIG_FILES
@@ -3856,7 +3856,7 @@ ac_cs_config_escaped=`printf "%s\n" "$ac_cs_config" | sed "s/^ //; s/'/'\\\\\\\\
38563856cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1
38573857ac_cs_config='$ac_cs_config_escaped'
38583858ac_cs_version="\\
3859−streamly config.status 0.10.0
3859+streamly config.status 0.10.1
38603860configured by $0, generated by GNU Autoconf 2.71,
38613861 with options \\"\$ac_cs_config\\"
38623862
configure.ac+1−1
@@ -3,7 +3,7 @@
33# See https://www.gnu.org/software/autoconf/manual/autoconf.html for help on
44# the macros used in this file.
55
6−AC_INIT([streamly], [0.10.0], [streamly@composewell.com], [streamly], [https://streamly.composewell.com])
6+AC_INIT([streamly], [0.10.1], [streamly@composewell.com], [streamly], [https://streamly.composewell.com])
77
88# To suppress "WARNING: unrecognized options: --with-compiler"
99AC_ARG_WITH([compiler], [GHC])
core/configure+9−9
@@ -1,6 +1,6 @@
11#! /bin/sh
22# Guess values for system-dependent variables and create Makefiles.
3−# Generated by GNU Autoconf 2.71 for streamly-core 0.2.1.
3+# Generated by GNU Autoconf 2.71 for streamly-core 0.2.2.
44#
55# Report bugs to <streamly@composewell.com>.
66#
@@ -610,8 +610,8 @@ MAKEFLAGS=
610610# Identity of this package.
611611PACKAGE_NAME='streamly-core'
612612PACKAGE_TARNAME='streamly-core'
613−PACKAGE_VERSION='0.2.1'
614−PACKAGE_STRING='streamly-core 0.2.1'
613+PACKAGE_VERSION='0.2.2'
614+PACKAGE_STRING='streamly-core 0.2.2'
615615PACKAGE_BUGREPORT='streamly@composewell.com'
616616PACKAGE_URL='https://streamly.composewell.com'
617617
@@ -1256,7 +1256,7 @@ if test "$ac_init_help" = "long"; then
12561256 # Omit some internal or obsolete options to make the list less imposing.
12571257 # This message is too long to be a string in the A/UX 3.1 sh.
12581258 cat <<_ACEOF
1259−\`configure' configures streamly-core 0.2.1 to adapt to many kinds of systems.
1259+\`configure' configures streamly-core 0.2.2 to adapt to many kinds of systems.
12601260
12611261Usage: $0 [OPTION]... [VAR=VALUE]...
12621262
@@ -1318,7 +1318,7 @@ fi
13181318
13191319if test -n "$ac_init_help"; then
13201320 case $ac_init_help in
1321− short | recursive ) echo "Configuration of streamly-core 0.2.1:";;
1321+ short | recursive ) echo "Configuration of streamly-core 0.2.2:";;
13221322 esac
13231323 cat <<\_ACEOF
13241324
@@ -1404,7 +1404,7 @@ fi
14041404test -n "$ac_init_help" && exit $ac_status
14051405if $ac_init_version; then
14061406 cat <<\_ACEOF
1407−streamly-core configure 0.2.1
1407+streamly-core configure 0.2.2
14081408generated by GNU Autoconf 2.71
14091409
14101410Copyright (C) 2021 Free Software Foundation, Inc.
@@ -1622,7 +1622,7 @@ cat >config.log <<_ACEOF
16221622This file contains any messages produced by compilers while
16231623running configure, to aid debugging if configure makes a mistake.
16241624
1625−It was created by streamly-core $as_me 0.2.1, which was
1625+It was created by streamly-core $as_me 0.2.2, which was
16261626generated by GNU Autoconf 2.71. Invocation command line was
16271627
16281628 $ $0$ac_configure_args_raw
@@ -3832,7 +3832,7 @@ cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1
38323832# report actual input values of CONFIG_FILES etc. instead of their
38333833# values after options handling.
38343834ac_log="
3835−This file was extended by streamly-core $as_me 0.2.1, which was
3835+This file was extended by streamly-core $as_me 0.2.2, which was
38363836generated by GNU Autoconf 2.71. Invocation command line was
38373837
38383838 CONFIG_FILES = $CONFIG_FILES
@@ -3888,7 +3888,7 @@ ac_cs_config_escaped=`printf "%s\n" "$ac_cs_config" | sed "s/^ //; s/'/'\\\\\\\\
38883888cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1
38893889ac_cs_config='$ac_cs_config_escaped'
38903890ac_cs_version="\\
3891−streamly-core config.status 0.2.1
3891+streamly-core config.status 0.2.2
38923892configured by $0, generated by GNU Autoconf 2.71,
38933893 with options \\"\$ac_cs_config\\"
38943894
core/configure.ac+1−1
@@ -3,7 +3,7 @@
33# See https://www.gnu.org/software/autoconf/manual/autoconf.html for help on
44# the macros used in this file.
55
6−AC_INIT([streamly-core], [0.2.1], [streamly@composewell.com], [streamly-core], [https://streamly.composewell.com])
6+AC_INIT([streamly-core], [0.2.2], [streamly@composewell.com], [streamly-core], [https://streamly.composewell.com])
77
88# To suppress "WARNING: unrecognized options: --with-compiler"
99AC_ARG_WITH([compiler], [GHC])
core/docs/ApiChangelogs/0.2.0-0.2.2.txtnew file+306
@@ -0,0 +1,306 @@
1+---------------------------------
2+API Annotations
3+---------------------------------
4+
5+[A] : Added
6+[R] : Removed
7+[C] : Changed
8+[O] : Old definition
9+[N] : New definition
10+[D] : Deprecated
11+
12+---------------------------------
13+API diff
14+---------------------------------
15+
16+[C] Streamly.Data.Stream
17+ [A] (FixityR,5)
18+ [A] (FixityR,5)
19+[C] Streamly.Data.MutArray.Generic
20+ [A] emptyOf :: MonadIO m => Int -> m (MutArray a)
21+ [A] createOf :: MonadIO m => Int -> Fold m a (MutArray a)
22+ [A] create :: MonadIO m => Fold m a (MutArray a)
23+[C] Streamly.Data.MutArray
24+ [A] pinnedEmptyOf :: forall m a. (MonadIO m, Unbox a) => Int -> m (MutArray a)
25+ [A] emptyOf :: (MonadIO m, Unbox a) => Int -> m (MutArray a)
26+ [A] createOf :: forall m a. (MonadIO m, Unbox a) => Int -> Fold m a (MutArray a)
27+ [A] create :: forall m a. (MonadIO m, Unbox a) => Fold m a (MutArray a)
28+ [A] appendN :: forall m a. (MonadIO m, Unbox a) => Int -> m (MutArray a) -> Fold m a (MutArray a)
29+ [A] append :: forall m a. (MonadIO m, Unbox a) => m (MutArray a) -> Fold m a (MutArray a)
30+[C] Streamly.Data.Array.Generic
31+ [A] createOf :: MonadIO m => Int -> Fold m a (Array a)
32+ [A] create :: MonadIO m => Fold m a (Array a)
33+[C] Streamly.Data.Array
34+ [A] class Serialize a
35+ [A] serializeAt :: Serialize a => Int -> MutByteArray -> a -> IO Int
36+ [A] pinnedSerialize :: Serialize a => a -> Array Word8
37+ [A] deserializeAt :: Serialize a => Int -> MutByteArray -> Int -> IO (Int, a)
38+ [A] deserialize :: Serialize a => Array Word8 -> a
39+ [A] createOf :: forall m a. (MonadIO m, Unbox a) => Int -> Fold m a (Array a)
40+ [A] create :: forall m a. (MonadIO m, Unbox a) => Fold m a (Array a)
41+ [A] addSizeTo :: Serialize a => Int -> a -> Int
42+
43+---------------------------------
44+Internal API diff
45+---------------------------------
46+
47+[C] Streamly.Internal.Unicode.Stream
48+ [A] encodeUtf16le' :: Stream m Char -> Stream m Word16
49+ [A] decodeUtf16le' :: Stream m Word16 -> Stream m Char
50+[A] Streamly.Internal.FileSystem.Path
51+ [A] class IsPath a
52+ [A] Rel
53+ [A] File
54+ [A] Dir
55+ [A] Abs
56+ [A] Streamly.Internal.FileSystem.Path.IsPath
57+ [A] instance Streamly.Internal.FileSystem.Path.IsPath Streamly.Internal.FileSystem.Path.Path
58+ [A] instance Streamly.Internal.FileSystem.Path.IsPath (Streamly.Internal.FileSystem.Path.Rel Streamly.Internal.FileSystem.Path.Path)
59+ [A] instance Streamly.Internal.FileSystem.Path.IsPath (Streamly.Internal.FileSystem.Path.Rel (Streamly.Internal.FileSystem.Path.File Streamly.Internal.FileSystem.Path.Path))
60+ [A] instance Streamly.Internal.FileSystem.Path.IsPath (Streamly.Internal.FileSystem.Path.Rel (Streamly.Internal.FileSystem.Path.Dir Streamly.Internal.FileSystem.Path.Path))
61+ [A] instance Streamly.Internal.FileSystem.Path.IsPath (Streamly.Internal.FileSystem.Path.File Streamly.Internal.FileSystem.Path.Path)
62+ [A] instance Streamly.Internal.FileSystem.Path.IsPath (Streamly.Internal.FileSystem.Path.Dir Streamly.Internal.FileSystem.Path.Path)
63+ [A] instance Streamly.Internal.FileSystem.Path.IsPath (Streamly.Internal.FileSystem.Path.Abs Streamly.Internal.FileSystem.Path.Path)
64+ [A] instance Streamly.Internal.FileSystem.Path.IsPath (Streamly.Internal.FileSystem.Path.Abs (Streamly.Internal.FileSystem.Path.File Streamly.Internal.FileSystem.Path.Path))
65+ [A] instance Streamly.Internal.FileSystem.Path.IsPath (Streamly.Internal.FileSystem.Path.Abs (Streamly.Internal.FileSystem.Path.Dir Streamly.Internal.FileSystem.Path.Path))
66+ [A] GHC.Show.Show
67+ [A] instance GHC.Show.Show Streamly.Internal.FileSystem.Path.PathException
68+ [A] GHC.Exception.Type.Exception
69+ [A] instance GHC.Exception.Type.Exception Streamly.Internal.FileSystem.Path.PathException
70+ [A] GHC.Classes.Eq
71+ [A] instance GHC.Classes.Eq Streamly.Internal.FileSystem.Path.PathException
72+ [A] Path
73+ [A] Path :: Array Word8 -> Path
74+ [A] toString :: Path -> [Char]
75+ [A] toPath :: IsPath a => a -> Path
76+ [A] toChunk :: Path -> Array Word8
77+ [A] toChars :: Monad m => Path -> Stream m Char
78+ [A] relfile :: QuasiQuoter
79+ [A] reldir :: QuasiQuoter
80+ [A] rel :: QuasiQuoter
81+ [A] primarySeparator :: Char
82+ [A] path :: QuasiQuoter
83+ [A] mkRelFile :: String -> Q Exp
84+ [A] mkRelDir :: String -> Q Exp
85+ [A] mkRel :: String -> Q Exp
86+ [A] mkPath :: String -> Q Exp
87+ [A] mkFile :: String -> Q Exp
88+ [A] mkDir :: String -> Q Exp
89+ [A] mkAbsFile :: String -> Q Exp
90+ [A] mkAbsDir :: String -> Q Exp
91+ [A] mkAbs :: String -> Q Exp
92+ [A] isSeparator :: Char -> Bool
93+ [A] fromString :: MonadThrow m => [Char] -> m Path
94+ [A] fromPathUnsafe :: IsPath a => Path -> a
95+ [A] fromPath :: (IsPath a, MonadThrow m) => Path -> m a
96+ [A] fromChunkUnsafe :: Array Word8 -> Path
97+ [A] fromChunk :: MonadThrow m => Array Word8 -> m Path
98+ [A] fromChars :: MonadThrow m => Stream Identity Char -> m Path
99+ [A] file :: QuasiQuoter
100+ [A] extendPath :: Path -> Path -> Path
101+ [A] extendDir :: (IsPath (a (Dir Path)), IsPath b, IsPath (a b)) => a (Dir Path) -> Rel b -> a b
102+ [A] dir :: QuasiQuoter
103+ [A] adaptPath :: (MonadThrow m, IsPath a, IsPath b) => a -> m b
104+ [A] absfile :: QuasiQuoter
105+ [A] absdir :: QuasiQuoter
106+ [A] abs :: QuasiQuoter
107+[C] Streamly.Internal.Data.Stream
108+ [A] (FixityR,5)
109+ [A] (FixityR,5)
110+ [C] splitInnerBySuffix
111+ [O] splitInnerBySuffix :: (Monad m, Eq (f a), Monoid (f a)) => (f a -> m (f a, Maybe (f a))) -> (f a -> f a -> m (f a)) -> Stream m (f a) -> Stream m (f a)
112+ [N] splitInnerBySuffix :: Monad m => (f a -> Bool) -> (f a -> m (f a, Maybe (f a))) -> (f a -> f a -> m (f a)) -> Stream m (f a) -> Stream m (f a)
113+ [D] sliceOnSuffix :: Monad m => (a -> Bool) -> Stream m a -> Stream m (Int, Int)
114+ [A] indexOnSuffix :: Monad m => (a -> Bool) -> Stream m a -> Stream m (Int, Int)
115+[C] Streamly.Internal.Data.MutByteArray
116+ [A] unsafePinnedAsPtr :: MonadIO m => MutByteArray -> (Ptr a -> m b) -> m b
117+ [A] unsafeAsPtr :: MonadIO m => MutByteArray -> (Ptr a -> m b) -> m b
118+ [D] nil :: MutByteArray
119+ [A] empty :: MutByteArray
120+ [D] asPtrUnsafe :: MonadIO m => MutByteArray -> (Ptr a -> m b) -> m b
121+[D] Streamly.Internal.Data.MutArray.Stream
122+ [D] writeChunks :: (MonadIO m, Unbox a) => Int -> Fold m a (StreamK n (MutArray a))
123+ [D] fromArrayStreamK :: (Unbox a, MonadIO m) => StreamK m (MutArray a) -> m (MutArray a)
124+ [D] flattenArraysRev :: forall m a. (MonadIO m, Unbox a) => Stream m (MutArray a) -> Stream m a
125+ [D] flattenArrays :: forall m a. (MonadIO m, Unbox a) => Stream m (MutArray a) -> Stream m a
126+[C] Streamly.Internal.Data.MutArray.Generic
127+ [D] writeWith :: MonadIO m => Int -> Fold m a (MutArray a)
128+ [D] writeNUnsafe :: MonadIO m => Int -> Fold m a (MutArray a)
129+ [A] unsafeCreateOf :: MonadIO m => Int -> Fold m a (MutArray a)
130+ [A] emptyOf :: MonadIO m => Int -> m (MutArray a)
131+ [A] createWith :: MonadIO m => Int -> Fold m a (MutArray a)
132+ [A] createOf :: MonadIO m => Int -> Fold m a (MutArray a)
133+ [A] create :: MonadIO m => Fold m a (MutArray a)
134+[C] Streamly.Internal.Data.MutArray
135+ [A] SpliceState
136+ [A] SpliceYielding :: arr -> SpliceState s arr -> SpliceState s arr
137+ [A] SpliceInitial :: s -> SpliceState s arr
138+ [A] SpliceFinish :: SpliceState s arr
139+ [A] SpliceBuffering :: s -> arr -> SpliceState s arr
140+ [R] MutByteArray
141+ [D] writeWith :: forall m a. (MonadIO m, Unbox a) => Int -> Fold m a (MutArray a)
142+ [D] writeRevN :: forall m a. (MonadIO m, Unbox a) => Int -> Fold m a (MutArray a)
143+ [D] writeNWithUnsafe :: forall m a. (MonadIO m, Unbox a) => (Int -> m (MutArray a)) -> Int -> Fold m a (MutArray a)
144+ [D] writeNUnsafe :: forall m a. (MonadIO m, Unbox a) => Int -> Fold m a (MutArray a)
145+ [D] writeChunks :: (MonadIO m, Unbox a) => Int -> Fold m a (StreamK n (MutArray a))
146+ [D] writeAppendWith :: forall m a. (MonadIO m, Unbox a) => (Int -> Int) -> m (MutArray a) -> Fold m a (MutArray a)
147+ [D] writeAppendNUnsafe :: forall m a. (MonadIO m, Unbox a) => Int -> m (MutArray a) -> Fold m a (MutArray a)
148+ [A] unsafePinnedCreateOf :: forall m a. (MonadIO m, Unbox a) => Int -> Fold m a (MutArray a)
149+ [A] unsafePinnedAsPtr :: MonadIO m => MutArray a -> (Ptr a -> m b) -> m b
150+ [A] unsafeCreateOfWith :: forall m a. (MonadIO m, Unbox a) => (Int -> m (MutArray a)) -> Int -> Fold m a (MutArray a)
151+ [A] unsafeCreateOf :: forall m a. (MonadIO m, Unbox a) => Int -> Fold m a (MutArray a)
152+ [A] unsafeAsPtr :: MonadIO m => MutArray a -> (Ptr a -> m b) -> m b
153+ [A] unsafeAppendN :: forall m a. (MonadIO m, Unbox a) => Int -> m (MutArray a) -> Fold m a (MutArray a)
154+ [A] toStreamWith :: forall m a. (Monad m, Unbox a) => (forall b. IO b -> m b) -> MutArray a -> Stream m a
155+ [A] toStreamRevWith :: forall m a. (Monad m, Unbox a) => (forall b. IO b -> m b) -> MutArray a -> Stream m a
156+ [R] toStreamDWith :: forall m a. (Monad m, Unbox a) => (forall b. IO b -> m b) -> MutArray a -> Stream m a
157+ [R] toStreamDRevWith :: forall m a. (Monad m, Unbox a) => (forall b. IO b -> m b) -> MutArray a -> Stream m a
158+ [A] slicerFromLen :: forall m a. (Monad m, Unbox a) => Int -> Int -> Unfold m (MutArray a) (MutArray a)
159+ [A] sliceIndexerFromLen :: forall m a. (Monad m, Unbox a) => Int -> Int -> Unfold m (MutArray a) (Int, Int)
160+ [A] revCreateOf :: forall m a. (MonadIO m, Unbox a) => Int -> Fold m a (MutArray a)
161+ [D] resizeExp :: forall m a. (MonadIO m, Unbox a) => Int -> MutArray a -> m (MutArray a)
162+ [D] resize :: forall m a. (MonadIO m, Unbox a) => Int -> MutArray a -> m (MutArray a)
163+ [A] pokeSkipUnsafe :: Int -> MutArray Word8 -> MutArray Word8
164+ [A] pokeAppendMay :: forall m a. (MonadIO m, Unbox a) => MutArray Word8 -> a -> m (Maybe (MutArray Word8))
165+ [A] pokeAppend :: forall m a. (MonadIO m, Unbox a) => MutArray Word8 -> a -> m (MutArray Word8)
166+ [D] pinnedWriteNUnsafe :: forall m a. (MonadIO m, Unbox a) => Int -> Fold m a (MutArray a)
167+ [D] pinnedWriteN :: forall m a. (MonadIO m, Unbox a) => Int -> Fold m a (MutArray a)
168+ [D] pinnedWrite :: forall m a. (MonadIO m, Unbox a) => Fold m a (MutArray a)
169+ [D] pinnedNewBytes :: MonadIO m => Int -> m (MutArray a)
170+ [A] pinnedEmptyOf :: forall m a. (MonadIO m, Unbox a) => Int -> m (MutArray a)
171+ [A] pinnedCreateOf :: forall m a. (MonadIO m, Unbox a) => Int -> Fold m a (MutArray a)
172+ [A] pinnedCreate :: forall m a. (MonadIO m, Unbox a) => Fold m a (MutArray a)
173+ [A] pinnedCompactLE :: forall m a. (MonadIO m, Unbox a) => Int -> Stream m (MutArray a) -> Stream m (MutArray a)
174+ [A] peekUnconsUnsafe :: forall m a. (MonadIO m, Unbox a) => MutArray Word8 -> m (a, MutArray Word8)
175+ [A] peekUncons :: forall m a. (MonadIO m, Unbox a) => MutArray Word8 -> m (Maybe a, MutArray Word8)
176+ [A] peekSkipUnsafe :: Int -> MutArray Word8 -> MutArray Word8
177+ [A] pPinnedCompactLE :: forall m a. (MonadIO m, Unbox a) => Int -> Parser (MutArray a) m (MutArray a)
178+ [A] pCompactLE :: forall m a. (MonadIO m, Unbox a) => Int -> Parser (MutArray a) m (MutArray a)
179+ [D] nil :: MutArray a
180+ [A] lPinnedCompactGE :: forall m a. (MonadIO m, Unbox a) => Int -> Fold m (MutArray a) () -> Fold m (MutArray a) ()
181+ [A] lCompactGE :: forall m a. (MonadIO m, Unbox a) => Int -> Fold m (MutArray a) () -> Fold m (MutArray a) ()
182+ [A] indexReaderWith :: (Monad m, Unbox a) => (forall b. IO b -> m b) -> Stream m Int -> Unfold m (MutArray a) a
183+ [A] indexReader :: (MonadIO m, Unbox a) => Stream m Int -> Unfold m (MutArray a) a
184+ [A] growExp :: forall m a. (MonadIO m, Unbox a) => Int -> MutArray a -> m (MutArray a)
185+ [A] grow :: forall m a. (MonadIO m, Unbox a) => Int -> MutArray a -> m (MutArray a)
186+ [D] getSlicesFromLen :: forall m a. (Monad m, Unbox a) => Int -> Int -> Unfold m (MutArray a) (MutArray a)
187+ [R] getIndicesD :: (Monad m, Unbox a) => (forall b. IO b -> m b) -> Stream m Int -> Unfold m (MutArray a) a
188+ [D] getIndices :: (MonadIO m, Unbox a) => Stream m Int -> Unfold m (MutArray a) a
189+ [D] genSlicesFromLen :: forall m a. (Monad m, Unbox a) => Int -> Int -> Unfold m (MutArray a) (Int, Int)
190+ [A] fromStreamN :: forall m a. (MonadIO m, Unbox a) => Int -> Stream m a -> m (MutArray a)
191+ [D] fromStreamDN :: forall m a. (MonadIO m, Unbox a) => Int -> Stream m a -> m (MutArray a)
192+ [D] fromStreamD :: (MonadIO m, Unbox a) => Stream m a -> m (MutArray a)
193+ [A] fromPureStreamN :: (MonadIO m, Unbox a) => Int -> Stream Identity a -> m (MutArray a)
194+ [A] fromPtrN :: MonadIO m => Int -> Ptr Word8 -> m (MutArray Word8)
195+ [A] fromChunksRealloced :: forall m a. (MonadIO m, Unbox a) => Stream m (MutArray a) -> m (MutArray a)
196+ [A] fromChunksK :: (Unbox a, MonadIO m) => StreamK m (MutArray a) -> m (MutArray a)
197+ [A] fromByteStr# :: MonadIO m => Addr# -> m (MutArray Word8)
198+ [D] fromArrayStreamK :: (Unbox a, MonadIO m) => StreamK m (MutArray a) -> m (MutArray a)
199+ [D] flattenArraysRev :: forall m a. (MonadIO m, Unbox a) => Stream m (MutArray a) -> Stream m a
200+ [D] flattenArrays :: forall m a. (MonadIO m, Unbox a) => Stream m (MutArray a) -> Stream m a
201+ [A] fPinnedCompactGE :: forall m a. (MonadIO m, Unbox a) => Int -> Fold m (MutArray a) (MutArray a)
202+ [A] fCompactGE :: forall m a. (MonadIO m, Unbox a) => Int -> Fold m (MutArray a) (MutArray a)
203+ [A] emptyOf :: (MonadIO m, Unbox a) => Int -> m (MutArray a)
204+ [A] empty :: MutArray a
205+ [A] createWith :: forall m a. (MonadIO m, Unbox a) => Int -> Fold m a (MutArray a)
206+ [A] createOfWith :: forall m a. (MonadIO m, Unbox a) => (Int -> m (MutArray a)) -> Int -> Fold m a (MutArray a)
207+ [A] createOf :: forall m a. (MonadIO m, Unbox a) => Int -> Fold m a (MutArray a)
208+ [A] create :: forall m a. (MonadIO m, Unbox a) => Fold m a (MutArray a)
209+ [A] concatWith :: forall m a. (Monad m, Unbox a) => (forall b. IO b -> m b) -> Stream m (MutArray a) -> Stream m a
210+ [A] concatRevWith :: forall m a. (Monad m, Unbox a) => (forall b. IO b -> m b) -> Stream m (MutArray a) -> Stream m a
211+ [A] concatRev :: forall m a. (MonadIO m, Unbox a) => Stream m (MutArray a) -> Stream m a
212+ [A] concat :: forall m a. (MonadIO m, Unbox a) => Stream m (MutArray a) -> Stream m a
213+ [A] compactOnByteSuffix :: MonadIO m => Word8 -> Stream m (MutArray Word8) -> Stream m (MutArray Word8)
214+ [A] compactOnByte :: MonadIO m => Word8 -> Stream m (MutArray Word8) -> Stream m (MutArray Word8)
215+ [A] compactLeAs :: forall m a. (MonadIO m, Unbox a) => PinnedState -> Int -> Stream m (MutArray a) -> Stream m (MutArray a)
216+ [A] compactLE :: (MonadIO m, Unbox a) => Int -> Stream m (MutArray a) -> Stream m (MutArray a)
217+ [A] compactGE :: (MonadIO m, Unbox a) => Int -> Stream m (MutArray a) -> Stream m (MutArray a)
218+ [A] compactEQ :: Int -> Stream m (MutArray a) -> Stream m (MutArray a)
219+ [D] cmp :: MonadIO m => MutArray a -> MutArray a -> m Ordering
220+ [A] byteEq :: MonadIO m => MutArray a -> MutArray a -> m Bool
221+ [A] byteCmp :: MonadIO m => MutArray a -> MutArray a -> m Ordering
222+ [A] buildChunks :: (MonadIO m, Unbox a) => Int -> Fold m a (StreamK n (MutArray a))
223+ [D] asPtrUnsafe :: MonadIO m => MutArray a -> (Ptr a -> m b) -> m b
224+ [A] appendWith :: forall m a. (MonadIO m, Unbox a) => (Int -> Int) -> m (MutArray a) -> Fold m a (MutArray a)
225+ [A] appendN :: forall m a. (MonadIO m, Unbox a) => Int -> m (MutArray a) -> Fold m a (MutArray a)
226+ [A] append :: forall m a. (MonadIO m, Unbox a) => m (MutArray a) -> Fold m a (MutArray a)
227+[D] Streamly.Internal.Data.Array.Stream
228+ [C] interposeSuffix
229+ [O] interposeSuffix :: (Monad m, Unbox a) => a -> Stream m (Array a) -> Stream m a
230+ [N] interposeSuffix :: forall m a. (Monad m, Unbox a) => a -> Stream m (Array a) -> Stream m a
231+ [D] flattenArraysRev :: forall m a. (MonadIO m, Unbox a) => Stream m (Array a) -> Stream m a
232+ [D] flattenArrays :: forall m a. (MonadIO m, Unbox a) => Stream m (Array a) -> Stream m a
233+ [C] concatRev
234+ [O] concatRev :: (Monad m, Unbox a) => Stream m (Array a) -> Stream m a
235+ [N] concatRev :: forall m a. (Monad m, Unbox a) => Stream m (Array a) -> Stream m a
236+ [D] bufferChunks :: (MonadIO m, Unbox a) => Stream m a -> m (StreamK m (Array a))
237+[C] Streamly.Internal.Data.Array.Generic
238+ [A] createOf :: MonadIO m => Int -> Fold m a (Array a)
239+ [A] create :: MonadIO m => Fold m a (Array a)
240+[C] Streamly.Internal.Data.Array
241+ [R] ArrayUnsafe
242+ [D] writeWith :: forall m a. (MonadIO m, Unbox a) => Int -> Fold m a (Array a)
243+ [D] writeNUnsafe :: forall m a. (MonadIO m, Unbox a) => Int -> Fold m a (Array a)
244+ [A] unsafePinnedCreateOf :: forall m a. (MonadIO m, Unbox a) => Int -> Fold m a (Array a)
245+ [A] unsafePinnedAsPtr :: MonadIO m => Array a -> (Ptr a -> m b) -> m b
246+ [A] unsafeCreateOf :: forall m a. (MonadIO m, Unbox a) => Int -> Fold m a (Array a)
247+ [D] toStreamDRev :: forall m a. (Monad m, Unbox a) => Array a -> Stream m a
248+ [D] toStreamD :: forall m a. (Monad m, Unbox a) => Array a -> Stream m a
249+ [C] splice
250+ [O] splice :: (MonadIO m, Unbox a) => Array a -> Array a -> m (Array a)
251+ [N] splice :: MonadIO m => Array a -> Array a -> m (Array a)
252+ [A] slicerFromLen :: forall m a. (Monad m, Unbox a) => Int -> Int -> Unfold m (Array a) (Array a)
253+ [A] sliceIndexerFromLen :: forall m a. (Monad m, Unbox a) => Int -> Int -> Unfold m (Array a) (Int, Int)
254+ [D] pinnedWriteNUnsafe :: forall m a. (MonadIO m, Unbox a) => Int -> Fold m a (Array a)
255+ [D] pinnedWriteNAligned :: forall m a. (MonadIO m, Unbox a) => Int -> Int -> Fold m a (Array a)
256+ [D] pinnedWriteN :: forall m a. (MonadIO m, Unbox a) => Int -> Fold m a (Array a)
257+ [D] pinnedWrite :: forall m a. (MonadIO m, Unbox a) => Fold m a (Array a)
258+ [A] pinnedCreateOf :: forall m a. (MonadIO m, Unbox a) => Int -> Fold m a (Array a)
259+ [A] pinnedCreate :: forall m a. (MonadIO m, Unbox a) => Fold m a (Array a)
260+ [A] pinnedCompactLE :: (MonadIO m, Unbox a) => Int -> Stream m (Array a) -> Stream m (Array a)
261+ [A] parseBreakChunksK :: forall m a b. (MonadIO m, Unbox a) => Parser a m b -> StreamK m (Array a) -> m (Either ParseError b, StreamK m (Array a))
262+ [D] nil :: Array a
263+ [A] lPinnedCompactGE :: (MonadIO m, Unbox a) => Int -> Fold m (Array a) () -> Fold m (Array a) ()
264+ [A] lCompactGE :: (MonadIO m, Unbox a) => Int -> Fold m (Array a) () -> Fold m (Array a) ()
265+ [A] interposeSuffix :: forall m a. (Monad m, Unbox a) => a -> Stream m (Array a) -> Stream m a
266+ [A] interpose :: (Monad m, Unbox a) => a -> Stream m (Array a) -> Stream m a
267+ [A] intercalateSuffix :: (Monad m, Unbox a) => Array a -> Stream m (Array a) -> Stream m a
268+ [A] indexReaderFromThenTo :: Unfold m (Int, Int, Int, Array a) a
269+ [A] indexReader :: (Monad m, Unbox a) => Stream m Int -> Unfold m (Array a) a
270+ [A] indexFinder :: (a -> Bool) -> Unfold Identity (Array a) Int
271+ [D] getSlicesFromLen :: forall m a. (Monad m, Unbox a) => Int -> Int -> Unfold m (Array a) (Array a)
272+ [R] getIndicesFromThenTo :: Unfold m (Int, Int, Int, Array a) a
273+ [D] getIndices :: (Monad m, Unbox a) => Stream m Int -> Unfold m (Array a) a
274+ [D] genSlicesFromLen :: forall m a. (Monad m, Unbox a) => Int -> Int -> Unfold m (Array a) (Int, Int)
275+ [D] fromStreamDN :: forall m a. (MonadIO m, Unbox a) => Int -> Stream m a -> m (Array a)
276+ [D] fromStreamD :: forall m a. (MonadIO m, Unbox a) => Stream m a -> m (Array a)
277+ [A] fromPureStreamN :: Unbox a => Int -> Stream Identity a -> Array a
278+ [A] fromPtrN :: Int -> Ptr Word8 -> Array Word8
279+ [A] fromChunksK :: (MonadIO m, Unbox a) => StreamK m (Array a) -> m (Array a)
280+ [A] fromChunks :: (MonadIO m, Unbox a) => Stream m (Array a) -> m (Array a)
281+ [A] fromByteStr :: Ptr Word8 -> Array Word8
282+ [A] foldChunks :: (MonadIO m, Unbox a) => Fold m a b -> Stream m (Array a) -> m b
283+ [A] foldBreakChunksK :: forall m a b. (MonadIO m, Unbox a) => Fold m a b -> StreamK m (Array a) -> m (b, StreamK m (Array a))
284+ [A] foldBreakChunks :: forall m a b. (MonadIO m, Unbox a) => Fold m a b -> Stream m (Array a) -> m (b, Stream m (Array a))
285+ [D] flattenArraysRev :: forall m a. (MonadIO m, Unbox a) => Stream m (Array a) -> Stream m a
286+ [D] flattenArrays :: forall m a. (MonadIO m, Unbox a) => Stream m (Array a) -> Stream m a
287+ [C] findIndicesOf
288+ [O] findIndicesOf :: (a -> Bool) -> Unfold Identity (Array a) Int
289+ [N] findIndicesOf :: (a -> Bool) -> Array a -> Stream Identity Int
290+ [A] fPinnedCompactGE :: (MonadIO m, Unbox a) => Int -> Fold m (Array a) (Array a)
291+ [A] fCompactGE :: (MonadIO m, Unbox a) => Int -> Fold m (Array a) (Array a)
292+ [A] empty :: Array a
293+ [A] createWith :: forall m a. (MonadIO m, Unbox a) => Int -> Fold m a (Array a)
294+ [A] createOf :: forall m a. (MonadIO m, Unbox a) => Int -> Fold m a (Array a)
295+ [A] create :: forall m a. (MonadIO m, Unbox a) => Fold m a (Array a)
296+ [A] concatRev :: forall m a. (Monad m, Unbox a) => Stream m (Array a) -> Stream m a
297+ [A] concat :: (Monad m, Unbox a) => Stream m (Array a) -> Stream m a
298+ [A] compactOnByteSuffix :: MonadIO m => Word8 -> Stream m (Array Word8) -> Stream m (Array Word8)
299+ [A] compactOnByte :: MonadIO m => Word8 -> Stream m (Array Word8) -> Stream m (Array Word8)
300+ [A] compactLE :: (MonadIO m, Unbox a) => Int -> Stream m (Array a) -> Stream m (Array a)
301+ [A] compactGE :: (MonadIO m, Unbox a) => Int -> Stream m (Array a) -> Stream m (Array a)
302+ [A] byteEq :: Array a -> Array a -> Bool
303+ [A] byteCmp :: Array a -> Array a -> Ordering
304+ [A] buildChunks :: (MonadIO m, Unbox a) => Stream m a -> m (StreamK m (Array a))
305+ [D] bufferChunks :: (MonadIO m, Unbox a) => Stream m a -> m (StreamK m (Array a))
306+ [D] asPtrUnsafe :: MonadIO m => Array a -> (Ptr a -> m b) -> m b
core/docs/Changelog.md+8
@@ -1,5 +1,13 @@
11# Changelog
22
3+## 0.2.2 (Jan 2024)
4+
5+* Add fixities `infixr 5` for `cons` and `consM` functions.
6+* Fix a bug in Array `Eq` instance when the type is a sum type with
7+ differently sized constructors.
8+* lpackArraysChunksOf, compact, writeChunksWith, putChunksWith now take the
9+ buffer size in number of array elements instead of bytes.
10+
311## 0.2.1 (Dec 2023)
412
513* Make the serialization of the unit constructor deterministic.
core/src/DocTestDataArray.hs+3
@@ -11,9 +11,12 @@
1111
1212>>> import qualified Streamly.Data.Array as Array
1313>>> import qualified Streamly.Data.Fold as Fold
14+>>> import qualified Streamly.Data.ParserK as ParserK
1415>>> import qualified Streamly.Data.Stream as Stream
16+>>> import qualified Streamly.Data.StreamK as StreamK
1517
1618For APIs that have not been released yet.
1719
1820>>> import qualified Streamly.Internal.Data.Array as Array
21+>>> import qualified Streamly.Internal.Data.Stream as Stream
1922-}
core/src/DocTestDataStream.hs+2
@@ -6,6 +6,7 @@
66>>> import Control.Monad.IO.Class (MonadIO (liftIO))
77>>> import Control.Monad.Trans.Class (lift)
88>>> import Control.Monad.Trans.Identity (runIdentityT)
9+>>> import Data.Char (isSpace)
910>>> import Data.Either (fromLeft, fromRight, isLeft, isRight, either)
1011>>> import Data.Maybe (fromJust, isJust)
1112>>> import Data.Function (fix, (&))
@@ -32,6 +33,7 @@ For APIs that have not been released yet.
3233>>> import qualified Streamly.Internal.Data.Fold as Fold
3334>>> import qualified Streamly.Internal.Data.Parser as Parser
3435>>> import qualified Streamly.Internal.Data.Stream as Stream
36+>>> import qualified Streamly.Internal.Data.StreamK as StreamK
3537>>> import qualified Streamly.Internal.Data.Unfold as Unfold
3638>>> import qualified Streamly.Internal.FileSystem.Dir as Dir
3739-}
core/src/DocTestFileSystemHandle.hs+1−1
@@ -8,7 +8,7 @@
88
99For APIs that have not been released yet.
1010
11−>>> import qualified Streamly.Internal.Data.Array as Array (writeNUnsafe)
11+>>> import qualified Streamly.Internal.Data.Array as Array (unsafeCreateOf)
1212>>> import qualified Streamly.Internal.Data.Unfold as Unfold (first)
1313>>> import qualified Streamly.Internal.FileSystem.Handle as Handle
1414>>> import qualified Streamly.Internal.System.IO as IO (defaultChunkSize)
core/src/Streamly/Data/Array.hs+30−27
@@ -31,42 +31,42 @@ module Streamly.Data.Array
3131 -- * The Array Type
3232 Array
3333
34+ -- * Pinning & Unpinning
35+ -- | Arrays are created unpinned by default unless pinned versions of
36+ -- creation APIs are used. Look for APIs with @pinned@ prefix in
37+ -- "Streamly.Internal.Data.Array" for some unreleased pinned creation APIs.
38+ -- If an array is to be sent to the OS without any further modification
39+ -- then it should be created pinned in the first place instead of pinning
40+ -- it later. Pinning an unpinned array has a copy overhead. OS interfacing
41+ -- APIs create a pinned array directly or convert an unpinned array to
42+ -- pinned array before sending it to the OS.
43+ , pin
44+ , unpin
45+ , isPinned
46+
3447 -- * Construction
3548 -- | When performance matters, the fastest way to generate an array is
36− -- 'writeN'. 'IsList' and 'IsString' instances can be
49+ -- 'createOf'. 'IsList' and 'IsString' instances can be
3750 -- used to conveniently construct arrays from literal values.
3851 -- 'OverloadedLists' extension or 'fromList' can be used to construct an
3952 -- array from a list literal. Similarly, 'OverloadedStrings' extension or
4053 -- 'fromList' can be used to construct an array from a string literal.
4154
42− -- Pure List APIs
55+ -- ** From Stream
56+ , createOf
57+ , create
58+ , writeLastN -- drop old (ring buffer)
59+
60+ -- ** From List
4361 , fromListN
4462 , fromList
4563
46− -- Monadic APIs
47− , writeN -- drop new
48− , write -- full buffer
49− , writeLastN -- drop old (ring buffer)
50−
51− -- * Conversion
64+ -- * To List
5265 -- 'GHC.Exts.toList' from "GHC.Exts" can be used to convert an array to a
5366 -- list.
5467 , toList
5568
56− -- * Pinning & Unpinning
57− -- | Arrays are created unpinned by default unless pinned versions of
58− -- creation APIs are used. Look for APIs with @pinned@ prefix in
59− -- "Streamly.Internal.Data.Array" for some unreleased pinned creation APIs.
60− -- If an array is to be sent to the OS without any further modification
61− -- then it should be created pinned in the first place instead of pinning
62− -- it later. Pinning an unpinned array has a copy overhead. OS interfacing
63− -- APIs create a pinned array directly or convert an unpinned array to
64− -- pinned array before sending it to the OS.
65− , pin
66− , unpin
67− , isPinned
68−
69− -- * Streams
69+ -- * To Stream
7070 , read
7171 , readRev
7272
@@ -90,6 +90,10 @@ module Streamly.Data.Array
9090 -- * Re-exports
9191 , Unbox (..)
9292 , Serialize(..)
93+
94+ -- * Deprecated
95+ , writeN -- drop new
96+ , write -- full buffer
9397 )
9498where
9599
@@ -122,7 +126,7 @@ import Prelude hiding (read, length)
122126--
123127-- Convert array to stream, transform, and fold back to array:
124128--
125−-- >>> amap f arr = Array.read arr & fmap f & Stream.fold Array.write
129+-- >>> amap f arr = Array.read arr & fmap f & Stream.fold (Array.createOf (Array.length arr))
126130-- >>> amap (+1) (Array.fromList [1,2,3::Int])
127131-- fromList [2,3,4]
128132--
@@ -149,12 +153,11 @@ import Prelude hiding (read, length)
149153-- See the 'fromPureStream' unreleased API to generate an array from an
150154-- Identity stream safely without using MonadIO constraint.
151155--
152−--
153156-- Note that 'Identity' streams can be generalized to IO streams:
154157--
155−-- >>> pure = Stream.fromList [1,2,3] :: Stream Identity Int
156−-- >>> generally = Stream.morphInner (return . runIdentity)
157−-- >>> Stream.fold Array.write (generally pure :: Stream IO Int)
158+-- >>> stream = Stream.fromList [1,2,3] :: Stream Identity Int
159+-- >>> pure2IO s = Stream.morphInner (return . runIdentity) s :: Stream IO Int
160+-- >>> Stream.fold Array.create (pure2IO stream)
158161-- fromList [1,2,3]
159162--
160163-- == Programming Tips
core/src/Streamly/Data/Array/Generic.hs+16−13
@@ -15,34 +15,37 @@ module Streamly.Data.Array.Generic
1515 ( Array
1616
1717 -- * Construction
18− , A.fromListN
19− , A.fromList
18+ , fromListN
19+ , fromList
2020
2121 -- MonadicAPIs
22− , A.writeN
23− , A.write
22+ , createOf
23+ , create
2424
2525 -- * Conversion
26− , A.toList
26+ , toList
2727
2828 -- * Streams
29− , A.read
30− , A.readRev
29+ , read
30+ , readRev
3131
3232 -- * Unfolds
33− , A.reader
33+ , reader
3434 -- , A.readerRev
3535
3636 -- * Random Access
37− , A.length
38− , A.getIndex
37+ , length
38+ , getIndex
3939
4040 -- -- * Folding Arrays
4141 -- , A.streamFold
4242 -- , A.fold
43+
44+ -- * Deprecated
45+ , writeN
46+ , write
4347 )
4448where
4549
46−import Streamly.Internal.Data.Array.Generic (Array)
47−
48−import qualified Streamly.Internal.Data.Array.Generic as A
50+import Streamly.Internal.Data.Array.Generic
51+import Prelude hiding (length, read)
core/src/Streamly/Data/MutArray.hs+12−6
@@ -32,14 +32,14 @@ module Streamly.Data.MutArray
3232 -- * Construction
3333
3434 -- Uninitialized Arrays
35− , new
36− , pinnedNew
35+ , emptyOf
36+ , pinnedEmptyOf
3737
3838 -- From containers
3939 , fromListN
4040 , fromList
41− , writeN -- drop new
42− , write -- full buffer
41+ , createOf
42+ , create
4343 -- writeLastN
4444
4545 -- * Pinning & Unpinning
@@ -51,8 +51,8 @@ module Streamly.Data.MutArray
5151 , snoc
5252
5353 -- * Appending streams
54− , writeAppendN -- XXX Rename to writeSnocN?
55− , writeAppend -- XXX Rename to writeSnoc?
54+ , appendN
55+ , append
5656
5757 -- * Inplace mutation
5858 , putIndex
@@ -88,6 +88,12 @@ module Streamly.Data.MutArray
8888
8989 -- * Deprecated
9090 , newPinned
91+ , new
92+ , pinnedNew
93+ , writeN
94+ , write
95+ , writeAppendN
96+ , writeAppend
9197 )
9298where
9399
core/src/Streamly/Data/MutArray/Generic.hs+8−3
@@ -23,11 +23,11 @@ module Streamly.Data.MutArray.Generic
2323 MutArray
2424
2525 -- * Construction
26− , new
26+ , emptyOf
2727 , fromListN
2828 , fromList
29− , writeN
30− , write
29+ , createOf
30+ , create
3131
3232 -- * Appending elements
3333 , snoc
@@ -56,6 +56,11 @@ module Streamly.Data.MutArray.Generic
5656
5757 -- * Size
5858 , length
59+
60+ -- * Deprecated
61+ , new
62+ , writeN
63+ , write
5964 )
6065where
6166
core/src/Streamly/Data/Stream.hs+129−20
@@ -70,6 +70,15 @@ module Streamly.Data.Stream
7070
7171 -- ** From Values
7272 -- | Generate a monadic stream from a seed value or values.
73+ --
74+ -- Non-empty pure streams can be cycled using repeat and unfold:
75+ --
76+ -- >>> cycle = Stream.unfoldMany Unfold.fromList . Stream.repeat
77+ --
78+ -- Non-empty StreamK can be cycled using append:
79+ --
80+ -- >>> cycle xs = let ys = xs `StreamK.append` ys in ys
81+ --
7382 , fromPure
7483 , fromEffect
7584 , repeat
@@ -212,21 +221,57 @@ module Streamly.Data.Stream
212221 -- operations like mapping a function over the stream.
213222 , foldrM
214223 , foldr
224+ -- foldr1
215225
216226 -- ** Specific Folds
217− -- | Usually you can use the folds in "Streamly.Data.Fold". However, some
218− -- folds that may be commonly used or may have an edge in performance in
219− -- some cases are provided here.
220− --
221− -- Useful idioms:
227+ -- | Streams are folded using folds in "Streamly.Data.Fold". Here are some
228+ -- idioms and equivalents of Data.List APIs using folds:
222229 --
223230 -- >>> foldlM' f a = Stream.fold (Fold.foldlM' f a)
224− -- >>> foldl1 f = Stream.fold (Fold.foldl1' f)
231+ -- >>> foldl1' f = Stream.fold (Fold.foldl1' f)
225232 -- >>> foldl' f a = Stream.fold (Fold.foldl' f a)
226233 -- >>> drain = Stream.fold Fold.drain
227234 -- >>> mapM_ f = Stream.fold (Fold.drainMapM f)
228235 -- >>> length = Stream.fold Fold.length
236+ -- >>> genericLength = Stream.fold Fold.lengthGeneric
229237 -- >>> head = Stream.fold Fold.one
238+ -- >>> last = Stream.fold Fold.latest
239+ -- >>> null = Stream.fold Fold.null
240+ -- >>> and = Stream.fold Fold.and
241+ -- >>> or = Stream.fold Fold.or
242+ -- >>> any p = Stream.fold (Fold.any p)
243+ -- >>> all p = Stream.fold (Fold.all p)
244+ -- >>> sum = Stream.fold Fold.sum
245+ -- >>> product = Stream.fold Fold.product
246+ -- >>> maximum = Stream.fold Fold.maximum
247+ -- >>> maximumBy cmp = Stream.fold (Fold.maximumBy cmp)
248+ -- >>> minimum = Stream.fold Fold.minimum
249+ -- >>> minimumBy cmp = Stream.fold (Fold.minimumBy cmp)
250+ -- >>> elem x = Stream.fold (Fold.elem x)
251+ -- >>> notElem x = Stream.fold (Fold.notElem x)
252+ -- >>> lookup x = Stream.fold (Fold.lookup x)
253+ -- >>> find p = Stream.fold (Fold.find p)
254+ -- >>> (!?) i = Stream.fold (Fold.index i)
255+ -- >>> genericIndex i = Stream.fold (Fold.indexGeneric i)
256+ -- >>> elemIndex x = Stream.fold (Fold.elemIndex x)
257+ -- >>> findIndex p = Stream.fold (Fold.findIndex p)
258+ --
259+ -- Some equivalents of Data.List APIs from the Stream module:
260+ --
261+ -- >>> head = fmap (fmap fst) . Stream.uncons
262+ -- >>> tail = fmap (fmap snd) . Stream.uncons
263+ -- >>> tail = Stream.tail -- unreleased API
264+ -- >>> init = Stream.init -- unreleased API
265+ --
266+ -- A Stream based toList fold implementation is provided below because it
267+ -- has a better performance compared to the fold.
268+
269+ -- Functions in Data.List, missing here:
270+ -- unsnoc = Stream.parseBreak (Parser.init Fold.toList)
271+ -- genericTake
272+ -- genericDrop
273+ -- genericSplitAt
274+ -- genericReplicate
230275 , toList
231276
232277 -- * Mapping
@@ -280,6 +325,15 @@ module Streamly.Data.Stream
280325 , scan
281326 , postscan
282327 -- XXX postscan1 can be implemented using Monoids or Refolds.
328+ -- The following scans from Data.List are not provided.
329+ -- XXX scanl
330+ -- XXX scanl1
331+ -- XXX scanr
332+ -- XXX scanr1
333+ -- XXX mapAccumL
334+ -- XXX mapAccumR
335+ -- XXX inits
336+ -- XXX tails
283337
284338 -- ** Specific scans
285339 -- Indexing can be considered as a special type of zipping where we zip a
@@ -288,6 +342,8 @@ module Streamly.Data.Stream
288342
289343 -- * Insertion
290344 -- | Add elements to the stream.
345+ --
346+ -- >>> insert = Stream.insertBy compare
291347
292348 -- Inserting elements is a special case of interleaving/merging streams.
293349 , insertBy
@@ -323,12 +379,15 @@ module Streamly.Data.Stream
323379 -- "Streamly.Internal.Data.Fold" can be used along with 'scanMaybe' to
324380 -- perform stateful filtering operations in general.
325381 --
326− -- Useful idioms:
382+ -- Idioms and equivalents of Data.List APIs:
327383 --
328384 -- >>> deleteBy cmp x = Stream.scanMaybe (Fold.deleteBy cmp x)
385+ -- >>> deleteBy = Stream.deleteBy -- unreleased API
386+ -- >>> delete = deleteBy (==)
329387 -- >>> findIndices p = Stream.scanMaybe (Fold.findIndices p)
330388 -- >>> elemIndices a = findIndices (== a)
331389 -- >>> uniq = Stream.scanMaybe (Fold.uniqBy (==))
390+ -- >>> partition p = Stream.fold (Fold.partition Fold.toList Fold.toList) . fmap (if p then Left else Right)
332391 , scanMaybe
333392 , take
334393 , takeWhile
@@ -336,6 +395,8 @@ module Streamly.Data.Stream
336395 , drop
337396 , dropWhile
338397 , dropWhileM
398+ -- XXX write to an array in reverse and then read in reverse
399+ -- > dropWhileEnd = reverse . dropWhile p . reverse
339400
340401 -- XXX These are available as scans in folds. We need to check the
341402 -- performance though. If these are common and we need convenient stream
@@ -372,6 +433,9 @@ module Streamly.Data.Stream
372433 -- >>> s = s1 `Stream.append` s2
373434
374435 -- ** Appending
436+ -- | Equivalent of Data.List append:
437+ --
438+ -- >>> (++) = Stream.append
375439 , append
376440
377441 -- ** Interleaving
@@ -382,8 +446,14 @@ module Streamly.Data.Stream
382446 , mergeByM
383447
384448 -- ** Zipping
449+ -- | Idioms and equivalents of Data.List APIs:
450+ --
451+ -- >>> zip = Stream.zipWith (,)
452+ -- >>> unzip = Stream.fold (Fold.unzip Fold.toList Fold.toList)
385453 , zipWith
386454 , zipWithM
455+ -- XXX zipWith3,4,5,6,7
456+ -- XXX unzip3,4,5,6,7
387457 -- , ZipStream (..)
388458
389459 -- ** Cross Product
@@ -416,48 +486,89 @@ module Streamly.Data.Stream
416486 -- One dimension loops are just a special case of nested loops. For
417487 -- example, 'concatMap' can degenerate to a simple map operation:
418488 --
419− -- > map f m = S.concatMap (\x -> S.fromPure (f x)) m
489+ -- >>> map f m = Stream.concatMap (\x -> Stream.fromPure (f x)) m
420490 --
421491 -- Similarly, 'concatMap' can perform filtering by mapping an element to a
422492 -- 'nil' stream:
423493 --
424− -- > filter p m = S.concatMap (\x -> if p x then S.fromPure x else S.nil) m
494+ -- >>> filter p m = Stream.concatMap (\x -> if p x then Stream.fromPure x else Stream.nil) m
495+ --
496+ -- Idioms and equivalents of Data.List APIs:
425497 --
498+ -- >>> concat = Stream.concatMap id
499+ -- >>> unlines = Stream.interposeSuffix '\n'
500+ -- >>> unlines = Stream.intercalateSuffix Unfold.fromList "\n"
501+ -- >>> unwords = Stream.interpose ' '
502+ -- >>> unwords = Stream.intercalate Unfold.fromList " "
426503
427504 , concatEffect
428505 , concatMap
429506 , concatMapM
430507
431508 -- * Repeated Fold
432− -- | Useful idioms:
509+ -- | Idioms and equivalents of Data.List APIs:
433510 --
434− -- >>> splitWithSuffix p f = Stream.foldMany (Fold.takeEndBy p f)
435− -- >>> splitOnSuffix p f = Stream.foldMany (Fold.takeEndBy_ p f)
436− -- >>> groupsBy eq f = Stream.parseMany (Parser.groupBy eq f)
437− -- >>> groupsByRolling eq f = Stream.parseMany (Parser.groupByRolling eq f)
438− -- >>> groupsOf n f = Stream.foldMany (Fold.take n f)
511+ -- >>> groupsOf n = Stream.foldMany (Fold.take n Fold.toList)
512+ -- >>> groupBy eq = Stream.groupsWhile eq Fold.toList
513+ -- >>> groupBy eq = Stream.parseMany (Parser.groupBy eq Fold.toList)
514+ -- >>> groupsByRolling eq = Stream.parseMany (Parser.groupByRolling eq Fold.toList)
515+ -- >>> groups = groupBy (==)
439516 , foldMany -- XXX Rename to foldRepeat
440517 , groupsOf
441518 , parseMany
442519
443520 -- * Splitting
521+ -- | Idioms and equivalents of Data.List APIs:
522+ --
523+ -- >>> splitWithSuffix p f = Stream.foldMany (Fold.takeEndBy p f)
524+ -- >>> splitOnSuffix p f = Stream.foldMany (Fold.takeEndBy_ p f)
525+ -- >>> lines = splitOnSuffix (== '\n')
526+ -- >>> words = Stream.wordsBy isSpace
527+ -- >>> splitAt n = Stream.fold (Fold.splitAt n Fold.toList Fold.toList)
528+ -- >>> span p = Parser.splitWith (,) (Parser.takeWhile p Fold.toList) (Parser.fromFold Fold.toList)
529+ -- >>> break p = span (not . p)
444530 , splitOn
445531 , wordsBy
446532
447533 -- * Buffered Operations
448534 -- | Operations that require buffering of the stream.
449535 -- Reverse is essentially a left fold followed by an unfold.
536+ --
537+ -- Idioms and equivalents of Data.List APIs:
538+ --
539+ -- >>> nub = Stream.fold Fold.toList . Stream.scanMaybe Fold.nub
540+ -- >>> nub = Stream.ordNub -- unreleased API
541+ -- >>> sortBy = StreamK.sortBy
542+ -- >>> sortOn f = StreamK.sortOn -- unreleased API
543+ -- >>> deleteFirstsBy = Stream.deleteFirstsBy -- unreleased
544+ -- >>> (\\) = Stream.deleteFirstsBy (==) -- unreleased
545+ -- >>> intersectBy = Stream.intersectBy -- unreleased
546+ -- >>> intersect = Stream.intersectBy (==) -- unreleased
547+ -- >>> unionBy = Stream.unionBy -- unreleased
548+ -- >>> union = Stream.unionBy (==) -- unreleased
549+ --
450550 , reverse
551+ -- XXX transpose: write the streams to arrays and then stream transposed.
552+ -- XXX subsequences
553+ -- XXX permutations
554+ -- , nub
555+ -- , ordNub
556+ -- , nubBy
451557
452558 -- * Multi-Stream folds
453559 -- | Operations that consume multiple streams at the same time.
454560 , eqBy
455561 , cmpBy
456562 , isPrefixOf
563+ , isInfixOf
564+ -- , isSuffixOf
565+ -- , isSuffixOfUnbox
457566 , isSubsequenceOf
458567
459568 -- trimming sequences
460− , stripPrefix
569+ -- , stripPrefix
570+ -- , stripSuffix
571+ -- , stripSuffixUnbox
461572
462573 -- Exceptions and resource management depend on the "exceptions" package
463574 -- XXX We can have IO Stream operations not depending on "exceptions"
@@ -521,10 +632,8 @@ where
521632import Streamly.Internal.Data.Stream
522633import Prelude
523634 hiding (filter, drop, dropWhile, take, takeWhile, zipWith, foldr,
524− foldl, map, mapM, mapM_, sequence, all, any, sum, product, elem,
525− notElem, maximum, minimum, head, last, tail, length, null,
526− reverse, iterate, init, and, or, lookup, foldr1, (!!),
527− scanl, scanl1, repeat, replicate, concatMap, span)
635+ mapM, sequence, reverse, iterate, foldr1, repeat, replicate,
636+ concatMap)
528637
529638import qualified Streamly.Internal.Data.Array.Type as Array
530639
core/src/Streamly/Internal/Data/Array.hs+551−128
@@ -2,7 +2,6 @@
22-- |
33-- Module : Streamly.Internal.Data.Array
44-- Copyright : (c) 2019 Composewell Technologies
5−--
65-- License : BSD3
76-- Maintainer : streamly@composewell.com
87-- Stability : experimental
@@ -20,30 +19,21 @@ module Streamly.Internal.Data.Array
2019 module Streamly.Internal.Data.Array.Type
2120
2221 -- * Construction
23− -- Stream Folds
24− , fromStreamN
25− , fromStream
26−
2722 -- Monadic Folds
2823 , writeLastN
2924
30− -- * Unfolds
31− , reader
32− , readerUnsafe
33− , producer -- experimental
34−
3525 -- * Random Access
3626 -- , (!!)
3727 , getIndex
3828 , getIndexRev
39− , last -- XXX getIndexLast?
40− , getIndices
41− , getIndicesFromThenTo
29+ , last -- XXX getLastIndex?
4230 -- , getIndicesFrom -- read from a given position to the end of file
4331 -- , getIndicesUpto -- read from beginning up to the given position
4432 -- , getIndicesFromTo
4533 -- , getIndicesFromRev -- read from a given position to the beginning of file
4634 -- , getIndicesUptoRev -- read from end to the given position in file
35+ , indexReader
36+ , indexReaderFromThenTo
4737
4838 -- * Size
4939 , null
@@ -51,6 +41,8 @@ module Streamly.Internal.Data.Array
5141 -- * Search
5242 , binarySearch
5343 , findIndicesOf
44+ -- getIndicesOf
45+ , indexFinder -- see splitOn
5446 -- , findIndexOf
5547 -- , find
5648
@@ -63,8 +55,8 @@ module Streamly.Internal.Data.Array
6355 -- * Subarrays
6456 , getSliceUnsafe
6557 -- , getSlice
66− , genSlicesFromLen
67− , getSlicesFromLen
58+ , sliceIndexerFromLen
59+ , slicerFromLen
6860 , splitOn
6961
7062 -- * Streaming Operations
@@ -74,11 +66,34 @@ module Streamly.Internal.Data.Array
7466 , streamFold
7567 , fold
7668
69+ -- * Stream of Arrays
70+
71+ -- XXX these are probably not very useful to have in this module as we can
72+ -- express these idiomatically using streams.
73+ , interpose
74+ , interposeSuffix
75+ , intercalateSuffix
76+
77+ , compactLE
78+ , pinnedCompactLE
79+ , compactOnByte
80+ , compactOnByteSuffix
81+
82+ , foldBreakChunks
83+ , foldChunks
84+ , foldBreakChunksK
85+ , parseBreakChunksK
86+
7787 -- * Serialization
7888 , encodeAs
7989 , serialize
8090 , pinnedSerialize
8191 , deserialize
92+
93+ -- * Deprecated
94+ , genSlicesFromLen
95+ , getSlicesFromLen
96+ , getIndices
8297 )
8398where
8499
@@ -86,37 +101,43 @@ where
86101#include "inline.hs"
87102#include "ArrayMacros.h"
88103
89−import Control.Monad (when)
90104import Control.Monad.IO.Class (MonadIO(..))
105+-- import Data.Bifunctor (first)
106+-- import Data.Either (fromRight)
91107import Data.Functor.Identity (Identity)
92108import Data.Proxy (Proxy(..))
93109import Data.Word (Word8)
94110import Foreign.C.String (CString)
95111import Foreign.Ptr (castPtr)
96112import Foreign.Storable (Storable)
113+import GHC.Types (SPEC(..))
97114import Streamly.Internal.Data.Unbox (Unbox(..))
98115import Prelude hiding (length, null, last, map, (!!), read, concat)
99116
100−import Streamly.Internal.Data.MutByteArray.Type (PinnedState(..))
117+import Streamly.Internal.Data.MutByteArray.Type (PinnedState(..), MutByteArray)
101118import Streamly.Internal.Data.Serialize.Type (Serialize)
102119import Streamly.Internal.Data.Fold.Type (Fold(..))
103−import Streamly.Internal.Data.Producer.Type (Producer(..))
104−import Streamly.Internal.Data.Stream (Stream)
105−import Streamly.Internal.Data.Tuple.Strict (Tuple3Fused'(..))
120+import Streamly.Internal.Data.Parser (Parser(..), Initial(..), ParseError(..))
121+import Streamly.Internal.Data.Stream (Stream(..))
122+import Streamly.Internal.Data.StreamK (StreamK)
123+import Streamly.Internal.Data.SVar.Type (adaptState, defState)
124+import Streamly.Internal.Data.Tuple.Strict (Tuple'(..), Tuple3Fused'(..))
106125import Streamly.Internal.Data.Unfold.Type (Unfold(..))
107126import Streamly.Internal.System.IO (unsafeInlineIO)
108127
128+import qualified Streamly.Internal.Data.Fold.Type as Fold
109129import qualified Streamly.Internal.Data.Serialize.Type as Serialize
110130import qualified Streamly.Internal.Data.MutByteArray.Type as MBA
111131import qualified Streamly.Internal.Data.MutArray as MA
112−import qualified Streamly.Internal.Data.Array.Type as A
113132import qualified Streamly.Internal.Data.Fold as FL
114−import qualified Streamly.Internal.Data.Producer.Type as Producer
115−import qualified Streamly.Internal.Data.Producer as Producer
116133import qualified Streamly.Internal.Data.Ring as RB
134+import qualified Streamly.Internal.Data.Parser as Parser
135+-- import qualified Streamly.Internal.Data.ParserK as ParserK
117136import qualified Streamly.Internal.Data.Stream as D
118137import qualified Streamly.Internal.Data.Stream as Stream
138+import qualified Streamly.Internal.Data.StreamK as StreamK
119139import qualified Streamly.Internal.Data.Unfold as Unfold
140+import qualified Prelude
120141
121142import Streamly.Internal.Data.Array.Type
122143
@@ -145,82 +166,10 @@ import Streamly.Internal.Data.Array.Type
145166-- if we use one array for each chunk. This is still significant to add
146167-- pressure to GC.
147168
148−-------------------------------------------------------------------------------
149−-- Construction
150−-------------------------------------------------------------------------------
151−
152−-- | Create an 'Array' from the first N elements of a stream. The array is
153−-- allocated to size N, if the stream terminates before N elements then the
154−-- array may hold less than N elements.
155−--
156−-- /Pre-release/
157−{-# INLINE fromStreamN #-}
158−fromStreamN :: (MonadIO m, Unbox a) => Int -> Stream m a -> m (Array a)
159−fromStreamN n m = do
160− when (n < 0) $ error "writeN: negative write count specified"
161− A.fromStreamDN n m
162−
163−-- | Create an 'Array' from a stream. This is useful when we want to create a
164−-- single array from a stream of unknown size. 'writeN' is at least twice
165−-- as efficient when the size is already known.
166−--
167−-- Note that if the input stream is too large memory allocation for the array
168−-- may fail. When the stream size is not known, `chunksOf` followed by
169−-- processing of indvidual arrays in the resulting stream should be preferred.
170−--
171−-- /Pre-release/
172−{-# INLINE fromStream #-}
173−fromStream :: (MonadIO m, Unbox a) => Stream m a -> m (Array a)
174−fromStream = Stream.fold A.write
175−-- write m = A.fromStreamD $ D.fromStreamK m
176−
177169-------------------------------------------------------------------------------
178170-- Elimination
179171-------------------------------------------------------------------------------
180172
181−{-# INLINE_NORMAL producer #-}
182−producer :: forall m a. (Monad m, Unbox a) => Producer m (Array a) a
183−producer =
184− Producer.translate A.unsafeThaw A.unsafeFreeze
185− $ MA.producerWith (return . unsafeInlineIO)
186−
187−-- | Unfold an array into a stream.
188−--
189−{-# INLINE_NORMAL reader #-}
190−reader :: forall m a. (Monad m, Unbox a) => Unfold m (Array a) a
191−reader = Producer.simplify producer
192−
193−-- | Unfold an array into a stream, does not check the end of the array, the
194−-- user is responsible for terminating the stream within the array bounds. For
195−-- high performance application where the end condition can be determined by
196−-- a terminating fold.
197−--
198−-- Written in the hope that it may be faster than "read", however, in the case
199−-- for which this was written, "read" proves to be faster even though the core
200−-- generated with unsafeRead looks simpler.
201−--
202−-- /Pre-release/
203−--
204−{-# INLINE_NORMAL readerUnsafe #-}
205−readerUnsafe :: forall m a. (Monad m, Unbox a) => Unfold m (Array a) a
206−readerUnsafe = Unfold step inject
207− where
208−
209− inject (Array contents start end) =
210− return (ArrayUnsafe contents end start)
211−
212− {-# INLINE_LATE step #-}
213− step (ArrayUnsafe contents end p) = do
214− -- unsafeInlineIO allows us to run this in Identity monad for pure
215− -- toList/foldr case which makes them much faster due to not
216− -- accumulating the list and fusing better with the pure consumers.
217− --
218− -- This should be safe as the array contents are guaranteed to be
219− -- evaluated/written to before we peek at them.
220− let !x = unsafeInlineIO $ peekAt p contents
221− let !p1 = INDEX_NEXT(p,a)
222− return $ D.Yield x (ArrayUnsafe contents end p1)
223−
224173-- |
225174--
226175-- >>> null arr = Array.byteLength arr == 0
@@ -228,7 +177,7 @@ readerUnsafe = Unfold step inject
228177-- /Pre-release/
229178{-# INLINE null #-}
230179null :: Array a -> Bool
231−null arr = A.byteLength arr == 0
180+null arr = byteLength arr == 0
232181
233182-- | Like 'getIndex' but indexes the array in reverse from the end.
234183--
@@ -256,6 +205,8 @@ last = getIndexRev 0
256205-- Folds with Array as the container
257206-------------------------------------------------------------------------------
258207
208+-- XXX We should generate this from Ring.
209+
259210-- | @writeLastN n@ folds a maximum of @n@ elements from the end of the input
260211-- stream to an 'Array'.
261212--
@@ -264,7 +215,7 @@ writeLastN ::
264215 (Storable a, Unbox a, MonadIO m) => Int -> Fold m a (Array a)
265216writeLastN n
266217 | n <= 0 = fmap (const mempty) FL.drain
267− | otherwise = A.unsafeFreeze <$> Fold step initial done done
218+ | otherwise = unsafeFreeze <$> Fold step initial done done
268219
269220 where
270221
@@ -277,11 +228,9 @@ writeLastN n
277228 in fmap f $ liftIO $ RB.new n
278229
279230 done (Tuple3Fused' rb rh i) = do
280− arr <- liftIO $ MA.new n
281− foldFunc i rh snoc' arr rb
282−
283− -- XXX We should write a read unfold for ring.
284− snoc' b a = liftIO $ MA.snocUnsafe b a
231+ arr <- MA.new n
232+ -- XXX We should write a read unfold for ring.
233+ foldFunc i rh MA.snocUnsafe arr rb
285234
286235 foldFunc i
287236 | i < n = RB.unsafeFoldRingM
@@ -307,19 +256,26 @@ binarySearch = undefined
307256-- compared to streams by using SIMD instructions.
308257-- We can also return a bit array instead.
309258
259+-- Can use SIMD.
260+
310261-- | Perform a linear search to find all the indices where a given element is
311262-- present in an array.
312263--
313264-- /Unimplemented/
314−findIndicesOf :: (a -> Bool) -> Unfold Identity (Array a) Int
315−findIndicesOf = undefined
265+indexFinder :: (a -> Bool) -> Unfold Identity (Array a) Int
266+indexFinder = undefined
267+
268+-- |
269+-- /Unimplemented/
270+findIndicesOf :: (a -> Bool) -> Array a -> Stream Identity Int
271+findIndicesOf p = Stream.unfold (indexFinder p)
316272
317273{-
318274findIndexOf :: (a -> Bool) -> Array a -> Maybe Int
319−findIndexOf p = Unfold.fold Fold.one . Stream.unfold (findIndicesOf p)
275+findIndexOf p = Unfold.fold Fold.one . Stream.unfold (indexFinder p)
320276
321277find :: (a -> Bool) -> Array a -> Bool
322−find = Unfold.fold Fold.null . Stream.unfold (findIndicesOf p)
278+find = Unfold.fold Fold.null . Stream.unfold (indexFinder p)
323279-}
324280
325281-------------------------------------------------------------------------------
@@ -361,29 +317,45 @@ splitOn :: (Monad m, Unbox a) =>
361317 (a -> Bool) -> Array a -> Stream m (Array a)
362318splitOn predicate arr =
363319 fmap (\(i, len) -> getSliceUnsafe i len arr)
364− $ D.sliceOnSuffix predicate (A.toStreamD arr)
320+ $ D.indexOnSuffix predicate (read arr)
365321
322+{-# INLINE sliceIndexerFromLen #-}
323+sliceIndexerFromLen :: forall m a. (Monad m, Unbox a)
324+ => Int -- ^ from index
325+ -> Int -- ^ length of the slice
326+ -> Unfold m (Array a) (Int, Int)
327+sliceIndexerFromLen from len =
328+ Unfold.lmap unsafeThaw (MA.sliceIndexerFromLen from len)
329+
330+{-# DEPRECATED genSlicesFromLen "Please use sliceIndexerFromLen instead." #-}
366331{-# INLINE genSlicesFromLen #-}
367332genSlicesFromLen :: forall m a. (Monad m, Unbox a)
368333 => Int -- ^ from index
369334 -> Int -- ^ length of the slice
370335 -> Unfold m (Array a) (Int, Int)
371−genSlicesFromLen from len =
372− Unfold.lmap A.unsafeThaw (MA.genSlicesFromLen from len)
336+genSlicesFromLen = sliceIndexerFromLen
373337
374338-- | Generate a stream of slices of specified length from an array, starting
375339-- from the supplied array index. The last slice may be shorter than the
376340-- requested length.
377341--
378342-- /Pre-release//
343+{-# INLINE slicerFromLen #-}
344+slicerFromLen :: forall m a. (Monad m, Unbox a)
345+ => Int -- ^ from index
346+ -> Int -- ^ length of the slice
347+ -> Unfold m (Array a) (Array a)
348+slicerFromLen from len =
349+ fmap unsafeFreeze
350+ $ Unfold.lmap unsafeThaw (MA.slicerFromLen from len)
351+
352+{-# DEPRECATED getSlicesFromLen "Please use slicerFromLen instead." #-}
379353{-# INLINE getSlicesFromLen #-}
380354getSlicesFromLen :: forall m a. (Monad m, Unbox a)
381355 => Int -- ^ from index
382356 -> Int -- ^ length of the slice
383357 -> Unfold m (Array a) (Array a)
384−getSlicesFromLen from len =
385− fmap A.unsafeFreeze
386− $ Unfold.lmap A.unsafeThaw (MA.getSlicesFromLen from len)
358+getSlicesFromLen = slicerFromLen
387359
388360-------------------------------------------------------------------------------
389361-- Random reads and writes
@@ -413,19 +385,25 @@ getIndex i arr =
413385-- @
414386-- read =
415387-- let u = lmap (\arr -> (0, length arr - 1)) Unfold.enumerateFromTo
416−-- in Unfold.lmap f (getIndices arr)
388+-- in Unfold.lmap f (indexReader arr)
417389--
418390-- readRev =
419391-- let i = length arr - 1
420−-- in Unfold.lmap f (getIndicesFromThenTo i (i - 1) 0)
392+-- in Unfold.lmap f (indexReaderFromThenTo i (i - 1) 0)
421393-- @
422394--
423395-- /Pre-release/
396+{-# INLINE indexReader #-}
397+indexReader :: (Monad m, Unbox a) => Stream m Int -> Unfold m (Array a) a
398+indexReader m =
399+ let unf = MA.indexReaderWith (return . unsafeInlineIO) m
400+ in Unfold.lmap unsafeThaw unf
401+
402+-- XXX DO NOT REMOVE, change the signature to use Stream instead of unfold
403+{-# DEPRECATED getIndices "Please use getIndices instead." #-}
424404{-# INLINE getIndices #-}
425405getIndices :: (Monad m, Unbox a) => Stream m Int -> Unfold m (Array a) a
426−getIndices m =
427− let unf = MA.getIndicesD (return . unsafeInlineIO) m
428− in Unfold.lmap A.unsafeThaw unf
406+getIndices = indexReader
429407
430408-- | Unfolds @(from, then, to, array)@ generating a finite stream whose first
431409-- element is the array value from the index @from@ and the successive elements
@@ -441,9 +419,9 @@ getIndices m =
441419-- @
442420--
443421-- /Unimplemented/
444−{-# INLINE getIndicesFromThenTo #-}
445−getIndicesFromThenTo :: Unfold m (Int, Int, Int, Array a) a
446−getIndicesFromThenTo = undefined
422+{-# INLINE indexReaderFromThenTo #-}
423+indexReaderFromThenTo :: Unfold m (Int, Int, Int, Array a) a
424+indexReaderFromThenTo = undefined
447425
448426-------------------------------------------------------------------------------
449427-- Transform via stream operations
@@ -460,7 +438,7 @@ getIndicesFromThenTo = undefined
460438{-# INLINE runPipe #-}
461439runPipe :: (MonadIO m, Unbox a, Unbox b)
462440 => Pipe m a b -> Array a -> m (Array b)
463−runPipe f arr = P.runPipe (toArrayMinChunk (length arr)) $ f (A.read arr)
441+runPipe f arr = P.runPipe (toArrayMinChunk (length arr)) $ f (read arr)
464442-}
465443
466444-- XXX For transformations that cannot change the number of elements e.g. "map"
@@ -474,7 +452,7 @@ runPipe f arr = P.runPipe (toArrayMinChunk (length arr)) $ f (A.read arr)
474452streamTransform :: forall m a b. (MonadIO m, Unbox a, Unbox b)
475453 => (Stream m a -> Stream m b) -> Array a -> m (Array b)
476454streamTransform f arr =
477− Stream.fold (A.writeWith (length arr)) $ f (A.read arr)
455+ Stream.fold (createWith (length arr)) $ f (read arr)
478456
479457-------------------------------------------------------------------------------
480458-- Casts
@@ -508,7 +486,7 @@ asBytes = castUnsafe
508486--
509487cast :: forall a b. (Unbox b) => Array a -> Maybe (Array b)
510488cast arr =
511− let len = A.byteLength arr
489+ let len = byteLength arr
512490 r = len `mod` SIZE_OF(b)
513491 in if r /= 0
514492 then Nothing
@@ -526,9 +504,9 @@ cast arr =
526504--
527505asCStringUnsafe :: Array a -> (CString -> IO b) -> IO b
528506asCStringUnsafe arr act = do
529− let arr1 = asBytes arr <> A.fromList [0]
530− -- asPtrUnsafe makes sure the array is pinned
531− asPtrUnsafe arr1 $ \ptr -> act (castPtr ptr)
507+ let arr1 = asBytes arr <> fromList [0]
508+ -- unsafePinnedAsPtr makes sure the array is pinned
509+ unsafePinnedAsPtr arr1 $ \ptr -> act (castPtr ptr)
532510
533511-------------------------------------------------------------------------------
534512-- Folds
@@ -541,14 +519,14 @@ asCStringUnsafe arr act = do
541519-- /Pre-release/
542520{-# INLINE fold #-}
543521fold :: forall m a b. (Monad m, Unbox a) => Fold m a b -> Array a -> m b
544−fold f arr = Stream.fold f (A.read arr)
522+fold f arr = Stream.fold f (read arr)
545523
546524-- | Fold an array using a stream fold operation.
547525--
548526-- /Pre-release/
549527{-# INLINE streamFold #-}
550528streamFold :: (Monad m, Unbox a) => (Stream m a -> m b) -> Array a -> m b
551−streamFold f arr = f (A.read arr)
529+streamFold f arr = f (read arr)
552530
553531--------------------------------------------------------------------------------
554532-- Serialization
@@ -593,3 +571,448 @@ deserialize arr@(Array {..}) = unsafeInlineIO $ do
593571 Serialize.deserializeAt arrStart arrContents (arrStart + lenArr)
594572 assertM(off == arrStart + lenArr)
595573 pure val
574+
575+-------------------------------------------------------------------------------
576+-- Streams of Arrays
577+-------------------------------------------------------------------------------
578+
579+-- TODO: efficiently compare two streams of arrays. Two streams can have chunks
580+-- of different sizes, we can handle that in the stream comparison abstraction.
581+-- This could be useful e.g. to fast compare whether two files differ.
582+
583+-- | Insert the given element between arrays and flatten.
584+--
585+-- >>> interpose x = Stream.interpose x Array.reader
586+--
587+{-# INLINE interpose #-}
588+interpose :: (Monad m, Unbox a) => a -> Stream m (Array a) -> Stream m a
589+interpose x = D.interpose x reader
590+
591+data FlattenState s =
592+ OuterLoop s
593+ | InnerLoop s !MutByteArray !Int !Int
594+
595+-- | Insert the given element after each array and flatten. This is similar to
596+-- unlines.
597+--
598+-- >>> interposeSuffix x = Stream.interposeSuffix x Array.reader
599+--
600+{-# INLINE_NORMAL interposeSuffix #-}
601+interposeSuffix :: forall m a. (Monad m, Unbox a)
602+ => a -> Stream m (Array a) -> Stream m a
603+-- This does not require MonadIO constraint.
604+-- interposeSuffix x = D.interposeSuffix x reader
605+interposeSuffix sep (D.Stream step state) = D.Stream step' (OuterLoop state)
606+
607+ where
608+
609+ {-# INLINE_LATE step' #-}
610+ step' gst (OuterLoop st) = do
611+ r <- step (adaptState gst) st
612+ return $ case r of
613+ D.Yield Array{..} s ->
614+ D.Skip (InnerLoop s arrContents arrStart arrEnd)
615+ D.Skip s -> D.Skip (OuterLoop s)
616+ D.Stop -> D.Stop
617+
618+ step' _ (InnerLoop st _ p end) | p == end =
619+ return $ D.Yield sep $ OuterLoop st
620+
621+ step' _ (InnerLoop st contents p end) = do
622+ x <- pure $ unsafeInlineIO $ peekAt p contents
623+ return $ D.Yield x (InnerLoop st contents (INDEX_NEXT(p,a)) end)
624+
625+-- | Insert the given array after each array and flatten.
626+--
627+-- >>> intercalateSuffix = Stream.intercalateSuffix Array.reader
628+--
629+{-# INLINE intercalateSuffix #-}
630+intercalateSuffix :: (Monad m, Unbox a)
631+ => Array a -> Stream m (Array a) -> Stream m a
632+intercalateSuffix = D.intercalateSuffix reader
633+
634+-- | @compactLE n@ coalesces adjacent arrays in the input stream
635+-- only if the combined size would be less than or equal to n.
636+--
637+-- Generates unpinned arrays irrespective of the pinning status of input
638+-- arrays.
639+{-# INLINE_NORMAL compactLE #-}
640+compactLE :: (MonadIO m, Unbox a)
641+ => Int -> Stream m (Array a) -> Stream m (Array a)
642+compactLE n stream =
643+ D.map unsafeFreeze $ MA.compactLE n $ D.map unsafeThaw stream
644+
645+-- | Pinned version of 'compactLE'.
646+{-# INLINE_NORMAL pinnedCompactLE #-}
647+pinnedCompactLE :: (MonadIO m, Unbox a)
648+ => Int -> Stream m (Array a) -> Stream m (Array a)
649+pinnedCompactLE n stream =
650+ D.map unsafeFreeze $ MA.pinnedCompactLE n $ D.map unsafeThaw stream
651+
652+-- | Split a stream of arrays on a given separator byte, dropping the separator
653+-- and coalescing all the arrays between two separators into a single array.
654+--
655+{-# INLINE compactOnByte #-}
656+compactOnByte
657+ :: (MonadIO m)
658+ => Word8
659+ -> Stream m (Array Word8)
660+ -> Stream m (Array Word8)
661+compactOnByte byte =
662+ fmap unsafeFreeze . MA.compactOnByte byte . fmap unsafeThaw
663+
664+-- | Like 'compactOnByte' considers the separator in suffix position instead of
665+-- infix position.
666+{-# INLINE compactOnByteSuffix #-}
667+compactOnByteSuffix
668+ :: (MonadIO m)
669+ => Word8
670+ -> Stream m (Array Word8)
671+ -> Stream m (Array Word8)
672+compactOnByteSuffix byte =
673+ fmap unsafeFreeze . MA.compactOnByteSuffix byte . fmap unsafeThaw
674+
675+-------------------------------------------------------------------------------
676+-- Folding Streams of Arrays
677+-------------------------------------------------------------------------------
678+
679+-- XXX This should not be used for breaking a stream as the D.cons used in
680+-- reconstructing the stream could be very bad for performance. This can only
681+-- be useful in folding without breaking.
682+{-# INLINE_NORMAL foldBreakChunks #-}
683+foldBreakChunks :: forall m a b. (MonadIO m, Unbox a) =>
684+ Fold m a b -> Stream m (Array a) -> m (b, Stream m (Array a))
685+foldBreakChunks (Fold fstep initial _ final) stream@(Stream step state) = do
686+ res <- initial
687+ case res of
688+ Fold.Partial fs -> go SPEC state fs
689+ Fold.Done fb -> return $! (fb, stream)
690+
691+ where
692+
693+ {-# INLINE go #-}
694+ go !_ st !fs = do
695+ r <- step defState st
696+ case r of
697+ Stream.Yield (Array contents start end) s ->
698+ let fp = Tuple' end contents
699+ in goArray SPEC s fp start fs
700+ Stream.Skip s -> go SPEC s fs
701+ Stream.Stop -> do
702+ b <- final fs
703+ return (b, D.nil)
704+
705+ goArray !_ s (Tuple' end _) !cur !fs
706+ | cur == end = do
707+ go SPEC s fs
708+ goArray !_ st fp@(Tuple' end contents) !cur !fs = do
709+ x <- liftIO $ peekAt cur contents
710+ res <- fstep fs x
711+ let next = INDEX_NEXT(cur,a)
712+ case res of
713+ Fold.Done b -> do
714+ let arr = Array contents next end
715+ return $! (b, D.cons arr (D.Stream step st))
716+ Fold.Partial fs1 -> goArray SPEC st fp next fs1
717+
718+-- This may be more robust wrt fusion compared to unfoldMany?
719+
720+-- | Fold a stream of arrays using a 'Fold'. This is equivalent to the
721+-- following:
722+--
723+-- >>> foldChunks f = Stream.fold f . Stream.unfoldMany Array.reader
724+--
725+foldChunks :: (MonadIO m, Unbox a) => Fold m a b -> Stream m (Array a) -> m b
726+foldChunks f s = fmap fst (foldBreakChunks f s)
727+-- foldStream f = Stream.fold f . Stream.unfoldMany reader
728+
729+-- | Fold a stream of arrays using a 'Fold' and return the remaining stream.
730+--
731+-- The following alternative to this function allows composing the fold using
732+-- the parser Monad:
733+--
734+-- @
735+-- foldBreakStreamK f s =
736+-- fmap (first (fromRight undefined))
737+-- $ StreamK.parseBreakChunks (ParserK.adaptC (Parser.fromFold f)) s
738+-- @
739+--
740+-- We can compare perf and remove this one or define it in terms of that.
741+--
742+foldBreakChunksK :: forall m a b. (MonadIO m, Unbox a) =>
743+ Fold m a b -> StreamK m (Array a) -> m (b, StreamK m (Array a))
744+{-
745+foldBreakChunksK f s =
746+ fmap (first (fromRight undefined))
747+ $ StreamK.parseBreakChunks (ParserK.adaptC (Parser.fromFold f)) s
748+-}
749+foldBreakChunksK (Fold fstep initial _ final) stream = do
750+ res <- initial
751+ case res of
752+ Fold.Partial fs -> go fs stream
753+ Fold.Done fb -> return (fb, stream)
754+
755+ where
756+
757+ {-# INLINE go #-}
758+ go !fs st = do
759+ let stop = (, StreamK.nil) <$> final fs
760+ single a = yieldk a StreamK.nil
761+ yieldk (Array contents start end) r =
762+ let fp = Tuple' end contents
763+ in goArray fs r fp start
764+ in StreamK.foldStream defState yieldk single stop st
765+
766+ goArray !fs st (Tuple' end _) !cur
767+ | cur == end = do
768+ go fs st
769+ goArray !fs st fp@(Tuple' end contents) !cur = do
770+ x <- liftIO $ peekAt cur contents
771+ res <- fstep fs x
772+ let next = INDEX_NEXT(cur,a)
773+ case res of
774+ Fold.Done b -> do
775+ let arr = Array contents next end
776+ return $! (b, StreamK.cons arr st)
777+ Fold.Partial fs1 -> goArray fs1 st fp next
778+
779+{-
780+-- This can be generalized to any type provided it can be unfolded to a stream
781+-- and it can be combined using a semigroup operation.
782+--
783+{-# INLINE_NORMAL parseBreakD #-}
784+parseBreakD ::
785+ forall m a b. (MonadIO m, MonadThrow m, Unbox a)
786+ => PRD.Parser a m b
787+ -> D.Stream m (Array.Array a)
788+ -> m (b, D.Stream m (Array.Array a))
789+parseBreakD
790+ (PRD.Parser pstep initial extract) stream@(D.Stream step state) = do
791+
792+ res <- initial
793+ case res of
794+ PRD.IPartial s -> go SPEC state (List []) s
795+ PRD.IDone b -> return (b, stream)
796+ PRD.IError err -> throwM $ ParseError err
797+
798+ where
799+
800+ -- "backBuf" contains last few items in the stream that we may have to
801+ -- backtrack to.
802+ --
803+ -- XXX currently we are using a dumb list based approach for backtracking
804+ -- buffer. This can be replaced by a sliding/ring buffer using Data.Array.
805+ -- That will allow us more efficient random back and forth movement.
806+ go !_ st backBuf !pst = do
807+ r <- step defState st
808+ case r of
809+ D.Yield (Array contents start end) s ->
810+ gobuf SPEC s backBuf
811+ (Tuple' end contents) start pst
812+ D.Skip s -> go SPEC s backBuf pst
813+ D.Stop -> do
814+ b <- extract pst
815+ return (b, D.nil)
816+
817+ -- Use strictness on "cur" to keep it unboxed
818+ gobuf !_ s backBuf (Tuple' end _) !cur !pst
819+ | cur == end = do
820+ go SPEC s backBuf pst
821+ gobuf !_ s backBuf fp@(Tuple' end contents) !cur !pst = do
822+ x <- liftIO $ peekByteIndex contents cur
823+ pRes <- pstep pst x
824+ let next = INDEX_NEXT(cur,a)
825+ case pRes of
826+ PR.Partial 0 pst1 ->
827+ gobuf SPEC s (List []) fp next pst1
828+ PR.Partial n pst1 -> do
829+ assert (n <= Prelude.length (x:getList backBuf)) (return ())
830+ let src0 = Prelude.take n (x:getList backBuf)
831+ arr0 = A.fromListN n (Prelude.reverse src0)
832+ arr1 = Array contents next end
833+ src = arr0 <> arr1
834+ let !(Array cont1 start end1) = src
835+ fp1 = Tuple' end1 cont1
836+ gobuf SPEC s (List []) fp1 start pst1
837+ PR.Continue 0 pst1 ->
838+ gobuf SPEC s (List (x:getList backBuf)) fp next pst1
839+ PR.Continue n pst1 -> do
840+ assert (n <= Prelude.length (x:getList backBuf)) (return ())
841+ let (src0, buf1) = splitAt n (x:getList backBuf)
842+ arr0 = A.fromListN n (Prelude.reverse src0)
843+ arr1 = Array contents next end
844+ src = arr0 <> arr1
845+ let !(Array cont1 start end1) = src
846+ fp1 = Tuple' end1 cont1
847+ gobuf SPEC s (List buf1) fp1 start pst1
848+ PR.Done 0 b -> do
849+ let arr = Array contents next end
850+ return (b, D.cons arr (D.Stream step s))
851+ PR.Done n b -> do
852+ assert (n <= Prelude.length (x:getList backBuf)) (return ())
853+ let src0 = Prelude.take n (x:getList backBuf)
854+ -- XXX create the array in reverse instead
855+ arr0 = A.fromListN n (Prelude.reverse src0)
856+ arr1 = Array contents next end
857+ -- XXX Use StreamK to avoid adding arbitrary layers of
858+ -- constructors every time.
859+ str = D.cons arr0 (D.cons arr1 (D.Stream step s))
860+ return (b, str)
861+ PR.Error err -> throwM $ ParseError err
862+-}
863+
864+-- | Parse an array stream using the supplied 'Parser'. Returns the parse
865+-- result and the unconsumed stream. Throws 'ParseError' if the parse fails.
866+--
867+-- The following alternative to this function allows composing the parser using
868+-- the parser Monad:
869+--
870+-- >>> parseBreakStreamK p = StreamK.parseBreakChunks (ParserK.adaptC p)
871+--
872+-- We can compare perf and remove this one or define it in terms of that.
873+--
874+-- /Internal/
875+--
876+{-# INLINE_NORMAL parseBreakChunksK #-}
877+parseBreakChunksK ::
878+ forall m a b. (MonadIO m, Unbox a)
879+ => Parser a m b
880+ -> StreamK m (Array a)
881+ -> m (Either ParseError b, StreamK m (Array a))
882+-- parseBreakStreamK p = StreamK.parseBreakChunks (ParserK.adaptC p)
883+parseBreakChunksK (Parser pstep initial extract) stream = do
884+ res <- initial
885+ case res of
886+ IPartial s -> go s stream []
887+ IDone b -> return (Right b, stream)
888+ IError err -> return (Left (ParseError err), stream)
889+
890+ where
891+
892+ -- "backBuf" contains last few items in the stream that we may have to
893+ -- backtrack to.
894+ --
895+ -- XXX currently we are using a dumb list based approach for backtracking
896+ -- buffer. This can be replaced by a sliding/ring buffer using Data.Array.
897+ -- That will allow us more efficient random back and forth movement.
898+ go !pst st backBuf = do
899+ let stop = goStop pst backBuf -- (, K.nil) <$> extract pst
900+ single a = yieldk a StreamK.nil
901+ yieldk arr r = goArray pst backBuf r arr
902+ in StreamK.foldStream defState yieldk single stop st
903+
904+ -- Use strictness on "cur" to keep it unboxed
905+ goArray !pst backBuf st (Array _ cur end) | cur == end = go pst st backBuf
906+ goArray !pst backBuf st (Array contents cur end) = do
907+ x <- liftIO $ peekAt cur contents
908+ pRes <- pstep pst x
909+ let next = INDEX_NEXT(cur,a)
910+ case pRes of
911+ Parser.Partial 0 s ->
912+ goArray s [] st (Array contents next end)
913+ Parser.Partial n s -> do
914+ assert (n <= Prelude.length (x:backBuf)) (return ())
915+ let src0 = Prelude.take n (x:backBuf)
916+ arr0 = fromListN n (Prelude.reverse src0)
917+ arr1 = Array contents next end
918+ src = arr0 <> arr1
919+ goArray s [] st src
920+ Parser.Continue 0 s ->
921+ goArray s (x:backBuf) st (Array contents next end)
922+ Parser.Continue n s -> do
923+ assert (n <= Prelude.length (x:backBuf)) (return ())
924+ let (src0, buf1) = Prelude.splitAt n (x:backBuf)
925+ arr0 = fromListN n (Prelude.reverse src0)
926+ arr1 = Array contents next end
927+ src = arr0 <> arr1
928+ goArray s buf1 st src
929+ Parser.Done 0 b -> do
930+ let arr = Array contents next end
931+ return (Right b, StreamK.cons arr st)
932+ Parser.Done n b -> do
933+ assert (n <= Prelude.length (x:backBuf)) (return ())
934+ let src0 = Prelude.take n (x:backBuf)
935+ -- XXX Use fromListRevN once implemented
936+ -- arr0 = A.fromListRevN n src0
937+ arr0 = fromListN n (Prelude.reverse src0)
938+ arr1 = Array contents next end
939+ str = StreamK.cons arr0 (StreamK.cons arr1 st)
940+ return (Right b, str)
941+ Parser.Error err -> do
942+ let n = Prelude.length backBuf
943+ arr0 = fromListN n (Prelude.reverse backBuf)
944+ arr1 = Array contents cur end
945+ str = StreamK.cons arr0 (StreamK.cons arr1 stream)
946+ return (Left (ParseError err), str)
947+
948+ -- This is a simplified goArray
949+ goExtract !pst backBuf (Array _ cur end)
950+ | cur == end = goStop pst backBuf
951+ goExtract !pst backBuf (Array contents cur end) = do
952+ x <- liftIO $ peekAt cur contents
953+ pRes <- pstep pst x
954+ let next = INDEX_NEXT(cur,a)
955+ case pRes of
956+ Parser.Partial 0 s ->
957+ goExtract s [] (Array contents next end)
958+ Parser.Partial n s -> do
959+ assert (n <= Prelude.length (x:backBuf)) (return ())
960+ let src0 = Prelude.take n (x:backBuf)
961+ arr0 = fromListN n (Prelude.reverse src0)
962+ arr1 = Array contents next end
963+ src = arr0 <> arr1
964+ goExtract s [] src
965+ Parser.Continue 0 s ->
966+ goExtract s backBuf (Array contents next end)
967+ Parser.Continue n s -> do
968+ assert (n <= Prelude.length (x:backBuf)) (return ())
969+ let (src0, buf1) = Prelude.splitAt n (x:backBuf)
970+ arr0 = fromListN n (Prelude.reverse src0)
971+ arr1 = Array contents next end
972+ src = arr0 <> arr1
973+ goExtract s buf1 src
974+ Parser.Done 0 b -> do
975+ let arr = Array contents next end
976+ return (Right b, StreamK.fromPure arr)
977+ Parser.Done n b -> do
978+ assert (n <= Prelude.length backBuf) (return ())
979+ let src0 = Prelude.take n backBuf
980+ -- XXX Use fromListRevN once implemented
981+ -- arr0 = A.fromListRevN n src0
982+ arr0 = fromListN n (Prelude.reverse src0)
983+ arr1 = Array contents next end
984+ str = StreamK.cons arr0 (StreamK.fromPure arr1)
985+ return (Right b, str)
986+ Parser.Error err -> do
987+ let n = Prelude.length backBuf
988+ arr0 = fromListN n (Prelude.reverse backBuf)
989+ arr1 = Array contents cur end
990+ str = StreamK.cons arr0 (StreamK.cons arr1 stream)
991+ return (Left (ParseError err), str)
992+
993+ -- This is a simplified goExtract
994+ {-# INLINE goStop #-}
995+ goStop !pst backBuf = do
996+ pRes <- extract pst
997+ case pRes of
998+ Parser.Partial _ _ -> error "Bug: parseBreak: Partial in extract"
999+ Parser.Continue 0 s ->
1000+ goStop s backBuf
1001+ Parser.Continue n s -> do
1002+ assert (n <= Prelude.length backBuf) (return ())
1003+ let (src0, buf1) = Prelude.splitAt n backBuf
1004+ arr = fromListN n (Prelude.reverse src0)
1005+ goExtract s buf1 arr
1006+ Parser.Done 0 b ->
1007+ return (Right b, StreamK.nil)
1008+ Parser.Done n b -> do
1009+ assert (n <= Prelude.length backBuf) (return ())
1010+ let src0 = Prelude.take n backBuf
1011+ -- XXX Use fromListRevN once implemented
1012+ -- arr0 = A.fromListRevN n src0
1013+ arr0 = fromListN n (Prelude.reverse src0)
1014+ return (Right b, StreamK.fromPure arr0)
1015+ Parser.Error err -> do
1016+ let n = Prelude.length backBuf
1017+ arr0 = fromListN n (Prelude.reverse backBuf)
1018+ return (Left (ParseError err), StreamK.fromPure arr0)
core/src/Streamly/Internal/Data/Array/Generic.hs+22−6
@@ -11,8 +11,8 @@ module Streamly.Internal.Data.Array.Generic
1111
1212 -- * Construction
1313 , nil
14− , writeN
15− , write
14+ , createOf
15+ , create
1616 , writeWith
1717 , writeLastN
1818
@@ -44,6 +44,10 @@ module Streamly.Internal.Data.Array.Generic
4444 , getIndex
4545 , getSliceUnsafe
4646 , strip
47+
48+ -- * Deprecated
49+ , writeN
50+ , write
4751 )
4852where
4953
@@ -104,21 +108,33 @@ nil = unsafePerformIO $ unsafeFreeze <$> MArray.nil
104108-- Construction - Folds
105109-------------------------------------------------------------------------------
106110
107−{-# INLINE_NORMAL writeN #-}
111+{-# INLINE_NORMAL createOf #-}
112+createOf :: MonadIO m => Int -> Fold m a (Array a)
113+createOf = fmap unsafeFreeze <$> MArray.createOf
114+
115+-- XXX Deprecate in major
116+-- {-# DEPRECATED writeN "Please use createOf instead." #-}
117+{-# INLINE writeN #-}
108118writeN :: MonadIO m => Int -> Fold m a (Array a)
109−writeN = fmap unsafeFreeze <$> MArray.writeN
119+writeN = createOf
110120
111121{-# INLINE_NORMAL writeWith #-}
112122writeWith :: MonadIO m => Int -> Fold m a (Array a)
113−writeWith elemCount = unsafeFreeze <$> MArray.writeWith elemCount
123+writeWith elemCount = unsafeFreeze <$> MArray.createWith elemCount
114124
115125-- | Fold the whole input to a single array.
116126--
117127-- /Caution! Do not use this on infinite streams./
118128--
129+{-# INLINE create #-}
130+create :: MonadIO m => Fold m a (Array a)
131+create = fmap unsafeFreeze MArray.create
132+
133+-- XXX Deprecate in major
134+-- {-# DEPRECATED write "Please use create instead." #-}
119135{-# INLINE write #-}
120136write :: MonadIO m => Fold m a (Array a)
121−write = fmap unsafeFreeze MArray.write
137+write = create
122138
123139fromPureStream :: Stream Identity a -> Array a
124140fromPureStream x =
core/src/Streamly/Internal/Data/Array/Stream.hs+57−564
@@ -12,6 +12,7 @@
1212-- keep this as a separate module and release it.
1313--
1414module Streamly.Internal.Data.Array.Stream
15+{-# DEPRECATED "Please use \"Streamly.Internal.Data.Array\" instead." #-}
1516 (
1617 -- * Creation
1718 Array.chunksOf
@@ -19,13 +20,13 @@ module Streamly.Internal.Data.Array.Stream
1920 , Array.bufferChunks
2021
2122 -- * Flattening to elements
22− , concat
23+ , Array.concat
2324 , Array.flattenArrays
24− , concatRev
25+ , Array.concatRev
2526 , Array.flattenArraysRev
26− , interpose
27− , interposeSuffix
28− , intercalateSuffix
27+ , Array.interpose
28+ , Array.interposeSuffix
29+ , Array.intercalateSuffix
2930 , unlines
3031
3132 -- * Elimination
@@ -36,10 +37,11 @@ module Streamly.Internal.Data.Array.Stream
3637 -- could also be implemented using parseBreak.
3738 , foldBreak
3839 , foldBreakD
39− -- The byte level parseBreak cannot work efficiently. Because the stream
40− -- will have to be a StreamK for backtracking, StreamK at byte level would
41− -- not be efficient.
42− , parseBreak -- StreamK.parseBreakChunks
40+ -- This is chunked parseBreak. A byte level parseBreak cannot work
41+ -- efficiently. Because the stream will have to be a StreamK for
42+ -- backtracking, StreamK at byte level would not be efficient.
43+ -- parseBreak p = K.parseBreakChunks (ParserK.adaptC p)
44+ , parseBreak
4345 -- , parseBreakD
4446 -- , foldManyChunks
4547 -- , parseManyChunks
@@ -77,6 +79,8 @@ where
7779import Data.Bifunctor (second)
7880import Control.Exception (assert)
7981import Control.Monad.IO.Class (MonadIO(..))
82+-- import Data.Bifunctor (first)
83+-- import Data.Either (fromRight)
8084import Data.Proxy (Proxy(..))
8185import Data.Word (Word8)
8286import Streamly.Internal.Data.Unbox (Unbox(..))
@@ -92,127 +96,32 @@ import Streamly.Internal.Data.Parser (ParseError(..))
9296import Streamly.Internal.Data.Stream (Stream)
9397import Streamly.Internal.Data.StreamK (StreamK, fromStream, toStream)
9498import Streamly.Internal.Data.SVar.Type (adaptState, defState)
95−import Streamly.Internal.Data.Tuple.Strict (Tuple'(..))
9699
97−import qualified Streamly.Data.Fold as FL
98100import qualified Streamly.Internal.Data.Array as A
99101import qualified Streamly.Internal.Data.Array as Array
100−import qualified Streamly.Internal.Data.MutArray.Type as MA
101−import qualified Streamly.Internal.Data.MutArray.Stream as AS
102−import qualified Streamly.Internal.Data.Fold.Type as FL (Fold(..), Step(..))
103102import qualified Streamly.Internal.Data.Parser as PR
104103import qualified Streamly.Internal.Data.Parser as PRD
105104 (Parser(..), Initial(..))
105+-- import qualified Streamly.Internal.Data.ParserK as ParserK
106106import qualified Streamly.Internal.Data.Stream as D
107107import qualified Streamly.Internal.Data.StreamK as K
108108
109109-- XXX Since these are immutable arrays MonadIO constraint can be removed from
110110-- most places.
111111
112−-------------------------------------------------------------------------------
113−-- Append
114−-------------------------------------------------------------------------------
115−
116−-- XXX efficiently compare two streams of arrays. Two streams can have chunks
117−-- of different sizes, we can handle that in the stream comparison abstraction.
118−-- This could be useful e.g. to fast compare whether two files differ.
119−
120−-- | Convert a stream of arrays into a stream of their elements.
121−--
122−-- Same as the following:
123−--
124−-- > concat = Stream.unfoldMany Array.read
125−--
126−-- @since 0.7.0
127−{-# INLINE concat #-}
128−concat :: (Monad m, Unbox a) => Stream m (Array a) -> Stream m a
129−-- concat m = fromStreamD $ A.flattenArrays (toStreamD m)
130−-- concat m = fromStreamD $ D.concatMap A.toStreamD (toStreamD m)
131−concat = D.unfoldMany A.reader
132−
133−-- | Convert a stream of arrays into a stream of their elements reversing the
134−-- contents of each array before flattening.
135−--
136−-- > concatRev = Stream.unfoldMany Array.readerRev
137−--
138−-- @since 0.7.0
139−{-# INLINE concatRev #-}
140−concatRev :: (Monad m, Unbox a) => Stream m (Array a) -> Stream m a
141−-- concatRev m = fromStreamD $ A.flattenArraysRev (toStreamD m)
142−concatRev = D.unfoldMany A.readerRev
143−
144112-------------------------------------------------------------------------------
145113-- Intersperse and append
146114-------------------------------------------------------------------------------
147115
148−-- | Flatten a stream of arrays after inserting the given element between
149−-- arrays.
150−--
151−-- /Pre-release/
152−{-# INLINE interpose #-}
153−interpose :: (Monad m, Unbox a) => a -> Stream m (Array a) -> Stream m a
154−interpose x = D.interpose x A.reader
155−
156−{-# INLINE intercalateSuffix #-}
157−intercalateSuffix :: (Monad m, Unbox a)
158− => Array a -> Stream m (Array a) -> Stream m a
159−intercalateSuffix = D.intercalateSuffix A.reader
160−
161−-- | Flatten a stream of arrays appending the given element after each
162−-- array.
163−--
164−-- @since 0.7.0
165−{-# INLINE interposeSuffix #-}
166−interposeSuffix :: (Monad m, Unbox a)
167− => a -> Stream m (Array a) -> Stream m a
168−-- interposeSuffix x = fromStreamD . A.unlines x . toStreamD
169−interposeSuffix x = D.interposeSuffix x A.reader
170−
171−data FlattenState s =
172− OuterLoop s
173− | InnerLoop s !MA.MutableByteArray !Int !Int
174−
175−-- XXX This is a special case of interposeSuffix, can be removed.
176−-- XXX Remove monadIO constraint
177116{-# INLINE_NORMAL unlines #-}
178117unlines :: forall m a. (MonadIO m, Unbox a)
179118 => a -> D.Stream m (Array a) -> D.Stream m a
180−unlines sep (D.Stream step state) = D.Stream step' (OuterLoop state)
181− where
182− {-# INLINE_LATE step' #-}
183− step' gst (OuterLoop st) = do
184− r <- step (adaptState gst) st
185− return $ case r of
186− D.Yield Array{..} s ->
187− D.Skip (InnerLoop s arrContents arrStart arrEnd)
188− D.Skip s -> D.Skip (OuterLoop s)
189− D.Stop -> D.Stop
190−
191− step' _ (InnerLoop st _ p end) | p == end =
192− return $ D.Yield sep $ OuterLoop st
193−
194− step' _ (InnerLoop st contents p end) = do
195− x <- liftIO $ peekByteIndex p contents
196− return $ D.Yield x (InnerLoop st contents (INDEX_NEXT(p,a)) end)
119+unlines = Array.interposeSuffix
197120
198121-------------------------------------------------------------------------------
199122-- Compact
200123-------------------------------------------------------------------------------
201124
202−-- XXX These would not be needed once we implement compactLEFold, see
203−-- module Streamly.Internal.Data.Stream.MutChunked
204−--
205−-- XXX Note that this thaws immutable arrays for appending, that may be
206−-- problematic if multiple users do the same thing, however, immutable arrays
207−-- would usually have no capacity to append, therefore, a copy will be forced
208−-- anyway. Confirm this. We can forcefully trim the array capacity before thaw
209−-- to ensure this.
210−{-# INLINE_NORMAL packArraysChunksOf #-}
211−packArraysChunksOf :: (MonadIO m, Unbox a)
212− => Int -> D.Stream m (Array a) -> D.Stream m (Array a)
213−packArraysChunksOf n str =
214− D.map A.unsafeFreeze $ AS.packArraysChunksOf n $ D.map A.unsafeThaw str
215−
216125-- XXX instead of writing two different versions of this operation, we should
217126-- write it as a pipe.
218127--
@@ -220,8 +129,7 @@ packArraysChunksOf n str =
220129{-# INLINE_NORMAL lpackArraysChunksOf #-}
221130lpackArraysChunksOf :: (MonadIO m, Unbox a)
222131 => Int -> Fold m (Array a) () -> Fold m (Array a) ()
223−lpackArraysChunksOf n fld =
224− FL.lmap A.unsafeThaw $ AS.lpackArraysChunksOf n (FL.lmap A.unsafeFreeze fld)
132+lpackArraysChunksOf = Array.lCompactGE
225133
226134-- | Coalesce adjacent arrays in incoming stream to form bigger arrays of a
227135-- maximum specified size in bytes.
@@ -230,68 +138,19 @@ lpackArraysChunksOf n fld =
230138{-# INLINE compact #-}
231139compact :: (MonadIO m, Unbox a)
232140 => Int -> Stream m (Array a) -> Stream m (Array a)
233−compact = packArraysChunksOf
141+compact = Array.compactLE
234142
235−-------------------------------------------------------------------------------
236−-- Split
237−-------------------------------------------------------------------------------
238−
239−data SplitState s arr
240− = Initial s
241− | Buffering s arr
242− | Splitting s arr
243− | Yielding arr (SplitState s arr)
244− | Finishing
245−
246−-- | Split a stream of arrays on a given separator byte, dropping the separator
247−-- and coalescing all the arrays between two separators into a single array.
143+-- | Given a stream of arrays, splice them all together to generate a single
144+-- array. The stream must be /finite/.
248145--
249146-- @since 0.7.0
250−{-# INLINE_NORMAL _splitOn #-}
251−_splitOn
252− :: MonadIO m
253− => Word8
254− -> D.Stream m (Array Word8)
255− -> D.Stream m (Array Word8)
256−_splitOn byte (D.Stream step state) = D.Stream step' (Initial state)
257−
258− where
259−
260− {-# INLINE_LATE step' #-}
261− step' gst (Initial st) = do
262− r <- step gst st
263− case r of
264− D.Yield arr s -> do
265− (arr1, marr2) <- A.breakOn byte arr
266− return $ case marr2 of
267− Nothing -> D.Skip (Buffering s arr1)
268− Just arr2 -> D.Skip (Yielding arr1 (Splitting s arr2))
269− D.Skip s -> return $ D.Skip (Initial s)
270− D.Stop -> return D.Stop
271−
272− step' gst (Buffering st buf) = do
273− r <- step gst st
274− case r of
275− D.Yield arr s -> do
276− (arr1, marr2) <- A.breakOn byte arr
277− buf' <- A.splice buf arr1
278− return $ case marr2 of
279− Nothing -> D.Skip (Buffering s buf')
280− Just x -> D.Skip (Yielding buf' (Splitting s x))
281− D.Skip s -> return $ D.Skip (Buffering s buf)
282− D.Stop -> return $
283− if A.byteLength buf == 0
284− then D.Stop
285− else D.Skip (Yielding buf Finishing)
286−
287− step' _ (Splitting st buf) = do
288− (arr1, marr2) <- A.breakOn byte buf
289− return $ case marr2 of
290− Nothing -> D.Skip $ Buffering st arr1
291− Just arr2 -> D.Skip $ Yielding arr1 (Splitting st arr2)
147+{-# INLINE toArray #-}
148+toArray :: (MonadIO m, Unbox a) => Stream m (Array a) -> m (Array a)
149+toArray = Array.fromChunks
292150
293− step' _ (Yielding arr next) = return $ D.Yield arr next
294− step' _ Finishing = return D.Stop
151+-------------------------------------------------------------------------------
152+-- Split
153+-------------------------------------------------------------------------------
295154
296155-- XXX Remove MonadIO constraint.
297156-- | Split a stream of arrays on a given separator byte, dropping the separator
@@ -304,7 +163,7 @@ splitOn
304163 => Word8
305164 -> Stream m (Array Word8)
306165 -> Stream m (Array Word8)
307−splitOn byte = D.splitInnerBy (A.breakOn byte) A.splice
166+splitOn = Array.compactOnByte
308167
309168{-# INLINE splitOnSuffix #-}
310169splitOnSuffix
@@ -312,8 +171,7 @@ splitOnSuffix
312171 => Word8
313172 -> Stream m (Array Word8)
314173 -> Stream m (Array Word8)
315−-- splitOn byte s = fromStreamD $ A.splitOn byte $ toStreamD s
316−splitOnSuffix byte = D.splitInnerBySuffix (A.breakOn byte) A.splice
174+splitOnSuffix = Array.compactOnByteSuffix
317175
318176-------------------------------------------------------------------------------
319177-- Elimination - Running folds
@@ -322,77 +180,24 @@ splitOnSuffix byte = D.splitInnerBySuffix (A.breakOn byte) A.splice
322180{-# INLINE_NORMAL foldBreakD #-}
323181foldBreakD :: forall m a b. (MonadIO m, Unbox a) =>
324182 Fold m a b -> D.Stream m (Array a) -> m (b, D.Stream m (Array a))
325−foldBreakD (FL.Fold fstep initial _ final) stream@(D.Stream step state) = do
326− res <- initial
327− case res of
328− FL.Partial fs -> go SPEC state fs
329− FL.Done fb -> return $! (fb, stream)
330−
331− where
332−
333− {-# INLINE go #-}
334− go !_ st !fs = do
335− r <- step defState st
336− case r of
337− D.Yield (Array contents start end) s ->
338− let fp = Tuple' end contents
339− in goArray SPEC s fp start fs
340− D.Skip s -> go SPEC s fs
341− D.Stop -> do
342− b <- final fs
343− return (b, D.nil)
344−
345− goArray !_ s (Tuple' end _) !cur !fs
346− | cur == end = do
347− go SPEC s fs
348− goArray !_ st fp@(Tuple' end contents) !cur !fs = do
349− x <- liftIO $ peekByteIndex cur contents
350− res <- fstep fs x
351− let next = INDEX_NEXT(cur,a)
352− case res of
353− FL.Done b -> do
354− let arr = Array contents next end
355− return $! (b, D.cons arr (D.Stream step st))
356− FL.Partial fs1 -> goArray SPEC st fp next fs1
357−
358−{-# INLINE_NORMAL foldBreakK #-}
359−foldBreakK :: forall m a b. (MonadIO m, Unbox a) =>
360− Fold m a b -> K.StreamK m (Array a) -> m (b, K.StreamK m (Array a))
361−foldBreakK (FL.Fold fstep initial _ final) stream = do
362− res <- initial
363− case res of
364− FL.Partial fs -> go fs stream
365− FL.Done fb -> return (fb, stream)
366−
367− where
368−
369− {-# INLINE go #-}
370− go !fs st = do
371− let stop = (, K.nil) <$> final fs
372− single a = yieldk a K.nil
373− yieldk (Array contents start end) r =
374− let fp = Tuple' end contents
375− in goArray fs r fp start
376− in K.foldStream defState yieldk single stop st
377−
378− goArray !fs st (Tuple' end _) !cur
379− | cur == end = do
380− go fs st
381− goArray !fs st fp@(Tuple' end contents) !cur = do
382− x <- liftIO $ peekByteIndex cur contents
383− res <- fstep fs x
384− let next = INDEX_NEXT(cur,a)
385− case res of
386− FL.Done b -> do
387− let arr = Array contents next end
388− return $! (b, K.cons arr st)
389− FL.Partial fs1 -> goArray fs1 st fp next
183+foldBreakD = Array.foldBreakChunks
390184
391185-- | Fold an array stream using the supplied 'Fold'. Returns the fold result
392186-- and the unconsumed stream.
393187--
394188-- > foldBreak f = runArrayFoldBreak (ChunkFold.fromFold f)
395189--
190+-- Instead of using this we can adapt the fold to ParserK and use
191+-- parseBreakChunks instead. ParserK allows composing using Monad as well.
192+--
193+-- @
194+-- foldBreak f s =
195+-- fmap (first (fromRight undefined))
196+-- $ K.parseBreakChunks (ParserK.adaptC (PR.fromFold f)) s
197+-- @
198+--
199+-- We can compare perf and remove this one or define it in terms of that.
200+--
396201-- /Internal/
397202--
398203{-# INLINE_NORMAL foldBreak #-}
@@ -401,14 +206,20 @@ foldBreak ::
401206 => Fold m a b
402207 -> StreamK m (A.Array a)
403208 -> m (b, StreamK m (A.Array a))
209+foldBreak = Array.foldBreakChunksK
210+--
404211-- foldBreak f s = fmap fromStreamD <$> foldBreakD f (toStreamD s)
405−foldBreak = foldBreakK
212+--
213+-- foldBreak f s =
214+-- fmap (first (fromRight undefined))
215+-- $ K.parseBreakChunks (ParserK.adaptC (PR.fromFold f)) s
216+--
406217-- If foldBreak performs better than runArrayFoldBreak we can use a rewrite
407218-- rule to rewrite runArrayFoldBreak to fold.
408219-- foldBreak f = runArrayFoldBreak (ChunkFold.fromFold f)
409220
410221-------------------------------------------------------------------------------
411−-- Fold to a single Array
222+-- Elimination - running element parsers
412223-------------------------------------------------------------------------------
413224
414225-- When we have to take an array partially, take the last part of the array.
@@ -454,341 +265,22 @@ splitAtArrayListRev n ls
454265 arr1 = Array contents end1 end
455266 in ([arr1], arr2:xs)
456267
457−-------------------------------------------------------------------------------
458−-- Fold to a single Array
459−-------------------------------------------------------------------------------
460−
461−{-
462−
463−-- XXX Both of these implementations of splicing seem to perform equally well.
464−-- We need to perform benchmarks over a range of sizes though.
465−
466−-- CAUTION! length must more than equal to lengths of all the arrays in the
467−-- stream.
468−{-# INLINE spliceArraysLenUnsafe #-}
469−spliceArraysLenUnsafe :: (MonadIO m, Unbox a)
470− => Int -> Stream m (MutArray a) -> m (MutArray a)
471−spliceArraysLenUnsafe len buffered = do
472− -- XXX The new array's pinned state should depend on the first element
473− -- of the stream. We should uncons the stream and do the required.
474− arr <- undefined
475− D.foldlM' MA.spliceUnsafe (return arr) buffered
476−
477−{-# INLINE _spliceArrays #-}
478−_spliceArrays :: (MonadIO m, Unbox a)
479− => Stream m (Array a) -> m (Array a)
480−_spliceArrays s = do
481− buffered <- D.foldr K.cons K.nil s
482− len <- K.fold FL.sum (fmap Array.length buffered)
483− -- XXX The new array's pinned state should depend on the first element
484− -- of the stream. We should uncons the stream and do the required.
485− arr <- undefined
486− final <- D.foldlM' writeArr (return arr) (toStream buffered)
487− return $ A.unsafeFreeze final
488−
489− where
490−
491− writeArr dst arr = MA.spliceUnsafe dst (A.unsafeThaw arr)
492−
493−{-# INLINE _spliceArraysBuffered #-}
494−_spliceArraysBuffered :: (MonadIO m, Unbox a)
495− => Stream m (Array a) -> m (Array a)
496−_spliceArraysBuffered s = do
497− buffered <- D.foldr K.cons K.nil s
498− len <- K.fold FL.sum (fmap Array.length buffered)
499− A.unsafeFreeze <$>
500− spliceArraysLenUnsafe len (fmap A.unsafeThaw (toStream buffered))
501−
502−-}
503−
504−{-# INLINE spliceArraysRealloced #-}
505−spliceArraysRealloced :: forall m a. (MonadIO m, Unbox a)
506− => Stream m (Array a) -> m (Array a)
507−spliceArraysRealloced s = do
508− res <- D.uncons s
509− case res of
510− Just (a, strm) -> do
511− arr <-
512− D.foldlM'
513− MA.spliceExp
514− (pure (A.unsafeThaw a))
515− (fmap A.unsafeThaw strm)
516− liftIO $ A.unsafeFreeze <$> MA.rightSize arr
517− Nothing -> pure A.nil
518−
519−-- XXX This should just be "fold A.write"
520−--
521−-- | Given a stream of arrays, splice them all together to generate a single
522−-- array. The stream must be /finite/.
523−--
524−-- @since 0.7.0
525−{-# INLINE toArray #-}
526−toArray :: (MonadIO m, Unbox a) => Stream m (Array a) -> m (Array a)
527−toArray = spliceArraysRealloced
528−-- spliceArrays = _spliceArraysBuffered
529−
530−-- exponentially increasing sizes of the chunks upto the max limit.
531−-- XXX this will be easier to implement with parsers/terminating folds
532−-- With this we should be able to reduce the number of chunks/allocations.
533−-- The reallocation/copy based toArray can also be implemented using this.
534−--
535−{-
536−{-# INLINE toArraysInRange #-}
537−toArraysInRange :: (MonadIO m, Unbox a)
538− => Int -> Int -> Fold m (Array a) b -> Fold m a b
539−toArraysInRange low high (Fold step initial extract) =
540−-}
541−
542−{-
543−-- | Fold the input to a pure buffered stream (List) of arrays.
544−{-# INLINE _toArraysOf #-}
545−_toArraysOf :: (MonadIO m, Unbox a)
546− => Int -> Fold m a (Stream Identity (Array a))
547−_toArraysOf n = FL.groupsOf n (A.writeNF n) FL.toStream
548−-}
549−
550−-------------------------------------------------------------------------------
551−-- Elimination - running element parsers
552−-------------------------------------------------------------------------------
553−
554268-- GHC parser does not accept {-# ANN type [] NoSpecConstr #-}, so we need
555269-- to make a newtype.
556270{-# ANN type List NoSpecConstr #-}
557271newtype List a = List {getList :: [a]}
558272
559−{-
560−-- This can be generalized to any type provided it can be unfolded to a stream
561−-- and it can be combined using a semigroup operation.
562−--
563−-- XXX This should be written using CPS (as parseK) if we want it to scale wrt
564−-- to the number of times it can be called on the same stream.
565−{-# INLINE_NORMAL parseBreakD #-}
566−parseBreakD ::
567− forall m a b. (MonadIO m, MonadThrow m, Unbox a)
568− => PRD.Parser a m b
569− -> D.Stream m (Array.Array a)
570− -> m (b, D.Stream m (Array.Array a))
571−parseBreakD
572− (PRD.Parser pstep initial extract) stream@(D.Stream step state) = do
573−
574− res <- initial
575− case res of
576− PRD.IPartial s -> go SPEC state (List []) s
577− PRD.IDone b -> return (b, stream)
578− PRD.IError err -> throwM $ ParseError err
579−
580− where
581−
582− -- "backBuf" contains last few items in the stream that we may have to
583− -- backtrack to.
584− --
585− -- XXX currently we are using a dumb list based approach for backtracking
586− -- buffer. This can be replaced by a sliding/ring buffer using Data.Array.
587− -- That will allow us more efficient random back and forth movement.
588− go !_ st backBuf !pst = do
589− r <- step defState st
590− case r of
591− D.Yield (Array contents start end) s ->
592− gobuf SPEC s backBuf
593− (Tuple' end contents) start pst
594− D.Skip s -> go SPEC s backBuf pst
595− D.Stop -> do
596− b <- extract pst
597− return (b, D.nil)
598−
599− -- Use strictness on "cur" to keep it unboxed
600− gobuf !_ s backBuf (Tuple' end _) !cur !pst
601− | cur == end = do
602− go SPEC s backBuf pst
603− gobuf !_ s backBuf fp@(Tuple' end contents) !cur !pst = do
604− x <- liftIO $ peekByteIndex contents cur
605− pRes <- pstep pst x
606− let next = INDEX_NEXT(cur,a)
607− case pRes of
608− PR.Partial 0 pst1 ->
609− gobuf SPEC s (List []) fp next pst1
610− PR.Partial n pst1 -> do
611− assert (n <= Prelude.length (x:getList backBuf)) (return ())
612− let src0 = Prelude.take n (x:getList backBuf)
613− arr0 = A.fromListN n (Prelude.reverse src0)
614− arr1 = Array contents next end
615− src = arr0 <> arr1
616− let !(Array cont1 start end1) = src
617− fp1 = Tuple' end1 cont1
618− gobuf SPEC s (List []) fp1 start pst1
619− PR.Continue 0 pst1 ->
620− gobuf SPEC s (List (x:getList backBuf)) fp next pst1
621− PR.Continue n pst1 -> do
622− assert (n <= Prelude.length (x:getList backBuf)) (return ())
623− let (src0, buf1) = splitAt n (x:getList backBuf)
624− arr0 = A.fromListN n (Prelude.reverse src0)
625− arr1 = Array contents next end
626− src = arr0 <> arr1
627− let !(Array cont1 start end1) = src
628− fp1 = Tuple' end1 cont1
629− gobuf SPEC s (List buf1) fp1 start pst1
630− PR.Done 0 b -> do
631− let arr = Array contents next end
632− return (b, D.cons arr (D.Stream step s))
633− PR.Done n b -> do
634− assert (n <= Prelude.length (x:getList backBuf)) (return ())
635− let src0 = Prelude.take n (x:getList backBuf)
636− -- XXX create the array in reverse instead
637− arr0 = A.fromListN n (Prelude.reverse src0)
638− arr1 = Array contents next end
639− -- XXX Use StreamK to avoid adding arbitrary layers of
640− -- constructors every time.
641− str = D.cons arr0 (D.cons arr1 (D.Stream step s))
642− return (b, str)
643− PR.Error err -> throwM $ ParseError err
644−-}
645−
646−{-# INLINE_NORMAL parseBreakK #-}
647−parseBreakK ::
648− forall m a b. (MonadIO m, Unbox a)
649− => PRD.Parser a m b
650− -> K.StreamK m (Array.Array a)
651− -> m (Either ParseError b, K.StreamK m (Array.Array a))
652−parseBreakK (PRD.Parser pstep initial extract) stream = do
653− res <- initial
654− case res of
655− PRD.IPartial s -> go s stream []
656− PRD.IDone b -> return (Right b, stream)
657− PRD.IError err -> return (Left (ParseError err), stream)
658−
659− where
660−
661− -- "backBuf" contains last few items in the stream that we may have to
662− -- backtrack to.
663− --
664− -- XXX currently we are using a dumb list based approach for backtracking
665− -- buffer. This can be replaced by a sliding/ring buffer using Data.Array.
666− -- That will allow us more efficient random back and forth movement.
667− go !pst st backBuf = do
668− let stop = goStop pst backBuf -- (, K.nil) <$> extract pst
669− single a = yieldk a K.nil
670− yieldk arr r = goArray pst backBuf r arr
671− in K.foldStream defState yieldk single stop st
672−
673− -- Use strictness on "cur" to keep it unboxed
674− goArray !pst backBuf st (Array _ cur end) | cur == end = go pst st backBuf
675− goArray !pst backBuf st (Array contents cur end) = do
676− x <- liftIO $ peekByteIndex cur contents
677− pRes <- pstep pst x
678− let next = INDEX_NEXT(cur,a)
679− case pRes of
680− PR.Partial 0 s ->
681− goArray s [] st (Array contents next end)
682− PR.Partial n s -> do
683− assert (n <= Prelude.length (x:backBuf)) (return ())
684− let src0 = Prelude.take n (x:backBuf)
685− arr0 = A.fromListN n (Prelude.reverse src0)
686− arr1 = Array contents next end
687− src = arr0 <> arr1
688− goArray s [] st src
689− PR.Continue 0 s ->
690− goArray s (x:backBuf) st (Array contents next end)
691− PR.Continue n s -> do
692− assert (n <= Prelude.length (x:backBuf)) (return ())
693− let (src0, buf1) = splitAt n (x:backBuf)
694− arr0 = A.fromListN n (Prelude.reverse src0)
695− arr1 = Array contents next end
696− src = arr0 <> arr1
697− goArray s buf1 st src
698− PR.Done 0 b -> do
699− let arr = Array contents next end
700− return (Right b, K.cons arr st)
701− PR.Done n b -> do
702− assert (n <= Prelude.length (x:backBuf)) (return ())
703− let src0 = Prelude.take n (x:backBuf)
704− -- XXX Use fromListRevN once implemented
705− -- arr0 = A.fromListRevN n src0
706− arr0 = A.fromListN n (Prelude.reverse src0)
707− arr1 = Array contents next end
708− str = K.cons arr0 (K.cons arr1 st)
709− return (Right b, str)
710− PR.Error err -> do
711− let n = Prelude.length backBuf
712− arr0 = A.fromListN n (Prelude.reverse backBuf)
713− arr1 = Array contents cur end
714− str = K.cons arr0 (K.cons arr1 stream)
715− return (Left (ParseError err), str)
716−
717− -- This is a simplified goArray
718− goExtract !pst backBuf (Array _ cur end)
719− | cur == end = goStop pst backBuf
720− goExtract !pst backBuf (Array contents cur end) = do
721− x <- liftIO $ peekByteIndex cur contents
722− pRes <- pstep pst x
723− let next = INDEX_NEXT(cur,a)
724− case pRes of
725− PR.Partial 0 s ->
726− goExtract s [] (Array contents next end)
727− PR.Partial n s -> do
728− assert (n <= Prelude.length (x:backBuf)) (return ())
729− let src0 = Prelude.take n (x:backBuf)
730− arr0 = A.fromListN n (Prelude.reverse src0)
731− arr1 = Array contents next end
732− src = arr0 <> arr1
733− goExtract s [] src
734− PR.Continue 0 s ->
735− goExtract s backBuf (Array contents next end)
736− PR.Continue n s -> do
737− assert (n <= Prelude.length (x:backBuf)) (return ())
738− let (src0, buf1) = splitAt n (x:backBuf)
739− arr0 = A.fromListN n (Prelude.reverse src0)
740− arr1 = Array contents next end
741− src = arr0 <> arr1
742− goExtract s buf1 src
743− PR.Done 0 b -> do
744− let arr = Array contents next end
745− return (Right b, K.fromPure arr)
746− PR.Done n b -> do
747− assert (n <= Prelude.length backBuf) (return ())
748− let src0 = Prelude.take n backBuf
749− -- XXX Use fromListRevN once implemented
750− -- arr0 = A.fromListRevN n src0
751− arr0 = A.fromListN n (Prelude.reverse src0)
752− arr1 = Array contents next end
753− str = K.cons arr0 (K.fromPure arr1)
754− return (Right b, str)
755− PR.Error err -> do
756− let n = Prelude.length backBuf
757− arr0 = A.fromListN n (Prelude.reverse backBuf)
758− arr1 = Array contents cur end
759− str = K.cons arr0 (K.cons arr1 stream)
760− return (Left (ParseError err), str)
761−
762− -- This is a simplified goExtract
763− {-# INLINE goStop #-}
764− goStop !pst backBuf = do
765− pRes <- extract pst
766− case pRes of
767− PR.Partial _ _ -> error "Bug: parseBreak: Partial in extract"
768− PR.Continue 0 s ->
769− goStop s backBuf
770− PR.Continue n s -> do
771− assert (n <= Prelude.length backBuf) (return ())
772− let (src0, buf1) = splitAt n backBuf
773− arr = A.fromListN n (Prelude.reverse src0)
774− goExtract s buf1 arr
775− PR.Done 0 b ->
776− return (Right b, K.nil)
777− PR.Done n b -> do
778− assert (n <= Prelude.length backBuf) (return ())
779− let src0 = Prelude.take n backBuf
780− -- XXX Use fromListRevN once implemented
781− -- arr0 = A.fromListRevN n src0
782− arr0 = A.fromListN n (Prelude.reverse src0)
783− return (Right b, K.fromPure arr0)
784− PR.Error err -> do
785− let n = Prelude.length backBuf
786− arr0 = A.fromListN n (Prelude.reverse backBuf)
787− return (Left (ParseError err), K.fromPure arr0)
788−
789273-- | Parse an array stream using the supplied 'Parser'. Returns the parse
790274-- result and the unconsumed stream. Throws 'ParseError' if the parse fails.
791275--
276+-- >> parseBreak p = K.parseBreakChunks (ParserK.adaptC p)
277+--
278+-- This is redundant and we can just use parseBreakChunks, as ParserK can be
279+-- composed using Monad. The only advantage of this is that we do not need to
280+-- adapt.
281+--
282+-- We can compare perf and remove this one or define it in terms of that.
283+--
792284-- /Internal/
793285--
794286{-# INLINE_NORMAL parseBreak #-}
@@ -801,7 +293,8 @@ parseBreak ::
801293parseBreak p s =
802294 fmap fromStreamD <$> parseBreakD (PRD.fromParserK p) (toStreamD s)
803295-}
804−parseBreak = parseBreakK
296+parseBreak = Array.parseBreakChunksK
297+-- parseBreak p = K.parseBreakChunks (ParserK.adaptC p)
805298
806299-------------------------------------------------------------------------------
807300-- Elimination - Running Array Folds and parsers
core/src/Streamly/Internal/Data/Array/Type.hs+489−96
@@ -1,8 +1,11 @@
11{-# LANGUAGE CPP #-}
2+{-# LANGUAGE TypeFamilies #-}
3+-- Must come after TypeFamilies, otherwise it is re-enabled.
4+-- MonoLocalBinds enabled by TypeFamilies causes perf regressions in general.
5+{-# LANGUAGE NoMonoLocalBinds #-}
26-- |
37-- Module : Streamly.Internal.Data.Array.Type
48-- Copyright : (c) 2020 Composewell Technologies
5−--
69-- License : BSD3-3-Clause
710-- Maintainer : streamly@composewell.com
811-- Stability : experimental
@@ -10,87 +13,146 @@
1013--
1114-- See notes in "Streamly.Internal.Data.MutArray.Type"
1215--
16+
1317module Streamly.Internal.Data.Array.Type
1418 (
1519 -- ** Type
1620 -- $arrayNotes
1721 Array (..)
18− , asPtrUnsafe
19− , nil
2022
21− -- ** Freezing and Thawing
23+ -- ** Conversion
24+ -- *** Mutable and Immutable
2225 , unsafeFreeze
2326 , unsafeFreezeWithShrink
2427 , unsafeThaw
2528
26− -- ** Pinning and Unpinning
29+ -- *** Pinned and Unpinned
2730 , pin
2831 , unpin
2932 , isPinned
3033
34+ -- *** Casting
35+ , unsafePinnedAsPtr
36+
3137 -- ** Construction
32− , splice
38+ , empty
3339
34− , fromList
35− , pinnedFromList
40+ -- *** Cloning
41+ -- XXX Why would we clone an immutable array?
42+ , clone
43+ , pinnedClone
44+
45+ -- *** Slicing
46+ -- | Get a subarray without copying
47+ , splitAt
48+ , breakOn -- XXX requires MonadIO
49+
50+ -- *** Stream Folds
51+ , unsafeMakePure
52+ , createOf
53+ , pinnedCreateOf
54+ , unsafeCreateOf
55+ , unsafePinnedCreateOf
56+ , create
57+ , pinnedCreate
58+ , createWith
59+
60+ -- *** From containers
3661 , fromListN
3762 , pinnedFromListN
38− , fromListRev
63+ , fromList
64+ , pinnedFromList
3965 , fromListRevN
40− , fromStreamDN
41− , fromStreamD
66+ , fromListRev
67+ , fromStreamN
68+ , fromStream
69+ , fromPureStreamN
4270 , fromPureStream
4371 , fromByteStr#
72+ , fromByteStr
73+ , fromPtrN
74+ , fromChunks
75+ , fromChunksK
4476
45− -- ** Split
46− , breakOn
77+ -- ** Reading
4778
48− -- ** Cloning arrays
49− , clone
50− , pinnedClone
79+ -- *** Indexing
80+ , unsafeIndexIO -- XXX unsafeGetIndexIO
81+ , getIndexUnsafe -- XXX unsafeGetIndex
5182
52− -- ** Elimination
53− , unsafeIndexIO
54− , getIndexUnsafe
55− , byteLength
83+ -- *** To Streams
84+ , read
85+ , readRev
86+ , toStreamK
87+ , toStreamKRev
88+
89+ -- *** To Containers
90+ , toList
91+
92+ -- *** Unfolds
93+ , producer -- experimental
94+ , readerUnsafe
95+ , reader
96+ , readerRev
97+
98+ -- *** Size
5699 , length
100+ , byteLength
57101
102+ -- ** Folding
58103 , foldl'
59104 , foldr
60− , splitAt
105+ , byteCmp
106+ , byteEq
61107
108+ -- ** Appending
109+ , splice -- XXX requires MonadIO
110+
111+ -- ** Streams of arrays
112+ -- *** Chunk
113+ -- | Group a stream into arrays.
114+ , chunksOf
115+ , pinnedChunksOf
116+ , buildChunks
117+
118+ -- *** Split
119+ -- | Split an array into slices.
120+
121+ -- *** Concat
122+ -- | Append the arrays in a stream to form a stream of elements.
123+ , concat
124+ , concatRev
125+
126+ -- *** Compact
127+ -- | Append the arrays in a stream to form a stream of larger arrays.
128+ , fCompactGE
129+ , fPinnedCompactGE
130+ , lCompactGE
131+ , lPinnedCompactGE
132+ , compactGE
133+
134+ -- ** Deprecated
135+ , asPtrUnsafe
136+ , unsafeIndex
137+ , bufferChunks
138+ , flattenArrays
139+ , flattenArraysRev
140+ , fromArrayStreamK
141+ , fromStreamDN
142+ , fromStreamD
62143 , toStreamD
63144 , toStreamDRev
64− , toStreamK
65− , toStreamKRev
66145 , toStream
67146 , toStreamRev
68− , read
69− , readRev
70− , readerRev
71− , toList
72−
73− -- ** Folds
147+ , nil
74148 , writeWith
75149 , writeN
76150 , pinnedWriteN
77151 , writeNUnsafe
78152 , pinnedWriteNUnsafe
79− , MA.ArrayUnsafe (..)
80153 , pinnedWriteNAligned
81154 , write
82155 , pinnedWrite
83− , unsafeMakePure
84−
85− -- ** Streams of arrays
86− , chunksOf
87− , pinnedChunksOf
88− , bufferChunks
89− , flattenArrays
90− , flattenArraysRev
91−
92− -- ** Deprecated
93− , unsafeIndex
94156 )
95157where
96158
@@ -98,31 +160,36 @@ where
98160#include "inline.hs"
99161
100162import Control.Exception (assert)
101−import Control.Monad (replicateM)
163+import Control.Monad (replicateM, when)
102164import Control.Monad.IO.Class (MonadIO(..))
103165import Data.Functor.Identity (Identity(..))
166+import Data.Int (Int8, Int16, Int32, Int64)
104167import Data.Proxy (Proxy(..))
105−import Data.Word (Word8)
168+import Data.Word (Word8, Word16, Word32, Word64)
106169import GHC.Base (build)
107170import GHC.Exts (IsList, IsString(..), Addr#)
108171
109172import GHC.IO (unsafePerformIO)
110173import GHC.Ptr (Ptr(..))
111−import Streamly.Internal.Data.MutArray.Type (MutArray(..), MutByteArray)
174+import Streamly.Internal.Data.Producer.Type (Producer(..))
175+import Streamly.Internal.Data.MutArray.Type (MutArray(..))
176+import Streamly.Internal.Data.MutByteArray.Type (MutByteArray)
112177import Streamly.Internal.Data.Fold.Type (Fold(..))
113178import Streamly.Internal.Data.Stream.Type (Stream)
179+import Streamly.Internal.Data.StreamK.Type (StreamK)
114180import Streamly.Internal.Data.Unbox (Unbox(..))
115181import Streamly.Internal.Data.Unfold.Type (Unfold(..))
116182import Text.Read (readPrec)
117183
118−import Prelude hiding (Foldable(..), read, unlines, splitAt)
184+import Prelude hiding (Foldable(..), concat, read, unlines, splitAt)
119185
120186import qualified GHC.Exts as Exts
187+import qualified Streamly.Internal.Data.Fold.Type as Fold
121188import qualified Streamly.Internal.Data.MutArray.Type as MA
122189import qualified Streamly.Internal.Data.Stream.Type as D
123−import qualified Streamly.Internal.Data.Stream.Generate as D
124190import qualified Streamly.Internal.Data.StreamK.Type as K
125191import qualified Streamly.Internal.Data.MutByteArray.Type as Unboxed
192+import qualified Streamly.Internal.Data.Producer as Producer
126193import qualified Streamly.Internal.Data.Unfold.Type as Unfold
127194import qualified Text.ParserCombinators.ReadPrec as ReadPrec
128195
@@ -176,15 +243,20 @@ data Array a =
176243
177244-- | Use an @Array a@ as @Ptr a@.
178245--
179−-- See 'MA.asPtrUnsafe' in the Mutable array module for more details.
246+-- See 'MA.unsafePinnedAsPtr' in the Mutable array module for more details.
180247--
181248-- /Unsafe/
182249--
183250-- /Pre-release/
184251--
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." #-}
185257{-# INLINE asPtrUnsafe #-}
186258asPtrUnsafe :: MonadIO m => Array a -> (Ptr a -> m b) -> m b
187−asPtrUnsafe arr = MA.asPtrUnsafe (unsafeThaw arr)
259+asPtrUnsafe = unsafePinnedAsPtr
188260
189261-------------------------------------------------------------------------------
190262-- Freezing and Thawing
@@ -252,11 +324,14 @@ isPinned = MA.isPinned . unsafeThaw
252324-- Construction
253325-------------------------------------------------------------------------------
254326
255−-- Splice two immutable arrays creating a new array.
327+-- | Copy two immutable arrays into a new array. If you want to splice more
328+-- than two arrays then this operation would be highly inefficient because it
329+-- would make a copy on every splice operation, instead use the
330+-- 'fromChunksK' operation to combine n immutable arrays.
256331{-# INLINE splice #-}
257−splice :: (MonadIO m, Unbox a) => Array a -> Array a -> m (Array a)
332+splice :: MonadIO m => Array a -> Array a -> m (Array a)
258333splice arr1 arr2 =
259− unsafeFreeze <$> MA.splice (unsafeThaw arr1) (unsafeThaw arr2)
334+ unsafeFreeze <$> MA.spliceCopy (unsafeThaw arr1) (unsafeThaw arr2)
260335
261336-- | Create an 'Array' from the first N elements of a list. The array is
262337-- allocated to size N, if the list terminates before N elements then the
@@ -300,15 +375,45 @@ pinnedFromList xs = unsafePerformIO $ unsafeFreeze <$> MA.pinnedFromList xs
300375fromListRev :: Unbox a => [a] -> Array a
301376fromListRev xs = unsafePerformIO $ unsafeFreeze <$> MA.fromListRev xs
302377
303−{-# INLINE_NORMAL fromStreamDN #-}
378+-- | Create an 'Array' from the first N elements of a stream. The array is
379+-- allocated to size N, if the stream terminates before N elements then the
380+-- array may hold less than N elements.
381+--
382+-- >>> fromStreamN n = Stream.fold (Array.writeN n)
383+--
384+-- /Pre-release/
385+{-# INLINE_NORMAL fromStreamN #-}
386+fromStreamN :: (MonadIO m, Unbox a) => Int -> Stream m a -> m (Array a)
387+fromStreamN n m = do
388+ when (n < 0) $ error "writeN: negative write count specified"
389+ unsafeFreeze <$> MA.fromStreamN n m
390+-- fromStreamN n = D.fold (writeN n)
391+
392+{-# DEPRECATED fromStreamDN "Please use fromStreamN instead." #-}
304393fromStreamDN :: forall m a. (MonadIO m, Unbox a)
305394 => Int -> D.Stream m a -> m (Array a)
306−fromStreamDN limit str = unsafeFreeze <$> MA.fromStreamDN limit str
395+fromStreamDN = fromStreamN
307396
397+-- | Create an 'Array' from a stream. This is useful when we want to create a
398+-- single array from a stream of unknown size. 'writeN' is at least twice
399+-- as efficient when the size is already known.
400+--
401+-- >>> fromStream = Stream.fold Array.write
402+--
403+-- Note that if the input stream is too large memory allocation for the array
404+-- may fail. When the stream size is not known, `chunksOf` followed by
405+-- processing of indvidual arrays in the resulting stream should be preferred.
406+--
407+-- /Pre-release/
308408{-# INLINE_NORMAL fromStreamD #-}
409+fromStream :: (MonadIO m, Unbox a) => Stream m a -> m (Array a)
410+fromStream = D.fold write
411+-- fromStreamD str = unsafeFreeze <$> MA.fromStream str
412+
413+{-# DEPRECATED fromStreamD "Please use fromStream instead." #-}
309414fromStreamD :: forall m a. (MonadIO m, Unbox a)
310415 => D.Stream m a -> m (Array a)
311−fromStreamD str = unsafeFreeze <$> MA.fromStreamD str
416+fromStreamD = fromStream
312417
313418-------------------------------------------------------------------------------
314419-- Cloning
@@ -338,10 +443,15 @@ pinnedClone = fmap unsafeFreeze . MA.pinnedClone . unsafeThaw
338443-- Streams of arrays
339444-------------------------------------------------------------------------------
340445
341−{-# INLINE bufferChunks #-}
446+{-# INLINE buildChunks #-}
447+buildChunks :: (MonadIO m, Unbox a) =>
448+ D.Stream m a -> m (K.StreamK m (Array a))
449+buildChunks m = D.foldr K.cons K.nil $ chunksOf defaultChunkSize m
450+
451+{-# DEPRECATED bufferChunks "Please use buildChunks instead." #-}
342452bufferChunks :: (MonadIO m, Unbox a) =>
343453 D.Stream m a -> m (K.StreamK m (Array a))
344−bufferChunks m = D.foldr K.cons K.nil $ chunksOf defaultChunkSize m
454+bufferChunks = buildChunks
345455
346456-- | @chunksOf n stream@ groups the elements in the input stream into arrays of
347457-- @n@ elements each.
@@ -362,27 +472,100 @@ pinnedChunksOf :: forall m a. (MonadIO m, Unbox a)
362472 => Int -> D.Stream m a -> D.Stream m (Array a)
363473pinnedChunksOf n str = D.map unsafeFreeze $ MA.pinnedChunksOf n str
364474
365−-- | Use the "read" unfold instead.
475+-- | Convert a stream of arrays into a stream of their elements.
366476--
367−-- @flattenArrays = unfoldMany read@
477+-- >>> concat = Stream.unfoldMany Array.reader
368478--
369−-- We can try this if there are any fusion issues in the unfold.
370−--
371−{-# INLINE_NORMAL flattenArrays #-}
479+{-# INLINE_NORMAL concat #-}
480+concat :: (Monad m, Unbox a) => Stream m (Array a) -> Stream m a
481+-- XXX this requires MonadIO whereas the unfoldMany version does not
482+concat = MA.concatWith (pure . unsafeInlineIO) . D.map unsafeThaw
483+-- concat = D.unfoldMany reader
484+
485+{-# DEPRECATED flattenArrays "Please use \"unfoldMany reader\" instead." #-}
486+{-# INLINE flattenArrays #-}
372487flattenArrays :: forall m a. (MonadIO m, Unbox a)
373488 => D.Stream m (Array a) -> D.Stream m a
374−flattenArrays = MA.flattenArrays . D.map unsafeThaw
489+flattenArrays = concat
375490
376−-- | Use the "readRev" unfold instead.
377−--
378−-- @flattenArrays = unfoldMany readRev@
491+-- | Convert a stream of arrays into a stream of their elements reversing the
492+-- contents of each array before flattening.
379493--
380−-- We can try this if there are any fusion issues in the unfold.
494+-- >>> concatRev = Stream.unfoldMany Array.readerRev
381495--
382−{-# INLINE_NORMAL flattenArraysRev #-}
496+{-# INLINE_NORMAL concatRev #-}
497+concatRev :: forall m a. (Monad m, Unbox a)
498+ => D.Stream m (Array a) -> D.Stream m a
499+-- XXX this requires MonadIO whereas the unfoldMany version does not
500+concatRev = MA.concatRevWith (pure . unsafeInlineIO) . D.map unsafeThaw
501+-- concatRev = D.unfoldMany readerRev
502+
503+{-# DEPRECATED flattenArraysRev "Please use \"unfoldMany readerRev\" instead." #-}
504+{-# INLINE flattenArraysRev #-}
383505flattenArraysRev :: forall m a. (MonadIO m, Unbox a)
384506 => D.Stream m (Array a) -> D.Stream m a
385−flattenArraysRev = MA.flattenArraysRev . D.map unsafeThaw
507+flattenArraysRev = concatRev
508+
509+-------------------------------------------------------------------------------
510+-- Compact
511+-------------------------------------------------------------------------------
512+
513+-- XXX Note that this thaws immutable arrays for appending, that may be
514+-- problematic if multiple users do the same thing, however, thawed immutable
515+-- arrays would have no capacity to append, therefore, a copy will be forced
516+-- anyway.
517+
518+-- | Fold @fCompactGE n@ coalesces adjacent arrays in the input stream
519+-- until the size becomes greater than or equal to n.
520+--
521+-- Generates unpinned arrays irrespective of the pinning status of input
522+-- arrays.
523+{-# INLINE_NORMAL fCompactGE #-}
524+fCompactGE :: (MonadIO m, Unbox a) => Int -> Fold m (Array a) (Array a)
525+fCompactGE n = fmap unsafeFreeze $ Fold.lmap unsafeThaw $ MA.fCompactGE n
526+
527+-- | PInned version of 'fCompactGE'.
528+{-# INLINE_NORMAL fPinnedCompactGE #-}
529+fPinnedCompactGE :: (MonadIO m, Unbox a) => Int -> Fold m (Array a) (Array a)
530+fPinnedCompactGE n =
531+ fmap unsafeFreeze $ Fold.lmap unsafeThaw $ MA.fPinnedCompactGE n
532+
533+-- | @compactGE n stream@ coalesces adjacent arrays in the @stream@ until
534+-- the size becomes greater than or equal to @n@.
535+--
536+-- >>> compactGE n = Stream.foldMany (Array.fCompactGE n)
537+--
538+-- Generates unpinned arrays irrespective of the pinning status of input
539+-- arrays.
540+{-# INLINE compactGE #-}
541+compactGE ::
542+ (MonadIO m, Unbox a)
543+ => Int -> Stream m (Array a) -> Stream m (Array a)
544+compactGE n stream =
545+ D.map unsafeFreeze $ MA.compactGE n $ D.map unsafeThaw stream
546+
547+-- | Like 'compactGE' but for transforming folds instead of stream.
548+--
549+-- >>> lCompactGE n = Fold.many (Array.fCompactGE n)
550+--
551+-- Generates unpinned arrays irrespective of the pinning status of input
552+-- arrays.
553+{-# INLINE_NORMAL lCompactGE #-}
554+lCompactGE :: (MonadIO m, Unbox a)
555+ => Int -> Fold m (Array a) () -> Fold m (Array a) ()
556+lCompactGE n fld =
557+ Fold.lmap unsafeThaw $ MA.lCompactGE n (Fold.lmap unsafeFreeze fld)
558+
559+-- | Pinned version of 'lCompactGE'.
560+{-# INLINE_NORMAL lPinnedCompactGE #-}
561+lPinnedCompactGE :: (MonadIO m, Unbox a)
562+ => Int -> Fold m (Array a) () -> Fold m (Array a) ()
563+lPinnedCompactGE n fld =
564+ Fold.lmap unsafeThaw $ MA.lPinnedCompactGE n (Fold.lmap unsafeFreeze fld)
565+
566+-------------------------------------------------------------------------------
567+-- Splitting
568+-------------------------------------------------------------------------------
386569
387570-- Drops the separator byte
388571{-# INLINE breakOn #-}
@@ -426,24 +609,68 @@ byteLength = MA.byteLength . unsafeThaw
426609length :: Unbox a => Array a -> Int
427610length arr = MA.length (unsafeThaw arr)
428611
612+{-# INLINE_NORMAL producer #-}
613+producer :: forall m a. (Monad m, Unbox a) => Producer m (Array a) a
614+producer =
615+ Producer.translate unsafeThaw unsafeFreeze
616+ $ MA.producerWith (return . unsafeInlineIO)
617+
618+-- | Unfold an array into a stream.
619+--
620+{-# INLINE_NORMAL reader #-}
621+reader :: forall m a. (Monad m, Unbox a) => Unfold m (Array a) a
622+reader = Producer.simplify producer
623+
624+-- | Unfold an array into a stream, does not check the end of the array, the
625+-- user is responsible for terminating the stream within the array bounds. For
626+-- high performance application where the end condition can be determined by
627+-- a terminating fold.
628+--
629+-- Written in the hope that it may be faster than "read", however, in the case
630+-- for which this was written, "read" proves to be faster even though the core
631+-- generated with unsafeRead looks simpler.
632+--
633+-- /Pre-release/
634+--
635+{-# INLINE_NORMAL readerUnsafe #-}
636+readerUnsafe :: forall m a. (Monad m, Unbox a) => Unfold m (Array a) a
637+readerUnsafe = Unfold step inject
638+ where
639+
640+ inject (Array contents start end) =
641+ return (MA.ArrayUnsafe contents end start)
642+
643+ {-# INLINE_LATE step #-}
644+ step (MA.ArrayUnsafe contents end p) = do
645+ -- unsafeInlineIO allows us to run this in Identity monad for pure
646+ -- toList/foldr case which makes them much faster due to not
647+ -- accumulating the list and fusing better with the pure consumers.
648+ --
649+ -- This should be safe as the array contents are guaranteed to be
650+ -- evaluated/written to before we peek at them.
651+ let !x = unsafeInlineIO $ peekAt p contents
652+ let !p1 = INDEX_NEXT(p,a)
653+ return $ D.Yield x (MA.ArrayUnsafe contents end p1)
654+
429655-- | Unfold an array into a stream in reverse order.
430656--
431657{-# INLINE_NORMAL readerRev #-}
432658readerRev :: forall m a. (Monad m, Unbox a) => Unfold m (Array a) a
433659readerRev = Unfold.lmap unsafeThaw $ MA.readerRevWith (return . unsafeInlineIO)
434660
661+{-# DEPRECATED toStreamD "Please use 'read' instead." #-}
435662{-# INLINE_NORMAL toStreamD #-}
436663toStreamD :: forall m a. (Monad m, Unbox a) => Array a -> D.Stream m a
437−toStreamD arr = MA.toStreamDWith (return . unsafeInlineIO) (unsafeThaw arr)
664+toStreamD = read
438665
439666{-# INLINE toStreamK #-}
440667toStreamK :: forall m a. (Monad m, Unbox a) => Array a -> K.StreamK m a
441668toStreamK arr = MA.toStreamKWith (return . unsafeInlineIO) (unsafeThaw arr)
442669
670+{-# DEPRECATED toStreamDRev "Please use 'readRev' instead." #-}
443671{-# INLINE_NORMAL toStreamDRev #-}
444672toStreamDRev :: forall m a. (Monad m, Unbox a) => Array a -> D.Stream m a
445−toStreamDRev arr =
446− MA.toStreamDRevWith (return . unsafeInlineIO) (unsafeThaw arr)
673+toStreamDRev = readRev
447674
448675{-# INLINE toStreamKRev #-}
449676toStreamKRev :: forall m a. (Monad m, Unbox a) => Array a -> K.StreamK m a
@@ -455,7 +682,7 @@ toStreamKRev arr =
455682-- /Pre-release/
456683{-# INLINE_EARLY read #-}
457684read :: (Monad m, Unbox a) => Array a -> Stream m a
458−read = toStreamD
685+read arr = MA.toStreamWith (return . unsafeInlineIO) (unsafeThaw arr)
459686
460687-- | Same as 'read'
461688--
@@ -472,7 +699,7 @@ toStream = read
472699-- /Pre-release/
473700{-# INLINE_EARLY readRev #-}
474701readRev :: (Monad m, Unbox a) => Array a -> Stream m a
475−readRev = toStreamDRev
702+readRev arr = MA.toStreamRevWith (return . unsafeInlineIO) (unsafeThaw arr)
476703
477704-- | Same as 'readRev'
478705--
@@ -496,6 +723,7 @@ foldr f z arr = runIdentity $ D.foldr f z $ toStreamD arr
496723-- | Create two slices of an array without copying the original array. The
497724-- specified index @i@ is the first index of the second slice.
498725--
726+{-# INLINE splitAt #-}
499727splitAt :: Unbox a => Int -> Array a -> (Array a, Array a)
500728splitAt i arr = (unsafeFreeze a, unsafeFreeze b)
501729 where
@@ -529,17 +757,28 @@ toList s = build (\c n -> toListFB c n s)
529757-- Folds
530758-------------------------------------------------------------------------------
531759
532−-- | @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
533761-- 'Array'.
534762--
535−{-# 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 #-}
536770writeN :: forall m a. (MonadIO m, Unbox a) => Int -> Fold m a (Array a)
537−writeN = fmap unsafeFreeze . MA.writeN
771+writeN = createOf
538772
539−-- | Like 'fromListN' but creates a pinned array.
540−{-# 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 #-}
541780pinnedWriteN :: forall m a. (MonadIO m, Unbox a) => Int -> Fold m a (Array a)
542−pinnedWriteN = fmap unsafeFreeze . MA.pinnedWriteN
781+pinnedWriteN = pinnedCreateOf
543782
544783-- | @pinnedWriteNAligned alignment n@ folds a maximum of @n@ elements from the input
545784-- stream to an 'Array' aligned to the given size.
@@ -547,44 +786,74 @@ pinnedWriteN = fmap unsafeFreeze . MA.pinnedWriteN
547786-- /Pre-release/
548787--
549788{-# INLINE_NORMAL pinnedWriteNAligned #-}
789+{-# DEPRECATED pinnedWriteNAligned "To be removed." #-}
550790pinnedWriteNAligned :: forall m a. (MonadIO m, Unbox a)
551791 => Int -> Int -> Fold m a (Array a)
552792pinnedWriteNAligned alignSize = fmap unsafeFreeze . MA.pinnedWriteNAligned alignSize
553793
554−-- | 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
555795-- driver must not call the step function more than 'n' times otherwise it will
556796-- corrupt the memory and crash. This function exists mainly because any
557797-- conditional in the step function blocks fusion causing 10x performance
558798-- slowdown.
559799--
560−{-# 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 #-}
561807writeNUnsafe :: forall m a. (MonadIO m, Unbox a)
562808 => Int -> Fold m a (Array a)
563−writeNUnsafe n = unsafeFreeze <$> MA.writeNUnsafe n
809+writeNUnsafe = unsafeCreateOf
564810
565−{-# INLINE_NORMAL pinnedWriteNUnsafe #-}
811+{-# INLINE_NORMAL unsafePinnedCreateOf #-}
812+unsafePinnedCreateOf :: forall m a. (MonadIO m, Unbox a)
813+ => Int -> Fold m a (Array a)
814+unsafePinnedCreateOf n = unsafeFreeze <$> MA.unsafePinnedCreateOf n
815+
816+{-# DEPRECATED pinnedWriteNUnsafe "Please use unsafePinnedCreateOf instead." #-}
817+{-# INLINE pinnedWriteNUnsafe #-}
566818pinnedWriteNUnsafe :: forall m a. (MonadIO m, Unbox a)
567819 => Int -> Fold m a (Array a)
568−pinnedWriteNUnsafe n = unsafeFreeze <$> MA.pinnedWriteNUnsafe n
820+pinnedWriteNUnsafe = unsafePinnedCreateOf
821+
822+{-# INLINE_NORMAL createWith #-}
823+createWith :: forall m a. (MonadIO m, Unbox a)
824+ => Int -> Fold m a (Array a)
825+-- createWith n = FL.rmapM spliceArrays $ toArraysOf n
826+createWith elemCount = unsafeFreeze <$> MA.createWith elemCount
569827
570−{-# INLINE_NORMAL writeWith #-}
828+{-# DEPRECATED writeWith "Please use createWith instead." #-}
829+{-# INLINE writeWith #-}
571830writeWith :: forall m a. (MonadIO m, Unbox a)
572831 => Int -> Fold m a (Array a)
573−-- writeWith n = FL.rmapM spliceArrays $ toArraysOf n
574−writeWith elemCount = unsafeFreeze <$> MA.writeWith elemCount
832+writeWith = createWith
575833
576834-- | Fold the whole input to a single array.
577835--
578836-- /Caution! Do not use this on infinite streams./
579837--
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." #-}
580844{-# INLINE write #-}
581845write :: forall m a. (MonadIO m, Unbox a) => Fold m a (Array a)
582−write = fmap unsafeFreeze MA.write
846+write = create
583847
584−-- | 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." #-}
585854{-# INLINE pinnedWrite #-}
586855pinnedWrite :: forall m a. (MonadIO m, Unbox a) => Fold m a (Array a)
587−pinnedWrite = fmap unsafeFreeze MA.pinnedWrite
856+pinnedWrite = pinnedCreate
588857
589858-- | Fold "step" has a dependency on "initial", and each step is dependent on
590859-- the previous invocation of step due to state passing, finally extract
@@ -616,6 +885,11 @@ unsafeMakePure (Fold step initial extract final) =
616885 (\s -> return $! unsafeInlineIO $ extract s)
617886 (\s -> return $! unsafeInlineIO $ final s)
618887
888+{-# INLINE fromPureStreamN #-}
889+fromPureStreamN :: Unbox a => Int -> Stream Identity a -> Array a
890+fromPureStreamN n x =
891+ unsafePerformIO $ fmap unsafeFreeze (MA.fromPureStreamN n x)
892+
619893-- | Convert a pure stream in Identity monad to an immutable array.
620894--
621895-- Same as the following but with better performance:
@@ -627,6 +901,19 @@ fromPureStream x = unsafePerformIO $ fmap (unsafeFreeze) (MA.fromPureStream x)
627901-- fromPureStream = runIdentity . D.fold (unsafeMakePure write)
628902-- fromPureStream = fromList . runIdentity . D.toList
629903
904+-- XXX This should be monadic.
905+
906+-- | Copy an immutable 'Ptr Word8' sequence into an array.
907+--
908+-- /Unsafe:/ The caller is responsible for safe addressing.
909+--
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.
912+fromPtrN :: Int -> Ptr Word8 -> Array Word8
913+fromPtrN n addr = unsafePerformIO $ fmap unsafeFreeze (MA.fromPtrN n addr)
914+
915+-- XXX This should be monadic.
916+
630917-- | Copy a null terminated immutable 'Addr#' Word8 sequence into an array.
631918--
632919-- /Unsafe:/ The caller is responsible for safe addressing.
@@ -637,8 +924,46 @@ fromPureStream x = unsafePerformIO $ fmap (unsafeFreeze) (MA.fromPureStream x)
637924-- >>> Array.toList $ Array.fromByteStr# "\1\2\3\0"#
638925-- [1,2,3]
639926--
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.
640929fromByteStr# :: Addr# -> Array Word8
641−fromByteStr# addr = fromPureStream (D.fromByteStr# addr)
930+fromByteStr# addr = unsafePerformIO $ fmap unsafeFreeze (MA.fromByteStr# addr)
931+
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.
936+fromByteStr :: Ptr Word8 -> Array Word8
937+fromByteStr (Ptr addr#) = fromByteStr# addr#
938+
939+-- XXX implement fromChunks/fromChunkList instead?
940+
941+-- | Convert an array stream to an array. Note that this requires peak memory
942+-- that is double the size of the array stream.
943+--
944+{-# INLINE fromChunksK #-}
945+fromChunksK :: (MonadIO m, Unbox a) => StreamK m (Array a) -> m (Array a)
946+fromChunksK stream =
947+ -- We buffer the entire stream and then allocate the target array of the
948+ -- same size, thus requiring double the memory.
949+ fmap unsafeFreeze $ MA.fromChunksK $ fmap unsafeThaw stream
950+
951+{-# DEPRECATED fromArrayStreamK "Please use fromChunksK instead." #-}
952+fromArrayStreamK :: (Unbox a, MonadIO m) => StreamK m (Array a) -> m (Array a)
953+fromArrayStreamK = fromChunksK
954+
955+-- | Given a stream of arrays, splice them all together to generate a single
956+-- array. The stream must be /finite/.
957+--
958+{-# INLINE fromChunks #-}
959+fromChunks :: (MonadIO m, Unbox a) => Stream m (Array a) -> m (Array a)
960+fromChunks s =
961+ -- XXX Check which implementation is better
962+ -- This may also require double the memory as we double the space every
963+ -- time, when copying the last array we may have reallocated almost double
964+ -- the space required before we right size it.
965+ fmap unsafeFreeze $ MA.fromChunksRealloced (fmap unsafeThaw s)
966+ -- fromChunkStreamK $ D.toStreamK s
642967
643968-------------------------------------------------------------------------------
644969-- Instances
@@ -670,12 +995,66 @@ instance Unbox a => IsList (Array a) where
670995 {-# INLINE toList #-}
671996 toList = toList
672997
673−-- XXX we are assuming that Unboxed equality means element equality. This may
674−-- or may not be correct? arrcmp is 40% faster compared to stream equality.
675−instance (Unbox a, Eq a) => Eq (Array a) where
998+-- | Byte compare two arrays. Compare the length of the arrays. If the length
999+-- is equal, compare the lexicographical ordering of two underlying byte arrays
1000+-- otherwise return the result of length comparison.
1001+--
1002+-- /Unsafe/: Note that the 'Unbox' instance of sum types with constructors of
1003+-- different sizes may leave some memory uninitialized which can make byte
1004+-- comparison unreliable.
1005+--
1006+-- /Pre-release/
1007+{-# INLINE byteCmp #-}
1008+byteCmp :: Array a -> Array a -> Ordering
1009+byteCmp arr1 arr2 =
1010+ -- unsafePerformIO?
1011+ unsafeInlineIO $! unsafeThaw arr1 `MA.byteCmp` unsafeThaw arr2
1012+
1013+-- | Byte equality of two arrays.
1014+--
1015+-- >>> byteEq arr1 arr2 = (==) EQ $ Array.byteCmp arr1 arr2
1016+--
1017+-- /Unsafe/: See 'byteCmp'.
1018+{-# INLINE byteEq #-}
1019+byteEq :: Array a -> Array a -> Bool
1020+byteEq arr1 arr2 = (==) EQ $ byteCmp arr1 arr2
1021+
1022+#define MK_EQ_INSTANCE(typ) \
1023+instance {-# OVERLAPPING #-} Eq (Array typ) where { \
1024+; {-# INLINE (==) #-} \
1025+; (==) = byteEq \
1026+}
1027+
1028+MK_EQ_INSTANCE(Char)
1029+MK_EQ_INSTANCE(Word8)
1030+MK_EQ_INSTANCE(Word16)
1031+MK_EQ_INSTANCE(Word32)
1032+
1033+-- XXX The Word64 default instance should be as fast because we are comparing
1034+-- 64-bit at a time.
1035+MK_EQ_INSTANCE(Word64)
1036+MK_EQ_INSTANCE(Int)
1037+MK_EQ_INSTANCE(Int8)
1038+MK_EQ_INSTANCE(Int16)
1039+MK_EQ_INSTANCE(Int32)
1040+
1041+-- XXX The Int64 default instance should be as fast.
1042+MK_EQ_INSTANCE(Int64)
1043+
1044+-- | If the type allows a byte-by-byte comparison this instance can be
1045+-- overlapped by a more specific instance that uses 'byteCmp'. Byte comparison
1046+-- can be significantly faster.
1047+--
1048+instance {-# OVERLAPPABLE #-} (Unbox a, Eq a) => Eq (Array a) where
6761049 {-# INLINE (==) #-}
6771050 arr1 == arr2 =
678− (==) EQ $ unsafeInlineIO $! unsafeThaw arr1 `MA.cmp` unsafeThaw arr2
1051+ -- Does unboxed byte equality mean element equality?
1052+ -- XXX This is incorrect for sum types, as we may have some
1053+ -- uninitialized memory in that case. If we always initialize the
1054+ -- unused memory to zero we can use this.
1055+ -- Byte comparison is 40% faster compared to stream equality.
1056+ -- (==) EQ $ unsafeInlineIO $! unsafeThaw arr1 `MA.cmp` unsafeThaw arr2
1057+ (toStreamD arr1 :: Stream Identity a) == toStreamD arr2
6791058
6801059instance (Unbox a, Ord a) => Ord (Array a) where
6811060 {-# INLINE compare #-}
@@ -740,15 +1119,29 @@ instance Foldable Array where
7401119-- Semigroup and Monoid
7411120-------------------------------------------------------------------------------
7421121
1122+-- XXX Deprecate and remove the Semigroup and Monoid instances because of
1123+-- potential misuse chances.
1124+
1125+-- | This should not be used for combining many or N arrays as it would copy
1126+-- the two arrays everytime to a new array. For coalescing multiple arrays use
1127+-- 'fromChunksK' instead.
7431128instance Unbox a => Semigroup (Array a) where
7441129 arr1 <> arr2 = unsafePerformIO $ splice arr1 arr2
7451130
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." #-}
7461139nil ::
7471140#ifdef DEVBUILD
7481141 Unbox a =>
7491142#endif
7501143 Array a
751−nil = Array Unboxed.nil 0 0
1144+nil = empty
7521145
7531146instance Unbox a => Monoid (Array a) where
7541147 mempty = nil
core/src/Streamly/Internal/Data/Fold/Combinators.hs−92
@@ -30,8 +30,6 @@ module Streamly.Internal.Data.Fold.Combinators
3030 -- *** Reducers
3131 , drainMapM
3232 , the
33− , length
34− , lengthGeneric
3533 , mean
3634 , rollingHash
3735 , defaultSalt
@@ -166,8 +164,6 @@ module Streamly.Internal.Data.Fold.Combinators
166164 -- ** Trimming
167165
168166 -- By elements
169− , takeEndBy
170− , takeEndBy_
171167 , takeEndBySeq
172168 , takeEndBySeq_
173169 {-
@@ -709,30 +705,6 @@ the = foldt' step initial id
709705-- To Summary
710706------------------------------------------------------------------------------
711707
712−-- | Like 'length', except with a more general 'Num' return value
713−--
714−-- Definition:
715−--
716−-- >>> lengthGeneric = fmap getSum $ Fold.foldMap (Sum . const 1)
717−-- >>> lengthGeneric = Fold.foldl' (\n _ -> n + 1) 0
718−--
719−-- /Pre-release/
720−{-# INLINE lengthGeneric #-}
721−lengthGeneric :: (Monad m, Num b) => Fold m a b
722−lengthGeneric = foldl' (\n _ -> n + 1) 0
723−
724−-- | Determine the length of the input stream.
725−--
726−-- Definition:
727−--
728−-- >>> length = Fold.lengthGeneric
729−-- >>> length = fmap getSum $ Fold.foldMap (Sum . const 1)
730−--
731−{-# INLINE length #-}
732−length :: Monad m => Fold m a Int
733−length = lengthGeneric
734−
735−
736708-- | Determine the sum of all elements of a stream of numbers. Returns additive
737709-- identity (@0@) when the stream is empty. Note that this is not numerically
738710-- stable for floating point numbers.
@@ -1510,70 +1482,6 @@ droppingWhileM p = Fold step initial extract extract
15101482droppingWhile :: Monad m => (a -> Bool) -> Fold m a (Maybe a)
15111483droppingWhile p = droppingWhileM (return . p)
15121484
1513−-- Note: Keep this consistent with S.splitOn. In fact we should eliminate
1514−-- S.splitOn in favor of the fold.
1515−--
1516−-- XXX Use Fold.many instead once it is fixed.
1517−-- > Stream.splitOnSuffix p f = Stream.foldMany (Fold.takeEndBy_ p f)
1518−
1519−-- | Like 'takeEndBy' but drops the element on which the predicate succeeds.
1520−--
1521−-- Example:
1522−--
1523−-- >>> input = Stream.fromList "hello\nthere\n"
1524−-- >>> line = Fold.takeEndBy_ (== '\n') Fold.toList
1525−-- >>> Stream.fold line input
1526−-- "hello"
1527−--
1528−-- >>> Stream.fold Fold.toList $ Stream.foldMany line input
1529−-- ["hello","there"]
1530−--
1531−{-# INLINE takeEndBy_ #-}
1532−takeEndBy_ :: Monad m => (a -> Bool) -> Fold m a b -> Fold m a b
1533−-- takeEndBy_ predicate = scanMaybe (takingEndBy_ predicate)
1534−takeEndBy_ predicate (Fold fstep finitial fextract ffinal) =
1535− Fold step finitial fextract ffinal
1536−
1537− where
1538−
1539− step s a =
1540− if not (predicate a)
1541− then fstep s a
1542− else Done <$> ffinal s
1543−
1544−-- Note:
1545−-- > Stream.splitWithSuffix p f = Stream.foldMany (Fold.takeEndBy p f)
1546−
1547−-- | Take the input, stop when the predicate succeeds taking the succeeding
1548−-- element as well.
1549−--
1550−-- Example:
1551−--
1552−-- >>> input = Stream.fromList "hello\nthere\n"
1553−-- >>> line = Fold.takeEndBy (== '\n') Fold.toList
1554−-- >>> Stream.fold line input
1555−-- "hello\n"
1556−--
1557−-- >>> Stream.fold Fold.toList $ Stream.foldMany line input
1558−-- ["hello\n","there\n"]
1559−--
1560−{-# INLINE takeEndBy #-}
1561−takeEndBy :: Monad m => (a -> Bool) -> Fold m a b -> Fold m a b
1562−-- takeEndBy predicate = scanMaybe (takingEndBy predicate)
1563−takeEndBy predicate (Fold fstep finitial fextract ffinal) =
1564− Fold step finitial fextract ffinal
1565−
1566− where
1567−
1568− step s a = do
1569− res <- fstep s a
1570− if not (predicate a)
1571− then return res
1572− else do
1573− case res of
1574− Partial s1 -> Done <$> ffinal s1
1575− Done b -> return $ Done b
1576−
15771485------------------------------------------------------------------------------
15781486-- Binary splitting on a separator
15791487------------------------------------------------------------------------------
core/src/Streamly/Internal/Data/Fold/Type.hs+91
@@ -367,6 +367,8 @@ module Streamly.Internal.Data.Fold.Type
367367 , toList
368368 , toStreamK
369369 , toStreamKRev
370+ , lengthGeneric
371+ , length
370372
371373 -- * Combinators
372374
@@ -391,6 +393,8 @@ module Streamly.Internal.Data.Fold.Type
391393 -- ** Trimming
392394 , take
393395 , taking
396+ , takeEndBy_
397+ , takeEndBy
394398 , dropping
395399
396400 -- ** Sequential application
@@ -767,6 +771,29 @@ toStreamKRev = foldl' (flip K.cons) K.nil
767771toStreamK :: Monad m => Fold m a (K.StreamK n a)
768772toStreamK = foldr K.cons K.nil
769773
774+-- | Like 'length', except with a more general 'Num' return value
775+--
776+-- Definition:
777+--
778+-- >>> lengthGeneric = fmap getSum $ Fold.foldMap (Sum . const 1)
779+-- >>> lengthGeneric = Fold.foldl' (\n _ -> n + 1) 0
780+--
781+-- /Pre-release/
782+{-# INLINE lengthGeneric #-}
783+lengthGeneric :: (Monad m, Num b) => Fold m a b
784+lengthGeneric = foldl' (\n _ -> n + 1) 0
785+
786+-- | Determine the length of the input stream.
787+--
788+-- Definition:
789+--
790+-- >>> length = Fold.lengthGeneric
791+-- >>> length = fmap getSum $ Fold.foldMap (Sum . const 1)
792+--
793+{-# INLINE length #-}
794+length :: Monad m => Fold m a Int
795+length = lengthGeneric
796+
770797------------------------------------------------------------------------------
771798-- Instances
772799------------------------------------------------------------------------------
@@ -1517,6 +1544,70 @@ take n (Fold fstep finitial fextract ffinal) = Fold step initial extract final
15171544
15181545 final (Tuple'Fused _ r) = ffinal r
15191546
1547+-- Note: Keep this consistent with S.splitOn. In fact we should eliminate
1548+-- S.splitOn in favor of the fold.
1549+--
1550+-- XXX Use Fold.many instead once it is fixed.
1551+-- > Stream.splitOnSuffix p f = Stream.foldMany (Fold.takeEndBy_ p f)
1552+
1553+-- | Like 'takeEndBy' but drops the element on which the predicate succeeds.
1554+--
1555+-- Example:
1556+--
1557+-- >>> input = Stream.fromList "hello\nthere\n"
1558+-- >>> line = Fold.takeEndBy_ (== '\n') Fold.toList
1559+-- >>> Stream.fold line input
1560+-- "hello"
1561+--
1562+-- >>> Stream.fold Fold.toList $ Stream.foldMany line input
1563+-- ["hello","there"]
1564+--
1565+{-# INLINE takeEndBy_ #-}
1566+takeEndBy_ :: Monad m => (a -> Bool) -> Fold m a b -> Fold m a b
1567+-- takeEndBy_ predicate = scanMaybe (takingEndBy_ predicate)
1568+takeEndBy_ predicate (Fold fstep finitial fextract ffinal) =
1569+ Fold step finitial fextract ffinal
1570+
1571+ where
1572+
1573+ step s a =
1574+ if not (predicate a)
1575+ then fstep s a
1576+ else Done <$> ffinal s
1577+
1578+-- Note:
1579+-- > Stream.splitWithSuffix p f = Stream.foldMany (Fold.takeEndBy p f)
1580+
1581+-- | Take the input, stop when the predicate succeeds taking the succeeding
1582+-- element as well.
1583+--
1584+-- Example:
1585+--
1586+-- >>> input = Stream.fromList "hello\nthere\n"
1587+-- >>> line = Fold.takeEndBy (== '\n') Fold.toList
1588+-- >>> Stream.fold line input
1589+-- "hello\n"
1590+--
1591+-- >>> Stream.fold Fold.toList $ Stream.foldMany line input
1592+-- ["hello\n","there\n"]
1593+--
1594+{-# INLINE takeEndBy #-}
1595+takeEndBy :: Monad m => (a -> Bool) -> Fold m a b -> Fold m a b
1596+-- takeEndBy predicate = scanMaybe (takingEndBy predicate)
1597+takeEndBy predicate (Fold fstep finitial fextract ffinal) =
1598+ Fold step finitial fextract ffinal
1599+
1600+ where
1601+
1602+ step s a = do
1603+ res <- fstep s a
1604+ if not (predicate a)
1605+ then return res
1606+ else do
1607+ case res of
1608+ Partial s1 -> Done <$> ffinal s1
1609+ Done b -> return $ Done b
1610+
15201611------------------------------------------------------------------------------
15211612-- Nesting
15221613------------------------------------------------------------------------------
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+151−35
@@ -11,81 +11,197 @@ module Streamly.Internal.Data.MutArray
1111 -- * MutArray.Type module
1212 module Streamly.Internal.Data.MutArray.Type
1313 -- * MutArray module
14− , splitOn
15− , genSlicesFromLen
16− , getSlicesFromLen
17− , fromStream
14+ , sliceIndexerFromLen
15+ , slicerFromLen
16+ , compactLE
17+ , pinnedCompactLE
18+ , compactOnByte
19+ , compactOnByteSuffix
1820 -- * Unboxed IORef
1921 , module Streamly.Internal.Data.IORef.Unboxed
22+
23+ -- * Deprecated
24+ , genSlicesFromLen
25+ , getSlicesFromLen
2026 )
2127where
2228
2329#include "inline.hs"
2430
2531import Control.Monad.IO.Class (MonadIO(..))
32+import Data.Word (Word8)
33+import Streamly.Internal.Data.MutByteArray.Type (PinnedState(..))
34+import Streamly.Internal.Data.Stream.Type (Stream)
2635import Streamly.Internal.Data.Unbox (Unbox)
27−import Streamly.Internal.Data.Stream (Stream)
2836import Streamly.Internal.Data.Unfold.Type (Unfold(..))
2937
30−import qualified Streamly.Internal.Data.Stream as D
38+import qualified Streamly.Internal.Data.Stream.Type as Stream
39+import qualified Streamly.Internal.Data.Stream.Nesting as Stream
40+-- import qualified Streamly.Internal.Data.Stream.Transform as Stream
3141import qualified Streamly.Internal.Data.Unfold as Unfold
3242
3343import Prelude hiding (foldr, length, read, splitAt)
3444import Streamly.Internal.Data.MutArray.Type
3545import Streamly.Internal.Data.IORef.Unboxed
3646
37−-- | Split the array into a stream of slices using a predicate. The element
38−-- matching the predicate is dropped.
39−--
40−-- /Pre-release/
41−{-# INLINE splitOn #-}
42−splitOn :: (MonadIO m, Unbox a) =>
43− (a -> Bool) -> MutArray a -> Stream m (MutArray a)
44−splitOn predicate arr =
45− fmap (\(i, len) -> getSliceUnsafe i len arr)
46− $ D.sliceOnSuffix predicate (read arr)
47−
4847-- | Generate a stream of array slice descriptors ((index, len)) of specified
4948-- length from an array, starting from the supplied array index. The last slice
5049-- may be shorter than the requested length depending on the array length.
5150--
5251-- /Pre-release/
53−{-# INLINE genSlicesFromLen #-}
54−genSlicesFromLen :: forall m a. (Monad m, Unbox a)
52+{-# INLINE sliceIndexerFromLen #-}
53+sliceIndexerFromLen :: forall m a. (Monad m, Unbox a)
5554 => Int -- ^ from index
5655 -> Int -- ^ length of the slice
5756 -> Unfold m (MutArray a) (Int, Int)
58−genSlicesFromLen from len =
57+sliceIndexerFromLen from len =
5958 let fromThenTo n = (from, from + len, n - 1)
6059 mkSlice n i = return (i, min len (n - i))
6160 in Unfold.lmap length
6261 $ Unfold.mapM2 mkSlice
6362 $ Unfold.lmap fromThenTo Unfold.enumerateFromThenTo
6463
64+{-# DEPRECATED genSlicesFromLen "Please use sliceIndexerFromLen instead." #-}
65+genSlicesFromLen :: forall m a. (Monad m, Unbox a)
66+ => Int -- ^ from index
67+ -> Int -- ^ length of the slice
68+ -> Unfold m (MutArray a) (Int, Int)
69+genSlicesFromLen = sliceIndexerFromLen
70+
6571-- | Generate a stream of slices of specified length from an array, starting
6672-- from the supplied array index. The last slice may be shorter than the
6773-- requested length depending on the array length.
6874--
6975-- /Pre-release/
70−{-# INLINE getSlicesFromLen #-}
71−getSlicesFromLen :: forall m a. (Monad m, Unbox a)
76+{-# INLINE slicerFromLen #-}
77+slicerFromLen :: forall m a. (Monad m, Unbox a)
7278 => Int -- ^ from index
7379 -> Int -- ^ length of the slice
7480 -> Unfold m (MutArray a) (MutArray a)
75−getSlicesFromLen from len =
81+slicerFromLen from len =
7682 let mkSlice arr (i, n) = return $ getSliceUnsafe i n arr
77− in Unfold.mapM2 mkSlice (genSlicesFromLen from len)
83+ in Unfold.mapM2 mkSlice (sliceIndexerFromLen from len)
7884
79−-- | Create an 'Array' from a stream. This is useful when we want to create a
80−-- single array from a stream of unknown size. 'writeN' is at least twice
81−-- as efficient when the size is already known.
85+{-# DEPRECATED getSlicesFromLen "Please use slicerFromLen instead." #-}
86+getSlicesFromLen :: forall m a. (Monad m, Unbox a)
87+ => Int -- ^ from index
88+ -> Int -- ^ length of the slice
89+ -> Unfold m (MutArray a) (MutArray a)
90+getSlicesFromLen = slicerFromLen
91+
92+-------------------------------------------------------------------------------
93+-- Compacting Streams of Arrays
94+-------------------------------------------------------------------------------
95+
96+-- | @compactLE maxElems@ coalesces adjacent arrays in the input stream
97+-- only if the combined size would be less than or equal to @maxElems@
98+-- elements. Note that it won't split an array if the original array is already
99+-- larger than maxElems.
82100--
83−-- Note that if the input stream is too large memory allocation for the array
84−-- may fail. When the stream size is not known, `chunksOf` followed by
85−-- processing of indvidual arrays in the resulting stream should be preferred.
101+-- @maxElems@ must be greater than 0.
86102--
87−-- /Pre-release/
88−{-# INLINE fromStream #-}
89−fromStream :: (MonadIO m, Unbox a) => Stream m a -> m (MutArray a)
90−fromStream = fromStreamD
91−-- fromStream (Stream m) = P.fold write m
103+-- Generates unpinned arrays irrespective of the pinning status of input
104+-- arrays.
105+{-# INLINE compactLE #-}
106+compactLE :: (MonadIO m, Unbox a) =>
107+ Int -> Stream m (MutArray a) -> Stream m (MutArray a)
108+-- XXX compactLE can be moved to MutArray/Type if we are not using the parser
109+-- to implement it.
110+compactLE = compactLeAs Unpinned
111+-- The parser version turns out to be a little bit slower.
112+-- compactLE n = Stream.catRights . Stream.parseManyD (pCompactLE n)
113+
114+-- | Pinned version of 'compactLE'.
115+{-# INLINE pinnedCompactLE #-}
116+pinnedCompactLE :: forall m a. (MonadIO m, Unbox a)
117+ => Int -> Stream m (MutArray a) -> Stream m (MutArray a)
118+pinnedCompactLE = compactLeAs Pinned
119+-- pinnedCompactLE n = Stream.catRights . Stream.parseManyD (pPinnedCompactLE n)
120+
121+data SplitState s arr
122+ = Initial s
123+ | Buffering s arr
124+ | Splitting s arr
125+ | Yielding arr (SplitState s arr)
126+ | Finishing
127+
128+-- | Split a stream of arrays on a given separator byte, dropping the separator
129+-- and coalescing all the arrays between two separators into a single array.
130+--
131+{-# INLINE_NORMAL _compactOnByteCustom #-}
132+_compactOnByteCustom
133+ :: MonadIO m
134+ => Word8
135+ -> Stream m (MutArray Word8)
136+ -> Stream m (MutArray Word8)
137+_compactOnByteCustom byte (Stream.Stream step state) =
138+ Stream.Stream step' (Initial state)
139+
140+ where
141+
142+ {-# INLINE_LATE step' #-}
143+ step' gst (Initial st) = do
144+ r <- step gst st
145+ case r of
146+ Stream.Yield arr s -> do
147+ (arr1, marr2) <- breakOn byte arr
148+ return $ case marr2 of
149+ Nothing -> Stream.Skip (Buffering s arr1)
150+ Just arr2 -> Stream.Skip (Yielding arr1 (Splitting s arr2))
151+ Stream.Skip s -> return $ Stream.Skip (Initial s)
152+ Stream.Stop -> return Stream.Stop
153+
154+ step' gst (Buffering st buf) = do
155+ r <- step gst st
156+ case r of
157+ Stream.Yield arr s -> do
158+ (arr1, marr2) <- breakOn byte arr
159+ -- XXX Use spliceExp instead and then rightSize?
160+ buf1 <- splice buf arr1
161+ return $ case marr2 of
162+ Nothing -> Stream.Skip (Buffering s buf1)
163+ Just x -> Stream.Skip (Yielding buf1 (Splitting s x))
164+ Stream.Skip s -> return $ Stream.Skip (Buffering s buf)
165+ Stream.Stop -> return $
166+ if byteLength buf == 0
167+ then Stream.Stop
168+ else Stream.Skip (Yielding buf Finishing)
169+
170+ step' _ (Splitting st buf) = do
171+ (arr1, marr2) <- breakOn byte buf
172+ return $ case marr2 of
173+ Nothing -> Stream.Skip $ Buffering st arr1
174+ Just arr2 -> Stream.Skip $ Yielding arr1 (Splitting st arr2)
175+
176+ step' _ (Yielding arr next) = return $ Stream.Yield arr next
177+ step' _ Finishing = return Stream.Stop
178+
179+-- XXX implement predicate based version of this
180+-- XXX Naming of predicate based vs custom version
181+
182+-- | Split a stream of arrays on a given separator byte, dropping the separator
183+-- and coalescing all the arrays between two separators into a single array.
184+--
185+{-# INLINE compactOnByte #-}
186+compactOnByte
187+ :: (MonadIO m)
188+ => Word8
189+ -> Stream m (MutArray Word8)
190+ -> Stream m (MutArray Word8)
191+-- XXX compare perf of custom vs idiomatic version
192+-- compactOnByte = _compactOnByteCustom
193+-- XXX use spliceExp and rightSize?
194+compactOnByte byte = Stream.splitInnerBy (breakOn byte) splice
195+
196+-- | Like 'compactOnByte' considers the separator in suffix position instead of
197+-- infix position.
198+{-# INLINE compactOnByteSuffix #-}
199+compactOnByteSuffix
200+ :: (MonadIO m)
201+ => Word8
202+ -> Stream m (MutArray Word8)
203+ -> Stream m (MutArray Word8)
204+compactOnByteSuffix byte =
205+ -- XXX use spliceExp and rightSize?
206+ Stream.splitInnerBySuffix
207+ (\arr -> byteLength arr == 0) (breakOn byte) splice
core/src/Streamly/Internal/Data/MutArray/Generic.hs+65−30
@@ -19,14 +19,14 @@ module Streamly.Internal.Data.MutArray.Generic
1919 , nil
2020
2121 -- *** Uninitialized Arrays
22− , new
22+ , emptyOf
2323 -- , newArrayWith
2424
2525 -- *** From streams
26− , writeNUnsafe
27− , writeN
28− , writeWith
29− , write
26+ , unsafeCreateOf
27+ , createOf
28+ , createWith -- createOfMin/createMin/createGE?
29+ , create
3030 , fromStreamN
3131 , fromStream
3232 , fromPureStream
@@ -160,6 +160,13 @@ module Streamly.Internal.Data.MutArray.Generic
160160 -- , appendSliceFrom
161161
162162 , clone
163+
164+ -- * Deprecated
165+ , new
166+ , writeNUnsafe
167+ , writeN
168+ , writeWith
169+ , write
163170 )
164171where
165172
@@ -236,13 +243,13 @@ bottomElement =
236243-- XXX Would be nice if GHC can provide something like newUninitializedArray# so
237244-- that we do not have to write undefined or error in the whole array.
238245
239−-- | @new count@ allocates a zero length array that can be extended to hold
246+-- | @emptyOf count@ allocates a zero length array that can be extended to hold
240247-- up to 'count' items without reallocating.
241248--
242249-- /Pre-release/
243−{-# INLINE new #-}
244−new :: MonadIO m => Int -> m (MutArray a)
245−new n@(I# n#) =
250+{-# INLINE emptyOf #-}
251+emptyOf :: MonadIO m => Int -> m (MutArray a)
252+emptyOf n@(I# n#) =
246253 liftIO
247254 $ IO
248255 $ \s# ->
@@ -251,6 +258,12 @@ new n@(I# n#) =
251258 let ma = MutArray arr# 0 0 n
252259 in (# s1#, ma #)
253260
261+-- XXX Deprecate in major
262+-- {-# DEPRECATED new "Please use emptyOf instead." #-}
263+{-# INLINE new #-}
264+new :: MonadIO m => Int -> m (MutArray a)
265+new = emptyOf
266+
254267-- XXX This could be pure?
255268
256269-- |
@@ -318,7 +331,7 @@ putIndices arr = FL.foldlM' step (return ())
318331
319332 where
320333
321− step () (i, x) = liftIO (putIndex i arr x)
334+ step () (i, x) = putIndex i arr x
322335
323336-- | Modify a given index of an array using a modifier function without checking
324337-- the bounds.
@@ -589,16 +602,16 @@ readRev arr@MutArray{..} =
589602arrayChunkSize :: Int
590603arrayChunkSize = 1024
591604
592−-- | Like 'writeN' but does not check the array bounds when writing. The fold
605+-- | Like 'createOf' but does not check the array bounds when writing. The fold
593606-- driver must not call the step function more than 'n' times otherwise it will
594607-- corrupt the memory and crash. This function exists mainly because any
595608-- conditional in the step function blocks fusion causing 10x performance
596609-- slowdown.
597610--
598611-- /Pre-release/
599−{-# INLINE_NORMAL writeNUnsafe #-}
600−writeNUnsafe :: MonadIO m => Int -> Fold m a (MutArray a)
601−writeNUnsafe n = Fold step initial return return
612+{-# INLINE_NORMAL unsafeCreateOf #-}
613+unsafeCreateOf :: MonadIO m => Int -> Fold m a (MutArray a)
614+unsafeCreateOf n = Fold step initial return return
602615
603616 where
604617
@@ -606,59 +619,81 @@ writeNUnsafe n = Fold step initial return return
606619
607620 step arr x = FL.Partial <$> snocUnsafe arr x
608621
609−-- | @writeN n@ folds a maximum of @n@ elements from the input stream to an
622+{-# DEPRECATED writeNUnsafe "Please use unsafeCreateOf instead." #-}
623+{-# INLINE writeNUnsafe #-}
624+writeNUnsafe :: MonadIO m => Int -> Fold m a (MutArray a)
625+writeNUnsafe = unsafeCreateOf
626+
627+-- | @createOf n@ folds a maximum of @n@ elements from the input stream to an
610628-- 'Array'.
611629--
612−-- >>> writeN n = Fold.take n (MutArray.writeNUnsafe n)
630+-- >>> createOf n = Fold.take n (MutArray.unsafeCreateOf n)
613631--
614632-- /Pre-release/
615−{-# INLINE_NORMAL writeN #-}
633+{-# INLINE_NORMAL createOf #-}
634+createOf :: MonadIO m => Int -> Fold m a (MutArray a)
635+createOf n = FL.take n $ unsafeCreateOf n
636+
637+-- XXX Deprecate in major
638+-- {-# DEPRECATED writeN "Please use createOf instead." #-}
639+{-# INLINE writeN #-}
616640writeN :: MonadIO m => Int -> Fold m a (MutArray a)
617−writeN n = FL.take n $ writeNUnsafe n
641+writeN = createOf
618642
619643-- >>> f n = MutArray.writeAppendWith (* 2) (MutArray.pinnedNew n)
620644-- >>> writeWith n = Fold.rmapM MutArray.rightSize (f n)
621645-- >>> writeWith n = Fold.rmapM MutArray.fromArrayStreamK (MutArray.writeChunks n)
622646
623−-- | @writeWith minCount@ folds the whole input to a single array. The array
647+-- | @createWith minCount@ folds the whole input to a single array. The array
624648-- starts at a size big enough to hold minCount elements, the size is doubled
625649-- every time the array needs to be grown.
626650--
627651-- /Caution! Do not use this on infinite streams./
628652--
629653-- /Pre-release/
630−{-# INLINE_NORMAL writeWith #-}
631−writeWith :: MonadIO m => Int -> Fold m a (MutArray a)
654+{-# INLINE_NORMAL createWith #-}
655+createWith :: MonadIO m => Int -> Fold m a (MutArray a)
632656-- writeWith n = FL.rmapM rightSize $ writeAppendWith (* 2) (pinnedNew n)
633−writeWith elemCount = FL.rmapM extract $ FL.foldlM' step initial
657+createWith elemCount = FL.rmapM extract $ FL.foldlM' step initial
634658
635659 where
636660
637661 initial = do
638− when (elemCount < 0) $ error "writeWith: elemCount is negative"
639− liftIO $ new elemCount
662+ when (elemCount < 0) $ error "createWith: elemCount is negative"
663+ new elemCount
640664
641665 step arr@(MutArray _ start end bound) x
642666 | end == bound = do
643667 let oldSize = end - start
644668 newSize = max (oldSize * 2) 1
645− arr1 <- liftIO $ realloc newSize arr
669+ arr1 <- realloc newSize arr
646670 snocUnsafe arr1 x
647671 step arr x = snocUnsafe arr x
648672
649− -- extract = liftIO . rightSize
673+ -- extract = rightSize
650674 extract = return
651675
676+{-# DEPRECATED writeWith "Please use createWith instead." #-}
677+{-# INLINE writeWith #-}
678+writeWith :: MonadIO m => Int -> Fold m a (MutArray a)
679+writeWith = createWith
680+
652681-- | Fold the whole input to a single array.
653682--
654−-- Same as 'writeWith' using an initial array size of 'arrayChunkSize' bytes
683+-- Same as 'createWith' using an initial array size of 'arrayChunkSize' bytes
655684-- rounded up to the element size.
656685--
657686-- /Caution! Do not use this on infinite streams./
658687--
688+{-# INLINE create #-}
689+create :: MonadIO m => Fold m a (MutArray a)
690+create = writeWith arrayChunkSize
691+
692+-- XXX Deprecate in major
693+-- {-# DEPRECATED write "Please use create instead." #-}
659694{-# INLINE write #-}
660695write :: MonadIO m => Fold m a (MutArray a)
661−write = writeWith arrayChunkSize
696+write = create
662697
663698-- | Create a 'MutArray' from the first @n@ elements of a stream. The
664699-- array is allocated to size @n@, if the stream terminates before @n@
@@ -683,7 +718,7 @@ fromList xs = fromStream $ D.fromList xs
683718{-# INLINABLE fromPureStream #-}
684719fromPureStream :: MonadIO m => Stream Identity a -> m (MutArray a)
685720fromPureStream xs =
686− liftIO $ D.fold write $ D.morphInner (return . runIdentity) xs
721+ D.fold write $ D.morphInner (return . runIdentity) xs
687722
688723-------------------------------------------------------------------------------
689724-- Chunking
@@ -815,7 +850,7 @@ putSliceUnsafe src srcStart dst dstStart len = liftIO $ do
815850
816851{-# INLINE clone #-}
817852clone :: MonadIO m => MutArray a -> m (MutArray a)
818−clone src = liftIO $ do
853+clone src = do
819854 let len = arrLen src
820855 dst <- new len
821856 putSliceUnsafe src 0 dst 0 len
core/src/Streamly/Internal/Data/MutArray/Stream.hs+11−195
@@ -12,6 +12,7 @@
1212-- keep this as a separate module and release it.
1313--
1414module Streamly.Internal.Data.MutArray.Stream
15+{-# DEPRECATED "Please use \"Streamly.Internal.Data.MutArray\" instead." #-}
1516 (
1617 -- * Generation
1718 MArray.chunksOf
@@ -21,7 +22,7 @@ module Streamly.Internal.Data.MutArray.Stream
2122
2223 -- * Compaction
2324 , packArraysChunksOf
24− , SpliceState (..)
25+ , MArray.SpliceState (..)
2526 , lpackArraysChunksOf
2627 , compact -- chunksCompact
2728 , compactLE
@@ -35,19 +36,12 @@ module Streamly.Internal.Data.MutArray.Stream
3536 )
3637where
3738
38−#include "inline.hs"
39−#include "ArrayMacros.h"
40−
4139import Control.Monad.IO.Class (MonadIO(..))
42−import Control.Monad (when)
43−import Data.Bifunctor (first)
44−import Data.Proxy (Proxy(..))
45−import Streamly.Internal.Data.Unbox (Unbox, sizeOf)
40+import Streamly.Internal.Data.Unbox (Unbox)
4641import Streamly.Internal.Data.MutArray.Type (MutArray(..))
4742import Streamly.Internal.Data.Fold.Type (Fold(..))
4843import Streamly.Internal.Data.Parser (ParseError)
4944import Streamly.Internal.Data.Stream.Type (Stream)
50−import Streamly.Internal.Data.Tuple.Strict (Tuple'(..))
5145
5246import qualified Streamly.Internal.Data.MutArray as MArray
5347import qualified Streamly.Internal.Data.Fold.Type as FL
@@ -58,12 +52,6 @@ import qualified Streamly.Internal.Data.Stream as D
5852-- Compact
5953-------------------------------------------------------------------------------
6054
61−data SpliceState s arr
62− = SpliceInitial s
63− | SpliceBuffering s arr
64− | SpliceYielding arr (SpliceState s arr)
65− | SpliceFinish
66−
6755-- XXX This can be removed once compactLEFold/compactLE are implemented.
6856--
6957-- | This mutates the first array (if it has space) to append values from the
@@ -78,117 +66,18 @@ data SpliceState s arr
7866-- actual array size may be less than the specified chunk size.
7967--
8068-- @since 0.7.0
81−{-# INLINE_NORMAL packArraysChunksOf #-}
69+{-# INLINE packArraysChunksOf #-}
8270packArraysChunksOf :: (MonadIO m, Unbox a)
8371 => Int -> D.Stream m (MutArray a) -> D.Stream m (MutArray a)
84−packArraysChunksOf n (D.Stream step state) =
85− D.Stream step' (SpliceInitial state)
86−
87− where
88−
89− {-# INLINE_LATE step' #-}
90− step' gst (SpliceInitial st) = do
91− when (n <= 0) $
92− -- XXX we can pass the module string from the higher level API
93− error $ "Streamly.Internal.Data.MutArray.Type.packArraysChunksOf: the size of "
94− ++ "arrays [" ++ show n ++ "] must be a natural number"
95− r <- step gst st
96− case r of
97− D.Yield arr s -> return $
98− let len = MArray.byteLength arr
99− in if len >= n
100− then D.Skip (SpliceYielding arr (SpliceInitial s))
101− else D.Skip (SpliceBuffering s arr)
102− D.Skip s -> return $ D.Skip (SpliceInitial s)
103− D.Stop -> return D.Stop
104−
105− step' gst (SpliceBuffering st buf) = do
106− r <- step gst st
107− case r of
108− D.Yield arr s -> do
109− let len = MArray.byteLength buf + MArray.byteLength arr
110− if len > n
111− then return $
112− D.Skip (SpliceYielding buf (SpliceBuffering s arr))
113− else do
114− buf' <- if MArray.byteCapacity buf < n
115− then liftIO $ MArray.realloc n buf
116− else return buf
117− buf'' <- MArray.splice buf' arr
118− return $ D.Skip (SpliceBuffering s buf'')
119− D.Skip s -> return $ D.Skip (SpliceBuffering s buf)
120− D.Stop -> return $ D.Skip (SpliceYielding buf SpliceFinish)
121−
122− step' _ SpliceFinish = return D.Stop
123−
124− step' _ (SpliceYielding arr next) = return $ D.Yield arr next
72+packArraysChunksOf = MArray.compactLE
12573
12674-- XXX Remove this once compactLEFold is implemented
12775-- lpackArraysChunksOf = Fold.many compactLEFold
12876--
129−{-# INLINE_NORMAL lpackArraysChunksOf #-}
77+{-# INLINE lpackArraysChunksOf #-}
13078lpackArraysChunksOf :: (MonadIO m, Unbox a)
13179 => Int -> Fold m (MutArray a) () -> Fold m (MutArray a) ()
132−lpackArraysChunksOf n (Fold step1 initial1 _ final1) =
133− Fold step initial extract final
134−
135− where
136−
137− initial = do
138− when (n <= 0) $
139− -- XXX we can pass the module string from the higher level API
140− error $ "Streamly.Internal.Data.MutArray.Type.packArraysChunksOf: the size of "
141− ++ "arrays [" ++ show n ++ "] must be a natural number"
142−
143− r <- initial1
144− return $ first (Tuple' Nothing) r
145−
146− step (Tuple' Nothing r1) arr =
147− let len = MArray.byteLength arr
148− in if len >= n
149− then do
150− r <- step1 r1 arr
151− case r of
152− FL.Done _ -> return $ FL.Done ()
153− FL.Partial s -> do
154− _ <- final1 s
155− res <- initial1
156− return $ first (Tuple' Nothing) res
157− else return $ FL.Partial $ Tuple' (Just arr) r1
158−
159− step (Tuple' (Just buf) r1) arr = do
160− let len = MArray.byteLength buf + MArray.byteLength arr
161− buf' <- if MArray.byteCapacity buf < len
162− then liftIO $ MArray.realloc (max n len) buf
163− else return buf
164− buf'' <- MArray.splice buf' arr
165−
166− -- XXX this is common in both the equations of step
167− if len >= n
168− then do
169− r <- step1 r1 buf''
170− case r of
171− FL.Done _ -> return $ FL.Done ()
172− FL.Partial s -> do
173− _ <- final1 s
174− res <- initial1
175− return $ first (Tuple' Nothing) res
176− else return $ FL.Partial $ Tuple' (Just buf'') r1
177−
178− -- XXX Several folds do extract >=> final, therefore, we need to make final
179− -- return "m b" rather than using extract post it if we want extract to be
180− -- partial.
181− --
182− -- extract forces the pending buffer to be sent to the fold which is not
183− -- what we want.
184− extract _ = error "lpackArraysChunksOf: not designed for scanning"
185−
186− final (Tuple' Nothing r1) = final1 r1
187− final (Tuple' (Just buf) r1) = do
188− r <- step1 r1 buf
189− case r of
190− FL.Partial rr -> final1 rr
191− FL.Done _ -> return ()
80+lpackArraysChunksOf = MArray.lCompactGE
19281
19382-- XXX Same as compactLE, to be removed once that is implemented.
19483--
@@ -208,48 +97,11 @@ compact = packArraysChunksOf
20897-- actual array size may be less than the specified chunk size.
20998--
21099-- /Internal/
211−{-# INLINE_NORMAL compactLEParserD #-}
100+{-# INLINE compactLEParserD #-}
212101compactLEParserD ::
213102 forall m a. (MonadIO m, Unbox a)
214103 => Int -> ParserD.Parser (MutArray a) m (MutArray a)
215−compactLEParserD n = ParserD.Parser step initial extract
216−
217− where
218−
219− nBytes = n * SIZE_OF(a)
220−
221− initial =
222− return
223− $ if n <= 0
224− then error
225− $ functionPath
226− ++ ": the size of arrays ["
227− ++ show n ++ "] must be a natural number"
228− else ParserD.IPartial Nothing
229−
230− step Nothing arr =
231− return
232− $ let len = MArray.byteLength arr
233− in if len >= nBytes
234− then ParserD.Done 0 arr
235− else ParserD.Partial 0 (Just arr)
236− step (Just buf) arr =
237− let len = MArray.byteLength buf + MArray.byteLength arr
238− in if len > nBytes
239− then return $ ParserD.Done 1 buf
240− else do
241− buf1 <-
242− if MArray.byteCapacity buf < nBytes
243− then liftIO $ MArray.realloc nBytes buf
244− else return buf
245− buf2 <- MArray.splice buf1 arr
246− return $ ParserD.Partial 0 (Just buf2)
247−
248− extract Nothing = return $ ParserD.Done 0 MArray.nil
249− extract (Just buf) = return $ ParserD.Done 0 buf
250−
251− functionPath =
252− "Streamly.Internal.Data.Stream.MutChunked.compactLEParserD"
104+compactLEParserD = MArray.pCompactLE
253105
254106-- | Coalesce adjacent arrays in incoming stream to form bigger arrays of a
255107-- minimum specified size. Note that if all the arrays in the stream together
@@ -258,47 +110,11 @@ compactLEParserD n = ParserD.Parser step initial extract
258110-- the specified size we stop coalescing further.
259111--
260112-- /Internal/
261−{-# INLINE_NORMAL compactGEFold #-}
113+{-# INLINE compactGEFold #-}
262114compactGEFold ::
263115 forall m a. (MonadIO m, Unbox a)
264116 => Int -> FL.Fold m (MutArray a) (MutArray a)
265−compactGEFold n = Fold step initial extract extract
266−
267− where
268−
269− nBytes = n * SIZE_OF(a)
270−
271− initial =
272− return
273− $ if n < 0
274− then error
275− $ functionPath
276− ++ ": the size of arrays ["
277− ++ show n ++ "] must be a natural number"
278− else FL.Partial Nothing
279−
280− step Nothing arr =
281− return
282− $ let len = MArray.byteLength arr
283− in if len >= nBytes
284− then FL.Done arr
285− else FL.Partial (Just arr)
286− step (Just buf) arr = do
287− let len = MArray.byteLength buf + MArray.byteLength arr
288− buf1 <-
289− if MArray.byteCapacity buf < len
290− then liftIO $ MArray.realloc (max len nBytes) buf
291− else return buf
292− buf2 <- MArray.splice buf1 arr
293− if len >= n
294− then return $ FL.Done buf2
295− else return $ FL.Partial (Just buf2)
296−
297− extract Nothing = return MArray.nil
298− extract (Just buf) = return buf
299−
300− functionPath =
301− "Streamly.Internal.Data.Stream.MutChunked.compactGEFold"
117+compactGEFold = MArray.fCompactGE
302118
303119-- | Coalesce adjacent arrays in incoming stream to form bigger arrays of a
304120-- maximum specified size in bytes.
core/src/Streamly/Internal/Data/MutArray/Type.hs+1178−339
@@ -31,39 +31,57 @@ module Streamly.Internal.Data.MutArray.Type
3131 -- ** Type
3232 -- $arrayNotes
3333 MutArray (..)
34− , MutByteArray
35− , MutableByteArray
34+
35+ -- ** Conversion
36+ -- *** Pinned and Unpinned
3637 , pin
3738 , unpin
3839 , isPinned
3940
40− -- -- * Constructing and Writing
41+ -- ** Casting
42+ , cast
43+ , castUnsafe -- XXX unsafeCast
44+ , asBytes
45+ , unsafePinnedAsPtr
46+ , unsafeAsPtr
47+
4148 -- ** Construction
42− , nil
49+ , empty
50+
51+ -- *** New
52+ -- | New arrays are always empty arrays with some reserve capacity to
53+ -- extend the length without reallocating.
54+ , emptyOf
55+ , newArrayWith -- emptyAlignedWith
56+ , pinnedEmptyOf
57+ , pinnedNewAligned -- XXX not required
58+ -- , new -- uninitialized array of specified length
59+
60+ -- *** Cloning
61+ , clone
62+ , pinnedClone
4363
44− -- *** Uninitialized Arrays
45− , pinnedNew
46− , pinnedNewBytes
47− , pinnedNewAligned
48− , new
49− , newArrayWith
64+ -- *** Slicing
65+ -- | Get a subarray without copying
66+ , getSliceUnsafe -- XXX unsafeGetSlice
67+ , getSlice
68+ , splitAt -- XXX should be able to express using getSlice
69+ , breakOn
5070
51− -- *** From streams
71+ -- *** Stream Folds
5272 , ArrayUnsafe (..)
53− , writeNWithUnsafe
54− , writeNWith
55− , writeNUnsafe
56− , pinnedWriteNUnsafe
57− , writeN
58− , pinnedWriteN
59− , pinnedWriteNAligned
60−
61− , writeWith
62− , write
63− , pinnedWrite
64−
65− , writeRevN
66− -- , writeRev
73+ , unsafeCreateOfWith
74+ , unsafeCreateOf
75+ , unsafePinnedCreateOf
76+ , pinnedCreateOf
77+ , createOfWith
78+ , createOf
79+ , revCreateOf
80+
81+ , pinnedCreate
82+ , createWith
83+ , create
84+ -- , revCreate
6785
6886 -- *** From containers
6987 , fromListN
@@ -72,13 +90,18 @@ module Streamly.Internal.Data.MutArray.Type
7290 , pinnedFromList
7391 , fromListRevN
7492 , fromListRev
75− , fromStreamDN
76− , fromStreamD
93+ , fromStreamN
94+ , fromStream
95+ , fromPureStreamN
7796 , fromPureStream
97+ , fromByteStr#
98+ , fromPtrN
99+ , fromChunksK
100+ , fromChunksRealloced -- fromSmallChunks
78101
79102 -- ** Random writes
80103 , putIndex
81− , putIndexUnsafe
104+ , putIndexUnsafe -- XXX unsafePutIndex
82105 , putIndices
83106 -- , putFromThenTo
84107 -- , putFrom -- start writing at the given position
@@ -86,77 +109,70 @@ module Streamly.Internal.Data.MutArray.Type
86109 -- , putFromTo
87110 -- , putFromRev
88111 -- , putUptoRev
89− , modifyIndexUnsafe
112+ , modifyIndexUnsafe -- XXX unsafeModifyIndex
90113 , modifyIndex
91114 , modifyIndices
92115 , modify
93116 , swapIndices
94117 , unsafeSwapIndices
95118
96− -- ** Growing and Shrinking
97− -- | Arrays grow only at the end, though it is possible to grow on both sides
98− -- and therefore have a cons as well as snoc. But that will require two
99− -- bounds in the array representation.
119+ -- ** Reading
100120
101− -- *** Appending elements
102− , snocWith
103− , snoc
104− , snocLinear
105− , snocMay
106− , snocUnsafe
107−
108− -- *** Appending streams
109− , writeAppendNUnsafe
110− , writeAppendN
111− , writeAppendWith
112− , writeAppend
113−
114− -- ** Eliminating and Reading
115−
116− -- *** To streams
117− , reader
118− , readerRevWith
119− , readerRev
121+ -- *** Indexing
122+ , getIndex
123+ , getIndexUnsafe -- XXX unsafeGetIndex
124+ -- , getFromThenTo
125+ , getIndexRev -- getRevIndex?
126+ , indexReader
127+ , indexReaderWith
120128
121− -- *** To containers
122− , toStreamDWith
123− , toStreamDRevWith
124− , toStreamKWith
125− , toStreamKRevWith
129+ -- *** To Streams
126130 , read
127131 , readRev
132+ , toStreamWith
133+ , toStreamRevWith
128134 , toStreamK
135+ , toStreamKWith
129136 , toStreamKRev
137+ , toStreamKRevWith
138+
139+ -- *** To Containers
130140 , toList
131141
142+ -- *** Unfolds
132143 -- experimental
133144 , producerWith
134145 , producer
135146
136− -- *** Random reads
137− , getIndex
138− , getIndexUnsafe
139− , getIndices
140− , getIndicesD
141− -- , getFromThenTo
142− , getIndexRev
143−
144− -- ** Memory Management
145− , blockSize
146− , arrayChunkBytes
147− , allocBytesToElemCount
148− , realloc
149− , resize
150− , resizeExp
151− , rightSize
147+ , reader
148+ , readerRevWith
149+ , readerRev
152150
153− -- ** Size
151+ -- ** Size and Capacity
152+ -- *** Size
154153 , length
155154 , byteLength
155+
156+ -- *** Capacity
156157 -- , capacity
157158 , byteCapacity
158159 , bytesFree
159160
161+ -- *** Capacity Management
162+ , blockSize
163+ , arrayChunkBytes
164+ , allocBytesToElemCount
165+ , realloc -- this is "resize"
166+ , grow
167+ , growExp
168+ , rightSize
169+
170+ -- ** Folding
171+ , foldl'
172+ , foldr
173+ , byteCmp
174+ , byteEq
175+
160176 -- ** In-place Mutation Algorithms
161177 , strip
162178 , reverse
@@ -167,62 +183,121 @@ module Streamly.Internal.Data.MutArray.Type
167183 , mergeBy
168184 , bubble
169185
170− -- ** Casting
171− , cast
172− , castUnsafe
173− , asBytes
174− , asPtrUnsafe
186+ -- ** Growing and Shrinking
187+ -- | Arrays grow only at the end, though it is possible to grow on both
188+ -- sides and therefore have a cons as well as snoc. But that will require
189+ -- both lower and upper bound in the array representation.
175190
176− -- ** Folding
177− , foldl'
178− , foldr
179− , cmp
191+ -- *** Appending elements
192+ , snocWith
193+ , snoc
194+ , snocLinear
195+ , snocMay
196+ , snocUnsafe -- XXX unsafeSnoc
197+
198+ -- *** Appending streams
199+ , unsafeAppendN
200+ , appendN
201+ , appendWith
202+ , append
203+
204+ -- *** Appending arrays
205+ , spliceCopy
206+ , spliceWith
207+ , splice
208+ , spliceExp
209+ , spliceUnsafe -- XXX unsafeSplice
210+ -- , putSlice
211+ -- , appendSlice
212+ -- , appendSliceFrom
213+
214+ -- ** Serialization using Unbox
215+ , pokeAppend
216+ , pokeAppendMay
217+ , pokeSkipUnsafe -- unsafePokeSkip
180218
181− -- ** Arrays of arrays
219+ -- ** Deserialization using Unbox
220+ , peekUncons
221+ , peekUnconsUnsafe -- unsafePeekUncons
222+ , peekSkipUnsafe -- unsafePeekSkip
223+
224+ -- Arrays of arrays
182225 -- We can add dimensionality parameter to the array type to get
183226 -- multidimensional arrays. Multidimensional arrays would just be a
184227 -- convenience wrapper on top of single dimensional arrays.
185228
186− -- | Operations dealing with multiple arrays, streams of arrays or
187− -- multidimensional array representations.
188−
189− -- *** Construct from streams
229+ -- ** Streams of Arrays
230+ -- *** Chunk
231+ -- | Group a stream into arrays.
190232 , chunksOf
191233 , pinnedChunksOf
192− , writeChunks
234+ , buildChunks
193235
194− -- *** Eliminate to streams
195− , flattenArrays
196− , flattenArraysRev
197− , fromArrayStreamK
236+ -- *** Split
237+ -- | Split an array into slices.
198238
199− -- *** Construct from arrays
200− -- get chunks without copying
201− , getSliceUnsafe
202− , getSlice
203239 -- , getSlicesFromLenN
204− , splitAt -- XXX should be able to express using getSlice
205− , breakOn
206−
207− -- ** Cloning arrays
208− , clone
209− , pinnedClone
210−
211− -- ** Appending arrays
212− , spliceCopy
213− , spliceWith
214− , splice
215− , spliceExp
216− , spliceUnsafe
217− -- , putSlice
218− -- , appendSlice
219− -- , appendSliceFrom
240+ , splitOn
241+ -- , slicesOf
242+
243+ -- *** Concat
244+ -- | Append the arrays in a stream to form a stream of elements.
245+ , concatWith
246+ , concatRevWith
247+ , concat
248+ , concatRev
249+
250+ -- *** Compact
251+ -- | Append the arrays in a stream to form a stream of larger arrays.
252+ , SpliceState (..)
253+ , pCompactLE
254+ , pPinnedCompactLE
255+ , compactLeAs
256+ , fCompactGE
257+ , fPinnedCompactGE
258+ , lCompactGE
259+ , lPinnedCompactGE
260+ , compactGE
261+ , compactEQ
220262
221263 -- ** Utilities
222264 , roundUpToPower2
223265 , memcpy
224266 , memcmp
225267 , c_memchr
268+
269+ -- * Deprecated
270+ , asPtrUnsafe
271+ , writeChunks
272+ , flattenArrays
273+ , flattenArraysRev
274+ , fromArrayStreamK
275+ , fromStreamDN
276+ , fromStreamD
277+ , cmp
278+ , getIndices
279+ , getIndicesWith
280+ , resize
281+ , resizeExp
282+ , nil
283+ , new
284+ , pinnedNew
285+ , pinnedNewBytes
286+ , writeAppendNUnsafe
287+ , writeAppendN
288+ , writeAppendWith
289+ , writeAppend
290+ , writeNWithUnsafe
291+ , writeNWith
292+ , writeNUnsafe
293+ , pinnedWriteNUnsafe
294+ , writeN
295+ , pinnedWriteN
296+ , pinnedWriteNAligned -- XXX not required
297+ , writeWith
298+ , write
299+ , pinnedWrite
300+ , writeRevN
226301 )
227302where
228303
@@ -233,6 +308,7 @@ where
233308
234309import Control.Monad (when, void)
235310import Control.Monad.IO.Class (MonadIO(..))
311+import Data.Bifunctor (first)
236312import Data.Bits (shiftR, (.|.), (.&.))
237313import Data.Functor.Identity (Identity(..))
238314import Data.Proxy (Proxy(..))
@@ -241,7 +317,6 @@ import Foreign.C.Types (CSize(..), CInt(..))
241317import Foreign.Ptr (plusPtr, minusPtr, nullPtr)
242318import Streamly.Internal.Data.MutByteArray.Type
243319 ( MutByteArray(..)
244− , MutableByteArray
245320 , PinnedState(..)
246321 , getMutableByteArray#
247322 , putSliceUnsafe
@@ -254,19 +329,23 @@ import GHC.Base
254329 , copyMutableByteArray#
255330 )
256331import GHC.Base (noinline)
257−import GHC.Exts (unsafeCoerce#)
332+import GHC.Exts (unsafeCoerce#, Addr#)
258333import GHC.Ptr (Ptr(..))
259334
260335import Streamly.Internal.Data.Fold.Type (Fold(..))
261336import Streamly.Internal.Data.Producer.Type (Producer (..))
262337import Streamly.Internal.Data.Stream.Type (Stream)
338+import Streamly.Internal.Data.Parser.Type (Parser (..))
263339import Streamly.Internal.Data.StreamK.Type (StreamK)
264340import Streamly.Internal.Data.SVar.Type (adaptState, defState)
341+import Streamly.Internal.Data.Tuple.Strict (Tuple'(..))
265342import Streamly.Internal.Data.Unfold.Type (Unfold(..))
266343import Streamly.Internal.System.IO (arrayPayloadSize, defaultChunkSize)
267344
268345import qualified Streamly.Internal.Data.Fold.Type as FL
269346import qualified Streamly.Internal.Data.MutByteArray.Type as Unboxed
347+import qualified Streamly.Internal.Data.Parser.Type as Parser
348+-- import qualified Streamly.Internal.Data.Fold.Type as Fold
270349import qualified Streamly.Internal.Data.Producer as Producer
271350import qualified Streamly.Internal.Data.Stream.Type as D
272351import qualified Streamly.Internal.Data.Stream.Lift as D
@@ -274,7 +353,7 @@ import qualified Streamly.Internal.Data.StreamK.Type as K
274353import qualified Prelude
275354
276355import Prelude hiding
277− (Foldable(..), read, unlines, splitAt, reverse, truncate)
356+ (Foldable(..), concat, read, unlines, splitAt, reverse, truncate)
278357
279358#include "DocTestDataMutArray.hs"
280359
@@ -282,6 +361,7 @@ import Prelude hiding
282361-- Foreign helpers
283362-------------------------------------------------------------------------------
284363
364+-- NOTE: Have to be "ccall unsafe" so that we can pass unpinned memory to these
285365foreign import ccall unsafe "string.h memcpy" c_memcpy
286366 :: Ptr Word8 -> Ptr Word8 -> CSize -> IO (Ptr Word8)
287367
@@ -291,6 +371,9 @@ foreign import ccall unsafe "string.h memchr" c_memchr
291371foreign import ccall unsafe "string.h memcmp" c_memcmp
292372 :: Ptr Word8 -> Ptr Word8 -> CSize -> IO CInt
293373
374+foreign import ccall unsafe "string.h strlen" c_strlen
375+ :: Ptr Word8 -> IO CSize
376+
294377-- | Given an 'Unboxed' type (unused first arg) and a number of bytes, return
295378-- how many elements of that type will completely fit in those bytes.
296379--
@@ -396,6 +479,8 @@ isPinned MutArray{..} = Unboxed.isPinned arrContents
396479-- we need more than that. See stg_pinnedNewAlignedByteArrayzh and
397480-- allocatePinned in GHC source.
398481
482+-- XXX Rename to emptyAlignedWith, alignSize should be first arg.
483+
399484-- | @newArrayWith allocator alignment count@ allocates a new array of zero
400485-- length and with a capacity to hold @count@ elements, using @allocator
401486-- size alignment@ as the memory allocator function.
@@ -419,12 +504,25 @@ newArrayWith alloc alignSize count = do
419504 , arrBound = size
420505 }
421506
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." #-}
422520nil ::
423521#ifdef DEVBUILD
424522 Unbox a =>
425523#endif
426524 MutArray a
427−nil = MutArray Unboxed.nil 0 0 0
525+nil = empty
428526
429527{-# INLINE newBytesAs #-}
430528newBytesAs :: MonadIO m =>
@@ -441,12 +539,13 @@ newBytesAs ps bytes = do
441539 , arrBound = bytes
442540 }
443541
444−-- | Allocates a pinned empty array that can hold 'count' items. The memory of
445−-- the array is uninitialized and the allocation is aligned as per the
446−-- '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.
447545--
448546-- /Pre-release/
449547{-# INLINE pinnedNewBytes #-}
548+{-# DEPRECATED pinnedNewBytes "Please use pinnedEmptyOf with appropriate calculation" #-}
450549pinnedNewBytes :: MonadIO m =>
451550#ifdef DEVBUILD
452551 Unbox a =>
@@ -471,21 +570,31 @@ newAs ps =
471570 (error "new: alignment is not used in unpinned arrays.")
472571
473572-- XXX can unaligned allocation be more efficient when alignment is not needed?
474−--
475−-- | Allocates an empty pinned array that can hold 'count' items. The memory of
476−-- the array is uninitialized and the allocation is aligned as per the 'Unboxed'
477−-- instance of the type.
478−--
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." #-}
479582{-# INLINE pinnedNew #-}
480583pinnedNew :: forall m a. (MonadIO m, Unbox a) => Int -> m (MutArray a)
481−pinnedNew = newAs Pinned
584+pinnedNew = pinnedEmptyOf
482585
483−-- | Allocates an empty unpinned array that can hold 'count' items. The memory
484−-- of the array is uninitialized.
586+-- | Allocates an unpinned array of zero length but growable to the specified
587+-- capacity without reallocation.
485588--
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." #-}
486595{-# INLINE new #-}
487596new :: (MonadIO m, Unbox a) => Int -> m (MutArray a)
488−new = newAs Unpinned
597+new = emptyOf
489598
490599-------------------------------------------------------------------------------
491600-- Random writes
@@ -533,7 +642,7 @@ putIndices arr = FL.foldlM' step (return ())
533642
534643 where
535644
536− step () (i, x) = liftIO (putIndex i arr x)
645+ step () (i, x) = putIndex i arr x
537646
538647-- | Modify a given index of an array using a modifier function.
539648--
@@ -729,22 +838,19 @@ arrayChunkBytes = 1024
729838roundDownTo :: Int -> Int -> Int
730839roundDownTo elemSize size = size - (size `mod` elemSize)
731840
732−-- XXX See if resizing can be implemented by reading the old array as a stream
733−-- and then using writeN to the new array.
734−--
735841-- NOTE: we are passing elemSize explicitly to avoid an Unboxed constraint.
736842-- Since this is not inlined Unboxed consrraint leads to dictionary passing
737843-- which complicates some inspection tests.
738844--
739−{-# NOINLINE reallocExplicit #-}
740−reallocExplicit :: Int -> Int -> MutArray a -> IO (MutArray a)
741−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
742848 assertM(arrEnd <= arrBound)
743849
744850 -- Allocate new array
745851 let newCapMaxInBytes = roundUpLargeArray newCapacityInBytes
746852 contents <-
747− if Unboxed.isPinned arrContents
853+ if ps == Pinned
748854 then Unboxed.pinnedNew newCapMaxInBytes
749855 else Unboxed.new newCapMaxInBytes
750856 let !(MutByteArray mbarrFrom#) = arrContents
@@ -769,6 +875,10 @@ reallocExplicit elemSize newCapacityInBytes MutArray{..} = do
769875 , arrBound = newCapInBytes
770876 }
771877
878+-- XXX Should these be called reallocBytes etc?
879+-- XXX We may also need reallocAs to allocate as pinned/unpinned explicitly. In
880+-- fact clone/pinnedClone can be implemented using reallocAs.
881+
772882-- | @realloc newCapacity array@ reallocates the array to the specified
773883-- capacity in bytes.
774884--
@@ -780,7 +890,12 @@ reallocExplicit elemSize newCapacityInBytes MutArray{..} = do
780890-- If the original array is pinned, the newly allocated array is also pinned.
781891{-# INLINABLE realloc #-}
782892realloc :: forall m a. (MonadIO m, Unbox a) => Int -> MutArray a -> m (MutArray a)
783−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
784899
785900-- | @reallocWith label capSizer minIncrBytes array@. The label is used
786901-- in error messages and the capSizer is used to determine the capacity of the
@@ -803,14 +918,14 @@ reallocWith label capSizer minIncrBytes arr = do
803918 where
804919
805920 badSize newSize =
806− concat
921+ Prelude.concat
807922 [ label
808923 , ": new array size (in bytes) is less than required size "
809924 , show newSize
810925 , ". Please check the sizing function passed."
811926 ]
812927
813−-- | @resize newCapacity array@ changes the total capacity of the array so that
928+-- | @grow newCapacity array@ changes the total capacity of the array so that
814929-- it is enough to hold the specified number of elements. Nothing is done if
815930-- the specified capacity is less than the length of the array.
816931--
@@ -818,34 +933,46 @@ reallocWith label capSizer minIncrBytes arr = do
818933-- the block size (4K).
819934--
820935-- /Pre-release/
821−{-# INLINE resize #-}
822−resize :: forall m a. (MonadIO m, Unbox a) =>
936+{-# INLINE grow #-}
937+grow :: forall m a. (MonadIO m, Unbox a) =>
823938 Int -> MutArray a -> m (MutArray a)
824−resize nElems arr@MutArray{..} = do
939+grow nElems arr@MutArray{..} = do
825940 let req = SIZE_OF(a) * nElems
826− len = arrEnd - arrStart
827− if req < len
941+ cap = arrBound - arrStart
942+ if req < cap
828943 then return arr
829944 else realloc req arr
830945
831−-- | Like 'resize' but if the byte capacity is more than 'largeObjectThreshold'
832−-- then it is rounded up to the closest power of 2.
946+{-# DEPRECATED resize "Please use grow instead." #-}
947+{-# INLINE resize #-}
948+resize :: forall m a. (MonadIO m, Unbox a) =>
949+ Int -> MutArray a -> m (MutArray a)
950+resize = grow
951+
952+-- | Like 'grow' but if the requested byte capacity is more than
953+-- 'largeObjectThreshold' then it is rounded up to the closest power of 2.
833954--
834955-- /Pre-release/
835−{-# INLINE resizeExp #-}
836−resizeExp :: forall m a. (MonadIO m, Unbox a) =>
956+{-# INLINE growExp #-}
957+growExp :: forall m a. (MonadIO m, Unbox a) =>
837958 Int -> MutArray a -> m (MutArray a)
838−resizeExp nElems arr@MutArray{..} = do
959+growExp nElems arr@MutArray{..} = do
839960 let req = roundUpLargeArray (SIZE_OF(a) * nElems)
840961 req1 =
841962 if req > largeObjectThreshold
842963 then roundUpToPower2 req
843964 else req
844− len = arrEnd - arrStart
845− if req1 < len
965+ cap = arrBound - arrStart
966+ if req1 < cap
846967 then return arr
847968 else realloc req1 arr
848969
970+{-# DEPRECATED resizeExp "Please use growExp instead." #-}
971+{-# INLINE resizeExp #-}
972+resizeExp :: forall m a. (MonadIO m, Unbox a) =>
973+ Int -> MutArray a -> m (MutArray a)
974+resizeExp = growExp
975+
849976-- | Resize the allocated memory to drop any reserved free space at the end of
850977-- the array and reallocate it to reduce wastage.
851978--
@@ -910,7 +1037,7 @@ snocUnsafe arr@MutArray{..} = snocNewEnd (INDEX_NEXT(arrEnd,a)) arr
9101037{-# INLINE snocMay #-}
9111038snocMay :: forall m a. (MonadIO m, Unbox a) =>
9121039 MutArray a -> a -> m (Maybe (MutArray a))
913−snocMay arr@MutArray{..} x = liftIO $ do
1040+snocMay arr@MutArray{..} x = do
9141041 let newEnd = INDEX_NEXT(arrEnd,a)
9151042 if newEnd <= arrBound
9161043 then Just <$> snocNewEnd newEnd arr x
@@ -924,7 +1051,7 @@ snocWithRealloc :: forall m a. (MonadIO m, Unbox a) =>
9241051 -> a
9251052 -> m (MutArray a)
9261053snocWithRealloc sizer arr x = do
927− arr1 <- liftIO $ reallocWith "snocWith" sizer (SIZE_OF(a)) arr
1054+ arr1 <- reallocWith "snocWith" sizer (SIZE_OF(a)) arr
9281055 snocUnsafe arr1 x
9291056
9301057-- | @snocWith sizer arr elem@ mutates @arr@ to append @elem@. The length of
@@ -946,7 +1073,7 @@ snocWith :: forall m a. (MonadIO m, Unbox a) =>
9461073 -> MutArray a
9471074 -> a
9481075 -> m (MutArray a)
949−snocWith allocSize arr x = liftIO $ do
1076+snocWith allocSize arr x = do
9501077 let newEnd = INDEX_NEXT(arrEnd arr,a)
9511078 if newEnd <= arrBound arr
9521079 then snocNewEnd newEnd arr x
@@ -990,6 +1117,126 @@ snoc = snocWith f
9901117 then oldSize * 2
9911118 else roundUpToPower2 oldSize * 2
9921119
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+
9931240-------------------------------------------------------------------------------
9941241-- Random reads
9951242-------------------------------------------------------------------------------
@@ -1032,15 +1279,10 @@ getIndexRev i MutArray{..} = do
10321279data GetIndicesState contents start end st =
10331280 GetIndicesState contents start end st
10341281
1035−-- | Given an unfold that generates array indices, read the elements on those
1036−-- indices from the supplied MutArray. An error is thrown if an index is out of
1037−-- bounds.
1038−--
1039−-- /Pre-release/
1040−{-# INLINE getIndicesD #-}
1041−getIndicesD :: (Monad m, Unbox a) =>
1282+{-# INLINE indexReaderWith #-}
1283+indexReaderWith :: (Monad m, Unbox a) =>
10421284 (forall b. IO b -> m b) -> D.Stream m Int -> Unfold m (MutArray a) a
1043−getIndicesD liftio (D.Stream stepi sti) = Unfold step inject
1285+indexReaderWith liftio (D.Stream stepi sti) = Unfold step inject
10441286
10451287 where
10461288
@@ -1059,9 +1301,26 @@ getIndicesD liftio (D.Stream stepi sti) = Unfold step inject
10591301 D.Skip s -> return $ D.Skip (GetIndicesState contents start end s)
10601302 D.Stop -> return D.Stop
10611303
1304+{-# DEPRECATED getIndicesWith "Please use indexReaderWith instead." #-}
1305+{-# INLINE getIndicesWith #-}
1306+getIndicesWith :: (Monad m, Unbox a) =>
1307+ (forall b. IO b -> m b) -> D.Stream m Int -> Unfold m (MutArray a) a
1308+getIndicesWith = indexReaderWith
1309+
1310+-- | Given an unfold that generates array indices, read the elements on those
1311+-- indices from the supplied MutArray. An error is thrown if an index is out of
1312+-- bounds.
1313+--
1314+-- /Pre-release/
1315+{-# INLINE indexReader #-}
1316+indexReader :: (MonadIO m, Unbox a) => Stream m Int -> Unfold m (MutArray a) a
1317+indexReader = indexReaderWith liftIO
1318+
1319+-- XXX DO NOT REMOVE, change the signature to use Stream instead of unfold
1320+{-# DEPRECATED getIndices "Please use indexReader instead." #-}
10621321{-# INLINE getIndices #-}
10631322getIndices :: (MonadIO m, Unbox a) => Stream m Int -> Unfold m (MutArray a) a
1064−getIndices = getIndicesD liftIO
1323+getIndices = indexReader
10651324
10661325-------------------------------------------------------------------------------
10671326-- Subarrays
@@ -1356,7 +1615,7 @@ chunksOfAs ps n (D.Stream step state) =
13561615--
13571616-- Same as the following but may be more efficient:
13581617--
1359−-- >>> chunksOf n = Stream.foldMany (MutArray.writeN n)
1618+-- >>> chunksOf n = Stream.foldMany (MutArray.createOf n)
13601619--
13611620-- /Pre-release/
13621621{-# INLINE_NORMAL chunksOf #-}
@@ -1365,17 +1624,29 @@ chunksOf :: forall m a. (MonadIO m, Unbox a)
13651624-- XXX the idiomatic implementation leads to large regression in the D.reverse'
13661625-- benchmark. It seems it has difficulty producing optimized code when
13671626-- converting to StreamK. Investigate GHC optimizations.
1368−-- chunksOf n = D.foldMany (writeN n)
1627+-- chunksOf n = D.foldMany (createOf n)
13691628chunksOf = chunksOfAs Unpinned
13701629
13711630-- | Like 'chunksOf' but creates pinned arrays.
13721631{-# INLINE_NORMAL pinnedChunksOf #-}
13731632pinnedChunksOf :: forall m a. (MonadIO m, Unbox a)
13741633 => Int -> D.Stream m a -> D.Stream m (MutArray a)
1375−-- pinnedChunksOf n = D.foldMany (pinnedWriteN n)
1634+-- pinnedChunksOf n = D.foldMany (pinnedCreateOf n)
13761635pinnedChunksOf = chunksOfAs Pinned
13771636
1378−-- XXX This should take a PinnedState
1637+-- | When we are buffering a stream of unknown size into an array we do not
1638+-- know how much space to pre-allocate. So we start with the min size and emit
1639+-- the array then keep on doubling the size every time. Thus we do not need to
1640+-- guess the optimum chunk size.
1641+--
1642+-- We can incorporate this in chunksOfAs if the additional size parameter does
1643+-- not impact perf.
1644+--
1645+{-# INLINE _chunksOfRange #-}
1646+_chunksOfRange :: -- (MonadIO m, Unbox a) =>
1647+ PinnedState -> Int -> Int -> D.Stream m a -> D.Stream m (MutArray a)
1648+_chunksOfRange _ps _low _hi = undefined
1649+
13791650-- XXX buffer to a list instead?
13801651-- | Buffer the stream into arrays in memory.
13811652{-# INLINE arrayStreamKFromStreamDAs #-}
@@ -1393,16 +1664,10 @@ data FlattenState s contents a =
13931664 OuterLoop s
13941665 | InnerLoop s contents !Int !Int
13951666
1396−-- | Use the "reader" unfold instead.
1397−--
1398−-- @flattenArrays = unfoldMany reader@
1399−--
1400−-- We can try this if there are any fusion issues in the unfold.
1401−--
1402−{-# INLINE_NORMAL flattenArrays #-}
1403−flattenArrays :: forall m a. (MonadIO m, Unbox a)
1404− => D.Stream m (MutArray a) -> D.Stream m a
1405−flattenArrays (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)
14061671
14071672 where
14081673
@@ -1419,19 +1684,30 @@ flattenArrays (D.Stream step state) = D.Stream step' (OuterLoop state)
14191684 return $ D.Skip $ OuterLoop st
14201685
14211686 step' _ (InnerLoop st contents p end) = do
1422− x <- liftIO $ peekAt p contents
1687+ x <- liftio $ peekAt p contents
14231688 return $ D.Yield x (InnerLoop st contents (INDEX_NEXT(p,a)) end)
14241689
1425−-- | Use the "readerRev" unfold instead.
1690+-- | Use the "reader" unfold instead.
14261691--
1427−-- @flattenArrays = unfoldMany readerRev@
1692+-- @concat = unfoldMany reader@
14281693--
14291694-- We can try this if there are any fusion issues in the unfold.
14301695--
1431−{-# INLINE_NORMAL flattenArraysRev #-}
1432−flattenArraysRev :: 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)
14331704 => D.Stream m (MutArray a) -> D.Stream m a
1434−flattenArraysRev (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)
14351711
14361712 where
14371713
@@ -1449,10 +1725,27 @@ flattenArraysRev (D.Stream step state) = D.Stream step' (OuterLoop state)
14491725 return $ D.Skip $ OuterLoop st
14501726
14511727 step' _ (InnerLoop st contents p start) = do
1452− x <- liftIO $ peekAt p contents
1728+ x <- liftio $ peekAt p contents
14531729 let cur = INDEX_PREV(p,a)
14541730 return $ D.Yield x (InnerLoop st contents cur start)
14551731
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+
1743+{-# DEPRECATED flattenArraysRev "Please use \"unfoldMany readerRev\" instead." #-}
1744+{-# INLINE flattenArraysRev #-}
1745+flattenArraysRev :: forall m a. (MonadIO m, Unbox a)
1746+ => D.Stream m (MutArray a) -> D.Stream m a
1747+flattenArraysRev = concatRev
1748+
14561749-------------------------------------------------------------------------------
14571750-- Unfolds
14581751-------------------------------------------------------------------------------
@@ -1570,11 +1863,11 @@ toList MutArray{..} = liftIO $ go arrStart
15701863 x <- peekAt p arrContents
15711864 (:) x <$> go (INDEX_NEXT(p,a))
15721865
1573−{-# INLINE_NORMAL toStreamDWith #-}
1574−toStreamDWith ::
1866+{-# INLINE_NORMAL toStreamWith #-}
1867+toStreamWith ::
15751868 forall m a. (Monad m, Unbox a)
15761869 => (forall b. IO b -> m b) -> MutArray a -> D.Stream m a
1577−toStreamDWith liftio MutArray{..} = D.Stream step arrStart
1870+toStreamWith liftio MutArray{..} = D.Stream step arrStart
15781871
15791872 where
15801873
@@ -1590,7 +1883,7 @@ toStreamDWith liftio MutArray{..} = D.Stream step arrStart
15901883--
15911884{-# INLINE_NORMAL read #-}
15921885read :: forall m a. (MonadIO m, Unbox a) => MutArray a -> D.Stream m a
1593−read = toStreamDWith liftIO
1886+read = toStreamWith liftIO
15941887
15951888{-# INLINE toStreamKWith #-}
15961889toStreamKWith ::
@@ -1609,11 +1902,11 @@ toStreamKWith liftio MutArray{..} = go arrStart
16091902toStreamK :: forall m a. (MonadIO m, Unbox a) => MutArray a -> StreamK m a
16101903toStreamK = toStreamKWith liftIO
16111904
1612−{-# INLINE_NORMAL toStreamDRevWith #-}
1613−toStreamDRevWith ::
1905+{-# INLINE_NORMAL toStreamRevWith #-}
1906+toStreamRevWith ::
16141907 forall m a. (Monad m, Unbox a)
16151908 => (forall b. IO b -> m b) -> MutArray a -> D.Stream m a
1616−toStreamDRevWith liftio MutArray{..} =
1909+toStreamRevWith liftio MutArray{..} =
16171910 let p = INDEX_PREV(arrEnd,a)
16181911 in D.Stream step p
16191912
@@ -1631,7 +1924,7 @@ toStreamDRevWith liftio MutArray{..} =
16311924--
16321925{-# INLINE_NORMAL readRev #-}
16331926readRev :: forall m a. (MonadIO m, Unbox a) => MutArray a -> D.Stream m a
1634−readRev = toStreamDRevWith liftIO
1927+readRev = toStreamRevWith liftIO
16351928
16361929{-# INLINE toStreamKRevWith #-}
16371930toStreamKRevWith ::
@@ -1680,7 +1973,7 @@ foldr f z arr = D.foldr f z $ read arr
16801973
16811974-- XXX Keep the bound intact to not lose any free space? Perf impact?
16821975
1683−-- | @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
16841977-- array.
16851978--
16861979-- Unsafe: Do not drive the fold beyond @n@ elements, it will lead to memory
@@ -1689,13 +1982,12 @@ foldr f z arr = D.foldr f z $ read arr
16891982-- Any free space left in the array after appending @n@ elements is lost.
16901983--
16911984-- /Internal/
1692−{-# INLINE_NORMAL writeAppendNUnsafe #-}
1693−writeAppendNUnsafe :: forall m a. (MonadIO m, Unbox a) =>
1985+{-# INLINE_NORMAL unsafeAppendN #-}
1986+unsafeAppendN :: forall m a. (MonadIO m, Unbox a) =>
16941987 Int
16951988 -> m (MutArray a)
16961989 -> Fold m a (MutArray a)
1697−writeAppendNUnsafe n action =
1698− fmap fromArrayUnsafe $ FL.foldlM' step initial
1990+unsafeAppendN n action = fmap fromArrayUnsafe $ FL.foldlM' step initial
16991991
17001992 where
17011993
@@ -1708,7 +2000,7 @@ writeAppendNUnsafe n action =
17082000 -- otherwise we lose that space.
17092001 arr1 <-
17102002 if free < needed
1711− then noinline reallocWith "writeAppendNUnsafeWith" (+ needed) needed arr
2003+ then noinline reallocWith "unsafeAppendN" (+ needed) needed arr
17122004 else return arr
17132005 return $ toArrayUnsafe arr1
17142006
@@ -1716,30 +2008,51 @@ writeAppendNUnsafe n action =
17162008 liftIO $ pokeAt end contents x
17172009 return $ ArrayUnsafe contents start (INDEX_NEXT(end,a))
17182010
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+
17192019-- | Append @n@ elements to an existing array. Any free space left in the array
17202020-- after appending @n@ elements is lost.
17212021--
1722−-- >>> writeAppendN n initial = Fold.take n (MutArray.writeAppendNUnsafe n initial)
2022+-- >>> appendN n initial = Fold.take n (MutArray.unsafeAppendN n initial)
17232023--
1724−{-# 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 #-}
17252032writeAppendN :: forall m a. (MonadIO m, Unbox a) =>
17262033 Int -> m (MutArray a) -> Fold m a (MutArray a)
1727−writeAppendN n initial = FL.take n (writeAppendNUnsafe n initial)
2034+writeAppendN = appendN
17282035
1729−-- | @writeAppendWith realloc action@ mutates the array generated by @action@ to
2036+-- | @appendWith realloc action@ mutates the array generated by @action@ to
17302037-- append the input stream. If there is no reserved space available in the
17312038-- array it is reallocated to a size in bytes determined by @realloc oldSize@,
17322039-- where @oldSize@ is the current size of the array in bytes.
17332040--
17342041-- Note that the returned array may be a mutated version of original array.
17352042--
1736−-- >>> writeAppendWith sizer = Fold.foldlM' (MutArray.snocWith sizer)
2043+-- >>> appendWith sizer = Fold.foldlM' (MutArray.snocWith sizer)
17372044--
17382045-- /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." #-}
17392052{-# INLINE writeAppendWith #-}
17402053writeAppendWith :: forall m a. (MonadIO m, Unbox a) =>
17412054 (Int -> Int) -> m (MutArray a) -> Fold m a (MutArray a)
1742−writeAppendWith sizer = FL.foldlM' (snocWith sizer)
2055+writeAppendWith = appendWith
17432056
17442057-- | @append action@ mutates the array generated by @action@ to append the
17452058-- input stream. If there is no reserved space available in the array it is
@@ -1747,26 +2060,33 @@ writeAppendWith sizer = FL.foldlM' (snocWith sizer)
17472060--
17482061-- Note that the returned array may be a mutated version of original array.
17492062--
1750−-- >>> writeAppend = MutArray.writeAppendWith (* 2)
2063+-- >>> append = MutArray.appendWith (* 2)
17512064--
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." #-}
17522072{-# INLINE writeAppend #-}
17532073writeAppend :: forall m a. (MonadIO m, Unbox a) =>
17542074 m (MutArray a) -> Fold m a (MutArray a)
1755−writeAppend = writeAppendWith (* 2)
2075+writeAppend = append
17562076
17572077-- XXX We can carry bound as well in the state to make sure we do not lose the
17582078-- remaining capacity. Need to check perf impact.
17592079--
1760−-- | Like 'writeNUnsafe' but takes a new array allocator @alloc size@ function
1761−-- as argument.
2080+-- | Like 'unsafeCreateOf' but takes a new array allocator @alloc size@
2081+-- function as argument.
17622082--
1763−-- >>> writeNWithUnsafe alloc n = MutArray.writeAppendNUnsafe (alloc n) n
2083+-- >>> unsafeCreateOfWith alloc n = MutArray.unsafeAppendN (alloc n) n
17642084--
17652085-- /Pre-release/
1766−{-# INLINE_NORMAL writeNWithUnsafe #-}
1767−writeNWithUnsafe :: forall m a. (MonadIO m, Unbox a)
2086+{-# INLINE_NORMAL unsafeCreateOfWith #-}
2087+unsafeCreateOfWith :: forall m a. (MonadIO m, Unbox a)
17682088 => (Int -> m (MutArray a)) -> Int -> Fold m a (MutArray a)
1769−writeNWithUnsafe alloc n = fromArrayUnsafe <$> FL.foldlM' step initial
2089+unsafeCreateOfWith alloc n = fromArrayUnsafe <$> FL.foldlM' step initial
17702090
17712091 where
17722092
@@ -1777,40 +2097,64 @@ writeNWithUnsafe alloc n = fromArrayUnsafe <$> FL.foldlM' step initial
17772097 return
17782098 $ ArrayUnsafe contents start (INDEX_NEXT(end,a))
17792099
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+
17802106{-# INLINE_NORMAL writeNUnsafeAs #-}
17812107writeNUnsafeAs :: forall m a. (MonadIO m, Unbox a)
17822108 => PinnedState -> Int -> Fold m a (MutArray a)
1783−writeNUnsafeAs ps = writeNWithUnsafe (newAs ps)
2109+writeNUnsafeAs ps = unsafeCreateOfWith (newAs ps)
17842110
1785−-- | 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
17862112-- driver must not call the step function more than 'n' times otherwise it will
17872113-- corrupt the memory and crash. This function exists mainly because any
17882114-- conditional in the step function blocks fusion causing 10x performance
17892115-- slowdown.
17902116--
1791−-- >>> writeNUnsafe = MutArray.writeNWithUnsafe MutArray.new
2117+-- >>> unsafeCreateOf = MutArray.unsafeCreateOfWith MutArray.emptyOf
17922118--
1793−{-# 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 #-}
17942126writeNUnsafe :: forall m a. (MonadIO m, Unbox a)
17952127 => Int -> Fold m a (MutArray a)
1796−writeNUnsafe = writeNUnsafeAs Unpinned
2128+writeNUnsafe = unsafeCreateOf
2129+
2130+-- | Like 'unsafeCreateOf' but creates a pinned array.
2131+{-# INLINE_NORMAL unsafePinnedCreateOf #-}
2132+unsafePinnedCreateOf :: forall m a. (MonadIO m, Unbox a)
2133+ => Int -> Fold m a (MutArray a)
2134+unsafePinnedCreateOf = writeNUnsafeAs Pinned
17972135
1798−-- | Like 'writeNUnsafe' but creates a pinned array.
1799−{-# INLINE_NORMAL pinnedWriteNUnsafe #-}
2136+{-# DEPRECATED pinnedWriteNUnsafe "Please use unsafePinnedCreateOf instead." #-}
2137+{-# INLINE pinnedWriteNUnsafe #-}
18002138pinnedWriteNUnsafe :: forall m a. (MonadIO m, Unbox a)
18012139 => Int -> Fold m a (MutArray a)
1802−pinnedWriteNUnsafe = writeNUnsafeAs Pinned
2140+pinnedWriteNUnsafe = unsafePinnedCreateOf
18032141
1804−-- | @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
18052143-- allocated using the @alloc@ function.
18062144--
1807−-- >>> writeNWith alloc n = Fold.take n (MutArray.writeNWithUnsafe alloc n)
1808−-- >>> 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
18092147--
1810−{-# 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 #-}
18112155writeNWith :: forall m a. (MonadIO m, Unbox a)
18122156 => (Int -> m (MutArray a)) -> Int -> Fold m a (MutArray a)
1813−writeNWith alloc n = FL.take n (writeNWithUnsafe alloc n)
2157+writeNWith = createOfWith
18142158
18152159{-# INLINE_NORMAL writeNAs #-}
18162160writeNAs ::
@@ -1818,28 +2162,42 @@ writeNAs ::
18182162 => PinnedState
18192163 -> Int
18202164 -> Fold m a (MutArray a)
1821−writeNAs ps = writeNWith (newAs ps)
2165+writeNAs ps = createOfWith (newAs ps)
18222166
1823−-- | @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
18242168-- 'MutArray'.
18252169--
1826−-- >>> writeN = MutArray.writeNWith MutArray.new
1827−-- >>> writeN n = Fold.take n (MutArray.writeNUnsafe n)
1828−-- >>> 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)
18292173--
1830−{-# 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 #-}
18312181writeN :: forall m a. (MonadIO m, Unbox a) => Int -> Fold m a (MutArray a)
1832−writeN = writeNAs Unpinned
2182+writeN = createOf
2183+
2184+-- | Like 'createOf' but creates a pinned array.
2185+{-# INLINE_NORMAL pinnedCreateOf #-}
2186+pinnedCreateOf ::
2187+ forall m a. (MonadIO m, Unbox a)
2188+ => Int
2189+ -> Fold m a (MutArray a)
2190+pinnedCreateOf = writeNAs Pinned
18332191
1834−-- | Like 'writeN' but creates a pinned array.
1835−{-# INLINE_NORMAL pinnedWriteN #-}
2192+{-# DEPRECATED pinnedWriteN "Please use pinnedCreateOf instead." #-}
2193+{-# INLINE pinnedWriteN #-}
18362194pinnedWriteN ::
18372195 forall m a. (MonadIO m, Unbox a)
18382196 => Int
18392197 -> Fold m a (MutArray a)
1840−pinnedWriteN = writeNAs Pinned
2198+pinnedWriteN = pinnedCreateOf
18412199
1842−-- | Like writeNWithUnsafe but writes the array in reverse order.
2200+-- | Like unsafeCreateOfWith but writes the array in reverse order.
18432201--
18442202-- /Internal/
18452203{-# INLINE_NORMAL writeRevNWithUnsafe #-}
@@ -1860,7 +2218,7 @@ writeRevNWithUnsafe alloc n = fromArrayUnsafe <$> FL.foldlM' step initial
18602218 return
18612219 $ ArrayUnsafe contents ptr end
18622220
1863−-- | Like writeNWith but writes the array in reverse order.
2221+-- | Like createOfWith but writes the array in reverse order.
18642222--
18652223-- /Internal/
18662224{-# INLINE_NORMAL writeRevNWith #-}
@@ -1868,31 +2226,36 @@ writeRevNWith :: forall m a. (MonadIO m, Unbox a)
18682226 => (Int -> m (MutArray a)) -> Int -> Fold m a (MutArray a)
18692227writeRevNWith alloc n = FL.take n (writeRevNWithUnsafe alloc n)
18702228
1871−-- | Like writeN but writes the array in reverse order.
2229+-- | Like 'createOf' but writes the array in reverse order.
18722230--
18732231-- /Pre-release/
1874−{-# 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 #-}
18752238writeRevN :: forall m a. (MonadIO m, Unbox a) => Int -> Fold m a (MutArray a)
1876−writeRevN = writeRevNWith new
2239+writeRevN = revCreateOf
18772240
18782241-- | @pinnedWriteNAligned align n@ folds a maximum of @n@ elements from the
18792242-- input stream to a 'MutArray' aligned to the given size.
18802243--
1881−-- >>> pinnedWriteNAligned align = MutArray.writeNWith (MutArray.pinnedNewAligned align)
1882−-- >>> 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)
18832246--
18842247-- /Pre-release/
18852248--
18862249{-# INLINE_NORMAL pinnedWriteNAligned #-}
18872250pinnedWriteNAligned :: forall m a. (MonadIO m, Unbox a)
18882251 => Int -> Int -> Fold m a (MutArray a)
1889−pinnedWriteNAligned align = writeNWith (pinnedNewAligned align)
2252+pinnedWriteNAligned align = createOfWith (pinnedNewAligned align)
18902253
18912254-- XXX Buffer to a list instead?
18922255
18932256-- | Buffer a stream into a stream of arrays.
18942257--
1895−-- >>> writeChunks n = Fold.many (MutArray.writeN n) Fold.toStreamK
2258+-- >>> buildChunks n = Fold.many (MutArray.createOf n) Fold.toStreamK
18962259--
18972260-- Breaking an array into an array stream can be useful to consume a large
18982261-- array sequentially such that memory of the array is released incrementatlly.
@@ -1901,37 +2264,43 @@ pinnedWriteNAligned align = writeNWith (pinnedNewAligned align)
19012264--
19022265-- /Unimplemented/
19032266--
1904−{-# INLINE_NORMAL writeChunks #-}
2267+{-# INLINE_NORMAL buildChunks #-}
2268+buildChunks :: (MonadIO m, Unbox a) =>
2269+ Int -> Fold m a (StreamK n (MutArray a))
2270+buildChunks n = FL.many (createOf n) FL.toStreamK
2271+
2272+{-# DEPRECATED writeChunks "Please use buildChunks instead." #-}
2273+{-# INLINE writeChunks #-}
19052274writeChunks :: (MonadIO m, Unbox a) =>
19062275 Int -> Fold m a (StreamK n (MutArray a))
1907−writeChunks n = FL.many (writeN n) FL.toStreamK
2276+writeChunks = buildChunks
19082277
19092278{-# INLINE_NORMAL writeWithAs #-}
19102279writeWithAs :: forall m a. (MonadIO m, Unbox a)
19112280 => PinnedState -> Int -> Fold m a (MutArray a)
1912−-- writeWithAs ps n = FL.rmapM rightSize $ writeAppendWith (* 2) (newAs ps n)
2281+-- writeWithAs ps n = FL.rmapM rightSize $ appendWith (* 2) (newAs ps n)
19132282writeWithAs ps elemCount =
19142283 FL.rmapM extract $ FL.foldlM' step initial
19152284
19162285 where
19172286
19182287 initial = do
1919− when (elemCount < 0) $ error "writeWith: elemCount is negative"
1920− liftIO $ newAs ps elemCount
2288+ when (elemCount < 0) $ error "createWith: elemCount is negative"
2289+ newAs ps elemCount
19212290
19222291 step arr@(MutArray _ start end bound) x
19232292 | INDEX_NEXT(end,a) > bound = do
19242293 let oldSize = end - start
19252294 newSize = max (oldSize * 2) 1
1926− arr1 <- liftIO $ reallocExplicit (SIZE_OF(a)) newSize arr
2295+ arr1 <- liftIO $ reallocExplicitAs ps (SIZE_OF(a)) newSize arr
19272296 snocUnsafe arr1 x
19282297 step arr x = snocUnsafe arr x
19292298
19302299 extract = liftIO . rightSize
19312300
1932−-- XXX Compare writeWith with fromStreamD which uses an array of streams
1933−-- implementation. We can write this using writeChunks above if that is faster.
1934−-- If writeWith is faster then we should use that to implement
2301+-- XXX Compare createWith with fromStreamD which uses an array of streams
2302+-- implementation. We can write this using buildChunks above if that is faster.
2303+-- If createWith is faster then we should use that to implement
19352304-- fromStreamD.
19362305--
19372306-- XXX The realloc based implementation needs to make one extra copy if we use
@@ -1946,39 +2315,56 @@ writeWithAs ps elemCount =
19462315-- XXX check if GHC's memory allocator is efficient enough. We can try the C
19472316-- malloc to compare against.
19482317
1949−-- | @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
19502319-- starts at a size big enough to hold minCount elements, the size is doubled
19512320-- every time the array needs to be grown.
19522321--
19532322-- /Caution! Do not use this on infinite streams./
19542323--
1955−-- >>> f n = MutArray.writeAppendWith (* 2) (MutArray.new n)
1956−-- >>> writeWith n = Fold.rmapM MutArray.rightSize (f n)
1957−-- >>> writeWith n = Fold.rmapM MutArray.fromArrayStreamK (MutArray.writeChunks 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)
19582327--
19592328-- /Pre-release/
1960−{-# 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 #-}
19612337writeWith :: forall m a. (MonadIO m, Unbox a)
19622338 => Int -> Fold m a (MutArray a)
1963−-- writeWith n = FL.rmapM rightSize $ writeAppendWith (* 2) (new n)
1964−writeWith = writeWithAs Unpinned
2339+writeWith = createWith
19652340
19662341-- | Fold the whole input to a single array.
19672342--
1968−-- Same as 'writeWith' using an initial array size of 'arrayChunkBytes' bytes
2343+-- Same as 'createWith' using an initial array size of 'arrayChunkBytes' bytes
19692344-- rounded up to the element size.
19702345--
19712346-- /Caution! Do not use this on infinite streams./
19722347--
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." #-}
19732354{-# INLINE write #-}
19742355write :: forall m a. (MonadIO m, Unbox a) => Fold m a (MutArray a)
1975−write = writeWith (allocBytesToElemCount (undefined :: a) arrayChunkBytes)
2356+write = create
2357+
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)
19762363
1977−-- | Like 'write' but creates a pinned array.
2364+{-# DEPRECATED pinnedWrite "Please use pinnedCreate instead." #-}
19782365{-# INLINE pinnedWrite #-}
19792366pinnedWrite :: forall m a. (MonadIO m, Unbox a) => Fold m a (MutArray a)
1980−pinnedWrite =
1981− writeWithAs Pinned (allocBytesToElemCount (undefined :: a) arrayChunkBytes)
2367+pinnedWrite = pinnedCreate
19822368
19832369-------------------------------------------------------------------------------
19842370-- construct from streams, known size
@@ -1988,7 +2374,7 @@ pinnedWrite =
19882374fromStreamDNAs :: forall m a. (MonadIO m, Unbox a)
19892375 => PinnedState -> Int -> D.Stream m a -> m (MutArray a)
19902376fromStreamDNAs ps limit str = do
1991− (arr :: MutArray a) <- liftIO $ newAs ps limit
2377+ (arr :: MutArray a) <- newAs ps limit
19922378 end <- D.foldlM' (fwrite (arrContents arr)) (return $ arrEnd arr) $ D.take limit str
19932379 return $ arr {arrEnd = end}
19942380
@@ -1998,15 +2384,21 @@ fromStreamDNAs ps limit str = do
19982384 liftIO $ pokeAt ptr arrContents x
19992385 return $ INDEX_NEXT(ptr,a)
20002386
2001−-- | Use the 'writeN' fold instead.
2387+-- | Use the 'createOf' fold instead.
20022388--
2003−-- >>> fromStreamDN n = Stream.fold (MutArray.writeN n)
2389+-- >>> fromStreamN n = Stream.fold (MutArray.createOf n)
20042390--
2005−{-# INLINE_NORMAL fromStreamDN #-}
2391+{-# INLINE_NORMAL fromStreamN #-}
2392+fromStreamN :: forall m a. (MonadIO m, Unbox a)
2393+ => Int -> D.Stream m a -> m (MutArray a)
2394+-- fromStreamDN n = D.fold (createOf n)
2395+fromStreamN = fromStreamDNAs Unpinned
2396+
2397+{-# DEPRECATED fromStreamDN "Please use fromStreamN instead." #-}
2398+{-# INLINE fromStreamDN #-}
20062399fromStreamDN :: forall m a. (MonadIO m, Unbox a)
20072400 => Int -> D.Stream m a -> m (MutArray a)
2008−-- fromStreamDN n = D.fold (writeN n)
2009−fromStreamDN = fromStreamDNAs Unpinned
2401+fromStreamDN = fromStreamN
20102402
20112403-- | Create a 'MutArray' from the first N elements of a list. The array is
20122404-- allocated to size N, if the list terminates before N elements then the
@@ -2026,16 +2418,70 @@ pinnedFromListN n xs = fromStreamDNAs Pinned n $ D.fromList xs
20262418-- /Pre-release/
20272419{-# INLINE fromListRevN #-}
20282420fromListRevN :: (MonadIO m, Unbox a) => Int -> [a] -> m (MutArray a)
2029−fromListRevN n xs = D.fold (writeRevN n) $ D.fromList xs
2421+fromListRevN n xs = D.fold (revCreateOf n) $ D.fromList xs
2422+
2423+-- | Convert a pure stream in Identity monad to a mutable array.
2424+{-# INLINABLE fromPureStreamN #-}
2425+fromPureStreamN :: (MonadIO m, Unbox a) =>
2426+ Int -> Stream Identity a -> m (MutArray a)
2427+fromPureStreamN n xs =
2428+ D.fold (createOf n) $ D.morphInner (return . runIdentity) xs
20302429
20312430-- | Convert a pure stream in Identity monad to a mutable array.
20322431{-# INLINABLE fromPureStream #-}
20332432fromPureStream :: (MonadIO m, Unbox a) => Stream Identity a -> m (MutArray a)
20342433fromPureStream xs =
2035− liftIO $ D.fold write $ D.morphInner (return . runIdentity) xs
2434+ D.fold create $ D.morphInner (return . runIdentity) xs
2435+
2436+{-# INLINABLE fromPtrN #-}
2437+fromPtrN :: MonadIO m => Int -> Ptr Word8 -> m (MutArray Word8)
2438+fromPtrN len addr = do
2439+ -- memcpy is better than stream copy when the size is known.
2440+ -- XXX We can implement a stream copy in a similar way by streaming Word64
2441+ -- first and then remaining Word8.
2442+ arr <- new len
2443+ _ <- unsafeAsPtr arr
2444+ (\ptr -> liftIO $ c_memcpy ptr addr (fromIntegral len))
2445+ return (arr {arrEnd = len})
2446+
2447+{-# INLINABLE fromByteStr# #-}
2448+fromByteStr# :: MonadIO m => Addr# -> m (MutArray Word8)
2449+fromByteStr# addr = do
2450+ -- It is better to count the size first and allocate exact space.
2451+ -- Also, memcpy is better than stream copy when the size is known.
2452+ -- C strlen compares 4 bytes at a time, so is better than the stream
2453+ -- version. https://github.com/bminor/glibc/blob/master/string/strlen.c
2454+ -- XXX We can possibly use a stream of Word64 to do the same.
2455+ -- fromByteStr# addr = fromPureStream (D.fromByteStr# addr)
2456+ len <- liftIO $ c_strlen (Ptr addr)
2457+ let lenInt = fromIntegral len
2458+ arr <- new lenInt
2459+ _ <- unsafeAsPtr arr (\ptr -> liftIO $ c_memcpy ptr (Ptr addr) len)
2460+ return (arr {arrEnd = lenInt})
20362461
20372462-------------------------------------------------------------------------------
2038−-- convert stream to a single array
2463+-- convert a stream of arrays to a single array by reallocating and copying
2464+-------------------------------------------------------------------------------
2465+
2466+-- XXX Both of these implementations of splicing seem to perform equally well.
2467+-- We need to perform benchmarks over a range of sizes though.
2468+
2469+-- | Also see 'fromChunksK'.
2470+{-# INLINE fromChunksRealloced #-}
2471+fromChunksRealloced :: forall m a. (MonadIO m, Unbox a)
2472+ => Stream m (MutArray a) -> m (MutArray a)
2473+fromChunksRealloced s = do
2474+ res <- D.uncons s
2475+ case res of
2476+ Just (a, strm) -> do
2477+ arr <- D.foldlM' spliceExp (pure a) strm
2478+ -- Reallocation is exponential so there may be 50% empty space in
2479+ -- worst case. One more reallocation to reclaim the space.
2480+ rightSize arr
2481+ Nothing -> pure nil
2482+
2483+-------------------------------------------------------------------------------
2484+-- convert a stream of arrays to a single array by buffering arrays first
20392485-------------------------------------------------------------------------------
20402486
20412487{-# INLINE arrayStreamKLength #-}
@@ -2045,34 +2491,72 @@ arrayStreamKLength as = K.foldl' (+) 0 (K.map length as)
20452491-- | Convert an array stream to an array. Note that this requires peak memory
20462492-- that is double the size of the array stream.
20472493--
2494+{-# INLINE fromChunkskAs #-}
2495+fromChunkskAs :: (Unbox a, MonadIO m) =>
2496+ PinnedState -> StreamK m (MutArray a) -> m (MutArray a)
2497+fromChunkskAs ps as = do
2498+ len <- arrayStreamKLength as
2499+ arr <- newAs ps len
2500+ -- XXX is StreamK fold faster or StreamD fold?
2501+ K.foldlM' spliceUnsafe (pure arr) as
2502+ -- fromStreamDN len $ D.unfoldMany reader $ D.fromStreamK as
2503+
2504+-- XXX Need to compare this with fromChunks and fromChunkList and keep the
2505+-- fastest or simplest one if all are equally fast.
2506+
2507+-- | Convert an array stream to an array. Note that this requires peak memory
2508+-- that is double the size of the array stream.
2509+--
2510+-- Also see 'fromChunksRealloced'.
2511+--
2512+{-# INLINE fromChunksK #-}
2513+fromChunksK :: (Unbox a, MonadIO m) =>
2514+ StreamK m (MutArray a) -> m (MutArray a)
2515+fromChunksK = fromChunkskAs Unpinned
2516+
2517+{-# DEPRECATED fromArrayStreamK "Please use fromChunksK instead." #-}
20482518{-# INLINE fromArrayStreamK #-}
20492519fromArrayStreamK :: (Unbox a, MonadIO m) =>
20502520 StreamK m (MutArray a) -> m (MutArray a)
2051−fromArrayStreamK as = do
2052− len <- arrayStreamKLength as
2053− fromStreamDN len $ D.unfoldMany reader $ D.fromStreamK as
2521+fromArrayStreamK = fromChunksK
20542522
20552523{-# INLINE fromStreamDAs #-}
20562524fromStreamDAs ::
20572525 (MonadIO m, Unbox a) => PinnedState -> D.Stream m a -> m (MutArray a)
2058−fromStreamDAs ps m = arrayStreamKFromStreamDAs ps m >>= fromArrayStreamK
2526+fromStreamDAs ps m =
2527+ arrayStreamKFromStreamDAs Unpinned m >>= fromChunkskAs ps
2528+
2529+-- | Create an 'Array' from a stream. This is useful when we want to create a
2530+-- single array from a stream of unknown size. 'createOf' is at least twice
2531+-- as efficient when the size is already known.
2532+--
2533+-- Note that if the input stream is too large memory allocation for the array
2534+-- may fail. When the stream size is not known, `chunksOf` followed by
2535+-- processing of indvidual arrays in the resulting stream should be preferred.
2536+--
2537+-- /Pre-release/
2538+{-# INLINE fromStream #-}
2539+fromStream :: (MonadIO m, Unbox a) => Stream m a -> m (MutArray a)
2540+fromStream = fromStreamDAs Unpinned
20592541
2542+-- fromStream (Stream m) = P.fold create m
20602543-- CAUTION: a very large number (millions) of arrays can degrade performance
20612544-- due to GC overhead because we need to buffer the arrays before we flatten
20622545-- all the arrays.
20632546--
2064−-- XXX Compare if this is faster or "fold write".
2547+-- XXX Compare if this is faster or "fold create".
20652548--
20662549-- | We could take the approach of doubling the memory allocation on each
20672550-- overflow. This would result in more or less the same amount of copying as in
20682551-- the chunking approach. However, if we have to shrink in the end then it may
20692552-- result in an extra copy of the entire data.
20702553--
2071−-- >>> fromStreamD = StreamD.fold MutArray.write
2554+-- >>> fromStreamD = StreamD.fold MutArray.create
20722555--
20732556{-# INLINE fromStreamD #-}
2557+{-# DEPRECATED fromStreamD "Please use fromStream instead." #-}
20742558fromStreamD :: (MonadIO m, Unbox a) => D.Stream m a -> m (MutArray a)
2075−fromStreamD = fromStreamDAs Unpinned
2559+fromStreamD = fromStream
20762560
20772561-- | Create a 'MutArray' from a list. The list must be of finite size.
20782562--
@@ -2097,6 +2581,10 @@ fromListRev xs = fromListRevN (Prelude.length xs) xs
20972581-- Cloning
20982582-------------------------------------------------------------------------------
20992583
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+
21002588{-# INLINE cloneAs #-}
21012589cloneAs ::
21022590 ( MonadIO m
@@ -2106,7 +2594,7 @@ cloneAs ::
21062594 )
21072595 => PinnedState -> MutArray a -> m (MutArray a)
21082596cloneAs ps src =
2109− liftIO $ do
2597+ do
21102598 let startSrc = arrStart src
21112599 srcLen = arrEnd src - startSrc
21122600 newArrContents <-
@@ -2145,17 +2633,16 @@ spliceCopy :: forall m a. MonadIO m =>
21452633 Unbox a =>
21462634#endif
21472635 MutArray a -> MutArray a -> m (MutArray a)
2148−spliceCopy arr1 arr2 = liftIO $ do
2636+spliceCopy arr1 arr2 = do
21492637 let start1 = arrStart arr1
21502638 start2 = arrStart arr2
21512639 len1 = arrEnd arr1 - start1
21522640 len2 = arrEnd arr2 - start2
2153− let newLen = len1 + len2
2641+ let len = len1 + len2
21542642 newArrContents <-
21552643 if Unboxed.isPinned (arrContents arr1)
2156− then Unboxed.pinnedNew newLen
2157− else Unboxed.new newLen
2158− let len = len1 + len2
2644+ then liftIO $ Unboxed.pinnedNew len
2645+ else liftIO $ Unboxed.new len
21592646 putSliceUnsafe (arrContents arr1) start1 newArrContents 0 len1
21602647 putSliceUnsafe (arrContents arr2) start2 newArrContents len1 len2
21612648 return $ MutArray newArrContents 0 len len
@@ -2167,7 +2654,7 @@ spliceCopy arr1 arr2 = liftIO $ do
21672654spliceUnsafe :: MonadIO m =>
21682655 MutArray a -> MutArray a -> m (MutArray a)
21692656spliceUnsafe dst src =
2170− liftIO $ do
2657+ do
21712658 let startSrc = arrStart src
21722659 srcLen = arrEnd src - startSrc
21732660 endDst = arrEnd dst
@@ -2189,7 +2676,7 @@ spliceWith :: forall m a. (MonadIO m, Unbox a) =>
21892676 (Int -> Int -> Int) -> MutArray a -> MutArray a -> m (MutArray a)
21902677spliceWith sizer dst@(MutArray _ start end bound) src = do
21912678{-
2192− let f = writeAppendWith (`sizer` byteLength src) (return dst)
2679+ let f = appendWith (`sizer` byteLength src) (return dst)
21932680 in D.fold f (toStreamD src)
21942681-}
21952682 assert (end <= bound) (return ())
@@ -2205,7 +2692,7 @@ spliceWith sizer dst@(MutArray _ start end bound) src = do
22052692 $ "splice: newSize is less than the total size "
22062693 ++ "of arrays being appended. Please check the "
22072694 ++ "sizer function passed."
2208− liftIO $ realloc newSizeInBytes dst
2695+ realloc newSizeInBytes dst
22092696 else return dst
22102697 spliceUnsafe dst1 src
22112698
@@ -2242,14 +2729,24 @@ spliceExp = spliceWith (\l1 l2 -> max (l1 * 2) (l1 + l2))
22422729-- Splitting
22432730-------------------------------------------------------------------------------
22442731
2732+-- | Generate a stream of array slices using a predicate. The array element
2733+-- matching the predicate is dropped.
2734+--
2735+-- /Pre-release/
2736+{-# INLINE splitOn #-}
2737+splitOn :: (MonadIO m, Unbox a) =>
2738+ (a -> Bool) -> MutArray a -> Stream m (MutArray a)
2739+splitOn predicate arr =
2740+ fmap (\(i, len) -> getSliceUnsafe i len arr)
2741+ $ D.indexOnSuffix predicate (read arr)
2742+
22452743-- | Drops the separator byte
22462744{-# INLINE breakOn #-}
22472745breakOn :: MonadIO m
22482746 => Word8 -> MutArray Word8 -> m (MutArray Word8, Maybe (MutArray Word8))
2249−breakOn sep arr@MutArray{..} = asPtrUnsafe arr $ \p -> liftIO $ do
2250− -- XXX Instead of using asPtrUnsafe (pinning memory) we can pass unlifted
2251− -- Addr# to memchr and it should be safe (from ghc 8.4).
2747+breakOn sep arr@MutArray{..} = unsafeAsPtr arr $ \p -> liftIO $ do
22522748 -- XXX We do not need memchr here, we can use a Haskell equivalent.
2749+ -- Need efficient stream based primitives that work on Word64.
22532750 loc <- c_memchr p sep (fromIntegral $ byteLength arr)
22542751 let sepIndex = loc `minusPtr` p
22552752 return $
@@ -2270,32 +2767,41 @@ breakOn sep arr@MutArray{..} = asPtrUnsafe arr $ \p -> liftIO $ do
22702767 }
22712768 )
22722769
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+
22732792-- | Create two slices of an array without copying the original array. The
22742793-- specified index @i@ is the first index of the second slice.
22752794--
2795+{-# INLINE splitAt #-}
22762796splitAt :: forall a. Unbox a => Int -> MutArray a -> (MutArray a, MutArray a)
2277−splitAt i arr@MutArray{..} =
2797+splitAt i arr =
22782798 let maxIndex = length arr - 1
22792799 in if i < 0
22802800 then error "sliceAt: negative array index"
22812801 else if i > maxIndex
22822802 then error $ "sliceAt: specified array index " ++ show i
22832803 ++ " is beyond the maximum index " ++ show maxIndex
2284− else let off = i * SIZE_OF(a)
2285− p = arrStart + off
2286− in ( MutArray
2287− { arrContents = arrContents
2288− , arrStart = arrStart
2289− , arrEnd = p
2290− , arrBound = p
2291− }
2292− , MutArray
2293− { arrContents = arrContents
2294− , arrStart = p
2295− , arrEnd = arrEnd
2296− , arrBound = arrBound
2297− }
2298− )
2804+ else unsafeSplitAt i arr
22992805
23002806-------------------------------------------------------------------------------
23012807-- Casting
@@ -2345,9 +2851,13 @@ cast arr =
23452851-- guarantees that unsafe calls will be performed in the calling thread. Making
23462852-- it safe to pass heap-allocated objects to unsafe functions.
23472853
2348−-- 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
23492855-- operations. And we are safe from Haskell perspective because we will be
23502856-- 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.
23512861
23522862-- | Use a @MutArray a@ as @Ptr a@. This is useful when we want to pass an
23532863-- array as a pointer to some operating system call or to a "safe" FFI call.
@@ -2365,48 +2875,377 @@ cast arr =
23652875--
23662876-- /Pre-release/
23672877--
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." #-}
23682885{-# INLINE asPtrUnsafe #-}
23692886asPtrUnsafe :: MonadIO m => MutArray a -> (Ptr a -> m b) -> m b
2370−asPtrUnsafe arr f =
2371− Unboxed.asPtrUnsafe
2887+asPtrUnsafe = unsafePinnedAsPtr
2888+
2889+{-# INLINE unsafeAsPtr #-}
2890+unsafeAsPtr :: MonadIO m => MutArray a -> (Ptr a -> m b) -> m b
2891+unsafeAsPtr arr f =
2892+ Unboxed.unsafeAsPtr
23722893 (arrContents arr) (\ptr -> f (ptr `plusPtr` arrStart arr))
23732894
23742895-------------------------------------------------------------------------------
23752896-- Equality
23762897-------------------------------------------------------------------------------
23772898
2378−-- | Compare the length of the arrays. If the length is equal, compare the
2379−-- lexicographical ordering of two underlying byte arrays otherwise return the
2380−-- result of length comparison.
2899+-- | Byte compare two arrays. Compare the length of the arrays. If the length
2900+-- is equal, compare the lexicographical ordering of two underlying byte arrays
2901+-- otherwise return the result of length comparison.
2902+--
2903+-- /Unsafe/: Note that the 'Unbox' instance of sum types with constructors of
2904+-- different sizes may leave some memory uninitialized which can make byte
2905+-- comparison unreliable.
23812906--
23822907-- /Pre-release/
2908+{-# INLINE byteCmp #-}
2909+byteCmp :: MonadIO m => MutArray a -> MutArray a -> m Ordering
2910+byteCmp arr1 arr2 = do
2911+ let marr1 = getMutableByteArray# (arrContents arr1)
2912+ marr2 = getMutableByteArray# (arrContents arr2)
2913+ !(I# st1#) = arrStart arr1
2914+ !(I# st2#) = arrStart arr2
2915+ !(I# len#) = byteLength arr1
2916+ case compare (byteLength arr1) (byteLength arr2) of
2917+ EQ -> do
2918+ r <- liftIO $ IO $ \s# ->
2919+ let res =
2920+ I#
2921+ (compareByteArrays#
2922+ (unsafeCoerce# marr1)
2923+ st1#
2924+ (unsafeCoerce# marr2)
2925+ st2#
2926+ len#)
2927+ in (# s#, res #)
2928+ return $ compare r 0
2929+ x -> return x
2930+
23832931{-# INLINE cmp #-}
2932+{-# DEPRECATED cmp "Please use byteCmp instead." #-}
23842933cmp :: MonadIO m => MutArray a -> MutArray a -> m Ordering
2385−cmp arr1 arr2 =
2386− liftIO
2387− $ do
2388− let marr1 = getMutableByteArray# (arrContents arr1)
2389− marr2 = getMutableByteArray# (arrContents arr2)
2390− !(I# st1#) = arrStart arr1
2391− !(I# st2#) = arrStart arr2
2392− !(I# len#) = byteLength arr1
2393− case compare (byteLength arr1) (byteLength arr2) of
2394− EQ -> do
2395− r <- liftIO $ IO $ \s# ->
2396− let res =
2397− I#
2398− (compareByteArrays#
2399− (unsafeCoerce# marr1)
2400− st1#
2401− (unsafeCoerce# marr2)
2402− st2#
2403− len#)
2404− in (# s#, res #)
2405− return $ compare r 0
2406− x -> return x
2934+cmp = byteCmp
24072935
2936+-- | Byte equality of two arrays.
2937+--
2938+-- >>> byteEq arr1 arr2 = (==) EQ $ MArray.byteCmp arr1 arr2
2939+--
2940+-- /Unsafe/: See 'byteCmp'.
2941+{-# INLINE byteEq #-}
2942+byteEq :: MonadIO m => MutArray a -> MutArray a -> m Bool
2943+byteEq arr1 arr2 = fmap (EQ ==) $ byteCmp arr1 arr2
2944+
2945+-------------------------------------------------------------------------------
2946+-- Compact
24082947-------------------------------------------------------------------------------
2409−-- NFData
2948+
2949+-- Note: LE versions avoid an extra copy compared to GE. LE parser trades
2950+-- backtracking one array in lieu of avoiding a copy. However, LE and GE both
2951+-- can leave some memory unused. They can split the last array to fit it
2952+-- exactly in the space.
2953+
2954+{-# INLINE_NORMAL pCompactLeAs #-}
2955+pCompactLeAs ::
2956+ forall m a. (MonadIO m, Unbox a)
2957+ => PinnedState -> Int -> Parser (MutArray a) m (MutArray a)
2958+pCompactLeAs ps maxElems = Parser step initial extract
2959+
2960+ where
2961+
2962+ maxBytes = maxElems * SIZE_OF(a)
2963+
2964+ functionName = "Streamly.Internal.Data.MutArray.pCompactLE"
2965+
2966+ initial =
2967+ return
2968+ $ if maxElems <= 0
2969+ then error
2970+ $ functionName
2971+ ++ ": the size of arrays ["
2972+ ++ show maxElems ++ "] must be a natural number"
2973+ else Parser.IPartial Nothing
2974+
2975+ step Nothing arr =
2976+ return
2977+ $ let len = byteLength arr
2978+ in if len >= maxBytes
2979+ then Parser.Done 0 arr
2980+ else Parser.Partial 0 (Just arr)
2981+ -- XXX Split the last array to use the space more compactly.
2982+ step (Just buf) arr =
2983+ let len = byteLength buf + byteLength arr
2984+ in if len > maxBytes
2985+ then return $ Parser.Done 1 buf
2986+ else do
2987+ buf1 <-
2988+ if byteCapacity buf < maxBytes
2989+ then liftIO $ reallocExplicitAs
2990+ ps (SIZE_OF(a)) maxBytes buf
2991+ else return buf
2992+ buf2 <- spliceUnsafe buf1 arr
2993+ return $ Parser.Partial 0 (Just buf2)
2994+
2995+ extract Nothing = return $ Parser.Done 0 nil
2996+ extract (Just buf) = return $ Parser.Done 0 buf
2997+
2998+-- | Parser @pCompactLE maxElems@ coalesces adjacent arrays in the input stream
2999+-- only if the combined size would be less than or equal to @maxElems@
3000+-- elements. Note that it won't split an array if the original array is already
3001+-- larger than maxElems.
3002+--
3003+-- @maxElems@ must be greater than 0.
3004+--
3005+-- Generates unpinned arrays irrespective of the pinning status of input
3006+-- arrays.
3007+--
3008+-- /Internal/
3009+{-# INLINE pCompactLE #-}
3010+pCompactLE ::
3011+ forall m a. (MonadIO m, Unbox a)
3012+ => Int -> Parser (MutArray a) m (MutArray a)
3013+pCompactLE = pCompactLeAs Unpinned
3014+
3015+-- | Pinned version of 'pCompactLE'.
3016+{-# INLINE pPinnedCompactLE #-}
3017+pPinnedCompactLE ::
3018+ forall m a. (MonadIO m, Unbox a)
3019+ => Int -> Parser (MutArray a) m (MutArray a)
3020+pPinnedCompactLE = pCompactLeAs Pinned
3021+
3022+data SpliceState s arr
3023+ = SpliceInitial s
3024+ | SpliceBuffering s arr
3025+ | SpliceYielding arr (SpliceState s arr)
3026+ | SpliceFinish
3027+
3028+-- This mutates the first array (if it has space) to append values from the
3029+-- second one. This would work for immutable arrays as well because an
3030+-- immutable array never has additional space so a new array is allocated
3031+-- instead of mutating it.
3032+
3033+{-# INLINE_NORMAL compactLeAs #-}
3034+compactLeAs :: forall m a. (MonadIO m, Unbox a)
3035+ => PinnedState -> Int -> D.Stream m (MutArray a) -> D.Stream m (MutArray a)
3036+compactLeAs ps maxElems (D.Stream step state) =
3037+ D.Stream step' (SpliceInitial state)
3038+
3039+ where
3040+
3041+ maxBytes = maxElems * SIZE_OF(a)
3042+
3043+ functionName = "Streamly.Internal.Data.MutArray.rCompactLE"
3044+
3045+ {-# INLINE_LATE step' #-}
3046+ step' gst (SpliceInitial st) = do
3047+ when (maxElems <= 0) $
3048+ -- XXX we can pass the module string from the higher level API
3049+ error $ functionName ++ ": the size of arrays [" ++ show maxElems
3050+ ++ "] must be a natural number"
3051+ r <- step gst st
3052+ case r of
3053+ D.Yield arr s -> return $
3054+ let len = byteLength arr
3055+ in if len >= maxBytes
3056+ then D.Skip (SpliceYielding arr (SpliceInitial s))
3057+ else D.Skip (SpliceBuffering s arr)
3058+ D.Skip s -> return $ D.Skip (SpliceInitial s)
3059+ D.Stop -> return D.Stop
3060+
3061+ -- XXX Split the last array to use the space more compactly.
3062+ step' gst (SpliceBuffering st buf) = do
3063+ r <- step gst st
3064+ case r of
3065+ D.Yield arr s -> do
3066+ let len = byteLength buf + byteLength arr
3067+ if len > maxBytes
3068+ then return $
3069+ D.Skip (SpliceYielding buf (SpliceBuffering s arr))
3070+ else do
3071+ buf1 <- if byteCapacity buf < maxBytes
3072+ then liftIO $ reallocExplicitAs
3073+ ps (SIZE_OF(a)) maxBytes buf
3074+ else return buf
3075+ buf2 <- spliceUnsafe buf1 arr
3076+ return $ D.Skip (SpliceBuffering s buf2)
3077+ D.Skip s -> return $ D.Skip (SpliceBuffering s buf)
3078+ D.Stop -> return $ D.Skip (SpliceYielding buf SpliceFinish)
3079+
3080+ step' _ SpliceFinish = return D.Stop
3081+
3082+ step' _ (SpliceYielding arr next) = return $ D.Yield arr next
3083+
3084+
3085+{-# INLINE_NORMAL fCompactGeAs #-}
3086+fCompactGeAs ::
3087+ forall m a. (MonadIO m, Unbox a)
3088+ => PinnedState -> Int -> FL.Fold m (MutArray a) (MutArray a)
3089+fCompactGeAs ps minElems = Fold step initial extract extract
3090+
3091+ where
3092+
3093+ minBytes = minElems * SIZE_OF(a)
3094+
3095+ functionName = "Streamly.Internal.Data.MutArray.fCompactGE"
3096+
3097+ initial =
3098+ return
3099+ $ if minElems < 0
3100+ then error
3101+ $ functionName
3102+ ++ ": the size of arrays ["
3103+ ++ show minElems ++ "] must be a natural number"
3104+ else FL.Partial Nothing
3105+
3106+ step Nothing arr =
3107+ return
3108+ $ let len = byteLength arr
3109+ in if len >= minBytes
3110+ then FL.Done arr
3111+ else FL.Partial (Just arr)
3112+ -- XXX Buffer arrays as a list to avoid copy and reallocations
3113+ step (Just buf) arr = do
3114+ let len = byteLength buf + byteLength arr
3115+ buf1 <-
3116+ if byteCapacity buf < len
3117+ then liftIO $ reallocExplicitAs
3118+ ps (SIZE_OF(a)) (max minBytes len) buf
3119+ else return buf
3120+ buf2 <- spliceUnsafe buf1 arr
3121+ if len >= minBytes
3122+ then return $ FL.Done buf2
3123+ else return $ FL.Partial (Just buf2)
3124+
3125+ extract Nothing = return nil
3126+ extract (Just buf) = return buf
3127+
3128+-- | Fold @fCompactGE minElems@ coalesces adjacent arrays in the input stream
3129+-- until the size becomes greater than or equal to @minElems@.
3130+--
3131+-- Generates unpinned arrays irrespective of the pinning status of input
3132+-- arrays.
3133+{-# INLINE fCompactGE #-}
3134+fCompactGE ::
3135+ forall m a. (MonadIO m, Unbox a)
3136+ => Int -> FL.Fold m (MutArray a) (MutArray a)
3137+fCompactGE = fCompactGeAs Unpinned
3138+
3139+-- | Pinned version of 'fCompactGE'.
3140+{-# INLINE fPinnedCompactGE #-}
3141+fPinnedCompactGE ::
3142+ forall m a. (MonadIO m, Unbox a)
3143+ => Int -> FL.Fold m (MutArray a) (MutArray a)
3144+fPinnedCompactGE = fCompactGeAs Pinned
3145+
3146+{-# INLINE_NORMAL lCompactGeAs #-}
3147+lCompactGeAs :: forall m a. (MonadIO m, Unbox a)
3148+ => PinnedState -> Int -> Fold m (MutArray a) () -> Fold m (MutArray a) ()
3149+-- The fold version turns out to be a little bit slower.
3150+-- lCompactGeAs ps n = FL.many (fCompactGeAs ps n)
3151+lCompactGeAs ps minElems (Fold step1 initial1 _ final1) =
3152+ Fold step initial extract final
3153+
3154+ where
3155+
3156+ minBytes = minElems * SIZE_OF(a)
3157+
3158+ functionName = "Streamly.Internal.Data.MutArray.lCompactGE"
3159+
3160+ initial = do
3161+ when (minElems <= 0) $
3162+ -- XXX we can pass the module string from the higher level API
3163+ error $ functionName ++ ": the size of arrays ["
3164+ ++ show minElems ++ "] must be a natural number"
3165+
3166+ r <- initial1
3167+ return $ first (Tuple' Nothing) r
3168+
3169+ {-# INLINE runInner #-}
3170+ runInner len acc buf =
3171+ if len >= minBytes
3172+ then do
3173+ r <- step1 acc buf
3174+ case r of
3175+ FL.Done _ -> return $ FL.Done ()
3176+ FL.Partial s -> do
3177+ _ <- final1 s
3178+ res <- initial1
3179+ return $ first (Tuple' Nothing) res
3180+ else return $ FL.Partial $ Tuple' (Just buf) acc
3181+
3182+ step (Tuple' Nothing r1) arr =
3183+ runInner (byteLength arr) r1 arr
3184+
3185+ -- XXX Buffer arrays as a list to avoid copy and reallocations
3186+ step (Tuple' (Just buf) r1) arr = do
3187+ let len = byteLength buf + byteLength arr
3188+ buf1 <- if byteCapacity buf < len
3189+ then liftIO $ reallocExplicitAs
3190+ ps (SIZE_OF(a)) (max minBytes len) buf
3191+ else return buf
3192+ buf2 <- spliceUnsafe buf1 arr
3193+ runInner len r1 buf2
3194+
3195+ -- XXX Several folds do extract >=> final, therefore, we need to make final
3196+ -- return "m b" rather than using extract post it if we want extract to be
3197+ -- partial.
3198+ --
3199+ -- extract forces the pending buffer to be sent to the fold which is not
3200+ -- what we want.
3201+ extract _ = error "lCompactGE: not designed for scanning"
3202+
3203+ final (Tuple' Nothing r1) = final1 r1
3204+ final (Tuple' (Just buf) r1) = do
3205+ r <- step1 r1 buf
3206+ case r of
3207+ FL.Partial rr -> final1 rr
3208+ FL.Done _ -> return ()
3209+
3210+-- | Like 'compactGE' but for transforming folds instead of stream.
3211+--
3212+-- >>> lCompactGE n = Fold.many (MutArray.fCompactGE n)
3213+--
3214+-- Generates unpinned arrays irrespective of the pinning status of input
3215+-- arrays.
3216+{-# INLINE lCompactGE #-}
3217+lCompactGE :: forall m a. (MonadIO m, Unbox a)
3218+ => Int -> Fold m (MutArray a) () -> Fold m (MutArray a) ()
3219+lCompactGE = lCompactGeAs Unpinned
3220+
3221+-- | Pinned version of 'lCompactGE'.
3222+{-# INLINE lPinnedCompactGE #-}
3223+lPinnedCompactGE :: forall m a. (MonadIO m, Unbox a)
3224+ => Int -> Fold m (MutArray a) () -> Fold m (MutArray a) ()
3225+lPinnedCompactGE = lCompactGeAs Pinned
3226+
3227+-- | @compactGE n stream@ coalesces adjacent arrays in the @stream@ until
3228+-- the size becomes greater than or equal to @n@.
3229+--
3230+-- >>> compactGE n = Stream.foldMany (MutArray.fCompactGE n)
3231+--
3232+{-# INLINE compactGE #-}
3233+compactGE ::
3234+ (MonadIO m, Unbox a)
3235+ => Int -> Stream m (MutArray a) -> Stream m (MutArray a)
3236+compactGE n = D.foldMany (fCompactGE n)
3237+
3238+-- | 'compactEQ n' coalesces adajacent arrays in the input stream to
3239+-- arrays of exact size @n@.
3240+--
3241+-- /Unimplemented/
3242+{-# INLINE compactEQ #-}
3243+compactEQ :: -- (MonadIO m, Unbox a) =>
3244+ Int -> Stream m (MutArray a) -> Stream m (MutArray a)
3245+compactEQ _n = undefined -- D.parseManyD (pCompactEQ n)
3246+
3247+-------------------------------------------------------------------------------
3248+-- In-place mutation algorithms
24103249-------------------------------------------------------------------------------
24113250
24123251-- | Strip elements which match with predicate from both ends.
core/src/Streamly/Internal/Data/MutByteArray/Type.hs+47−12
@@ -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,19 +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
42+ , nil
3843 ) where
3944
4045import Control.Monad.IO.Class (MonadIO(..))
@@ -53,7 +58,7 @@ import GHC.Exts
5358
5459data PinnedState
5560 = Pinned
56− | Unpinned
61+ | Unpinned deriving (Show, Eq)
5762
5863-- XXX can use UnliftedNewtypes
5964
@@ -82,6 +87,9 @@ touch :: MutByteArray -> IO ()
8287touch (MutByteArray contents) =
8388 IO $ \s -> case touch# contents s of s' -> (# s', () #)
8489
90+-- XXX Some functions in this module are "IO" and others are "m", we need to
91+-- make it consistent.
92+
8593-- XXX We can provide another API for "unsafe" FFI calls passing an unlifted
8694-- pointer to the FFI call. For unsafe calls we do not need to pin the array.
8795-- We can pass an unlifted pointer to the FFI routine to avoid GC kicking in
@@ -110,9 +118,9 @@ touch (MutByteArray contents) =
110118--
111119-- /Pre-release/
112120--
113−{-# INLINE asPtrUnsafe #-}
114−asPtrUnsafe :: MonadIO m => MutByteArray -> (Ptr a -> m b) -> m b
115−asPtrUnsafe arr f = do
121+{-# INLINE unsafePinnedAsPtr #-}
122+unsafePinnedAsPtr :: MonadIO m => MutByteArray -> (Ptr a -> m b) -> m b
123+unsafePinnedAsPtr arr f = do
116124 contents <- liftIO $ pin arr
117125 let !ptr = Ptr (byteArrayContents#
118126 (unsafeCoerce# (getMutableByteArray# contents)))
@@ -120,14 +128,35 @@ asPtrUnsafe arr f = do
120128 liftIO $ touch contents
121129 return r
122130
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+
136+-- | For use with unsafe FFI functions. Does not force pin the array memory.
137+{-# INLINE unsafeAsPtr #-}
138+unsafeAsPtr :: MonadIO m => MutByteArray -> (Ptr a -> m b) -> m b
139+unsafeAsPtr arr f = do
140+ let !ptr = Ptr (byteArrayContents#
141+ (unsafeCoerce# (getMutableByteArray# arr)))
142+ r <- f ptr
143+ liftIO $ touch arr
144+ return r
145+
123146--------------------------------------------------------------------------------
124147-- Creation
125148--------------------------------------------------------------------------------
126149
127−{-# NOINLINE nil #-}
150+{-# NOINLINE empty #-}
151+empty :: MutByteArray
152+empty = unsafePerformIO $ new 0
153+
154+{-# DEPRECATED nil "Please use empty instead" #-}
128155nil :: MutByteArray
129−nil = unsafePerformIO $ new 0
156+nil = empty
130157
158+-- XXX add "newRounded" to round up the large size to the next page boundary
159+-- and return the allocated size.
131160{-# INLINE new #-}
132161new :: Int -> IO MutByteArray
133162new nbytes | nbytes < 0 =
@@ -141,7 +170,7 @@ new (I# nbytes) = IO $ \s ->
141170{-# INLINE pinnedNew #-}
142171pinnedNew :: Int -> IO MutByteArray
143172pinnedNew nbytes | nbytes < 0 =
144− errorWithoutStackTrace "pinnedNewByteArray: size must be >= 0"
173+ errorWithoutStackTrace "pinnedNew: size must be >= 0"
145174pinnedNew (I# nbytes) = IO $ \s ->
146175 case newPinnedByteArray# nbytes s of
147176 (# s', mbarr# #) ->
@@ -167,6 +196,12 @@ newBytesAs Pinned = pinnedNew
167196-- Copying
168197-------------------------------------------------------------------------------
169198
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+
170205-- | Put a sub range of a source array into a subrange of a destination array.
171206-- This is not safe as it does not check the bounds of neither the src array
172207-- 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+2−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
@@ -154,6 +142,8 @@ import Streamly.Internal.Data.Stream.Type
154142nil :: Applicative m => Stream m a
155143nil = Stream (\_ _ -> pure Stop) ()
156144
145+infixr 5 `cons`
146+
157147-- XXX implement in terms of consM?
158148-- cons x = consM (return x)
159149-- From an implementation perspective, StreamK.cons translates into a
core/src/Streamly/Internal/Data/Stream/Nesting.hs+19−146
@@ -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,9 +131,8 @@ module Streamly.Internal.Data.Stream.Nesting
147131 -- ** Splitting
148132 -- | A special case of parsing.
149133 , wordsBy
150− , splitOnSeq
151− , splitOnSuffixSeq
152− , sliceOnSuffix
134+ , splitOnSeq -- XXX splitOnSeg
135+ , splitOnSuffixSeq -- XXX splitOnSegSuffix, splitOnTrailer
153136
154137 -- XXX Implement these as folds or parsers instead.
155138 , splitOnSuffixSeqAny
@@ -158,9 +141,8 @@ module Streamly.Internal.Data.Stream.Nesting
158141
159142 -- * Transform (Nested Containers)
160143 -- | Opposite to compact in ArrayStream
161− , splitInnerBy
162− , splitInnerBySuffix
163− , intersectBySorted
144+ , splitInnerBy -- XXX innerSplitOn
145+ , splitInnerBySuffix -- XXX innerSplitOnSuffix
164146
165147 -- * Reduce By Streams
166148 , dropPrefix
@@ -182,12 +164,9 @@ import Fusion.Plugin.Types (Fuse(..))
182164import GHC.Types (SPEC(..))
183165
184166import Streamly.Internal.Data.Array.Type (Array(..))
185−import Streamly.Internal.Data.Fold.Step (Step(..))
186167import Streamly.Internal.Data.Fold.Type (Fold(..))
187168import Streamly.Internal.Data.Parser (ParseError(..))
188−import Streamly.Internal.Data.Refold.Type (Refold(..))
189169import Streamly.Internal.Data.SVar.Type (adaptState)
190−import Streamly.Internal.Data.Tuple.Strict (Tuple'(..))
191170import Streamly.Internal.Data.Unbox (Unbox, sizeOf)
192171import Streamly.Internal.Data.Unfold.Type (Unfold(..))
193172
@@ -613,49 +592,6 @@ mergeFstBy :: -- Monad m =>
613592mergeFstBy _f _m1 _m2 = undefined
614593 -- fromStreamK $ D.mergeFstBy f (toStreamD m1) (toStreamD m2)
615594
616−-------------------------------------------------------------------------------
617−-- Intersection of sorted streams
618−-------------------------------------------------------------------------------
619−
620−-- Assuming the streams are sorted in ascending order
621−{-# INLINE_NORMAL intersectBySorted #-}
622−intersectBySorted :: Monad m
623− => (a -> a -> Ordering) -> Stream m a -> Stream m a -> Stream m a
624−intersectBySorted cmp (Stream stepa ta) (Stream stepb tb) =
625− Stream step
626− ( ta -- left stream state
627− , tb -- right stream state
628− , Nothing -- left value
629− , Nothing -- right value
630− )
631−
632− where
633−
634− {-# INLINE_LATE step #-}
635− -- step 1, fetch the first value
636− step gst (sa, sb, Nothing, b) = do
637− r <- stepa gst sa
638− return $ case r of
639− Yield a sa' -> Skip (sa', sb, Just a, b) -- step 2/3
640− Skip sa' -> Skip (sa', sb, Nothing, b)
641− Stop -> Stop
642−
643− -- step 2, fetch the second value
644− step gst (sa, sb, a@(Just _), Nothing) = do
645− r <- stepb gst sb
646− return $ case r of
647− Yield b sb' -> Skip (sa, sb', a, Just b) -- step 3
648− Skip sb' -> Skip (sa, sb', a, Nothing)
649− Stop -> Stop
650−
651− -- step 3, compare the two values
652− step _ (sa, sb, Just a, Just b) = do
653− let res = cmp a b
654− return $ case res of
655− GT -> Skip (sa, sb, Just a, Nothing) -- step 2
656− LT -> Skip (sa, sb, Nothing, Just b) -- step 1
657− EQ -> Yield a (sa, sb, Nothing, Just b) -- step 1
658−
659595------------------------------------------------------------------------------
660596-- Combine N Streams - unfoldMany
661597------------------------------------------------------------------------------
@@ -1294,77 +1230,6 @@ foldIterateM func seed0 (Stream step state) =
12941230 stepOuter _ (FIterYield a next) = return $ Yield a next
12951231 stepOuter _ FIterStop = return Stop
12961232
1297−{-# ANN type CIterState Fuse #-}
1298−data CIterState s f fs b
1299− = CIterInit s f
1300− | CIterConsume s fs
1301− | CIterYield b (CIterState s f fs b)
1302− | CIterStop
1303−
1304−-- | Like 'foldIterateM' but using the 'Refold' type instead. This could be
1305−-- much more efficient due to stream fusion.
1306−--
1307−-- /Internal/
1308−{-# INLINE_NORMAL refoldIterateM #-}
1309−refoldIterateM ::
1310− Monad m => Refold m b a b -> m b -> Stream m a -> Stream m b
1311−refoldIterateM (Refold fstep finject fextract) initial (Stream step state) =
1312− Stream stepOuter (CIterInit state initial)
1313−
1314− where
1315−
1316− {-# INLINE iterStep #-}
1317− iterStep st action = do
1318− res <- action
1319− return
1320− $ Skip
1321− $ case res of
1322− FL.Partial fs -> CIterConsume st fs
1323− FL.Done fb -> CIterYield fb $ CIterInit st (return fb)
1324−
1325− {-# INLINE_LATE stepOuter #-}
1326− stepOuter _ (CIterInit st action) = do
1327− iterStep st (action >>= finject)
1328− stepOuter gst (CIterConsume st fs) = do
1329− r <- step (adaptState gst) st
1330− case r of
1331− Yield x s -> iterStep s (fstep fs x)
1332− Skip s -> return $ Skip $ CIterConsume s fs
1333− Stop -> do
1334− b <- fextract fs
1335− return $ Skip $ CIterYield b CIterStop
1336− stepOuter _ (CIterYield a next) = return $ Yield a next
1337− stepOuter _ CIterStop = return Stop
1338−
1339−-- "n" elements at the end are dropped by the fold.
1340−{-# INLINE sliceBy #-}
1341−sliceBy :: Monad m => Fold m a Int -> Int -> Refold m (Int, Int) a (Int, Int)
1342−sliceBy (Fold step1 initial1 extract1 _final) n = Refold step inject extract
1343−
1344− where
1345−
1346− inject (i, len) = do
1347− r <- initial1
1348− return $ case r of
1349− Partial s -> Partial $ Tuple' (i + len + n) s
1350− Done l -> Done (i, l)
1351−
1352− step (Tuple' i s) x = do
1353− r <- step1 s x
1354− return $ case r of
1355− Partial s1 -> Partial $ Tuple' i s1
1356− Done len -> Done (i, len)
1357−
1358− extract (Tuple' i s) = (i,) <$> extract1 s
1359−
1360−{-# INLINE sliceOnSuffix #-}
1361−sliceOnSuffix :: Monad m => (a -> Bool) -> Stream m a -> Stream m (Int, Int)
1362−sliceOnSuffix predicate =
1363− -- Scan the stream with the given refold
1364− refoldIterateM
1365− (sliceBy (FL.takeEndBy_ predicate FL.length) 1)
1366− (return (-1, 0))
1367−
13681233------------------------------------------------------------------------------
13691234-- Parsing
13701235------------------------------------------------------------------------------
@@ -3065,12 +2930,13 @@ splitInnerBy splitter joiner (Stream step1 state1) =
30652930-- | Performs infix separator style splitting.
30662931{-# INLINE_NORMAL splitInnerBySuffix #-}
30672932splitInnerBySuffix
3068− :: (Monad m, Eq (f a), Monoid (f a))
3069− => (f a -> m (f a, Maybe (f a))) -- splitter
2933+ :: Monad m
2934+ => (f a -> Bool) -- isEmpty?
2935+ -> (f a -> m (f a, Maybe (f a))) -- splitter
30702936 -> (f a -> f a -> m (f a)) -- joiner
30712937 -> Stream m (f a)
30722938 -> Stream m (f a)
3073−splitInnerBySuffix splitter joiner (Stream step1 state1) =
2939+splitInnerBySuffix isEmpty splitter joiner (Stream step1 state1) =
30742940 Stream step (SplitInitial state1)
30752941
30762942 where
@@ -3097,10 +2963,11 @@ splitInnerBySuffix splitter joiner (Stream step1 state1) =
30972963 Nothing -> Skip (SplitBuffering s buf')
30982964 Just x2 -> Skip (SplitYielding buf' (SplitSplitting s x2))
30992965 Skip s -> return $ Skip (SplitBuffering s buf)
3100− Stop -> return $
3101− if buf == mempty
3102− then Stop
3103− else Skip (SplitYielding buf SplitFinishing)
2966+ Stop ->
2967+ return $
2968+ if isEmpty buf
2969+ then Stop
2970+ else Skip (SplitYielding buf SplitFinishing)
31042971
31052972 step _ (SplitSplitting st buf) = do
31062973 (x1, mx2) <- splitter buf
@@ -3119,6 +2986,8 @@ splitInnerBySuffix splitter joiner (Stream step1 state1) =
31192986--
31202987-- Space: @O(1)@
31212988--
2989+-- See also stripPrefix.
2990+--
31222991-- /Unimplemented/
31232992{-# INLINE dropPrefix #-}
31242993dropPrefix ::
@@ -3131,6 +3000,8 @@ dropPrefix = error "Not implemented yet!"
31313000--
31323001-- Space: @O(n)@ where n is the length of the infix.
31333002--
3003+-- See also stripInfix.
3004+--
31343005-- /Unimplemented/
31353006{-# INLINE dropInfix #-}
31363007dropInfix ::
@@ -3143,6 +3014,8 @@ dropInfix = error "Not implemented yet!"
31433014--
31443015-- Space: @O(n)@ where n is the length of the suffix.
31453016--
3017+-- See also stripSuffix.
3018+--
31463019-- /Unimplemented/
31473020{-# INLINE dropSuffix #-}
31483021dropSuffix ::
core/src/Streamly/Internal/Data/Stream/Top.hs+243−115
@@ -8,38 +8,73 @@
88-- Portability : GHC
99--
1010-- Top level module that can depend on all other lower level Stream modules.
11+--
12+-- Design notes:
13+--
14+-- The order of arguments in the join operations should ideally be opposite. It
15+-- should be such that the infinite stream is the last one. The transformation
16+-- should be on the last argument, so if you curry the functions with all other
17+-- arguments we get a @Stream -> Stream@ function. The first stream argument
18+-- may be considered as a config or modifier for the operation.
19+--
20+-- Benefit of changing the order is that we get a more intuitive Stream ->
21+-- Stream transformation after currying all other arguments. The inner loop
22+-- streams become arguments for the transformation, more like local modifiers
23+-- for the global outer stream as the last argument. Thus we can continue using
24+-- transformations on the outer stream in a composed pipeline. Otherwise we can
25+-- use flip to flip the order.
26+--
27+-- The fact that the inner stream can be used in the loop multiple times also
28+-- tells that this is not the real effectful stream, it is more like a pure
29+-- stream or an array. In fact we may consider using an Identity streams as
30+-- inner streams in which case these functions will not look nice.
31+--
32+-- Downsides:
33+--
34+-- * Maybe less intuitive to think about, because we usually think the first
35+-- stream as the outer loop and second as the inner.
36+-- * Zip and merge operations will continue using the opposite order.
37+-- * Need to change the order of cross, crossWith operations as well
38+-- * It will be inconsistent with Data.List. The functions cannot be used as
39+-- intuitive operators.
40+--
41+-- The choice is similar to concatMap vs bind. concatMap is pipeline
42+-- composition friendly but bind is user intuition friendly. Another option is
43+-- to have other functions with a different argument order e.g. flippedCross
44+-- instead of cross.
45+--
46+-- If we change the order we have to make sure that we have a consistent
47+-- convention for set-like and the cross join operations.
1148
1249module Streamly.Internal.Data.Stream.Top
1350 (
14− -- * Transformation
15− -- ** Sampling
51+ -- * Sampling
1652 -- | Value agnostic filtering.
1753 strideFromThen
1854
19− -- * Nesting
20− -- ** Set like operations
21− -- | These are not exactly set operations because streams are not
22− -- necessarily sets, they may have duplicated elements. These operations
23− -- are generic i.e. they work on streams of unconstrained types, therefore,
24− -- they have quadratic performance characterstics. For better performance
25− -- using Set structures see the Streamly.Internal.Data.Stream.Container
26− -- module.
27− , filterInStreamGenericBy
28− , deleteInStreamGenericBy
29− , unionWithStreamGenericBy
30−
31− -- ** Set like operations on sorted streams
32− , filterInStreamAscBy
33− , deleteInStreamAscBy
34− , unionWithStreamAscBy
35−
36− -- ** Join operations
37− , joinInnerGeneric
38−
39− -- * Joins on sorted stream
40− , joinInnerAscBy
41− , joinLeftAscBy
42− , joinOuterAscBy
55+ -- * Straight Joins
56+ -- | These are set-like operations but not exactly set operations because
57+ -- streams are not necessarily sets, they may have duplicated elements.
58+ -- These operations are generic i.e. they work on streams of unconstrained
59+ -- types, therefore, they have quadratic performance characterstics. For
60+ -- better performance using Set or Map structures see the
61+ -- Streamly.Internal.Data.Stream.Container module.
62+ , intersectBy
63+ , deleteFirstsBy
64+ , unionBy
65+
66+ -- Set like operations on sorted streams
67+ , sortedIntersectBy
68+ , sortedDeleteFirstsBy
69+ , sortedUnionBy
70+
71+ -- * Cross Joins
72+ , innerJoin
73+
74+ -- Joins on sorted stream
75+ , innerSortedJoin
76+ , leftSortedJoin
77+ , outerSortedJoin
4378 )
4479where
4580
@@ -48,7 +83,7 @@ where
4883import Control.Monad.IO.Class (MonadIO(..))
4984import Data.IORef (newIORef, readIORef, modifyIORef')
5085import Streamly.Internal.Data.Fold.Type (Fold)
51−import Streamly.Internal.Data.Stream.Type (Stream, cross)
86+import Streamly.Internal.Data.Stream.Type (Stream(..), Step(..), cross)
5287
5388import qualified Data.List as List
5489import qualified Streamly.Internal.Data.Fold as Fold
@@ -110,29 +145,40 @@ strideFromThen offset stride =
110145-- binary search if we have an Ord instance or Ordering returning function. The
111146-- time complexity would then become (m x log n).
112147
113−-- | Like 'cross' but emits only those tuples where @a == b@ using the
114−-- supplied equality predicate.
148+-- | Like 'cross' but emits only those tuples where @a == b@ using the supplied
149+-- equality predicate. This is essentially a @cross intersection@ of two
150+-- streams.
115151--
116152-- Definition:
117153--
118−-- >>> joinInnerGeneric eq s1 s2 = Stream.filter (\(a, b) -> a `eq` b) $ Stream.cross s1 s2
154+-- >>> innerJoin eq s1 s2 = Stream.filter (\(a, b) -> a `eq` b) $ Stream.cross s1 s2
155+--
156+-- The second (inner) stream must be finite. Moreover, it must be either pure
157+-- or capable of multiple evaluations. If not then the caller should cache it
158+-- in an 'Data.Array.Array', if the type does not have an 'Unbox' instance then
159+-- use the Generic 'Data.Array.Generic.Array'. Convert the array to stream
160+-- before calling this function. Caching may also improve performance if the
161+-- stream is expensive to evaluate.
119162--
120−-- You should almost always prefer @joinInnerOrd@ over 'joinInnerGeneric' if
121−-- possible. @joinInnerOrd@ is an order of magnitude faster but may take more
122−-- space for caching the second stream.
163+-- If you care about performance this function should be your last choice among
164+-- all inner joins. 'Streamly.Internal.Data.Unfold.innerJoin' is a much faster
165+-- fused alternative. 'innerSortedJoin' is a faster alternative when streams
166+-- are sorted. 'innerOrdJoin' is an order of magnitude faster alternative when
167+-- the type has an 'Ord' instance.
123168--
124−-- See 'Streamly.Internal.Data.Unfold.joinInnerGeneric' for a much faster fused
125−-- alternative.
169+-- Note: Conceptually, this is a commutative operation. Result includes all the
170+-- elements from the left and the right stream. The order of streams can be
171+-- changed without affecting results, except for the ordering within the tuple.
126172--
127173-- Time: O(m x n)
128174--
129175-- /Pre-release/
130−{-# INLINE joinInnerGeneric #-}
131−joinInnerGeneric :: Monad m =>
176+{-# INLINE innerJoin #-}
177+innerJoin :: Monad m =>
132178 (a -> b -> Bool) -> Stream m a -> Stream m b -> Stream m (a, b)
133−joinInnerGeneric eq s1 s2 = Stream.filter (\(a, b) -> a `eq` b) $ cross s1 s2
179+innerJoin eq s1 s2 = Stream.filter (\(a, b) -> a `eq` b) $ cross s1 s2
134180{-
135−joinInnerGeneric eq s1 s2 = do
181+innerJoin eq s1 s2 = do
136182 -- ConcatMap works faster than bind
137183 Stream.concatMap (\a ->
138184 Stream.concatMap (\b ->
@@ -143,44 +189,44 @@ joinInnerGeneric eq s1 s2 = do
143189 ) s1
144190-}
145191
146−-- | A more efficient 'joinInner' for sorted streams.
192+-- | A more efficient 'innerJoin' for sorted streams.
147193--
148194-- Space: O(1)
149195--
150196-- Time: O(m + n)
151197--
152198-- /Unimplemented/
153−{-# INLINE joinInnerAscBy #-}
154−joinInnerAscBy ::
199+{-# INLINE innerSortedJoin #-}
200+innerSortedJoin ::
155201 (a -> b -> Ordering) -> Stream m a -> Stream m b -> Stream m (a, b)
156−joinInnerAscBy = undefined
202+innerSortedJoin = undefined
157203
158−-- | A more efficient 'joinLeft' for sorted streams.
204+-- | A more efficient 'leftJoin' for sorted streams.
159205--
160206-- Space: O(1)
161207--
162208-- Time: O(m + n)
163209--
164210-- /Unimplemented/
165−{-# INLINE joinLeftAscBy #-}
166−joinLeftAscBy :: -- Monad m =>
211+{-# INLINE leftSortedJoin #-}
212+leftSortedJoin :: -- Monad m =>
167213 (a -> b -> Ordering) -> Stream m a -> Stream m b -> Stream m (a, Maybe b)
168−joinLeftAscBy _eq _s1 _s2 = undefined
214+leftSortedJoin _eq _s1 _s2 = undefined
169215
170−-- | A more efficient 'joinOuter' for sorted streams.
216+-- | A more efficient 'outerJoin' for sorted streams.
171217--
172218-- Space: O(1)
173219--
174220-- Time: O(m + n)
175221--
176222-- /Unimplemented/
177−{-# INLINE joinOuterAscBy #-}
178−joinOuterAscBy :: -- Monad m =>
223+{-# INLINE outerSortedJoin #-}
224+outerSortedJoin :: -- Monad m =>
179225 (a -> b -> Ordering)
180226 -> Stream m a
181227 -> Stream m b
182228 -> Stream m (Maybe a, Maybe b)
183−joinOuterAscBy _eq _s1 _s2 = undefined
229+outerSortedJoin _eq _s1 _s2 = undefined
184230
185231------------------------------------------------------------------------------
186232-- Set operations (special joins)
@@ -190,8 +236,8 @@ joinOuterAscBy _eq _s1 _s2 = undefined
190236-- the best would be to use an Array with linear search. If the second stream
191237-- is sorted we can also use a binary search, using Ord constraint.
192238
193−-- | Keep only those elements in the second stream that are present in the
194−-- first stream too. The first stream is folded to a container using the
239+-- | Keep only those elements in the first stream that are present in the
240+-- second stream too. The second stream is folded to a container using the
195241-- supplied fold and then the elements in the container are looked up using the
196242-- supplied lookup function.
197243--
@@ -206,23 +252,30 @@ filterStreamWith :: Monad m =>
206252filterStreamWith fld member s1 s2 =
207253 Stream.concatEffect
208254 $ do
209− xs <- Stream.fold fld s1
210− return $ Stream.filter (`member` xs) s2
255+ xs <- Stream.fold fld s2
256+ return $ Stream.filter (`member` xs) s1
211257
212−-- | 'filterInStreamGenericBy' retains only those elements in the second stream that
213−-- are present in the first stream.
214−--
215−-- >>> Stream.fold Fold.toList $ Stream.filterInStreamGenericBy (==) (Stream.fromList [1,2,2,4]) (Stream.fromList [2,1,1,3])
216−-- [2,1,1]
217−--
218−-- >>> Stream.fold Fold.toList $ Stream.filterInStreamGenericBy (==) (Stream.fromList [2,1,1,3]) (Stream.fromList [1,2,2,4])
219−-- [1,2,2]
258+-- XXX instead of folding the second stream to a list we could use it directly.
259+-- If the user wants they can generate the stream from an array and also call
260+-- uniq or nub on it. We can provide a convenience Stream -> Stream to cache
261+-- a finite stream in an array and serve it from the cache. The user can decide
262+-- what is best based on the context. They can also choose to use a boxed or
263+-- unboxed array for caching. To force caching we can make the second stream
264+-- monad type Identity. But that may be less flexible. One option is to use
265+-- cachedIntersectBy etc for automatic caching.
266+
267+-- | 'intersectBy' returns a subsequence of the first stream which intersects
268+-- with the second stream. Note that this is not a commutative operation unlike
269+-- a set intersection, because of duplicate elements in the stream the order of
270+-- the streams matters. This is similar to 'Data.List.intersectBy'. Note that
271+-- intersectBy is a special case of 'innerJoin'.
220272--
221−-- Similar to the list intersectBy operation but with the stream argument order
222−-- flipped.
273+-- >>> f s1 s2 = Stream.fold Fold.toList $ Stream.intersectBy (==) (Stream.fromList s1) (Stream.fromList s2)
274+-- >>> f [1,3,4,4,5]) [2,3,4,5,5]
275+-- [3,4,4,5]
223276--
224−-- The first stream must be finite and must not block. Second stream is
225−-- processed only after the first stream is fully realized.
277+-- First stream can be infinite, the second stream must be finite and must be
278+-- capable of multiple evaluations.
226279--
227280-- Space: O(n) where @n@ is the number of elements in the second stream.
228281--
@@ -230,46 +283,87 @@ filterStreamWith fld member s1 s2 =
230283-- @n@ is the number of elements in the second stream.
231284--
232285-- /Pre-release/
233−{-# INLINE filterInStreamGenericBy #-}
234−filterInStreamGenericBy :: Monad m =>
286+{-# INLINE intersectBy #-}
287+intersectBy :: Monad m =>
235288 (a -> a -> Bool) -> Stream m a -> Stream m a -> Stream m a
236−filterInStreamGenericBy eq =
289+intersectBy eq =
237290 -- XXX Use an (unboxed) array instead.
238291 filterStreamWith
239292 (Fold.scanMaybe (Fold.uniqBy eq) Fold.toListRev)
240293 (List.any . eq)
241294
242−-- | Like 'filterInStreamGenericBy' but assumes that the input streams are sorted in
295+-------------------------------------------------------------------------------
296+-- Intersection of sorted streams
297+-------------------------------------------------------------------------------
298+
299+-- XXX The sort order is not important as long both the streams have the same
300+-- sort order. We need to move only in one direction in each stream.
301+-- XXX Fix the argument order to use the same behavior as intersectBy.
302+
303+-- | Like 'intersectBy' but assumes that the input streams are sorted in
243304-- ascending order. To use it on streams sorted in descending order pass an
244305-- inverted comparison function returning GT for less than and LT for greater
245306-- than.
246307--
308+-- Both streams can be infinite.
309+--
247310-- Space: O(1)
248311--
249312-- Time: O(m+n)
250313--
251314-- /Pre-release/
252−{-# INLINE filterInStreamAscBy #-}
253−filterInStreamAscBy :: Monad m =>
315+{-# INLINE_NORMAL sortedIntersectBy #-}
316+sortedIntersectBy :: Monad m =>
254317 (a -> a -> Ordering) -> Stream m a -> Stream m a -> Stream m a
255−filterInStreamAscBy eq s1 s2 = Stream.intersectBySorted eq s2 s1
318+sortedIntersectBy cmp (Stream stepa ta) (Stream stepb tb) =
319+ Stream step
320+ ( ta -- left stream state
321+ , tb -- right stream state
322+ , Nothing -- left value
323+ , Nothing -- right value
324+ )
256325
257−-- | Delete all elements of the first stream from the seconds stream. If an
258−-- element occurs multiple times in the first stream as many occurrences of it
259−-- are deleted from the second stream.
260−--
261−-- >>> Stream.fold Fold.toList $ Stream.deleteInStreamGenericBy (==) (Stream.fromList [1,2,3]) (Stream.fromList [1,2,2])
262−-- [2]
326+ where
327+
328+ {-# INLINE_LATE step #-}
329+ -- step 1, fetch the first value
330+ step gst (sa, sb, Nothing, b) = do
331+ r <- stepa gst sa
332+ return $ case r of
333+ Yield a sa' -> Skip (sa', sb, Just a, b) -- step 2/3
334+ Skip sa' -> Skip (sa', sb, Nothing, b)
335+ Stop -> Stop
336+
337+ -- step 2, fetch the second value
338+ step gst (sa, sb, a@(Just _), Nothing) = do
339+ r <- stepb gst sb
340+ return $ case r of
341+ Yield b sb' -> Skip (sa, sb', a, Just b) -- step 3
342+ Skip sb' -> Skip (sa, sb', a, Nothing)
343+ Stop -> Stop
344+
345+ -- step 3, compare the two values
346+ step _ (sa, sb, Just a, Just b) = do
347+ let res = cmp a b
348+ return $ case res of
349+ GT -> Skip (sa, sb, Just a, Nothing) -- step 2
350+ LT -> Skip (sa, sb, Nothing, Just b) -- step 1
351+ EQ -> Yield a (sa, sb, Nothing, Just b) -- step 1
352+
353+-- | Returns a subsequence of the first stream, deleting first occurrences of
354+-- those elements that are present in the second stream. Note that this is not
355+-- a commutative operation. This is similar to the 'Data.List.deleteFirstsBy'.
263356--
264−-- The following laws hold:
357+-- >>> f xs ys = Stream.fold Fold.toList $ Stream.deleteFirstsBy (==) (Stream.fromList xs) (Stream.fromList ys)
358+-- >>> f [1,2,2,3,3,5] [1,2,2,3,4]
359+-- [2,3,5]
265360--
266−-- > deleteInStreamGenericBy (==) s1 (s1 `append` s2) === s2
267−-- > deleteInStreamGenericBy (==) s1 (s1 `interleave` s2) === s2
361+-- The following holds:
268362--
269−-- Same as the list 'Data.List.//' operation but with argument order flipped.
363+-- > deleteFirstsBy (==) (Stream.nub s2 `append` s1) s2 === s1
364+-- > deleteFirstsBy (==) (Stream.nub s2 `interleave` s1) s2 === s1
270365--
271−-- The first stream must be finite and must not block. Second stream is
272−-- processed only after the first stream is fully realized.
366+-- First stream can be infinite, second stream must be finite.
273367--
274368-- Space: O(m) where @m@ is the number of elements in the first stream.
275369--
@@ -277,58 +371,83 @@ filterInStreamAscBy eq s1 s2 = Stream.intersectBySorted eq s2 s1
277371-- @n@ is the number of elements in the second stream.
278372--
279373-- /Pre-release/
280−{-# INLINE deleteInStreamGenericBy #-}
281−deleteInStreamGenericBy :: Monad m =>
374+{-# INLINE deleteFirstsBy #-}
375+deleteFirstsBy :: Monad m =>
282376 (a -> a -> Bool) -> Stream m a -> Stream m a -> Stream m a
283−deleteInStreamGenericBy eq s1 s2 =
284− Stream.concatEffect
285− $ do
286− -- This may work well if s1 is small
287− -- If s1 is big we can go through s1, deleting elements from s2 and
288− -- not emitting an element if it was successfully deleted from s2.
289− -- we will need a deleteBy that can return whether the element was
290− -- deleted or not.
291− xs <- Stream.fold Fold.toList s2
292− let f = Fold.foldl' (flip (List.deleteBy eq)) xs
293− fmap Stream.fromList $ Stream.fold f s1
377+deleteFirstsBy eq s2 s1 =
378+ -- XXX s2 can be a sorted mutable array and we can use binary
379+ -- search to find. Mark the element deleted, count the deletions
380+ -- and reconsolidate the array when a min number of elements is
381+ -- deleted.
294382
295−-- | A more efficient 'deleteInStreamGenericBy' for streams sorted in ascending order.
383+ -- XXX Use StreamK or list as second argument instead of Stream to avoid
384+ -- concatEffect?
385+ Stream.concatEffect $ do
386+ xs <- Stream.toList s1
387+ -- It reverses the list but that is fine.
388+ let del x =
389+ List.foldl' (\(ys,res) y ->
390+ if x `eq` y
391+ then (ys, True)
392+ else (x:ys, res)) ([], False)
393+ g (ys,_) x =
394+ let (ys1, deleted) = del x ys
395+ in if deleted
396+ then (ys1, Nothing)
397+ else (ys1, Just x)
398+ in return
399+ $ Stream.catMaybes
400+ $ fmap snd
401+ $ Stream.postscanl' g (xs, Nothing) s2
402+
403+-- | A more efficient 'deleteFirstsBy' for streams sorted in ascending order.
404+--
405+-- Both streams can be infinite.
296406--
297407-- Space: O(1)
298408--
299409-- /Unimplemented/
300−{-# INLINE deleteInStreamAscBy #-}
301−deleteInStreamAscBy :: -- (Monad m) =>
410+{-# INLINE sortedDeleteFirstsBy #-}
411+sortedDeleteFirstsBy :: -- (Monad m) =>
302412 (a -> a -> Ordering) -> Stream m a -> Stream m a -> Stream m a
303−deleteInStreamAscBy _eq _s1 _s2 = undefined
413+sortedDeleteFirstsBy _eq _s1 _s2 = undefined
304414
305415-- XXX Remove the MonadIO constraint. We can just cache one stream and then
306416-- implement using differenceEqBy.
307417
308−-- | This essentially appends to the second stream all the occurrences of
309−-- elements in the first stream that are not already present in the second
310−-- stream.
418+-- | Returns the first stream appended with those unique elements from the
419+-- second stream that are not already present in the first stream. Note that
420+-- this is not a commutative operation unlike a set union, argument order
421+-- matters. The behavior is similar to the 'Data.List.unionBy'.
311422--
312423-- Equivalent to the following except that @s2@ is evaluated only once:
313424--
314−-- >>> unionWithStreamGenericBy eq s1 s2 = s2 `Stream.append` (Stream.deleteInStreamGenericBy eq s2 s1)
425+-- >>> unionBy eq s1 s2 = s1 `Stream.append` Stream.deleteFirstsBy eq s1 (Stream.nub s2)
315426--
316427-- Example:
317428--
318−-- >>> Stream.fold Fold.toList $ Stream.unionWithStreamGenericBy (==) (Stream.fromList [1,1,2,3]) (Stream.fromList [1,2,2,4])
429+-- >>> f s1 s2 = Stream.fold Fold.toList $ Stream.unionBy (==) (Stream.fromList s1) (Stream.fromList s2)
430+-- >>> f [1,2,2,4] [1,1,2,3,3]
319431-- [1,2,2,4,3]
320432--
433+-- First stream can be infinite, but second stream must be finite. Note that if
434+-- the first stream is infinite the union means just the first stream. Thus
435+-- union is useful only when both streams are finite. See 'sortedUnionBy' where
436+-- union can work on infinite streams if they are sorted.
437+--
321438-- Space: O(n)
322439--
323440-- Time: O(m x n)
324441--
325442-- /Pre-release/
326−{-# INLINE unionWithStreamGenericBy #-}
327−unionWithStreamGenericBy :: MonadIO m =>
443+{-# INLINE unionBy #-}
444+unionBy :: MonadIO m =>
328445 (a -> a -> Bool) -> Stream m a -> Stream m a -> Stream m a
329−unionWithStreamGenericBy eq s1 s2 =
446+unionBy eq s2 s1 =
330447 Stream.concatEffect
331448 $ do
449+ -- XXX use a rewrite rule such that if a list converted to stream
450+ -- is passed to unionBy then this becomes an identity operation.
332451 xs <- Stream.fold Fold.toList s1
333452 -- XXX we can use postscanlMAfter' instead of IORef
334453 ref <- liftIO $ newIORef $! List.nubBy eq xs
@@ -341,12 +460,21 @@ unionWithStreamGenericBy eq s1 s2 =
341460 return $ Stream.fromList xs1
342461 return $ Stream.mapM f s2 `Stream.append` s3
343462
344−-- | A more efficient 'unionWithStreamGenericBy' for sorted streams.
463+-- | A more efficient 'unionBy' for sorted streams.
464+--
465+-- Note that the behavior is different from 'unionBy'. In 'unionBy' we append
466+-- the unique elements from second stream only after exhausting the first one
467+-- whereas in sorted streams we can determine unique elements early even when
468+-- we are going through the first stream. Thus the result is an interleaving of
469+-- the two streams, merging those elements from the second stream that are not
470+-- present in the first.
345471--
346472-- Space: O(1)
347473--
474+-- Both streams can be infinite.
475+--
348476-- /Unimplemented/
349−{-# INLINE unionWithStreamAscBy #-}
350−unionWithStreamAscBy :: -- (Monad m) =>
477+{-# INLINE sortedUnionBy #-}
478+sortedUnionBy :: -- (Monad m) =>
351479 (a -> a -> Ordering) -> Stream m a -> Stream m a -> Stream m a
352−unionWithStreamAscBy _eq _s1 _s2 = undefined
480+sortedUnionBy _eq _s1 _s2 = undefined
core/src/Streamly/Internal/Data/Stream/Transform.hs+53−27
@@ -21,8 +21,6 @@ module Streamly.Internal.Data.Stream.Transform
2121
2222 -- * Mapping
2323 -- | Stateless one-to-one maps.
24− , map
25− , mapM
2624 , sequence
2725
2826 -- * Mapping Effects
@@ -75,7 +73,7 @@ module Streamly.Internal.Data.Stream.Transform
7573 , scanMaybe
7674 , filter
7775 , filterM
78− , deleteBy
76+ , deleteBy -- deleteOnceBy?
7977 , uniqBy
8078 , uniq
8179 , prune
@@ -83,9 +81,8 @@ module Streamly.Internal.Data.Stream.Transform
8381
8482 -- * Trimming
8583 -- | Produce a subset of the stream trimmed at ends.
86− , take
87− , takeWhile
88− , takeWhileM
84+ , initNonEmpty
85+ , tailNonEmpty
8986 , takeWhileLast
9087 , takeWhileAround
9188 , drop
@@ -132,7 +129,6 @@ module Streamly.Internal.Data.Stream.Transform
132129 -- * Searching
133130 , findIndices
134131 , elemIndices
135− , slicesBy
136132
137133 -- * Rolling map
138134 -- | Map using the previous element.
@@ -1074,6 +1070,55 @@ repeated = undefined
10741070-- Trimming
10751071------------------------------------------------------------------------------
10761072
1073+-- | init for non-empty streams, fails for empty stream case.
1074+--
1075+{-# INLINE initNonEmpty #-}
1076+initNonEmpty :: Monad m => Stream m a -> Stream m a
1077+initNonEmpty (Stream step1 state1) = Stream step (Nothing, state1)
1078+
1079+ where
1080+
1081+ step gst (Nothing, s1) = do
1082+ r <- step1 (adaptState gst) s1
1083+ return $
1084+ case r of
1085+ Yield x s -> Skip (Just x, s)
1086+ Skip s -> Skip (Nothing, s)
1087+ Stop -> error "initNonEmpty: empty Stream"
1088+
1089+ step gst (Just a, s1) = do
1090+ r <- step1 (adaptState gst) s1
1091+ return $
1092+ case r of
1093+ Yield x s -> Yield a (Just x, s)
1094+ Skip s -> Skip (Just a, s)
1095+ Stop -> Stop
1096+
1097+-- | tail for non-empty streams, fails for empty stream case.
1098+--
1099+-- See also 'tail' for a non-partial version of this function..
1100+{-# INLINE tailNonEmpty #-}
1101+tailNonEmpty :: Monad m => Stream m a -> Stream m a
1102+tailNonEmpty (Stream step1 state1) = Stream step (Nothing, state1)
1103+
1104+ where
1105+
1106+ step gst (Nothing, s1) = do
1107+ r <- step1 (adaptState gst) s1
1108+ return $
1109+ case r of
1110+ Yield x s -> Skip (Just x, s)
1111+ Skip s -> Skip (Nothing, s)
1112+ Stop -> error "tailNonEmpty: empty Stream"
1113+
1114+ step gst (Just a, s1) = do
1115+ r <- step1 (adaptState gst) s1
1116+ return $
1117+ case r of
1118+ Yield x s -> Yield x (Just x, s)
1119+ Skip s -> Skip (Just a, s)
1120+ Stop -> Stop
1121+
10771122-- | Take all consecutive elements at the end of the stream for which the
10781123-- predicate is true.
10791124--
@@ -1537,7 +1582,7 @@ reverse m = Stream step Nothing
15371582{-# INLINE reverseUnbox #-}
15381583reverseUnbox :: (MonadIO m, Unbox a) => Stream m a -> Stream m a
15391584reverseUnbox =
1540− A.flattenArraysRev -- unfoldMany A.readRev
1585+ A.concatRev -- unfoldMany A.readerRev
15411586 . fromStreamK
15421587 . K.reverse
15431588 . toStreamK
@@ -1709,25 +1754,6 @@ findIndices p (Stream step state) = Stream step' (state, 0)
17091754elemIndices :: (Monad m, Eq a) => a -> Stream m a -> Stream m Int
17101755elemIndices a = findIndices (== a)
17111756
1712−{-# INLINE_NORMAL slicesBy #-}
1713−slicesBy :: Monad m => (a -> Bool) -> Stream m a -> Stream m (Int, Int)
1714−slicesBy p (Stream step1 state1) = Stream step (Just (state1, 0, 0))
1715−
1716− where
1717−
1718− {-# INLINE_LATE step #-}
1719− step gst (Just (st, i, len)) = i `seq` len `seq` do
1720− r <- step1 (adaptState gst) st
1721− return
1722− $ case r of
1723− Yield x s ->
1724− if p x
1725− then Yield (i, len + 1) (Just (s, i + len + 1, 0))
1726− else Skip (Just (s, i, len + 1))
1727− Skip s -> Skip (Just (s, i, len))
1728− Stop -> if len == 0 then Stop else Yield (i, len) Nothing
1729− step _ Nothing = return Stop
1730−
17311757------------------------------------------------------------------------------
17321758-- Rolling map
17331759------------------------------------------------------------------------------
core/src/Streamly/Internal/Data/Stream/Transformer.hs+2−1
@@ -11,10 +11,11 @@
1111
1212module Streamly.Internal.Data.Stream.Transformer
1313 (
14+ -- * Fold to Transformer Monad
1415 foldlT
1516 , foldrT
1617
17− -- * Transform Inner Monad
18+ -- * Inner Monad Operations
1819 , liftInner
1920 , runReaderT
2021 , usingReaderT
core/src/Streamly/Internal/Data/Stream/Type.hs+132−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
@@ -123,14 +132,22 @@ module Streamly.Internal.Data.Stream.Type
123132 , foldManyPost
124133 , groupsOf
125134 , refoldMany
135+ , refoldIterateM
126136
127137 -- * Fold Iterate
128138 , reduceIterateBfs
129139 , foldIterateBfs
130140
141+ -- * Splitting
142+ , indexOnSuffix
143+
131144 -- * Multi-stream folds
145+ -- | These should probably be expressed using zipping operations.
132146 , eqBy
133147 , cmpBy
148+
149+ -- * Deprecated
150+ , sliceOnSuffix
134151 )
135152where
136153
@@ -162,6 +179,7 @@ import Streamly.Internal.Data.Maybe.Strict (Maybe'(..), toMaybe)
162179import Streamly.Internal.Data.Refold.Type (Refold(..))
163180import Streamly.Internal.Data.Stream.Step (Step (..))
164181import Streamly.Internal.Data.SVar.Type (State, adaptState, defState)
182+import Streamly.Internal.Data.Tuple.Strict (Tuple'(..))
165183import Streamly.Internal.Data.Unfold.Type (Unfold(..))
166184
167185import qualified Streamly.Internal.Data.Fold.Type as FL hiding (foldr)
@@ -212,6 +230,9 @@ pattern Stream step state <- (unShare -> UnStream step state)
212230nilM :: Applicative m => m b -> Stream m a
213231nilM m = Stream (\_ _ -> m $> Stop) ()
214232
233+
234+infixr 5 `consM`
235+
215236-- | Like 'cons' but fuses an effect instead of a pure value.
216237{-# INLINE_NORMAL consM #-}
217238consM :: Applicative m => m a -> Stream m a -> Stream m a
@@ -551,8 +572,12 @@ foldAdd f =
551572-- >>> Stream.foldrM step (return False) s
552573-- True
553574--
575+-- >>> import Control.Monad (join)
576+-- >>> foldrM f z = join $ Stream.foldr f z
577+--
554578{-# INLINE_NORMAL foldrM #-}
555579foldrM :: 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
556581foldrM f z (Stream step state) = go SPEC state
557582 where
558583 {-# INLINE_LATE go #-}
@@ -1971,6 +1996,110 @@ refoldMany (Refold fstep inject extract) action (Stream step state) =
19711996 step' _ (FoldManyYield b next) = return $ Yield b next
19721997 step' _ FoldManyDone = return Stop
19731998
1999+{-# ANN type CIterState Fuse #-}
2000+data CIterState s f fs b
2001+ = CIterInit s f
2002+ | CIterConsume s fs
2003+ | CIterYield b (CIterState s f fs b)
2004+ | CIterStop
2005+
2006+-- | Like 'foldIterateM' but using the 'Refold' type instead. This could be
2007+-- much more efficient due to stream fusion.
2008+--
2009+-- /Internal/
2010+{-# INLINE_NORMAL refoldIterateM #-}
2011+refoldIterateM ::
2012+ Monad m => Refold m b a b -> m b -> Stream m a -> Stream m b
2013+refoldIterateM (Refold fstep finject fextract) initial (Stream step state) =
2014+ Stream stepOuter (CIterInit state initial)
2015+
2016+ where
2017+
2018+ {-# INLINE iterStep #-}
2019+ iterStep st action = do
2020+ res <- action
2021+ return
2022+ $ Skip
2023+ $ case res of
2024+ FL.Partial fs -> CIterConsume st fs
2025+ FL.Done fb -> CIterYield fb $ CIterInit st (return fb)
2026+
2027+ {-# INLINE_LATE stepOuter #-}
2028+ stepOuter _ (CIterInit st action) = do
2029+ iterStep st (action >>= finject)
2030+ stepOuter gst (CIterConsume st fs) = do
2031+ r <- step (adaptState gst) st
2032+ case r of
2033+ Yield x s -> iterStep s (fstep fs x)
2034+ Skip s -> return $ Skip $ CIterConsume s fs
2035+ Stop -> do
2036+ b <- fextract fs
2037+ return $ Skip $ CIterYield b CIterStop
2038+ stepOuter _ (CIterYield a next) = return $ Yield a next
2039+ stepOuter _ CIterStop = return Stop
2040+
2041+-- | The refold @indexerBy f n@ takes an (index, len) tuple as initial input,
2042+-- and returns @(index + len + n, b)@ as output where @b@ is the output of the
2043+-- fold.
2044+{-# INLINE indexerBy #-}
2045+indexerBy :: Monad m =>
2046+ Fold m a Int -> Int -> Refold m (Int, Int) a (Int, Int)
2047+indexerBy (Fold step1 initial1 extract1 _final) n =
2048+ Refold step inject extract
2049+
2050+ where
2051+
2052+ inject (i, len) = do
2053+ r <- initial1
2054+ return $ case r of
2055+ FL.Partial s -> FL.Partial $ Tuple' (i + len + n) s
2056+ FL.Done l -> FL.Done (i, l)
2057+
2058+ step (Tuple' i s) x = do
2059+ r <- step1 s x
2060+ return $ case r of
2061+ FL.Partial s1 -> FL.Partial $ Tuple' i s1
2062+ FL.Done len -> FL.Done (i, len)
2063+
2064+ extract (Tuple' i s) = (i,) <$> extract1 s
2065+
2066+-- | Like 'splitOnSuffix' but generates a stream of (index, len) tuples marking
2067+-- the places where the predicate matches in the stream.
2068+--
2069+-- /Pre-release/
2070+{-# INLINE indexOnSuffix #-}
2071+indexOnSuffix :: Monad m =>
2072+ (a -> Bool) -> Stream m a -> Stream m (Int, Int)
2073+indexOnSuffix predicate =
2074+ -- Scan the stream with the given refold
2075+ refoldIterateM
2076+ (indexerBy (FL.takeEndBy_ predicate FL.length) 1)
2077+ (return (-1, 0))
2078+
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+
2099+{-# DEPRECATED sliceOnSuffix "Please use indexOnSuffix instead." #-}
2100+sliceOnSuffix :: Monad m => (a -> Bool) -> Stream m a -> Stream m (Int, Int)
2101+sliceOnSuffix = indexOnSuffix
2102+
19742103------------------------------------------------------------------------------
19752104-- Stream with a cross product style monad instance
19762105------------------------------------------------------------------------------
core/src/Streamly/Internal/Data/StreamK.hs+19−28
@@ -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
@@ -37,8 +36,6 @@ module Streamly.Internal.Data.StreamK
3736 -- * Elimination
3837 -- ** General Folds
3938 , foldr1
40− , foldlM'
41− , foldlMx'
4239 , fold
4340 , foldBreak
4441 , foldEither
@@ -69,13 +66,13 @@ module Streamly.Internal.Data.StreamK
6966 , find
7067 , (!!)
7168
69+ -- ** To Containers
70+ , toList
71+ , toStream
72+
7273 -- ** Map and Fold
7374 , mapM_
7475
75− -- ** Conversions
76− , toList
77− , hoist
78−
7976 -- * Transformation
8077 -- ** By folding (scans)
8178 , scanl'
@@ -102,6 +99,7 @@ module Streamly.Internal.Data.StreamK
10299
103100 -- ** Reordering
104101 , sortBy
102+ , sortOn
105103
106104 -- ** Map and Filter
107105 , mapMaybe
@@ -117,6 +115,9 @@ module Streamly.Internal.Data.StreamK
117115 -- ** Transformation comprehensions
118116 , the
119117
118+ -- ** Transforming Inner Monad
119+ , hoist
120+
120121 -- * Exceptions
121122 , handle
122123
@@ -133,6 +134,7 @@ import Control.Exception (mask_, Exception)
133134import Control.Monad (void, join)
134135import Control.Monad.Catch (MonadCatch)
135136import Control.Monad.IO.Class (MonadIO(..))
137+import Data.Ord (comparing)
136138import Data.Proxy (Proxy(..))
137139import GHC.Types (SPEC(..))
138140import Streamly.Internal.Data.Array.Type (Array(..))
@@ -306,19 +308,6 @@ foldr1 step m = do
306308 yieldk a r = fmap (step p) (go a r)
307309 in foldStream defState yieldk single stp m1
308310
309−-- XXX replace the recursive "go" with explicit continuations.
310−-- | Like 'foldx', but with a monadic step function.
311−{-# INLINABLE foldlMx' #-}
312−foldlMx' :: Monad m
313− => (x -> a -> m x) -> m x -> (x -> m b) -> StreamK m a -> m b
314−foldlMx' step begin done = go begin
315− where
316− go !acc m1 =
317− let stop = acc >>= done
318− single a = acc >>= \b -> step b a >>= done
319− yieldk a r = acc >>= \b -> step b a >>= \x -> go (return x) r
320− in foldStream defState yieldk single stop m1
321−
322311-- | Fold a stream using the supplied left 'Fold' and reducing the resulting
323312-- expression strictly at each step. The behavior is similar to 'foldl''. A
324313-- 'Fold' can terminate early without consuming the full stream. See the
@@ -470,11 +459,6 @@ foldConcat
470459 Stream.Skip s -> go1 SPEC fs s
471460 Stream.Stop -> return $ Left fs
472461
473−-- | Like 'foldl'' but with a monadic step function.
474−{-# INLINE foldlM' #-}
475−foldlM' :: Monad m => (b -> a -> m b) -> m b -> StreamK m a -> m b
476−foldlM' step begin = foldlMx' step begin return
477−
478462------------------------------------------------------------------------------
479463-- Specialized folds
480464------------------------------------------------------------------------------
@@ -1737,3 +1721,10 @@ sortBy cmp =
17371721 . Stream.catRights -- its a non-failing backtracking parser
17381722 . Stream.parseMany (fmap (either id id) p)
17391723 . 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+80−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
@@ -84,6 +88,8 @@ module Streamly.Internal.Data.StreamK.Type
8488 -- ** Strict Left Folds
8589 , Streamly.Internal.Data.StreamK.Type.foldl'
8690 , foldlx'
91+ , foldlMx'
92+ , foldlM'
8793
8894 -- ** Lazy Right Folds
8995 , Streamly.Internal.Data.StreamK.Type.foldr
@@ -91,8 +97,11 @@ module Streamly.Internal.Data.StreamK.Type
9197 -- ** Specific Folds
9298 , drain
9399 , null
100+ , headNonEmpty
94101 , tail
102+ , tailNonEmpty
95103 , init
104+ , initNonEmpty
96105
97106 -- * Mapping
98107 , map
@@ -817,6 +826,24 @@ foldlx' step begin done m = get $ go m begin
817826foldl' :: Monad m => (b -> a -> b) -> b -> StreamK m a -> m b
818827foldl' step begin = foldlx' step begin id
819828
829+-- XXX replace the recursive "go" with explicit continuations.
830+-- | Like 'foldx', but with a monadic step function.
831+{-# INLINABLE foldlMx' #-}
832+foldlMx' :: Monad m
833+ => (x -> a -> m x) -> m x -> (x -> m b) -> StreamK m a -> m b
834+foldlMx' step begin done = go begin
835+ where
836+ go !acc m1 =
837+ let stop = acc >>= done
838+ single a = acc >>= \b -> step b a >>= done
839+ yieldk a r = acc >>= \b -> step b a >>= \x -> go (return x) r
840+ in foldStream defState yieldk single stop m1
841+
842+-- | Like 'foldl'' but with a monadic step function.
843+{-# INLINE foldlM' #-}
844+foldlM' :: Monad m => (b -> a -> m b) -> m b -> StreamK m a -> m b
845+foldlM' step begin = foldlMx' step begin return
846+
820847------------------------------------------------------------------------------
821848-- Specialized folds
822849------------------------------------------------------------------------------
@@ -1787,14 +1814,42 @@ iterateMWith cns step = go
17871814 !next <- s
17881815 foldStreamShared st stp sng yld (return next `cns` go (step next))
17891816
1790−{-# INLINE headPartial #-}
1791−headPartial :: Monad m => StreamK m a -> m a
1792−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
17931843
1794−{-# INLINE tailPartial #-}
1795−tailPartial :: StreamK m a -> StreamK m a
1796−tailPartial m = mkStream $ \st yld sng stp ->
1797− 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"
17981853 single _ = stp
17991854 yieldk _ r = foldStream st yld sng stp r
18001855 in foldStream st yieldk single stop m
@@ -1859,10 +1914,10 @@ mfix f = mkStream $ \st yld sng stp ->
18591914 where
18601915
18611916 -- fix the head element of the stream
1862− xs = fix (f . headPartial)
1917+ xs = fix (f . headNonEmpty)
18631918
18641919 -- now fix the tail recursively
1865− ys = mfix (tailPartial . f)
1920+ ys = mfix (tailNonEmpty . f)
18661921
18671922-------------------------------------------------------------------------------
18681923-- Conversions
@@ -1893,6 +1948,15 @@ uncons m =
18931948 yieldk a r = pure (Just (a, r))
18941949 in foldStream defState yieldk single stop m
18951950
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+--
18961960{-# INLINE tail #-}
18971961tail :: Applicative m => StreamK m a -> m (Maybe (StreamK m a))
18981962tail =
@@ -1901,9 +1965,14 @@ tail =
19011965 yieldk _ r = pure $ Just r
19021966 in foldStream defState yieldk single stop
19031967
1904−-- | 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.
19051970--
1906−-- 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.
19071976--
19081977-- /Pre-release/
19091978{-# INLINE init #-}
core/src/Streamly/Internal/Data/Time/Units.hs−4
@@ -1,7 +1,3 @@
1−{-# LANGUAGE DataKinds #-}
2−{-# LANGUAGE PolyKinds #-}
3−{-# LANGUAGE UnboxedTuples #-}
4−
51-- |
62-- Module : Streamly.Internal.Data.Time.Units
73-- Copyright : (c) 2019 Composewell Technologies
core/src/Streamly/Internal/Data/Unbox.hs+4−1
@@ -1,5 +1,8 @@
11{-# LANGUAGE DataKinds #-}
2−{-# LANGUAGE DefaultSignatures #-}
2+{-# LANGUAGE TypeFamilies #-}
3+-- Must come after TypeFamilies, otherwise it is re-enabled.
4+-- MonoLocalBinds enabled by TypeFamilies causes perf regressions in general.
5+{-# LANGUAGE NoMonoLocalBinds #-}
36{-# LANGUAGE UnboxedTuples #-}
47{-# LANGUAGE UndecidableInstances #-}
58
core/src/Streamly/Internal/Data/Unfold.hs+6−4
@@ -82,7 +82,7 @@ module Streamly.Internal.Data.Unfold
8282 , dropWhileM
8383
8484 -- ** Cross product
85− , joinInnerGeneric
85+ , innerJoin
8686
8787 -- ** Resource Management
8888 -- | 'bracket' is the most general resource management operation, all other
@@ -677,10 +677,12 @@ dropWhileM f (Unfold step inject) = Unfold step' inject'
677677dropWhile :: Monad m => (b -> Bool) -> Unfold m a b -> Unfold m a b
678678dropWhile f = dropWhileM (return . f)
679679
680−{-# INLINE_NORMAL joinInnerGeneric #-}
681−joinInnerGeneric :: Monad m =>
680+-- | Cross intersection of two unfolds. See
681+-- 'Streamly.Internal.Data.Stream.innerJoin' for more details.
682+{-# INLINE_NORMAL innerJoin #-}
683+innerJoin :: Monad m =>
682684 (b -> c -> Bool) -> Unfold m a b -> Unfold m a c -> Unfold m a (b, c)
683−joinInnerGeneric eq s1 s2 = filter (\(a, b) -> a `eq` b) $ cross s1 s2
685+innerJoin eq s1 s2 = filter (\(a, b) -> a `eq` b) $ cross s1 s2
684686
685687------------------------------------------------------------------------------
686688-- Exceptions
core/src/Streamly/Internal/FileSystem/File.hs+4−9
@@ -103,15 +103,14 @@ import qualified Control.Monad.Catch as MC
103103import qualified System.IO as SIO
104104
105105import Streamly.Data.Fold (groupsOf, drain)
106−import Streamly.Internal.Data.Array.Type (Array(..), pinnedWriteNUnsafe)
106+import Streamly.Internal.Data.Array.Type (Array(..))
107107import Streamly.Internal.Data.Fold.Type (Fold(..))
108108import Streamly.Data.Stream (Stream)
109−import Streamly.Internal.Data.Unbox (Unbox)
110109import Streamly.Internal.Data.Unfold.Type (Unfold(..))
111110-- import Streamly.String (encodeUtf8, decodeUtf8, foldLines)
112111import Streamly.Internal.System.IO (defaultChunkSize)
113112
114−import qualified Streamly.Data.Array as A
113+import qualified Streamly.Internal.Data.Array as A
115114import qualified Streamly.Data.Stream as S
116115import qualified Streamly.Data.Unfold as UF
117116import qualified Streamly.Internal.Data.Array.Type as IA (pinnedChunksOf)
@@ -339,10 +338,6 @@ readWithBufferOf = readerWith
339338reader :: (MonadIO m, MonadCatch m) => Unfold m FilePath Word8
340339reader = UF.many A.reader (usingFile FH.chunkReader)
341340
342−{-# INLINE concatChunks #-}
343−concatChunks :: (Monad m, Unbox a) => Stream m (Array a) -> Stream m a
344−concatChunks = S.unfoldMany A.reader
345−
346341-- | Generate a stream of bytes from a file specified by path. The stream ends
347342-- when EOF is encountered. File is locked using multiple reader and single
348343-- writer locking mode.
@@ -351,7 +346,7 @@ concatChunks = S.unfoldMany A.reader
351346--
352347{-# INLINE read #-}
353348read :: (MonadIO m, MonadCatch m) => FilePath -> Stream m Word8
354−read file = concatChunks $ withFile file ReadMode FH.readChunks
349+read file = A.concat $ withFile file ReadMode FH.readChunks
355350
356351{-# DEPRECATED toBytes "Please use 'read' instead" #-}
357352{-# INLINE toBytes #-}
@@ -467,7 +462,7 @@ writeChunks path = Fold step initial extract final
467462writeWith :: (MonadIO m, MonadCatch m)
468463 => Int -> FilePath -> Fold m Word8 ()
469464writeWith n path =
470− groupsOf n (pinnedWriteNUnsafe n) (writeChunks path)
465+ groupsOf n (A.unsafePinnedCreateOf n) (writeChunks path)
471466
472467{-# DEPRECATED writeWithBufferOf "Please use 'writeWith' instead" #-}
473468{-# INLINE writeWithBufferOf #-}
core/src/Streamly/Internal/FileSystem/Handle.hs+21−28
@@ -40,8 +40,8 @@ module Streamly.Internal.FileSystem.Handle
4040
4141 -- * Streams
4242 , read
43− , readWith
44− , readChunksWith
43+ , readWith -- readConcatChunksOf
44+ , readChunksWith -- readChunksOf
4545 , readChunks
4646
4747 -- * Unfolds
@@ -59,10 +59,10 @@ module Streamly.Internal.FileSystem.Handle
5959 -- , writeUtf8ByLines
6060 -- , writeByFrames
6161 -- , writeLines
62− , writeWith
62+ , writeWith -- writeChunksOf
6363 , writeChunks
64− , writeChunksWith
65− , writeMaybesWith
64+ , writeChunksWith -- writeCompactChunksOf
65+ , writeMaybesWith -- writeCompactJustsOf
6666
6767 -- * Refolds
6868 , writer
@@ -131,16 +131,13 @@ import Streamly.Internal.Data.Fold (Fold)
131131import Streamly.Internal.Data.Refold.Type (Refold(..))
132132import Streamly.Internal.Data.Unfold.Type (Unfold(..))
133133import Streamly.Internal.Data.Array.Type
134− (Array(..), pinnedWriteNUnsafe, unsafeFreezeWithShrink, byteLength)
134+ (Array(..), unsafeFreezeWithShrink, byteLength)
135135import Streamly.Internal.Data.Stream.Type (Stream)
136−import Streamly.Internal.Data.Array.Stream (lpackArraysChunksOf)
137136-- import Streamly.String (encodeUtf8, decodeUtf8, foldLines)
138137import Streamly.Internal.System.IO (defaultChunkSize)
139138
140139import qualified Streamly.Data.Fold as FL
141−import qualified Streamly.Data.Array as A
142−import qualified Streamly.Internal.Data.Array.Type as A
143−import qualified Streamly.Internal.Data.Array.Stream as AS
140+import qualified Streamly.Internal.Data.Array as A
144141import qualified Streamly.Internal.Data.MutArray.Type as MArray
145142import qualified Streamly.Internal.Data.Refold.Type as Refold
146143import qualified Streamly.Internal.Data.Fold.Type as FL(refoldMany)
@@ -169,6 +166,8 @@ import qualified Streamly.Internal.Data.StreamK.Type as K (mkStream)
169166-- Array IO (Input)
170167-------------------------------------------------------------------------------
171168
169+-- XXX add an API that compacts the arrays to an exact size.
170+
172171-- | Read a 'ByteArray' consisting of one or more bytes from a file handle. If
173172-- no data is available on the handle it blocks until at least one byte becomes
174173-- available. If any data is available then it immediately returns that data
@@ -178,9 +177,9 @@ import qualified Streamly.Internal.Data.StreamK.Type as K (mkStream)
178177{-# INLINABLE getChunk #-}
179178getChunk :: MonadIO m => Int -> Handle -> m (Array Word8)
180179getChunk size h = liftIO $ do
181− arr <- MArray.pinnedNewBytes size
180+ arr :: MArray.MutArray Word8 <- MArray.pinnedEmptyOf size
182181 -- ptr <- mallocPlainForeignPtrAlignedBytes size (alignment (undefined :: Word8))
183− MArray.asPtrUnsafe arr $ \p -> do
182+ MArray.unsafePinnedAsPtr arr $ \p -> do
184183 n <- hGetBufSome h p size
185184 -- XXX shrink only if the diff is significant
186185 return $
@@ -338,10 +337,6 @@ readerWith = UF.many A.reader chunkReaderWith
338337readWithBufferOf :: MonadIO m => Unfold m (Int, Handle) Word8
339338readWithBufferOf = readerWith
340339
341−{-# INLINE concatChunks #-}
342−concatChunks :: (Monad m, Unbox a) => Stream m (Array a) -> Stream m a
343−concatChunks = S.unfoldMany A.reader
344−
345340-- | @readWith bufsize handle@ reads a byte stream from a file
346341-- handle, reads are performed in chunks of up to @bufsize@.
347342--
@@ -350,7 +345,7 @@ concatChunks = S.unfoldMany A.reader
350345-- /Pre-release/
351346{-# INLINE readWith #-}
352347readWith :: MonadIO m => Int -> Handle -> Stream m Word8
353−readWith size h = concatChunks $ readChunksWith size h
348+readWith size h = A.concat $ readChunksWith size h
354349
355350-- | Unfolds a file handle into a byte stream. IO requests to the device are
356351-- performed in sizes of
@@ -369,7 +364,7 @@ reader = UF.many A.reader chunkReader
369364-- /Pre-release/
370365{-# INLINE read #-}
371366read :: MonadIO m => Handle -> Stream m Word8
372−read = concatChunks . readChunks
367+read = A.concat . readChunks
373368
374369-------------------------------------------------------------------------------
375370-- Writing
@@ -384,12 +379,12 @@ read = concatChunks . readChunks
384379{-# INLINABLE putChunk #-}
385380putChunk :: MonadIO m => Handle -> Array a -> m ()
386381putChunk _ arr | byteLength arr == 0 = return ()
387−putChunk h arr = A.asPtrUnsafe arr $ \ptr ->
382+putChunk h arr = A.unsafePinnedAsPtr arr $ \ptr ->
388383 liftIO $ hPutBuf h ptr aLen
389384
390385 where
391386
392− -- XXX We should have the length passed by asPtrUnsafe itself.
387+ -- XXX We should have the length passed by unsafePinnedAsPtr itself.
393388 aLen = A.byteLength arr
394389
395390-------------------------------------------------------------------------------
@@ -421,7 +416,7 @@ putChunks h = S.fold (FL.drainMapM (putChunk h))
421416{-# INLINE putChunksWith #-}
422417putChunksWith :: (MonadIO m, Unbox a)
423418 => Int -> Handle -> Stream m (Array a) -> m ()
424−putChunksWith n h xs = putChunks h $ AS.compact n xs
419+putChunksWith n h xs = putChunks h $ A.compactLE n xs
425420
426421-- > putBytesWith n h m = Handle.putChunks h $ A.pinnedChunksOf n m
427422
@@ -461,8 +456,6 @@ writeChunks h = FL.drainMapM (putChunk h)
461456chunkWriter :: MonadIO m => Refold m Handle (Array a) ()
462457chunkWriter = Refold.drainBy putChunk
463458
464−-- XXX lpackArraysChunksOf should be written idiomatically
465−
466459-- | @writeChunksWith bufsize handle@ writes a stream of arrays
467460-- to @handle@ after coalescing the adjacent arrays in chunks of @bufsize@.
468461-- We never split an array, if a single array is bigger than the specified size
@@ -472,7 +465,7 @@ chunkWriter = Refold.drainBy putChunk
472465{-# INLINE writeChunksWith #-}
473466writeChunksWith :: (MonadIO m, Unbox a)
474467 => Int -> Handle -> Fold m (Array a) ()
475−writeChunksWith n h = lpackArraysChunksOf n (writeChunks h)
468+writeChunksWith n h = A.lCompactGE n (writeChunks h)
476469
477470-- | Same as 'writeChunksWith'
478471--
@@ -496,11 +489,11 @@ writeChunksWithBufferOf = writeChunksWith
496489-- Bytes in the input stream are collected into a buffer until we have a chunk
497490-- of @reqSize@ and then written to the IO device.
498491--
499−-- >>> writeWith n h = Fold.groupsOf n (Array.writeNUnsafe n) (Handle.writeChunks h)
492+-- >>> writeWith n h = Fold.groupsOf n (Array.unsafeCreateOf n) (Handle.writeChunks h)
500493--
501494{-# INLINE writeWith #-}
502495writeWith :: MonadIO m => Int -> Handle -> Fold m Word8 ()
503−writeWith n h = FL.groupsOf n (pinnedWriteNUnsafe n) (writeChunks h)
496+writeWith n h = FL.groupsOf n (A.unsafePinnedCreateOf n) (writeChunks h)
504497
505498-- | Same as 'writeWith'
506499--
@@ -518,7 +511,7 @@ writeWithBufferOf = writeWith
518511writeMaybesWith :: (MonadIO m )
519512 => Int -> Handle -> Fold m (Maybe Word8) ()
520513writeMaybesWith n h =
521− let writeNJusts = FL.lmap fromJust $ A.pinnedWriteN n
514+ let writeNJusts = FL.lmap fromJust $ A.pinnedCreateOf n
522515 writeOnNothing = FL.takeEndBy_ isNothing writeNJusts
523516 in FL.many writeOnNothing (writeChunks h)
524517
@@ -528,7 +521,7 @@ writeMaybesWith n h =
528521{-# INLINE writerWith #-}
529522writerWith :: MonadIO m => Int -> Refold m Handle Word8 ()
530523writerWith n =
531− FL.refoldMany (FL.take n $ pinnedWriteNUnsafe n) chunkWriter
524+ FL.refoldMany (FL.take n $ A.unsafePinnedCreateOf n) chunkWriter
532525
533526-- | Write a byte stream to a file handle. Accumulates the input in chunks of
534527-- up to 'Streamly.Internal.Data.Array.Type.defaultChunkSize' before writing
core/src/Streamly/Internal/FileSystem/Path.hsnew file+515
@@ -0,0 +1,515 @@
1+{-# LANGUAGE TemplateHaskell #-}
2+
3+-- |
4+-- Module : Streamly.Internal.FileSystem.Path
5+-- Copyright : (c) 2023 Composewell Technologies
6+-- License : BSD3
7+-- Maintainer : streamly@composewell.com
8+-- Portability : GHC
9+--
10+-- Well typed and flexible file systems paths, preserving the OS and filesystem
11+-- encoding.
12+--
13+-- You can choose the level of type safety you want. 'Path' is the basic path
14+-- type which can represent a file, directory, absolute or relative path with
15+-- no restrictions. Depending on how much type safety you want you can choose
16+-- appropriate type wrappers to wrap 'Path'. @File Path@ mandates the path to
17+-- be a file whereas @Abs (File Path)@ mandates it to be an absolute path
18+-- representing a file.
19+--
20+-- You can upgrade or downgrade the safety. Whenever a less restrictive path
21+-- type is converted to a more restrctive path type the conversion involves
22+-- checks and it may fail. However, a more restrictive path type can be freely
23+-- converted to a less restrictive one.
24+--
25+-- See the @streamly-filepath@ package for interworking with the 'OsPath' type.
26+-- The 'Path' type can be converted to and from 'OsPath' type at zero cost
27+-- since the underlying representation of both is the same.
28+
29+-- Conventions: A trailing separator on a path indicates that it is a directory.
30+-- However, the absence of a trailing separator does not convey any
31+-- information, it could either be a directory or a file.
32+
33+-- You may also find the 'str' quasiquoter from "Streamly.Unicode.String" to be
34+-- useful in creating paths.
35+--
36+module Streamly.Internal.FileSystem.Path
37+ (
38+ -- * Path Types
39+ Path (..)
40+ , File
41+ , Dir
42+ , Abs
43+ , Rel
44+
45+ -- * Conversions
46+ , IsPath (..)
47+ , adaptPath
48+
49+ -- * Construction
50+ , fromChunk
51+ , fromChunkUnsafe
52+ , fromString
53+ , fromChars
54+
55+ -- * Statically Verified Literals
56+ -- quasiquoters
57+ , path
58+ , abs
59+ , rel
60+ , dir
61+ , file
62+ , absdir
63+ , reldir
64+ , absfile
65+ , relfile
66+
67+ -- * Statically Verified Strings
68+ -- TH macros
69+ , mkPath
70+ , mkAbs
71+ , mkRel
72+ , mkDir
73+ , mkFile
74+ , mkAbsDir
75+ , mkRelDir
76+ , mkAbsFile
77+ , mkRelFile
78+
79+ -- * Elimination
80+ , toChunk
81+ , toString
82+ , toChars
83+
84+ -- * 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
90+ , extendPath
91+ , extendDir
92+ )
93+where
94+
95+import Control.Exception (Exception)
96+import Control.Monad.Catch (MonadThrow(..))
97+import Data.Char (chr)
98+import Data.Functor.Identity (Identity(..))
99+import Data.Word (Word8)
100+#if defined(mingw32_HOST_OS) || defined(__MINGW32__)
101+import Data.Word (Word16)
102+#endif
103+import Language.Haskell.TH (Q, Exp)
104+import Language.Haskell.TH.Quote (QuasiQuoter)
105+import Streamly.Internal.Data.Array (Array)
106+import Streamly.Internal.Data.Stream (Stream)
107+import System.IO.Unsafe (unsafePerformIO)
108+
109+import qualified Streamly.Internal.Data.Array as Array
110+import qualified Streamly.Internal.Data.Fold as Fold
111+import qualified Streamly.Internal.Data.MutArray as MutArray
112+import qualified Streamly.Internal.Data.Stream as Stream
113+import qualified Streamly.Internal.Unicode.Stream as Unicode
114+
115+import Prelude hiding (abs)
116+
117+#if defined(mingw32_HOST_OS) || defined(__MINGW32__)
118+#define WORD_TYPE Word16
119+#define SEPARATOR 92
120+#else
121+#define WORD_TYPE Word8
122+#define SEPARATOR 47
123+#endif
124+
125+------------------------------------------------------------------------------
126+-- Exceptions
127+------------------------------------------------------------------------------
128+
129+-- | Exceptions thrown by path operations.
130+data PathException =
131+ InvalidPath String
132+ | InvalidAbsPath String
133+ | InvalidRelPath String
134+ | InvalidFilePath String
135+ | InvalidDirPath String
136+ deriving (Show,Eq)
137+
138+instance Exception PathException
139+
140+-- XXX Path must not contain null char on Posix. System calls consider the path
141+-- as null terminated.
142+-- XXX Maintain the Array with null termination because Unix system calls
143+-- require a null terminated string, also they return null terminated strings
144+-- as paths. Implementation of path append will have to handle the null
145+-- termination. Or we can choose to always copy the array when using it in
146+-- system calls.
147+
148+-- XXX The eq instance needs to make sure that the paths are equivalent. If we
149+-- normalize the paths we can do a byte comparison. However, on windows paths
150+-- are case insensitive but the case is preserved, therefore, we cannot
151+-- normalize and need to do case insensitive comparison.
152+
153+------------------------------------------------------------------------------
154+-- Types
155+------------------------------------------------------------------------------
156+
157+-- | A type representing file system paths for directories or files.
158+newtype Path = Path (Array WORD_TYPE) -- deriving Eq
159+
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+
169+-- XXX Do we need a type for file or dir Name as names cannot have the
170+-- separator char and there may be other restrictions on names? For example,
171+-- length restriction. A file name cannot be "." or "..". We can use the types
172+-- "File Name" and "Dir Name" to represent names. Also, file systems may put
173+-- limits on names. Can have an IsName type class with members Name, (File
174+-- Name), (Dir Name).
175+
176+-- | A type representing a file path.
177+newtype File a = File a
178+
179+-- | A type representing a directory path.
180+newtype Dir a = Dir a
181+
182+-- | A type representing absolute paths.
183+newtype Abs a = Abs a
184+
185+-- | A type representing relative paths.
186+newtype Rel a = Rel a
187+
188+------------------------------------------------------------------------------
189+-- Conversions
190+------------------------------------------------------------------------------
191+
192+-- | A member of 'IsPath' knows how to convert to and from the 'Path' type.
193+class IsPath a where
194+ -- | Like 'fromPath' but does not check the properties of 'Path'. Provides
195+ -- performance and simplicity when we know that the properties of the path
196+ -- are already verified, for example, when we get the path from the file
197+ -- system or the OS APIs.
198+ fromPathUnsafe :: Path -> a
199+
200+ -- | Convert a raw 'Path' to other forms of well-typed paths. It may fail
201+ -- if the path does not satisfy the properties of the target type.
202+ --
203+ -- Path components may have limits.
204+ -- Total path length may have a limit.
205+ fromPath :: MonadThrow m => Path -> m a
206+
207+ -- | Convert a well-typed path to a raw 'Path'. Never fails.
208+ toPath :: a -> Path
209+
210+instance IsPath Path where
211+ fromPathUnsafe = id
212+ fromPath = pure
213+ toPath = id
214+
215+instance IsPath (File Path) where
216+ fromPathUnsafe p = File p
217+ fromPath p = pure (File p)
218+ toPath (File p) = p
219+
220+instance IsPath (Dir Path) where
221+ fromPathUnsafe p = Dir p
222+ fromPath p = pure (Dir p)
223+ toPath (Dir p) = p
224+
225+instance IsPath (Abs Path) where
226+ fromPathUnsafe p = Abs p
227+ fromPath p = pure (Abs p)
228+ toPath (Abs p) = p
229+
230+instance IsPath (Rel Path) where
231+ fromPathUnsafe p = Rel p
232+ fromPath p = pure (Rel p)
233+ toPath (Rel p) = p
234+
235+instance IsPath (Abs (File Path)) where
236+ fromPathUnsafe p = Abs (File p)
237+ fromPath p = pure (Abs (File p))
238+ toPath (Abs (File p)) = p
239+
240+instance IsPath (Abs (Dir Path)) where
241+ fromPathUnsafe p = Abs (Dir p)
242+ fromPath p = pure (Abs (Dir p))
243+ toPath (Abs (Dir p)) = p
244+
245+instance IsPath (Rel (File Path)) where
246+ fromPathUnsafe p = Rel (File p)
247+ fromPath p = pure (Rel (File p))
248+ toPath (Rel (File p)) = p
249+
250+instance IsPath (Rel (Dir Path)) where
251+ fromPathUnsafe p = Rel (Dir p)
252+ fromPath p = pure (Rel (Dir p))
253+ toPath (Rel (Dir p)) = p
254+
255+-- XXX Use rewrite rules to eliminate intermediate conversions for better
256+-- efficiency.
257+
258+-- | Convert a path type to another path type. This operation may fail with a
259+-- 'PathException' when converting a less restrictive path type to a more
260+-- restrictive one.
261+adaptPath :: (MonadThrow m, IsPath a, IsPath b) => a -> m b
262+adaptPath p = fromPath $ toPath p
263+
264+------------------------------------------------------------------------------
265+-- Construction
266+------------------------------------------------------------------------------
267+
268+-- A chunk is essentially an untyped Array i.e. Array Word8. We can either use
269+-- the term ByteArray for that or just Chunk. The latter is shorter and we have
270+-- been using it consistently in streamly.
271+
272+-- | /Unsafe/: On Posix, a path cannot contain null characters. On Windows, the
273+-- array passed must be a multiple of 2 bytes as the underlying representation
274+-- uses 'Word16'.
275+{-# INLINE fromChunkUnsafe #-}
276+fromChunkUnsafe :: Array Word8 -> Path
277+fromChunkUnsafe arr = Path (Array.castUnsafe arr)
278+
279+-- | On Posix it may fail if the byte array contains null characters. On
280+-- Windows the array passed must be a multiple of 2 bytes as the underlying
281+-- representation uses 'Word16'.
282+--
283+-- Throws 'InvalidPath'.
284+fromChunk :: MonadThrow m => Array Word8 -> m Path
285+fromChunk arr =
286+ case Array.cast arr of
287+ Nothing ->
288+ -- XXX Windows only message.
289+ throwM
290+ $ InvalidPath
291+ $ "Encoded path length " ++ show (Array.byteLength arr)
292+ ++ " is not a multiple of 16-bit."
293+ Just x -> pure (Path x)
294+
295+-- | Convert 'Path' to an array of bytes.
296+toChunk :: Path -> Array Word8
297+toChunk (Path arr) = Array.asBytes arr
298+
299+-- | Encode a Unicode char stream to 'Path' using strict UTF-8 encoding on
300+-- Posix. On Posix it may fail if the stream contains null characters.
301+-- TBD: Use UTF16LE on Windows.
302+fromChars :: MonadThrow m => Stream Identity Char -> m Path
303+fromChars s =
304+ let n = runIdentity $ Stream.fold Fold.length s
305+#if defined(mingw32_HOST_OS) || defined(__MINGW32__)
306+ in pure $ Path (Array.fromPureStreamN n (Unicode.encodeUtf16le' s))
307+#else
308+ in pure $ Path (Array.fromPureStreamN n (Unicode.encodeUtf8' s))
309+#endif
310+
311+-- | Decode the path to a stream of Unicode chars using strict UTF-8 decoding
312+-- on Posix.
313+-- TBD: Use UTF16LE on Windows.
314+toChars :: Monad m => Path -> Stream m Char
315+toChars (Path arr) =
316+#if defined(mingw32_HOST_OS) || defined(__MINGW32__)
317+ Unicode.decodeUtf16le' $ Array.read arr
318+#else
319+ Unicode.decodeUtf8' $ Array.read arr
320+#endif
321+
322+-- | Encode a Unicode string to 'Path' using strict UTF-8 encoding on Posix.
323+-- On Posix it may fail if the stream contains null characters.
324+-- TBD: Use UTF16LE on Windows.
325+fromString :: MonadThrow m => [Char] -> m Path
326+fromString = fromChars . Stream.fromList
327+
328+-- | Decode the path to a Unicode string using strict UTF-8 decoding on Posix.
329+-- TBD: Use UTF16LE on Windows.
330+toString :: Path -> [Char]
331+toString = runIdentity . Stream.toList . toChars
332+
333+------------------------------------------------------------------------------
334+-- Statically Verified Literals
335+------------------------------------------------------------------------------
336+
337+-- XXX Build these on top of the str quasiquoter so that we get the
338+-- interpolation for free.
339+
340+-- | Generates a 'Path' type from an interpolated string literal.
341+--
342+-- /Unimplemented/
343+path :: QuasiQuoter
344+path = undefined
345+
346+-- | Generates an @Abs Path@ type from an interpolated string literal.
347+--
348+-- /Unimplemented/
349+abs :: QuasiQuoter
350+abs = undefined
351+
352+-- | Generates an @Rel Path@ type from an interpolated string literal.
353+--
354+-- /Unimplemented/
355+rel :: QuasiQuoter
356+rel = undefined
357+
358+-- | Generates an @Dir Path@ type from an interpolated string literal.
359+--
360+-- /Unimplemented/
361+dir :: QuasiQuoter
362+dir = undefined
363+
364+-- | Generates an @File Path@ type from an interpolated string literal.
365+--
366+-- /Unimplemented/
367+file :: QuasiQuoter
368+file = undefined
369+
370+-- | Generates an @Abs (Dir Path)@ type from an interpolated string literal.
371+--
372+-- /Unimplemented/
373+absdir :: QuasiQuoter
374+absdir = undefined
375+
376+-- | Generates an @Rel (Dir Path)@ type from an interpolated string literal.
377+--
378+-- /Unimplemented/
379+reldir :: QuasiQuoter
380+reldir = undefined
381+
382+-- | Generates an @Abs (File Path)@ type from an interpolated string literal.
383+--
384+-- /Unimplemented/
385+absfile :: QuasiQuoter
386+absfile = undefined
387+
388+-- | Generates an @Rel (File Path)@ type from an interpolated string literal.
389+--
390+-- /Unimplemented/
391+relfile :: QuasiQuoter
392+relfile = undefined
393+
394+------------------------------------------------------------------------------
395+-- Statically Verified Strings
396+------------------------------------------------------------------------------
397+
398+-- | Generates a 'Path' type.
399+--
400+-- /Unimplemented/
401+mkPath :: String -> Q Exp
402+mkPath = undefined
403+
404+-- | Generates an @Abs Path@ type.
405+--
406+-- /Unimplemented/
407+mkAbs :: String -> Q Exp
408+mkAbs = undefined
409+
410+-- | Generates an @Rel Path@ type.
411+--
412+-- /Unimplemented/
413+mkRel :: String -> Q Exp
414+mkRel = undefined
415+
416+-- | Generates an @Dir Path@ type.
417+--
418+-- /Unimplemented/
419+mkDir :: String -> Q Exp
420+mkDir = undefined
421+
422+-- | Generates an @File Path@ type.
423+--
424+-- /Unimplemented/
425+mkFile :: String -> Q Exp
426+mkFile = undefined
427+
428+-- | Generates an @Abs (Dir Path)@ type.
429+--
430+-- /Unimplemented/
431+mkAbsDir :: String -> Q Exp
432+mkAbsDir = undefined
433+
434+-- | Generates an @Rel (Dir Path)@ type.
435+--
436+-- /Unimplemented/
437+mkRelDir :: String -> Q Exp
438+mkRelDir = undefined
439+
440+-- | Generates an @Abs (File Path)@ type.
441+--
442+-- /Unimplemented/
443+mkAbsFile :: String -> Q Exp
444+mkAbsFile = undefined
445+
446+-- | Generates an @Rel (File Path)@ type.
447+--
448+-- /Unimplemented/
449+mkRelFile :: String -> Q Exp
450+mkRelFile = undefined
451+
452+------------------------------------------------------------------------------
453+-- Operations
454+------------------------------------------------------------------------------
455+
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
475+
476+-- If we append an absolute path it may fail with an error if the 'Path'
477+-- implementation stores absolute path information (a leading separator char).
478+-- However, the implementation may choose to store the path as a list of
479+-- components in which case we cannot distinguish an absolute path from
480+-- relative.
481+
482+-- | Like 'extendDir' but for the less restrictive 'Path' type which will always
483+-- create a syntactically valid 'Path' type but it may not be semantically valid
484+-- because we may append an absolute path or we may append to a file path.
485+-- The onus lies on the user to ensure that the first path is not a file and
486+-- the second path is not absolute.
487+extendPath :: Path -> Path -> Path
488+extendPath (Path a) (Path b) =
489+ let len = Array.byteLength a + 1 + Array.byteLength b
490+ -- XXX Check the leading separator or drive identifier. However,
491+ -- checking the drive letter may add an additional overhead (can it be
492+ -- arbitrarily long?), if it is significant we may want to have a
493+ -- separate combinePathChecked API for that.
494+ --
495+ -- Also, do not add a separator char if the first path has a trailing
496+ -- separator.
497+ newArr = unsafePerformIO $ do
498+ arr <- MutArray.new len
499+ arr1 <- MutArray.spliceUnsafe arr (Array.unsafeThaw a)
500+ arr2 <- MutArray.snocUnsafe arr1 separatorWord
501+ arr3 <- MutArray.spliceUnsafe arr2 (Array.unsafeThaw b)
502+ return (Array.unsafeFreeze arr3)
503+ in Path newArr
504+
505+-- The only safety we need for paths is: (1) The first path can only be a Dir
506+-- type path, and (2) second path can only be a Rel path.
507+-- Can this be coerced to create unsafe versions?
508+
509+-- | Extend a directory path by appending a relative path to it. This is the
510+-- equivalent to the @</>@ operator from the @filepath@ package.
511+{-# INLINE extendDir #-}
512+extendDir :: (IsPath (a (Dir Path)), IsPath b, IsPath (a b)) =>
513+ (a (Dir Path)) -> Rel b -> a b
514+extendDir a (Rel b) =
515+ fromPathUnsafe $ extendPath (toPath a) (toPath b)
core/src/Streamly/Internal/Unicode/Stream.hs+30−3
@@ -28,6 +28,7 @@ module Streamly.Internal.Unicode.Stream
2828 , decodeUtf8
2929 , decodeUtf8'
3030 , decodeUtf8_
31+ , decodeUtf16le'
3132
3233 -- ** Resumable UTF-8 Decoding
3334 , DecodeError(..)
@@ -55,6 +56,7 @@ module Streamly.Internal.Unicode.Stream
5556 , encodeUtf8'
5657 , encodeUtf8_
5758 , encodeStrings
59+ , encodeUtf16le'
5860 {-
5961 -- * Operations on character strings
6062 , strip -- (dropAround isSpace)
@@ -97,7 +99,7 @@ import Data.Char (chr, ord)
9799#if MIN_VERSION_base(4,17,0)
98100import Data.Char (generalCategory, GeneralCategory(Space))
99101#endif
100−import Data.Word (Word8)
102+import Data.Word (Word8, Word16)
101103import Foreign.Marshal.Alloc (mallocBytes)
102104import Foreign.Storable (Storable(..))
103105#ifndef __GHCJS__
@@ -109,7 +111,7 @@ import GHC.IO.Encoding.Failure (isSurrogate)
109111import GHC.Ptr (Ptr (..), plusPtr)
110112import System.IO.Unsafe (unsafePerformIO)
111113import Streamly.Internal.Data.Array.Type (Array(..))
112−import Streamly.Internal.Data.MutArray.Type (MutByteArray)
114+import Streamly.Internal.Data.MutByteArray.Type (MutByteArray)
113115import Streamly.Internal.Data.Fold (Fold)
114116import Streamly.Internal.Data.Parser (Parser)
115117import Streamly.Internal.Data.Stream (Stream)
@@ -676,6 +678,19 @@ decodeUtf8_ = decodeUtf8D_
676678decodeUtf8Lax :: Monad m => Stream m Word8 -> Stream m Char
677679decodeUtf8Lax = decodeUtf8
678680
681+-------------------------------------------------------------------------------
682+-- Decoding Utf16
683+-------------------------------------------------------------------------------
684+
685+-- | Decode a UTF-16 little endian encoded bytestream to a stream of Unicode
686+-- characters. The function throws an error if an invalid codepoint is
687+-- encountered.
688+--
689+-- /Unimplemented/
690+{-# INLINE decodeUtf16le' #-}
691+decodeUtf16le' :: Stream m Word16 -> Stream m Char
692+decodeUtf16le' = undefined
693+
679694-------------------------------------------------------------------------------
680695-- Decoding Array Streams
681696-------------------------------------------------------------------------------
@@ -946,6 +961,18 @@ encodeUtf8_ = encodeUtf8D_
946961encodeUtf8Lax :: Monad m => Stream m Char -> Stream m Word8
947962encodeUtf8Lax = encodeUtf8
948963
964+-------------------------------------------------------------------------------
965+-- Encoding to Utf16
966+-------------------------------------------------------------------------------
967+
968+-- | Encode a stream of Unicode characters to a UTF-16 little endian encoded
969+-- bytestream.
970+--
971+-- /Unimplemented/
972+{-# INLINE encodeUtf16le' #-}
973+encodeUtf16le' :: Stream m Char -> Stream m Word16
974+encodeUtf16le' = undefined
975+
949976-------------------------------------------------------------------------------
950977-- Decoding string literals
951978-------------------------------------------------------------------------------
@@ -1030,7 +1057,7 @@ stripHead = Stream.dropWhile isSpace
10301057-- >>> Stream.fold Fold.toList $ Unicode.lines Fold.toList (Stream.fromList "lines\nthis\nstring\n\n\n")
10311058-- ["lines","this","string","",""]
10321059--
1033−-- > lines = Stream.splitOnSuffix (== '\n')
1060+-- >>> lines f = Stream.foldMany (Fold.takeEndBy_ (== '\n') f)
10341061--
10351062-- /Pre-release/
10361063{-# 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+15−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 (#.)
@@ -372,6 +376,7 @@ library
372376 , Streamly.Internal.Unicode.Array
373377
374378 -- Filesystem/IO
379+ , Streamly.Internal.FileSystem.Path
375380 , Streamly.Internal.FileSystem.Handle
376381 , Streamly.Internal.FileSystem.File
377382 , Streamly.Internal.FileSystem.Dir
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/Developer/paths.rst+25−2
@@ -140,12 +140,35 @@ know exactly how the file system stores the path. Otherwise if we display
140140it differently, the user may use the displayed result to find the file
141141and may not find it.
142142
143+OS Specific Path Encodings
144+--------------------------
145+
146+In general, the OS does not have to look inside the path components
147+other than the ability to recognize the separator char to parse the
148+path into components and to construct it from components. However, in
149+some cases the OS may may have to look inside the path e.g. "." or ".."
150+special paths on Unix.
151+
152+On Unix a path component is just a blob of bytes. The user can encode
153+and decode this blob of bytes as they desire. All they need to care
154+about is that the path separator byte is respected. However, the the
155+path component blobs may be changed by the underlying filesystem
156+e.g. apple HFS may normalize the path before storing, therefore, the
157+representation may change when it is returned back to the user.
158+
159+On modern Windows systems a path component is UTF16-LE
160+encoded. Theoretically, from the OS perspective, it does not have to
161+look inside the path components other than determining whether a 16-bit
162+unit represents the separator character, therefore, users can use any
163+encoding as long as it encodes to 16-bit multiples and the separator is
164+preserved as equivalent to the UTF16-LE representation.
165+
143166Type Safety Requirements
144167------------------------
145168
146169* Safety against using an absolute path where a relative path is to be
147− used and vice-versa.
148−
170+ used and vice-versa.
171+
149172 * Validations for absolute or relative path when constructing a path.
150173 * We cannot append an absolute path to another path
151174* Safety against using a file name where a directory name is to be used and
docs/User/Project/Changelog.md+4
@@ -2,6 +2,10 @@
22
33<!-- See rendered changelog at https://streamly.composewell.com -->
44
5+## 0.10.1 (Jan 2024)
6+
7+* Fix TH macros in `Streamly.Data.Stream.MkType` for GHC 9.6 and above.
8+
59## 0.10.0 (Nov 2023)
610
711See
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/Data/Stream/Prelude.hs+25−15
@@ -6,6 +6,11 @@
66-- Stability : experimental
77-- Portability : GHC
88--
9+-- For upgrading to streamly-0.9.0+ please read the
10+-- <https://github.com/composewell/streamly/blob/streamly-0.10.0/docs/User/Project/Upgrading-0.8-to-0.9.md Streamly-0.9.0 upgrade guide>.
11+-- Also, see the "Streamly.Data.Stream.MkType" module for direct replacement of
12+-- stream types that have been removed in 0.9.0.
13+--
914-- All Stream related combinators including the streamly-core
1015-- "Streamly.Data.Stream" module, concurrency, time and lifted
1116-- exception operations. For more pre-release operations also see
@@ -56,25 +61,28 @@ module Streamly.Data.Stream.Prelude
5661 -- | Stream combinators using a concurrent channel.
5762
5863 -- *** Evaluate
59− -- | Evaluates a serial stream asynchronously using a concurency channel.
64+ -- | Evaluate a stream as a whole concurrently with respect to the consumer
65+ -- of the stream.
6066
6167 , parEval
6268
6369 -- *** Generate
64− -- | Uses a concurrency channel to evaluate multiple actions concurrently.
70+ -- | Generate a stream by evaluating multiple actions concurrently.
6571 , parRepeatM
6672 , parReplicateM
6773 , fromCallback
6874
6975 -- *** Map
70− -- | Uses a concurrency channel to evaluate multiple mapped actions
71− -- concurrently.
76+ -- | Map actions on a stream such that the mapped actions are evaluated
77+ -- concurrently with each other.
7278
7379 , parMapM
7480 , parSequence
7581
7682 -- *** Combine two
77− -- | Use a channel for each pair.
83+ -- | Combine two streams such that each stream as a whole is evaluated
84+ -- concurrently with respect to the other stream as well as the consumer of
85+ -- the resulting stream.
7886 , parZipWithM
7987 , parZipWith
8088 , parMergeByM
@@ -152,16 +160,18 @@ import Streamly.Internal.Data.Stream.Prelude
152160-- 'parConcatMap', and 'parConcatIterate', all concurrency combinators can be
153161-- expressed in terms of these.
154162--
155−-- 'parEval', evaluates a single stream asynchronously, a worker thread runs
156−-- the stream and buffers the results, and the consumer of the stream runs in
157−-- another thread consuming it from the buffer, thus decoupling the production
158−-- and consumption of the stream. This can be used to run different stages of a
159−-- pipeline concurrently.
160−--
161−-- 'parConcatMap' is used to evaluate multiple streams concurrently and combine
162−-- the results. A stream generator function is mapped to the input stream and
163−-- all the generated streams are then evaluated concurrently, and the results
164−-- are combined.
163+-- 'parEval' evaluates a stream as a whole asynchronously with respect to
164+-- the consumer of the stream. A worker thread evaluates multiple elements of
165+-- the stream ahead of time and buffers the results; the consumer of the stream
166+-- runs in another thread consuming the elements from the buffer, thus
167+-- decoupling the production and consumption of the stream. 'parEval' can be
168+-- used to run different stages of a pipeline concurrently.
169+--
170+-- 'parConcatMap' is used to evaluate multiple actions in a stream concurrently
171+-- with respect to each other or to evaluate multiple streams concurrently and
172+-- combine the results. A stream generator function is mapped to the input
173+-- stream and all the generated streams are then evaluated concurrently, and
174+-- the results are combined.
165175--
166176-- 'parConcatIterate' is like 'parConcatMap' but iterates a stream generator
167177-- function recursively over the stream. This can be used to traverse trees or
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/Concurrent.hs+19−11
@@ -147,15 +147,23 @@ parEvalD modifier m = D.Stream step Nothing
147147 D.Stop -> D.Stop
148148-}
149149
150−-- | Evaluate a stream asynchronously. In a serial stream, each element of the
151−-- stream is generated as it is demanded by the consumer. `parEval` evaluates
152−-- multiple elements of the stream ahead of time and serves the results from a
153−-- buffer.
154−--
155−-- Note that the evaluation requires only one thread as only one stream needs
156−-- to be evaluated. Therefore, the concurrency options that are relevant to
157−-- multiple streams won't apply here e.g. maxThreads, eager, interleaved,
158−-- ordered, stopWhen options won't have any effect.
150+-- | 'parEval' evaluates a stream as a whole asynchronously with respect to
151+-- the consumer of the stream. A worker thread evaluates multiple elements of
152+-- the stream ahead of time and buffers the results; the consumer of the stream
153+-- runs in another thread consuming the elements from the buffer, thus
154+-- decoupling the production and consumption of the stream. 'parEval' can be
155+-- used to run different stages of a pipeline concurrently.
156+--
157+-- It is important to note that 'parEval' does not evaluate individual actions
158+-- in the stream concurrently with respect to each other, it merely evaluates
159+-- the stream serially but in a different thread than the consumer thread,
160+-- thus the consumer and producer can run concurrently. See 'parMapM' and
161+-- 'parSequence' to evaluate actions in the stream concurrently.
162+--
163+-- The evaluation requires only one thread as only one stream needs to be
164+-- evaluated. Therefore, the concurrency options that are relevant to multiple
165+-- streams do not apply here e.g. maxThreads, eager, interleaved, ordered,
166+-- stopWhen options do not have any effect on 'parEval'.
159167--
160168-- Useful idioms:
161169--
@@ -646,8 +654,8 @@ parConcatIterate modifier f input =
646654
647655 where
648656
649− iterateStream channel stream =
650− parConcatMapChanKGeneric modifier channel (generate channel) stream
657+ iterateStream channel =
658+ parConcatMapChanKGeneric modifier channel (generate channel)
651659
652660 generate channel x =
653661 -- XXX The channel q should be FIFO for DFS, otherwise it is BFS
src/Streamly/Internal/Data/Stream/IsStream/Common.hs+1−1
@@ -529,7 +529,7 @@ reverse' :: (IsStream t, MonadIO m, Unbox a) => t m a -> t m a
529529-- reverse' s = fromStreamD $ D.reverse' $ toStreamD s
530530reverse' =
531531 fromStreamD
532− . A.flattenArraysRev -- unfoldMany A.readRev
532+ . A.concatRev -- unfoldMany A.readerRev
533533 . D.fromStreamK
534534 . K.reverse
535535 . D.toStreamK
src/Streamly/Internal/Data/Stream/IsStream/Expand.hs+5
@@ -1,3 +1,8 @@
1+{-# LANGUAGE TypeFamilies #-}
2+-- Must come after TypeFamilies, otherwise it is re-enabled.
3+-- MonoLocalBinds enabled by TypeFamilies causes perf regressions in general.
4+{-# LANGUAGE NoMonoLocalBinds #-}
5+
16{-# OPTIONS_GHC -Wno-deprecations #-}
27
38-- |
src/Streamly/Internal/Data/Stream/IsStream/Reduce.hs+1−1
@@ -1624,4 +1624,4 @@ splitInnerBySuffix
16241624 -> t m (f a)
16251625 -> t m (f a)
16261626splitInnerBySuffix splitter joiner xs =
1627− fromStreamD $ D.splitInnerBySuffix splitter joiner $ toStreamD xs
1627+ fromStreamD $ D.splitInnerBySuffix (== mempty) splitter joiner $ toStreamD xs
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/MkType.hs+33−34
@@ -334,10 +334,14 @@ monadthrowInstance _Type =
334334 [ clause
335335 []
336336 (normalB
337− (infixE
338− (Just (varE _lift))
339− (varE _dotOp)
340− (Just (varE _throwM))))
337+ (infixE
338+ (Just (conE _Type))
339+ (varE _dotOp)
340+ (Just
341+ (infixE
342+ (Just (varE (mkName "Stream.fromEffect")))
343+ (varE _dotOp)
344+ (Just (varE _throwM))))))
341345 []
342346 ]
343347 ]
@@ -504,7 +508,7 @@ flattenDec (ma:mas) = do
504508-- >>> expr <- runQ (mkZipType "ZipStream" "zipApply" False)
505509-- >>> putStrLn $ pprint expr
506510-- newtype ZipStream m a
507−-- = ZipStream (Stream.Stream m a)
511+-- = ZipStream (Stream.Stream m a)
508512-- deriving Foldable
509513-- mkZipStream :: Stream.Stream m a -> ZipStream m a
510514-- mkZipStream = ZipStream
@@ -516,19 +520,17 @@ flattenDec (ma:mas) = do
516520-- deriving instance GHC.Classes.Eq a => Eq (ZipStream Identity a)
517521-- deriving instance GHC.Classes.Ord a => Ord (ZipStream Identity a)
518522-- instance Show a => Show (ZipStream Identity a)
519−-- where {-# INLINE show #-}
520−-- show (ZipStream strm) = show strm
523+-- where {{-# INLINE show #-}; show (ZipStream strm) = show strm}
521524-- instance Read a => Read (ZipStream Identity a)
522−-- where {-# INLINE readPrec #-}
523−-- readPrec = fmap ZipStream readPrec
525+-- where {{-# INLINE readPrec #-}; readPrec = fmap ZipStream readPrec}
524526-- instance Monad m => Functor (ZipStream m)
525−-- where {-# INLINE fmap #-}
526−-- fmap f (ZipStream strm) = ZipStream (fmap f strm)
527+-- where {{-# INLINE fmap #-};
528+-- fmap f (ZipStream strm) = ZipStream (fmap f strm)}
527529-- instance Monad m => Applicative (ZipStream m)
528−-- where {-# INLINE pure #-}
529−-- pure = ZipStream . Stream.repeat
530−-- {-# INLINE (<*>) #-}
531−-- (<*>) (ZipStream strm1) (ZipStream strm2) = ZipStream (zipApply strm1 strm2)
530+-- where {{-# INLINE pure #-};
531+-- pure = ZipStream . Stream.repeat;
532+-- {-# INLINE (<*>) #-};
533+-- (<*>) (ZipStream strm1) (ZipStream strm2) = ZipStream (zipApply strm1 strm2)}
532534mkZipType
533535 :: String -- ^ Name of the type
534536 -> String -- ^ Function to use for (\<*\>)
@@ -577,27 +579,25 @@ mkZipType dtNameStr apOpStr isConcurrent =
577579-- unParallel :: Parallel m a -> Stream.Stream m a
578580-- unParallel (Parallel strm) = strm
579581-- instance Monad m => Functor (Parallel m)
580−-- where {-# INLINE fmap #-}
581−-- fmap f (Parallel strm) = Parallel (fmap f strm)
582+-- where {{-# INLINE fmap #-};
583+-- fmap f (Parallel strm) = Parallel (fmap f strm)}
582584-- instance Stream.MonadAsync m => Monad (Parallel m)
583−-- where {-# INLINE (>>=) #-}
584−-- (>>=) (Parallel strm1) f = let f1 a = unParallel (f a)
585−-- in Parallel (parBind strm1 f1)
585+-- where {{-# INLINE (>>=) #-};
586+-- (>>=) (Parallel strm1) f = let f1 a = unParallel (f a)
587+-- in Parallel (parBind strm1 f1)}
586588-- instance Stream.MonadAsync m => Applicative (Parallel m)
587−-- where {-# INLINE pure #-}
588−-- pure = Parallel . Stream.fromPure
589−-- {-# INLINE (<*>) #-}
590−-- (<*>) = ap
589+-- where {{-# INLINE pure #-};
590+-- pure = Parallel . Stream.fromPure;
591+-- {-# INLINE (<*>) #-};
592+-- (<*>) = ap}
591593-- instance (Monad (Parallel m), MonadIO m) => MonadIO (Parallel m)
592−-- where {-# INLINE liftIO #-}
593−-- liftIO = Parallel . (Stream.fromEffect . liftIO)
594−-- instance MonadTrans Parallel
595−-- where {-# INLINE lift #-}
596−-- lift = Parallel . Stream.fromEffect
594+-- where {{-# INLINE liftIO #-};
595+-- liftIO = Parallel . (Stream.fromEffect . liftIO)}
597596-- instance (Monad (Parallel m),
598597-- MonadThrow m) => MonadThrow (Parallel m)
599−-- where {-# INLINE throwM #-}
600−-- throwM = lift . throwM
598+-- where {{-# INLINE throwM #-};
599+-- throwM = Parallel . (Stream.fromEffect . throwM)}
600+
601601mkCrossType
602602 :: String -- ^ Name of the type
603603 -> String -- ^ Function to use for (>>=)
@@ -617,7 +617,7 @@ mkCrossType dtNameStr bindOpStr isConcurrent =
617617 , readInstance _Type
618618 ]
619619 else []
620− , sequence
620+ , sequence $
621621 [ functorInstance _Type
622622 , mkStreamMonad dtNameStr classConstraints bindOpStr
623623 , mkStreamApplicative
@@ -627,10 +627,9 @@ mkCrossType dtNameStr bindOpStr isConcurrent =
627627 "Stream.fromPure"
628628 "ap"
629629 , monadioInstance _Type
630− , monadtransInstance _Type
631630 , monadthrowInstance _Type
632631 -- , monadreaderInstance _Type
633− ]
632+ ] ++ [monadtransInstance _Type | not isConcurrent]
634633 ]
635634
636635 where
src/Streamly/Internal/Data/Stream/Serial.hs+5
@@ -1,4 +1,9 @@
1+{-# LANGUAGE TypeFamilies #-}
2+-- Must come after TypeFamilies, otherwise it is re-enabled.
3+-- MonoLocalBinds enabled by TypeFamilies causes perf regressions in general.
4+{-# LANGUAGE NoMonoLocalBinds #-}
15{-# LANGUAGE UndecidableInstances #-}
6+
27{-# OPTIONS_GHC -Wno-deprecations #-}
38{-# OPTIONS_GHC -Wno-redundant-constraints #-}
49
src/Streamly/Internal/Data/Stream/Zip.hs+5
@@ -1,4 +1,9 @@
1+{-# LANGUAGE TypeFamilies #-}
2+-- Must come after TypeFamilies, otherwise it is re-enabled.
3+-- MonoLocalBinds enabled by TypeFamilies causes perf regressions in general.
4+{-# LANGUAGE NoMonoLocalBinds #-}
15{-# LANGUAGE UndecidableInstances #-}
6+
27{-# OPTIONS_GHC -Wno-deprecations #-}
38
49-- |
src/Streamly/Internal/FileSystem/Event.hs+3
@@ -20,6 +20,9 @@
2020-- * "Streamly.Internal.FileSystem.Event.Linux"
2121-- * "Streamly.Internal.FileSystem.Event.Windows"
2222
23+-- XXX Need to ensure that the signatures of the exported APIs are same on all
24+-- platforms.
25+
2326module Streamly.Internal.FileSystem.Event
2427 (
2528 -- * Creating a Watch
src/Streamly/Internal/FileSystem/Event/Darwin.hs+1−1
@@ -451,7 +451,7 @@ openWatch Config{..} paths = do
451451
452452 withPathName :: Array Word8 -> (PathName -> IO a) -> IO a
453453 withPathName arr act = do
454− A.asPtrUnsafe arr $ \ptr ->
454+ A.unsafePinnedAsPtr arr $ \ptr ->
455455 let pname = PathName (castPtr ptr) (fromIntegral (A.length arr))
456456 in act pname
457457
src/Streamly/Internal/FileSystem/Event/Linux.hs+2−2
@@ -188,7 +188,7 @@ import qualified Streamly.FileSystem.Handle as FH
188188import qualified Streamly.Unicode.Stream as U
189189
190190import qualified Streamly.Internal.Data.Array as A
191− ( fromStream, asCStringUnsafe, asPtrUnsafe
191+ ( fromStream, asCStringUnsafe, unsafePinnedAsPtr
192192 , getSliceUnsafe, read
193193 )
194194import qualified Streamly.Internal.FileSystem.Dir as Dir (readDirs)
@@ -809,7 +809,7 @@ readOneEvent :: Config -> Watch -> Parser Word8 IO Event
809809readOneEvent cfg wt@(Watch _ wdMap) = do
810810 let headerLen = sizeOf (undefined :: CInt) + 12
811811 arr <- PR.takeEQ headerLen (A.writeN headerLen)
812− (ewd, eflags, cookie, pathLen) <- PR.fromEffect $ A.asPtrUnsafe arr readHeader
812+ (ewd, eflags, cookie, pathLen) <- PR.fromEffect $ A.unsafePinnedAsPtr arr readHeader
813813 -- XXX need the "initial" in parsers to return a step type so that "take 0"
814814 -- can return without an input. otherwise if pathLen is 0 we will keep
815815 -- waiting to read one more char before we return this event.
src/Streamly/Internal/FileSystem/FD.hs+5−5
@@ -130,7 +130,7 @@ import qualified GHC.IO.Device as RawIO
130130import Streamly.Data.Array (Array, Unbox)
131131import Streamly.Data.Stream (Stream)
132132
133−import Streamly.Internal.Data.Array (byteLength, unsafeFreeze, asPtrUnsafe)
133+import Streamly.Internal.Data.Array (byteLength, unsafeFreeze, unsafePinnedAsPtr)
134134import Streamly.Internal.System.IO (defaultChunkSize)
135135
136136#if !defined(mingw32_HOST_OS)
@@ -147,7 +147,7 @@ import qualified Streamly.Internal.System.IOVec.Type as RawIO
147147import qualified Streamly.Data.Array as A
148148import qualified Streamly.Data.Fold as FL
149149import qualified Streamly.Internal.Data.MutArray as MArray
150− (MutArray(..), asPtrUnsafe, pinnedNewBytes)
150+ (MutArray(..), unsafePinnedAsPtr, pinnedNewBytes)
151151import qualified Streamly.Internal.Data.Array.Stream as AS
152152import qualified Streamly.Internal.Data.Stream as S
153153import qualified Streamly.Internal.Data.Stream as D
@@ -220,7 +220,7 @@ readArrayUpto :: Int -> Handle -> IO (Array Word8)
220220readArrayUpto size (Handle fd) = do
221221 arr <- MArray.pinnedNewBytes size
222222 -- ptr <- mallocPlainForeignPtrAlignedBytes size (alignment (undefined :: Word8))
223− MArray.asPtrUnsafe arr $ \p -> do
223+ MArray.unsafePinnedAsPtr arr $ \p -> do
224224 -- n <- hGetBufSome h p size
225225#if MIN_VERSION_base(4,15,0)
226226 n <- RawIO.read fd p 0 size
@@ -244,7 +244,7 @@ readArrayUpto size (Handle fd) = do
244244writeArray :: Unbox a => Handle -> Array a -> IO ()
245245writeArray _ arr | A.length arr == 0 = return ()
246246writeArray (Handle fd) arr =
247− asPtrUnsafe arr $ \p ->
247+ unsafePinnedAsPtr arr $ \p ->
248248 -- RawIO.writeAll fd (castPtr p) aLen
249249#if MIN_VERSION_base(4,15,0)
250250 RawIO.write fd (castPtr p) 0 aLen
@@ -270,7 +270,7 @@ writeArray (Handle fd) arr =
270270writeIOVec :: Handle -> Array RawIO.IOVec -> IO ()
271271writeIOVec _ iov | A.length iov == 0 = return ()
272272writeIOVec (Handle fd) iov =
273− asPtrUnsafe iov $ \p ->
273+ unsafePinnedAsPtr iov $ \p ->
274274 RawIO.writevAll fd p (A.length iov)
275275-}
276276#endif
src/Streamly/Internal/Network/Inet/TCP.hs+3−3
@@ -116,7 +116,6 @@ import Prelude hiding (read)
116116import Streamly.Internal.Control.Concurrent (MonadAsync)
117117import Streamly.Internal.Control.ForkLifted (fork)
118118import Streamly.Data.Array (Array)
119−import Streamly.Internal.Data.Array (pinnedWriteNUnsafe)
120119import Streamly.Internal.Data.Fold ( Fold(..) )
121120import Streamly.Data.Stream (Stream)
122121import Streamly.Internal.Data.Tuple.Strict (Tuple'(..))
@@ -132,7 +131,8 @@ import qualified Streamly.Data.Array as A
132131import qualified Streamly.Data.Fold as FL
133132import qualified Streamly.Data.Stream as S
134133import qualified Streamly.Data.Unfold as UF
135−import qualified Streamly.Internal.Data.Array as A (pinnedChunksOf)
134+import qualified Streamly.Internal.Data.Array as A
135+ (pinnedChunksOf, unsafePinnedCreateOf)
136136import qualified Streamly.Internal.Data.Unfold as UF (bracketIO)
137137import qualified Streamly.Internal.Data.Fold as FL (Step(..), reduce)
138138
@@ -435,7 +435,7 @@ writeWithBufferOf
435435 -> PortNumber
436436 -> Fold m Word8 ()
437437writeWithBufferOf n addr port =
438− FL.groupsOf n (pinnedWriteNUnsafe n) (writeChunks addr port)
438+ FL.groupsOf n (A.unsafePinnedCreateOf n) (writeChunks addr port)
439439
440440-- | Write a stream to the supplied IPv4 host address and port number.
441441--
src/Streamly/Internal/Network/Socket.hs+11−10
@@ -87,7 +87,6 @@ import Prelude hiding (read)
8787import qualified Network.Socket as Net
8888
8989import Streamly.Internal.Data.Array (Array(..))
90−import Streamly.Internal.Data.Array.Stream (lpackArraysChunksOf)
9190import Streamly.Data.Fold (Fold)
9291import Streamly.Data.Stream (Stream)
9392import Streamly.Internal.Data.Unfold (Unfold(..))
@@ -99,10 +98,10 @@ import qualified Streamly.Data.Fold as FL
9998import qualified Streamly.Data.Stream as S
10099import qualified Streamly.Data.Unfold as UF
101100import qualified Streamly.Internal.Data.Array as A
102− ( unsafeFreeze, asPtrUnsafe, byteLength, pinnedChunksOf,
103− pinnedWriteN, pinnedWriteNUnsafe )
101+ ( unsafeFreeze, unsafePinnedAsPtr, byteLength, pinnedChunksOf,
102+ pinnedCreateOf, unsafePinnedCreateOf, lCompactGE )
104103import qualified Streamly.Internal.Data.MutArray as MArray
105− (MutArray(..), asPtrUnsafe, pinnedNewBytes)
104+ (MutArray(..), unsafePinnedAsPtr, pinnedEmptyOf)
106105import qualified Streamly.Internal.Data.Stream as S (fromStreamK, Stream(..), Step(..))
107106import qualified Streamly.Internal.Data.StreamK as K (mkStream)
108107
@@ -253,6 +252,8 @@ accept tcpListenQ spec addr =
253252-- Array IO (Input)
254253-------------------------------------------------------------------------------
255254
255+-- XXX add an API that compacts the arrays to an exact size.
256+
256257{-# INLINABLE readArrayUptoWith #-}
257258readArrayUptoWith
258259 :: (h -> Ptr Word8 -> Int -> IO Int)
@@ -260,9 +261,9 @@ readArrayUptoWith
260261 -> h
261262 -> IO (Array Word8)
262263readArrayUptoWith f size h = do
263− arr <- MArray.pinnedNewBytes size
264+ arr <- MArray.pinnedEmptyOf size
264265 -- ptr <- mallocPlainForeignPtrAlignedBytes size (alignment (undefined :: Word8))
265− MArray.asPtrUnsafe arr $ \p -> do
266+ MArray.unsafePinnedAsPtr arr $ \p -> do
266267 n <- f h p size
267268 let v = A.unsafeFreeze
268269 $ arr { MArray.arrEnd = n, MArray.arrBound = size }
@@ -310,7 +311,7 @@ writeArrayWith :: Unbox a
310311 -> Array a
311312 -> IO ()
312313writeArrayWith _ _ arr | A.length arr == 0 = return ()
313−writeArrayWith f h arr = A.asPtrUnsafe arr $ \ptr -> f h (castPtr ptr) aLen
314+writeArrayWith f h arr = A.unsafePinnedAsPtr arr $ \ptr -> f h (castPtr ptr) aLen
314315
315316 where
316317
@@ -473,7 +474,7 @@ writeChunks h = FL.drainMapM (liftIO . putChunk h)
473474{-# INLINE writeChunksWith #-}
474475writeChunksWith :: (MonadIO m, Unbox a)
475476 => Int -> Socket -> Fold m (Array a) ()
476−writeChunksWith n h = lpackArraysChunksOf n (writeChunks h)
477+writeChunksWith n h = A.lCompactGE n (writeChunks h)
477478
478479-- | Same as 'writeChunksWith'
479480--
@@ -504,7 +505,7 @@ putBytesWith n h m = putChunks h $ A.pinnedChunksOf n m
504505--
505506{-# INLINE writeWith #-}
506507writeWith :: MonadIO m => Int -> Socket -> Fold m Word8 ()
507−writeWith n h = FL.groupsOf n (A.pinnedWriteNUnsafe n) (writeChunks h)
508+writeWith n h = FL.groupsOf n (A.unsafePinnedCreateOf n) (writeChunks h)
508509
509510-- | Same as 'writeWith'
510511--
@@ -522,7 +523,7 @@ writeWithBufferOf = writeWith
522523writeMaybesWith :: (MonadIO m )
523524 => Int -> Socket -> Fold m (Maybe Word8) ()
524525writeMaybesWith n h =
525− let writeNJusts = FL.lmap fromJust $ A.pinnedWriteN n
526+ let writeNJusts = FL.lmap fromJust $ A.pinnedCreateOf n
526527 writeOnNothing = FL.takeEndBy_ isNothing writeNJusts
527528 in FL.many writeOnNothing (writeChunks h)
528529
src/Streamly/Internal/Unicode/Char.hs+3−3
@@ -227,13 +227,13 @@ partialComposeD (Stream step state) = Stream step' (ComposeNone state)
227227 Stop -> Skip $ YieldList rbuf ComposeStop
228228
229229 {-# INLINE initHangul #-}
230− initHangul c st = ComposeJamo (Hangul c) st
230+ initHangul c = ComposeJamo (Hangul c)
231231
232232 {-# INLINE initJamo #-}
233− initJamo c st = ComposeJamo (Jamo c) st
233+ initJamo c = ComposeJamo (Jamo c)
234234
235235 {-# INLINE initReg #-}
236− initReg !c st = ComposeReg 0 [c] st
236+ initReg !c = ComposeReg 0 [c]
237237
238238 {-# INLINE composeNone #-}
239239 composeNone ch st
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]
test/Streamly/Test/Data/Fold.hs+7−6
@@ -2,7 +2,8 @@
22
33module Main (main) where
44
5−import Data.List (sort)
5+import Data.List (sort, sortBy)
6+import Data.Ord (comparing, Down(..))
67import Data.Semigroup (Sum(..), getSum)
78import Streamly.Test.Common (checkListEqual, listEquals)
89import Test.QuickCheck
@@ -412,7 +413,7 @@ partitionByM =
412413 where
413414
414415 action ls = do
415− let f = \x -> if odd x then return (Left x) else return (Right x)
416+ let f x = if odd x then return (Left x) else return (Right x)
416417 v1 <-
417418 run
418419 $ Stream.fold (Fold.partitionByM f Fold.length Fold.length)
@@ -429,7 +430,7 @@ partitionByFstM =
429430 where
430431
431432 action _ = do
432− let f = \x -> if odd x then return (Left x) else return (Right x)
433+ let f x = if odd x then return (Left x) else return (Right x)
433434 v1 <-
434435 run
435436 $ Stream.fold
@@ -447,7 +448,7 @@ partitionByMinM1 =
447448 where
448449
449450 action _ = do
450− let f = \x -> if odd x then return (Left x) else return (Right x)
451+ let f x = if odd x then return (Left x) else return (Right x)
451452 v1 <-
452453 run
453454 $ Stream.fold
@@ -465,7 +466,7 @@ partitionByMinM2 =
465466 where
466467
467468 action _ = do
468− let f = \x -> if odd x then return (Left x) else return (Right x)
469+ let f x = if odd x then return (Left x) else return (Right x)
469470 v1 <-
470471 run
471472 $ Stream.fold
@@ -690,7 +691,7 @@ topBy isTop = forAll (listOf (chooseInt (-50, 100))) $ \ls0 ->
690691 if isTop
691692 then do
692693 lst <- Stream.fold (Fold.top n) (Stream.fromList ls) >>= MArray.toList
693− assert ((Prelude.take n . Prelude.reverse . sort) ls == lst)
694+ assert ((Prelude.take n . sortBy (comparing Down)) ls == lst)
694695 else do
695696 lst <- Stream.fold (Fold.bottom n) (Stream.fromList ls) >>= MArray.toList
696697 assert ((Prelude.take n . sort) ls == lst)
test/Streamly/Test/Data/Serialize.hs+20−1
@@ -109,9 +109,19 @@ $(Serialize.deriveSerializeWith
109109 CONF
110110 [d|instance Serialize a => Serialize (BinTree a)|])
111111
112+-- XXX This may not terminate, or could become really large.
112113instance Arbitrary a => Arbitrary (BinTree a) where
113114 arbitrary = oneof [Leaf <$> arbitrary, Tree <$> arbitrary <*> arbitrary]
114115
116+-- Make a balanced tree of given level
117+mkBinTree :: (Arbitrary a) => Int -> IO (BinTree a)
118+mkBinTree = go (generate arbitrary)
119+
120+ where
121+
122+ go r 0 = Leaf <$> r
123+ go r n = Tree <$> go r (n - 1) <*> go r (n - 1)
124+
115125--------------------------------------------------------------------------------
116126-- Record syntax type
117127--------------------------------------------------------------------------------
@@ -207,6 +217,10 @@ roundtrip
207217 -> IO ()
208218roundtrip val = do
209219
220+ -- For debugging large size generated by arbitrary
221+ -- let sz = Serialize.addSizeTo 0 val
222+ -- putStrLn $ "Size is: " ++ show sz
223+
210224 val `shouldBe` Array.deserialize (Array.pinnedSerialize val)
211225
212226 res <- poke val
@@ -270,7 +284,12 @@ testCases = do
270284 $ \(x :: [CustomDatatype]) -> roundtrip x
271285
272286 limitQC
273− $ prop "BinTree" $ \(x :: BinTree Int) -> roundtrip x
287+ $ prop "BinTree"
288+ $ forAll (elements [1..15])
289+ (\(x :: Int) -> do
290+ (r :: BinTree Int) <- mkBinTree x
291+ roundtrip r
292+ )
274293
275294 where
276295
test/Streamly/Test/Data/Serialize/TH.hs+2−2
@@ -26,7 +26,7 @@ import Test.QuickCheck.Gen (oneof)
2626genDatatype :: String -> Int -> Q [Dec]
2727genDatatype tyName numCons =
2828 sequence
29− ([ dataD
29+ [ dataD
3030 (pure [])
3131 (mkName tyName)
3232 []
@@ -49,7 +49,7 @@ genDatatype tyName numCons =
4949 []
5050 ]
5151 ]
52− ])
52+ ]
5353
5454 where
5555
test/Streamly/Test/FileSystem/Handle.hs+2−2
@@ -75,7 +75,7 @@ readFromHandle =
7575
7676readWithBufferFromHandle :: IO (Stream IO Char)
7777readWithBufferFromHandle =
78− let f1 = (\h -> (1024, h))
78+ let f1 h = (1024, h)
7979 f2 = Unicode.decodeUtf8 . Stream.unfold Handle.readerWith . f1
8080 in executor f2
8181
@@ -88,7 +88,7 @@ readChunksFromHandle =
8888
8989readChunksWithBuffer :: IO (Stream IO Char)
9090readChunksWithBuffer =
91− let f1 = (\h -> (1024, h))
91+ let f1 h = (1024, h)
9292 f2 =
9393 Unicode.decodeUtf8
9494 . Stream.concatMap Array.read
test/Streamly/Test/Unicode/Char.hs+1−1
@@ -54,7 +54,7 @@ checkEqual opName op (mc1, mc2) = do
5454checkOp :: String -> NormalizationMode -> [(Text, Text)] -> IO Bool
5555checkOp name op pairs = do
5656 res <- mapM (checkEqual name (normalize op)) pairs
57− return $ all (== True) res
57+ return $ and res
5858
5959checkNFC :: (Text, Text, Text, Text, Text) -> IO Bool
6060checkNFC (c1, c2, c3, c4, c5) =