on d5342f6 help sign in
.circleci/config.yml+277−73
since splitsnapshots
unifiedsplit
@@ -1,7 +1,7 @@
1version: 2.1
1version: 2.1
2
2
3#-----------------------------------------------------------------------------
3#-----------------------------------------------------------------------------
4−# packcheck-0.7.0
4+# packcheck-0.4.2
5# Packcheck global environment variables
5# Packcheck global environment variables
6#-----------------------------------------------------------------------------
6#-----------------------------------------------------------------------------
7
7
@@ -11,8 +11,6 @@ env: &env
11 # Common options
11 # Common options
12 # ------------------------------------------------------------------------
12 # ------------------------------------------------------------------------
13 # GHC_OPTIONS: "-Werror"
13 # GHC_OPTIONS: "-Werror"
14− # For updating see: https://downloads.haskell.org/~ghcup/
15− GHCUP_VERSION: 0.1.20.0
16 CABAL_REINIT_CONFIG: "y"
14 CABAL_REINIT_CONFIG: "y"
17 LC_ALL: "C.UTF-8"
15 LC_ALL: "C.UTF-8"
18
16
@@ -24,6 +22,7 @@ env: &env
24 # DISABLE_DOCS: "y"
22 # DISABLE_DOCS: "y"
25 # DISABLE_SDIST_BUILD: "y"
23 # DISABLE_SDIST_BUILD: "y"
26 # DISABLE_DIST_CHECKS: "y"
24 # DISABLE_DIST_CHECKS: "y"
25+ ENABLE_INSTALL: "y"
27
26
28 # ------------------------------------------------------------------------
27 # ------------------------------------------------------------------------
29 # stack options
28 # stack options
@@ -39,12 +38,15 @@ env: &env
39 # cabal options
38 # cabal options
40 # ------------------------------------------------------------------------
39 # ------------------------------------------------------------------------
41 CABAL_CHECK_RELAX: "y"
40 CABAL_CHECK_RELAX: "y"
41+ CABAL_NO_SANDBOX: "y"
42+ CABAL_HACKAGE_MIRROR: "hackage.haskell.org:http://hackage.fpcomplete.com"
42 CABAL_BUILD_OPTIONS: "-j1 --flag limit-build-mem"
43 CABAL_BUILD_OPTIONS: "-j1 --flag limit-build-mem"
43
44
44 # ------------------------------------------------------------------------
45 # ------------------------------------------------------------------------
45 # Where to find the required tools
46 # Where to find the required tools
46 # ------------------------------------------------------------------------
47 # ------------------------------------------------------------------------
47− PATH: /sbin:/usr/sbin:/bin:/usr/bin
48+ PATH: /opt/ghc/bin:/opt/ghcjs/bin:/sbin:/usr/sbin:/bin:/usr/bin
49+ TOOLS_DIR: /opt
48
50
49 # ------------------------------------------------------------------------
51 # ------------------------------------------------------------------------
50 # Location of packcheck.sh (the shell script invoked to perform CI tests ).
52 # Location of packcheck.sh (the shell script invoked to perform CI tests ).
@@ -57,15 +59,15 @@ env: &env
57 # If you have not committed packcheck.sh in your repo at PACKCHECK
59 # If you have not committed packcheck.sh in your repo at PACKCHECK
58 # then it is automatically pulled from this URL.
60 # then it is automatically pulled from this URL.
59 PACKCHECK_GITHUB_URL: "https://raw.githubusercontent.com/composewell/packcheck"
61 PACKCHECK_GITHUB_URL: "https://raw.githubusercontent.com/composewell/packcheck"
60− PACKCHECK_GITHUB_COMMIT: "f6cfd7b7a33b4bf6fd63cce6a3fb3d26f5031403"
62+ PACKCHECK_GITHUB_COMMIT: "a302772c8f3791c477edd1764b82f8d1f5b22fdf"
61
63
62executors:
64executors:
63 amd64-executor:
65 amd64-executor:
64 docker:
66 docker:
65− - image: ubuntu:latest
67+ - image: ubuntu:xenial
66 x86-executor:
68 x86-executor:
67 docker:
69 docker:
68− - image: i386/ubuntu:focal
70+ - image: i386/ubuntu:eoan
69
71
70#-----------------------------------------------------------------------------
72#-----------------------------------------------------------------------------
71# Common utility stuff, not to be modified usually
73# Common utility stuff, not to be modified usually
@@ -77,50 +79,38 @@ preinstall: &preinstall
77 # required for https/cache save and restore
79 # required for https/cache save and restore
78 apt-get install -y ca-certificates
80 apt-get install -y ca-certificates
79
81
80− # Alternative way of installing ghc and cabal, directly from
82+ # required to (re)generate the configure script
81− # haskell.org instead of using ghcup. NOTE: this is for Debian
82− # only and is debian release specific.
83− # gnupg is required for apt-key to work
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
88−
89− # required for outbound https for stack and for stack setup
90− apt-get install -y netbase xz-utils make
83+ apt-get install -y autoconf
91− apt-get install -y zlib1g-dev
92
84
93− # For ghcup to install ghc
85+ # For ghc and cabal-install packages from hvr's ppa
94− if test -n "$GHCUP_VERSION"
86+ # gnupg is required for apt-key to work
95− then
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
96− apt-get install -y gcc
90+ apt-get install -y software-properties-common
97− apt-get install -y g++
91+ add-apt-repository -y ppa:hvr/ghc
98− fi
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
99
95
100− # libgmp required by ghc for linking
96+ # required for packcheck
101− apt-get install -y libgmp-dev
97+ apt-get install -y curl
102− apt-get install -y libtinfo-dev
103
98
104− # Required by cabal when git URL is specified in project file
99+ # required for circleci.
100+ apt-get install -y libcurl4-gnutls-dev
105 apt-get install -y git
101 apt-get install -y git
102+ apt-get install -y libtinfo-dev
106
103
107− # Required for and by packcheck
104+ # required for outbound https for stack and for stack setup
108− apt-get install -y curl
105+ apt-get install -y netbase xz-utils make
109
106
107+project-preinstall: &project-preinstall
108+ run: |
110 # Get packcheck if needed
109 # Get packcheck if needed
111− if test ! -e "$PACKCHECK"
110+ CURL=$(which curl)
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
111+ PACKCHECK_URL=${PACKCHECK_GITHUB_URL}/${PACKCHECK_GITHUB_COMMIT}/packcheck.sh
118− curl --fail -sL -o "$PACKCHECK" $PACKCHECK_URL || exit 1
112+ if test ! -e "$PACKCHECK"; then $CURL -sL -o "$PACKCHECK" $PACKCHECK_URL; fi;
119− chmod +x $PACKCHECK
120− elif test ! -x "$PACKCHECK"
121− then
122− chmod +x $PACKCHECK
113+ chmod +x $PACKCHECK
123− fi
124
114
125restore: &restore
115restore: &restore
126 # Needs to happen after installing ca-certificates
116 # Needs to happen after installing ca-certificates
@@ -136,71 +126,271 @@ save: &save
136 save_cache:
126 save_cache:
137 key: v1-{{ .Environment.CIRCLE_JOB }}-{{ .Branch }}
127 key: v1-{{ .Environment.CIRCLE_JOB }}-{{ .Branch }}
138 paths:
128 paths:
139− - ~/.local
140 - ~/.cabal
129 - ~/.cabal
130+ - ~/.ghc
131+ - ~/.local
141 - ~/.stack
132 - ~/.stack
142− - ~/.ghcup
143
133
144#-----------------------------------------------------------------------------
134#-----------------------------------------------------------------------------
145# Build matrix
135# Build matrix
146#-----------------------------------------------------------------------------
136#-----------------------------------------------------------------------------
147
137
148jobs:
138jobs:
149− cabal-ghc-8_6_5:
139+ cabal-ghc-8_10_4:
150 <<: *env
140 <<: *env
151 executor: amd64-executor
141 executor: amd64-executor
152 steps:
142 steps:
143+ - *preinstall
153 - checkout
144 - checkout
145+ - *project-preinstall
154 - *restore
146 - *restore
155− - *preinstall
156 - run:
147 - run:
157 environment:
148 environment:
158− GHCVER: "8.6.5"
159− CABAL_VERSION: "3.6.2.0"
160 CABAL_PROJECT: "cabal.project"
149 CABAL_PROJECT: "cabal.project"
150+ DISABLE_TEST: "yes"
151+ DISABLE_BENCH: "yes"
161 DISABLE_SDIST_BUILD: "yes"
152 DISABLE_SDIST_BUILD: "yes"
162− CABAL_BUILD_OPTIONS: "--flag debug --flag -opt"
163 command: |
153 command: |
154+ apt-get install -y ghc-8.10.4
155+ apt-get install -y cabal-install-3.4
164 bash -c "$PACKCHECK cabal"
156 bash -c "$PACKCHECK cabal"
165 - *save
157 - *save
166− cabal-ghc-9_8_1-docspec:
158+ stack-ghc-8_8:
167 <<: *env
159 <<: *env
168 executor: amd64-executor
160 executor: amd64-executor
169 steps:
161 steps:
162+ - *preinstall
170 - checkout
163 - checkout
164+ - *project-preinstall
171 - *restore
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:
172 - *preinstall
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:
211+ <<: *env
212+ executor: amd64-executor
213+ steps:
214+ - *preinstall
215+ - checkout
216+ - *project-preinstall
217+ - *restore
173 - run:
218 - run:
174 environment:
219 environment:
175− GHCVER: "9.8.1"
220+ BUILD: "cabal-v2"
176− CABAL_VERSION: "3.10.1.0"
177− CABAL_PROJECT: "cabal.project.doctest"
178− DISABLE_SDIST_BUILD: "y"
179− DISABLE_TEST: "y"
221+ DISABLE_TEST: "yes"
180− DISABLE_BENCH: "y"
222+ DISABLE_BENCH: "yes"
181− DISABLE_DOCS: "y"
223+ DISABLE_DOCS: "yes"
182− ENABLE_DOCSPEC: "y"
224+ DISABLE_SDIST_BUILD: "yes"
183− DOCSPEC_URL: https://github.com/phadej/cabal-extras/releases/download/cabal-docspec-0.0.0.20210111/cabal-docspec-0.0.0.20210111.xz
225+ DISABLE_DIST_CHECKS: "yes"
184− DOCSPEC_OPTIONS: "--timeout 60 --check-properties --property-variables xs"
185 command: |
226 command: |
227+ apt-get install -y ghc-8.2.2
228+ apt-get install -y cabal-install-3.2
186− bash -c "$PACKCHECK cabal"
229+ bash -c "$PACKCHECK $BUILD"
187 - *save
230 - *save
188− hlint-trailing-spaces:
231+ cabal-ghc-8_0_2:
189 <<: *env
232 <<: *env
190 executor: amd64-executor
233 executor: amd64-executor
191 steps:
234 steps:
235+ - *preinstall
192 - checkout
236 - checkout
237+ - *project-preinstall
193 - *restore
238 - *restore
239+ - run:
240+ 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"
247+ 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:
194 - *preinstall
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"
264+ - *save
265+ stack-ghc-8_4:
266+ <<: *env
267+ executor: amd64-executor
268+ steps:
269+ - *preinstall
270+ - checkout
271+ - *project-preinstall
272+ - *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:
280+ - *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
195 - run:
295 - run:
196− name: hlint and trailing spaces
296+ name: GHCJS 8.4+no-test+no-docs
197 environment:
297 environment:
298+ BUILD: cabal-v2
299+ ENABLE_GHCJS: "yes"
300+ DISABLE_TEST: "yes"
301+ ENABLE_INSTALL: ""
302+ DISABLE_DOCS: "yes"
198 DISABLE_SDIST_BUILD: "yes"
303 DISABLE_SDIST_BUILD: "yes"
199− HLINT_VERSION: 3.6.1
304+ 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+ hlint-src:
378+ <<: *env
379+ executor: amd64-executor
380+ steps:
381+ - *preinstall
382+ - checkout
383+ - *project-preinstall
384+ - *restore
385+ - run:
386+ name: Hlint src
387+ environment:
388+ BUILD: hlint
200 HLINT_OPTIONS: lint
389 HLINT_OPTIONS: lint
390+ DISABLE_SDIST_BUILD: "yes"
201 HLINT_TARGETS: core/src src test benchmark
391 HLINT_TARGETS: core/src src test benchmark
202 command: |
392 command: |
203− bash -c "$PACKCHECK hlint" || exit 1
393+ bash -c "$PACKCHECK $BUILD" || exit 1
204 echo "Checking trailing spaces..."
394 echo "Checking trailing spaces..."
205 count=$(find . -name "*.hs" -exec grep -H '\ $' {} \; | tee /dev/tty | wc -l)
395 count=$(find . -name "*.hs" -exec grep -H '\ $' {} \; | tee /dev/tty | wc -l)
206 exit $count
396 exit $count
@@ -210,9 +400,23 @@ workflows:
210 version: 2
400 version: 2
211 build:
401 build:
212 jobs:
402 jobs:
213− #- cabal-ghc-8_6_5:
403+ - cabal-ghc-8_10_4:
214− # name: 8.6.5-debug-unoptimized
404+ name: GHC 8.10.4 + sdist + no-test + no-bench
405+ #- cabal-ghc-8.6.5
406+ #- cabal-ghc-8_4_4:
407+ # name: GHC 8.4.4 + x86 + debug
408+ #- cabal-ghc-8_2_2:
409+ # name: GHC 8.2.2 + no-test + no-bench + no-docs
215− - cabal-ghc-9_8_1-docspec:
410+ #- cabal-ghc-8_0_2:
216− name: ghc-9.8.1-docspec
411+ # name: GHC 8.0.2 + no-test + no-bench + no-docs
217− - hlint-trailing-spaces:
412+ #- cabal-ghc-7.10.3
218− name: hlint and trailing spaces
413+ # - cabal-ghcjs-8_4:
414+ # name: GHCJS 8.4 + no-test + no-docs
415+ #- stack-ghc-8.4
416+ #- stack-ghc-8.2
417+ #- coveralls-ghc-8.2.2
418+ #- coveralls-ghc-8.8.3
419+ #- coveralls-ghc-8_10_2:
420+ # name: GHC 8.10.2 + inspection + coverage + Werror
421+ - hlint-src:
422+ name: Hlint src + Trailing Spaces
.github/workflows/haskell.yml+60−61
@@ -13,15 +13,9 @@ jobs:
13 # packcheck environment variables
13 # packcheck environment variables
14 LC_ALL: C.UTF-8
14 LC_ALL: C.UTF-8
15 BUILD: ${{ matrix.build }}
15 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 }}
21 GHCVER: ${{ matrix.ghc_version }}
16 GHCVER: ${{ matrix.ghc_version }}
22 CABALVER: ${{ matrix.cabal_version }}
17 CABALVER: ${{ matrix.cabal_version }}
23 DISABLE_DOCS: ${{ matrix.disable_docs }}
18 DISABLE_DOCS: ${{ matrix.disable_docs }}
24− ENABLE_DOCSPEC: ${{ matrix.enable_docspec }}
25 DISABLE_TEST: ${{ matrix.disable_test }}
19 DISABLE_TEST: ${{ matrix.disable_test }}
26 DISABLE_BENCH: ${{ matrix.disable_bench }}
20 DISABLE_BENCH: ${{ matrix.disable_bench }}
27 DISABLE_DIST_CHECKS: ${{ matrix.disable_dist_checks }}
21 DISABLE_DIST_CHECKS: ${{ matrix.disable_dist_checks }}
@@ -45,18 +39,14 @@ jobs:
45 STACK_BUILD_OPTIONS: ${{ matrix.stack_build_options }}
39 STACK_BUILD_OPTIONS: ${{ matrix.stack_build_options }}
46
40
47 # packcheck location and revision
41 # packcheck location and revision
48− PACKCHECK: "./packcheck.sh"
42+ PACKCHECK_LOCAL_PATH: "./packcheck.sh"
49 PACKCHECK_GITHUB_URL: "https://raw.githubusercontent.com/composewell/packcheck"
43 PACKCHECK_GITHUB_URL: "https://raw.githubusercontent.com/composewell/packcheck"
50− PACKCHECK_GITHUB_COMMIT: "v0.7.0"
44+ PACKCHECK_GITHUB_COMMIT: "a302772c8f3791c477edd1764b82f8d1f5b22fdf"
51
45
52 # Pull token from "secrets" setting of the github repo
46 # Pull token from "secrets" setting of the github repo
53 COVERALLS_TOKEN: ${{ secrets.COVERALLS_TOKEN }}
47 COVERALLS_TOKEN: ${{ secrets.COVERALLS_TOKEN }}
54 COVERAGE: ${{ matrix.coverage }}
48 COVERAGE: ${{ matrix.coverage }}
55−
49+ DOCTEST: ${{ matrix.doctest }}
56− # hlint
57− HLINT_VERSION: 3.6.1
58− HLINT_OPTIONS: "lint"
59− HLINT_TARGETS: "core/src src test benchmark"
60
50
61 # Subdir
51 # Subdir
62 SUBDIR: ${{ matrix.subdir }}
52 SUBDIR: ${{ matrix.subdir }}
@@ -69,7 +59,7 @@ jobs:
69 # The order is important to optimize fail-fast.
59 # The order is important to optimize fail-fast.
70 name:
60 name:
71 - 9.8.1-Werror
61 - 9.8.1-Werror
72− # - 9.8.1-docspec
62+ - 9.2.8-docspec
73 # - 8.10.7-coverage
63 # - 8.10.7-coverage
74
64
75 # Note: if cabal.project is not specified benchmarks and tests won't
65 # Note: if cabal.project is not specified benchmarks and tests won't
@@ -78,19 +68,9 @@ jobs:
78 include:
68 include:
79 - name: head
69 - name: head
80 ghc_version: head
70 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"
91 runner: ubuntu-latest
71 runner: ubuntu-latest
92 build: cabal
72 build: cabal
93− cabal_version: 3.10.2.0
73+ cabal_version: 3.10.1.0
94 cabal_project: cabal.project.ghc-head
74 cabal_project: cabal.project.ghc-head
95 disable_sdist_build: "y"
75 disable_sdist_build: "y"
96 ignore_error: true
76 ignore_error: true
@@ -103,18 +83,6 @@ jobs:
103 disable_sdist_build: "y"
83 disable_sdist_build: "y"
104 cabal_project: cabal.project.Werror
84 cabal_project: cabal.project.Werror
105 ignore_error: false
85 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
118 - name: 9.8.1-fusion-inspection
86 - name: 9.8.1-fusion-inspection
119 ghc_version: 9.8.1
87 ghc_version: 9.8.1
120 runner: ubuntu-latest
88 runner: ubuntu-latest
@@ -148,6 +116,14 @@ jobs:
148 cabal_version: 3.6.2.0
116 cabal_version: 3.6.2.0
149 disable_sdist_build: "y"
117 disable_sdist_build: "y"
150 ignore_error: false
118 ignore_error: false
119+ - name: 9.2.8-docspec
120+ ghc_version: 9.2.8
121+ runner: ubuntu-latest
122+ doctest: "y"
123+ cabal_version: 3.6.2.0
124+ cabal_project: cabal.project
125+ disable_sdist_build: "y"
126+ ignore_error: false
151 - name: 9.0.2-streamly-sdist
127 - name: 9.0.2-streamly-sdist
152 ghc_version: 9.0.2
128 ghc_version: 9.0.2
153 runner: ubuntu-latest
129 runner: ubuntu-latest
@@ -194,44 +170,66 @@ jobs:
194 cabal_build_options: "--flag debug --flag -opt"
170 cabal_build_options: "--flag debug --flag -opt"
195 disable_sdist_build: "y"
171 disable_sdist_build: "y"
196 ignore_error: false
172 ignore_error: false
197− - name: hlint
198− build: hlint
199− runner: ubuntu-latest
200− ignore_error: true
201
173
202 steps:
174 steps:
203 - uses: actions/checkout@v2
175 - uses: actions/checkout@v2
204
176
205 - uses: actions/cache@v1
177 - uses: actions/cache@v1
206− name: Cache common directories
178+ name: Cache ~/.cabal
207 with:
179 with:
208− path: |
209− ~/.local
210− ~/.cabal
180+ path: ~/.cabal
211− ~/.stack
212− ~/.ghcup
213 # Bump the key version to clear the cache
181 # Bump the key version to clear the cache
214 key: ${{ runner.os }}-${{ matrix.ghc_version }}-cabal-v2
182 key: ${{ runner.os }}-${{ matrix.ghc_version }}-cabal-v2
215
183
216 - name: Download packcheck
184 - name: Download packcheck
217 run: |
185 run: |
218− if test ! -e "$PACKCHECK"
186+ # Get packcheck if needed
219− then
187+ CURL=$(which curl)
220− if test -z "$PACKCHECK_GITHUB_COMMIT"
188+ PACKCHECK_URL=${PACKCHECK_GITHUB_URL}/${PACKCHECK_GITHUB_COMMIT}/packcheck.sh
221− then
189+ if test ! -e "$PACKCHECK_LOCAL_PATH"; then $CURL -sL -o "$PACKCHECK_LOCAL_PATH" $PACKCHECK_URL; fi;
222− die "PACKCHECK_GITHUB_COMMIT is not specified."
190+ chmod +x $PACKCHECK_LOCAL_PATH
223− fi
191+
224− PACKCHECK_URL=${PACKCHECK_GITHUB_URL}/${PACKCHECK_GITHUB_COMMIT}/packcheck.sh
192+ os=$(uname -s -m)
225− curl --fail -sL -o "$PACKCHECK" $PACKCHECK_URL || exit 1
193+ case "$os" in
194+ "Linux x86_64") GHCUP_ARCH="x86_64-linux" ;;
195+ "Darwin x86_64") GHCUP_ARCH="x86_64-apple-darwin" ;;
196+ *) echo "Unknown OS/Arch: $os"; exit 1;;
197+ esac
198+
199+ # Check available versions here: https://downloads.haskell.org/~ghcup/
200+ GHCUP_VER=0.1.18.0
201+ $CURL -sL -o ./ghcup https://downloads.haskell.org/~ghcup/$GHCUP_VER/${GHCUP_ARCH}-ghcup-$GHCUP_VER
226− chmod +x $PACKCHECK
202+ chmod +x ./ghcup
227− elif test ! -x "$PACKCHECK"
203+ if test "$GHCVER" = "head"
228 then
204 then
229− chmod +x $PACKCHECK
205+ # The URL may change, to find a working URL go to https://gitlab.haskell.org/ghc/ghc/-/jobs/
206+ # Find a debian10 job, click on a passed/failed job, at the
207+ # end of the output you will find the tar.xz name, put that after
208+ # "raw/", and put the job name after "job=".
209+ # Also see https://github.com/mpickering/ghc-artefact-nix/blob/master/gitlab-artifact.nix
210+ ./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
211+ else
212+ ./ghcup install ghc $GHCVER
230 fi
213 fi
214+ ./ghcup install cabal $CABALVER
215+ ./ghcup set ghc $GHCVER
216+ ./ghcup set cabal $CABALVER
231
217
232 - name: Run tests
218 - name: Run tests
233 run: |
219 run: |
220+ if test -n "$DOCTEST"
221+ then
222+ mkdir -p $HOME/.cabal/bin
223+ 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
224+ echo '0829bd034fba901cbcfe491d98ed8b28fd54f9cb5c91fa8e1ac62dc4413c9562 cabal-docspec.xz' | sha256sum -c -
225+ xz -d < cabal-docspec.xz > $HOME/.cabal/bin/cabal-docspec
226+ rm -f cabal-docspec.xz
227+ chmod a+x $HOME/.cabal/bin/cabal-docspec
228+ cabal update
229+ cabal build --project-file cabal.project.doctest all
230+ $HOME/.cabal/bin/cabal-docspec --version
231+ $HOME/.cabal/bin/cabal-docspec --timeout 60 --check-properties --property-variables xs
234− if test -n "$COVERAGE"
232+ elif test -n "$COVERAGE"
235 then
233 then
236 # Run tests with coverage
234 # Run tests with coverage
237 cabal update
235 cabal update
@@ -244,15 +242,16 @@ jobs:
244 # XXX Uncomment this and fix it properly later
242 # XXX Uncomment this and fix it properly later
245 # hpc-coveralls --repo-token="$COVERALLS_TOKEN" --coverage-mode=StrictlyFullLines
243 # hpc-coveralls --repo-token="$COVERALLS_TOKEN" --coverage-mode=StrictlyFullLines
246 else
244 else
245+ export TOOLS_DIR=/opt
247 # /usr/local/opt/curl/bin for macOS
246 # /usr/local/opt/curl/bin for macOS
248 export PATH=$HOME/.local/bin:$HOME/.ghcup/bin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/local/opt/curl/bin
247 export PATH=$HOME/.local/bin:$HOME/.ghcup/bin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/local/opt/curl/bin
249 if test -n "$SUBDIR"
248 if test -n "$SUBDIR"
250 then
249 then
251− mv "$PACKCHECK" "$SUBDIR/packcheck.sh"
250+ mv "$PACKCHECK_LOCAL_PATH" "$SUBDIR/packcheck.sh"
252 # This is required as dist-newstyle will be created on the top level
251 # This is required as dist-newstyle will be created on the top level
253 # directory as it is considered the working dir.
252 # directory as it is considered the working dir.
254 rm cabal.project
253 rm cabal.project
255 cd "$SUBDIR"
254 cd "$SUBDIR"
256 fi
255 fi
257− bash -c "$PACKCHECK $BUILD"
256+ bash -c "$PACKCHECK_LOCAL_PATH $BUILD"
258 fi
257 fi
.hlint.ignore−5
@@ -2,9 +2,6 @@ core/src/Streamly/Internal/Data/Stream/StreamK/Type.hs
2core/src/Streamly/Internal/Data/Pipe/Type.hs
2core/src/Streamly/Internal/Data/Pipe/Type.hs
3core/src/Streamly/Internal/Unicode/Stream.hs
3core/src/Streamly/Internal/Unicode/Stream.hs
4src/Streamly/Internal/Data/SmallArray/Type.hs
4src/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
8test/Streamly/Test/Data/Array.hs
5test/Streamly/Test/Data/Array.hs
9test/Streamly/Test/Data/Parser.hs
6test/Streamly/Test/Data/Parser.hs
10test/Streamly/Test/Data/ParserK.hs
7test/Streamly/Test/Data/ParserK.hs
@@ -13,7 +10,6 @@ test/Streamly/Test/Data/Unfold.hs
13test/Streamly/Test/FileSystem/Event.hs
10test/Streamly/Test/FileSystem/Event.hs
14test/Streamly/Test/Prelude/Concurrent.hs
11test/Streamly/Test/Prelude/Concurrent.hs
15test/Streamly/Test/Data/Stream/Concurrent.hs
12test/Streamly/Test/Data/Stream/Concurrent.hs
16−test/lib/Streamly/Test/Prelude/Common.hs
17test/Streamly/Test/Prelude/Fold.hs
13test/Streamly/Test/Prelude/Fold.hs
18test/Streamly/Test/Prelude/Rate.hs
14test/Streamly/Test/Prelude/Rate.hs
19test/Streamly/Test/Prelude/Serial.hs
15test/Streamly/Test/Prelude/Serial.hs
@@ -31,7 +27,6 @@ benchmark/Streamly/Benchmark/Data/Stream/StreamK.hs
31benchmark/Streamly/Benchmark/Data/Unfold.hs
27benchmark/Streamly/Benchmark/Data/Unfold.hs
32benchmark/Streamly/Benchmark/FileSystem/Handle.hs
28benchmark/Streamly/Benchmark/FileSystem/Handle.hs
33benchmark/Streamly/Benchmark/Prelude/Async.hs
29benchmark/Streamly/Benchmark/Prelude/Async.hs
34−benchmark/Streamly/Benchmark/Prelude/Concurrent.hs
35benchmark/Streamly/Benchmark/Prelude/Merge.hs
30benchmark/Streamly/Benchmark/Prelude/Merge.hs
36benchmark/Streamly/Benchmark/Prelude/Parallel.hs
31benchmark/Streamly/Benchmark/Prelude/Parallel.hs
37benchmark/Streamly/Benchmark/Prelude/Rate.hs
32benchmark/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)
269{-# INLINE unzipWithFstM #-}
269{-# INLINE unzipWithFstM #-}
270unzipWithFstM :: Monad m => Stream m Int -> m (Int, Int)
270unzipWithFstM :: Monad m => Stream m Int -> m (Int, Int)
271unzipWithFstM = do
271unzipWithFstM = do
272− let f a = return (a + 1, a)
272+ let f = \a -> return (a + 1, a)
273 Stream.fold (FL.unzipWithFstM f FL.sum FL.length)
273 Stream.fold (FL.unzipWithFstM f FL.sum FL.length)
274
274
275{-# INLINE unzipWithMinM #-}
275{-# INLINE unzipWithMinM #-}
276unzipWithMinM :: Monad m => Stream m Int -> m (Int, Int)
276unzipWithMinM :: Monad m => Stream m Int -> m (Int, Int)
277unzipWithMinM = do
277unzipWithMinM = do
278− let f a = return (a + 1, a)
278+ let f = \a -> return (a + 1, a)
279 Stream.fold (FL.unzipWithMinM f FL.sum FL.length)
279 Stream.fold (FL.unzipWithMinM f FL.sum FL.length)
280
280
281-------------------------------------------------------------------------------
281-------------------------------------------------------------------------------
benchmark/Streamly/Benchmark/Data/Serialize.hs+15−10
@@ -1,5 +1,4 @@
1{-# LANGUAGE TemplateHaskell #-}
1{-# LANGUAGE TemplateHaskell #-}
2−{- HLINT ignore -}
3
2
4#undef FUSION_CHECK
3#undef FUSION_CHECK
5#ifdef FUSION_CHECK
4#ifdef FUSION_CHECK
@@ -19,7 +18,6 @@ module Main (main) where
19-------------------------------------------------------------------------------
18-------------------------------------------------------------------------------
20
19
21import Control.DeepSeq (NFData(..), deepseq)
20import Control.DeepSeq (NFData(..), deepseq)
22−import Control.Monad (when, void)
23import GHC.Generics (Generic)
21import GHC.Generics (Generic)
24import System.Random (randomRIO)
22import System.Random (randomRIO)
25#ifndef USE_UNBOX
23#ifndef USE_UNBOX
@@ -266,14 +264,13 @@ $(deriveSerialize [d|instance Serialize a => Serialize (BinTree a)|])
266instance NFData a => NFData (BinTree a) where
264instance NFData a => NFData (BinTree a) where
267 {-# INLINE rnf #-}
265 {-# INLINE rnf #-}
268 rnf (Leaf a) = rnf a `seq` ()
266 rnf (Leaf a) = rnf a `seq` ()
269− rnf (Tree l r) = rnf l `seq` rnf r
267+ rnf (Tree l r) = rnf l `seq` rnf r `seq` ()
270
268
271−-- XXX This may not terminate, or could become really large.
272instance Arbitrary a => Arbitrary (BinTree a) where
269instance Arbitrary a => Arbitrary (BinTree a) where
273 arbitrary = oneof [Leaf <$> arbitrary, Tree <$> arbitrary <*> arbitrary]
270 arbitrary = oneof [Leaf <$> arbitrary, Tree <$> arbitrary <*> arbitrary]
274
271
275mkBinTree :: (Arbitrary a) => Int -> IO (BinTree a)
272mkBinTree :: (Arbitrary a) => Int -> IO (BinTree a)
276−mkBinTree = go (generate arbitrary)
273+mkBinTree = go (generate $ arbitrary)
277
274
278 where
275 where
279
276
@@ -298,14 +295,16 @@ getSize = addSizeTo 0
298-- Common helpers
295-- Common helpers
299-------------------------------------------------------------------------------
296-------------------------------------------------------------------------------
300
297
301−{- HLINT ignore "Eta reduce" -}
302-- Parts of "f" that are dependent on val will not be optimized out.
298-- Parts of "f" that are dependent on val will not be optimized out.
303{-# INLINE loop #-}
299{-# INLINE loop #-}
304loop :: Int -> (a -> IO b) -> a -> IO ()
300loop :: Int -> (a -> IO b) -> a -> IO ()
305loop count f val = go count val
301loop count f val = go count val
306 where
302 where
307
303
304+ go n x = do
305+ if n > 0
308− go n x = when (n > 0) $ f x >> go (n-1) x
306+ then f x >> go (n-1) x
307+ else return ()
309
308
310-- The first arg of "f" is the environment which is not threaded around in the
309-- The first arg of "f" is the environment which is not threaded around in the
311-- loop.
310-- loop.
@@ -314,7 +313,10 @@ loopWith :: Int -> (e -> a -> IO b) -> e -> a -> IO ()
314loopWith count f e val = go count val
313loopWith count f e val = go count val
315 where
314 where
316
315
316+ go n x = do
317+ if n > 0
317− go n x = when (n > 0) $ f e x >> go (n-1) x
318+ then f e x >> go (n-1) x
319+ else return ()
318
320
319benchSink :: NFData b => String -> Int -> (Int -> IO b) -> Benchmark
321benchSink :: NFData b => String -> Int -> (Int -> IO b) -> Benchmark
320benchSink name times f = bench name (nfIO (randomRIO (times, times) >>= f))
322benchSink name times f = bench name (nfIO (randomRIO (times, times) >>= f))
@@ -340,7 +342,7 @@ pokeTimesWithSize val times = do
340
342
341{-# INLINE poke #-}
343{-# INLINE poke #-}
342poke :: SERIALIZE_CLASS a => MutByteArray -> a -> IO ()
344poke :: SERIALIZE_CLASS a => MutByteArray -> a -> IO ()
343−poke arr val = void (SERIALIZE_OP 0 arr val)
345+poke arr val = SERIALIZE_OP 0 arr val >> return ()
344
346
345{-# INLINE pokeTimes #-}
347{-# INLINE pokeTimes #-}
346pokeTimes :: SERIALIZE_CLASS a => a -> Int -> IO ()
348pokeTimes :: SERIALIZE_CLASS a => a -> Int -> IO ()
@@ -354,7 +356,7 @@ encode :: SERIALIZE_CLASS a => a -> IO ()
354encode val = do
356encode val = do
355 let n = getSize val
357 let n = getSize val
356 arr <- MBA.new n
358 arr <- MBA.new n
357− void (SERIALIZE_OP 0 arr val)
359+ SERIALIZE_OP 0 arr val >> return ()
358
360
359{-# INLINE encodeTimes #-}
361{-# INLINE encodeTimes #-}
360encodeTimes :: SERIALIZE_CLASS a => a -> Int -> IO ()
362encodeTimes :: SERIALIZE_CLASS a => a -> Int -> IO ()
@@ -560,6 +562,8 @@ main = do
560 let !len = length lInt -- evaluate the list
562 let !len = length lInt -- evaluate the list
561#endif
563#endif
562#ifndef FUSION_CHECK
564#ifndef FUSION_CHECK
565+ -- This can take too much memory/CPU, need to restrict the test
566+ -- runQC
563#ifdef USE_UNBOX
567#ifdef USE_UNBOX
564 runWithCLIOpts defaultStreamSize allBenchmarks
568 runWithCLIOpts defaultStreamSize allBenchmarks
565#else
569#else
@@ -567,6 +571,7 @@ main = do
567 `seq` runWithCLIOpts
571 `seq` runWithCLIOpts
568 defaultStreamSize
572 defaultStreamSize
569 (allBenchmarks tInt lInt recL recR)
573 (allBenchmarks tInt lInt recL recR)
574+
570#endif
575#endif
571#else
576#else
572 -- Enable FUSION_CHECK macro at the beginning of the file
577 -- 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(..))
18genLargeRecord :: String -> Int -> Q [Dec]
18genLargeRecord :: String -> Int -> Q [Dec]
19genLargeRecord tyName numFields =
19genLargeRecord tyName numFields =
20 sequence
20 sequence
21− [ dataD
21+ ([ dataD
22 (pure [])
22 (pure [])
23 (mkName tyName)
23 (mkName tyName)
24 []
24 []
@@ -28,7 +28,7 @@ genLargeRecord tyName numFields =
28 , mkValueSigDec
28 , mkValueSigDec
29 , mkValueDec
29 , mkValueDec
30 , nfDataInstance tyName
30 , nfDataInstance tyName
31− ]
31+ ])
32
32
33 where
33 where
34
34
@@ -64,7 +64,7 @@ genLargeRecord tyName numFields =
64 (foldl
64 (foldl
65 (\b a -> [|$(b) $(a)|])
65 (\b a -> [|$(b) $(a)|])
66 (conE (mkName tyName))
66 (conE (mkName tyName))
67− (conE '() <$ [0 .. (numFields - 1)])))
67+ (const (conE '()) <$> [0 .. (numFields - 1)])))
68 []
68 []
69 ]
69 ]
70 mkCon nm = recC (mkName nm) (mkField <$> [0 .. (numFields - 1)])
70 mkCon nm = recC (mkName nm) (mkField <$> [0 .. (numFields - 1)])
benchmark/Streamly/Benchmark/Data/Stream/Eliminate.hs−2
@@ -132,14 +132,12 @@ foldableAny :: Int -> Int -> Bool
132foldableAny value n =
132foldableAny value n =
133 Prelude.any (> (value + 1)) (sourceUnfoldr value n :: Stream Identity Int)
133 Prelude.any (> (value + 1)) (sourceUnfoldr value n :: Stream Identity Int)
134
134
135−{- HLINT ignore "Use all"-}
136{-# INLINE foldableAnd #-}
135{-# INLINE foldableAnd #-}
137foldableAnd :: Int -> Int -> Bool
136foldableAnd :: Int -> Int -> Bool
138foldableAnd value n =
137foldableAnd value n =
139 Prelude.and $ fmap
138 Prelude.and $ fmap
140 (<= (value + 1)) (sourceUnfoldr value n :: Stream Identity Int)
139 (<= (value + 1)) (sourceUnfoldr value n :: Stream Identity Int)
141
140
142−{- HLINT ignore "Use any"-}
143{-# INLINE foldableOr #-}
141{-# INLINE foldableOr #-}
144foldableOr :: Int -> Int -> Bool
142foldableOr :: Int -> Int -> Bool
145foldableOr value n =
143foldableOr value n =
benchmark/Streamly/Benchmark/Data/Stream/StreamKAlt.hs+1−1
@@ -452,4 +452,4 @@ o_1_space =
452 ]
452 ]
453
453
454main :: IO ()
454main :: IO ()
455−main = defaultMain [o_1_space]
455+main = defaultMain $ concat [o_1_space]
benchmark/Streamly/Benchmark/Data/Stream/ToStreamK.hs+1−1
@@ -120,7 +120,7 @@ fromFoldable streamLen n = S.fromFoldable [n..n+streamLen]
120{-# INLINE fromFoldableM #-}
120{-# INLINE fromFoldableM #-}
121fromFoldableM :: Monad m => Int -> Int -> Stream m Int
121fromFoldableM :: Monad m => Int -> Int -> Stream m Int
122fromFoldableM streamLen n =
122fromFoldableM streamLen n =
123− Prelude.foldr (S.consM . return) S.nil [n .. n + streamLen]
123+ Prelude.foldr S.consM S.nil (Prelude.fmap return [n..n+streamLen])
124
124
125{-
125{-
126{-# INLINABLE concatMapFoldableWith #-}
126{-# INLINABLE concatMapFoldableWith #-}
benchmark/Streamly/Benchmark/FileSystem/Handle/Read.hs+8−7
@@ -33,6 +33,7 @@ import qualified Streamly.Data.Stream as Stream
33import qualified Streamly.Data.Fold as Fold
33import qualified Streamly.Data.Fold as Fold
34import qualified Streamly.FileSystem.Handle as FH
34import qualified Streamly.FileSystem.Handle as FH
35import qualified Streamly.Internal.Data.Array as A
35import qualified Streamly.Internal.Data.Array as A
36+import qualified Streamly.Internal.Data.Array as AT
36import qualified Streamly.Internal.Data.Fold as FL
37import qualified Streamly.Internal.Data.Fold as FL
37import qualified Streamly.Internal.Data.Stream as IP
38import qualified Streamly.Internal.Data.Stream as IP
38import qualified Streamly.Internal.FileSystem.Handle as IFH
39import qualified Streamly.Internal.FileSystem.Handle as IFH
@@ -47,7 +48,7 @@ import Streamly.Benchmark.Common.Handle
47#ifdef INSPECTION
48#ifdef INSPECTION
48import Streamly.Internal.Data.Stream (Step(..), FoldMany)
49import Streamly.Internal.Data.Stream (Step(..), FoldMany)
49
50
50−import qualified Streamly.Internal.Data.MutArray as MutArray
51+import qualified Streamly.Internal.Data.MutArray as MA
51import qualified Streamly.Internal.Data.Stream as D
52import qualified Streamly.Internal.Data.Stream as D
52import qualified Streamly.Internal.Data.Unfold as IUF
53import qualified Streamly.Internal.Data.Unfold as IUF
53
54
@@ -68,7 +69,7 @@ readLast = S.fold Fold.last . S.unfold FH.reader
68inspect $ hasNoTypeClasses 'readLast
69inspect $ hasNoTypeClasses 'readLast
69inspect $ 'readLast `hasNoType` ''Step -- S.unfold
70inspect $ 'readLast `hasNoType` ''Step -- S.unfold
70inspect $ 'readLast `hasNoType` ''IUF.ConcatState -- FH.read/UF.many
71inspect $ 'readLast `hasNoType` ''IUF.ConcatState -- FH.read/UF.many
71−inspect $ 'readLast `hasNoType` ''MutArray.ArrayUnsafe -- FH.read/A.read
72+inspect $ 'readLast `hasNoType` ''MA.ArrayUnsafe -- FH.read/A.read
72#endif
73#endif
73
74
74-- assert that flattenArrays constructors are not present
75-- assert that flattenArrays constructors are not present
@@ -80,7 +81,7 @@ readCountBytes = S.fold Fold.length . S.unfold FH.reader
80inspect $ hasNoTypeClasses 'readCountBytes
81inspect $ hasNoTypeClasses 'readCountBytes
81inspect $ 'readCountBytes `hasNoType` ''Step -- S.unfold
82inspect $ 'readCountBytes `hasNoType` ''Step -- S.unfold
82inspect $ 'readCountBytes `hasNoType` ''IUF.ConcatState -- FH.read/UF.many
83inspect $ 'readCountBytes `hasNoType` ''IUF.ConcatState -- FH.read/UF.many
83−inspect $ 'readCountBytes `hasNoType` ''MutArray.ArrayUnsafe -- FH.read/A.read
84+inspect $ 'readCountBytes `hasNoType` ''MA.ArrayUnsafe -- FH.read/A.read
84#endif
85#endif
85
86
86-- | Count the number of lines in a file.
87-- | Count the number of lines in a file.
@@ -95,7 +96,7 @@ readCountLines =
95inspect $ hasNoTypeClasses 'readCountLines
96inspect $ hasNoTypeClasses 'readCountLines
96inspect $ 'readCountLines `hasNoType` ''Step
97inspect $ 'readCountLines `hasNoType` ''Step
97inspect $ 'readCountLines `hasNoType` ''IUF.ConcatState -- FH.read/UF.many
98inspect $ 'readCountLines `hasNoType` ''IUF.ConcatState -- FH.read/UF.many
98−inspect $ 'readCountLines `hasNoType` ''MutArray.ArrayUnsafe -- FH.read/A.read
99+inspect $ 'readCountLines `hasNoType` ''MA.ArrayUnsafe -- FH.read/A.read
99#endif
100#endif
100
101
101-- | Count the number of words in a file.
102-- | Count the number of words in a file.
@@ -119,7 +120,7 @@ readSumBytes = S.fold Fold.sum . S.unfold FH.reader
119inspect $ hasNoTypeClasses 'readSumBytes
120inspect $ hasNoTypeClasses 'readSumBytes
120inspect $ 'readSumBytes `hasNoType` ''Step
121inspect $ 'readSumBytes `hasNoType` ''Step
121inspect $ 'readSumBytes `hasNoType` ''IUF.ConcatState -- FH.read/UF.many
122inspect $ 'readSumBytes `hasNoType` ''IUF.ConcatState -- FH.read/UF.many
122−inspect $ 'readSumBytes `hasNoType` ''MutArray.ArrayUnsafe -- FH.read/A.read
123+inspect $ 'readSumBytes `hasNoType` ''MA.ArrayUnsafe -- FH.read/A.read
123#endif
124#endif
124
125
125-- XXX When we mark this with INLINE and we have two benchmarks using S.drain
126-- XXX When we mark this with INLINE and we have two benchmarks using S.drain
@@ -235,13 +236,13 @@ groupsOf :: Int -> Handle -> IO Int
235groupsOf n inh =
236groupsOf n inh =
236 -- writeNUnsafe gives 2.5x boost here over writeN.
237 -- writeNUnsafe gives 2.5x boost here over writeN.
237 S.fold Fold.length
238 S.fold Fold.length
238− $ IP.groupsOf n (A.writeNUnsafe n) (S.unfold FH.reader inh)
239+ $ IP.groupsOf n (AT.writeNUnsafe n) (S.unfold FH.reader inh)
239
240
240#ifdef INSPECTION
241#ifdef INSPECTION
241inspect $ hasNoTypeClasses 'groupsOf
242inspect $ hasNoTypeClasses 'groupsOf
242inspect $ 'groupsOf `hasNoType` ''Step
243inspect $ 'groupsOf `hasNoType` ''Step
243inspect $ 'groupsOf `hasNoType` ''FoldMany
244inspect $ 'groupsOf `hasNoType` ''FoldMany
244−inspect $ 'groupsOf `hasNoType` ''MutArray.ArrayUnsafe -- AT.writeNUnsafe
245+inspect $ 'groupsOf `hasNoType` ''AT.ArrayUnsafe -- AT.writeNUnsafe
245 -- FH.read/A.read
246 -- FH.read/A.read
246inspect $ 'groupsOf `hasNoType` ''IUF.ConcatState -- FH.read/UF.many
247inspect $ 'groupsOf `hasNoType` ''IUF.ConcatState -- FH.read/UF.many
247#endif
248#endif
benchmark/Streamly/Benchmark/FileSystem/Handle/ReadWrite.hs+9−8
@@ -42,7 +42,8 @@ import Streamly.Internal.Data.Stream (Step(..))
42
42
43import qualified Streamly.Internal.Data.Stream as D
43import qualified Streamly.Internal.Data.Stream as D
44import qualified Streamly.Internal.Data.Tuple.Strict as Strict
44import qualified Streamly.Internal.Data.Tuple.Strict as Strict
45−import qualified Streamly.Internal.Data.MutArray as MutArray
45+import qualified Streamly.Internal.Data.MutArray.Stream as MAS
46+import qualified Streamly.Internal.Data.Array as AT
46
47
47import Test.Inspection
48import Test.Inspection
48#endif
49#endif
@@ -82,7 +83,7 @@ copyStream inh outh = S.fold (FH.write outh) (S.unfold FH.reader inh)
82inspect $ hasNoTypeClasses 'copyStream
83inspect $ hasNoTypeClasses 'copyStream
83inspect $ 'copyStream `hasNoType` ''Step -- S.unfold
84inspect $ 'copyStream `hasNoType` ''Step -- S.unfold
84inspect $ 'copyStream `hasNoType` ''IUF.ConcatState -- FH.read/UF.many
85inspect $ 'copyStream `hasNoType` ''IUF.ConcatState -- FH.read/UF.many
85−inspect $ 'copyStream `hasNoType` ''MutArray.ArrayUnsafe -- FH.write/writeNUnsafe
86+inspect $ 'copyStream `hasNoType` ''AT.ArrayUnsafe -- FH.write/writeNUnsafe
86 -- FH.read/A.read
87 -- FH.read/A.read
87inspect $ 'copyStream `hasNoType` ''Strict.Tuple3' -- FH.write/chunksOf
88inspect $ 'copyStream `hasNoType` ''Strict.Tuple3' -- FH.write/chunksOf
88#endif
89#endif
@@ -108,8 +109,8 @@ readFromBytesNull inh devNull = IFH.putBytes devNull $ S.unfold FH.reader inh
108#ifdef INSPECTION
109#ifdef INSPECTION
109inspect $ hasNoTypeClasses 'readFromBytesNull
110inspect $ hasNoTypeClasses 'readFromBytesNull
110inspect $ 'readFromBytesNull `hasNoType` ''Step
111inspect $ 'readFromBytesNull `hasNoType` ''Step
111−inspect $ 'readFromBytesNull `hasNoType` ''MutArray.SpliceState
112+inspect $ 'readFromBytesNull `hasNoType` ''MAS.SpliceState
112−inspect $ 'readFromBytesNull `hasNoType` ''MutArray.ArrayUnsafe -- FH.fromBytes/S.chunksOf
113+inspect $ 'readFromBytesNull `hasNoType` ''AT.ArrayUnsafe -- FH.fromBytes/S.chunksOf
113inspect $ 'readFromBytesNull `hasNoType` ''D.FoldMany
114inspect $ 'readFromBytesNull `hasNoType` ''D.FoldMany
114#endif
115#endif
115
116
@@ -122,8 +123,8 @@ readWithFromBytesNull inh devNull =
122#ifdef INSPECTION
123#ifdef INSPECTION
123inspect $ hasNoTypeClasses 'readWithFromBytesNull
124inspect $ hasNoTypeClasses 'readWithFromBytesNull
124inspect $ 'readWithFromBytesNull `hasNoType` ''Step
125inspect $ 'readWithFromBytesNull `hasNoType` ''Step
125−inspect $ 'readWithFromBytesNull `hasNoType` ''MutArray.SpliceState
126+inspect $ 'readWithFromBytesNull `hasNoType` ''MAS.SpliceState
126−inspect $ 'readWithFromBytesNull `hasNoType` ''MutArray.ArrayUnsafe -- FH.fromBytes/S.chunksOf
127+inspect $ 'readWithFromBytesNull `hasNoType` ''AT.ArrayUnsafe -- FH.fromBytes/S.chunksOf
127inspect $ 'readWithFromBytesNull `hasNoType` ''D.FoldMany
128inspect $ 'readWithFromBytesNull `hasNoType` ''D.FoldMany
128#endif
129#endif
129
130
@@ -170,7 +171,7 @@ writeReadWith inh devNull = IUF.fold fld unf (defaultChunkSize, inh)
170inspect $ hasNoTypeClasses 'writeReadWith
171inspect $ hasNoTypeClasses 'writeReadWith
171inspect $ 'writeReadWith `hasNoType` ''Step
172inspect $ 'writeReadWith `hasNoType` ''Step
172inspect $ 'writeReadWith `hasNoType` ''IUF.ConcatState -- FH.read/UF.many
173inspect $ 'writeReadWith `hasNoType` ''IUF.ConcatState -- FH.read/UF.many
173−inspect $ 'writeReadWith `hasNoType` ''MutArray.ArrayUnsafe -- FH.write/writeNUnsafe
174+inspect $ 'writeReadWith `hasNoType` ''AT.ArrayUnsafe -- FH.write/writeNUnsafe
174 -- FH.read/A.read
175 -- FH.read/A.read
175#endif
176#endif
176
177
@@ -187,7 +188,7 @@ writeRead inh devNull = IUF.fold fld unf inh
187inspect $ hasNoTypeClasses 'writeRead
188inspect $ hasNoTypeClasses 'writeRead
188inspect $ 'writeRead `hasNoType` ''Step
189inspect $ 'writeRead `hasNoType` ''Step
189inspect $ 'writeRead `hasNoType` ''IUF.ConcatState -- FH.read/UF.many
190inspect $ 'writeRead `hasNoType` ''IUF.ConcatState -- FH.read/UF.many
190−inspect $ 'writeRead `hasNoType` ''MutArray.ArrayUnsafe -- FH.write/writeNUnsafe
191+inspect $ 'writeRead `hasNoType` ''AT.ArrayUnsafe -- FH.write/writeNUnsafe
191 -- FH.read/A.read
192 -- FH.read/A.read
192#endif
193#endif
193
194
benchmark/Streamly/Benchmark/Unicode/Stream.hs+3−4
@@ -45,10 +45,9 @@ import Streamly.Benchmark.Common.Handle
45#ifdef INSPECTION
45#ifdef INSPECTION
46import Streamly.Internal.Data.MutByteArray (Unbox)
46import Streamly.Internal.Data.MutByteArray (Unbox)
47import Streamly.Internal.Data.Stream (Step(..))
47import Streamly.Internal.Data.Stream (Step(..))
48−import qualified Streamly.Internal.Data.Array as Array
49−import qualified Streamly.Internal.Data.MutArray as MutArray
50import qualified Streamly.Internal.Data.Fold as Fold
48import qualified Streamly.Internal.Data.Fold as Fold
51import qualified Streamly.Internal.Data.Tuple.Strict as Strict
49import qualified Streamly.Internal.Data.Tuple.Strict as Strict
50+import qualified Streamly.Internal.Data.Array as Array
52
51
53import Test.Inspection
52import Test.Inspection
54#endif
53#endif
@@ -224,7 +223,7 @@ inspect $ 'copyStreamLatin1' `hasNoType` ''Step
224inspect $ 'copyStreamLatin1' `hasNoType` ''Unfold.ConcatState -- Handle.read/UF.many
223inspect $ 'copyStreamLatin1' `hasNoType` ''Unfold.ConcatState -- Handle.read/UF.many
225
224
226inspect $ 'copyStreamLatin1' `hasNoType` ''Fold.Step
225inspect $ 'copyStreamLatin1' `hasNoType` ''Fold.Step
227−inspect $ 'copyStreamLatin1' `hasNoType` ''MutArray.ArrayUnsafe -- Handle.write/writeNUnsafe
226+inspect $ 'copyStreamLatin1' `hasNoType` ''Array.ArrayUnsafe -- Handle.write/writeNUnsafe
228 -- Handle.read/Array.read
227 -- Handle.read/Array.read
229inspect $ 'copyStreamLatin1' `hasNoType` ''Strict.Tuple3' -- Handle.write/chunksOf
228inspect $ 'copyStreamLatin1' `hasNoType` ''Strict.Tuple3' -- Handle.write/chunksOf
230#endif
229#endif
@@ -245,7 +244,7 @@ inspect $ 'copyStreamLatin1 `hasNoType` ''Unfold.ConcatState -- Handle.read/UF.m
245
244
246inspect $ 'copyStreamLatin1 `hasNoType` ''Fold.ManyState
245inspect $ 'copyStreamLatin1 `hasNoType` ''Fold.ManyState
247inspect $ 'copyStreamLatin1 `hasNoType` ''Fold.Step
246inspect $ 'copyStreamLatin1 `hasNoType` ''Fold.Step
248−inspect $ 'copyStreamLatin1 `hasNoType` ''MutArray.ArrayUnsafe -- Handle.write/writeNUnsafe
247+inspect $ 'copyStreamLatin1 `hasNoType` ''Array.ArrayUnsafe -- Handle.write/writeNUnsafe
249 -- Handle.read/Array.read
248 -- Handle.read/Array.read
250inspect $ 'copyStreamLatin1 `hasNoType` ''Strict.Tuple3' -- Handle.write/chunksOf
249inspect $ 'copyStreamLatin1 `hasNoType` ''Strict.Tuple3' -- Handle.write/chunksOf
251#endif
250#endif
benchmark/streamly-benchmarks.cabal+1−1
@@ -188,7 +188,7 @@ common bench-depends
188 , unordered-containers >= 0.2 && < 0.3
188 , unordered-containers >= 0.2 && < 0.3
189 , process >= 1.4 && < 1.7
189 , process >= 1.4 && < 1.7
190 , directory >= 1.2.2 && < 1.4
190 , directory >= 1.2.2 && < 1.4
191− , filepath >= 1.4.1 && < 1.6
191+ , filepath >= 1.4.1 && < 1.5
192 , ghc-prim >= 0.4 && < 0.12
192 , ghc-prim >= 0.4 && < 0.12
193 , tasty-bench >= 0.3 && < 0.4
193 , tasty-bench >= 0.3 && < 0.4
194 , tasty >= 1.4.1 && < 1.6
194 , tasty >= 1.4.1 && < 1.6
configure+9−9
@@ -1,6 +1,6 @@
1#! /bin/sh
1#! /bin/sh
2# Guess values for system-dependent variables and create Makefiles.
2# Guess values for system-dependent variables and create Makefiles.
3−# Generated by GNU Autoconf 2.71 for streamly 0.10.1.
3+# Generated by GNU Autoconf 2.71 for streamly 0.10.0.
4#
4#
5# Report bugs to <streamly@composewell.com>.
5# Report bugs to <streamly@composewell.com>.
6#
6#
@@ -610,8 +610,8 @@ MAKEFLAGS=
610# Identity of this package.
610# Identity of this package.
611PACKAGE_NAME='streamly'
611PACKAGE_NAME='streamly'
612PACKAGE_TARNAME='streamly'
612PACKAGE_TARNAME='streamly'
613−PACKAGE_VERSION='0.10.1'
613+PACKAGE_VERSION='0.10.0'
614−PACKAGE_STRING='streamly 0.10.1'
614+PACKAGE_STRING='streamly 0.10.0'
615PACKAGE_BUGREPORT='streamly@composewell.com'
615PACKAGE_BUGREPORT='streamly@composewell.com'
616PACKAGE_URL='https://streamly.composewell.com'
616PACKAGE_URL='https://streamly.composewell.com'
617
617
@@ -1224,7 +1224,7 @@ if test "$ac_init_help" = "long"; then
1224 # Omit some internal or obsolete options to make the list less imposing.
1224 # Omit some internal or obsolete options to make the list less imposing.
1225 # This message is too long to be a string in the A/UX 3.1 sh.
1225 # This message is too long to be a string in the A/UX 3.1 sh.
1226 cat <<_ACEOF
1226 cat <<_ACEOF
1227−\`configure' configures streamly 0.10.1 to adapt to many kinds of systems.
1227+\`configure' configures streamly 0.10.0 to adapt to many kinds of systems.
1228
1228
1229Usage: $0 [OPTION]... [VAR=VALUE]...
1229Usage: $0 [OPTION]... [VAR=VALUE]...
1230
1230
@@ -1286,7 +1286,7 @@ fi
1286
1286
1287if test -n "$ac_init_help"; then
1287if test -n "$ac_init_help"; then
1288 case $ac_init_help in
1288 case $ac_init_help in
1289− short | recursive ) echo "Configuration of streamly 0.10.1:";;
1289+ short | recursive ) echo "Configuration of streamly 0.10.0:";;
1290 esac
1290 esac
1291 cat <<\_ACEOF
1291 cat <<\_ACEOF
1292
1292
@@ -1372,7 +1372,7 @@ fi
1372test -n "$ac_init_help" && exit $ac_status
1372test -n "$ac_init_help" && exit $ac_status
1373if $ac_init_version; then
1373if $ac_init_version; then
1374 cat <<\_ACEOF
1374 cat <<\_ACEOF
1375−streamly configure 0.10.1
1375+streamly configure 0.10.0
1376generated by GNU Autoconf 2.71
1376generated by GNU Autoconf 2.71
1377
1377
1378Copyright (C) 2021 Free Software Foundation, Inc.
1378Copyright (C) 2021 Free Software Foundation, Inc.
@@ -1500,7 +1500,7 @@ cat >config.log <<_ACEOF
1500This file contains any messages produced by compilers while
1500This file contains any messages produced by compilers while
1501running configure, to aid debugging if configure makes a mistake.
1501running configure, to aid debugging if configure makes a mistake.
1502
1502
1503−It was created by streamly $as_me 0.10.1, which was
1503+It was created by streamly $as_me 0.10.0, which was
1504generated by GNU Autoconf 2.71. Invocation command line was
1504generated by GNU Autoconf 2.71. Invocation command line was
1505
1505
1506 $ $0$ac_configure_args_raw
1506 $ $0$ac_configure_args_raw
@@ -3800,7 +3800,7 @@ cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1
3800# report actual input values of CONFIG_FILES etc. instead of their
3800# report actual input values of CONFIG_FILES etc. instead of their
3801# values after options handling.
3801# values after options handling.
3802ac_log="
3802ac_log="
3803−This file was extended by streamly $as_me 0.10.1, which was
3803+This file was extended by streamly $as_me 0.10.0, which was
3804generated by GNU Autoconf 2.71. Invocation command line was
3804generated by GNU Autoconf 2.71. Invocation command line was
3805
3805
3806 CONFIG_FILES = $CONFIG_FILES
3806 CONFIG_FILES = $CONFIG_FILES
@@ -3856,7 +3856,7 @@ ac_cs_config_escaped=`printf "%s\n" "$ac_cs_config" | sed "s/^ //; s/'/'\\\\\\\\
3856cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1
3856cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1
3857ac_cs_config='$ac_cs_config_escaped'
3857ac_cs_config='$ac_cs_config_escaped'
3858ac_cs_version="\\
3858ac_cs_version="\\
3859−streamly config.status 0.10.1
3859+streamly config.status 0.10.0
3860configured by $0, generated by GNU Autoconf 2.71,
3860configured by $0, generated by GNU Autoconf 2.71,
3861 with options \\"\$ac_cs_config\\"
3861 with options \\"\$ac_cs_config\\"
3862
3862
configure.ac+1−1
@@ -3,7 +3,7 @@
3# See https://www.gnu.org/software/autoconf/manual/autoconf.html for help on
3# See https://www.gnu.org/software/autoconf/manual/autoconf.html for help on
4# the macros used in this file.
4# the macros used in this file.
5
5
6−AC_INIT([streamly], [0.10.1], [streamly@composewell.com], [streamly], [https://streamly.composewell.com])
6+AC_INIT([streamly], [0.10.0], [streamly@composewell.com], [streamly], [https://streamly.composewell.com])
7
7
8# To suppress "WARNING: unrecognized options: --with-compiler"
8# To suppress "WARNING: unrecognized options: --with-compiler"
9AC_ARG_WITH([compiler], [GHC])
9AC_ARG_WITH([compiler], [GHC])
core/configure+9−9
@@ -1,6 +1,6 @@
1#! /bin/sh
1#! /bin/sh
2# Guess values for system-dependent variables and create Makefiles.
2# Guess values for system-dependent variables and create Makefiles.
3−# Generated by GNU Autoconf 2.71 for streamly-core 0.2.2.
3+# Generated by GNU Autoconf 2.71 for streamly-core 0.2.0.
4#
4#
5# Report bugs to <streamly@composewell.com>.
5# Report bugs to <streamly@composewell.com>.
6#
6#
@@ -610,8 +610,8 @@ MAKEFLAGS=
610# Identity of this package.
610# Identity of this package.
611PACKAGE_NAME='streamly-core'
611PACKAGE_NAME='streamly-core'
612PACKAGE_TARNAME='streamly-core'
612PACKAGE_TARNAME='streamly-core'
613−PACKAGE_VERSION='0.2.2'
613+PACKAGE_VERSION='0.2.0'
614−PACKAGE_STRING='streamly-core 0.2.2'
614+PACKAGE_STRING='streamly-core 0.2.0'
615PACKAGE_BUGREPORT='streamly@composewell.com'
615PACKAGE_BUGREPORT='streamly@composewell.com'
616PACKAGE_URL='https://streamly.composewell.com'
616PACKAGE_URL='https://streamly.composewell.com'
617
617
@@ -1256,7 +1256,7 @@ if test "$ac_init_help" = "long"; then
1256 # Omit some internal or obsolete options to make the list less imposing.
1256 # Omit some internal or obsolete options to make the list less imposing.
1257 # This message is too long to be a string in the A/UX 3.1 sh.
1257 # This message is too long to be a string in the A/UX 3.1 sh.
1258 cat <<_ACEOF
1258 cat <<_ACEOF
1259−\`configure' configures streamly-core 0.2.2 to adapt to many kinds of systems.
1259+\`configure' configures streamly-core 0.2.0 to adapt to many kinds of systems.
1260
1260
1261Usage: $0 [OPTION]... [VAR=VALUE]...
1261Usage: $0 [OPTION]... [VAR=VALUE]...
1262
1262
@@ -1318,7 +1318,7 @@ fi
1318
1318
1319if test -n "$ac_init_help"; then
1319if test -n "$ac_init_help"; then
1320 case $ac_init_help in
1320 case $ac_init_help in
1321− short | recursive ) echo "Configuration of streamly-core 0.2.2:";;
1321+ short | recursive ) echo "Configuration of streamly-core 0.2.0:";;
1322 esac
1322 esac
1323 cat <<\_ACEOF
1323 cat <<\_ACEOF
1324
1324
@@ -1404,7 +1404,7 @@ fi
1404test -n "$ac_init_help" && exit $ac_status
1404test -n "$ac_init_help" && exit $ac_status
1405if $ac_init_version; then
1405if $ac_init_version; then
1406 cat <<\_ACEOF
1406 cat <<\_ACEOF
1407−streamly-core configure 0.2.2
1407+streamly-core configure 0.2.0
1408generated by GNU Autoconf 2.71
1408generated by GNU Autoconf 2.71
1409
1409
1410Copyright (C) 2021 Free Software Foundation, Inc.
1410Copyright (C) 2021 Free Software Foundation, Inc.
@@ -1622,7 +1622,7 @@ cat >config.log <<_ACEOF
1622This file contains any messages produced by compilers while
1622This file contains any messages produced by compilers while
1623running configure, to aid debugging if configure makes a mistake.
1623running configure, to aid debugging if configure makes a mistake.
1624
1624
1625−It was created by streamly-core $as_me 0.2.2, which was
1625+It was created by streamly-core $as_me 0.2.0, which was
1626generated by GNU Autoconf 2.71. Invocation command line was
1626generated by GNU Autoconf 2.71. Invocation command line was
1627
1627
1628 $ $0$ac_configure_args_raw
1628 $ $0$ac_configure_args_raw
@@ -3832,7 +3832,7 @@ cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1
3832# report actual input values of CONFIG_FILES etc. instead of their
3832# report actual input values of CONFIG_FILES etc. instead of their
3833# values after options handling.
3833# values after options handling.
3834ac_log="
3834ac_log="
3835−This file was extended by streamly-core $as_me 0.2.2, which was
3835+This file was extended by streamly-core $as_me 0.2.0, which was
3836generated by GNU Autoconf 2.71. Invocation command line was
3836generated by GNU Autoconf 2.71. Invocation command line was
3837
3837
3838 CONFIG_FILES = $CONFIG_FILES
3838 CONFIG_FILES = $CONFIG_FILES
@@ -3888,7 +3888,7 @@ ac_cs_config_escaped=`printf "%s\n" "$ac_cs_config" | sed "s/^ //; s/'/'\\\\\\\\
3888cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1
3888cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1
3889ac_cs_config='$ac_cs_config_escaped'
3889ac_cs_config='$ac_cs_config_escaped'
3890ac_cs_version="\\
3890ac_cs_version="\\
3891−streamly-core config.status 0.2.2
3891+streamly-core config.status 0.2.0
3892configured by $0, generated by GNU Autoconf 2.71,
3892configured by $0, generated by GNU Autoconf 2.71,
3893 with options \\"\$ac_cs_config\\"
3893 with options \\"\$ac_cs_config\\"
3894
3894
core/configure.ac+1−1
@@ -3,7 +3,7 @@
3# See https://www.gnu.org/software/autoconf/manual/autoconf.html for help on
3# See https://www.gnu.org/software/autoconf/manual/autoconf.html for help on
4# the macros used in this file.
4# the macros used in this file.
5
5
6−AC_INIT([streamly-core], [0.2.2], [streamly@composewell.com], [streamly-core], [https://streamly.composewell.com])
6+AC_INIT([streamly-core], [0.2.0], [streamly@composewell.com], [streamly-core], [https://streamly.composewell.com])
7
7
8# To suppress "WARNING: unrecognized options: --with-compiler"
8# To suppress "WARNING: unrecognized options: --with-compiler"
9AC_ARG_WITH([compiler], [GHC])
9AC_ARG_WITH([compiler], [GHC])
core/docs/ApiChangelogs/0.2.0-0.2.2.txtdeleted−306
@@ -1,306 +0,0 @@
1−---------------------------------
2−API Annotations
3−---------------------------------
4−
5−[A] : Added
6−[R] : Removed
7−[C] : Changed
8−[O] : Old definition
9−[N] : New definition
10−[D] : Deprecated
11−
12−---------------------------------
13−API diff
14−---------------------------------
15−
16−[C] Streamly.Data.Stream
17− [A] (FixityR,5)
18− [A] (FixityR,5)
19−[C] Streamly.Data.MutArray.Generic
20− [A] emptyOf :: MonadIO m => Int -> m (MutArray a)
21− [A] createOf :: MonadIO m => Int -> Fold m a (MutArray a)
22− [A] create :: MonadIO m => Fold m a (MutArray a)
23−[C] Streamly.Data.MutArray
24− [A] pinnedEmptyOf :: forall m a. (MonadIO m, Unbox a) => Int -> m (MutArray a)
25− [A] emptyOf :: (MonadIO m, Unbox a) => Int -> m (MutArray a)
26− [A] createOf :: forall m a. (MonadIO m, Unbox a) => Int -> Fold m a (MutArray a)
27− [A] create :: forall m a. (MonadIO m, Unbox a) => Fold m a (MutArray a)
28− [A] appendN :: forall m a. (MonadIO m, Unbox a) => Int -> m (MutArray a) -> Fold m a (MutArray a)
29− [A] append :: forall m a. (MonadIO m, Unbox a) => m (MutArray a) -> Fold m a (MutArray a)
30−[C] Streamly.Data.Array.Generic
31− [A] createOf :: MonadIO m => Int -> Fold m a (Array a)
32− [A] create :: MonadIO m => Fold m a (Array a)
33−[C] Streamly.Data.Array
34− [A] class Serialize a
35− [A] serializeAt :: Serialize a => Int -> MutByteArray -> a -> IO Int
36− [A] pinnedSerialize :: Serialize a => a -> Array Word8
37− [A] deserializeAt :: Serialize a => Int -> MutByteArray -> Int -> IO (Int, a)
38− [A] deserialize :: Serialize a => Array Word8 -> a
39− [A] createOf :: forall m a. (MonadIO m, Unbox a) => Int -> Fold m a (Array a)
40− [A] create :: forall m a. (MonadIO m, Unbox a) => Fold m a (Array a)
41− [A] addSizeTo :: Serialize a => Int -> a -> Int
42−
43−---------------------------------
44−Internal API diff
45−---------------------------------
46−
47−[C] Streamly.Internal.Unicode.Stream
48− [A] encodeUtf16le' :: Stream m Char -> Stream m Word16
49− [A] decodeUtf16le' :: Stream m Word16 -> Stream m Char
50−[A] Streamly.Internal.FileSystem.Path
51− [A] class IsPath a
52− [A] Rel
53− [A] File
54− [A] Dir
55− [A] Abs
56− [A] Streamly.Internal.FileSystem.Path.IsPath
57− [A] instance Streamly.Internal.FileSystem.Path.IsPath Streamly.Internal.FileSystem.Path.Path
58− [A] instance Streamly.Internal.FileSystem.Path.IsPath (Streamly.Internal.FileSystem.Path.Rel Streamly.Internal.FileSystem.Path.Path)
59− [A] instance Streamly.Internal.FileSystem.Path.IsPath (Streamly.Internal.FileSystem.Path.Rel (Streamly.Internal.FileSystem.Path.File Streamly.Internal.FileSystem.Path.Path))
60− [A] instance Streamly.Internal.FileSystem.Path.IsPath (Streamly.Internal.FileSystem.Path.Rel (Streamly.Internal.FileSystem.Path.Dir Streamly.Internal.FileSystem.Path.Path))
61− [A] instance Streamly.Internal.FileSystem.Path.IsPath (Streamly.Internal.FileSystem.Path.File Streamly.Internal.FileSystem.Path.Path)
62− [A] instance Streamly.Internal.FileSystem.Path.IsPath (Streamly.Internal.FileSystem.Path.Dir Streamly.Internal.FileSystem.Path.Path)
63− [A] instance Streamly.Internal.FileSystem.Path.IsPath (Streamly.Internal.FileSystem.Path.Abs Streamly.Internal.FileSystem.Path.Path)
64− [A] instance Streamly.Internal.FileSystem.Path.IsPath (Streamly.Internal.FileSystem.Path.Abs (Streamly.Internal.FileSystem.Path.File Streamly.Internal.FileSystem.Path.Path))
65− [A] instance Streamly.Internal.FileSystem.Path.IsPath (Streamly.Internal.FileSystem.Path.Abs (Streamly.Internal.FileSystem.Path.Dir Streamly.Internal.FileSystem.Path.Path))
66− [A] GHC.Show.Show
67− [A] instance GHC.Show.Show Streamly.Internal.FileSystem.Path.PathException
68− [A] GHC.Exception.Type.Exception
69− [A] instance GHC.Exception.Type.Exception Streamly.Internal.FileSystem.Path.PathException
70− [A] GHC.Classes.Eq
71− [A] instance GHC.Classes.Eq Streamly.Internal.FileSystem.Path.PathException
72− [A] Path
73− [A] Path :: Array Word8 -> Path
74− [A] toString :: Path -> [Char]
75− [A] toPath :: IsPath a => a -> Path
76− [A] toChunk :: Path -> Array Word8
77− [A] toChars :: Monad m => Path -> Stream m Char
78− [A] relfile :: QuasiQuoter
79− [A] reldir :: QuasiQuoter
80− [A] rel :: QuasiQuoter
81− [A] primarySeparator :: Char
82− [A] path :: QuasiQuoter
83− [A] mkRelFile :: String -> Q Exp
84− [A] mkRelDir :: String -> Q Exp
85− [A] mkRel :: String -> Q Exp
86− [A] mkPath :: String -> Q Exp
87− [A] mkFile :: String -> Q Exp
88− [A] mkDir :: String -> Q Exp
89− [A] mkAbsFile :: String -> Q Exp
90− [A] mkAbsDir :: String -> Q Exp
91− [A] mkAbs :: String -> Q Exp
92− [A] isSeparator :: Char -> Bool
93− [A] fromString :: MonadThrow m => [Char] -> m Path
94− [A] fromPathUnsafe :: IsPath a => Path -> a
95− [A] fromPath :: (IsPath a, MonadThrow m) => Path -> m a
96− [A] fromChunkUnsafe :: Array Word8 -> Path
97− [A] fromChunk :: MonadThrow m => Array Word8 -> m Path
98− [A] fromChars :: MonadThrow m => Stream Identity Char -> m Path
99− [A] file :: QuasiQuoter
100− [A] extendPath :: Path -> Path -> Path
101− [A] extendDir :: (IsPath (a (Dir Path)), IsPath b, IsPath (a b)) => a (Dir Path) -> Rel b -> a b
102− [A] dir :: QuasiQuoter
103− [A] adaptPath :: (MonadThrow m, IsPath a, IsPath b) => a -> m b
104− [A] absfile :: QuasiQuoter
105− [A] absdir :: QuasiQuoter
106− [A] abs :: QuasiQuoter
107−[C] Streamly.Internal.Data.Stream
108− [A] (FixityR,5)
109− [A] (FixityR,5)
110− [C] splitInnerBySuffix
111− [O] splitInnerBySuffix :: (Monad m, Eq (f a), Monoid (f a)) => (f a -> m (f a, Maybe (f a))) -> (f a -> f a -> m (f a)) -> Stream m (f a) -> Stream m (f a)
112− [N] splitInnerBySuffix :: Monad m => (f a -> Bool) -> (f a -> m (f a, Maybe (f a))) -> (f a -> f a -> m (f a)) -> Stream m (f a) -> Stream m (f a)
113− [D] sliceOnSuffix :: Monad m => (a -> Bool) -> Stream m a -> Stream m (Int, Int)
114− [A] indexOnSuffix :: Monad m => (a -> Bool) -> Stream m a -> Stream m (Int, Int)
115−[C] Streamly.Internal.Data.MutByteArray
116− [A] unsafePinnedAsPtr :: MonadIO m => MutByteArray -> (Ptr a -> m b) -> m b
117− [A] unsafeAsPtr :: MonadIO m => MutByteArray -> (Ptr a -> m b) -> m b
118− [D] nil :: MutByteArray
119− [A] empty :: MutByteArray
120− [D] asPtrUnsafe :: MonadIO m => MutByteArray -> (Ptr a -> m b) -> m b
121−[D] Streamly.Internal.Data.MutArray.Stream
122− [D] writeChunks :: (MonadIO m, Unbox a) => Int -> Fold m a (StreamK n (MutArray a))
123− [D] fromArrayStreamK :: (Unbox a, MonadIO m) => StreamK m (MutArray a) -> m (MutArray a)
124− [D] flattenArraysRev :: forall m a. (MonadIO m, Unbox a) => Stream m (MutArray a) -> Stream m a
125− [D] flattenArrays :: forall m a. (MonadIO m, Unbox a) => Stream m (MutArray a) -> Stream m a
126−[C] Streamly.Internal.Data.MutArray.Generic
127− [D] writeWith :: MonadIO m => Int -> Fold m a (MutArray a)
128− [D] writeNUnsafe :: MonadIO m => Int -> Fold m a (MutArray a)
129− [A] unsafeCreateOf :: MonadIO m => Int -> Fold m a (MutArray a)
130− [A] emptyOf :: MonadIO m => Int -> m (MutArray a)
131− [A] createWith :: MonadIO m => Int -> Fold m a (MutArray a)
132− [A] createOf :: MonadIO m => Int -> Fold m a (MutArray a)
133− [A] create :: MonadIO m => Fold m a (MutArray a)
134−[C] Streamly.Internal.Data.MutArray
135− [A] SpliceState
136− [A] SpliceYielding :: arr -> SpliceState s arr -> SpliceState s arr
137− [A] SpliceInitial :: s -> SpliceState s arr
138− [A] SpliceFinish :: SpliceState s arr
139− [A] SpliceBuffering :: s -> arr -> SpliceState s arr
140− [R] MutByteArray
141− [D] writeWith :: forall m a. (MonadIO m, Unbox a) => Int -> Fold m a (MutArray a)
142− [D] writeRevN :: forall m a. (MonadIO m, Unbox a) => Int -> Fold m a (MutArray a)
143− [D] writeNWithUnsafe :: forall m a. (MonadIO m, Unbox a) => (Int -> m (MutArray a)) -> Int -> Fold m a (MutArray a)
144− [D] writeNUnsafe :: forall m a. (MonadIO m, Unbox a) => Int -> Fold m a (MutArray a)
145− [D] writeChunks :: (MonadIO m, Unbox a) => Int -> Fold m a (StreamK n (MutArray a))
146− [D] writeAppendWith :: forall m a. (MonadIO m, Unbox a) => (Int -> Int) -> m (MutArray a) -> Fold m a (MutArray a)
147− [D] writeAppendNUnsafe :: forall m a. (MonadIO m, Unbox a) => Int -> m (MutArray a) -> Fold m a (MutArray a)
148− [A] unsafePinnedCreateOf :: forall m a. (MonadIO m, Unbox a) => Int -> Fold m a (MutArray a)
149− [A] unsafePinnedAsPtr :: MonadIO m => MutArray a -> (Ptr a -> m b) -> m b
150− [A] unsafeCreateOfWith :: forall m a. (MonadIO m, Unbox a) => (Int -> m (MutArray a)) -> Int -> Fold m a (MutArray a)
151− [A] unsafeCreateOf :: forall m a. (MonadIO m, Unbox a) => Int -> Fold m a (MutArray a)
152− [A] unsafeAsPtr :: MonadIO m => MutArray a -> (Ptr a -> m b) -> m b
153− [A] unsafeAppendN :: forall m a. (MonadIO m, Unbox a) => Int -> m (MutArray a) -> Fold m a (MutArray a)
154− [A] toStreamWith :: forall m a. (Monad m, Unbox a) => (forall b. IO b -> m b) -> MutArray a -> Stream m a
155− [A] toStreamRevWith :: forall m a. (Monad m, Unbox a) => (forall b. IO b -> m b) -> MutArray a -> Stream m a
156− [R] toStreamDWith :: forall m a. (Monad m, Unbox a) => (forall b. IO b -> m b) -> MutArray a -> Stream m a
157− [R] toStreamDRevWith :: forall m a. (Monad m, Unbox a) => (forall b. IO b -> m b) -> MutArray a -> Stream m a
158− [A] slicerFromLen :: forall m a. (Monad m, Unbox a) => Int -> Int -> Unfold m (MutArray a) (MutArray a)
159− [A] sliceIndexerFromLen :: forall m a. (Monad m, Unbox a) => Int -> Int -> Unfold m (MutArray a) (Int, Int)
160− [A] revCreateOf :: forall m a. (MonadIO m, Unbox a) => Int -> Fold m a (MutArray a)
161− [D] resizeExp :: forall m a. (MonadIO m, Unbox a) => Int -> MutArray a -> m (MutArray a)
162− [D] resize :: forall m a. (MonadIO m, Unbox a) => Int -> MutArray a -> m (MutArray a)
163− [A] pokeSkipUnsafe :: Int -> MutArray Word8 -> MutArray Word8
164− [A] pokeAppendMay :: forall m a. (MonadIO m, Unbox a) => MutArray Word8 -> a -> m (Maybe (MutArray Word8))
165− [A] pokeAppend :: forall m a. (MonadIO m, Unbox a) => MutArray Word8 -> a -> m (MutArray Word8)
166− [D] pinnedWriteNUnsafe :: forall m a. (MonadIO m, Unbox a) => Int -> Fold m a (MutArray a)
167− [D] pinnedWriteN :: forall m a. (MonadIO m, Unbox a) => Int -> Fold m a (MutArray a)
168− [D] pinnedWrite :: forall m a. (MonadIO m, Unbox a) => Fold m a (MutArray a)
169− [D] pinnedNewBytes :: MonadIO m => Int -> m (MutArray a)
170− [A] pinnedEmptyOf :: forall m a. (MonadIO m, Unbox a) => Int -> m (MutArray a)
171− [A] pinnedCreateOf :: forall m a. (MonadIO m, Unbox a) => Int -> Fold m a (MutArray a)
172− [A] pinnedCreate :: forall m a. (MonadIO m, Unbox a) => Fold m a (MutArray a)
173− [A] pinnedCompactLE :: forall m a. (MonadIO m, Unbox a) => Int -> Stream m (MutArray a) -> Stream m (MutArray a)
174− [A] peekUnconsUnsafe :: forall m a. (MonadIO m, Unbox a) => MutArray Word8 -> m (a, MutArray Word8)
175− [A] peekUncons :: forall m a. (MonadIO m, Unbox a) => MutArray Word8 -> m (Maybe a, MutArray Word8)
176− [A] peekSkipUnsafe :: Int -> MutArray Word8 -> MutArray Word8
177− [A] pPinnedCompactLE :: forall m a. (MonadIO m, Unbox a) => Int -> Parser (MutArray a) m (MutArray a)
178− [A] pCompactLE :: forall m a. (MonadIO m, Unbox a) => Int -> Parser (MutArray a) m (MutArray a)
179− [D] nil :: MutArray a
180− [A] lPinnedCompactGE :: forall m a. (MonadIO m, Unbox a) => Int -> Fold m (MutArray a) () -> Fold m (MutArray a) ()
181− [A] lCompactGE :: forall m a. (MonadIO m, Unbox a) => Int -> Fold m (MutArray a) () -> Fold m (MutArray a) ()
182− [A] indexReaderWith :: (Monad m, Unbox a) => (forall b. IO b -> m b) -> Stream m Int -> Unfold m (MutArray a) a
183− [A] indexReader :: (MonadIO m, Unbox a) => Stream m Int -> Unfold m (MutArray a) a
184− [A] growExp :: forall m a. (MonadIO m, Unbox a) => Int -> MutArray a -> m (MutArray a)
185− [A] grow :: forall m a. (MonadIO m, Unbox a) => Int -> MutArray a -> m (MutArray a)
186− [D] getSlicesFromLen :: forall m a. (Monad m, Unbox a) => Int -> Int -> Unfold m (MutArray a) (MutArray a)
187− [R] getIndicesD :: (Monad m, Unbox a) => (forall b. IO b -> m b) -> Stream m Int -> Unfold m (MutArray a) a
188− [D] getIndices :: (MonadIO m, Unbox a) => Stream m Int -> Unfold m (MutArray a) a
189− [D] genSlicesFromLen :: forall m a. (Monad m, Unbox a) => Int -> Int -> Unfold m (MutArray a) (Int, Int)
190− [A] fromStreamN :: forall m a. (MonadIO m, Unbox a) => Int -> Stream m a -> m (MutArray a)
191− [D] fromStreamDN :: forall m a. (MonadIO m, Unbox a) => Int -> Stream m a -> m (MutArray a)
192− [D] fromStreamD :: (MonadIO m, Unbox a) => Stream m a -> m (MutArray a)
193− [A] fromPureStreamN :: (MonadIO m, Unbox a) => Int -> Stream Identity a -> m (MutArray a)
194− [A] fromPtrN :: MonadIO m => Int -> Ptr Word8 -> m (MutArray Word8)
195− [A] fromChunksRealloced :: forall m a. (MonadIO m, Unbox a) => Stream m (MutArray a) -> m (MutArray a)
196− [A] fromChunksK :: (Unbox a, MonadIO m) => StreamK m (MutArray a) -> m (MutArray a)
197− [A] fromByteStr# :: MonadIO m => Addr# -> m (MutArray Word8)
198− [D] fromArrayStreamK :: (Unbox a, MonadIO m) => StreamK m (MutArray a) -> m (MutArray a)
199− [D] flattenArraysRev :: forall m a. (MonadIO m, Unbox a) => Stream m (MutArray a) -> Stream m a
200− [D] flattenArrays :: forall m a. (MonadIO m, Unbox a) => Stream m (MutArray a) -> Stream m a
201− [A] fPinnedCompactGE :: forall m a. (MonadIO m, Unbox a) => Int -> Fold m (MutArray a) (MutArray a)
202− [A] fCompactGE :: forall m a. (MonadIO m, Unbox a) => Int -> Fold m (MutArray a) (MutArray a)
203− [A] emptyOf :: (MonadIO m, Unbox a) => Int -> m (MutArray a)
204− [A] empty :: MutArray a
205− [A] createWith :: forall m a. (MonadIO m, Unbox a) => Int -> Fold m a (MutArray a)
206− [A] createOfWith :: forall m a. (MonadIO m, Unbox a) => (Int -> m (MutArray a)) -> Int -> Fold m a (MutArray a)
207− [A] createOf :: forall m a. (MonadIO m, Unbox a) => Int -> Fold m a (MutArray a)
208− [A] create :: forall m a. (MonadIO m, Unbox a) => Fold m a (MutArray a)
209− [A] concatWith :: forall m a. (Monad m, Unbox a) => (forall b. IO b -> m b) -> Stream m (MutArray a) -> Stream m a
210− [A] concatRevWith :: forall m a. (Monad m, Unbox a) => (forall b. IO b -> m b) -> Stream m (MutArray a) -> Stream m a
211− [A] concatRev :: forall m a. (MonadIO m, Unbox a) => Stream m (MutArray a) -> Stream m a
212− [A] concat :: forall m a. (MonadIO m, Unbox a) => Stream m (MutArray a) -> Stream m a
213− [A] compactOnByteSuffix :: MonadIO m => Word8 -> Stream m (MutArray Word8) -> Stream m (MutArray Word8)
214− [A] compactOnByte :: MonadIO m => Word8 -> Stream m (MutArray Word8) -> Stream m (MutArray Word8)
215− [A] compactLeAs :: forall m a. (MonadIO m, Unbox a) => PinnedState -> Int -> Stream m (MutArray a) -> Stream m (MutArray a)
216− [A] compactLE :: (MonadIO m, Unbox a) => Int -> Stream m (MutArray a) -> Stream m (MutArray a)
217− [A] compactGE :: (MonadIO m, Unbox a) => Int -> Stream m (MutArray a) -> Stream m (MutArray a)
218− [A] compactEQ :: Int -> Stream m (MutArray a) -> Stream m (MutArray a)
219− [D] cmp :: MonadIO m => MutArray a -> MutArray a -> m Ordering
220− [A] byteEq :: MonadIO m => MutArray a -> MutArray a -> m Bool
221− [A] byteCmp :: MonadIO m => MutArray a -> MutArray a -> m Ordering
222− [A] buildChunks :: (MonadIO m, Unbox a) => Int -> Fold m a (StreamK n (MutArray a))
223− [D] asPtrUnsafe :: MonadIO m => MutArray a -> (Ptr a -> m b) -> m b
224− [A] appendWith :: forall m a. (MonadIO m, Unbox a) => (Int -> Int) -> m (MutArray a) -> Fold m a (MutArray a)
225− [A] appendN :: forall m a. (MonadIO m, Unbox a) => Int -> m (MutArray a) -> Fold m a (MutArray a)
226− [A] append :: forall m a. (MonadIO m, Unbox a) => m (MutArray a) -> Fold m a (MutArray a)
227−[D] Streamly.Internal.Data.Array.Stream
228− [C] interposeSuffix
229− [O] interposeSuffix :: (Monad m, Unbox a) => a -> Stream m (Array a) -> Stream m a
230− [N] interposeSuffix :: forall m a. (Monad m, Unbox a) => a -> Stream m (Array a) -> Stream m a
231− [D] flattenArraysRev :: forall m a. (MonadIO m, Unbox a) => Stream m (Array a) -> Stream m a
232− [D] flattenArrays :: forall m a. (MonadIO m, Unbox a) => Stream m (Array a) -> Stream m a
233− [C] concatRev
234− [O] concatRev :: (Monad m, Unbox a) => Stream m (Array a) -> Stream m a
235− [N] concatRev :: forall m a. (Monad m, Unbox a) => Stream m (Array a) -> Stream m a
236− [D] bufferChunks :: (MonadIO m, Unbox a) => Stream m a -> m (StreamK m (Array a))
237−[C] Streamly.Internal.Data.Array.Generic
238− [A] createOf :: MonadIO m => Int -> Fold m a (Array a)
239− [A] create :: MonadIO m => Fold m a (Array a)
240−[C] Streamly.Internal.Data.Array
241− [R] ArrayUnsafe
242− [D] writeWith :: forall m a. (MonadIO m, Unbox a) => Int -> Fold m a (Array a)
243− [D] writeNUnsafe :: forall m a. (MonadIO m, Unbox a) => Int -> Fold m a (Array a)
244− [A] unsafePinnedCreateOf :: forall m a. (MonadIO m, Unbox a) => Int -> Fold m a (Array a)
245− [A] unsafePinnedAsPtr :: MonadIO m => Array a -> (Ptr a -> m b) -> m b
246− [A] unsafeCreateOf :: forall m a. (MonadIO m, Unbox a) => Int -> Fold m a (Array a)
247− [D] toStreamDRev :: forall m a. (Monad m, Unbox a) => Array a -> Stream m a
248− [D] toStreamD :: forall m a. (Monad m, Unbox a) => Array a -> Stream m a
249− [C] splice
250− [O] splice :: (MonadIO m, Unbox a) => Array a -> Array a -> m (Array a)
251− [N] splice :: MonadIO m => Array a -> Array a -> m (Array a)
252− [A] slicerFromLen :: forall m a. (Monad m, Unbox a) => Int -> Int -> Unfold m (Array a) (Array a)
253− [A] sliceIndexerFromLen :: forall m a. (Monad m, Unbox a) => Int -> Int -> Unfold m (Array a) (Int, Int)
254− [D] pinnedWriteNUnsafe :: forall m a. (MonadIO m, Unbox a) => Int -> Fold m a (Array a)
255− [D] pinnedWriteNAligned :: forall m a. (MonadIO m, Unbox a) => Int -> Int -> Fold m a (Array a)
256− [D] pinnedWriteN :: forall m a. (MonadIO m, Unbox a) => Int -> Fold m a (Array a)
257− [D] pinnedWrite :: forall m a. (MonadIO m, Unbox a) => Fold m a (Array a)
258− [A] pinnedCreateOf :: forall m a. (MonadIO m, Unbox a) => Int -> Fold m a (Array a)
259− [A] pinnedCreate :: forall m a. (MonadIO m, Unbox a) => Fold m a (Array a)
260− [A] pinnedCompactLE :: (MonadIO m, Unbox a) => Int -> Stream m (Array a) -> Stream m (Array a)
261− [A] parseBreakChunksK :: forall m a b. (MonadIO m, Unbox a) => Parser a m b -> StreamK m (Array a) -> m (Either ParseError b, StreamK m (Array a))
262− [D] nil :: Array a
263− [A] lPinnedCompactGE :: (MonadIO m, Unbox a) => Int -> Fold m (Array a) () -> Fold m (Array a) ()
264− [A] lCompactGE :: (MonadIO m, Unbox a) => Int -> Fold m (Array a) () -> Fold m (Array a) ()
265− [A] interposeSuffix :: forall m a. (Monad m, Unbox a) => a -> Stream m (Array a) -> Stream m a
266− [A] interpose :: (Monad m, Unbox a) => a -> Stream m (Array a) -> Stream m a
267− [A] intercalateSuffix :: (Monad m, Unbox a) => Array a -> Stream m (Array a) -> Stream m a
268− [A] indexReaderFromThenTo :: Unfold m (Int, Int, Int, Array a) a
269− [A] indexReader :: (Monad m, Unbox a) => Stream m Int -> Unfold m (Array a) a
270− [A] indexFinder :: (a -> Bool) -> Unfold Identity (Array a) Int
271− [D] getSlicesFromLen :: forall m a. (Monad m, Unbox a) => Int -> Int -> Unfold m (Array a) (Array a)
272− [R] getIndicesFromThenTo :: Unfold m (Int, Int, Int, Array a) a
273− [D] getIndices :: (Monad m, Unbox a) => Stream m Int -> Unfold m (Array a) a
274− [D] genSlicesFromLen :: forall m a. (Monad m, Unbox a) => Int -> Int -> Unfold m (Array a) (Int, Int)
275− [D] fromStreamDN :: forall m a. (MonadIO m, Unbox a) => Int -> Stream m a -> m (Array a)
276− [D] fromStreamD :: forall m a. (MonadIO m, Unbox a) => Stream m a -> m (Array a)
277− [A] fromPureStreamN :: Unbox a => Int -> Stream Identity a -> Array a
278− [A] fromPtrN :: Int -> Ptr Word8 -> Array Word8
279− [A] fromChunksK :: (MonadIO m, Unbox a) => StreamK m (Array a) -> m (Array a)
280− [A] fromChunks :: (MonadIO m, Unbox a) => Stream m (Array a) -> m (Array a)
281− [A] fromByteStr :: Ptr Word8 -> Array Word8
282− [A] foldChunks :: (MonadIO m, Unbox a) => Fold m a b -> Stream m (Array a) -> m b
283− [A] foldBreakChunksK :: forall m a b. (MonadIO m, Unbox a) => Fold m a b -> StreamK m (Array a) -> m (b, StreamK m (Array a))
284− [A] foldBreakChunks :: forall m a b. (MonadIO m, Unbox a) => Fold m a b -> Stream m (Array a) -> m (b, Stream m (Array a))
285− [D] flattenArraysRev :: forall m a. (MonadIO m, Unbox a) => Stream m (Array a) -> Stream m a
286− [D] flattenArrays :: forall m a. (MonadIO m, Unbox a) => Stream m (Array a) -> Stream m a
287− [C] findIndicesOf
288− [O] findIndicesOf :: (a -> Bool) -> Unfold Identity (Array a) Int
289− [N] findIndicesOf :: (a -> Bool) -> Array a -> Stream Identity Int
290− [A] fPinnedCompactGE :: (MonadIO m, Unbox a) => Int -> Fold m (Array a) (Array a)
291− [A] fCompactGE :: (MonadIO m, Unbox a) => Int -> Fold m (Array a) (Array a)
292− [A] empty :: Array a
293− [A] createWith :: forall m a. (MonadIO m, Unbox a) => Int -> Fold m a (Array a)
294− [A] createOf :: forall m a. (MonadIO m, Unbox a) => Int -> Fold m a (Array a)
295− [A] create :: forall m a. (MonadIO m, Unbox a) => Fold m a (Array a)
296− [A] concatRev :: forall m a. (Monad m, Unbox a) => Stream m (Array a) -> Stream m a
297− [A] concat :: (Monad m, Unbox a) => Stream m (Array a) -> Stream m a
298− [A] compactOnByteSuffix :: MonadIO m => Word8 -> Stream m (Array Word8) -> Stream m (Array Word8)
299− [A] compactOnByte :: MonadIO m => Word8 -> Stream m (Array Word8) -> Stream m (Array Word8)
300− [A] compactLE :: (MonadIO m, Unbox a) => Int -> Stream m (Array a) -> Stream m (Array a)
301− [A] compactGE :: (MonadIO m, Unbox a) => Int -> Stream m (Array a) -> Stream m (Array a)
302− [A] byteEq :: Array a -> Array a -> Bool
303− [A] byteCmp :: Array a -> Array a -> Ordering
304− [A] buildChunks :: (MonadIO m, Unbox a) => Stream m a -> m (StreamK m (Array a))
305− [D] bufferChunks :: (MonadIO m, Unbox a) => Stream m a -> m (StreamK m (Array a))
306− [D] asPtrUnsafe :: MonadIO m => Array a -> (Ptr a -> m b) -> m b
core/docs/Changelog.md+1−15
@@ -1,18 +1,5 @@
1# Changelog
1# Changelog
2
2
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−
11−## 0.2.1 (Dec 2023)
12−
13−* Make the serialization of the unit constructor deterministic.
14−* Expose `pinnedSerialize` & `deserialize` via `Streamly.Data.Array`.
15−
16## 0.2.0 (Nov 2023)
3## 0.2.0 (Nov 2023)
17
4
18See [0.1.0-0.2.0 API Changelog](https://github.com/composewell/streamly/blob/streamly-0.10.0/core/docs/ApiChangelogs/0.1.0-0.2.0.txt)
5See [0.1.0-0.2.0 API Changelog](https://github.com/composewell/streamly/blob/streamly-0.10.0/core/docs/ApiChangelogs/0.1.0-0.2.0.txt)
@@ -54,8 +41,7 @@ for a full list of deprecations.
54* Fold constructor has changed, added a `final` field to support
41* Fold constructor has changed, added a `final` field to support
55 finalization and cleanup of a chain of folds. The `extract` field is
42 finalization and cleanup of a chain of folds. The `extract` field is
56 now used only for mapping the fold internal state to fold result for
43 now used only for mapping the fold internal state to fold result for
57− scanning purposes. If your fold does not require cleanup you can just use
44+ scanning purposes.
58− your existing `extract` function as `final` as well to adapt to this change.
59* Many low level internal modules have been removed, they are entirely
45* Many low level internal modules have been removed, they are entirely
60 exported from higher level internal modules. If you were importing any
46 exported from higher level internal modules. If you were importing any
61 of the missing low level modules then import the higher level modules instead.
47 of the missing low level modules then import the higher level modules instead.
core/src/DocTestDataArray.hs−3
@@ -11,12 +11,9 @@
11
11
12>>> import qualified Streamly.Data.Array as Array
12>>> import qualified Streamly.Data.Array as Array
13>>> import qualified Streamly.Data.Fold as Fold
13>>> import qualified Streamly.Data.Fold as Fold
14−>>> import qualified Streamly.Data.ParserK as ParserK
15>>> import qualified Streamly.Data.Stream as Stream
14>>> import qualified Streamly.Data.Stream as Stream
16−>>> import qualified Streamly.Data.StreamK as StreamK
17
15
18For APIs that have not been released yet.
16For APIs that have not been released yet.
19
17
20>>> import qualified Streamly.Internal.Data.Array as Array
18>>> import qualified Streamly.Internal.Data.Array as Array
21−>>> import qualified Streamly.Internal.Data.Stream as Stream
22-}
19-}
core/src/DocTestDataStream.hs−2
@@ -6,7 +6,6 @@
6>>> import Control.Monad.IO.Class (MonadIO (liftIO))
6>>> import Control.Monad.IO.Class (MonadIO (liftIO))
7>>> import Control.Monad.Trans.Class (lift)
7>>> import Control.Monad.Trans.Class (lift)
8>>> import Control.Monad.Trans.Identity (runIdentityT)
8>>> import Control.Monad.Trans.Identity (runIdentityT)
9−>>> import Data.Char (isSpace)
10>>> import Data.Either (fromLeft, fromRight, isLeft, isRight, either)
9>>> import Data.Either (fromLeft, fromRight, isLeft, isRight, either)
11>>> import Data.Maybe (fromJust, isJust)
10>>> import Data.Maybe (fromJust, isJust)
12>>> import Data.Function (fix, (&))
11>>> import Data.Function (fix, (&))
@@ -33,7 +32,6 @@ For APIs that have not been released yet.
33>>> import qualified Streamly.Internal.Data.Fold as Fold
32>>> import qualified Streamly.Internal.Data.Fold as Fold
34>>> import qualified Streamly.Internal.Data.Parser as Parser
33>>> import qualified Streamly.Internal.Data.Parser as Parser
35>>> import qualified Streamly.Internal.Data.Stream as Stream
34>>> import qualified Streamly.Internal.Data.Stream as Stream
36−>>> import qualified Streamly.Internal.Data.StreamK as StreamK
37>>> import qualified Streamly.Internal.Data.Unfold as Unfold
35>>> import qualified Streamly.Internal.Data.Unfold as Unfold
38>>> import qualified Streamly.Internal.FileSystem.Dir as Dir
36>>> import qualified Streamly.Internal.FileSystem.Dir as Dir
39-}
37-}
core/src/DocTestFileSystemHandle.hs+1−1
@@ -8,7 +8,7 @@
8
8
9For APIs that have not been released yet.
9For APIs that have not been released yet.
10
10
11−>>> import qualified Streamly.Internal.Data.Array as Array (unsafeCreateOf)
11+>>> import qualified Streamly.Internal.Data.Array as Array (writeNUnsafe)
12>>> import qualified Streamly.Internal.Data.Unfold as Unfold (first)
12>>> import qualified Streamly.Internal.Data.Unfold as Unfold (first)
13>>> import qualified Streamly.Internal.FileSystem.Handle as Handle
13>>> import qualified Streamly.Internal.FileSystem.Handle as Handle
14>>> import qualified Streamly.Internal.System.IO as IO (defaultChunkSize)
14>>> import qualified Streamly.Internal.System.IO as IO (defaultChunkSize)
core/src/Streamly/Data/Array.hs+28−35
@@ -31,42 +31,42 @@ module Streamly.Data.Array
31 -- * The Array Type
31 -- * The Array Type
32 Array
32 Array
33
33
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−
47 -- * Construction
34 -- * Construction
48 -- | When performance matters, the fastest way to generate an array is
35 -- | When performance matters, the fastest way to generate an array is
49− -- 'createOf'. 'IsList' and 'IsString' instances can be
36+ -- 'writeN'. 'IsList' and 'IsString' instances can be
50 -- used to conveniently construct arrays from literal values.
37 -- used to conveniently construct arrays from literal values.
51 -- 'OverloadedLists' extension or 'fromList' can be used to construct an
38 -- 'OverloadedLists' extension or 'fromList' can be used to construct an
52 -- array from a list literal. Similarly, 'OverloadedStrings' extension or
39 -- array from a list literal. Similarly, 'OverloadedStrings' extension or
53 -- 'fromList' can be used to construct an array from a string literal.
40 -- 'fromList' can be used to construct an array from a string literal.
54
41
55− -- ** From Stream
56− , createOf
57− , create
58− , writeLastN -- drop old (ring buffer)
59−
60− -- ** From List
42+ -- Pure List APIs
61 , fromListN
43 , fromListN
62 , fromList
44 , fromList
63
45
64− -- * To List
46+ -- Monadic APIs
47+ , writeN -- drop new
48+ , write -- full buffer
49+ , writeLastN -- drop old (ring buffer)
50+
51+ -- * Conversion
65 -- 'GHC.Exts.toList' from "GHC.Exts" can be used to convert an array to a
52 -- 'GHC.Exts.toList' from "GHC.Exts" can be used to convert an array to a
66 -- list.
53 -- list.
67 , toList
54 , toList
68
55
69− -- * To Stream
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
70 , read
70 , read
71 , readRev
71 , readRev
72
72
@@ -83,24 +83,16 @@ module Streamly.Data.Array
83 -- , (!!)
83 -- , (!!)
84 , getIndex
84 , getIndex
85
85
86− -- * Serialization
87− , pinnedSerialize
88− , deserialize
89−
90 -- * Re-exports
86 -- * Re-exports
91 , Unbox (..)
87 , Unbox (..)
92− , Serialize(..)
93
88
94− -- * Deprecated
95− , writeN -- drop new
96− , write -- full buffer
97 )
89 )
98where
90where
99
91
100#include "inline.hs"
92#include "inline.hs"
101
93
102import Streamly.Internal.Data.Array
94import Streamly.Internal.Data.Array
103−import Streamly.Internal.Data.MutByteArray (Unbox(..), Serialize(..))
95+import Streamly.Internal.Data.Unbox (Unbox (..))
104
96
105import Prelude hiding (read, length)
97import Prelude hiding (read, length)
106
98
@@ -126,7 +118,7 @@ import Prelude hiding (read, length)
126--
118--
127-- Convert array to stream, transform, and fold back to array:
119-- Convert array to stream, transform, and fold back to array:
128--
120--
129−-- >>> amap f arr = Array.read arr & fmap f & Stream.fold (Array.createOf (Array.length arr))
121+-- >>> amap f arr = Array.read arr & fmap f & Stream.fold Array.write
130-- >>> amap (+1) (Array.fromList [1,2,3::Int])
122-- >>> amap (+1) (Array.fromList [1,2,3::Int])
131-- fromList [2,3,4]
123-- fromList [2,3,4]
132--
124--
@@ -153,11 +145,12 @@ import Prelude hiding (read, length)
153-- See the 'fromPureStream' unreleased API to generate an array from an
145-- See the 'fromPureStream' unreleased API to generate an array from an
154-- Identity stream safely without using MonadIO constraint.
146-- Identity stream safely without using MonadIO constraint.
155--
147--
148+--
156-- Note that 'Identity' streams can be generalized to IO streams:
149-- Note that 'Identity' streams can be generalized to IO streams:
157--
150--
158−-- >>> stream = Stream.fromList [1,2,3] :: Stream Identity Int
151+-- >>> pure = Stream.fromList [1,2,3] :: Stream Identity Int
159−-- >>> pure2IO s = Stream.morphInner (return . runIdentity) s :: Stream IO Int
152+-- >>> generally = Stream.morphInner (return . runIdentity)
160−-- >>> Stream.fold Array.create (pure2IO stream)
153+-- >>> Stream.fold Array.write (generally pure :: Stream IO Int)
161-- fromList [1,2,3]
154-- fromList [1,2,3]
162--
155--
163-- == Programming Tips
156-- == Programming Tips
core/src/Streamly/Data/Array/Generic.hs+13−16
@@ -15,37 +15,34 @@ module Streamly.Data.Array.Generic
15 ( Array
15 ( Array
16
16
17 -- * Construction
17 -- * Construction
18− , fromListN
18+ , A.fromListN
19− , fromList
19+ , A.fromList
20
20
21 -- MonadicAPIs
21 -- MonadicAPIs
22− , createOf
22+ , A.writeN
23− , create
23+ , A.write
24
24
25 -- * Conversion
25 -- * Conversion
26− , toList
26+ , A.toList
27
27
28 -- * Streams
28 -- * Streams
29− , read
29+ , A.read
30− , readRev
30+ , A.readRev
31
31
32 -- * Unfolds
32 -- * Unfolds
33− , reader
33+ , A.reader
34 -- , A.readerRev
34 -- , A.readerRev
35
35
36 -- * Random Access
36 -- * Random Access
37− , length
37+ , A.length
38− , getIndex
38+ , A.getIndex
39
39
40 -- -- * Folding Arrays
40 -- -- * Folding Arrays
41 -- , A.streamFold
41 -- , A.streamFold
42 -- , A.fold
42 -- , A.fold
43−
44− -- * Deprecated
45− , writeN
46− , write
47 )
43 )
48where
44where
49
45
50−import Streamly.Internal.Data.Array.Generic
46+import Streamly.Internal.Data.Array.Generic (Array)
51−import Prelude hiding (length, read)
47+
48+import qualified Streamly.Internal.Data.Array.Generic as A
core/src/Streamly/Data/MutArray.hs+6−12
@@ -32,14 +32,14 @@ module Streamly.Data.MutArray
32 -- * Construction
32 -- * Construction
33
33
34 -- Uninitialized Arrays
34 -- Uninitialized Arrays
35− , emptyOf
35+ , new
36− , pinnedEmptyOf
36+ , pinnedNew
37
37
38 -- From containers
38 -- From containers
39 , fromListN
39 , fromListN
40 , fromList
40 , fromList
41− , createOf
41+ , writeN -- drop new
42− , create
42+ , write -- full buffer
43 -- writeLastN
43 -- writeLastN
44
44
45 -- * Pinning & Unpinning
45 -- * Pinning & Unpinning
@@ -51,8 +51,8 @@ module Streamly.Data.MutArray
51 , snoc
51 , snoc
52
52
53 -- * Appending streams
53 -- * Appending streams
54− , appendN
54+ , writeAppendN -- XXX Rename to writeSnocN?
55− , append
55+ , writeAppend -- XXX Rename to writeSnoc?
56
56
57 -- * Inplace mutation
57 -- * Inplace mutation
58 , putIndex
58 , putIndex
@@ -88,12 +88,6 @@ module Streamly.Data.MutArray
88
88
89 -- * Deprecated
89 -- * Deprecated
90 , newPinned
90 , newPinned
91− , new
92− , pinnedNew
93− , writeN
94− , write
95− , writeAppendN
96− , writeAppend
97 )
91 )
98where
92where
99
93
core/src/Streamly/Data/MutArray/Generic.hs+3−8
@@ -23,11 +23,11 @@ module Streamly.Data.MutArray.Generic
23 MutArray
23 MutArray
24
24
25 -- * Construction
25 -- * Construction
26− , emptyOf
26+ , new
27 , fromListN
27 , fromListN
28 , fromList
28 , fromList
29− , createOf
29+ , writeN
30− , create
30+ , write
31
31
32 -- * Appending elements
32 -- * Appending elements
33 , snoc
33 , snoc
@@ -56,11 +56,6 @@ module Streamly.Data.MutArray.Generic
56
56
57 -- * Size
57 -- * Size
58 , length
58 , length
59−
60− -- * Deprecated
61− , new
62− , writeN
63− , write
64 )
59 )
65where
60where
66
61
core/src/Streamly/Data/MutByteArray.hs+5−14
@@ -21,9 +21,9 @@
21-- Higher level unboxed array modules "Streamly.Data.Array" and
21-- Higher level unboxed array modules "Streamly.Data.Array" and
22-- "Streamly.Data.MutArray" are built on top of this module. Unboxed arrays are
22-- "Streamly.Data.MutArray" are built on top of this module. Unboxed arrays are
23-- essentially serialized Haskell values. Array modules provide higher level
23-- essentially serialized Haskell values. Array modules provide higher level
24−-- serialization routines like 'Streamly.Internal.Data.Array.pinnedSerialize'
24+-- serialization routines like 'Streamly.Data.Array.pinnedSerialize'
25−-- and 'Streamly.Internal.Data.Array.deserialize' from the
25+-- and 'Streamly.Data.Array.deserialize' from the
26−-- "Streamly.Internal.Data.Array" module.
26+-- "Streamly.Data.Array" module.
27--
27--
28-- == Mutable Byte Array
28-- == Mutable Byte Array
29--
29--
@@ -55,19 +55,10 @@
55-- and then use the type class methods to serialize and deserialize to and from
55-- and then use the type class methods to serialize and deserialize to and from
56-- a 'MutByteArray'.
56-- a 'MutByteArray'.
57--
57--
58−-- See 'Streamly.Internal.Data.Array.pinnedSerialize' and
58+-- See 'Streamly.Data.Array.pinnedSerialize' and
59−-- 'Streamly.Internal.Data.Array.deserialize' for 'Array' type based
59+-- 'Streamly.Data.Array.deserialize' for 'Array' type based
60-- serialization.
60-- serialization.
61--
61--
62−-- == Comparing serialized values
63−--
64−-- When using the `Unbox` type class the same value may result in differing
65−-- serialized bytes because of unused uninitialized data in case of sum types.
66−-- Therefore, byte comparison of serialized values is not reliable.
67−--
68−-- However, the 'Serialize' type class guarantees that the serialized values
69−-- are always exactly the same and byte comparison of serialized is reliable.
70−--
71module Streamly.Data.MutByteArray
62module Streamly.Data.MutByteArray
72 (
63 (
73
64
core/src/Streamly/Data/Stream.hs+20−129
@@ -70,15 +70,6 @@ module Streamly.Data.Stream
70
70
71 -- ** From Values
71 -- ** From Values
72 -- | Generate a monadic stream from a seed value or values.
72 -- | 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− --
82 , fromPure
73 , fromPure
83 , fromEffect
74 , fromEffect
84 , repeat
75 , repeat
@@ -221,57 +212,21 @@ module Streamly.Data.Stream
221 -- operations like mapping a function over the stream.
212 -- operations like mapping a function over the stream.
222 , foldrM
213 , foldrM
223 , foldr
214 , foldr
224− -- foldr1
225
215
226 -- ** Specific Folds
216 -- ** Specific Folds
227− -- | Streams are folded using folds in "Streamly.Data.Fold". Here are some
217+ -- | Usually you can use the folds in "Streamly.Data.Fold". However, some
228− -- idioms and equivalents of Data.List APIs using folds:
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:
229 --
222 --
230 -- >>> foldlM' f a = Stream.fold (Fold.foldlM' f a)
223 -- >>> foldlM' f a = Stream.fold (Fold.foldlM' f a)
231− -- >>> foldl1' f = Stream.fold (Fold.foldl1' f)
224+ -- >>> foldl1 f = Stream.fold (Fold.foldl1' f)
232 -- >>> foldl' f a = Stream.fold (Fold.foldl' f a)
225 -- >>> foldl' f a = Stream.fold (Fold.foldl' f a)
233 -- >>> drain = Stream.fold Fold.drain
226 -- >>> drain = Stream.fold Fold.drain
234 -- >>> mapM_ f = Stream.fold (Fold.drainMapM f)
227 -- >>> mapM_ f = Stream.fold (Fold.drainMapM f)
235 -- >>> length = Stream.fold Fold.length
228 -- >>> length = Stream.fold Fold.length
236− -- >>> genericLength = Stream.fold Fold.lengthGeneric
237 -- >>> head = Stream.fold Fold.one
229 -- >>> 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
275 , toList
230 , toList
276
231
277 -- * Mapping
232 -- * Mapping
@@ -325,15 +280,6 @@ module Streamly.Data.Stream
325 , scan
280 , scan
326 , postscan
281 , postscan
327 -- XXX postscan1 can be implemented using Monoids or Refolds.
282 -- 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
337
283
338 -- ** Specific scans
284 -- ** Specific scans
339 -- Indexing can be considered as a special type of zipping where we zip a
285 -- Indexing can be considered as a special type of zipping where we zip a
@@ -342,8 +288,6 @@ module Streamly.Data.Stream
342
288
343 -- * Insertion
289 -- * Insertion
344 -- | Add elements to the stream.
290 -- | Add elements to the stream.
345− --
346− -- >>> insert = Stream.insertBy compare
347
291
348 -- Inserting elements is a special case of interleaving/merging streams.
292 -- Inserting elements is a special case of interleaving/merging streams.
349 , insertBy
293 , insertBy
@@ -379,15 +323,12 @@ module Streamly.Data.Stream
379 -- "Streamly.Internal.Data.Fold" can be used along with 'scanMaybe' to
323 -- "Streamly.Internal.Data.Fold" can be used along with 'scanMaybe' to
380 -- perform stateful filtering operations in general.
324 -- perform stateful filtering operations in general.
381 --
325 --
382− -- Idioms and equivalents of Data.List APIs:
326+ -- Useful idioms:
383 --
327 --
384 -- >>> deleteBy cmp x = Stream.scanMaybe (Fold.deleteBy cmp x)
328 -- >>> deleteBy cmp x = Stream.scanMaybe (Fold.deleteBy cmp x)
385− -- >>> deleteBy = Stream.deleteBy -- unreleased API
386− -- >>> delete = deleteBy (==)
387 -- >>> findIndices p = Stream.scanMaybe (Fold.findIndices p)
329 -- >>> findIndices p = Stream.scanMaybe (Fold.findIndices p)
388 -- >>> elemIndices a = findIndices (== a)
330 -- >>> elemIndices a = findIndices (== a)
389 -- >>> uniq = Stream.scanMaybe (Fold.uniqBy (==))
331 -- >>> uniq = Stream.scanMaybe (Fold.uniqBy (==))
390− -- >>> partition p = Stream.fold (Fold.partition Fold.toList Fold.toList) . fmap (if p then Left else Right)
391 , scanMaybe
332 , scanMaybe
392 , take
333 , take
393 , takeWhile
334 , takeWhile
@@ -395,8 +336,6 @@ module Streamly.Data.Stream
395 , drop
336 , drop
396 , dropWhile
337 , dropWhile
397 , dropWhileM
338 , dropWhileM
398− -- XXX write to an array in reverse and then read in reverse
399− -- > dropWhileEnd = reverse . dropWhile p . reverse
400
339
401 -- XXX These are available as scans in folds. We need to check the
340 -- XXX These are available as scans in folds. We need to check the
402 -- performance though. If these are common and we need convenient stream
341 -- performance though. If these are common and we need convenient stream
@@ -433,9 +372,6 @@ module Streamly.Data.Stream
433 -- >>> s = s1 `Stream.append` s2
372 -- >>> s = s1 `Stream.append` s2
434
373
435 -- ** Appending
374 -- ** Appending
436− -- | Equivalent of Data.List append:
437− --
438− -- >>> (++) = Stream.append
439 , append
375 , append
440
376
441 -- ** Interleaving
377 -- ** Interleaving
@@ -446,14 +382,8 @@ module Streamly.Data.Stream
446 , mergeByM
382 , mergeByM
447
383
448 -- ** Zipping
384 -- ** Zipping
449− -- | Idioms and equivalents of Data.List APIs:
450− --
451− -- >>> zip = Stream.zipWith (,)
452− -- >>> unzip = Stream.fold (Fold.unzip Fold.toList Fold.toList)
453 , zipWith
385 , zipWith
454 , zipWithM
386 , zipWithM
455− -- XXX zipWith3,4,5,6,7
456− -- XXX unzip3,4,5,6,7
457 -- , ZipStream (..)
387 -- , ZipStream (..)
458
388
459 -- ** Cross Product
389 -- ** Cross Product
@@ -486,89 +416,48 @@ module Streamly.Data.Stream
486 -- One dimension loops are just a special case of nested loops. For
416 -- One dimension loops are just a special case of nested loops. For
487 -- example, 'concatMap' can degenerate to a simple map operation:
417 -- example, 'concatMap' can degenerate to a simple map operation:
488 --
418 --
489− -- >>> map f m = Stream.concatMap (\x -> Stream.fromPure (f x)) m
419+ -- > map f m = S.concatMap (\x -> S.fromPure (f x)) m
490 --
420 --
491 -- Similarly, 'concatMap' can perform filtering by mapping an element to a
421 -- Similarly, 'concatMap' can perform filtering by mapping an element to a
492 -- 'nil' stream:
422 -- 'nil' stream:
493 --
423 --
494− -- >>> filter p m = Stream.concatMap (\x -> if p x then Stream.fromPure x else Stream.nil) m
424+ -- > filter p m = S.concatMap (\x -> if p x then S.fromPure x else S.nil) m
495− --
496− -- Idioms and equivalents of Data.List APIs:
497 --
425 --
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 " "
503
426
504 , concatEffect
427 , concatEffect
505 , concatMap
428 , concatMap
506 , concatMapM
429 , concatMapM
507
430
508 -- * Repeated Fold
431 -- * Repeated Fold
509− -- | Idioms and equivalents of Data.List APIs:
432+ -- | Useful idioms:
510 --
433 --
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)
511− -- >>> groupsOf n = Stream.foldMany (Fold.take n Fold.toList)
438+ -- >>> groupsOf n f = Stream.foldMany (Fold.take n f)
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 (==)
516 , foldMany -- XXX Rename to foldRepeat
439 , foldMany -- XXX Rename to foldRepeat
517 , groupsOf
440 , groupsOf
518 , parseMany
441 , parseMany
519
442
520 -- * Splitting
443 -- * 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)
530 , splitOn
444 , splitOn
531 , wordsBy
445 , wordsBy
532
446
533 -- * Buffered Operations
447 -- * Buffered Operations
534 -- | Operations that require buffering of the stream.
448 -- | Operations that require buffering of the stream.
535 -- Reverse is essentially a left fold followed by an unfold.
449 -- 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− --
550 , reverse
450 , 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
557
451
558 -- * Multi-Stream folds
452 -- * Multi-Stream folds
559 -- | Operations that consume multiple streams at the same time.
453 -- | Operations that consume multiple streams at the same time.
560 , eqBy
454 , eqBy
561 , cmpBy
455 , cmpBy
562 , isPrefixOf
456 , isPrefixOf
563− , isInfixOf
564− -- , isSuffixOf
565− -- , isSuffixOfUnbox
566 , isSubsequenceOf
457 , isSubsequenceOf
567
458
568 -- trimming sequences
459 -- trimming sequences
569− -- , stripPrefix
460+ , stripPrefix
570− -- , stripSuffix
571− -- , stripSuffixUnbox
572
461
573 -- Exceptions and resource management depend on the "exceptions" package
462 -- Exceptions and resource management depend on the "exceptions" package
574 -- XXX We can have IO Stream operations not depending on "exceptions"
463 -- XXX We can have IO Stream operations not depending on "exceptions"
@@ -632,8 +521,10 @@ where
632import Streamly.Internal.Data.Stream
521import Streamly.Internal.Data.Stream
633import Prelude
522import Prelude
634 hiding (filter, drop, dropWhile, take, takeWhile, zipWith, foldr,
523 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,
635− mapM, sequence, reverse, iterate, foldr1, repeat, replicate,
526+ reverse, iterate, init, and, or, lookup, foldr1, (!!),
636− concatMap)
527+ scanl, scanl1, repeat, replicate, concatMap, span)
637
528
638import qualified Streamly.Internal.Data.Array.Type as Array
529import qualified Streamly.Internal.Data.Array.Type as Array
639
530
core/src/Streamly/Internal/Data/Array.hs+128−559
@@ -2,6 +2,7 @@
2-- |
2-- |
3-- Module : Streamly.Internal.Data.Array
3-- Module : Streamly.Internal.Data.Array
4-- Copyright : (c) 2019 Composewell Technologies
4-- Copyright : (c) 2019 Composewell Technologies
5+--
5-- License : BSD3
6-- License : BSD3
6-- Maintainer : streamly@composewell.com
7-- Maintainer : streamly@composewell.com
7-- Stability : experimental
8-- Stability : experimental
@@ -19,21 +20,30 @@ module Streamly.Internal.Data.Array
19 module Streamly.Internal.Data.Array.Type
20 module Streamly.Internal.Data.Array.Type
20
21
21 -- * Construction
22 -- * Construction
23+ -- Stream Folds
24+ , fromStreamN
25+ , fromStream
26+
22 -- Monadic Folds
27 -- Monadic Folds
23 , writeLastN
28 , writeLastN
24
29
30+ -- * Unfolds
31+ , reader
32+ , readerUnsafe
33+ , producer -- experimental
34+
25 -- * Random Access
35 -- * Random Access
26 -- , (!!)
36 -- , (!!)
27 , getIndex
37 , getIndex
28 , getIndexRev
38 , getIndexRev
29− , last -- XXX getLastIndex?
39+ , last -- XXX getIndexLast?
40+ , getIndices
41+ , getIndicesFromThenTo
30 -- , getIndicesFrom -- read from a given position to the end of file
42 -- , getIndicesFrom -- read from a given position to the end of file
31 -- , getIndicesUpto -- read from beginning up to the given position
43 -- , getIndicesUpto -- read from beginning up to the given position
32 -- , getIndicesFromTo
44 -- , getIndicesFromTo
33 -- , getIndicesFromRev -- read from a given position to the beginning of file
45 -- , getIndicesFromRev -- read from a given position to the beginning of file
34 -- , getIndicesUptoRev -- read from end to the given position in file
46 -- , getIndicesUptoRev -- read from end to the given position in file
35− , indexReader
36− , indexReaderFromThenTo
37
47
38 -- * Size
48 -- * Size
39 , null
49 , null
@@ -41,8 +51,6 @@ module Streamly.Internal.Data.Array
41 -- * Search
51 -- * Search
42 , binarySearch
52 , binarySearch
43 , findIndicesOf
53 , findIndicesOf
44− -- getIndicesOf
45− , indexFinder -- see splitOn
46 -- , findIndexOf
54 -- , findIndexOf
47 -- , find
55 -- , find
48
56
@@ -55,8 +63,8 @@ module Streamly.Internal.Data.Array
55 -- * Subarrays
63 -- * Subarrays
56 , getSliceUnsafe
64 , getSliceUnsafe
57 -- , getSlice
65 -- , getSlice
58− , sliceIndexerFromLen
66+ , genSlicesFromLen
59− , slicerFromLen
67+ , getSlicesFromLen
60 , splitOn
68 , splitOn
61
69
62 -- * Streaming Operations
70 -- * Streaming Operations
@@ -66,34 +74,11 @@ module Streamly.Internal.Data.Array
66 , streamFold
74 , streamFold
67 , fold
75 , fold
68
76
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−
87 -- * Serialization
77 -- * Serialization
88 , encodeAs
78 , encodeAs
89 , serialize
79 , serialize
90 , pinnedSerialize
80 , pinnedSerialize
91 , deserialize
81 , deserialize
92−
93− -- * Deprecated
94− , genSlicesFromLen
95− , getSlicesFromLen
96− , getIndices
97 )
82 )
98where
83where
99
84
@@ -101,43 +86,37 @@ where
101#include "inline.hs"
86#include "inline.hs"
102#include "ArrayMacros.h"
87#include "ArrayMacros.h"
103
88
89+import Control.Monad (when)
104import Control.Monad.IO.Class (MonadIO(..))
90import Control.Monad.IO.Class (MonadIO(..))
105−-- import Data.Bifunctor (first)
106−-- import Data.Either (fromRight)
107import Data.Functor.Identity (Identity)
91import Data.Functor.Identity (Identity)
108import Data.Proxy (Proxy(..))
92import Data.Proxy (Proxy(..))
109import Data.Word (Word8)
93import Data.Word (Word8)
110import Foreign.C.String (CString)
94import Foreign.C.String (CString)
111import Foreign.Ptr (castPtr)
95import Foreign.Ptr (castPtr)
112import Foreign.Storable (Storable)
96import Foreign.Storable (Storable)
113−import GHC.Types (SPEC(..))
114import Streamly.Internal.Data.Unbox (Unbox(..))
97import Streamly.Internal.Data.Unbox (Unbox(..))
115import Prelude hiding (length, null, last, map, (!!), read, concat)
98import Prelude hiding (length, null, last, map, (!!), read, concat)
116
99
117−import Streamly.Internal.Data.MutByteArray.Type (PinnedState(..), MutByteArray)
100+import Streamly.Internal.Data.MutByteArray.Type (PinnedState(..))
118import Streamly.Internal.Data.Serialize.Type (Serialize)
101import Streamly.Internal.Data.Serialize.Type (Serialize)
119import Streamly.Internal.Data.Fold.Type (Fold(..))
102import Streamly.Internal.Data.Fold.Type (Fold(..))
120−import Streamly.Internal.Data.Parser (Parser(..), Initial(..), ParseError(..))
103+import Streamly.Internal.Data.Producer.Type (Producer(..))
121−import Streamly.Internal.Data.Stream (Stream(..))
104+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'(..))
105+import Streamly.Internal.Data.Tuple.Strict (Tuple3Fused'(..))
125import Streamly.Internal.Data.Unfold.Type (Unfold(..))
106import Streamly.Internal.Data.Unfold.Type (Unfold(..))
126import Streamly.Internal.System.IO (unsafeInlineIO)
107import Streamly.Internal.System.IO (unsafeInlineIO)
127
108
128−import qualified Streamly.Internal.Data.Fold.Type as Fold
129import qualified Streamly.Internal.Data.Serialize.Type as Serialize
109import qualified Streamly.Internal.Data.Serialize.Type as Serialize
130import qualified Streamly.Internal.Data.MutByteArray.Type as MBA
110import qualified Streamly.Internal.Data.MutByteArray.Type as MBA
131import qualified Streamly.Internal.Data.MutArray as MA
111import qualified Streamly.Internal.Data.MutArray as MA
112+import qualified Streamly.Internal.Data.Array.Type as A
132import qualified Streamly.Internal.Data.Fold as FL
113import 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
133import qualified Streamly.Internal.Data.Ring as RB
116import 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
136import qualified Streamly.Internal.Data.Stream as D
117import qualified Streamly.Internal.Data.Stream as D
137import qualified Streamly.Internal.Data.Stream as Stream
118import qualified Streamly.Internal.Data.Stream as Stream
138−import qualified Streamly.Internal.Data.StreamK as StreamK
139import qualified Streamly.Internal.Data.Unfold as Unfold
119import qualified Streamly.Internal.Data.Unfold as Unfold
140−import qualified Prelude
141
120
142import Streamly.Internal.Data.Array.Type
121import Streamly.Internal.Data.Array.Type
143
122
@@ -166,10 +145,82 @@ import Streamly.Internal.Data.Array.Type
166-- if we use one array for each chunk. This is still significant to add
145-- if we use one array for each chunk. This is still significant to add
167-- pressure to GC.
146-- pressure to GC.
168
147
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+
169-------------------------------------------------------------------------------
177-------------------------------------------------------------------------------
170-- Elimination
178-- Elimination
171-------------------------------------------------------------------------------
179-------------------------------------------------------------------------------
172
180
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+
173-- |
224-- |
174--
225--
175-- >>> null arr = Array.byteLength arr == 0
226-- >>> null arr = Array.byteLength arr == 0
@@ -177,7 +228,7 @@ import Streamly.Internal.Data.Array.Type
177-- /Pre-release/
228-- /Pre-release/
178{-# INLINE null #-}
229{-# INLINE null #-}
179null :: Array a -> Bool
230null :: Array a -> Bool
180−null arr = byteLength arr == 0
231+null arr = A.byteLength arr == 0
181
232
182-- | Like 'getIndex' but indexes the array in reverse from the end.
233-- | Like 'getIndex' but indexes the array in reverse from the end.
183--
234--
@@ -205,8 +256,6 @@ last = getIndexRev 0
205-- Folds with Array as the container
256-- Folds with Array as the container
206-------------------------------------------------------------------------------
257-------------------------------------------------------------------------------
207
258
208−-- XXX We should generate this from Ring.
209−
210-- | @writeLastN n@ folds a maximum of @n@ elements from the end of the input
259-- | @writeLastN n@ folds a maximum of @n@ elements from the end of the input
211-- stream to an 'Array'.
260-- stream to an 'Array'.
212--
261--
@@ -215,7 +264,7 @@ writeLastN ::
215 (Storable a, Unbox a, MonadIO m) => Int -> Fold m a (Array a)
264 (Storable a, Unbox a, MonadIO m) => Int -> Fold m a (Array a)
216writeLastN n
265writeLastN n
217 | n <= 0 = fmap (const mempty) FL.drain
266 | n <= 0 = fmap (const mempty) FL.drain
218− | otherwise = unsafeFreeze <$> Fold step initial done done
267+ | otherwise = A.unsafeFreeze <$> Fold step initial done done
219
268
220 where
269 where
221
270
@@ -228,9 +277,11 @@ writeLastN n
228 in fmap f $ liftIO $ RB.new n
277 in fmap f $ liftIO $ RB.new n
229
278
230 done (Tuple3Fused' rb rh i) = do
279 done (Tuple3Fused' rb rh i) = do
231− arr <- MA.new n
280+ arr <- liftIO $ MA.new n
281+ foldFunc i rh snoc' arr rb
282+
232− -- XXX We should write a read unfold for ring.
283+ -- XXX We should write a read unfold for ring.
233− foldFunc i rh MA.snocUnsafe arr rb
284+ snoc' b a = liftIO $ MA.snocUnsafe b a
234
285
235 foldFunc i
286 foldFunc i
236 | i < n = RB.unsafeFoldRingM
287 | i < n = RB.unsafeFoldRingM
@@ -256,26 +307,19 @@ binarySearch = undefined
256-- compared to streams by using SIMD instructions.
307-- compared to streams by using SIMD instructions.
257-- We can also return a bit array instead.
308-- We can also return a bit array instead.
258
309
259−-- Can use SIMD.
260−
261-- | Perform a linear search to find all the indices where a given element is
310-- | Perform a linear search to find all the indices where a given element is
262-- present in an array.
311-- present in an array.
263--
312--
264-- /Unimplemented/
313-- /Unimplemented/
265−indexFinder :: (a -> Bool) -> Unfold Identity (Array a) Int
314+findIndicesOf :: (a -> Bool) -> Unfold Identity (Array a) Int
266−indexFinder = undefined
315+findIndicesOf = undefined
267−
268−-- |
269−-- /Unimplemented/
270−findIndicesOf :: (a -> Bool) -> Array a -> Stream Identity Int
271−findIndicesOf p = Stream.unfold (indexFinder p)
272
316
273{-
317{-
274findIndexOf :: (a -> Bool) -> Array a -> Maybe Int
318findIndexOf :: (a -> Bool) -> Array a -> Maybe Int
275−findIndexOf p = Unfold.fold Fold.one . Stream.unfold (indexFinder p)
319+findIndexOf p = Unfold.fold Fold.one . Stream.unfold (findIndicesOf p)
276
320
277find :: (a -> Bool) -> Array a -> Bool
321find :: (a -> Bool) -> Array a -> Bool
278−find = Unfold.fold Fold.null . Stream.unfold (indexFinder p)
322+find = Unfold.fold Fold.null . Stream.unfold (findIndicesOf p)
279-}
323-}
280
324
281-------------------------------------------------------------------------------
325-------------------------------------------------------------------------------
@@ -317,45 +361,29 @@ splitOn :: (Monad m, Unbox a) =>
317 (a -> Bool) -> Array a -> Stream m (Array a)
361 (a -> Bool) -> Array a -> Stream m (Array a)
318splitOn predicate arr =
362splitOn predicate arr =
319 fmap (\(i, len) -> getSliceUnsafe i len arr)
363 fmap (\(i, len) -> getSliceUnsafe i len arr)
320− $ D.indexOnSuffix predicate (read arr)
364+ $ D.sliceOnSuffix predicate (A.toStreamD arr)
321−
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
365
330−{-# DEPRECATED genSlicesFromLen "Please use sliceIndexerFromLen instead." #-}
331{-# INLINE genSlicesFromLen #-}
366{-# INLINE genSlicesFromLen #-}
332genSlicesFromLen :: forall m a. (Monad m, Unbox a)
367genSlicesFromLen :: forall m a. (Monad m, Unbox a)
333 => Int -- ^ from index
368 => Int -- ^ from index
334 -> Int -- ^ length of the slice
369 -> Int -- ^ length of the slice
335 -> Unfold m (Array a) (Int, Int)
370 -> Unfold m (Array a) (Int, Int)
336−genSlicesFromLen = sliceIndexerFromLen
371+genSlicesFromLen from len =
372+ Unfold.lmap A.unsafeThaw (MA.genSlicesFromLen from len)
337
373
338-- | Generate a stream of slices of specified length from an array, starting
374-- | Generate a stream of slices of specified length from an array, starting
339-- from the supplied array index. The last slice may be shorter than the
375-- from the supplied array index. The last slice may be shorter than the
340-- requested length.
376-- requested length.
341--
377--
342-- /Pre-release//
378-- /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." #-}
353{-# INLINE getSlicesFromLen #-}
379{-# INLINE getSlicesFromLen #-}
354getSlicesFromLen :: forall m a. (Monad m, Unbox a)
380getSlicesFromLen :: forall m a. (Monad m, Unbox a)
355 => Int -- ^ from index
381 => Int -- ^ from index
356 -> Int -- ^ length of the slice
382 -> Int -- ^ length of the slice
357 -> Unfold m (Array a) (Array a)
383 -> Unfold m (Array a) (Array a)
358−getSlicesFromLen = slicerFromLen
384+getSlicesFromLen from len =
385+ fmap A.unsafeFreeze
386+ $ Unfold.lmap A.unsafeThaw (MA.getSlicesFromLen from len)
359
387
360-------------------------------------------------------------------------------
388-------------------------------------------------------------------------------
361-- Random reads and writes
389-- Random reads and writes
@@ -385,25 +413,19 @@ getIndex i arr =
385-- @
413-- @
386-- read =
414-- read =
387-- let u = lmap (\arr -> (0, length arr - 1)) Unfold.enumerateFromTo
415-- let u = lmap (\arr -> (0, length arr - 1)) Unfold.enumerateFromTo
388−-- in Unfold.lmap f (indexReader arr)
416+-- in Unfold.lmap f (getIndices arr)
389--
417--
390-- readRev =
418-- readRev =
391-- let i = length arr - 1
419-- let i = length arr - 1
392−-- in Unfold.lmap f (indexReaderFromThenTo i (i - 1) 0)
420+-- in Unfold.lmap f (getIndicesFromThenTo i (i - 1) 0)
393-- @
421-- @
394--
422--
395-- /Pre-release/
423-- /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." #-}
404{-# INLINE getIndices #-}
424{-# INLINE getIndices #-}
405getIndices :: (Monad m, Unbox a) => Stream m Int -> Unfold m (Array a) a
425getIndices :: (Monad m, Unbox a) => Stream m Int -> Unfold m (Array a) a
406−getIndices = indexReader
426+getIndices m =
427+ let unf = MA.getIndicesD (return . unsafeInlineIO) m
428+ in Unfold.lmap A.unsafeThaw unf
407
429
408-- | Unfolds @(from, then, to, array)@ generating a finite stream whose first
430-- | Unfolds @(from, then, to, array)@ generating a finite stream whose first
409-- element is the array value from the index @from@ and the successive elements
431-- element is the array value from the index @from@ and the successive elements
@@ -419,9 +441,9 @@ getIndices = indexReader
419-- @
441-- @
420--
442--
421-- /Unimplemented/
443-- /Unimplemented/
422−{-# INLINE indexReaderFromThenTo #-}
444+{-# INLINE getIndicesFromThenTo #-}
423−indexReaderFromThenTo :: Unfold m (Int, Int, Int, Array a) a
445+getIndicesFromThenTo :: Unfold m (Int, Int, Int, Array a) a
424−indexReaderFromThenTo = undefined
446+getIndicesFromThenTo = undefined
425
447
426-------------------------------------------------------------------------------
448-------------------------------------------------------------------------------
427-- Transform via stream operations
449-- Transform via stream operations
@@ -438,7 +460,7 @@ indexReaderFromThenTo = undefined
438{-# INLINE runPipe #-}
460{-# INLINE runPipe #-}
439runPipe :: (MonadIO m, Unbox a, Unbox b)
461runPipe :: (MonadIO m, Unbox a, Unbox b)
440 => Pipe m a b -> Array a -> m (Array b)
462 => Pipe m a b -> Array a -> m (Array b)
441−runPipe f arr = P.runPipe (toArrayMinChunk (length arr)) $ f (read arr)
463+runPipe f arr = P.runPipe (toArrayMinChunk (length arr)) $ f (A.read arr)
442-}
464-}
443
465
444-- XXX For transformations that cannot change the number of elements e.g. "map"
466-- XXX For transformations that cannot change the number of elements e.g. "map"
@@ -452,7 +474,7 @@ runPipe f arr = P.runPipe (toArrayMinChunk (length arr)) $ f (read arr)
452streamTransform :: forall m a b. (MonadIO m, Unbox a, Unbox b)
474streamTransform :: forall m a b. (MonadIO m, Unbox a, Unbox b)
453 => (Stream m a -> Stream m b) -> Array a -> m (Array b)
475 => (Stream m a -> Stream m b) -> Array a -> m (Array b)
454streamTransform f arr =
476streamTransform f arr =
455− Stream.fold (createWith (length arr)) $ f (read arr)
477+ Stream.fold (A.writeWith (length arr)) $ f (A.read arr)
456
478
457-------------------------------------------------------------------------------
479-------------------------------------------------------------------------------
458-- Casts
480-- Casts
@@ -486,7 +508,7 @@ asBytes = castUnsafe
486--
508--
487cast :: forall a b. (Unbox b) => Array a -> Maybe (Array b)
509cast :: forall a b. (Unbox b) => Array a -> Maybe (Array b)
488cast arr =
510cast arr =
489− let len = byteLength arr
511+ let len = A.byteLength arr
490 r = len `mod` SIZE_OF(b)
512 r = len `mod` SIZE_OF(b)
491 in if r /= 0
513 in if r /= 0
492 then Nothing
514 then Nothing
@@ -504,9 +526,9 @@ cast arr =
504--
526--
505asCStringUnsafe :: Array a -> (CString -> IO b) -> IO b
527asCStringUnsafe :: Array a -> (CString -> IO b) -> IO b
506asCStringUnsafe arr act = do
528asCStringUnsafe arr act = do
507− let arr1 = asBytes arr <> fromList [0]
529+ let arr1 = asBytes arr <> A.fromList [0]
508− -- unsafePinnedAsPtr makes sure the array is pinned
530+ -- asPtrUnsafe makes sure the array is pinned
509− unsafePinnedAsPtr arr1 $ \ptr -> act (castPtr ptr)
531+ asPtrUnsafe arr1 $ \ptr -> act (castPtr ptr)
510
532
511-------------------------------------------------------------------------------
533-------------------------------------------------------------------------------
512-- Folds
534-- Folds
@@ -519,14 +541,14 @@ asCStringUnsafe arr act = do
519-- /Pre-release/
541-- /Pre-release/
520{-# INLINE fold #-}
542{-# INLINE fold #-}
521fold :: forall m a b. (Monad m, Unbox a) => Fold m a b -> Array a -> m b
543fold :: forall m a b. (Monad m, Unbox a) => Fold m a b -> Array a -> m b
522−fold f arr = Stream.fold f (read arr)
544+fold f arr = Stream.fold f (A.read arr)
523
545
524-- | Fold an array using a stream fold operation.
546-- | Fold an array using a stream fold operation.
525--
547--
526-- /Pre-release/
548-- /Pre-release/
527{-# INLINE streamFold #-}
549{-# INLINE streamFold #-}
528streamFold :: (Monad m, Unbox a) => (Stream m a -> m b) -> Array a -> m b
550streamFold :: (Monad m, Unbox a) => (Stream m a -> m b) -> Array a -> m b
529−streamFold f arr = f (read arr)
551+streamFold f arr = f (A.read arr)
530
552
531--------------------------------------------------------------------------------
553--------------------------------------------------------------------------------
532-- Serialization
554-- Serialization
@@ -542,10 +564,6 @@ encodeAs ps a =
542 assertM(len == off)
564 assertM(len == off)
543 pure $ Array mbarr 0 off
565 pure $ Array mbarr 0 off
544
566
545−-- |
546−-- Properties:
547−-- 1. Identity: @deserialize . serialize == id@
548−-- 2. Encoded equivalence: @serialize a == serialize a@
549{-# INLINE serialize #-}
567{-# INLINE serialize #-}
550serialize :: Serialize a => a -> Array Word8
568serialize :: Serialize a => a -> Array Word8
551serialize = encodeAs Unpinned
569serialize = encodeAs Unpinned
@@ -553,10 +571,6 @@ serialize = encodeAs Unpinned
553-- | Serialize a Haskell type to a pinned byte array. The array is allocated
571-- | Serialize a Haskell type to a pinned byte array. The array is allocated
554-- using pinned memory so that it can be used directly in OS APIs for writing
572-- using pinned memory so that it can be used directly in OS APIs for writing
555-- to file or sending over the network.
573-- to file or sending over the network.
556−--
557−-- Properties:
558−-- 1. Identity: @deserialize . pinnedSerialize == id@
559−-- 2. Encoded equivalence: @pinnedSerialize a == pinnedSerialize a@
560{-# INLINE pinnedSerialize #-}
574{-# INLINE pinnedSerialize #-}
561pinnedSerialize :: Serialize a => a -> Array Word8
575pinnedSerialize :: Serialize a => a -> Array Word8
562pinnedSerialize = encodeAs Pinned
576pinnedSerialize = encodeAs Pinned
@@ -571,448 +585,3 @@ deserialize arr@(Array {..}) = unsafeInlineIO $ do
571 Serialize.deserializeAt arrStart arrContents (arrStart + lenArr)
585 Serialize.deserializeAt arrStart arrContents (arrStart + lenArr)
572 assertM(off == arrStart + lenArr)
586 assertM(off == arrStart + lenArr)
573 pure val
587 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+6−22
@@ -11,8 +11,8 @@ module Streamly.Internal.Data.Array.Generic
11
11
12 -- * Construction
12 -- * Construction
13 , nil
13 , nil
14− , createOf
14+ , writeN
15− , create
15+ , write
16 , writeWith
16 , writeWith
17 , writeLastN
17 , writeLastN
18
18
@@ -44,10 +44,6 @@ module Streamly.Internal.Data.Array.Generic
44 , getIndex
44 , getIndex
45 , getSliceUnsafe
45 , getSliceUnsafe
46 , strip
46 , strip
47−
48− -- * Deprecated
49− , writeN
50− , write
51 )
47 )
52where
48where
53
49
@@ -108,33 +104,21 @@ nil = unsafePerformIO $ unsafeFreeze <$> MArray.nil
108-- Construction - Folds
104-- Construction - Folds
109-------------------------------------------------------------------------------
105-------------------------------------------------------------------------------
110
106
111−{-# INLINE_NORMAL createOf #-}
107+{-# INLINE_NORMAL writeN #-}
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 #-}
118writeN :: MonadIO m => Int -> Fold m a (Array a)
108writeN :: MonadIO m => Int -> Fold m a (Array a)
119−writeN = createOf
109+writeN = fmap unsafeFreeze <$> MArray.writeN
120
110
121{-# INLINE_NORMAL writeWith #-}
111{-# INLINE_NORMAL writeWith #-}
122writeWith :: MonadIO m => Int -> Fold m a (Array a)
112writeWith :: MonadIO m => Int -> Fold m a (Array a)
123−writeWith elemCount = unsafeFreeze <$> MArray.createWith elemCount
113+writeWith elemCount = unsafeFreeze <$> MArray.writeWith elemCount
124
114
125-- | Fold the whole input to a single array.
115-- | Fold the whole input to a single array.
126--
116--
127-- /Caution! Do not use this on infinite streams./
117-- /Caution! Do not use this on infinite streams./
128--
118--
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." #-}
135{-# INLINE write #-}
119{-# INLINE write #-}
136write :: MonadIO m => Fold m a (Array a)
120write :: MonadIO m => Fold m a (Array a)
137−write = create
121+write = fmap unsafeFreeze MArray.write
138
122
139fromPureStream :: Stream Identity a -> Array a
123fromPureStream :: Stream Identity a -> Array a
140fromPureStream x =
124fromPureStream x =
core/src/Streamly/Internal/Data/Array/Stream.hs+564−57
@@ -12,7 +12,6 @@
12-- keep this as a separate module and release it.
12-- keep this as a separate module and release it.
13--
13--
14module Streamly.Internal.Data.Array.Stream
14module Streamly.Internal.Data.Array.Stream
15−{-# DEPRECATED "Please use \"Streamly.Internal.Data.Array\" instead." #-}
16 (
15 (
17 -- * Creation
16 -- * Creation
18 Array.chunksOf
17 Array.chunksOf
@@ -20,13 +19,13 @@ module Streamly.Internal.Data.Array.Stream
20 , Array.bufferChunks
19 , Array.bufferChunks
21
20
22 -- * Flattening to elements
21 -- * Flattening to elements
23− , Array.concat
22+ , concat
24 , Array.flattenArrays
23 , Array.flattenArrays
25− , Array.concatRev
24+ , concatRev
26 , Array.flattenArraysRev
25 , Array.flattenArraysRev
27− , Array.interpose
26+ , interpose
28− , Array.interposeSuffix
27+ , interposeSuffix
29− , Array.intercalateSuffix
28+ , intercalateSuffix
30 , unlines
29 , unlines
31
30
32 -- * Elimination
31 -- * Elimination
@@ -37,11 +36,10 @@ module Streamly.Internal.Data.Array.Stream
37 -- could also be implemented using parseBreak.
36 -- could also be implemented using parseBreak.
38 , foldBreak
37 , foldBreak
39 , foldBreakD
38 , foldBreakD
40− -- This is chunked parseBreak. A byte level parseBreak cannot work
39+ -- The byte level parseBreak cannot work efficiently. Because the stream
41− -- efficiently. Because the stream will have to be a StreamK for
42− -- backtracking, StreamK at byte level would not be efficient.
40+ -- will have to be a StreamK for backtracking, StreamK at byte level would
41+ -- not be efficient.
43− -- parseBreak p = K.parseBreakChunks (ParserK.adaptC p)
42+ , parseBreak -- StreamK.parseBreakChunks
44− , parseBreak
45 -- , parseBreakD
43 -- , parseBreakD
46 -- , foldManyChunks
44 -- , foldManyChunks
47 -- , parseManyChunks
45 -- , parseManyChunks
@@ -79,8 +77,6 @@ where
79import Data.Bifunctor (second)
77import Data.Bifunctor (second)
80import Control.Exception (assert)
78import Control.Exception (assert)
81import Control.Monad.IO.Class (MonadIO(..))
79import Control.Monad.IO.Class (MonadIO(..))
82−-- import Data.Bifunctor (first)
83−-- import Data.Either (fromRight)
84import Data.Proxy (Proxy(..))
80import Data.Proxy (Proxy(..))
85import Data.Word (Word8)
81import Data.Word (Word8)
86import Streamly.Internal.Data.Unbox (Unbox(..))
82import Streamly.Internal.Data.Unbox (Unbox(..))
@@ -96,32 +92,127 @@ import Streamly.Internal.Data.Parser (ParseError(..))
96import Streamly.Internal.Data.Stream (Stream)
92import Streamly.Internal.Data.Stream (Stream)
97import Streamly.Internal.Data.StreamK (StreamK, fromStream, toStream)
93import Streamly.Internal.Data.StreamK (StreamK, fromStream, toStream)
98import Streamly.Internal.Data.SVar.Type (adaptState, defState)
94import Streamly.Internal.Data.SVar.Type (adaptState, defState)
95+import Streamly.Internal.Data.Tuple.Strict (Tuple'(..))
99
96
97+import qualified Streamly.Data.Fold as FL
100import qualified Streamly.Internal.Data.Array as A
98import qualified Streamly.Internal.Data.Array as A
101import qualified Streamly.Internal.Data.Array as Array
99import 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(..))
102import qualified Streamly.Internal.Data.Parser as PR
103import qualified Streamly.Internal.Data.Parser as PR
103import qualified Streamly.Internal.Data.Parser as PRD
104import qualified Streamly.Internal.Data.Parser as PRD
104 (Parser(..), Initial(..))
105 (Parser(..), Initial(..))
105−-- import qualified Streamly.Internal.Data.ParserK as ParserK
106import qualified Streamly.Internal.Data.Stream as D
106import qualified Streamly.Internal.Data.Stream as D
107import qualified Streamly.Internal.Data.StreamK as K
107import qualified Streamly.Internal.Data.StreamK as K
108
108
109-- XXX Since these are immutable arrays MonadIO constraint can be removed from
109-- XXX Since these are immutable arrays MonadIO constraint can be removed from
110-- most places.
110-- most places.
111
111
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+
112-------------------------------------------------------------------------------
144-------------------------------------------------------------------------------
113-- Intersperse and append
145-- Intersperse and append
114-------------------------------------------------------------------------------
146-------------------------------------------------------------------------------
115
147
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
116{-# INLINE_NORMAL unlines #-}
177{-# INLINE_NORMAL unlines #-}
117unlines :: forall m a. (MonadIO m, Unbox a)
178unlines :: forall m a. (MonadIO m, Unbox a)
118 => a -> D.Stream m (Array a) -> D.Stream m a
179 => a -> D.Stream m (Array a) -> D.Stream m a
119−unlines = Array.interposeSuffix
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)
120
197
121-------------------------------------------------------------------------------
198-------------------------------------------------------------------------------
122-- Compact
199-- Compact
123-------------------------------------------------------------------------------
200-------------------------------------------------------------------------------
124
201
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+
125-- XXX instead of writing two different versions of this operation, we should
216-- XXX instead of writing two different versions of this operation, we should
126-- write it as a pipe.
217-- write it as a pipe.
127--
218--
@@ -129,7 +220,8 @@ unlines = Array.interposeSuffix
129{-# INLINE_NORMAL lpackArraysChunksOf #-}
220{-# INLINE_NORMAL lpackArraysChunksOf #-}
130lpackArraysChunksOf :: (MonadIO m, Unbox a)
221lpackArraysChunksOf :: (MonadIO m, Unbox a)
131 => Int -> Fold m (Array a) () -> Fold m (Array a) ()
222 => Int -> Fold m (Array a) () -> Fold m (Array a) ()
132−lpackArraysChunksOf = Array.lCompactGE
223+lpackArraysChunksOf n fld =
224+ FL.lmap A.unsafeThaw $ AS.lpackArraysChunksOf n (FL.lmap A.unsafeFreeze fld)
133
225
134-- | Coalesce adjacent arrays in incoming stream to form bigger arrays of a
226-- | Coalesce adjacent arrays in incoming stream to form bigger arrays of a
135-- maximum specified size in bytes.
227-- maximum specified size in bytes.
@@ -138,20 +230,69 @@ lpackArraysChunksOf = Array.lCompactGE
138{-# INLINE compact #-}
230{-# INLINE compact #-}
139compact :: (MonadIO m, Unbox a)
231compact :: (MonadIO m, Unbox a)
140 => Int -> Stream m (Array a) -> Stream m (Array a)
232 => Int -> Stream m (Array a) -> Stream m (Array a)
141−compact = Array.compactLE
233+compact = packArraysChunksOf
142−
143−-- | Given a stream of arrays, splice them all together to generate a single
144−-- array. The stream must be /finite/.
145−--
146−-- @since 0.7.0
147−{-# INLINE toArray #-}
148−toArray :: (MonadIO m, Unbox a) => Stream m (Array a) -> m (Array a)
149−toArray = Array.fromChunks
150
234
151-------------------------------------------------------------------------------
235-------------------------------------------------------------------------------
152-- Split
236-- Split
153-------------------------------------------------------------------------------
237-------------------------------------------------------------------------------
154
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.
248+--
249+-- @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)
292+
293+ step' _ (Yielding arr next) = return $ D.Yield arr next
294+ step' _ Finishing = return D.Stop
295+
155-- XXX Remove MonadIO constraint.
296-- XXX Remove MonadIO constraint.
156-- | Split a stream of arrays on a given separator byte, dropping the separator
297-- | Split a stream of arrays on a given separator byte, dropping the separator
157-- and coalescing all the arrays between two separators into a single array.
298-- and coalescing all the arrays between two separators into a single array.
@@ -163,7 +304,7 @@ splitOn
163 => Word8
304 => Word8
164 -> Stream m (Array Word8)
305 -> Stream m (Array Word8)
165 -> Stream m (Array Word8)
306 -> Stream m (Array Word8)
166−splitOn = Array.compactOnByte
307+splitOn byte = D.splitInnerBy (A.breakOn byte) A.splice
167
308
168{-# INLINE splitOnSuffix #-}
309{-# INLINE splitOnSuffix #-}
169splitOnSuffix
310splitOnSuffix
@@ -171,7 +312,8 @@ splitOnSuffix
171 => Word8
312 => Word8
172 -> Stream m (Array Word8)
313 -> Stream m (Array Word8)
173 -> Stream m (Array Word8)
314 -> Stream m (Array Word8)
174−splitOnSuffix = Array.compactOnByteSuffix
315+-- splitOn byte s = fromStreamD $ A.splitOn byte $ toStreamD s
316+splitOnSuffix byte = D.splitInnerBySuffix (A.breakOn byte) A.splice
175
317
176-------------------------------------------------------------------------------
318-------------------------------------------------------------------------------
177-- Elimination - Running folds
319-- Elimination - Running folds
@@ -180,24 +322,77 @@ splitOnSuffix = Array.compactOnByteSuffix
180{-# INLINE_NORMAL foldBreakD #-}
322{-# INLINE_NORMAL foldBreakD #-}
181foldBreakD :: forall m a b. (MonadIO m, Unbox a) =>
323foldBreakD :: forall m a b. (MonadIO m, Unbox a) =>
182 Fold m a b -> D.Stream m (Array a) -> m (b, D.Stream m (Array a))
324 Fold m a b -> D.Stream m (Array a) -> m (b, D.Stream m (Array a))
183−foldBreakD = Array.foldBreakChunks
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
184
390
185-- | Fold an array stream using the supplied 'Fold'. Returns the fold result
391-- | Fold an array stream using the supplied 'Fold'. Returns the fold result
186-- and the unconsumed stream.
392-- and the unconsumed stream.
187--
393--
188-- > foldBreak f = runArrayFoldBreak (ChunkFold.fromFold f)
394-- > foldBreak f = runArrayFoldBreak (ChunkFold.fromFold f)
189--
395--
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−--
201-- /Internal/
396-- /Internal/
202--
397--
203{-# INLINE_NORMAL foldBreak #-}
398{-# INLINE_NORMAL foldBreak #-}
@@ -206,20 +401,14 @@ foldBreak ::
206 => Fold m a b
401 => Fold m a b
207 -> StreamK m (A.Array a)
402 -> StreamK m (A.Array a)
208 -> m (b, StreamK m (A.Array a))
403 -> m (b, StreamK m (A.Array a))
209−foldBreak = Array.foldBreakChunksK
210−--
211-- foldBreak f s = fmap fromStreamD <$> foldBreakD f (toStreamD s)
404-- foldBreak f s = fmap fromStreamD <$> foldBreakD f (toStreamD s)
212−--
213−-- foldBreak f s =
405+foldBreak = foldBreakK
214−-- fmap (first (fromRight undefined))
215−-- $ K.parseBreakChunks (ParserK.adaptC (PR.fromFold f)) s
216−--
217-- If foldBreak performs better than runArrayFoldBreak we can use a rewrite
406-- If foldBreak performs better than runArrayFoldBreak we can use a rewrite
218-- rule to rewrite runArrayFoldBreak to fold.
407-- rule to rewrite runArrayFoldBreak to fold.
219-- foldBreak f = runArrayFoldBreak (ChunkFold.fromFold f)
408-- foldBreak f = runArrayFoldBreak (ChunkFold.fromFold f)
220
409
221-------------------------------------------------------------------------------
410-------------------------------------------------------------------------------
222−-- Elimination - running element parsers
411+-- Fold to a single Array
223-------------------------------------------------------------------------------
412-------------------------------------------------------------------------------
224
413
225-- When we have to take an array partially, take the last part of the array.
414-- When we have to take an array partially, take the last part of the array.
@@ -265,22 +454,341 @@ splitAtArrayListRev n ls
265 arr1 = Array contents end1 end
454 arr1 = Array contents end1 end
266 in ([arr1], arr2:xs)
455 in ([arr1], arr2:xs)
267
456
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+
268-- GHC parser does not accept {-# ANN type [] NoSpecConstr #-}, so we need
554-- GHC parser does not accept {-# ANN type [] NoSpecConstr #-}, so we need
269-- to make a newtype.
555-- to make a newtype.
270{-# ANN type List NoSpecConstr #-}
556{-# ANN type List NoSpecConstr #-}
271newtype List a = List {getList :: [a]}
557newtype List a = List {getList :: [a]}
272
558
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+
273-- | Parse an array stream using the supplied 'Parser'. Returns the parse
789-- | Parse an array stream using the supplied 'Parser'. Returns the parse
274-- result and the unconsumed stream. Throws 'ParseError' if the parse fails.
790-- result and the unconsumed stream. Throws 'ParseError' if the parse fails.
275--
791--
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−--
284-- /Internal/
792-- /Internal/
285--
793--
286{-# INLINE_NORMAL parseBreak #-}
794{-# INLINE_NORMAL parseBreak #-}
@@ -293,8 +801,7 @@ parseBreak ::
293parseBreak p s =
801parseBreak p s =
294 fmap fromStreamD <$> parseBreakD (PRD.fromParserK p) (toStreamD s)
802 fmap fromStreamD <$> parseBreakD (PRD.fromParserK p) (toStreamD s)
295-}
803-}
296−parseBreak = Array.parseBreakChunksK
804+parseBreak = parseBreakK
297−-- parseBreak p = K.parseBreakChunks (ParserK.adaptC p)
298
805
299-------------------------------------------------------------------------------
806-------------------------------------------------------------------------------
300-- Elimination - Running Array Folds and parsers
807-- Elimination - Running Array Folds and parsers
core/src/Streamly/Internal/Data/Array/Type.hs+96−489
@@ -1,11 +1,8 @@
1{-# LANGUAGE CPP #-}
1{-# 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 #-}
6-- |
2-- |
7-- Module : Streamly.Internal.Data.Array.Type
3-- Module : Streamly.Internal.Data.Array.Type
8-- Copyright : (c) 2020 Composewell Technologies
4-- Copyright : (c) 2020 Composewell Technologies
5+--
9-- License : BSD3-3-Clause
6-- License : BSD3-3-Clause
10-- Maintainer : streamly@composewell.com
7-- Maintainer : streamly@composewell.com
11-- Stability : experimental
8-- Stability : experimental
@@ -13,146 +10,87 @@
13--
10--
14-- See notes in "Streamly.Internal.Data.MutArray.Type"
11-- See notes in "Streamly.Internal.Data.MutArray.Type"
15--
12--
16−
17module Streamly.Internal.Data.Array.Type
13module Streamly.Internal.Data.Array.Type
18 (
14 (
19 -- ** Type
15 -- ** Type
20 -- $arrayNotes
16 -- $arrayNotes
21 Array (..)
17 Array (..)
18+ , asPtrUnsafe
19+ , nil
22
20
23− -- ** Conversion
21+ -- ** Freezing and Thawing
24− -- *** Mutable and Immutable
25 , unsafeFreeze
22 , unsafeFreeze
26 , unsafeFreezeWithShrink
23 , unsafeFreezeWithShrink
27 , unsafeThaw
24 , unsafeThaw
28
25
29− -- *** Pinned and Unpinned
26+ -- ** Pinning and Unpinning
30 , pin
27 , pin
31 , unpin
28 , unpin
32 , isPinned
29 , isPinned
33
30
34− -- *** Casting
35− , unsafePinnedAsPtr
36−
37 -- ** Construction
31 -- ** Construction
38− , empty
32+ , splice
39−
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
33
50− -- *** Stream Folds
51− , unsafeMakePure
52− , createOf
53− , pinnedCreateOf
54− , unsafeCreateOf
55− , unsafePinnedCreateOf
56− , create
57− , pinnedCreate
58− , createWith
59−
60− -- *** From containers
61− , fromListN
62− , pinnedFromListN
63 , fromList
34 , fromList
64 , pinnedFromList
35 , pinnedFromList
65− , fromListRevN
36+ , fromListN
37+ , pinnedFromListN
66 , fromListRev
38 , fromListRev
67− , fromStreamN
39+ , fromListRevN
68− , fromStream
40+ , fromStreamDN
69− , fromPureStreamN
41+ , fromStreamD
70 , fromPureStream
42 , fromPureStream
71 , fromByteStr#
43 , fromByteStr#
72− , fromByteStr
73− , fromPtrN
74− , fromChunks
75− , fromChunksK
76
44
77− -- ** Reading
45+ -- ** Split
46+ , breakOn
78
47
79− -- *** Indexing
48+ -- ** Cloning arrays
80− , unsafeIndexIO -- XXX unsafeGetIndexIO
49+ , clone
81− , getIndexUnsafe -- XXX unsafeGetIndex
50+ , pinnedClone
82−
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
51
98− -- *** Size
52+ -- ** Elimination
99− , length
53+ , unsafeIndexIO
54+ , getIndexUnsafe
100 , byteLength
55 , byteLength
56+ , length
101
57
102− -- ** Folding
103 , foldl'
58 , foldl'
104 , foldr
59 , foldr
105− , byteCmp
60+ , splitAt
106− , byteEq
107−
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
61
134− -- ** Deprecated
135− , asPtrUnsafe
136− , unsafeIndex
137− , bufferChunks
138− , flattenArrays
139− , flattenArraysRev
140− , fromArrayStreamK
141− , fromStreamDN
142− , fromStreamD
143 , toStreamD
62 , toStreamD
144 , toStreamDRev
63 , toStreamDRev
64+ , toStreamK
65+ , toStreamKRev
145 , toStream
66 , toStream
146 , toStreamRev
67 , toStreamRev
147− , nil
68+ , read
69+ , readRev
70+ , readerRev
71+ , toList
72+
73+ -- ** Folds
148 , writeWith
74 , writeWith
149 , writeN
75 , writeN
150 , pinnedWriteN
76 , pinnedWriteN
151 , writeNUnsafe
77 , writeNUnsafe
152 , pinnedWriteNUnsafe
78 , pinnedWriteNUnsafe
79+ , MA.ArrayUnsafe (..)
153 , pinnedWriteNAligned
80 , pinnedWriteNAligned
154 , write
81 , write
155 , pinnedWrite
82 , pinnedWrite
83+ , unsafeMakePure
84+
85+ -- ** Streams of arrays
86+ , chunksOf
87+ , pinnedChunksOf
88+ , bufferChunks
89+ , flattenArrays
90+ , flattenArraysRev
91+
92+ -- ** Deprecated
93+ , unsafeIndex
156 )
94 )
157where
95where
158
96
@@ -160,36 +98,31 @@ where
160#include "inline.hs"
98#include "inline.hs"
161
99
162import Control.Exception (assert)
100import Control.Exception (assert)
163−import Control.Monad (replicateM, when)
101+import Control.Monad (replicateM)
164import Control.Monad.IO.Class (MonadIO(..))
102import Control.Monad.IO.Class (MonadIO(..))
165import Data.Functor.Identity (Identity(..))
103import Data.Functor.Identity (Identity(..))
166−import Data.Int (Int8, Int16, Int32, Int64)
167import Data.Proxy (Proxy(..))
104import Data.Proxy (Proxy(..))
168−import Data.Word (Word8, Word16, Word32, Word64)
105+import Data.Word (Word8)
169import GHC.Base (build)
106import GHC.Base (build)
170import GHC.Exts (IsList, IsString(..), Addr#)
107import GHC.Exts (IsList, IsString(..), Addr#)
171
108
172import GHC.IO (unsafePerformIO)
109import GHC.IO (unsafePerformIO)
173import GHC.Ptr (Ptr(..))
110import GHC.Ptr (Ptr(..))
174−import Streamly.Internal.Data.Producer.Type (Producer(..))
111+import Streamly.Internal.Data.MutArray.Type (MutArray(..), MutByteArray)
175−import Streamly.Internal.Data.MutArray.Type (MutArray(..))
176−import Streamly.Internal.Data.MutByteArray.Type (MutByteArray)
177import Streamly.Internal.Data.Fold.Type (Fold(..))
112import Streamly.Internal.Data.Fold.Type (Fold(..))
178import Streamly.Internal.Data.Stream.Type (Stream)
113import Streamly.Internal.Data.Stream.Type (Stream)
179−import Streamly.Internal.Data.StreamK.Type (StreamK)
180import Streamly.Internal.Data.Unbox (Unbox(..))
114import Streamly.Internal.Data.Unbox (Unbox(..))
181import Streamly.Internal.Data.Unfold.Type (Unfold(..))
115import Streamly.Internal.Data.Unfold.Type (Unfold(..))
182import Text.Read (readPrec)
116import Text.Read (readPrec)
183
117
184−import Prelude hiding (Foldable(..), concat, read, unlines, splitAt)
118+import Prelude hiding (Foldable(..), read, unlines, splitAt)
185
119
186import qualified GHC.Exts as Exts
120import qualified GHC.Exts as Exts
187−import qualified Streamly.Internal.Data.Fold.Type as Fold
188import qualified Streamly.Internal.Data.MutArray.Type as MA
121import qualified Streamly.Internal.Data.MutArray.Type as MA
189import qualified Streamly.Internal.Data.Stream.Type as D
122import qualified Streamly.Internal.Data.Stream.Type as D
123+import qualified Streamly.Internal.Data.Stream.Generate as D
190import qualified Streamly.Internal.Data.StreamK.Type as K
124import qualified Streamly.Internal.Data.StreamK.Type as K
191import qualified Streamly.Internal.Data.MutByteArray.Type as Unboxed
125import qualified Streamly.Internal.Data.MutByteArray.Type as Unboxed
192−import qualified Streamly.Internal.Data.Producer as Producer
193import qualified Streamly.Internal.Data.Unfold.Type as Unfold
126import qualified Streamly.Internal.Data.Unfold.Type as Unfold
194import qualified Text.ParserCombinators.ReadPrec as ReadPrec
127import qualified Text.ParserCombinators.ReadPrec as ReadPrec
195
128
@@ -243,20 +176,15 @@ data Array a =
243
176
244-- | Use an @Array a@ as @Ptr a@.
177-- | Use an @Array a@ as @Ptr a@.
245--
178--
246−-- See 'MA.unsafePinnedAsPtr' in the Mutable array module for more details.
179+-- See 'MA.asPtrUnsafe' in the Mutable array module for more details.
247--
180--
248-- /Unsafe/
181-- /Unsafe/
249--
182--
250-- /Pre-release/
183-- /Pre-release/
251--
184--
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." #-}
257{-# INLINE asPtrUnsafe #-}
185{-# INLINE asPtrUnsafe #-}
258asPtrUnsafe :: MonadIO m => Array a -> (Ptr a -> m b) -> m b
186asPtrUnsafe :: MonadIO m => Array a -> (Ptr a -> m b) -> m b
259−asPtrUnsafe = unsafePinnedAsPtr
187+asPtrUnsafe arr = MA.asPtrUnsafe (unsafeThaw arr)
260
188
261-------------------------------------------------------------------------------
189-------------------------------------------------------------------------------
262-- Freezing and Thawing
190-- Freezing and Thawing
@@ -324,14 +252,11 @@ isPinned = MA.isPinned . unsafeThaw
324-- Construction
252-- Construction
325-------------------------------------------------------------------------------
253-------------------------------------------------------------------------------
326
254
327−-- | Copy two immutable arrays into a new array. If you want to splice more
255+-- Splice two immutable arrays creating a new array.
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.
331{-# INLINE splice #-}
256{-# INLINE splice #-}
332−splice :: MonadIO m => Array a -> Array a -> m (Array a)
257+splice :: (MonadIO m, Unbox a) => Array a -> Array a -> m (Array a)
333splice arr1 arr2 =
258splice arr1 arr2 =
334− unsafeFreeze <$> MA.spliceCopy (unsafeThaw arr1) (unsafeThaw arr2)
259+ unsafeFreeze <$> MA.splice (unsafeThaw arr1) (unsafeThaw arr2)
335
260
336-- | Create an 'Array' from the first N elements of a list. The array is
261-- | Create an 'Array' from the first N elements of a list. The array is
337-- allocated to size N, if the list terminates before N elements then the
262-- allocated to size N, if the list terminates before N elements then the
@@ -375,45 +300,15 @@ pinnedFromList xs = unsafePerformIO $ unsafeFreeze <$> MA.pinnedFromList xs
375fromListRev :: Unbox a => [a] -> Array a
300fromListRev :: Unbox a => [a] -> Array a
376fromListRev xs = unsafePerformIO $ unsafeFreeze <$> MA.fromListRev xs
301fromListRev xs = unsafePerformIO $ unsafeFreeze <$> MA.fromListRev xs
377
302
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 #-}
303+{-# INLINE_NORMAL fromStreamDN #-}
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." #-}
393fromStreamDN :: forall m a. (MonadIO m, Unbox a)
304fromStreamDN :: forall m a. (MonadIO m, Unbox a)
394 => Int -> D.Stream m a -> m (Array a)
305 => Int -> D.Stream m a -> m (Array a)
395−fromStreamDN = fromStreamN
306+fromStreamDN limit str = unsafeFreeze <$> MA.fromStreamDN limit str
396
307
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/
408{-# INLINE_NORMAL fromStreamD #-}
308{-# 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." #-}
414fromStreamD :: forall m a. (MonadIO m, Unbox a)
309fromStreamD :: forall m a. (MonadIO m, Unbox a)
415 => D.Stream m a -> m (Array a)
310 => D.Stream m a -> m (Array a)
416−fromStreamD = fromStream
311+fromStreamD str = unsafeFreeze <$> MA.fromStreamD str
417
312
418-------------------------------------------------------------------------------
313-------------------------------------------------------------------------------
419-- Cloning
314-- Cloning
@@ -443,15 +338,10 @@ pinnedClone = fmap unsafeFreeze . MA.pinnedClone . unsafeThaw
443-- Streams of arrays
338-- Streams of arrays
444-------------------------------------------------------------------------------
339-------------------------------------------------------------------------------
445
340
446−{-# INLINE buildChunks #-}
341+{-# INLINE bufferChunks #-}
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." #-}
452bufferChunks :: (MonadIO m, Unbox a) =>
342bufferChunks :: (MonadIO m, Unbox a) =>
453 D.Stream m a -> m (K.StreamK m (Array a))
343 D.Stream m a -> m (K.StreamK m (Array a))
454−bufferChunks = buildChunks
344+bufferChunks m = D.foldr K.cons K.nil $ chunksOf defaultChunkSize m
455
345
456-- | @chunksOf n stream@ groups the elements in the input stream into arrays of
346-- | @chunksOf n stream@ groups the elements in the input stream into arrays of
457-- @n@ elements each.
347-- @n@ elements each.
@@ -472,100 +362,27 @@ pinnedChunksOf :: forall m a. (MonadIO m, Unbox a)
472 => Int -> D.Stream m a -> D.Stream m (Array a)
362 => Int -> D.Stream m a -> D.Stream m (Array a)
473pinnedChunksOf n str = D.map unsafeFreeze $ MA.pinnedChunksOf n str
363pinnedChunksOf n str = D.map unsafeFreeze $ MA.pinnedChunksOf n str
474
364
475−-- | Convert a stream of arrays into a stream of their elements.
365+-- | Use the "read" unfold instead.
476--
366--
477−-- >>> concat = Stream.unfoldMany Array.reader
367+-- @flattenArrays = unfoldMany read@
478--
368--
479−{-# INLINE_NORMAL concat #-}
369+-- We can try this if there are any fusion issues in the unfold.
480−concat :: (Monad m, Unbox a) => Stream m (Array a) -> Stream m a
370+--
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 #-}
371+{-# INLINE_NORMAL flattenArrays #-}
487flattenArrays :: forall m a. (MonadIO m, Unbox a)
372flattenArrays :: forall m a. (MonadIO m, Unbox a)
488 => D.Stream m (Array a) -> D.Stream m a
373 => D.Stream m (Array a) -> D.Stream m a
489−flattenArrays = concat
374+flattenArrays = MA.flattenArrays . D.map unsafeThaw
490
375
491−-- | Convert a stream of arrays into a stream of their elements reversing the
376+-- | Use the "readRev" unfold instead.
492−-- contents of each array before flattening.
493--
377--
494−-- >>> concatRev = Stream.unfoldMany Array.readerRev
378+-- @flattenArrays = unfoldMany readRev@
495--
379--
496−{-# INLINE_NORMAL concatRev #-}
380+-- We can try this if there are any fusion issues in the unfold.
497−concatRev :: forall m a. (Monad m, Unbox a)
381+--
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 #-}
382+{-# INLINE_NORMAL flattenArraysRev #-}
505flattenArraysRev :: forall m a. (MonadIO m, Unbox a)
383flattenArraysRev :: forall m a. (MonadIO m, Unbox a)
506 => D.Stream m (Array a) -> D.Stream m a
384 => D.Stream m (Array a) -> D.Stream m a
507−flattenArraysRev = concatRev
385+flattenArraysRev = MA.flattenArraysRev . D.map unsafeThaw
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−-------------------------------------------------------------------------------
569
386
570-- Drops the separator byte
387-- Drops the separator byte
571{-# INLINE breakOn #-}
388{-# INLINE breakOn #-}
@@ -609,68 +426,24 @@ byteLength = MA.byteLength . unsafeThaw
609length :: Unbox a => Array a -> Int
426length :: Unbox a => Array a -> Int
610length arr = MA.length (unsafeThaw arr)
427length arr = MA.length (unsafeThaw arr)
611
428
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−
655-- | Unfold an array into a stream in reverse order.
429-- | Unfold an array into a stream in reverse order.
656--
430--
657{-# INLINE_NORMAL readerRev #-}
431{-# INLINE_NORMAL readerRev #-}
658readerRev :: forall m a. (Monad m, Unbox a) => Unfold m (Array a) a
432readerRev :: forall m a. (Monad m, Unbox a) => Unfold m (Array a) a
659readerRev = Unfold.lmap unsafeThaw $ MA.readerRevWith (return . unsafeInlineIO)
433readerRev = Unfold.lmap unsafeThaw $ MA.readerRevWith (return . unsafeInlineIO)
660
434
661−{-# DEPRECATED toStreamD "Please use 'read' instead." #-}
662{-# INLINE_NORMAL toStreamD #-}
435{-# INLINE_NORMAL toStreamD #-}
663toStreamD :: forall m a. (Monad m, Unbox a) => Array a -> D.Stream m a
436toStreamD :: forall m a. (Monad m, Unbox a) => Array a -> D.Stream m a
664−toStreamD = read
437+toStreamD arr = MA.toStreamDWith (return . unsafeInlineIO) (unsafeThaw arr)
665
438
666{-# INLINE toStreamK #-}
439{-# INLINE toStreamK #-}
667toStreamK :: forall m a. (Monad m, Unbox a) => Array a -> K.StreamK m a
440toStreamK :: forall m a. (Monad m, Unbox a) => Array a -> K.StreamK m a
668toStreamK arr = MA.toStreamKWith (return . unsafeInlineIO) (unsafeThaw arr)
441toStreamK arr = MA.toStreamKWith (return . unsafeInlineIO) (unsafeThaw arr)
669
442
670−{-# DEPRECATED toStreamDRev "Please use 'readRev' instead." #-}
671{-# INLINE_NORMAL toStreamDRev #-}
443{-# INLINE_NORMAL toStreamDRev #-}
672toStreamDRev :: forall m a. (Monad m, Unbox a) => Array a -> D.Stream m a
444toStreamDRev :: forall m a. (Monad m, Unbox a) => Array a -> D.Stream m a
673−toStreamDRev = readRev
445+toStreamDRev arr =
446+ MA.toStreamDRevWith (return . unsafeInlineIO) (unsafeThaw arr)
674
447
675{-# INLINE toStreamKRev #-}
448{-# INLINE toStreamKRev #-}
676toStreamKRev :: forall m a. (Monad m, Unbox a) => Array a -> K.StreamK m a
449toStreamKRev :: forall m a. (Monad m, Unbox a) => Array a -> K.StreamK m a
@@ -682,7 +455,7 @@ toStreamKRev arr =
682-- /Pre-release/
455-- /Pre-release/
683{-# INLINE_EARLY read #-}
456{-# INLINE_EARLY read #-}
684read :: (Monad m, Unbox a) => Array a -> Stream m a
457read :: (Monad m, Unbox a) => Array a -> Stream m a
685−read arr = MA.toStreamWith (return . unsafeInlineIO) (unsafeThaw arr)
458+read = toStreamD
686
459
687-- | Same as 'read'
460-- | Same as 'read'
688--
461--
@@ -699,7 +472,7 @@ toStream = read
699-- /Pre-release/
472-- /Pre-release/
700{-# INLINE_EARLY readRev #-}
473{-# INLINE_EARLY readRev #-}
701readRev :: (Monad m, Unbox a) => Array a -> Stream m a
474readRev :: (Monad m, Unbox a) => Array a -> Stream m a
702−readRev arr = MA.toStreamRevWith (return . unsafeInlineIO) (unsafeThaw arr)
475+readRev = toStreamDRev
703
476
704-- | Same as 'readRev'
477-- | Same as 'readRev'
705--
478--
@@ -723,7 +496,6 @@ foldr f z arr = runIdentity $ D.foldr f z $ toStreamD arr
723-- | Create two slices of an array without copying the original array. The
496-- | Create two slices of an array without copying the original array. The
724-- specified index @i@ is the first index of the second slice.
497-- specified index @i@ is the first index of the second slice.
725--
498--
726−{-# INLINE splitAt #-}
727splitAt :: Unbox a => Int -> Array a -> (Array a, Array a)
499splitAt :: Unbox a => Int -> Array a -> (Array a, Array a)
728splitAt i arr = (unsafeFreeze a, unsafeFreeze b)
500splitAt i arr = (unsafeFreeze a, unsafeFreeze b)
729 where
501 where
@@ -757,28 +529,17 @@ toList s = build (\c n -> toListFB c n s)
757-- Folds
529-- Folds
758-------------------------------------------------------------------------------
530-------------------------------------------------------------------------------
759
531
760−-- | @createOf n@ folds a maximum of @n@ elements from the input stream to an
532+-- | @writeN n@ folds a maximum of @n@ elements from the input stream to an
761-- 'Array'.
533-- 'Array'.
762--
534--
763−{-# INLINE_NORMAL createOf #-}
535+{-# INLINE_NORMAL writeN #-}
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 #-}
770writeN :: forall m a. (MonadIO m, Unbox a) => Int -> Fold m a (Array a)
536writeN :: forall m a. (MonadIO m, Unbox a) => Int -> Fold m a (Array a)
771−writeN = createOf
537+writeN = fmap unsafeFreeze . MA.writeN
772
538
773−-- | Like 'createOf' but creates a pinned array.
539+-- | Like 'fromListN' 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 #-}
540+{-# INLINE_NORMAL pinnedWriteN #-}
780pinnedWriteN :: forall m a. (MonadIO m, Unbox a) => Int -> Fold m a (Array a)
541pinnedWriteN :: forall m a. (MonadIO m, Unbox a) => Int -> Fold m a (Array a)
781−pinnedWriteN = pinnedCreateOf
542+pinnedWriteN = fmap unsafeFreeze . MA.pinnedWriteN
782
543
783-- | @pinnedWriteNAligned alignment n@ folds a maximum of @n@ elements from the input
544-- | @pinnedWriteNAligned alignment n@ folds a maximum of @n@ elements from the input
784-- stream to an 'Array' aligned to the given size.
545-- stream to an 'Array' aligned to the given size.
@@ -786,74 +547,44 @@ pinnedWriteN = pinnedCreateOf
786-- /Pre-release/
547-- /Pre-release/
787--
548--
788{-# INLINE_NORMAL pinnedWriteNAligned #-}
549{-# INLINE_NORMAL pinnedWriteNAligned #-}
789−{-# DEPRECATED pinnedWriteNAligned "To be removed." #-}
790pinnedWriteNAligned :: forall m a. (MonadIO m, Unbox a)
550pinnedWriteNAligned :: forall m a. (MonadIO m, Unbox a)
791 => Int -> Int -> Fold m a (Array a)
551 => Int -> Int -> Fold m a (Array a)
792pinnedWriteNAligned alignSize = fmap unsafeFreeze . MA.pinnedWriteNAligned alignSize
552pinnedWriteNAligned alignSize = fmap unsafeFreeze . MA.pinnedWriteNAligned alignSize
793
553
794−-- | Like 'createOf' but does not check the array bounds when writing. The fold
554+-- | Like 'writeN' but does not check the array bounds when writing. The fold
795-- driver must not call the step function more than 'n' times otherwise it will
555-- driver must not call the step function more than 'n' times otherwise it will
796-- corrupt the memory and crash. This function exists mainly because any
556-- corrupt the memory and crash. This function exists mainly because any
797-- conditional in the step function blocks fusion causing 10x performance
557-- conditional in the step function blocks fusion causing 10x performance
798-- slowdown.
558-- slowdown.
799--
559--
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 #-}
560+{-# INLINE_NORMAL writeNUnsafe #-}
807writeNUnsafe :: forall m a. (MonadIO m, Unbox a)
561writeNUnsafe :: forall m a. (MonadIO m, Unbox a)
808 => Int -> Fold m a (Array a)
562 => Int -> Fold m a (Array a)
809−writeNUnsafe = unsafeCreateOf
563+writeNUnsafe n = unsafeFreeze <$> MA.writeNUnsafe n
810
564
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 #-}
565+{-# INLINE_NORMAL pinnedWriteNUnsafe #-}
818pinnedWriteNUnsafe :: forall m a. (MonadIO m, Unbox a)
566pinnedWriteNUnsafe :: forall m a. (MonadIO m, Unbox a)
819 => Int -> Fold m a (Array a)
567 => Int -> Fold m a (Array a)
820−pinnedWriteNUnsafe = unsafePinnedCreateOf
568+pinnedWriteNUnsafe n = unsafeFreeze <$> MA.pinnedWriteNUnsafe n
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
827
569
828−{-# DEPRECATED writeWith "Please use createWith instead." #-}
829−{-# INLINE writeWith #-}
570+{-# INLINE_NORMAL writeWith #-}
830writeWith :: forall m a. (MonadIO m, Unbox a)
571writeWith :: forall m a. (MonadIO m, Unbox a)
831 => Int -> Fold m a (Array a)
572 => Int -> Fold m a (Array a)
832−writeWith = createWith
573+-- writeWith n = FL.rmapM spliceArrays $ toArraysOf n
574+writeWith elemCount = unsafeFreeze <$> MA.writeWith elemCount
833
575
834-- | Fold the whole input to a single array.
576-- | Fold the whole input to a single array.
835--
577--
836-- /Caution! Do not use this on infinite streams./
578-- /Caution! Do not use this on infinite streams./
837--
579--
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." #-}
844{-# INLINE write #-}
580{-# INLINE write #-}
845write :: forall m a. (MonadIO m, Unbox a) => Fold m a (Array a)
581write :: forall m a. (MonadIO m, Unbox a) => Fold m a (Array a)
846−write = create
582+write = fmap unsafeFreeze MA.write
847
583
848−-- | Like 'create' but creates a pinned array.
584+-- | Like 'write' 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." #-}
854{-# INLINE pinnedWrite #-}
585{-# INLINE pinnedWrite #-}
855pinnedWrite :: forall m a. (MonadIO m, Unbox a) => Fold m a (Array a)
586pinnedWrite :: forall m a. (MonadIO m, Unbox a) => Fold m a (Array a)
856−pinnedWrite = pinnedCreate
587+pinnedWrite = fmap unsafeFreeze MA.pinnedWrite
857
588
858-- | Fold "step" has a dependency on "initial", and each step is dependent on
589-- | Fold "step" has a dependency on "initial", and each step is dependent on
859-- the previous invocation of step due to state passing, finally extract
590-- the previous invocation of step due to state passing, finally extract
@@ -885,11 +616,6 @@ unsafeMakePure (Fold step initial extract final) =
885 (\s -> return $! unsafeInlineIO $ extract s)
616 (\s -> return $! unsafeInlineIO $ extract s)
886 (\s -> return $! unsafeInlineIO $ final s)
617 (\s -> return $! unsafeInlineIO $ final s)
887
618
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−
893-- | Convert a pure stream in Identity monad to an immutable array.
619-- | Convert a pure stream in Identity monad to an immutable array.
894--
620--
895-- Same as the following but with better performance:
621-- Same as the following but with better performance:
@@ -901,19 +627,6 @@ fromPureStream x = unsafePerformIO $ fmap (unsafeFreeze) (MA.fromPureStream x)
901-- fromPureStream = runIdentity . D.fold (unsafeMakePure write)
627-- fromPureStream = runIdentity . D.fold (unsafeMakePure write)
902-- fromPureStream = fromList . runIdentity . D.toList
628-- fromPureStream = fromList . runIdentity . D.toList
903
629
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−
917-- | Copy a null terminated immutable 'Addr#' Word8 sequence into an array.
630-- | Copy a null terminated immutable 'Addr#' Word8 sequence into an array.
918--
631--
919-- /Unsafe:/ The caller is responsible for safe addressing.
632-- /Unsafe:/ The caller is responsible for safe addressing.
@@ -924,46 +637,8 @@ fromPtrN n addr = unsafePerformIO $ fmap unsafeFreeze (MA.fromPtrN n addr)
924-- >>> Array.toList $ Array.fromByteStr# "\1\2\3\0"#
637-- >>> Array.toList $ Array.fromByteStr# "\1\2\3\0"#
925-- [1,2,3]
638-- [1,2,3]
926--
639--
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.
929fromByteStr# :: Addr# -> Array Word8
640fromByteStr# :: Addr# -> Array Word8
930−fromByteStr# addr = unsafePerformIO $ fmap unsafeFreeze (MA.fromByteStr# addr)
641+fromByteStr# addr = fromPureStream (D.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
967
642
968-------------------------------------------------------------------------------
643-------------------------------------------------------------------------------
969-- Instances
644-- Instances
@@ -995,66 +670,12 @@ instance Unbox a => IsList (Array a) where
995 {-# INLINE toList #-}
670 {-# INLINE toList #-}
996 toList = toList
671 toList = toList
997
672
998−-- | Byte compare two arrays. Compare the length of the arrays. If the length
673+-- XXX we are assuming that Unboxed equality means element equality. This may
999−-- is equal, compare the lexicographical ordering of two underlying byte arrays
674+-- or may not be correct? arrcmp is 40% faster compared to stream equality.
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
675+instance (Unbox a, Eq a) => Eq (Array a) where
1049 {-# INLINE (==) #-}
676 {-# INLINE (==) #-}
1050 arr1 == arr2 =
677 arr1 == 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
678+ (==) EQ $ unsafeInlineIO $! unsafeThaw arr1 `MA.cmp` unsafeThaw arr2
1057− (toStreamD arr1 :: Stream Identity a) == toStreamD arr2
1058
679
1059instance (Unbox a, Ord a) => Ord (Array a) where
680instance (Unbox a, Ord a) => Ord (Array a) where
1060 {-# INLINE compare #-}
681 {-# INLINE compare #-}
@@ -1119,29 +740,15 @@ instance Foldable Array where
1119-- Semigroup and Monoid
740-- Semigroup and Monoid
1120-------------------------------------------------------------------------------
741-------------------------------------------------------------------------------
1121
742
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.
1128instance Unbox a => Semigroup (Array a) where
743instance Unbox a => Semigroup (Array a) where
1129 arr1 <> arr2 = unsafePerformIO $ splice arr1 arr2
744 arr1 <> arr2 = unsafePerformIO $ splice arr1 arr2
1130
745
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." #-}
1139nil ::
746nil ::
1140#ifdef DEVBUILD
747#ifdef DEVBUILD
1141 Unbox a =>
748 Unbox a =>
1142#endif
749#endif
1143 Array a
750 Array a
1144−nil = empty
751+nil = Array Unboxed.nil 0 0
1145
752
1146instance Unbox a => Monoid (Array a) where
753instance Unbox a => Monoid (Array a) where
1147 mempty = nil
754 mempty = nil
core/src/Streamly/Internal/Data/Fold/Combinators.hs+92
@@ -30,6 +30,8 @@ module Streamly.Internal.Data.Fold.Combinators
30 -- *** Reducers
30 -- *** Reducers
31 , drainMapM
31 , drainMapM
32 , the
32 , the
33+ , length
34+ , lengthGeneric
33 , mean
35 , mean
34 , rollingHash
36 , rollingHash
35 , defaultSalt
37 , defaultSalt
@@ -164,6 +166,8 @@ module Streamly.Internal.Data.Fold.Combinators
164 -- ** Trimming
166 -- ** Trimming
165
167
166 -- By elements
168 -- By elements
169+ , takeEndBy
170+ , takeEndBy_
167 , takeEndBySeq
171 , takeEndBySeq
168 , takeEndBySeq_
172 , takeEndBySeq_
169 {-
173 {-
@@ -705,6 +709,30 @@ the = foldt' step initial id
705-- To Summary
709-- To Summary
706------------------------------------------------------------------------------
710------------------------------------------------------------------------------
707
711
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+
708-- | Determine the sum of all elements of a stream of numbers. Returns additive
736-- | Determine the sum of all elements of a stream of numbers. Returns additive
709-- identity (@0@) when the stream is empty. Note that this is not numerically
737-- identity (@0@) when the stream is empty. Note that this is not numerically
710-- stable for floating point numbers.
738-- stable for floating point numbers.
@@ -1482,6 +1510,70 @@ droppingWhileM p = Fold step initial extract extract
1482droppingWhile :: Monad m => (a -> Bool) -> Fold m a (Maybe a)
1510droppingWhile :: Monad m => (a -> Bool) -> Fold m a (Maybe a)
1483droppingWhile p = droppingWhileM (return . p)
1511droppingWhile p = droppingWhileM (return . p)
1484
1512
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+
1485------------------------------------------------------------------------------
1577------------------------------------------------------------------------------
1486-- Binary splitting on a separator
1578-- Binary splitting on a separator
1487------------------------------------------------------------------------------
1579------------------------------------------------------------------------------
core/src/Streamly/Internal/Data/Fold/Type.hs−91
@@ -367,8 +367,6 @@ module Streamly.Internal.Data.Fold.Type
367 , toList
367 , toList
368 , toStreamK
368 , toStreamK
369 , toStreamKRev
369 , toStreamKRev
370− , lengthGeneric
371− , length
372
370
373 -- * Combinators
371 -- * Combinators
374
372
@@ -393,8 +391,6 @@ module Streamly.Internal.Data.Fold.Type
393 -- ** Trimming
391 -- ** Trimming
394 , take
392 , take
395 , taking
393 , taking
396− , takeEndBy_
397− , takeEndBy
398 , dropping
394 , dropping
399
395
400 -- ** Sequential application
396 -- ** Sequential application
@@ -771,29 +767,6 @@ toStreamKRev = foldl' (flip K.cons) K.nil
771toStreamK :: Monad m => Fold m a (K.StreamK n a)
767toStreamK :: Monad m => Fold m a (K.StreamK n a)
772toStreamK = foldr K.cons K.nil
768toStreamK = foldr K.cons K.nil
773
769
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−
797------------------------------------------------------------------------------
770------------------------------------------------------------------------------
798-- Instances
771-- Instances
799------------------------------------------------------------------------------
772------------------------------------------------------------------------------
@@ -1544,70 +1517,6 @@ take n (Fold fstep finitial fextract ffinal) = Fold step initial extract final
1544
1517
1545 final (Tuple'Fused _ r) = ffinal r
1518 final (Tuple'Fused _ r) = ffinal r
1546
1519
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−
1611------------------------------------------------------------------------------
1520------------------------------------------------------------------------------
1612-- Nesting
1521-- Nesting
1613------------------------------------------------------------------------------
1522------------------------------------------------------------------------------
core/src/Streamly/Internal/Data/IsMap.hs−4
@@ -1,7 +1,3 @@
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-- |
1-- |
6-- Module : Streamly.Internal.Data.IsMap
2-- Module : Streamly.Internal.Data.IsMap
7-- Copyright : (c) 2022 Composewell Technologies
3-- Copyright : (c) 2022 Composewell Technologies
core/src/Streamly/Internal/Data/MutArray.hs+35−151
@@ -11,197 +11,81 @@ module Streamly.Internal.Data.MutArray
11 -- * MutArray.Type module
11 -- * MutArray.Type module
12 module Streamly.Internal.Data.MutArray.Type
12 module Streamly.Internal.Data.MutArray.Type
13 -- * MutArray module
13 -- * MutArray module
14− , sliceIndexerFromLen
14+ , splitOn
15− , slicerFromLen
16− , compactLE
17− , pinnedCompactLE
18− , compactOnByte
19− , compactOnByteSuffix
20− -- * Unboxed IORef
21− , module Streamly.Internal.Data.IORef.Unboxed
22−
23− -- * Deprecated
24 , genSlicesFromLen
15 , genSlicesFromLen
25 , getSlicesFromLen
16 , getSlicesFromLen
17+ , fromStream
18+ -- * Unboxed IORef
19+ , module Streamly.Internal.Data.IORef.Unboxed
26 )
20 )
27where
21where
28
22
29#include "inline.hs"
23#include "inline.hs"
30
24
31import Control.Monad.IO.Class (MonadIO(..))
25import 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)
35import Streamly.Internal.Data.Unbox (Unbox)
26import Streamly.Internal.Data.Unbox (Unbox)
27+import Streamly.Internal.Data.Stream (Stream)
36import Streamly.Internal.Data.Unfold.Type (Unfold(..))
28import Streamly.Internal.Data.Unfold.Type (Unfold(..))
37
29
38−import qualified Streamly.Internal.Data.Stream.Type as Stream
30+import qualified Streamly.Internal.Data.Stream as D
39−import qualified Streamly.Internal.Data.Stream.Nesting as Stream
40−-- import qualified Streamly.Internal.Data.Stream.Transform as Stream
41import qualified Streamly.Internal.Data.Unfold as Unfold
31import qualified Streamly.Internal.Data.Unfold as Unfold
42
32
43import Prelude hiding (foldr, length, read, splitAt)
33import Prelude hiding (foldr, length, read, splitAt)
44import Streamly.Internal.Data.MutArray.Type
34import Streamly.Internal.Data.MutArray.Type
45import Streamly.Internal.Data.IORef.Unboxed
35import Streamly.Internal.Data.IORef.Unboxed
46
36
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+
47-- | Generate a stream of array slice descriptors ((index, len)) of specified
48-- | Generate a stream of array slice descriptors ((index, len)) of specified
48-- length from an array, starting from the supplied array index. The last slice
49-- length from an array, starting from the supplied array index. The last slice
49-- may be shorter than the requested length depending on the array length.
50-- may be shorter than the requested length depending on the array length.
50--
51--
51-- /Pre-release/
52-- /Pre-release/
52−{-# INLINE sliceIndexerFromLen #-}
53+{-# INLINE genSlicesFromLen #-}
53−sliceIndexerFromLen :: forall m a. (Monad m, Unbox a)
54+genSlicesFromLen :: forall m a. (Monad m, Unbox a)
54 => Int -- ^ from index
55 => Int -- ^ from index
55 -> Int -- ^ length of the slice
56 -> Int -- ^ length of the slice
56 -> Unfold m (MutArray a) (Int, Int)
57 -> Unfold m (MutArray a) (Int, Int)
57−sliceIndexerFromLen from len =
58+genSlicesFromLen from len =
58 let fromThenTo n = (from, from + len, n - 1)
59 let fromThenTo n = (from, from + len, n - 1)
59 mkSlice n i = return (i, min len (n - i))
60 mkSlice n i = return (i, min len (n - i))
60 in Unfold.lmap length
61 in Unfold.lmap length
61 $ Unfold.mapM2 mkSlice
62 $ Unfold.mapM2 mkSlice
62 $ Unfold.lmap fromThenTo Unfold.enumerateFromThenTo
63 $ Unfold.lmap fromThenTo Unfold.enumerateFromThenTo
63
64
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−
71-- | Generate a stream of slices of specified length from an array, starting
65-- | Generate a stream of slices of specified length from an array, starting
72-- from the supplied array index. The last slice may be shorter than the
66-- from the supplied array index. The last slice may be shorter than the
73-- requested length depending on the array length.
67-- requested length depending on the array length.
74--
68--
75-- /Pre-release/
69-- /Pre-release/
76−{-# INLINE slicerFromLen #-}
77−slicerFromLen :: forall m a. (Monad m, Unbox a)
78− => Int -- ^ from index
79− -> Int -- ^ length of the slice
80− -> Unfold m (MutArray a) (MutArray a)
81−slicerFromLen from len =
82− let mkSlice arr (i, n) = return $ getSliceUnsafe i n arr
83− in Unfold.mapM2 mkSlice (sliceIndexerFromLen from len)
84−
85−{-# DEPRECATED getSlicesFromLen "Please use slicerFromLen instead." #-}
70+{-# INLINE getSlicesFromLen #-}
86getSlicesFromLen :: forall m a. (Monad m, Unbox a)
71getSlicesFromLen :: forall m a. (Monad m, Unbox a)
87 => Int -- ^ from index
72 => Int -- ^ from index
88 -> Int -- ^ length of the slice
73 -> Int -- ^ length of the slice
89 -> Unfold m (MutArray a) (MutArray a)
74 -> Unfold m (MutArray a) (MutArray a)
90−getSlicesFromLen = slicerFromLen
75+getSlicesFromLen from len =
91−
76+ let mkSlice arr (i, n) = return $ getSliceUnsafe i n arr
92−-------------------------------------------------------------------------------
77+ in Unfold.mapM2 mkSlice (genSlicesFromLen from len)
93−-- Compacting Streams of Arrays
94−-------------------------------------------------------------------------------
95
78
96−-- | @compactLE maxElems@ coalesces adjacent arrays in the input stream
79+-- | Create an 'Array' from a stream. This is useful when we want to create a
97−-- only if the combined size would be less than or equal to @maxElems@
80+-- single array from a stream of unknown size. 'writeN' is at least twice
98−-- elements. Note that it won't split an array if the original array is already
81+-- as efficient when the size is already known.
99−-- larger than maxElems.
100--
82--
101−-- @maxElems@ must be greater than 0.
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.
102--
86--
103−-- Generates unpinned arrays irrespective of the pinning status of input
87+-- /Pre-release/
104−-- arrays.
88+{-# INLINE fromStream #-}
105−{-# INLINE compactLE #-}
106−compactLE :: (MonadIO m, Unbox a) =>
107− Int -> Stream m (MutArray a) -> Stream m (MutArray a)
89+fromStream :: (MonadIO m, Unbox a) => Stream m a -> m (MutArray a)
108−-- XXX compactLE can be moved to MutArray/Type if we are not using the parser
90+fromStream = fromStreamD
109−-- to implement it.
91+-- fromStream (Stream m) = P.fold write m
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+30−65
@@ -19,14 +19,14 @@ module Streamly.Internal.Data.MutArray.Generic
19 , nil
19 , nil
20
20
21 -- *** Uninitialized Arrays
21 -- *** Uninitialized Arrays
22− , emptyOf
22+ , new
23 -- , newArrayWith
23 -- , newArrayWith
24
24
25 -- *** From streams
25 -- *** From streams
26− , unsafeCreateOf
26+ , writeNUnsafe
27− , createOf
27+ , writeN
28− , createWith -- createOfMin/createMin/createGE?
28+ , writeWith
29− , create
29+ , write
30 , fromStreamN
30 , fromStreamN
31 , fromStream
31 , fromStream
32 , fromPureStream
32 , fromPureStream
@@ -160,13 +160,6 @@ module Streamly.Internal.Data.MutArray.Generic
160 -- , appendSliceFrom
160 -- , appendSliceFrom
161
161
162 , clone
162 , clone
163−
164− -- * Deprecated
165− , new
166− , writeNUnsafe
167− , writeN
168− , writeWith
169− , write
170 )
163 )
171where
164where
172
165
@@ -243,13 +236,13 @@ bottomElement =
243-- XXX Would be nice if GHC can provide something like newUninitializedArray# so
236-- XXX Would be nice if GHC can provide something like newUninitializedArray# so
244-- that we do not have to write undefined or error in the whole array.
237-- that we do not have to write undefined or error in the whole array.
245
238
246−-- | @emptyOf count@ allocates a zero length array that can be extended to hold
239+-- | @new count@ allocates a zero length array that can be extended to hold
247-- up to 'count' items without reallocating.
240-- up to 'count' items without reallocating.
248--
241--
249-- /Pre-release/
242-- /Pre-release/
250−{-# INLINE emptyOf #-}
243+{-# INLINE new #-}
251−emptyOf :: MonadIO m => Int -> m (MutArray a)
244+new :: MonadIO m => Int -> m (MutArray a)
252−emptyOf n@(I# n#) =
245+new n@(I# n#) =
253 liftIO
246 liftIO
254 $ IO
247 $ IO
255 $ \s# ->
248 $ \s# ->
@@ -258,12 +251,6 @@ emptyOf n@(I# n#) =
258 let ma = MutArray arr# 0 0 n
251 let ma = MutArray arr# 0 0 n
259 in (# s1#, ma #)
252 in (# s1#, ma #)
260
253
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−
267-- XXX This could be pure?
254-- XXX This could be pure?
268
255
269-- |
256-- |
@@ -331,7 +318,7 @@ putIndices arr = FL.foldlM' step (return ())
331
318
332 where
319 where
333
320
334− step () (i, x) = putIndex i arr x
321+ step () (i, x) = liftIO (putIndex i arr x)
335
322
336-- | Modify a given index of an array using a modifier function without checking
323-- | Modify a given index of an array using a modifier function without checking
337-- the bounds.
324-- the bounds.
@@ -602,16 +589,16 @@ readRev arr@MutArray{..} =
602arrayChunkSize :: Int
589arrayChunkSize :: Int
603arrayChunkSize = 1024
590arrayChunkSize = 1024
604
591
605−-- | Like 'createOf' but does not check the array bounds when writing. The fold
592+-- | Like 'writeN' but does not check the array bounds when writing. The fold
606-- driver must not call the step function more than 'n' times otherwise it will
593-- driver must not call the step function more than 'n' times otherwise it will
607-- corrupt the memory and crash. This function exists mainly because any
594-- corrupt the memory and crash. This function exists mainly because any
608-- conditional in the step function blocks fusion causing 10x performance
595-- conditional in the step function blocks fusion causing 10x performance
609-- slowdown.
596-- slowdown.
610--
597--
611-- /Pre-release/
598-- /Pre-release/
612−{-# INLINE_NORMAL unsafeCreateOf #-}
599+{-# INLINE_NORMAL writeNUnsafe #-}
613−unsafeCreateOf :: MonadIO m => Int -> Fold m a (MutArray a)
600+writeNUnsafe :: MonadIO m => Int -> Fold m a (MutArray a)
614−unsafeCreateOf n = Fold step initial return return
601+writeNUnsafe n = Fold step initial return return
615
602
616 where
603 where
617
604
@@ -619,81 +606,59 @@ unsafeCreateOf n = Fold step initial return return
619
606
620 step arr x = FL.Partial <$> snocUnsafe arr x
607 step arr x = FL.Partial <$> snocUnsafe arr x
621
608
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
609+-- | @writeN n@ folds a maximum of @n@ elements from the input stream to an
628-- 'Array'.
610-- 'Array'.
629--
611--
630−-- >>> createOf n = Fold.take n (MutArray.unsafeCreateOf n)
612+-- >>> writeN n = Fold.take n (MutArray.writeNUnsafe n)
631--
613--
632-- /Pre-release/
614-- /Pre-release/
633−{-# INLINE_NORMAL createOf #-}
615+{-# INLINE_NORMAL writeN #-}
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 #-}
640writeN :: MonadIO m => Int -> Fold m a (MutArray a)
616writeN :: MonadIO m => Int -> Fold m a (MutArray a)
641−writeN = createOf
617+writeN n = FL.take n $ writeNUnsafe n
642
618
643-- >>> f n = MutArray.writeAppendWith (* 2) (MutArray.pinnedNew n)
619-- >>> f n = MutArray.writeAppendWith (* 2) (MutArray.pinnedNew n)
644-- >>> writeWith n = Fold.rmapM MutArray.rightSize (f n)
620-- >>> writeWith n = Fold.rmapM MutArray.rightSize (f n)
645-- >>> writeWith n = Fold.rmapM MutArray.fromArrayStreamK (MutArray.writeChunks n)
621-- >>> writeWith n = Fold.rmapM MutArray.fromArrayStreamK (MutArray.writeChunks n)
646
622
647−-- | @createWith minCount@ folds the whole input to a single array. The array
623+-- | @writeWith minCount@ folds the whole input to a single array. The array
648-- starts at a size big enough to hold minCount elements, the size is doubled
624-- starts at a size big enough to hold minCount elements, the size is doubled
649-- every time the array needs to be grown.
625-- every time the array needs to be grown.
650--
626--
651-- /Caution! Do not use this on infinite streams./
627-- /Caution! Do not use this on infinite streams./
652--
628--
653-- /Pre-release/
629-- /Pre-release/
654−{-# INLINE_NORMAL createWith #-}
630+{-# INLINE_NORMAL writeWith #-}
655−createWith :: MonadIO m => Int -> Fold m a (MutArray a)
631+writeWith :: MonadIO m => Int -> Fold m a (MutArray a)
656-- writeWith n = FL.rmapM rightSize $ writeAppendWith (* 2) (pinnedNew n)
632-- writeWith n = FL.rmapM rightSize $ writeAppendWith (* 2) (pinnedNew n)
657−createWith elemCount = FL.rmapM extract $ FL.foldlM' step initial
633+writeWith elemCount = FL.rmapM extract $ FL.foldlM' step initial
658
634
659 where
635 where
660
636
661 initial = do
637 initial = do
662− when (elemCount < 0) $ error "createWith: elemCount is negative"
638+ when (elemCount < 0) $ error "writeWith: elemCount is negative"
663− new elemCount
639+ liftIO $ new elemCount
664
640
665 step arr@(MutArray _ start end bound) x
641 step arr@(MutArray _ start end bound) x
666 | end == bound = do
642 | end == bound = do
667 let oldSize = end - start
643 let oldSize = end - start
668 newSize = max (oldSize * 2) 1
644 newSize = max (oldSize * 2) 1
669− arr1 <- realloc newSize arr
645+ arr1 <- liftIO $ realloc newSize arr
670 snocUnsafe arr1 x
646 snocUnsafe arr1 x
671 step arr x = snocUnsafe arr x
647 step arr x = snocUnsafe arr x
672
648
673− -- extract = rightSize
649+ -- extract = liftIO . rightSize
674 extract = return
650 extract = return
675
651
676−{-# DEPRECATED writeWith "Please use createWith instead." #-}
677−{-# INLINE writeWith #-}
678−writeWith :: MonadIO m => Int -> Fold m a (MutArray a)
679−writeWith = createWith
680−
681-- | Fold the whole input to a single array.
652-- | Fold the whole input to a single array.
682--
653--
683−-- Same as 'createWith' using an initial array size of 'arrayChunkSize' bytes
654+-- Same as 'writeWith' using an initial array size of 'arrayChunkSize' bytes
684-- rounded up to the element size.
655-- rounded up to the element size.
685--
656--
686-- /Caution! Do not use this on infinite streams./
657-- /Caution! Do not use this on infinite streams./
687--
658--
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." #-}
694{-# INLINE write #-}
659{-# INLINE write #-}
695write :: MonadIO m => Fold m a (MutArray a)
660write :: MonadIO m => Fold m a (MutArray a)
696−write = create
661+write = writeWith arrayChunkSize
697
662
698-- | Create a 'MutArray' from the first @n@ elements of a stream. The
663-- | Create a 'MutArray' from the first @n@ elements of a stream. The
699-- array is allocated to size @n@, if the stream terminates before @n@
664-- array is allocated to size @n@, if the stream terminates before @n@
@@ -718,7 +683,7 @@ fromList xs = fromStream $ D.fromList xs
718{-# INLINABLE fromPureStream #-}
683{-# INLINABLE fromPureStream #-}
719fromPureStream :: MonadIO m => Stream Identity a -> m (MutArray a)
684fromPureStream :: MonadIO m => Stream Identity a -> m (MutArray a)
720fromPureStream xs =
685fromPureStream xs =
721− D.fold write $ D.morphInner (return . runIdentity) xs
686+ liftIO $ D.fold write $ D.morphInner (return . runIdentity) xs
722
687
723-------------------------------------------------------------------------------
688-------------------------------------------------------------------------------
724-- Chunking
689-- Chunking
@@ -850,7 +815,7 @@ putSliceUnsafe src srcStart dst dstStart len = liftIO $ do
850
815
851{-# INLINE clone #-}
816{-# INLINE clone #-}
852clone :: MonadIO m => MutArray a -> m (MutArray a)
817clone :: MonadIO m => MutArray a -> m (MutArray a)
853−clone src = do
818+clone src = liftIO $ do
854 let len = arrLen src
819 let len = arrLen src
855 dst <- new len
820 dst <- new len
856 putSliceUnsafe src 0 dst 0 len
821 putSliceUnsafe src 0 dst 0 len
core/src/Streamly/Internal/Data/MutArray/Stream.hs+195−11
@@ -12,7 +12,6 @@
12-- keep this as a separate module and release it.
12-- keep this as a separate module and release it.
13--
13--
14module Streamly.Internal.Data.MutArray.Stream
14module Streamly.Internal.Data.MutArray.Stream
15−{-# DEPRECATED "Please use \"Streamly.Internal.Data.MutArray\" instead." #-}
16 (
15 (
17 -- * Generation
16 -- * Generation
18 MArray.chunksOf
17 MArray.chunksOf
@@ -22,7 +21,7 @@ module Streamly.Internal.Data.MutArray.Stream
22
21
23 -- * Compaction
22 -- * Compaction
24 , packArraysChunksOf
23 , packArraysChunksOf
25− , MArray.SpliceState (..)
24+ , SpliceState (..)
26 , lpackArraysChunksOf
25 , lpackArraysChunksOf
27 , compact -- chunksCompact
26 , compact -- chunksCompact
28 , compactLE
27 , compactLE
@@ -36,12 +35,19 @@ module Streamly.Internal.Data.MutArray.Stream
36 )
35 )
37where
36where
38
37
38+#include "inline.hs"
39+#include "ArrayMacros.h"
40+
39import Control.Monad.IO.Class (MonadIO(..))
41import Control.Monad.IO.Class (MonadIO(..))
42+import Control.Monad (when)
43+import Data.Bifunctor (first)
44+import Data.Proxy (Proxy(..))
40−import Streamly.Internal.Data.Unbox (Unbox)
45+import Streamly.Internal.Data.Unbox (Unbox, sizeOf)
41import Streamly.Internal.Data.MutArray.Type (MutArray(..))
46import Streamly.Internal.Data.MutArray.Type (MutArray(..))
42import Streamly.Internal.Data.Fold.Type (Fold(..))
47import Streamly.Internal.Data.Fold.Type (Fold(..))
43import Streamly.Internal.Data.Parser (ParseError)
48import Streamly.Internal.Data.Parser (ParseError)
44import Streamly.Internal.Data.Stream.Type (Stream)
49import Streamly.Internal.Data.Stream.Type (Stream)
50+import Streamly.Internal.Data.Tuple.Strict (Tuple'(..))
45
51
46import qualified Streamly.Internal.Data.MutArray as MArray
52import qualified Streamly.Internal.Data.MutArray as MArray
47import qualified Streamly.Internal.Data.Fold.Type as FL
53import qualified Streamly.Internal.Data.Fold.Type as FL
@@ -52,6 +58,12 @@ import qualified Streamly.Internal.Data.Stream as D
52-- Compact
58-- Compact
53-------------------------------------------------------------------------------
59-------------------------------------------------------------------------------
54
60
61+data SpliceState s arr
62+ = SpliceInitial s
63+ | SpliceBuffering s arr
64+ | SpliceYielding arr (SpliceState s arr)
65+ | SpliceFinish
66+
55-- XXX This can be removed once compactLEFold/compactLE are implemented.
67-- XXX This can be removed once compactLEFold/compactLE are implemented.
56--
68--
57-- | This mutates the first array (if it has space) to append values from the
69-- | This mutates the first array (if it has space) to append values from the
@@ -66,18 +78,117 @@ import qualified Streamly.Internal.Data.Stream as D
66-- actual array size may be less than the specified chunk size.
78-- actual array size may be less than the specified chunk size.
67--
79--
68-- @since 0.7.0
80-- @since 0.7.0
69−{-# INLINE packArraysChunksOf #-}
81+{-# INLINE_NORMAL packArraysChunksOf #-}
70packArraysChunksOf :: (MonadIO m, Unbox a)
82packArraysChunksOf :: (MonadIO m, Unbox a)
71 => Int -> D.Stream m (MutArray a) -> D.Stream m (MutArray a)
83 => Int -> D.Stream m (MutArray a) -> D.Stream m (MutArray a)
72−packArraysChunksOf = MArray.compactLE
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
73
125
74-- XXX Remove this once compactLEFold is implemented
126-- XXX Remove this once compactLEFold is implemented
75-- lpackArraysChunksOf = Fold.many compactLEFold
127-- lpackArraysChunksOf = Fold.many compactLEFold
76--
128--
77−{-# INLINE lpackArraysChunksOf #-}
129+{-# INLINE_NORMAL lpackArraysChunksOf #-}
78lpackArraysChunksOf :: (MonadIO m, Unbox a)
130lpackArraysChunksOf :: (MonadIO m, Unbox a)
79 => Int -> Fold m (MutArray a) () -> Fold m (MutArray a) ()
131 => Int -> Fold m (MutArray a) () -> Fold m (MutArray a) ()
80−lpackArraysChunksOf = MArray.lCompactGE
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 ()
81
192
82-- XXX Same as compactLE, to be removed once that is implemented.
193-- XXX Same as compactLE, to be removed once that is implemented.
83--
194--
@@ -97,11 +208,48 @@ compact = packArraysChunksOf
97-- actual array size may be less than the specified chunk size.
208-- actual array size may be less than the specified chunk size.
98--
209--
99-- /Internal/
210-- /Internal/
100−{-# INLINE compactLEParserD #-}
211+{-# INLINE_NORMAL compactLEParserD #-}
101compactLEParserD ::
212compactLEParserD ::
102 forall m a. (MonadIO m, Unbox a)
213 forall m a. (MonadIO m, Unbox a)
103 => Int -> ParserD.Parser (MutArray a) m (MutArray a)
214 => Int -> ParserD.Parser (MutArray a) m (MutArray a)
104−compactLEParserD = MArray.pCompactLE
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"
105
253
106-- | Coalesce adjacent arrays in incoming stream to form bigger arrays of a
254-- | Coalesce adjacent arrays in incoming stream to form bigger arrays of a
107-- minimum specified size. Note that if all the arrays in the stream together
255-- minimum specified size. Note that if all the arrays in the stream together
@@ -110,11 +258,47 @@ compactLEParserD = MArray.pCompactLE
110-- the specified size we stop coalescing further.
258-- the specified size we stop coalescing further.
111--
259--
112-- /Internal/
260-- /Internal/
113−{-# INLINE compactGEFold #-}
261+{-# INLINE_NORMAL compactGEFold #-}
114compactGEFold ::
262compactGEFold ::
115 forall m a. (MonadIO m, Unbox a)
263 forall m a. (MonadIO m, Unbox a)
116 => Int -> FL.Fold m (MutArray a) (MutArray a)
264 => Int -> FL.Fold m (MutArray a) (MutArray a)
117−compactGEFold = MArray.fCompactGE
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"
118
302
119-- | Coalesce adjacent arrays in incoming stream to form bigger arrays of a
303-- | Coalesce adjacent arrays in incoming stream to form bigger arrays of a
120-- maximum specified size in bytes.
304-- maximum specified size in bytes.
core/src/Streamly/Internal/Data/MutArray/Type.hs+339−1178
@@ -31,57 +31,39 @@ module Streamly.Internal.Data.MutArray.Type
31 -- ** Type
31 -- ** Type
32 -- $arrayNotes
32 -- $arrayNotes
33 MutArray (..)
33 MutArray (..)
34−
34+ , MutByteArray
35− -- ** Conversion
35+ , MutableByteArray
36− -- *** Pinned and Unpinned
37 , pin
36 , pin
38 , unpin
37 , unpin
39 , isPinned
38 , isPinned
40
39
41− -- ** Casting
40+ -- -- * Constructing and Writing
42− , cast
43− , castUnsafe -- XXX unsafeCast
44− , asBytes
45− , unsafePinnedAsPtr
46− , unsafeAsPtr
47−
48 -- ** Construction
41 -- ** Construction
49− , empty
42+ , nil
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
63
43
64− -- *** Slicing
44+ -- *** Uninitialized Arrays
65− -- | Get a subarray without copying
45+ , pinnedNew
66− , getSliceUnsafe -- XXX unsafeGetSlice
46+ , pinnedNewBytes
67− , getSlice
47+ , pinnedNewAligned
68− , splitAt -- XXX should be able to express using getSlice
48+ , new
69− , breakOn
49+ , newArrayWith
70
50
71− -- *** Stream Folds
51+ -- *** From streams
72 , ArrayUnsafe (..)
52 , ArrayUnsafe (..)
73− , unsafeCreateOfWith
53+ , writeNWithUnsafe
74− , unsafeCreateOf
54+ , writeNWith
75− , unsafePinnedCreateOf
55+ , writeNUnsafe
76− , pinnedCreateOf
56+ , pinnedWriteNUnsafe
77− , createOfWith
57+ , writeN
78− , createOf
58+ , pinnedWriteN
79− , revCreateOf
59+ , pinnedWriteNAligned
80−
60+
81− , pinnedCreate
61+ , writeWith
82− , createWith
62+ , write
83− , create
63+ , pinnedWrite
84− -- , revCreate
64+
65+ , writeRevN
66+ -- , writeRev
85
67
86 -- *** From containers
68 -- *** From containers
87 , fromListN
69 , fromListN
@@ -90,18 +72,13 @@ module Streamly.Internal.Data.MutArray.Type
90 , pinnedFromList
72 , pinnedFromList
91 , fromListRevN
73 , fromListRevN
92 , fromListRev
74 , fromListRev
93− , fromStreamN
75+ , fromStreamDN
94− , fromStream
76+ , fromStreamD
95− , fromPureStreamN
96 , fromPureStream
77 , fromPureStream
97− , fromByteStr#
98− , fromPtrN
99− , fromChunksK
100− , fromChunksRealloced -- fromSmallChunks
101
78
102 -- ** Random writes
79 -- ** Random writes
103 , putIndex
80 , putIndex
104− , putIndexUnsafe -- XXX unsafePutIndex
81+ , putIndexUnsafe
105 , putIndices
82 , putIndices
106 -- , putFromThenTo
83 -- , putFromThenTo
107 -- , putFrom -- start writing at the given position
84 -- , putFrom -- start writing at the given position
@@ -109,69 +86,76 @@ module Streamly.Internal.Data.MutArray.Type
109 -- , putFromTo
86 -- , putFromTo
110 -- , putFromRev
87 -- , putFromRev
111 -- , putUptoRev
88 -- , putUptoRev
112− , modifyIndexUnsafe -- XXX unsafeModifyIndex
89+ , modifyIndexUnsafe
113 , modifyIndex
90 , modifyIndex
114 , modifyIndices
91 , modifyIndices
115 , modify
92 , modify
116 , swapIndices
93 , swapIndices
117 , unsafeSwapIndices
94 , unsafeSwapIndices
118
95
119− -- ** Reading
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.
120
100
121− -- *** Indexing
101+ -- *** Appending elements
122− , getIndex
102+ , snocWith
123− , getIndexUnsafe -- XXX unsafeGetIndex
103+ , snoc
124− -- , getFromThenTo
104+ , snocLinear
125− , getIndexRev -- getRevIndex?
105+ , snocMay
126− , indexReader
106+ , snocUnsafe
127− , indexReaderWith
128
107
129− -- *** To Streams
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
120+
121+ -- *** To containers
122+ , toStreamDWith
123+ , toStreamDRevWith
124+ , toStreamKWith
125+ , toStreamKRevWith
130 , read
126 , read
131 , readRev
127 , readRev
132− , toStreamWith
133− , toStreamRevWith
134 , toStreamK
128 , toStreamK
135− , toStreamKWith
136 , toStreamKRev
129 , toStreamKRev
137− , toStreamKRevWith
138−
139− -- *** To Containers
140 , toList
130 , toList
141
131
142− -- *** Unfolds
143 -- experimental
132 -- experimental
144 , producerWith
133 , producerWith
145 , producer
134 , producer
146
135
147− , reader
136+ -- *** Random reads
148− , readerRevWith
137+ , getIndex
149− , readerRev
138+ , getIndexUnsafe
150−
139+ , getIndices
151− -- ** Size and Capacity
140+ , getIndicesD
152− -- *** Size
141+ -- , getFromThenTo
153− , length
142+ , getIndexRev
154− , byteLength
155−
156− -- *** Capacity
157− -- , capacity
158− , byteCapacity
159− , bytesFree
160
143
161− -- *** Capacity Management
144+ -- ** Memory Management
162 , blockSize
145 , blockSize
163 , arrayChunkBytes
146 , arrayChunkBytes
164 , allocBytesToElemCount
147 , allocBytesToElemCount
165− , realloc -- this is "resize"
148+ , realloc
166− , grow
149+ , resize
167− , growExp
150+ , resizeExp
168 , rightSize
151 , rightSize
169
152
170− -- ** Folding
153+ -- ** Size
171− , foldl'
154+ , length
172− , foldr
155+ , byteLength
173− , byteCmp
156+ -- , capacity
174− , byteEq
157+ , byteCapacity
158+ , bytesFree
175
159
176 -- ** In-place Mutation Algorithms
160 -- ** In-place Mutation Algorithms
177 , strip
161 , strip
@@ -183,121 +167,62 @@ module Streamly.Internal.Data.MutArray.Type
183 , mergeBy
167 , mergeBy
184 , bubble
168 , bubble
185
169
186− -- ** Growing and Shrinking
170+ -- ** Casting
187− -- | Arrays grow only at the end, though it is possible to grow on both
171+ , cast
188− -- sides and therefore have a cons as well as snoc. But that will require
172+ , castUnsafe
189− -- both lower and upper bound in the array representation.
173+ , asBytes
190−
174+ , asPtrUnsafe
191− -- *** Appending elements
192− , snocWith
193− , snoc
194− , snocLinear
195− , snocMay
196− , snocUnsafe -- XXX unsafeSnoc
197−
198− -- *** Appending streams
199− , unsafeAppendN
200− , appendN
201− , appendWith
202− , append
203−
204− -- *** Appending arrays
205− , spliceCopy
206− , spliceWith
207− , splice
208− , spliceExp
209− , spliceUnsafe -- XXX unsafeSplice
210− -- , putSlice
211− -- , appendSlice
212− -- , appendSliceFrom
213−
214− -- ** Serialization using Unbox
215− , pokeAppend
216− , pokeAppendMay
217− , pokeSkipUnsafe -- unsafePokeSkip
218
175
219− -- ** Deserialization using Unbox
176+ -- ** Folding
220− , peekUncons
177+ , foldl'
221− , peekUnconsUnsafe -- unsafePeekUncons
178+ , foldr
222− , peekSkipUnsafe -- unsafePeekSkip
179+ , cmp
223
180
224− -- Arrays of arrays
181+ -- ** Arrays of arrays
225 -- We can add dimensionality parameter to the array type to get
182 -- We can add dimensionality parameter to the array type to get
226 -- multidimensional arrays. Multidimensional arrays would just be a
183 -- multidimensional arrays. Multidimensional arrays would just be a
227 -- convenience wrapper on top of single dimensional arrays.
184 -- convenience wrapper on top of single dimensional arrays.
228
185
229− -- ** Streams of Arrays
186+ -- | Operations dealing with multiple arrays, streams of arrays or
230− -- *** Chunk
187+ -- multidimensional array representations.
231− -- | Group a stream into arrays.
188+
189+ -- *** Construct from streams
232 , chunksOf
190 , chunksOf
233 , pinnedChunksOf
191 , pinnedChunksOf
234− , buildChunks
192+ , writeChunks
235
193
236− -- *** Split
194+ -- *** Eliminate to streams
237− -- | Split an array into slices.
195+ , flattenArrays
196+ , flattenArraysRev
197+ , fromArrayStreamK
238
198
199+ -- *** Construct from arrays
200+ -- get chunks without copying
201+ , getSliceUnsafe
202+ , getSlice
239 -- , getSlicesFromLenN
203 -- , getSlicesFromLenN
240− , splitOn
204+ , splitAt -- XXX should be able to express using getSlice
241− -- , slicesOf
205+ , breakOn
242−
206+
243− -- *** Concat
207+ -- ** Cloning arrays
244− -- | Append the arrays in a stream to form a stream of elements.
208+ , clone
245− , concatWith
209+ , pinnedClone
246− , concatRevWith
210+
247− , concat
211+ -- ** Appending arrays
248− , concatRev
212+ , spliceCopy
249−
213+ , spliceWith
250− -- *** Compact
214+ , splice
251− -- | Append the arrays in a stream to form a stream of larger arrays.
215+ , spliceExp
252− , SpliceState (..)
216+ , spliceUnsafe
253− , pCompactLE
217+ -- , putSlice
254− , pPinnedCompactLE
218+ -- , appendSlice
255− , compactLeAs
219+ -- , appendSliceFrom
256− , fCompactGE
257− , fPinnedCompactGE
258− , lCompactGE
259− , lPinnedCompactGE
260− , compactGE
261− , compactEQ
262
220
263 -- ** Utilities
221 -- ** Utilities
264 , roundUpToPower2
222 , roundUpToPower2
265 , memcpy
223 , memcpy
266 , memcmp
224 , memcmp
267 , c_memchr
225 , 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
301 )
226 )
302where
227where
303
228
@@ -308,7 +233,6 @@ where
308
233
309import Control.Monad (when, void)
234import Control.Monad (when, void)
310import Control.Monad.IO.Class (MonadIO(..))
235import Control.Monad.IO.Class (MonadIO(..))
311−import Data.Bifunctor (first)
312import Data.Bits (shiftR, (.|.), (.&.))
236import Data.Bits (shiftR, (.|.), (.&.))
313import Data.Functor.Identity (Identity(..))
237import Data.Functor.Identity (Identity(..))
314import Data.Proxy (Proxy(..))
238import Data.Proxy (Proxy(..))
@@ -317,6 +241,7 @@ import Foreign.C.Types (CSize(..), CInt(..))
317import Foreign.Ptr (plusPtr, minusPtr, nullPtr)
241import Foreign.Ptr (plusPtr, minusPtr, nullPtr)
318import Streamly.Internal.Data.MutByteArray.Type
242import Streamly.Internal.Data.MutByteArray.Type
319 ( MutByteArray(..)
243 ( MutByteArray(..)
244+ , MutableByteArray
320 , PinnedState(..)
245 , PinnedState(..)
321 , getMutableByteArray#
246 , getMutableByteArray#
322 , putSliceUnsafe
247 , putSliceUnsafe
@@ -329,23 +254,19 @@ import GHC.Base
329 , copyMutableByteArray#
254 , copyMutableByteArray#
330 )
255 )
331import GHC.Base (noinline)
256import GHC.Base (noinline)
332−import GHC.Exts (unsafeCoerce#, Addr#)
257+import GHC.Exts (unsafeCoerce#)
333import GHC.Ptr (Ptr(..))
258import GHC.Ptr (Ptr(..))
334
259
335import Streamly.Internal.Data.Fold.Type (Fold(..))
260import Streamly.Internal.Data.Fold.Type (Fold(..))
336import Streamly.Internal.Data.Producer.Type (Producer (..))
261import Streamly.Internal.Data.Producer.Type (Producer (..))
337import Streamly.Internal.Data.Stream.Type (Stream)
262import Streamly.Internal.Data.Stream.Type (Stream)
338−import Streamly.Internal.Data.Parser.Type (Parser (..))
339import Streamly.Internal.Data.StreamK.Type (StreamK)
263import Streamly.Internal.Data.StreamK.Type (StreamK)
340import Streamly.Internal.Data.SVar.Type (adaptState, defState)
264import Streamly.Internal.Data.SVar.Type (adaptState, defState)
341−import Streamly.Internal.Data.Tuple.Strict (Tuple'(..))
342import Streamly.Internal.Data.Unfold.Type (Unfold(..))
265import Streamly.Internal.Data.Unfold.Type (Unfold(..))
343import Streamly.Internal.System.IO (arrayPayloadSize, defaultChunkSize)
266import Streamly.Internal.System.IO (arrayPayloadSize, defaultChunkSize)
344
267
345import qualified Streamly.Internal.Data.Fold.Type as FL
268import qualified Streamly.Internal.Data.Fold.Type as FL
346import qualified Streamly.Internal.Data.MutByteArray.Type as Unboxed
269import 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
349import qualified Streamly.Internal.Data.Producer as Producer
270import qualified Streamly.Internal.Data.Producer as Producer
350import qualified Streamly.Internal.Data.Stream.Type as D
271import qualified Streamly.Internal.Data.Stream.Type as D
351import qualified Streamly.Internal.Data.Stream.Lift as D
272import qualified Streamly.Internal.Data.Stream.Lift as D
@@ -353,7 +274,7 @@ import qualified Streamly.Internal.Data.StreamK.Type as K
353import qualified Prelude
274import qualified Prelude
354
275
355import Prelude hiding
276import Prelude hiding
356− (Foldable(..), concat, read, unlines, splitAt, reverse, truncate)
277+ (Foldable(..), read, unlines, splitAt, reverse, truncate)
357
278
358#include "DocTestDataMutArray.hs"
279#include "DocTestDataMutArray.hs"
359
280
@@ -361,7 +282,6 @@ import Prelude hiding
361-- Foreign helpers
282-- Foreign helpers
362-------------------------------------------------------------------------------
283-------------------------------------------------------------------------------
363
284
364−-- NOTE: Have to be "ccall unsafe" so that we can pass unpinned memory to these
365foreign import ccall unsafe "string.h memcpy" c_memcpy
285foreign import ccall unsafe "string.h memcpy" c_memcpy
366 :: Ptr Word8 -> Ptr Word8 -> CSize -> IO (Ptr Word8)
286 :: Ptr Word8 -> Ptr Word8 -> CSize -> IO (Ptr Word8)
367
287
@@ -371,9 +291,6 @@ foreign import ccall unsafe "string.h memchr" c_memchr
371foreign import ccall unsafe "string.h memcmp" c_memcmp
291foreign import ccall unsafe "string.h memcmp" c_memcmp
372 :: Ptr Word8 -> Ptr Word8 -> CSize -> IO CInt
292 :: Ptr Word8 -> Ptr Word8 -> CSize -> IO CInt
373
293
374−foreign import ccall unsafe "string.h strlen" c_strlen
375− :: Ptr Word8 -> IO CSize
376−
377-- | Given an 'Unboxed' type (unused first arg) and a number of bytes, return
294-- | Given an 'Unboxed' type (unused first arg) and a number of bytes, return
378-- how many elements of that type will completely fit in those bytes.
295-- how many elements of that type will completely fit in those bytes.
379--
296--
@@ -479,8 +396,6 @@ isPinned MutArray{..} = Unboxed.isPinned arrContents
479-- we need more than that. See stg_pinnedNewAlignedByteArrayzh and
396-- we need more than that. See stg_pinnedNewAlignedByteArrayzh and
480-- allocatePinned in GHC source.
397-- allocatePinned in GHC source.
481
398
482−-- XXX Rename to emptyAlignedWith, alignSize should be first arg.
483−
484-- | @newArrayWith allocator alignment count@ allocates a new array of zero
399-- | @newArrayWith allocator alignment count@ allocates a new array of zero
485-- length and with a capacity to hold @count@ elements, using @allocator
400-- length and with a capacity to hold @count@ elements, using @allocator
486-- size alignment@ as the memory allocator function.
401-- size alignment@ as the memory allocator function.
@@ -504,25 +419,12 @@ newArrayWith alloc alignSize count = do
504 , arrBound = size
419 , arrBound = size
505 }
420 }
506
421
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." #-}
520nil ::
422nil ::
521#ifdef DEVBUILD
423#ifdef DEVBUILD
522 Unbox a =>
424 Unbox a =>
523#endif
425#endif
524 MutArray a
426 MutArray a
525−nil = empty
427+nil = MutArray Unboxed.nil 0 0 0
526
428
527{-# INLINE newBytesAs #-}
429{-# INLINE newBytesAs #-}
528newBytesAs :: MonadIO m =>
430newBytesAs :: MonadIO m =>
@@ -539,13 +441,12 @@ newBytesAs ps bytes = do
539 , arrBound = bytes
441 , arrBound = bytes
540 }
442 }
541
443
542−-- | Allocates a pinned empty array that with a reserved capacity of bytes.
444+-- | Allocates a pinned empty array that can hold 'count' items. The memory of
543−-- The memory of the array is uninitialized and the allocation is aligned as
445+-- the array is uninitialized and the allocation is aligned as per the
544−-- per the 'Unboxed' instance of the type.
446+-- 'Unboxed' instance of the type.
545--
447--
546-- /Pre-release/
448-- /Pre-release/
547{-# INLINE pinnedNewBytes #-}
449{-# INLINE pinnedNewBytes #-}
548−{-# DEPRECATED pinnedNewBytes "Please use pinnedEmptyOf with appropriate calculation" #-}
549pinnedNewBytes :: MonadIO m =>
450pinnedNewBytes :: MonadIO m =>
550#ifdef DEVBUILD
451#ifdef DEVBUILD
551 Unbox a =>
452 Unbox a =>
@@ -570,31 +471,21 @@ newAs ps =
570 (error "new: alignment is not used in unpinned arrays.")
471 (error "new: alignment is not used in unpinned arrays.")
571
472
572-- XXX can unaligned allocation be more efficient when alignment is not needed?
473-- XXX can unaligned allocation be more efficient when alignment is not needed?
573−
474+--
574−-- | Allocates a pinned array of zero length but growable to the specified
475+-- | Allocates an empty pinned array that can hold 'count' items. The memory of
575−-- capacity without reallocation.
476+-- the array is uninitialized and the allocation is aligned as per the 'Unboxed'
576−{-# INLINE pinnedEmptyOf #-}
477+-- instance of the type.
577−pinnedEmptyOf :: forall m a. (MonadIO m, Unbox a) => Int -> m (MutArray a)
478+--
578−pinnedEmptyOf = newAs Pinned
579−
580−-- XXX Deprecate in major
581−-- {-# DEPRECATED pinnedNew "Please use pinnedEmptyOf instead." #-}
582{-# INLINE pinnedNew #-}
479{-# INLINE pinnedNew #-}
583pinnedNew :: forall m a. (MonadIO m, Unbox a) => Int -> m (MutArray a)
480pinnedNew :: forall m a. (MonadIO m, Unbox a) => Int -> m (MutArray a)
584−pinnedNew = pinnedEmptyOf
481+pinnedNew = newAs Pinned
585
482
586−-- | Allocates an unpinned array of zero length but growable to the specified
483+-- | Allocates an empty unpinned array that can hold 'count' items. The memory
587−-- capacity without reallocation.
484+-- of the array is uninitialized.
588--
485--
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." #-}
595{-# INLINE new #-}
486{-# INLINE new #-}
596new :: (MonadIO m, Unbox a) => Int -> m (MutArray a)
487new :: (MonadIO m, Unbox a) => Int -> m (MutArray a)
597−new = emptyOf
488+new = newAs Unpinned
598
489
599-------------------------------------------------------------------------------
490-------------------------------------------------------------------------------
600-- Random writes
491-- Random writes
@@ -642,7 +533,7 @@ putIndices arr = FL.foldlM' step (return ())
642
533
643 where
534 where
644
535
645− step () (i, x) = putIndex i arr x
536+ step () (i, x) = liftIO (putIndex i arr x)
646
537
647-- | Modify a given index of an array using a modifier function.
538-- | Modify a given index of an array using a modifier function.
648--
539--
@@ -838,19 +729,22 @@ arrayChunkBytes = 1024
838roundDownTo :: Int -> Int -> Int
729roundDownTo :: Int -> Int -> Int
839roundDownTo elemSize size = size - (size `mod` elemSize)
730roundDownTo elemSize size = size - (size `mod` elemSize)
840
731
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+--
841-- NOTE: we are passing elemSize explicitly to avoid an Unboxed constraint.
735-- NOTE: we are passing elemSize explicitly to avoid an Unboxed constraint.
842-- Since this is not inlined Unboxed consrraint leads to dictionary passing
736-- Since this is not inlined Unboxed consrraint leads to dictionary passing
843-- which complicates some inspection tests.
737-- which complicates some inspection tests.
844--
738--
845−{-# NOINLINE reallocExplicitAs #-}
739+{-# NOINLINE reallocExplicit #-}
846−reallocExplicitAs :: PinnedState -> Int -> Int -> MutArray a -> IO (MutArray a)
740+reallocExplicit :: Int -> Int -> MutArray a -> IO (MutArray a)
847−reallocExplicitAs ps elemSize newCapacityInBytes MutArray{..} = do
741+reallocExplicit elemSize newCapacityInBytes MutArray{..} = do
848 assertM(arrEnd <= arrBound)
742 assertM(arrEnd <= arrBound)
849
743
850 -- Allocate new array
744 -- Allocate new array
851 let newCapMaxInBytes = roundUpLargeArray newCapacityInBytes
745 let newCapMaxInBytes = roundUpLargeArray newCapacityInBytes
852 contents <-
746 contents <-
853− if ps == Pinned
747+ if Unboxed.isPinned arrContents
854 then Unboxed.pinnedNew newCapMaxInBytes
748 then Unboxed.pinnedNew newCapMaxInBytes
855 else Unboxed.new newCapMaxInBytes
749 else Unboxed.new newCapMaxInBytes
856 let !(MutByteArray mbarrFrom#) = arrContents
750 let !(MutByteArray mbarrFrom#) = arrContents
@@ -875,10 +769,6 @@ reallocExplicitAs ps elemSize newCapacityInBytes MutArray{..} = do
875 , arrBound = newCapInBytes
769 , arrBound = newCapInBytes
876 }
770 }
877
771
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−
882-- | @realloc newCapacity array@ reallocates the array to the specified
772-- | @realloc newCapacity array@ reallocates the array to the specified
883-- capacity in bytes.
773-- capacity in bytes.
884--
774--
@@ -890,12 +780,7 @@ reallocExplicitAs ps elemSize newCapacityInBytes MutArray{..} = do
890-- If the original array is pinned, the newly allocated array is also pinned.
780-- If the original array is pinned, the newly allocated array is also pinned.
891{-# INLINABLE realloc #-}
781{-# INLINABLE realloc #-}
892realloc :: forall m a. (MonadIO m, Unbox a) => Int -> MutArray a -> m (MutArray a)
782realloc :: forall m a. (MonadIO m, Unbox a) => Int -> MutArray a -> m (MutArray a)
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
783+realloc bytes arr = liftIO $ reallocExplicit (SIZE_OF(a)) bytes arr
899
784
900-- | @reallocWith label capSizer minIncrBytes array@. The label is used
785-- | @reallocWith label capSizer minIncrBytes array@. The label is used
901-- in error messages and the capSizer is used to determine the capacity of the
786-- in error messages and the capSizer is used to determine the capacity of the
@@ -918,14 +803,14 @@ reallocWith label capSizer minIncrBytes arr = do
918 where
803 where
919
804
920 badSize newSize =
805 badSize newSize =
921− Prelude.concat
806+ concat
922 [ label
807 [ label
923 , ": new array size (in bytes) is less than required size "
808 , ": new array size (in bytes) is less than required size "
924 , show newSize
809 , show newSize
925 , ". Please check the sizing function passed."
810 , ". Please check the sizing function passed."
926 ]
811 ]
927
812
928−-- | @grow newCapacity array@ changes the total capacity of the array so that
813+-- | @resize newCapacity array@ changes the total capacity of the array so that
929-- it is enough to hold the specified number of elements. Nothing is done if
814-- it is enough to hold the specified number of elements. Nothing is done if
930-- the specified capacity is less than the length of the array.
815-- the specified capacity is less than the length of the array.
931--
816--
@@ -933,46 +818,34 @@ reallocWith label capSizer minIncrBytes arr = do
933-- the block size (4K).
818-- the block size (4K).
934--
819--
935-- /Pre-release/
820-- /Pre-release/
936−{-# INLINE grow #-}
821+{-# INLINE resize #-}
937−grow :: forall m a. (MonadIO m, Unbox a) =>
822+resize :: forall m a. (MonadIO m, Unbox a) =>
938 Int -> MutArray a -> m (MutArray a)
823 Int -> MutArray a -> m (MutArray a)
939−grow nElems arr@MutArray{..} = do
824+resize nElems arr@MutArray{..} = do
940 let req = SIZE_OF(a) * nElems
825 let req = SIZE_OF(a) * nElems
941− cap = arrBound - arrStart
826+ len = arrEnd - arrStart
942− if req < cap
827+ if req < len
943 then return arr
828 then return arr
944 else realloc req arr
829 else realloc req arr
945
830
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
831+-- | Like 'resize' but if the byte capacity is more than 'largeObjectThreshold'
953−-- 'largeObjectThreshold' then it is rounded up to the closest power of 2.
832+-- then it is rounded up to the closest power of 2.
954--
833--
955-- /Pre-release/
834-- /Pre-release/
956−{-# INLINE growExp #-}
835+{-# INLINE resizeExp #-}
957−growExp :: forall m a. (MonadIO m, Unbox a) =>
836+resizeExp :: forall m a. (MonadIO m, Unbox a) =>
958 Int -> MutArray a -> m (MutArray a)
837 Int -> MutArray a -> m (MutArray a)
959−growExp nElems arr@MutArray{..} = do
838+resizeExp nElems arr@MutArray{..} = do
960 let req = roundUpLargeArray (SIZE_OF(a) * nElems)
839 let req = roundUpLargeArray (SIZE_OF(a) * nElems)
961 req1 =
840 req1 =
962 if req > largeObjectThreshold
841 if req > largeObjectThreshold
963 then roundUpToPower2 req
842 then roundUpToPower2 req
964 else req
843 else req
965− cap = arrBound - arrStart
844+ len = arrEnd - arrStart
966− if req1 < cap
845+ if req1 < len
967 then return arr
846 then return arr
968 else realloc req1 arr
847 else realloc req1 arr
969
848
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−
976-- | Resize the allocated memory to drop any reserved free space at the end of
849-- | Resize the allocated memory to drop any reserved free space at the end of
977-- the array and reallocate it to reduce wastage.
850-- the array and reallocate it to reduce wastage.
978--
851--
@@ -1037,7 +910,7 @@ snocUnsafe arr@MutArray{..} = snocNewEnd (INDEX_NEXT(arrEnd,a)) arr
1037{-# INLINE snocMay #-}
910{-# INLINE snocMay #-}
1038snocMay :: forall m a. (MonadIO m, Unbox a) =>
911snocMay :: forall m a. (MonadIO m, Unbox a) =>
1039 MutArray a -> a -> m (Maybe (MutArray a))
912 MutArray a -> a -> m (Maybe (MutArray a))
1040−snocMay arr@MutArray{..} x = do
913+snocMay arr@MutArray{..} x = liftIO $ do
1041 let newEnd = INDEX_NEXT(arrEnd,a)
914 let newEnd = INDEX_NEXT(arrEnd,a)
1042 if newEnd <= arrBound
915 if newEnd <= arrBound
1043 then Just <$> snocNewEnd newEnd arr x
916 then Just <$> snocNewEnd newEnd arr x
@@ -1051,7 +924,7 @@ snocWithRealloc :: forall m a. (MonadIO m, Unbox a) =>
1051 -> a
924 -> a
1052 -> m (MutArray a)
925 -> m (MutArray a)
1053snocWithRealloc sizer arr x = do
926snocWithRealloc sizer arr x = do
1054− arr1 <- reallocWith "snocWith" sizer (SIZE_OF(a)) arr
927+ arr1 <- liftIO $ reallocWith "snocWith" sizer (SIZE_OF(a)) arr
1055 snocUnsafe arr1 x
928 snocUnsafe arr1 x
1056
929
1057-- | @snocWith sizer arr elem@ mutates @arr@ to append @elem@. The length of
930-- | @snocWith sizer arr elem@ mutates @arr@ to append @elem@. The length of
@@ -1073,7 +946,7 @@ snocWith :: forall m a. (MonadIO m, Unbox a) =>
1073 -> MutArray a
946 -> MutArray a
1074 -> a
947 -> a
1075 -> m (MutArray a)
948 -> m (MutArray a)
1076−snocWith allocSize arr x = do
949+snocWith allocSize arr x = liftIO $ do
1077 let newEnd = INDEX_NEXT(arrEnd arr,a)
950 let newEnd = INDEX_NEXT(arrEnd arr,a)
1078 if newEnd <= arrBound arr
951 if newEnd <= arrBound arr
1079 then snocNewEnd newEnd arr x
952 then snocNewEnd newEnd arr x
@@ -1117,126 +990,6 @@ snoc = snocWith f
1117 then oldSize * 2
990 then oldSize * 2
1118 else roundUpToPower2 oldSize * 2
991 else roundUpToPower2 oldSize * 2
1119
992
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−
1240-------------------------------------------------------------------------------
993-------------------------------------------------------------------------------
1241-- Random reads
994-- Random reads
1242-------------------------------------------------------------------------------
995-------------------------------------------------------------------------------
@@ -1279,10 +1032,15 @@ getIndexRev i MutArray{..} = do
1279data GetIndicesState contents start end st =
1032data GetIndicesState contents start end st =
1280 GetIndicesState contents start end st
1033 GetIndicesState contents start end st
1281
1034
1282−{-# INLINE indexReaderWith #-}
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 #-}
1283−indexReaderWith :: (Monad m, Unbox a) =>
1041+getIndicesD :: (Monad m, Unbox a) =>
1284 (forall b. IO b -> m b) -> D.Stream m Int -> Unfold m (MutArray a) a
1042 (forall b. IO b -> m b) -> D.Stream m Int -> Unfold m (MutArray a) a
1285−indexReaderWith liftio (D.Stream stepi sti) = Unfold step inject
1043+getIndicesD liftio (D.Stream stepi sti) = Unfold step inject
1286
1044
1287 where
1045 where
1288
1046
@@ -1301,26 +1059,9 @@ indexReaderWith liftio (D.Stream stepi sti) = Unfold step inject
1301 D.Skip s -> return $ D.Skip (GetIndicesState contents start end s)
1059 D.Skip s -> return $ D.Skip (GetIndicesState contents start end s)
1302 D.Stop -> return D.Stop
1060 D.Stop -> return D.Stop
1303
1061
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." #-}
1321{-# INLINE getIndices #-}
1062{-# INLINE getIndices #-}
1322getIndices :: (MonadIO m, Unbox a) => Stream m Int -> Unfold m (MutArray a) a
1063getIndices :: (MonadIO m, Unbox a) => Stream m Int -> Unfold m (MutArray a) a
1323−getIndices = indexReader
1064+getIndices = getIndicesD liftIO
1324
1065
1325-------------------------------------------------------------------------------
1066-------------------------------------------------------------------------------
1326-- Subarrays
1067-- Subarrays
@@ -1615,7 +1356,7 @@ chunksOfAs ps n (D.Stream step state) =
1615--
1356--
1616-- Same as the following but may be more efficient:
1357-- Same as the following but may be more efficient:
1617--
1358--
1618−-- >>> chunksOf n = Stream.foldMany (MutArray.createOf n)
1359+-- >>> chunksOf n = Stream.foldMany (MutArray.writeN n)
1619--
1360--
1620-- /Pre-release/
1361-- /Pre-release/
1621{-# INLINE_NORMAL chunksOf #-}
1362{-# INLINE_NORMAL chunksOf #-}
@@ -1624,29 +1365,17 @@ chunksOf :: forall m a. (MonadIO m, Unbox a)
1624-- XXX the idiomatic implementation leads to large regression in the D.reverse'
1365-- XXX the idiomatic implementation leads to large regression in the D.reverse'
1625-- benchmark. It seems it has difficulty producing optimized code when
1366-- benchmark. It seems it has difficulty producing optimized code when
1626-- converting to StreamK. Investigate GHC optimizations.
1367-- converting to StreamK. Investigate GHC optimizations.
1627−-- chunksOf n = D.foldMany (createOf n)
1368+-- chunksOf n = D.foldMany (writeN n)
1628chunksOf = chunksOfAs Unpinned
1369chunksOf = chunksOfAs Unpinned
1629
1370
1630-- | Like 'chunksOf' but creates pinned arrays.
1371-- | Like 'chunksOf' but creates pinned arrays.
1631{-# INLINE_NORMAL pinnedChunksOf #-}
1372{-# INLINE_NORMAL pinnedChunksOf #-}
1632pinnedChunksOf :: forall m a. (MonadIO m, Unbox a)
1373pinnedChunksOf :: forall m a. (MonadIO m, Unbox a)
1633 => Int -> D.Stream m a -> D.Stream m (MutArray a)
1374 => Int -> D.Stream m a -> D.Stream m (MutArray a)
1634−-- pinnedChunksOf n = D.foldMany (pinnedCreateOf n)
1375+-- pinnedChunksOf n = D.foldMany (pinnedWriteN n)
1635pinnedChunksOf = chunksOfAs Pinned
1376pinnedChunksOf = chunksOfAs Pinned
1636
1377
1637−-- | When we are buffering a stream of unknown size into an array we do not
1378+-- XXX This should take a PinnedState
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−
1650-- XXX buffer to a list instead?
1379-- XXX buffer to a list instead?
1651-- | Buffer the stream into arrays in memory.
1380-- | Buffer the stream into arrays in memory.
1652{-# INLINE arrayStreamKFromStreamDAs #-}
1381{-# INLINE arrayStreamKFromStreamDAs #-}
@@ -1664,10 +1393,16 @@ data FlattenState s contents a =
1664 OuterLoop s
1393 OuterLoop s
1665 | InnerLoop s contents !Int !Int
1394 | InnerLoop s contents !Int !Int
1666
1395
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+--
1667−{-# INLINE_NORMAL concatWith #-}
1402+{-# INLINE_NORMAL flattenArrays #-}
1668−concatWith :: forall m a. (Monad m, Unbox a)
1403+flattenArrays :: forall m a. (MonadIO m, Unbox a)
1669− => (forall b. IO b -> m b) -> D.Stream m (MutArray a) -> D.Stream m a
1404+ => D.Stream m (MutArray a) -> D.Stream m a
1670−concatWith liftio (D.Stream step state) = D.Stream step' (OuterLoop state)
1405+flattenArrays (D.Stream step state) = D.Stream step' (OuterLoop state)
1671
1406
1672 where
1407 where
1673
1408
@@ -1684,30 +1419,19 @@ concatWith liftio (D.Stream step state) = D.Stream step' (OuterLoop state)
1684 return $ D.Skip $ OuterLoop st
1419 return $ D.Skip $ OuterLoop st
1685
1420
1686 step' _ (InnerLoop st contents p end) = do
1421 step' _ (InnerLoop st contents p end) = do
1687− x <- liftio $ peekAt p contents
1422+ x <- liftIO $ peekAt p contents
1688 return $ D.Yield x (InnerLoop st contents (INDEX_NEXT(p,a)) end)
1423 return $ D.Yield x (InnerLoop st contents (INDEX_NEXT(p,a)) end)
1689
1424
1690−-- | Use the "reader" unfold instead.
1425+-- | Use the "readerRev" unfold instead.
1691--
1426--
1692−-- @concat = unfoldMany reader@
1427+-- @flattenArrays = unfoldMany readerRev@
1693--
1428--
1694-- We can try this if there are any fusion issues in the unfold.
1429-- We can try this if there are any fusion issues in the unfold.
1695--
1430--
1696−{-# INLINE_NORMAL concat #-}
1431+{-# INLINE_NORMAL flattenArraysRev #-}
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)
1432+flattenArraysRev :: forall m a. (MonadIO m, Unbox a)
1704 => D.Stream m (MutArray a) -> D.Stream m a
1433 => D.Stream m (MutArray a) -> D.Stream m a
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)
1434+flattenArraysRev (D.Stream step state) = D.Stream step' (OuterLoop state)
1711
1435
1712 where
1436 where
1713
1437
@@ -1725,27 +1449,10 @@ concatRevWith liftio (D.Stream step state) = D.Stream step' (OuterLoop state)
1725 return $ D.Skip $ OuterLoop st
1449 return $ D.Skip $ OuterLoop st
1726
1450
1727 step' _ (InnerLoop st contents p start) = do
1451 step' _ (InnerLoop st contents p start) = do
1728− x <- liftio $ peekAt p contents
1452+ x <- liftIO $ peekAt p contents
1729 let cur = INDEX_PREV(p,a)
1453 let cur = INDEX_PREV(p,a)
1730 return $ D.Yield x (InnerLoop st contents cur start)
1454 return $ D.Yield x (InnerLoop st contents cur start)
1731
1455
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−
1749-------------------------------------------------------------------------------
1456-------------------------------------------------------------------------------
1750-- Unfolds
1457-- Unfolds
1751-------------------------------------------------------------------------------
1458-------------------------------------------------------------------------------
@@ -1863,11 +1570,11 @@ toList MutArray{..} = liftIO $ go arrStart
1863 x <- peekAt p arrContents
1570 x <- peekAt p arrContents
1864 (:) x <$> go (INDEX_NEXT(p,a))
1571 (:) x <$> go (INDEX_NEXT(p,a))
1865
1572
1866−{-# INLINE_NORMAL toStreamWith #-}
1573+{-# INLINE_NORMAL toStreamDWith #-}
1867−toStreamWith ::
1574+toStreamDWith ::
1868 forall m a. (Monad m, Unbox a)
1575 forall m a. (Monad m, Unbox a)
1869 => (forall b. IO b -> m b) -> MutArray a -> D.Stream m a
1576 => (forall b. IO b -> m b) -> MutArray a -> D.Stream m a
1870−toStreamWith liftio MutArray{..} = D.Stream step arrStart
1577+toStreamDWith liftio MutArray{..} = D.Stream step arrStart
1871
1578
1872 where
1579 where
1873
1580
@@ -1883,7 +1590,7 @@ toStreamWith liftio MutArray{..} = D.Stream step arrStart
1883--
1590--
1884{-# INLINE_NORMAL read #-}
1591{-# INLINE_NORMAL read #-}
1885read :: forall m a. (MonadIO m, Unbox a) => MutArray a -> D.Stream m a
1592read :: forall m a. (MonadIO m, Unbox a) => MutArray a -> D.Stream m a
1886−read = toStreamWith liftIO
1593+read = toStreamDWith liftIO
1887
1594
1888{-# INLINE toStreamKWith #-}
1595{-# INLINE toStreamKWith #-}
1889toStreamKWith ::
1596toStreamKWith ::
@@ -1902,11 +1609,11 @@ toStreamKWith liftio MutArray{..} = go arrStart
1902toStreamK :: forall m a. (MonadIO m, Unbox a) => MutArray a -> StreamK m a
1609toStreamK :: forall m a. (MonadIO m, Unbox a) => MutArray a -> StreamK m a
1903toStreamK = toStreamKWith liftIO
1610toStreamK = toStreamKWith liftIO
1904
1611
1905−{-# INLINE_NORMAL toStreamRevWith #-}
1612+{-# INLINE_NORMAL toStreamDRevWith #-}
1906−toStreamRevWith ::
1613+toStreamDRevWith ::
1907 forall m a. (Monad m, Unbox a)
1614 forall m a. (Monad m, Unbox a)
1908 => (forall b. IO b -> m b) -> MutArray a -> D.Stream m a
1615 => (forall b. IO b -> m b) -> MutArray a -> D.Stream m a
1909−toStreamRevWith liftio MutArray{..} =
1616+toStreamDRevWith liftio MutArray{..} =
1910 let p = INDEX_PREV(arrEnd,a)
1617 let p = INDEX_PREV(arrEnd,a)
1911 in D.Stream step p
1618 in D.Stream step p
1912
1619
@@ -1924,7 +1631,7 @@ toStreamRevWith liftio MutArray{..} =
1924--
1631--
1925{-# INLINE_NORMAL readRev #-}
1632{-# INLINE_NORMAL readRev #-}
1926readRev :: forall m a. (MonadIO m, Unbox a) => MutArray a -> D.Stream m a
1633readRev :: forall m a. (MonadIO m, Unbox a) => MutArray a -> D.Stream m a
1927−readRev = toStreamRevWith liftIO
1634+readRev = toStreamDRevWith liftIO
1928
1635
1929{-# INLINE toStreamKRevWith #-}
1636{-# INLINE toStreamKRevWith #-}
1930toStreamKRevWith ::
1637toStreamKRevWith ::
@@ -1973,7 +1680,7 @@ foldr f z arr = D.foldr f z $ read arr
1973
1680
1974-- XXX Keep the bound intact to not lose any free space? Perf impact?
1681-- XXX Keep the bound intact to not lose any free space? Perf impact?
1975
1682
1976−-- | @unsafeAppendN n arr@ appends up to @n@ input items to the supplied
1683+-- | @writeAppendNUnsafe n arr@ appends up to @n@ input items to the supplied
1977-- array.
1684-- array.
1978--
1685--
1979-- Unsafe: Do not drive the fold beyond @n@ elements, it will lead to memory
1686-- Unsafe: Do not drive the fold beyond @n@ elements, it will lead to memory
@@ -1982,12 +1689,13 @@ foldr f z arr = D.foldr f z $ read arr
1982-- Any free space left in the array after appending @n@ elements is lost.
1689-- Any free space left in the array after appending @n@ elements is lost.
1983--
1690--
1984-- /Internal/
1691-- /Internal/
1985−{-# INLINE_NORMAL unsafeAppendN #-}
1692+{-# INLINE_NORMAL writeAppendNUnsafe #-}
1986−unsafeAppendN :: forall m a. (MonadIO m, Unbox a) =>
1693+writeAppendNUnsafe :: forall m a. (MonadIO m, Unbox a) =>
1987 Int
1694 Int
1988 -> m (MutArray a)
1695 -> m (MutArray a)
1989 -> Fold m a (MutArray a)
1696 -> Fold m a (MutArray a)
1697+writeAppendNUnsafe n action =
1990−unsafeAppendN n action = fmap fromArrayUnsafe $ FL.foldlM' step initial
1698+ fmap fromArrayUnsafe $ FL.foldlM' step initial
1991
1699
1992 where
1700 where
1993
1701
@@ -2000,7 +1708,7 @@ unsafeAppendN n action = fmap fromArrayUnsafe $ FL.foldlM' step initial
2000 -- otherwise we lose that space.
1708 -- otherwise we lose that space.
2001 arr1 <-
1709 arr1 <-
2002 if free < needed
1710 if free < needed
2003− then noinline reallocWith "unsafeAppendN" (+ needed) needed arr
1711+ then noinline reallocWith "writeAppendNUnsafeWith" (+ needed) needed arr
2004 else return arr
1712 else return arr
2005 return $ toArrayUnsafe arr1
1713 return $ toArrayUnsafe arr1
2006
1714
@@ -2008,51 +1716,30 @@ unsafeAppendN n action = fmap fromArrayUnsafe $ FL.foldlM' step initial
2008 liftIO $ pokeAt end contents x
1716 liftIO $ pokeAt end contents x
2009 return $ ArrayUnsafe contents start (INDEX_NEXT(end,a))
1717 return $ ArrayUnsafe contents start (INDEX_NEXT(end,a))
2010
1718
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−
2019-- | Append @n@ elements to an existing array. Any free space left in the array
1719-- | Append @n@ elements to an existing array. Any free space left in the array
2020-- after appending @n@ elements is lost.
1720-- after appending @n@ elements is lost.
2021--
1721--
2022−-- >>> appendN n initial = Fold.take n (MutArray.unsafeAppendN n initial)
1722+-- >>> writeAppendN n initial = Fold.take n (MutArray.writeAppendNUnsafe n initial)
2023--
1723--
2024−{-# INLINE_NORMAL appendN #-}
1724+{-# INLINE_NORMAL writeAppendN #-}
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 #-}
2032writeAppendN :: forall m a. (MonadIO m, Unbox a) =>
1725writeAppendN :: forall m a. (MonadIO m, Unbox a) =>
2033 Int -> m (MutArray a) -> Fold m a (MutArray a)
1726 Int -> m (MutArray a) -> Fold m a (MutArray a)
2034−writeAppendN = appendN
1727+writeAppendN n initial = FL.take n (writeAppendNUnsafe n initial)
2035
1728
2036−-- | @appendWith realloc action@ mutates the array generated by @action@ to
1729+-- | @writeAppendWith realloc action@ mutates the array generated by @action@ to
2037-- append the input stream. If there is no reserved space available in the
1730-- append the input stream. If there is no reserved space available in the
2038-- array it is reallocated to a size in bytes determined by @realloc oldSize@,
1731-- array it is reallocated to a size in bytes determined by @realloc oldSize@,
2039-- where @oldSize@ is the current size of the array in bytes.
1732-- where @oldSize@ is the current size of the array in bytes.
2040--
1733--
2041-- Note that the returned array may be a mutated version of original array.
1734-- Note that the returned array may be a mutated version of original array.
2042--
1735--
2043−-- >>> appendWith sizer = Fold.foldlM' (MutArray.snocWith sizer)
1736+-- >>> writeAppendWith sizer = Fold.foldlM' (MutArray.snocWith sizer)
2044--
1737--
2045-- /Pre-release/
1738-- /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." #-}
2052{-# INLINE writeAppendWith #-}
1739{-# INLINE writeAppendWith #-}
2053writeAppendWith :: forall m a. (MonadIO m, Unbox a) =>
1740writeAppendWith :: forall m a. (MonadIO m, Unbox a) =>
2054 (Int -> Int) -> m (MutArray a) -> Fold m a (MutArray a)
1741 (Int -> Int) -> m (MutArray a) -> Fold m a (MutArray a)
2055−writeAppendWith = appendWith
1742+writeAppendWith sizer = FL.foldlM' (snocWith sizer)
2056
1743
2057-- | @append action@ mutates the array generated by @action@ to append the
1744-- | @append action@ mutates the array generated by @action@ to append the
2058-- input stream. If there is no reserved space available in the array it is
1745-- input stream. If there is no reserved space available in the array it is
@@ -2060,33 +1747,26 @@ writeAppendWith = appendWith
2060--
1747--
2061-- Note that the returned array may be a mutated version of original array.
1748-- Note that the returned array may be a mutated version of original array.
2062--
1749--
2063−-- >>> append = MutArray.appendWith (* 2)
1750+-- >>> writeAppend = MutArray.writeAppendWith (* 2)
2064--
1751--
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." #-}
2072{-# INLINE writeAppend #-}
1752{-# INLINE writeAppend #-}
2073writeAppend :: forall m a. (MonadIO m, Unbox a) =>
1753writeAppend :: forall m a. (MonadIO m, Unbox a) =>
2074 m (MutArray a) -> Fold m a (MutArray a)
1754 m (MutArray a) -> Fold m a (MutArray a)
2075−writeAppend = append
1755+writeAppend = writeAppendWith (* 2)
2076
1756
2077-- XXX We can carry bound as well in the state to make sure we do not lose the
1757-- XXX We can carry bound as well in the state to make sure we do not lose the
2078-- remaining capacity. Need to check perf impact.
1758-- remaining capacity. Need to check perf impact.
2079--
1759--
2080−-- | Like 'unsafeCreateOf' but takes a new array allocator @alloc size@
1760+-- | Like 'writeNUnsafe' but takes a new array allocator @alloc size@ function
2081−-- function as argument.
1761+-- as argument.
2082--
1762--
2083−-- >>> unsafeCreateOfWith alloc n = MutArray.unsafeAppendN (alloc n) n
1763+-- >>> writeNWithUnsafe alloc n = MutArray.writeAppendNUnsafe (alloc n) n
2084--
1764--
2085-- /Pre-release/
1765-- /Pre-release/
2086−{-# INLINE_NORMAL unsafeCreateOfWith #-}
1766+{-# INLINE_NORMAL writeNWithUnsafe #-}
2087−unsafeCreateOfWith :: forall m a. (MonadIO m, Unbox a)
1767+writeNWithUnsafe :: forall m a. (MonadIO m, Unbox a)
2088 => (Int -> m (MutArray a)) -> Int -> Fold m a (MutArray a)
1768 => (Int -> m (MutArray a)) -> Int -> Fold m a (MutArray a)
2089−unsafeCreateOfWith alloc n = fromArrayUnsafe <$> FL.foldlM' step initial
1769+writeNWithUnsafe alloc n = fromArrayUnsafe <$> FL.foldlM' step initial
2090
1770
2091 where
1771 where
2092
1772
@@ -2097,64 +1777,40 @@ unsafeCreateOfWith alloc n = fromArrayUnsafe <$> FL.foldlM' step initial
2097 return
1777 return
2098 $ ArrayUnsafe contents start (INDEX_NEXT(end,a))
1778 $ ArrayUnsafe contents start (INDEX_NEXT(end,a))
2099
1779
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−
2106{-# INLINE_NORMAL writeNUnsafeAs #-}
1780{-# INLINE_NORMAL writeNUnsafeAs #-}
2107writeNUnsafeAs :: forall m a. (MonadIO m, Unbox a)
1781writeNUnsafeAs :: forall m a. (MonadIO m, Unbox a)
2108 => PinnedState -> Int -> Fold m a (MutArray a)
1782 => PinnedState -> Int -> Fold m a (MutArray a)
2109−writeNUnsafeAs ps = unsafeCreateOfWith (newAs ps)
1783+writeNUnsafeAs ps = writeNWithUnsafe (newAs ps)
2110
1784
2111−-- | Like 'createOf' but does not check the array bounds when writing. The fold
1785+-- | Like 'writeN' but does not check the array bounds when writing. The fold
2112-- driver must not call the step function more than 'n' times otherwise it will
1786-- driver must not call the step function more than 'n' times otherwise it will
2113-- corrupt the memory and crash. This function exists mainly because any
1787-- corrupt the memory and crash. This function exists mainly because any
2114-- conditional in the step function blocks fusion causing 10x performance
1788-- conditional in the step function blocks fusion causing 10x performance
2115-- slowdown.
1789-- slowdown.
2116--
1790--
2117−-- >>> unsafeCreateOf = MutArray.unsafeCreateOfWith MutArray.emptyOf
1791+-- >>> writeNUnsafe = MutArray.writeNWithUnsafe MutArray.new
2118--
1792--
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 #-}
1793+{-# INLINE_NORMAL writeNUnsafe #-}
2126writeNUnsafe :: forall m a. (MonadIO m, Unbox a)
1794writeNUnsafe :: forall m a. (MonadIO m, Unbox a)
2127 => Int -> Fold m a (MutArray a)
1795 => Int -> Fold m a (MutArray a)
2128−writeNUnsafe = unsafeCreateOf
1796+writeNUnsafe = writeNUnsafeAs Unpinned
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
2135
1797
2136−{-# DEPRECATED pinnedWriteNUnsafe "Please use unsafePinnedCreateOf instead." #-}
1798+-- | Like 'writeNUnsafe' but creates a pinned array.
2137−{-# INLINE pinnedWriteNUnsafe #-}
1799+{-# INLINE_NORMAL pinnedWriteNUnsafe #-}
2138pinnedWriteNUnsafe :: forall m a. (MonadIO m, Unbox a)
1800pinnedWriteNUnsafe :: forall m a. (MonadIO m, Unbox a)
2139 => Int -> Fold m a (MutArray a)
1801 => Int -> Fold m a (MutArray a)
2140−pinnedWriteNUnsafe = unsafePinnedCreateOf
1802+pinnedWriteNUnsafe = writeNUnsafeAs Pinned
2141
1803
2142−-- | @createOfWith alloc n@ folds a maximum of @n@ elements into an array
1804+-- | @writeNWith alloc n@ folds a maximum of @n@ elements into an array
2143-- allocated using the @alloc@ function.
1805-- allocated using the @alloc@ function.
2144--
1806--
2145−-- >>> createOfWith alloc n = Fold.take n (MutArray.unsafeCreateOfWith alloc n)
1807+-- >>> writeNWith alloc n = Fold.take n (MutArray.writeNWithUnsafe alloc n)
2146−-- >>> createOfWith alloc n = MutArray.appendN (alloc n) n
1808+-- >>> writeNWith alloc n = MutArray.writeAppendN (alloc n) n
2147--
1809--
2148−{-# INLINE_NORMAL createOfWith #-}
1810+{-# INLINE_NORMAL writeNWith #-}
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 #-}
2155writeNWith :: forall m a. (MonadIO m, Unbox a)
1811writeNWith :: forall m a. (MonadIO m, Unbox a)
2156 => (Int -> m (MutArray a)) -> Int -> Fold m a (MutArray a)
1812 => (Int -> m (MutArray a)) -> Int -> Fold m a (MutArray a)
2157−writeNWith = createOfWith
1813+writeNWith alloc n = FL.take n (writeNWithUnsafe alloc n)
2158
1814
2159{-# INLINE_NORMAL writeNAs #-}
1815{-# INLINE_NORMAL writeNAs #-}
2160writeNAs ::
1816writeNAs ::
@@ -2162,42 +1818,28 @@ writeNAs ::
2162 => PinnedState
1818 => PinnedState
2163 -> Int
1819 -> Int
2164 -> Fold m a (MutArray a)
1820 -> Fold m a (MutArray a)
2165−writeNAs ps = createOfWith (newAs ps)
1821+writeNAs ps = writeNWith (newAs ps)
2166
1822
2167−-- | @createOf n@ folds a maximum of @n@ elements from the input stream to an
1823+-- | @writeN n@ folds a maximum of @n@ elements from the input stream to an
2168-- 'MutArray'.
1824-- 'MutArray'.
2169--
1825--
2170−-- >>> createOf = MutArray.createOfWith MutArray.new
1826+-- >>> writeN = MutArray.writeNWith MutArray.new
2171−-- >>> createOf n = Fold.take n (MutArray.unsafeCreateOf n)
1827+-- >>> writeN n = Fold.take n (MutArray.writeNUnsafe n)
2172−-- >>> createOf n = MutArray.appendN n (MutArray.emptyOf n)
1828+-- >>> writeN n = MutArray.writeAppendN n (MutArray.new n)
2173--
1829--
2174−{-# INLINE_NORMAL createOf #-}
1830+{-# INLINE_NORMAL writeN #-}
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 #-}
2181writeN :: forall m a. (MonadIO m, Unbox a) => Int -> Fold m a (MutArray a)
1831writeN :: forall m a. (MonadIO m, Unbox a) => Int -> Fold m a (MutArray a)
2182−writeN = createOf
1832+writeN = writeNAs Unpinned
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
2191
1833
2192−{-# DEPRECATED pinnedWriteN "Please use pinnedCreateOf instead." #-}
1834+-- | Like 'writeN' but creates a pinned array.
2193−{-# INLINE pinnedWriteN #-}
1835+{-# INLINE_NORMAL pinnedWriteN #-}
2194pinnedWriteN ::
1836pinnedWriteN ::
2195 forall m a. (MonadIO m, Unbox a)
1837 forall m a. (MonadIO m, Unbox a)
2196 => Int
1838 => Int
2197 -> Fold m a (MutArray a)
1839 -> Fold m a (MutArray a)
2198−pinnedWriteN = pinnedCreateOf
1840+pinnedWriteN = writeNAs Pinned
2199
1841
2200−-- | Like unsafeCreateOfWith but writes the array in reverse order.
1842+-- | Like writeNWithUnsafe but writes the array in reverse order.
2201--
1843--
2202-- /Internal/
1844-- /Internal/
2203{-# INLINE_NORMAL writeRevNWithUnsafe #-}
1845{-# INLINE_NORMAL writeRevNWithUnsafe #-}
@@ -2218,7 +1860,7 @@ writeRevNWithUnsafe alloc n = fromArrayUnsafe <$> FL.foldlM' step initial
2218 return
1860 return
2219 $ ArrayUnsafe contents ptr end
1861 $ ArrayUnsafe contents ptr end
2220
1862
2221−-- | Like createOfWith but writes the array in reverse order.
1863+-- | Like writeNWith but writes the array in reverse order.
2222--
1864--
2223-- /Internal/
1865-- /Internal/
2224{-# INLINE_NORMAL writeRevNWith #-}
1866{-# INLINE_NORMAL writeRevNWith #-}
@@ -2226,36 +1868,31 @@ writeRevNWith :: forall m a. (MonadIO m, Unbox a)
2226 => (Int -> m (MutArray a)) -> Int -> Fold m a (MutArray a)
1868 => (Int -> m (MutArray a)) -> Int -> Fold m a (MutArray a)
2227writeRevNWith alloc n = FL.take n (writeRevNWithUnsafe alloc n)
1869writeRevNWith alloc n = FL.take n (writeRevNWithUnsafe alloc n)
2228
1870
2229−-- | Like 'createOf' but writes the array in reverse order.
1871+-- | Like writeN but writes the array in reverse order.
2230--
1872--
2231-- /Pre-release/
1873-- /Pre-release/
2232−{-# INLINE_NORMAL revCreateOf #-}
1874+{-# INLINE_NORMAL writeRevN #-}
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 #-}
2238writeRevN :: forall m a. (MonadIO m, Unbox a) => Int -> Fold m a (MutArray a)
1875writeRevN :: forall m a. (MonadIO m, Unbox a) => Int -> Fold m a (MutArray a)
2239−writeRevN = revCreateOf
1876+writeRevN = writeRevNWith new
2240
1877
2241-- | @pinnedWriteNAligned align n@ folds a maximum of @n@ elements from the
1878-- | @pinnedWriteNAligned align n@ folds a maximum of @n@ elements from the
2242-- input stream to a 'MutArray' aligned to the given size.
1879-- input stream to a 'MutArray' aligned to the given size.
2243--
1880--
2244−-- >>> pinnedWriteNAligned align = MutArray.createOfWith (MutArray.pinnedNewAligned align)
1881+-- >>> pinnedWriteNAligned align = MutArray.writeNWith (MutArray.pinnedNewAligned align)
2245−-- >>> pinnedWriteNAligned align n = MutArray.appendN n (MutArray.pinnedNewAligned align n)
1882+-- >>> pinnedWriteNAligned align n = MutArray.writeAppendN n (MutArray.pinnedNewAligned align n)
2246--
1883--
2247-- /Pre-release/
1884-- /Pre-release/
2248--
1885--
2249{-# INLINE_NORMAL pinnedWriteNAligned #-}
1886{-# INLINE_NORMAL pinnedWriteNAligned #-}
2250pinnedWriteNAligned :: forall m a. (MonadIO m, Unbox a)
1887pinnedWriteNAligned :: forall m a. (MonadIO m, Unbox a)
2251 => Int -> Int -> Fold m a (MutArray a)
1888 => Int -> Int -> Fold m a (MutArray a)
2252−pinnedWriteNAligned align = createOfWith (pinnedNewAligned align)
1889+pinnedWriteNAligned align = writeNWith (pinnedNewAligned align)
2253
1890
2254-- XXX Buffer to a list instead?
1891-- XXX Buffer to a list instead?
2255
1892
2256-- | Buffer a stream into a stream of arrays.
1893-- | Buffer a stream into a stream of arrays.
2257--
1894--
2258−-- >>> buildChunks n = Fold.many (MutArray.createOf n) Fold.toStreamK
1895+-- >>> writeChunks n = Fold.many (MutArray.writeN n) Fold.toStreamK
2259--
1896--
2260-- Breaking an array into an array stream can be useful to consume a large
1897-- Breaking an array into an array stream can be useful to consume a large
2261-- array sequentially such that memory of the array is released incrementatlly.
1898-- array sequentially such that memory of the array is released incrementatlly.
@@ -2264,43 +1901,37 @@ pinnedWriteNAligned align = createOfWith (pinnedNewAligned align)
2264--
1901--
2265-- /Unimplemented/
1902-- /Unimplemented/
2266--
1903--
2267−{-# INLINE_NORMAL buildChunks #-}
1904+{-# INLINE_NORMAL writeChunks #-}
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 #-}
2274writeChunks :: (MonadIO m, Unbox a) =>
1905writeChunks :: (MonadIO m, Unbox a) =>
2275 Int -> Fold m a (StreamK n (MutArray a))
1906 Int -> Fold m a (StreamK n (MutArray a))
2276−writeChunks = buildChunks
1907+writeChunks n = FL.many (writeN n) FL.toStreamK
2277
1908
2278{-# INLINE_NORMAL writeWithAs #-}
1909{-# INLINE_NORMAL writeWithAs #-}
2279writeWithAs :: forall m a. (MonadIO m, Unbox a)
1910writeWithAs :: forall m a. (MonadIO m, Unbox a)
2280 => PinnedState -> Int -> Fold m a (MutArray a)
1911 => PinnedState -> Int -> Fold m a (MutArray a)
2281−-- writeWithAs ps n = FL.rmapM rightSize $ appendWith (* 2) (newAs ps n)
1912+-- writeWithAs ps n = FL.rmapM rightSize $ writeAppendWith (* 2) (newAs ps n)
2282writeWithAs ps elemCount =
1913writeWithAs ps elemCount =
2283 FL.rmapM extract $ FL.foldlM' step initial
1914 FL.rmapM extract $ FL.foldlM' step initial
2284
1915
2285 where
1916 where
2286
1917
2287 initial = do
1918 initial = do
2288− when (elemCount < 0) $ error "createWith: elemCount is negative"
1919+ when (elemCount < 0) $ error "writeWith: elemCount is negative"
2289− newAs ps elemCount
1920+ liftIO $ newAs ps elemCount
2290
1921
2291 step arr@(MutArray _ start end bound) x
1922 step arr@(MutArray _ start end bound) x
2292 | INDEX_NEXT(end,a) > bound = do
1923 | INDEX_NEXT(end,a) > bound = do
2293 let oldSize = end - start
1924 let oldSize = end - start
2294 newSize = max (oldSize * 2) 1
1925 newSize = max (oldSize * 2) 1
2295− arr1 <- liftIO $ reallocExplicitAs ps (SIZE_OF(a)) newSize arr
1926+ arr1 <- liftIO $ reallocExplicit (SIZE_OF(a)) newSize arr
2296 snocUnsafe arr1 x
1927 snocUnsafe arr1 x
2297 step arr x = snocUnsafe arr x
1928 step arr x = snocUnsafe arr x
2298
1929
2299 extract = liftIO . rightSize
1930 extract = liftIO . rightSize
2300
1931
2301−-- XXX Compare createWith with fromStreamD which uses an array of streams
1932+-- XXX Compare writeWith with fromStreamD which uses an array of streams
2302−-- implementation. We can write this using buildChunks above if that is faster.
1933+-- implementation. We can write this using writeChunks above if that is faster.
2303−-- If createWith is faster then we should use that to implement
1934+-- If writeWith is faster then we should use that to implement
2304-- fromStreamD.
1935-- fromStreamD.
2305--
1936--
2306-- XXX The realloc based implementation needs to make one extra copy if we use
1937-- XXX The realloc based implementation needs to make one extra copy if we use
@@ -2315,56 +1946,39 @@ writeWithAs ps elemCount =
2315-- XXX check if GHC's memory allocator is efficient enough. We can try the C
1946-- XXX check if GHC's memory allocator is efficient enough. We can try the C
2316-- malloc to compare against.
1947-- malloc to compare against.
2317
1948
2318−-- | @createWith minCount@ folds the whole input to a single array. The array
1949+-- | @writeWith minCount@ folds the whole input to a single array. The array
2319-- starts at a size big enough to hold minCount elements, the size is doubled
1950-- starts at a size big enough to hold minCount elements, the size is doubled
2320-- every time the array needs to be grown.
1951-- every time the array needs to be grown.
2321--
1952--
2322-- /Caution! Do not use this on infinite streams./
1953-- /Caution! Do not use this on infinite streams./
2323--
1954--
2324−-- >>> f n = MutArray.appendWith (* 2) (MutArray.emptyOf n)
1955+-- >>> f n = MutArray.writeAppendWith (* 2) (MutArray.new n)
2325−-- >>> createWith n = Fold.rmapM MutArray.rightSize (f n)
1956+-- >>> writeWith n = Fold.rmapM MutArray.rightSize (f n)
2326−-- >>> createWith n = Fold.rmapM MutArray.fromChunksK (MutArray.buildChunks n)
1957+-- >>> writeWith n = Fold.rmapM MutArray.fromArrayStreamK (MutArray.writeChunks n)
2327--
1958--
2328-- /Pre-release/
1959-- /Pre-release/
2329−{-# INLINE_NORMAL createWith #-}
1960+{-# INLINE_NORMAL writeWith #-}
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 #-}
2337writeWith :: forall m a. (MonadIO m, Unbox a)
1961writeWith :: forall m a. (MonadIO m, Unbox a)
2338 => Int -> Fold m a (MutArray a)
1962 => Int -> Fold m a (MutArray a)
2339−writeWith = createWith
1963+-- writeWith n = FL.rmapM rightSize $ writeAppendWith (* 2) (new n)
1964+writeWith = writeWithAs Unpinned
2340
1965
2341-- | Fold the whole input to a single array.
1966-- | Fold the whole input to a single array.
2342--
1967--
2343−-- Same as 'createWith' using an initial array size of 'arrayChunkBytes' bytes
1968+-- Same as 'writeWith' using an initial array size of 'arrayChunkBytes' bytes
2344-- rounded up to the element size.
1969-- rounded up to the element size.
2345--
1970--
2346-- /Caution! Do not use this on infinite streams./
1971-- /Caution! Do not use this on infinite streams./
2347--
1972--
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." #-}
2354{-# INLINE write #-}
1973{-# INLINE write #-}
2355write :: forall m a. (MonadIO m, Unbox a) => Fold m a (MutArray a)
1974write :: forall m a. (MonadIO m, Unbox a) => Fold m a (MutArray a)
2356−write = create
1975+write = writeWith (allocBytesToElemCount (undefined :: a) arrayChunkBytes)
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)
2363
1976
2364−{-# DEPRECATED pinnedWrite "Please use pinnedCreate instead." #-}
1977+-- | Like 'write' but creates a pinned array.
2365{-# INLINE pinnedWrite #-}
1978{-# INLINE pinnedWrite #-}
2366pinnedWrite :: forall m a. (MonadIO m, Unbox a) => Fold m a (MutArray a)
1979pinnedWrite :: forall m a. (MonadIO m, Unbox a) => Fold m a (MutArray a)
2367−pinnedWrite = pinnedCreate
1980+pinnedWrite =
1981+ writeWithAs Pinned (allocBytesToElemCount (undefined :: a) arrayChunkBytes)
2368
1982
2369-------------------------------------------------------------------------------
1983-------------------------------------------------------------------------------
2370-- construct from streams, known size
1984-- construct from streams, known size
@@ -2374,7 +1988,7 @@ pinnedWrite = pinnedCreate
2374fromStreamDNAs :: forall m a. (MonadIO m, Unbox a)
1988fromStreamDNAs :: forall m a. (MonadIO m, Unbox a)
2375 => PinnedState -> Int -> D.Stream m a -> m (MutArray a)
1989 => PinnedState -> Int -> D.Stream m a -> m (MutArray a)
2376fromStreamDNAs ps limit str = do
1990fromStreamDNAs ps limit str = do
2377− (arr :: MutArray a) <- newAs ps limit
1991+ (arr :: MutArray a) <- liftIO $ newAs ps limit
2378 end <- D.foldlM' (fwrite (arrContents arr)) (return $ arrEnd arr) $ D.take limit str
1992 end <- D.foldlM' (fwrite (arrContents arr)) (return $ arrEnd arr) $ D.take limit str
2379 return $ arr {arrEnd = end}
1993 return $ arr {arrEnd = end}
2380
1994
@@ -2384,21 +1998,15 @@ fromStreamDNAs ps limit str = do
2384 liftIO $ pokeAt ptr arrContents x
1998 liftIO $ pokeAt ptr arrContents x
2385 return $ INDEX_NEXT(ptr,a)
1999 return $ INDEX_NEXT(ptr,a)
2386
2000
2387−-- | Use the 'createOf' fold instead.
2001+-- | Use the 'writeN' fold instead.
2388--
2002--
2389−-- >>> fromStreamN n = Stream.fold (MutArray.createOf n)
2003+-- >>> fromStreamDN n = Stream.fold (MutArray.writeN n)
2390--
2004--
2391−{-# INLINE_NORMAL fromStreamN #-}
2005+{-# INLINE_NORMAL fromStreamDN #-}
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 #-}
2399fromStreamDN :: forall m a. (MonadIO m, Unbox a)
2006fromStreamDN :: forall m a. (MonadIO m, Unbox a)
2400 => Int -> D.Stream m a -> m (MutArray a)
2007 => Int -> D.Stream m a -> m (MutArray a)
2401−fromStreamDN = fromStreamN
2008+-- fromStreamDN n = D.fold (writeN n)
2009+fromStreamDN = fromStreamDNAs Unpinned
2402
2010
2403-- | Create a 'MutArray' from the first N elements of a list. The array is
2011-- | Create a 'MutArray' from the first N elements of a list. The array is
2404-- allocated to size N, if the list terminates before N elements then the
2012-- allocated to size N, if the list terminates before N elements then the
@@ -2418,70 +2026,16 @@ pinnedFromListN n xs = fromStreamDNAs Pinned n $ D.fromList xs
2418-- /Pre-release/
2026-- /Pre-release/
2419{-# INLINE fromListRevN #-}
2027{-# INLINE fromListRevN #-}
2420fromListRevN :: (MonadIO m, Unbox a) => Int -> [a] -> m (MutArray a)
2028fromListRevN :: (MonadIO m, Unbox a) => Int -> [a] -> m (MutArray a)
2421−fromListRevN n xs = D.fold (revCreateOf n) $ D.fromList xs
2029+fromListRevN n xs = D.fold (writeRevN 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
2429
2030
2430-- | Convert a pure stream in Identity monad to a mutable array.
2031-- | Convert a pure stream in Identity monad to a mutable array.
2431{-# INLINABLE fromPureStream #-}
2032{-# INLINABLE fromPureStream #-}
2432fromPureStream :: (MonadIO m, Unbox a) => Stream Identity a -> m (MutArray a)
2033fromPureStream :: (MonadIO m, Unbox a) => Stream Identity a -> m (MutArray a)
2433fromPureStream xs =
2034fromPureStream xs =
2434− D.fold create $ D.morphInner (return . runIdentity) xs
2035+ liftIO $ D.fold write $ 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})
2461
2036
2462-------------------------------------------------------------------------------
2037-------------------------------------------------------------------------------
2463−-- convert a stream of arrays to a single array by reallocating and copying
2038+-- convert stream to a single array
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
2485-------------------------------------------------------------------------------
2039-------------------------------------------------------------------------------
2486
2040
2487{-# INLINE arrayStreamKLength #-}
2041{-# INLINE arrayStreamKLength #-}
@@ -2491,72 +2045,34 @@ arrayStreamKLength as = K.foldl' (+) 0 (K.map length as)
2491-- | Convert an array stream to an array. Note that this requires peak memory
2045-- | Convert an array stream to an array. Note that this requires peak memory
2492-- that is double the size of the array stream.
2046-- that is double the size of the array stream.
2493--
2047--
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." #-}
2518{-# INLINE fromArrayStreamK #-}
2048{-# INLINE fromArrayStreamK #-}
2519fromArrayStreamK :: (Unbox a, MonadIO m) =>
2049fromArrayStreamK :: (Unbox a, MonadIO m) =>
2520 StreamK m (MutArray a) -> m (MutArray a)
2050 StreamK m (MutArray a) -> m (MutArray a)
2521−fromArrayStreamK = fromChunksK
2051+fromArrayStreamK as = do
2052+ len <- arrayStreamKLength as
2053+ fromStreamDN len $ D.unfoldMany reader $ D.fromStreamK as
2522
2054
2523{-# INLINE fromStreamDAs #-}
2055{-# INLINE fromStreamDAs #-}
2524fromStreamDAs ::
2056fromStreamDAs ::
2525 (MonadIO m, Unbox a) => PinnedState -> D.Stream m a -> m (MutArray a)
2057 (MonadIO m, Unbox a) => PinnedState -> D.Stream m a -> m (MutArray a)
2526−fromStreamDAs ps m =
2058+fromStreamDAs ps m = arrayStreamKFromStreamDAs ps m >>= fromArrayStreamK
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
2541
2059
2542−-- fromStream (Stream m) = P.fold create m
2543-- CAUTION: a very large number (millions) of arrays can degrade performance
2060-- CAUTION: a very large number (millions) of arrays can degrade performance
2544-- due to GC overhead because we need to buffer the arrays before we flatten
2061-- due to GC overhead because we need to buffer the arrays before we flatten
2545-- all the arrays.
2062-- all the arrays.
2546--
2063--
2547−-- XXX Compare if this is faster or "fold create".
2064+-- XXX Compare if this is faster or "fold write".
2548--
2065--
2549-- | We could take the approach of doubling the memory allocation on each
2066-- | We could take the approach of doubling the memory allocation on each
2550-- overflow. This would result in more or less the same amount of copying as in
2067-- overflow. This would result in more or less the same amount of copying as in
2551-- the chunking approach. However, if we have to shrink in the end then it may
2068-- the chunking approach. However, if we have to shrink in the end then it may
2552-- result in an extra copy of the entire data.
2069-- result in an extra copy of the entire data.
2553--
2070--
2554−-- >>> fromStreamD = StreamD.fold MutArray.create
2071+-- >>> fromStreamD = StreamD.fold MutArray.write
2555--
2072--
2556{-# INLINE fromStreamD #-}
2073{-# INLINE fromStreamD #-}
2557−{-# DEPRECATED fromStreamD "Please use fromStream instead." #-}
2558fromStreamD :: (MonadIO m, Unbox a) => D.Stream m a -> m (MutArray a)
2074fromStreamD :: (MonadIO m, Unbox a) => D.Stream m a -> m (MutArray a)
2559−fromStreamD = fromStream
2075+fromStreamD = fromStreamDAs Unpinned
2560
2076
2561-- | Create a 'MutArray' from a list. The list must be of finite size.
2077-- | Create a 'MutArray' from a list. The list must be of finite size.
2562--
2078--
@@ -2581,10 +2097,6 @@ fromListRev xs = fromListRevN (Prelude.length xs) xs
2581-- Cloning
2097-- Cloning
2582-------------------------------------------------------------------------------
2098-------------------------------------------------------------------------------
2583
2099
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−
2588{-# INLINE cloneAs #-}
2100{-# INLINE cloneAs #-}
2589cloneAs ::
2101cloneAs ::
2590 ( MonadIO m
2102 ( MonadIO m
@@ -2594,7 +2106,7 @@ cloneAs ::
2594 )
2106 )
2595 => PinnedState -> MutArray a -> m (MutArray a)
2107 => PinnedState -> MutArray a -> m (MutArray a)
2596cloneAs ps src =
2108cloneAs ps src =
2597− do
2109+ liftIO $ do
2598 let startSrc = arrStart src
2110 let startSrc = arrStart src
2599 srcLen = arrEnd src - startSrc
2111 srcLen = arrEnd src - startSrc
2600 newArrContents <-
2112 newArrContents <-
@@ -2633,16 +2145,17 @@ spliceCopy :: forall m a. MonadIO m =>
2633 Unbox a =>
2145 Unbox a =>
2634#endif
2146#endif
2635 MutArray a -> MutArray a -> m (MutArray a)
2147 MutArray a -> MutArray a -> m (MutArray a)
2636−spliceCopy arr1 arr2 = do
2148+spliceCopy arr1 arr2 = liftIO $ do
2637 let start1 = arrStart arr1
2149 let start1 = arrStart arr1
2638 start2 = arrStart arr2
2150 start2 = arrStart arr2
2639 len1 = arrEnd arr1 - start1
2151 len1 = arrEnd arr1 - start1
2640 len2 = arrEnd arr2 - start2
2152 len2 = arrEnd arr2 - start2
2641− let len = len1 + len2
2153+ let newLen = len1 + len2
2642 newArrContents <-
2154 newArrContents <-
2643 if Unboxed.isPinned (arrContents arr1)
2155 if Unboxed.isPinned (arrContents arr1)
2644− then liftIO $ Unboxed.pinnedNew len
2156+ then Unboxed.pinnedNew newLen
2645− else liftIO $ Unboxed.new len
2157+ else Unboxed.new newLen
2158+ let len = len1 + len2
2646 putSliceUnsafe (arrContents arr1) start1 newArrContents 0 len1
2159 putSliceUnsafe (arrContents arr1) start1 newArrContents 0 len1
2647 putSliceUnsafe (arrContents arr2) start2 newArrContents len1 len2
2160 putSliceUnsafe (arrContents arr2) start2 newArrContents len1 len2
2648 return $ MutArray newArrContents 0 len len
2161 return $ MutArray newArrContents 0 len len
@@ -2654,7 +2167,7 @@ spliceCopy arr1 arr2 = do
2654spliceUnsafe :: MonadIO m =>
2167spliceUnsafe :: MonadIO m =>
2655 MutArray a -> MutArray a -> m (MutArray a)
2168 MutArray a -> MutArray a -> m (MutArray a)
2656spliceUnsafe dst src =
2169spliceUnsafe dst src =
2657− do
2170+ liftIO $ do
2658 let startSrc = arrStart src
2171 let startSrc = arrStart src
2659 srcLen = arrEnd src - startSrc
2172 srcLen = arrEnd src - startSrc
2660 endDst = arrEnd dst
2173 endDst = arrEnd dst
@@ -2676,7 +2189,7 @@ spliceWith :: forall m a. (MonadIO m, Unbox a) =>
2676 (Int -> Int -> Int) -> MutArray a -> MutArray a -> m (MutArray a)
2189 (Int -> Int -> Int) -> MutArray a -> MutArray a -> m (MutArray a)
2677spliceWith sizer dst@(MutArray _ start end bound) src = do
2190spliceWith sizer dst@(MutArray _ start end bound) src = do
2678{-
2191{-
2679− let f = appendWith (`sizer` byteLength src) (return dst)
2192+ let f = writeAppendWith (`sizer` byteLength src) (return dst)
2680 in D.fold f (toStreamD src)
2193 in D.fold f (toStreamD src)
2681-}
2194-}
2682 assert (end <= bound) (return ())
2195 assert (end <= bound) (return ())
@@ -2692,7 +2205,7 @@ spliceWith sizer dst@(MutArray _ start end bound) src = do
2692 $ "splice: newSize is less than the total size "
2205 $ "splice: newSize is less than the total size "
2693 ++ "of arrays being appended. Please check the "
2206 ++ "of arrays being appended. Please check the "
2694 ++ "sizer function passed."
2207 ++ "sizer function passed."
2695− realloc newSizeInBytes dst
2208+ liftIO $ realloc newSizeInBytes dst
2696 else return dst
2209 else return dst
2697 spliceUnsafe dst1 src
2210 spliceUnsafe dst1 src
2698
2211
@@ -2729,24 +2242,14 @@ spliceExp = spliceWith (\l1 l2 -> max (l1 * 2) (l1 + l2))
2729-- Splitting
2242-- Splitting
2730-------------------------------------------------------------------------------
2243-------------------------------------------------------------------------------
2731
2244
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−
2743-- | Drops the separator byte
2245-- | Drops the separator byte
2744{-# INLINE breakOn #-}
2246{-# INLINE breakOn #-}
2745breakOn :: MonadIO m
2247breakOn :: MonadIO m
2746 => Word8 -> MutArray Word8 -> m (MutArray Word8, Maybe (MutArray Word8))
2248 => Word8 -> MutArray Word8 -> m (MutArray Word8, Maybe (MutArray Word8))
2747−breakOn sep arr@MutArray{..} = unsafeAsPtr arr $ \p -> liftIO $ do
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).
2748 -- XXX We do not need memchr here, we can use a Haskell equivalent.
2252 -- XXX We do not need memchr here, we can use a Haskell equivalent.
2749− -- Need efficient stream based primitives that work on Word64.
2750 loc <- c_memchr p sep (fromIntegral $ byteLength arr)
2253 loc <- c_memchr p sep (fromIntegral $ byteLength arr)
2751 let sepIndex = loc `minusPtr` p
2254 let sepIndex = loc `minusPtr` p
2752 return $
2255 return $
@@ -2767,41 +2270,32 @@ breakOn sep arr@MutArray{..} = unsafeAsPtr arr $ \p -> liftIO $ do
2767 }
2270 }
2768 )
2271 )
2769
2272
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−
2792-- | Create two slices of an array without copying the original array. The
2273-- | Create two slices of an array without copying the original array. The
2793-- specified index @i@ is the first index of the second slice.
2274-- specified index @i@ is the first index of the second slice.
2794--
2275--
2795−{-# INLINE splitAt #-}
2796splitAt :: forall a. Unbox a => Int -> MutArray a -> (MutArray a, MutArray a)
2276splitAt :: forall a. Unbox a => Int -> MutArray a -> (MutArray a, MutArray a)
2797−splitAt i arr =
2277+splitAt i arr@MutArray{..} =
2798 let maxIndex = length arr - 1
2278 let maxIndex = length arr - 1
2799 in if i < 0
2279 in if i < 0
2800 then error "sliceAt: negative array index"
2280 then error "sliceAt: negative array index"
2801 else if i > maxIndex
2281 else if i > maxIndex
2802 then error $ "sliceAt: specified array index " ++ show i
2282 then error $ "sliceAt: specified array index " ++ show i
2803 ++ " is beyond the maximum index " ++ show maxIndex
2283 ++ " is beyond the maximum index " ++ show maxIndex
2804− else unsafeSplitAt i arr
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+ )
2805
2299
2806-------------------------------------------------------------------------------
2300-------------------------------------------------------------------------------
2807-- Casting
2301-- Casting
@@ -2851,13 +2345,9 @@ cast arr =
2851-- guarantees that unsafe calls will be performed in the calling thread. Making
2345-- guarantees that unsafe calls will be performed in the calling thread. Making
2852-- it safe to pass heap-allocated objects to unsafe functions.
2346-- it safe to pass heap-allocated objects to unsafe functions.
2853
2347
2854−-- XXX Should we just name it asPtr, the unsafety is implicit for any pointer
2348+-- Should we just name it asPtr, the unsafety is implicit for any pointer
2855-- operations. And we are safe from Haskell perspective because we will be
2349-- operations. And we are safe from Haskell perspective because we will be
2856-- pinning the memory.
2350-- 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.
2861
2351
2862-- | Use a @MutArray a@ as @Ptr a@. This is useful when we want to pass an
2352-- | Use a @MutArray a@ as @Ptr a@. This is useful when we want to pass an
2863-- array as a pointer to some operating system call or to a "safe" FFI call.
2353-- array as a pointer to some operating system call or to a "safe" FFI call.
@@ -2875,377 +2365,48 @@ cast arr =
2875--
2365--
2876-- /Pre-release/
2366-- /Pre-release/
2877--
2367--
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." #-}
2885{-# INLINE asPtrUnsafe #-}
2368{-# INLINE asPtrUnsafe #-}
2886asPtrUnsafe :: MonadIO m => MutArray a -> (Ptr a -> m b) -> m b
2369asPtrUnsafe :: MonadIO m => MutArray a -> (Ptr a -> m b) -> m b
2887−asPtrUnsafe = unsafePinnedAsPtr
2370+asPtrUnsafe arr f =
2888−
2371+ Unboxed.asPtrUnsafe
2889−{-# INLINE unsafeAsPtr #-}
2890−unsafeAsPtr :: MonadIO m => MutArray a -> (Ptr a -> m b) -> m b
2891−unsafeAsPtr arr f =
2892− Unboxed.unsafeAsPtr
2893 (arrContents arr) (\ptr -> f (ptr `plusPtr` arrStart arr))
2372 (arrContents arr) (\ptr -> f (ptr `plusPtr` arrStart arr))
2894
2373
2895-------------------------------------------------------------------------------
2374-------------------------------------------------------------------------------
2896-- Equality
2375-- Equality
2897-------------------------------------------------------------------------------
2376-------------------------------------------------------------------------------
2898
2377
2899−-- | Byte compare two arrays. Compare the length of the arrays. If the length
2378+-- | Compare the length of the arrays. If the length is equal, compare the
2900−-- is equal, compare the lexicographical ordering of two underlying byte arrays
2379+-- lexicographical ordering of two underlying byte arrays otherwise return the
2901−-- otherwise return the result of length comparison.
2380+-- 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.
2906--
2381--
2907-- /Pre-release/
2382-- /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−
2931{-# INLINE cmp #-}
2383{-# INLINE cmp #-}
2932−{-# DEPRECATED cmp "Please use byteCmp instead." #-}
2933cmp :: MonadIO m => MutArray a -> MutArray a -> m Ordering
2384cmp :: MonadIO m => MutArray a -> MutArray a -> m Ordering
2934−cmp = byteCmp
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
2935
2407
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
2947-------------------------------------------------------------------------------
2408-------------------------------------------------------------------------------
2948−
2409+-- NFData
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
3249-------------------------------------------------------------------------------
2410-------------------------------------------------------------------------------
3250
2411
3251-- | Strip elements which match with predicate from both ends.
2412-- | Strip elements which match with predicate from both ends.
core/src/Streamly/Internal/Data/MutByteArray/Type.hs+12−47
@@ -13,7 +13,7 @@ module Streamly.Internal.Data.MutByteArray.Type
13 -- ** MutByteArray
13 -- ** MutByteArray
14 MutByteArray(..)
14 MutByteArray(..)
15 , MutableByteArray
15 , MutableByteArray
16− , getMutableByteArray# -- XXX getMutByteArray#
16+ , getMutableByteArray#
17
17
18 -- ** Pinning
18 -- ** Pinning
19 , PinnedState(..)
19 , PinnedState(..)
@@ -22,24 +22,19 @@ module Streamly.Internal.Data.MutByteArray.Type
22 , unpin
22 , unpin
23
23
24 -- ** Allocation
24 -- ** Allocation
25− , empty
25+ , nil
26− , newBytesAs -- XXX should be removed
26+ , newBytesAs
27 , new
27 , new
28 , pinnedNew
28 , pinnedNew
29− , pinnedNewAlignedBytes -- XXX should be removed
29+ , pinnedNewAlignedBytes
30
30
31 -- ** Access
31 -- ** Access
32− , sizeOfMutableByteArray -- XXX length
32+ , sizeOfMutableByteArray
33 , putSliceUnsafe
33 , putSliceUnsafe
34 , cloneSliceUnsafeAs
34 , cloneSliceUnsafeAs
35 , cloneSliceUnsafe
35 , cloneSliceUnsafe
36 , pinnedCloneSliceUnsafe
36 , pinnedCloneSliceUnsafe
37− , unsafePinnedAsPtr
38− , unsafeAsPtr
39−
40− -- ** Deprecated
41 , asPtrUnsafe
37 , asPtrUnsafe
42− , nil
43 ) where
38 ) where
44
39
45import Control.Monad.IO.Class (MonadIO(..))
40import Control.Monad.IO.Class (MonadIO(..))
@@ -58,7 +53,7 @@ import GHC.Exts
58
53
59data PinnedState
54data PinnedState
60 = Pinned
55 = Pinned
61− | Unpinned deriving (Show, Eq)
56+ | Unpinned
62
57
63-- XXX can use UnliftedNewtypes
58-- XXX can use UnliftedNewtypes
64
59
@@ -87,9 +82,6 @@ touch :: MutByteArray -> IO ()
87touch (MutByteArray contents) =
82touch (MutByteArray contents) =
88 IO $ \s -> case touch# contents s of s' -> (# s', () #)
83 IO $ \s -> case touch# contents s of s' -> (# s', () #)
89
84
90−-- XXX Some functions in this module are "IO" and others are "m", we need to
91−-- make it consistent.
92−
93-- XXX We can provide another API for "unsafe" FFI calls passing an unlifted
85-- XXX We can provide another API for "unsafe" FFI calls passing an unlifted
94-- pointer to the FFI call. For unsafe calls we do not need to pin the array.
86-- pointer to the FFI call. For unsafe calls we do not need to pin the array.
95-- We can pass an unlifted pointer to the FFI routine to avoid GC kicking in
87-- We can pass an unlifted pointer to the FFI routine to avoid GC kicking in
@@ -118,9 +110,9 @@ touch (MutByteArray contents) =
118--
110--
119-- /Pre-release/
111-- /Pre-release/
120--
112--
121−{-# INLINE unsafePinnedAsPtr #-}
113+{-# INLINE asPtrUnsafe #-}
122−unsafePinnedAsPtr :: MonadIO m => MutByteArray -> (Ptr a -> m b) -> m b
114+asPtrUnsafe :: MonadIO m => MutByteArray -> (Ptr a -> m b) -> m b
123−unsafePinnedAsPtr arr f = do
115+asPtrUnsafe arr f = do
124 contents <- liftIO $ pin arr
116 contents <- liftIO $ pin arr
125 let !ptr = Ptr (byteArrayContents#
117 let !ptr = Ptr (byteArrayContents#
126 (unsafeCoerce# (getMutableByteArray# contents)))
118 (unsafeCoerce# (getMutableByteArray# contents)))
@@ -128,35 +120,14 @@ unsafePinnedAsPtr arr f = do
128 liftIO $ touch contents
120 liftIO $ touch contents
129 return r
121 return r
130
122
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−
146--------------------------------------------------------------------------------
123--------------------------------------------------------------------------------
147-- Creation
124-- Creation
148--------------------------------------------------------------------------------
125--------------------------------------------------------------------------------
149
126
150−{-# NOINLINE empty #-}
127+{-# NOINLINE nil #-}
151−empty :: MutByteArray
152−empty = unsafePerformIO $ new 0
153−
154−{-# DEPRECATED nil "Please use empty instead" #-}
155nil :: MutByteArray
128nil :: MutByteArray
156−nil = empty
129+nil = unsafePerformIO $ new 0
157
130
158−-- XXX add "newRounded" to round up the large size to the next page boundary
159−-- and return the allocated size.
160{-# INLINE new #-}
131{-# INLINE new #-}
161new :: Int -> IO MutByteArray
132new :: Int -> IO MutByteArray
162new nbytes | nbytes < 0 =
133new nbytes | nbytes < 0 =
@@ -170,7 +141,7 @@ new (I# nbytes) = IO $ \s ->
170{-# INLINE pinnedNew #-}
141{-# INLINE pinnedNew #-}
171pinnedNew :: Int -> IO MutByteArray
142pinnedNew :: Int -> IO MutByteArray
172pinnedNew nbytes | nbytes < 0 =
143pinnedNew nbytes | nbytes < 0 =
173− errorWithoutStackTrace "pinnedNew: size must be >= 0"
144+ errorWithoutStackTrace "pinnedNewByteArray: size must be >= 0"
174pinnedNew (I# nbytes) = IO $ \s ->
145pinnedNew (I# nbytes) = IO $ \s ->
175 case newPinnedByteArray# nbytes s of
146 case newPinnedByteArray# nbytes s of
176 (# s', mbarr# #) ->
147 (# s', mbarr# #) ->
@@ -196,12 +167,6 @@ newBytesAs Pinned = pinnedNew
196-- Copying
167-- Copying
197-------------------------------------------------------------------------------
168-------------------------------------------------------------------------------
198
169
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−
205-- | Put a sub range of a source array into a subrange of a destination array.
170-- | Put a sub range of a source array into a subrange of a destination array.
206-- This is not safe as it does not check the bounds of neither the src array
171-- This is not safe as it does not check the bounds of neither the src array
207-- nor the destination array.
172-- nor the destination array.
core/src/Streamly/Internal/Data/Serialize/TH.hs+3−9
@@ -7,11 +7,7 @@
7-- Maintainer : streamly@composewell.com
7-- Maintainer : streamly@composewell.com
8-- Stability : experimental
8-- Stability : experimental
9-- Portability : GHC
9-- 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−
15module Streamly.Internal.Data.Serialize.TH
11module Streamly.Internal.Data.Serialize.TH
16 (
12 (
17 -- Deriving
13 -- Deriving
@@ -221,7 +217,6 @@ mkDeserializeExpr True False headTy tyOfTy =
221mkDeserializeExpr False False headTy tyOfTy =
217mkDeserializeExpr False False headTy tyOfTy =
222 case tyOfTy of
218 case tyOfTy of
223 -- Unit constructor
219 -- Unit constructor
224− -- XXX Should we peek and check if the byte value is 0?
225 UnitType cname ->
220 UnitType cname ->
226 [|pure ($(varE _initialOffset) + 1, $(conE cname))|]
221 [|pure ($(varE _initialOffset) + 1, $(conE cname))|]
227 -- Product type
222 -- Product type
@@ -346,8 +341,7 @@ mkSerializeExpr True False tyOfTy =
346mkSerializeExpr False False tyOfTy =
341mkSerializeExpr False False tyOfTy =
347 case tyOfTy of
342 case tyOfTy of
348 -- Unit type
343 -- Unit type
349− UnitType _ ->
344+ UnitType _ -> [|pure ($(varE _initialOffset) + 1)|]
350− [|serializeAt $(varE _initialOffset) $(varE _arr) (0 :: Word8)|]
351 -- Product type
345 -- Product type
352 (TheType (SimpleDataCon cname fields)) ->
346 (TheType (SimpleDataCon cname fields)) ->
353 letE
347 letE
@@ -401,7 +395,7 @@ mkSerializeDec (SerializeConfig {..}) headTy cons = do
401 'serializeAt
395 'serializeAt
402 [ Clause
396 [ Clause
403 (if isUnitType cons && not cfgConstructorTagAsString
397 (if isUnitType cons && not cfgConstructorTagAsString
404− then [VarP _initialOffset, VarP _arr, WildP]
398+ then [VarP _initialOffset, WildP, WildP]
405 else [VarP _initialOffset, VarP _arr, VarP _val])
399 else [VarP _initialOffset, VarP _arr, VarP _val])
406 (NormalB pokeMethod)
400 (NormalB pokeMethod)
407 []
401 []
core/src/Streamly/Internal/Data/Stream/Container.hs+48−56
@@ -11,17 +11,16 @@
11
11
12module Streamly.Internal.Data.Stream.Container
12module Streamly.Internal.Data.Stream.Container
13 (
13 (
14− -- * Deduplication
14+ nub
15− ordNub
16
15
17− -- * Joins
16+ -- * Joins for unconstrained types
18− , leftJoin
17+ , joinLeftGeneric
19− , outerJoin
18+ , joinOuterGeneric
20
19
21− -- * Ord Joins
20+ -- * Joins with Ord constraint
22− , innerOrdJoin
21+ , joinInner
23− , leftOrdJoin
22+ , joinLeft
24− , outerOrdJoin
23+ , joinOuter
25 )
24 )
26where
25where
27
26
@@ -48,15 +47,12 @@ import qualified Streamly.Internal.Data.Stream.Transformer as Stream
48
47
49#include "DocTestDataStream.hs"
48#include "DocTestDataStream.hs"
50
49
51−-- | @nub@ specialized to 'Ord' types for better performance. Returns a
50+-- | The memory used is proportional to the number of unique elements in the
52−-- subsequence of the stream removing any duplicate elements.
51+-- stream. If we want to limit the memory we can just use "take" to limit the
53−--
52+-- uniq elements in the stream.
54−-- The memory used is proportional to the number of unique elements in the
53+{-# INLINE_NORMAL nub #-}
55−-- stream. One way to limit the memory is to use @take@ on the resulting
54+nub :: (Monad m, Ord a) => Stream m a -> Stream m a
56−-- stream to limit the unique elements in the stream.
55+nub (Stream step1 state1) = Stream step (Set.empty, state1)
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)
60
56
61 where
57 where
62
58
@@ -82,8 +78,8 @@ toMap =
82--
78--
83-- XXX An IntMap may be faster when the keys are Int.
79-- XXX An IntMap may be faster when the keys are Int.
84-- XXX Use hashmap instead of map?
80-- XXX Use hashmap instead of map?
85−
81+--
86−-- | 'innerJoin' specialized to 'Ord' types for better performance.
82+-- | Like 'joinInner' but uses a 'Map' for efficiency.
87--
83--
88-- If the input streams have duplicate keys, the behavior is undefined.
84-- If the input streams have duplicate keys, the behavior is undefined.
89--
85--
@@ -94,10 +90,10 @@ toMap =
94-- Time: O(m + n)
90-- Time: O(m + n)
95--
91--
96-- /Pre-release/
92-- /Pre-release/
97−{-# INLINE innerOrdJoin #-}
93+{-# INLINE joinInner #-}
98−innerOrdJoin :: (Monad m, Ord k) =>
94+joinInner :: (Monad m, Ord k) =>
99 Stream m (k, a) -> Stream m (k, b) -> Stream m (k, a, b)
95 Stream m (k, a) -> Stream m (k, b) -> Stream m (k, a, b)
100−innerOrdJoin s1 s2 =
96+joinInner s1 s2 =
101 Stream.concatEffect $ do
97 Stream.concatEffect $ do
102 km <- toMap s2
98 km <- toMap s2
103 pure $ Stream.mapMaybe (joinAB km) s1
99 pure $ Stream.mapMaybe (joinAB km) s1
@@ -110,33 +106,32 @@ innerOrdJoin s1 s2 =
110 Nothing -> Nothing
106 Nothing -> Nothing
111
107
112-- XXX We can do this concurrently.
108-- XXX We can do this concurrently.
113−-- XXX Check performance of StreamD vs StreamK
114-- XXX If the second stream is sorted and passed as an Array or a seek capable
109-- XXX If the second stream is sorted and passed as an Array or a seek capable
115-- stream then we could use binary search if we have an Ord instance or
110-- stream then we could use binary search if we have an Ord instance or
116-- Ordering returning function. The time complexity would then become (m x log
111-- Ordering returning function. The time complexity would then become (m x log
117-- n).
112-- n).
118
113
119−-- | Like 'innerJoin' but emits @(a, Just b)@ whenever a and b are equal, for
114+-- XXX Check performance of StreamD vs StreamK
120−-- those @a@'s that are not equal to any @b@ emits @(a, Nothing)@.
115+
121−--
116+-- | Like 'joinInner' but emit @(a, Just b)@, and additionally, for those @a@'s
122−-- This is a generalization of 'innerJoin' to include all elements from the
117+-- that are not equal to any @b@ emit @(a, Nothing)@.
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.
125--
118--
126−-- All the caveats mentioned in 'innerJoin' apply here as well. Right join is
119+-- The second stream is evaluated multiple times. If the stream is a
127−-- not provided because it is just a flipped left join:
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.
128--
123--
129−-- >>> rightJoin eq = flip (Stream.leftJoin eq)
124+-- >>> joinRightGeneric eq = flip (Stream.joinLeftGeneric eq)
130--
125--
131-- Space: O(n) assuming the second stream is cached in memory.
126-- Space: O(n) assuming the second stream is cached in memory.
132--
127--
133-- Time: O(m x n)
128-- Time: O(m x n)
134--
129--
135-- /Unimplemented/
130-- /Unimplemented/
136−{-# INLINE leftJoin #-}
131+{-# INLINE joinLeftGeneric #-}
137−leftJoin :: Monad m =>
132+joinLeftGeneric :: Monad m =>
138 (a -> b -> Bool) -> Stream m a -> Stream m b -> Stream m (a, Maybe b)
133 (a -> b -> Bool) -> Stream m a -> Stream m b -> Stream m (a, Maybe b)
139−leftJoin eq s1 s2 = Stream.evalStateT (return False) $ unCross $ do
134+joinLeftGeneric eq s1 s2 = Stream.evalStateT (return False) $ unCross $ do
140 a <- mkCross (Stream.liftInner s1)
135 a <- mkCross (Stream.liftInner s1)
141 -- XXX should we use StreamD monad here?
136 -- XXX should we use StreamD monad here?
142 -- XXX Is there a better way to perform some action at the end of a loop
137 -- XXX Is there a better way to perform some action at the end of a loop
@@ -157,17 +152,19 @@ leftJoin eq s1 s2 = Stream.evalStateT (return False) $ unCross $ do
157 else mkCross Stream.nil
152 else mkCross Stream.nil
158 Nothing -> return (a, Nothing)
153 Nothing -> return (a, Nothing)
159
154
160−-- | 'leftJoin' specialized to 'Ord' types for better performance.
155+-- XXX rename to joinLeftOrd?
156+
157+-- | A more efficient 'joinLeft' using a hashmap for efficiency.
161--
158--
162-- Space: O(n)
159-- Space: O(n)
163--
160--
164-- Time: O(m + n)
161-- Time: O(m + n)
165--
162--
166-- /Pre-release/
163-- /Pre-release/
167−{-# INLINE leftOrdJoin #-}
164+{-# INLINE joinLeft #-}
168−leftOrdJoin :: (Ord k, Monad m) =>
165+joinLeft :: (Ord k, Monad m) =>
169 Stream m (k, a) -> Stream m (k, b) -> Stream m (k, a, Maybe b)
166 Stream m (k, a) -> Stream m (k, b) -> Stream m (k, a, Maybe b)
170−leftOrdJoin s1 s2 =
167+joinLeft s1 s2 =
171 Stream.concatEffect $ do
168 Stream.concatEffect $ do
172 km <- toMap s2
169 km <- toMap s2
173 return $ fmap (joinAB km) s1
170 return $ fmap (joinAB km) s1
@@ -180,19 +177,14 @@ leftOrdJoin s1 s2 =
180 Nothing -> (k, a, Nothing)
177 Nothing -> (k, a, Nothing)
181
178
182-- XXX We can do this concurrently.
179-- XXX We can do this concurrently.
183−-- XXX Check performance of StreamD vs StreamK cross operation.
184
180
185−-- | Like 'leftJoin' but emits a @(Just a, Just b)@. Like 'leftJoin', for those
181+-- XXX Check performance of StreamD vs StreamK
182+
183+-- | Like 'joinLeft' but emits a @(Just a, Just b)@. Like 'joinLeft', for those
186-- @a@'s that are not equal to any @b@ emit @(Just a, Nothing)@, but
184-- @a@'s that are not equal to any @b@ emit @(Just a, Nothing)@, but
187-- additionally, for those @b@'s that are not equal to any @a@ emit @(Nothing,
185-- additionally, for those @b@'s that are not equal to any @a@ emit @(Nothing,
188-- Just b)@.
186-- Just b)@.
189--
187--
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−--
196-- For space efficiency use the smaller stream as the second stream.
188-- For space efficiency use the smaller stream as the second stream.
197--
189--
198-- Space: O(n)
190-- Space: O(n)
@@ -200,15 +192,15 @@ leftOrdJoin s1 s2 =
200-- Time: O(m x n)
192-- Time: O(m x n)
201--
193--
202-- /Pre-release/
194-- /Pre-release/
203−{-# INLINE outerJoin #-}
195+{-# INLINE joinOuterGeneric #-}
204−outerJoin :: MonadIO m =>
196+joinOuterGeneric :: MonadIO m =>
205 (a -> b -> Bool)
197 (a -> b -> Bool)
206 -> Stream m a
198 -> Stream m a
207 -> Stream m b
199 -> Stream m b
208 -> Stream m (Maybe a, Maybe b)
200 -> Stream m (Maybe a, Maybe b)
209−outerJoin eq s1 s2 =
201+joinOuterGeneric eq s1 s =
210 Stream.concatEffect $ do
202 Stream.concatEffect $ do
211− inputArr <- Array.fromStream s2
203+ inputArr <- Array.fromStream s
212 let len = Array.length inputArr
204 let len = Array.length inputArr
213 foundArr <-
205 foundArr <-
214 Stream.fold
206 Stream.fold
@@ -262,18 +254,18 @@ outerJoin eq s1 s2 =
262-- a flag. At the end go through @Stream m b@ and find those that are not in that
254-- a flag. At the end go through @Stream m b@ and find those that are not in that
263-- hash to return (Nothing, b).
255-- hash to return (Nothing, b).
264
256
265−-- | 'outerJoin' specialized to 'Ord' types for better performance.
257+-- | Like 'joinOuter' but uses a 'Map' for efficiency.
266--
258--
267-- Space: O(m + n)
259-- Space: O(m + n)
268--
260--
269-- Time: O(m + n)
261-- Time: O(m + n)
270--
262--
271-- /Pre-release/
263-- /Pre-release/
272−{-# INLINE outerOrdJoin #-}
264+{-# INLINE joinOuter #-}
273−outerOrdJoin ::
265+joinOuter ::
274 (Ord k, MonadIO m) =>
266 (Ord k, MonadIO m) =>
275 Stream m (k, a) -> Stream m (k, b) -> Stream m (k, Maybe a, Maybe b)
267 Stream m (k, a) -> Stream m (k, b) -> Stream m (k, Maybe a, Maybe b)
276−outerOrdJoin s1 s2 =
268+joinOuter s1 s2 =
277 Stream.concatEffect $ do
269 Stream.concatEffect $ do
278 km1 <- kvFold s1
270 km1 <- kvFold s1
279 km2 <- kvFold s2
271 km2 <- kvFold s2
core/src/Streamly/Internal/Data/Stream/Eliminate.hs+25−33
@@ -13,24 +13,36 @@
13--
13--
14module Streamly.Internal.Data.Stream.Eliminate
14module Streamly.Internal.Data.Stream.Eliminate
15 (
15 (
16− -- * Running a Parser
16+ -- * Running a 'Fold'
17− parse
17+ fold
18+
19+ -- -- * Running a 'Parser'
20+ , parse
18 , parseD
21 , parseD
19 , parseBreak
22 , parseBreak
20 , parseBreakD
23 , parseBreakD
21
24
22− -- * Deconstruction
25+ -- * Stream Deconstruction
23 , uncons
26 , uncons
24
27
25 -- * Right Folds
28 -- * Right Folds
29+ , foldrM
30+ , foldr
31+ , foldrMx
26 , foldr1
32 , foldr1
27
33
34+ -- * Left Folds
35+ , foldlM'
36+ , foldl'
37+ , foldlMx'
38+ , foldlx'
39+
28 -- * Specific Fold Functions
40 -- * Specific Fold Functions
41+ , drain
29 , mapM_ -- Map and Fold
42 , mapM_ -- Map and Fold
30 , null
43 , null
31 , head
44 , head
32 , headElse
45 , headElse
33− , init
34 , tail
46 , tail
35 , last
47 , last
36 , elem
48 , elem
@@ -48,9 +60,16 @@ module Streamly.Internal.Data.Stream.Eliminate
48 , the
60 , the
49
61
50 -- * To containers
62 -- * To containers
63+ , toList
51 , toListRev
64 , toListRev
52
65
53 -- * Multi-Stream Folds
66 -- * Multi-Stream Folds
67+ -- ** Comparisons
68+ -- | These should probably be expressed using zipping operations.
69+ , eqBy
70+ , cmpBy
71+
72+ -- ** Substreams
54 -- | These should probably be expressed using parsers.
73 -- | These should probably be expressed using parsers.
55 , isPrefixOf
74 , isPrefixOf
56 , isInfixOf
75 , isInfixOf
@@ -71,7 +90,7 @@ import Foreign.Storable (Storable)
71import GHC.Exts (SpecConstrAnnotation(..))
90import GHC.Exts (SpecConstrAnnotation(..))
72import GHC.Types (SPEC(..))
91import GHC.Types (SPEC(..))
73import Streamly.Internal.Data.Parser (ParseError(..))
92import Streamly.Internal.Data.Parser (ParseError(..))
74−import Streamly.Internal.Data.SVar.Type (adaptState, defState)
93+import Streamly.Internal.Data.SVar.Type (defState)
75import Streamly.Internal.Data.Unbox (Unbox)
94import Streamly.Internal.Data.Unbox (Unbox)
76
95
77import Streamly.Internal.Data.Maybe.Strict (Maybe'(..))
96import Streamly.Internal.Data.Maybe.Strict (Maybe'(..))
@@ -86,7 +105,7 @@ import qualified Streamly.Internal.Data.Stream.Transform as StreamD
86
105
87import Prelude hiding
106import Prelude hiding
88 ( Foldable(..), all, any, head, last, lookup, mapM, mapM_
107 ( Foldable(..), all, any, head, last, lookup, mapM, mapM_
89− , notElem, splitAt, init, tail, (!!))
108+ , notElem, splitAt, tail, (!!))
90import Data.Foldable (length)
109import Data.Foldable (length)
91import Streamly.Internal.Data.Stream.Type
110import Streamly.Internal.Data.Stream.Type
92
111
@@ -372,35 +391,9 @@ head = foldrM (\x _ -> return (Just x)) (return Nothing)
372headElse :: Monad m => a -> Stream m a -> m a
391headElse :: Monad m => a -> Stream m a -> m a
373headElse a = foldrM (\x _ -> return x) (return a)
392headElse a = foldrM (\x _ -> return x) (return a)
374
393
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−--
398-- Does not fuse, has the same performance as the StreamK version.
394-- Does not fuse, has the same performance as the StreamK version.
399−--
400{-# INLINE_NORMAL tail #-}
395{-# INLINE_NORMAL tail #-}
401tail :: Monad m => Stream m a -> m (Maybe (Stream m a))
396tail :: Monad m => Stream m a -> m (Maybe (Stream m a))
402−tail = fmap (fmap snd) . uncons
403−{-
404tail (UnStream step state) = go SPEC state
397tail (UnStream step state) = go SPEC state
405 where
398 where
406 go !_ st = do
399 go !_ st = do
@@ -409,7 +402,6 @@ tail (UnStream step state) = go SPEC state
409 Yield _ s -> return (Just $ Stream step s)
402 Yield _ s -> return (Just $ Stream step s)
410 Skip s -> go SPEC s
403 Skip s -> go SPEC s
411 Stop -> return Nothing
404 Stop -> return Nothing
412−-}
413
405
414-- XXX will it fuse? need custom impl?
406-- XXX will it fuse? need custom impl?
415{-# INLINE_NORMAL last #-}
407{-# INLINE_NORMAL last #-}
core/src/Streamly/Internal/Data/Stream/Exception.hs+6−9
@@ -9,20 +9,17 @@
9
9
10module Streamly.Internal.Data.Stream.Exception
10module Streamly.Internal.Data.Stream.Exception
11 (
11 (
12− -- * Resources
12+ gbracket_
13− before
14− , afterIO
15− , afterUnsafe
16− , finallyIO
17− , finallyUnsafe
18− , gbracket_
19 , gbracket
13 , gbracket
14+ , before
15+ , afterUnsafe
16+ , afterIO
20 , bracketUnsafe
17 , bracketUnsafe
21 , bracketIO3
18 , bracketIO3
22 , bracketIO
19 , bracketIO
23−
24− -- * Exceptions
25 , onException
20 , onException
21+ , finallyUnsafe
22+ , finallyIO
26 , ghandle
23 , ghandle
27 , handle
24 , handle
28 )
25 )
core/src/Streamly/Internal/Data/Stream/Generate.hs+12−2
@@ -16,13 +16,20 @@ module Streamly.Internal.Data.Stream.Generate
16 (
16 (
17 -- * Primitives
17 -- * Primitives
18 nil
18 nil
19+ , nilM
19 , cons
20 , cons
21+ , consM
22+
23+ -- * From 'Unfold'
24+ , unfold
20
25
21 -- * Unfolding
26 -- * Unfolding
22 , unfoldr
27 , unfoldr
23 , unfoldrM
28 , unfoldrM
24
29
25 -- * From Values
30 -- * From Values
31+ , fromPure
32+ , fromEffect
26 , repeat
33 , repeat
27 , repeatM
34 , repeatM
28 , replicate
35 , replicate
@@ -88,6 +95,7 @@ module Streamly.Internal.Data.Stream.Generate
88 -- * From Containers
95 -- * From Containers
89 -- | Transform an input structure into a stream.
96 -- | Transform an input structure into a stream.
90
97
98+ , fromList
91 , fromListM
99 , fromListM
92 , fromFoldable
100 , fromFoldable
93 , fromFoldableM
101 , fromFoldableM
@@ -96,6 +104,10 @@ module Streamly.Internal.Data.Stream.Generate
96 , fromPtr
104 , fromPtr
97 , fromPtrN
105 , fromPtrN
98 , fromByteStr#
106 , fromByteStr#
107+
108+ -- * Conversions
109+ , fromStreamK
110+ , toStreamK
99 )
111 )
100where
112where
101
113
@@ -142,8 +154,6 @@ import Streamly.Internal.Data.Stream.Type
142nil :: Applicative m => Stream m a
154nil :: Applicative m => Stream m a
143nil = Stream (\_ _ -> pure Stop) ()
155nil = Stream (\_ _ -> pure Stop) ()
144
156
145−infixr 5 `cons`
146−
147-- XXX implement in terms of consM?
157-- XXX implement in terms of consM?
148-- cons x = consM (return x)
158-- cons x = consM (return x)
149-- From an implementation perspective, StreamK.cons translates into a
159-- From an implementation perspective, StreamK.cons translates into a
core/src/Streamly/Internal/Data/Stream/Nesting.hs+146−19
@@ -55,6 +55,11 @@ module Streamly.Internal.Data.Stream.Nesting
55 -- yields an element. A special case of unfoldRoundRobin.
55 -- yields an element. A special case of unfoldRoundRobin.
56 , roundRobin -- interleaveFair?/ParallelFair
56 , roundRobin -- interleaveFair?/ParallelFair
57
57
58+ -- *** Zipping
59+ -- | Zip corresponding elements of two streams.
60+ , zipWith
61+ , zipWithM
62+
58 -- *** Merging
63 -- *** Merging
59 -- | Interleave elements from two streams based on a condition.
64 -- | Interleave elements from two streams based on a condition.
60 , mergeBy
65 , mergeBy
@@ -71,10 +76,17 @@ module Streamly.Internal.Data.Stream.Nesting
71 -- unfoldMany: Unfold m a b -> t m a -> t m b
76 -- unfoldMany: Unfold m a b -> t m a -> t m b
72 -- @
77 -- @
73
78
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+
74 -- *** ConcatUnfold
85 -- *** ConcatUnfold
75 -- | Generate streams by using an unfold on each element of an input
86 -- | Generate streams by using an unfold on each element of an input
76 -- stream, append the resulting streams and flatten. A special case of
87 -- stream, append the resulting streams and flatten. A special case of
77 -- gintercalate.
88 -- gintercalate.
89+ , unfoldMany
78 , ConcatUnfoldInterleaveState (..)
90 , ConcatUnfoldInterleaveState (..)
79 , unfoldInterleave
91 , unfoldInterleave
80 , unfoldRoundRobin
92 , unfoldRoundRobin
@@ -106,8 +118,11 @@ module Streamly.Internal.Data.Stream.Nesting
106
118
107 -- ** Folding
119 -- ** Folding
108 -- | Apply folds on a stream.
120 -- | Apply folds on a stream.
121+ , foldMany
122+ , refoldMany
109 , foldSequence
123 , foldSequence
110 , foldIterateM
124 , foldIterateM
125+ , refoldIterateM
111
126
112 -- ** Parsing
127 -- ** Parsing
113 -- | Parsing is opposite to flattening. 'parseMany' is dual to concatMap or
128 -- | Parsing is opposite to flattening. 'parseMany' is dual to concatMap or
@@ -124,6 +139,7 @@ module Streamly.Internal.Data.Stream.Nesting
124
139
125 -- ** Grouping
140 -- ** Grouping
126 -- | Group segments of a stream and fold. Special case of parsing.
141 -- | Group segments of a stream and fold. Special case of parsing.
142+ , groupsOf
127 , groupsBy
143 , groupsBy
128 , groupsWhile
144 , groupsWhile
129 , groupsRollingBy
145 , groupsRollingBy
@@ -131,8 +147,9 @@ module Streamly.Internal.Data.Stream.Nesting
131 -- ** Splitting
147 -- ** Splitting
132 -- | A special case of parsing.
148 -- | A special case of parsing.
133 , wordsBy
149 , wordsBy
134− , splitOnSeq -- XXX splitOnSeg
150+ , splitOnSeq
135− , splitOnSuffixSeq -- XXX splitOnSegSuffix, splitOnTrailer
151+ , splitOnSuffixSeq
152+ , sliceOnSuffix
136
153
137 -- XXX Implement these as folds or parsers instead.
154 -- XXX Implement these as folds or parsers instead.
138 , splitOnSuffixSeqAny
155 , splitOnSuffixSeqAny
@@ -141,8 +158,9 @@ module Streamly.Internal.Data.Stream.Nesting
141
158
142 -- * Transform (Nested Containers)
159 -- * Transform (Nested Containers)
143 -- | Opposite to compact in ArrayStream
160 -- | Opposite to compact in ArrayStream
144− , splitInnerBy -- XXX innerSplitOn
161+ , splitInnerBy
145− , splitInnerBySuffix -- XXX innerSplitOnSuffix
162+ , splitInnerBySuffix
163+ , intersectBySorted
146
164
147 -- * Reduce By Streams
165 -- * Reduce By Streams
148 , dropPrefix
166 , dropPrefix
@@ -164,9 +182,12 @@ import Fusion.Plugin.Types (Fuse(..))
164import GHC.Types (SPEC(..))
182import GHC.Types (SPEC(..))
165
183
166import Streamly.Internal.Data.Array.Type (Array(..))
184import Streamly.Internal.Data.Array.Type (Array(..))
185+import Streamly.Internal.Data.Fold.Step (Step(..))
167import Streamly.Internal.Data.Fold.Type (Fold(..))
186import Streamly.Internal.Data.Fold.Type (Fold(..))
168import Streamly.Internal.Data.Parser (ParseError(..))
187import Streamly.Internal.Data.Parser (ParseError(..))
188+import Streamly.Internal.Data.Refold.Type (Refold(..))
169import Streamly.Internal.Data.SVar.Type (adaptState)
189import Streamly.Internal.Data.SVar.Type (adaptState)
190+import Streamly.Internal.Data.Tuple.Strict (Tuple'(..))
170import Streamly.Internal.Data.Unbox (Unbox, sizeOf)
191import Streamly.Internal.Data.Unbox (Unbox, sizeOf)
171import Streamly.Internal.Data.Unfold.Type (Unfold(..))
192import Streamly.Internal.Data.Unfold.Type (Unfold(..))
172
193
@@ -592,6 +613,49 @@ mergeFstBy :: -- Monad m =>
592mergeFstBy _f _m1 _m2 = undefined
613mergeFstBy _f _m1 _m2 = undefined
593 -- fromStreamK $ D.mergeFstBy f (toStreamD m1) (toStreamD m2)
614 -- fromStreamK $ D.mergeFstBy f (toStreamD m1) (toStreamD m2)
594
615
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+
595------------------------------------------------------------------------------
659------------------------------------------------------------------------------
596-- Combine N Streams - unfoldMany
660-- Combine N Streams - unfoldMany
597------------------------------------------------------------------------------
661------------------------------------------------------------------------------
@@ -1230,6 +1294,77 @@ foldIterateM func seed0 (Stream step state) =
1230 stepOuter _ (FIterYield a next) = return $ Yield a next
1294 stepOuter _ (FIterYield a next) = return $ Yield a next
1231 stepOuter _ FIterStop = return Stop
1295 stepOuter _ FIterStop = return Stop
1232
1296
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+
1233------------------------------------------------------------------------------
1368------------------------------------------------------------------------------
1234-- Parsing
1369-- Parsing
1235------------------------------------------------------------------------------
1370------------------------------------------------------------------------------
@@ -2930,13 +3065,12 @@ splitInnerBy splitter joiner (Stream step1 state1) =
2930-- | Performs infix separator style splitting.
3065-- | Performs infix separator style splitting.
2931{-# INLINE_NORMAL splitInnerBySuffix #-}
3066{-# INLINE_NORMAL splitInnerBySuffix #-}
2932splitInnerBySuffix
3067splitInnerBySuffix
2933− :: Monad m
3068+ :: (Monad m, Eq (f a), Monoid (f a))
2934− => (f a -> Bool) -- isEmpty?
3069+ => (f a -> m (f a, Maybe (f a))) -- splitter
2935− -> (f a -> m (f a, Maybe (f a))) -- splitter
2936 -> (f a -> f a -> m (f a)) -- joiner
3070 -> (f a -> f a -> m (f a)) -- joiner
2937 -> Stream m (f a)
3071 -> Stream m (f a)
2938 -> Stream m (f a)
3072 -> Stream m (f a)
2939−splitInnerBySuffix isEmpty splitter joiner (Stream step1 state1) =
3073+splitInnerBySuffix splitter joiner (Stream step1 state1) =
2940 Stream step (SplitInitial state1)
3074 Stream step (SplitInitial state1)
2941
3075
2942 where
3076 where
@@ -2963,11 +3097,10 @@ splitInnerBySuffix isEmpty splitter joiner (Stream step1 state1) =
2963 Nothing -> Skip (SplitBuffering s buf')
3097 Nothing -> Skip (SplitBuffering s buf')
2964 Just x2 -> Skip (SplitYielding buf' (SplitSplitting s x2))
3098 Just x2 -> Skip (SplitYielding buf' (SplitSplitting s x2))
2965 Skip s -> return $ Skip (SplitBuffering s buf)
3099 Skip s -> return $ Skip (SplitBuffering s buf)
2966− Stop ->
3100+ Stop -> return $
2967− return $
3101+ if buf == mempty
2968− if isEmpty buf
3102+ then Stop
2969− then Stop
3103+ else Skip (SplitYielding buf SplitFinishing)
2970− else Skip (SplitYielding buf SplitFinishing)
2971
3104
2972 step _ (SplitSplitting st buf) = do
3105 step _ (SplitSplitting st buf) = do
2973 (x1, mx2) <- splitter buf
3106 (x1, mx2) <- splitter buf
@@ -2986,8 +3119,6 @@ splitInnerBySuffix isEmpty splitter joiner (Stream step1 state1) =
2986--
3119--
2987-- Space: @O(1)@
3120-- Space: @O(1)@
2988--
3121--
2989−-- See also stripPrefix.
2990−--
2991-- /Unimplemented/
3122-- /Unimplemented/
2992{-# INLINE dropPrefix #-}
3123{-# INLINE dropPrefix #-}
2993dropPrefix ::
3124dropPrefix ::
@@ -3000,8 +3131,6 @@ dropPrefix = error "Not implemented yet!"
3000--
3131--
3001-- Space: @O(n)@ where n is the length of the infix.
3132-- Space: @O(n)@ where n is the length of the infix.
3002--
3133--
3003−-- See also stripInfix.
3004−--
3005-- /Unimplemented/
3134-- /Unimplemented/
3006{-# INLINE dropInfix #-}
3135{-# INLINE dropInfix #-}
3007dropInfix ::
3136dropInfix ::
@@ -3014,8 +3143,6 @@ dropInfix = error "Not implemented yet!"
3014--
3143--
3015-- Space: @O(n)@ where n is the length of the suffix.
3144-- Space: @O(n)@ where n is the length of the suffix.
3016--
3145--
3017−-- See also stripSuffix.
3018−--
3019-- /Unimplemented/
3146-- /Unimplemented/
3020{-# INLINE dropSuffix #-}
3147{-# INLINE dropSuffix #-}
3021dropSuffix ::
3148dropSuffix ::
core/src/Streamly/Internal/Data/Stream/Top.hs+115−243
@@ -8,73 +8,38 @@
8-- Portability : GHC
8-- Portability : GHC
9--
9--
10-- Top level module that can depend on all other lower level Stream modules.
10-- 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.
48
11
49module Streamly.Internal.Data.Stream.Top
12module Streamly.Internal.Data.Stream.Top
50 (
13 (
51− -- * Sampling
14+ -- * Transformation
15+ -- ** Sampling
52 -- | Value agnostic filtering.
16 -- | Value agnostic filtering.
53 strideFromThen
17 strideFromThen
54
18
55− -- * Straight Joins
19+ -- * Nesting
56− -- | These are set-like operations but not exactly set operations because
20+ -- ** Set like operations
57− -- streams are not necessarily sets, they may have duplicated elements.
21+ -- | These are not exactly set operations because streams are not
58− -- These operations are generic i.e. they work on streams of unconstrained
22+ -- necessarily sets, they may have duplicated elements. These operations
59− -- types, therefore, they have quadratic performance characterstics. For
23+ -- are generic i.e. they work on streams of unconstrained types, therefore,
60− -- better performance using Set or Map structures see the
24+ -- they have quadratic performance characterstics. For better performance
61− -- Streamly.Internal.Data.Stream.Container module.
25+ -- using Set structures see the Streamly.Internal.Data.Stream.Container
62− , intersectBy
26+ -- module.
63− , deleteFirstsBy
27+ , filterInStreamGenericBy
64− , unionBy
28+ , deleteInStreamGenericBy
65−
29+ , unionWithStreamGenericBy
66− -- Set like operations on sorted streams
30+
67− , sortedIntersectBy
31+ -- ** Set like operations on sorted streams
68− , sortedDeleteFirstsBy
32+ , filterInStreamAscBy
69− , sortedUnionBy
33+ , deleteInStreamAscBy
70−
34+ , unionWithStreamAscBy
71− -- * Cross Joins
35+
72− , innerJoin
36+ -- ** Join operations
73−
37+ , joinInnerGeneric
74− -- Joins on sorted stream
38+
75− , innerSortedJoin
39+ -- * Joins on sorted stream
76− , leftSortedJoin
40+ , joinInnerAscBy
77− , outerSortedJoin
41+ , joinLeftAscBy
42+ , joinOuterAscBy
78 )
43 )
79where
44where
80
45
@@ -83,7 +48,7 @@ where
83import Control.Monad.IO.Class (MonadIO(..))
48import Control.Monad.IO.Class (MonadIO(..))
84import Data.IORef (newIORef, readIORef, modifyIORef')
49import Data.IORef (newIORef, readIORef, modifyIORef')
85import Streamly.Internal.Data.Fold.Type (Fold)
50import Streamly.Internal.Data.Fold.Type (Fold)
86−import Streamly.Internal.Data.Stream.Type (Stream(..), Step(..), cross)
51+import Streamly.Internal.Data.Stream.Type (Stream, cross)
87
52
88import qualified Data.List as List
53import qualified Data.List as List
89import qualified Streamly.Internal.Data.Fold as Fold
54import qualified Streamly.Internal.Data.Fold as Fold
@@ -145,40 +110,29 @@ strideFromThen offset stride =
145-- binary search if we have an Ord instance or Ordering returning function. The
110-- binary search if we have an Ord instance or Ordering returning function. The
146-- time complexity would then become (m x log n).
111-- time complexity would then become (m x log n).
147
112
148−-- | Like 'cross' but emits only those tuples where @a == b@ using the supplied
113+-- | Like 'cross' but emits only those tuples where @a == b@ using the
149−-- equality predicate. This is essentially a @cross intersection@ of two
114+-- supplied equality predicate.
150−-- streams.
151--
115--
152-- Definition:
116-- Definition:
153--
117--
154−-- >>> innerJoin eq s1 s2 = Stream.filter (\(a, b) -> a `eq` b) $ Stream.cross s1 s2
118+-- >>> joinInnerGeneric 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.
162--
119--
163−-- If you care about performance this function should be your last choice among
120+-- You should almost always prefer @joinInnerOrd@ over 'joinInnerGeneric' if
164−-- all inner joins. 'Streamly.Internal.Data.Unfold.innerJoin' is a much faster
121+-- possible. @joinInnerOrd@ is an order of magnitude faster but may take more
165−-- fused alternative. 'innerSortedJoin' is a faster alternative when streams
122+-- space for caching the second stream.
166−-- are sorted. 'innerOrdJoin' is an order of magnitude faster alternative when
167−-- the type has an 'Ord' instance.
168--
123--
169−-- Note: Conceptually, this is a commutative operation. Result includes all the
124+-- See 'Streamly.Internal.Data.Unfold.joinInnerGeneric' for a much faster fused
170−-- elements from the left and the right stream. The order of streams can be
125+-- alternative.
171−-- changed without affecting results, except for the ordering within the tuple.
172--
126--
173-- Time: O(m x n)
127-- Time: O(m x n)
174--
128--
175-- /Pre-release/
129-- /Pre-release/
176−{-# INLINE innerJoin #-}
130+{-# INLINE joinInnerGeneric #-}
177−innerJoin :: Monad m =>
131+joinInnerGeneric :: Monad m =>
178 (a -> b -> Bool) -> Stream m a -> Stream m b -> Stream m (a, b)
132 (a -> b -> Bool) -> Stream m a -> Stream m b -> Stream m (a, b)
179−innerJoin eq s1 s2 = Stream.filter (\(a, b) -> a `eq` b) $ cross s1 s2
133+joinInnerGeneric eq s1 s2 = Stream.filter (\(a, b) -> a `eq` b) $ cross s1 s2
180{-
134{-
181−innerJoin eq s1 s2 = do
135+joinInnerGeneric eq s1 s2 = do
182 -- ConcatMap works faster than bind
136 -- ConcatMap works faster than bind
183 Stream.concatMap (\a ->
137 Stream.concatMap (\a ->
184 Stream.concatMap (\b ->
138 Stream.concatMap (\b ->
@@ -189,44 +143,44 @@ innerJoin eq s1 s2 = do
189 ) s1
143 ) s1
190-}
144-}
191
145
192−-- | A more efficient 'innerJoin' for sorted streams.
146+-- | A more efficient 'joinInner' for sorted streams.
193--
147--
194-- Space: O(1)
148-- Space: O(1)
195--
149--
196-- Time: O(m + n)
150-- Time: O(m + n)
197--
151--
198-- /Unimplemented/
152-- /Unimplemented/
199−{-# INLINE innerSortedJoin #-}
153+{-# INLINE joinInnerAscBy #-}
200−innerSortedJoin ::
154+joinInnerAscBy ::
201 (a -> b -> Ordering) -> Stream m a -> Stream m b -> Stream m (a, b)
155 (a -> b -> Ordering) -> Stream m a -> Stream m b -> Stream m (a, b)
202−innerSortedJoin = undefined
156+joinInnerAscBy = undefined
203
157
204−-- | A more efficient 'leftJoin' for sorted streams.
158+-- | A more efficient 'joinLeft' for sorted streams.
205--
159--
206-- Space: O(1)
160-- Space: O(1)
207--
161--
208-- Time: O(m + n)
162-- Time: O(m + n)
209--
163--
210-- /Unimplemented/
164-- /Unimplemented/
211−{-# INLINE leftSortedJoin #-}
165+{-# INLINE joinLeftAscBy #-}
212−leftSortedJoin :: -- Monad m =>
166+joinLeftAscBy :: -- Monad m =>
213 (a -> b -> Ordering) -> Stream m a -> Stream m b -> Stream m (a, Maybe b)
167 (a -> b -> Ordering) -> Stream m a -> Stream m b -> Stream m (a, Maybe b)
214−leftSortedJoin _eq _s1 _s2 = undefined
168+joinLeftAscBy _eq _s1 _s2 = undefined
215
169
216−-- | A more efficient 'outerJoin' for sorted streams.
170+-- | A more efficient 'joinOuter' for sorted streams.
217--
171--
218-- Space: O(1)
172-- Space: O(1)
219--
173--
220-- Time: O(m + n)
174-- Time: O(m + n)
221--
175--
222-- /Unimplemented/
176-- /Unimplemented/
223−{-# INLINE outerSortedJoin #-}
177+{-# INLINE joinOuterAscBy #-}
224−outerSortedJoin :: -- Monad m =>
178+joinOuterAscBy :: -- Monad m =>
225 (a -> b -> Ordering)
179 (a -> b -> Ordering)
226 -> Stream m a
180 -> Stream m a
227 -> Stream m b
181 -> Stream m b
228 -> Stream m (Maybe a, Maybe b)
182 -> Stream m (Maybe a, Maybe b)
229−outerSortedJoin _eq _s1 _s2 = undefined
183+joinOuterAscBy _eq _s1 _s2 = undefined
230
184
231------------------------------------------------------------------------------
185------------------------------------------------------------------------------
232-- Set operations (special joins)
186-- Set operations (special joins)
@@ -236,8 +190,8 @@ outerSortedJoin _eq _s1 _s2 = undefined
236-- the best would be to use an Array with linear search. If the second stream
190-- the best would be to use an Array with linear search. If the second stream
237-- is sorted we can also use a binary search, using Ord constraint.
191-- is sorted we can also use a binary search, using Ord constraint.
238
192
239−-- | Keep only those elements in the first stream that are present in the
193+-- | Keep only those elements in the second stream that are present in the
240−-- second stream too. The second stream is folded to a container using the
194+-- first stream too. The first stream is folded to a container using the
241-- supplied fold and then the elements in the container are looked up using the
195-- supplied fold and then the elements in the container are looked up using the
242-- supplied lookup function.
196-- supplied lookup function.
243--
197--
@@ -252,30 +206,23 @@ filterStreamWith :: Monad m =>
252filterStreamWith fld member s1 s2 =
206filterStreamWith fld member s1 s2 =
253 Stream.concatEffect
207 Stream.concatEffect
254 $ do
208 $ do
255− xs <- Stream.fold fld s2
209+ xs <- Stream.fold fld s1
256− return $ Stream.filter (`member` xs) s1
210+ return $ Stream.filter (`member` xs) s2
257−
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
211
267−-- | 'intersectBy' returns a subsequence of the first stream which intersects
212+-- | 'filterInStreamGenericBy' retains only those elements in the second stream that
268−-- with the second stream. Note that this is not a commutative operation unlike
213+-- are present in the first stream.
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'.
272--
214--
273−-- >>> f s1 s2 = Stream.fold Fold.toList $ Stream.intersectBy (==) (Stream.fromList s1) (Stream.fromList s2)
215+-- >>> Stream.fold Fold.toList $ Stream.filterInStreamGenericBy (==) (Stream.fromList [1,2,2,4]) (Stream.fromList [2,1,1,3])
274−-- >>> f [1,3,4,4,5]) [2,3,4,5,5]
216+-- [2,1,1]
275−-- [3,4,4,5]
276--
217--
277−-- First stream can be infinite, the second stream must be finite and must be
218+-- >>> Stream.fold Fold.toList $ Stream.filterInStreamGenericBy (==) (Stream.fromList [2,1,1,3]) (Stream.fromList [1,2,2,4])
278−-- capable of multiple evaluations.
219+-- [1,2,2]
220+--
221+-- Similar to the list intersectBy operation but with the stream argument order
222+-- flipped.
223+--
224+-- The first stream must be finite and must not block. Second stream is
225+-- processed only after the first stream is fully realized.
279--
226--
280-- Space: O(n) where @n@ is the number of elements in the second stream.
227-- Space: O(n) where @n@ is the number of elements in the second stream.
281--
228--
@@ -283,87 +230,46 @@ filterStreamWith fld member s1 s2 =
283-- @n@ is the number of elements in the second stream.
230-- @n@ is the number of elements in the second stream.
284--
231--
285-- /Pre-release/
232-- /Pre-release/
286−{-# INLINE intersectBy #-}
233+{-# INLINE filterInStreamGenericBy #-}
287−intersectBy :: Monad m =>
234+filterInStreamGenericBy :: Monad m =>
288 (a -> a -> Bool) -> Stream m a -> Stream m a -> Stream m a
235 (a -> a -> Bool) -> Stream m a -> Stream m a -> Stream m a
289−intersectBy eq =
236+filterInStreamGenericBy eq =
290 -- XXX Use an (unboxed) array instead.
237 -- XXX Use an (unboxed) array instead.
291 filterStreamWith
238 filterStreamWith
292 (Fold.scanMaybe (Fold.uniqBy eq) Fold.toListRev)
239 (Fold.scanMaybe (Fold.uniqBy eq) Fold.toListRev)
293 (List.any . eq)
240 (List.any . eq)
294
241
295−-------------------------------------------------------------------------------
242+-- | Like 'filterInStreamGenericBy' but assumes that the input streams are sorted in
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
304-- ascending order. To use it on streams sorted in descending order pass an
243-- ascending order. To use it on streams sorted in descending order pass an
305-- inverted comparison function returning GT for less than and LT for greater
244-- inverted comparison function returning GT for less than and LT for greater
306-- than.
245-- than.
307--
246--
308−-- Both streams can be infinite.
309−--
310-- Space: O(1)
247-- Space: O(1)
311--
248--
312-- Time: O(m+n)
249-- Time: O(m+n)
313--
250--
314-- /Pre-release/
251-- /Pre-release/
315−{-# INLINE_NORMAL sortedIntersectBy #-}
252+{-# INLINE filterInStreamAscBy #-}
316−sortedIntersectBy :: Monad m =>
253+filterInStreamAscBy :: Monad m =>
317 (a -> a -> Ordering) -> Stream m a -> Stream m a -> Stream m a
254 (a -> a -> Ordering) -> Stream m a -> Stream m a -> Stream m a
318−sortedIntersectBy cmp (Stream stepa ta) (Stream stepb tb) =
255+filterInStreamAscBy eq s1 s2 = Stream.intersectBySorted eq s2 s1
319− Stream step
320− ( ta -- left stream state
321− , tb -- right stream state
322− , Nothing -- left value
323− , Nothing -- right value
324− )
325−
326− where
327
256
328− {-# INLINE_LATE step #-}
257+-- | Delete all elements of the first stream from the seconds stream. If an
329− -- step 1, fetch the first value
258+-- element occurs multiple times in the first stream as many occurrences of it
330− step gst (sa, sb, Nothing, b) = do
259+-- are deleted from the second stream.
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'.
356--
260--
357−-- >>> f xs ys = Stream.fold Fold.toList $ Stream.deleteFirstsBy (==) (Stream.fromList xs) (Stream.fromList ys)
261+-- >>> Stream.fold Fold.toList $ Stream.deleteInStreamGenericBy (==) (Stream.fromList [1,2,3]) (Stream.fromList [1,2,2])
358−-- >>> f [1,2,2,3,3,5] [1,2,2,3,4]
262+-- [2]
359−-- [2,3,5]
360--
263--
361−-- The following holds:
264+-- The following laws hold:
362--
265--
363−-- > deleteFirstsBy (==) (Stream.nub s2 `append` s1) s2 === s1
266+-- > deleteInStreamGenericBy (==) s1 (s1 `append` s2) === s2
364−-- > deleteFirstsBy (==) (Stream.nub s2 `interleave` s1) s2 === s1
267+-- > deleteInStreamGenericBy (==) s1 (s1 `interleave` s2) === s2
365--
268--
366−-- First stream can be infinite, second stream must be finite.
269+-- Same as the list 'Data.List.//' operation but with argument order flipped.
270+--
271+-- The first stream must be finite and must not block. Second stream is
272+-- processed only after the first stream is fully realized.
367--
273--
368-- Space: O(m) where @m@ is the number of elements in the first stream.
274-- Space: O(m) where @m@ is the number of elements in the first stream.
369--
275--
@@ -371,83 +277,58 @@ sortedIntersectBy cmp (Stream stepa ta) (Stream stepb tb) =
371-- @n@ is the number of elements in the second stream.
277-- @n@ is the number of elements in the second stream.
372--
278--
373-- /Pre-release/
279-- /Pre-release/
374−{-# INLINE deleteFirstsBy #-}
280+{-# INLINE deleteInStreamGenericBy #-}
375−deleteFirstsBy :: Monad m =>
281+deleteInStreamGenericBy :: Monad m =>
376 (a -> a -> Bool) -> Stream m a -> Stream m a -> Stream m a
282 (a -> a -> Bool) -> Stream m a -> Stream m a -> Stream m a
377−deleteFirstsBy eq s2 s1 =
283+deleteInStreamGenericBy eq s1 s2 =
378− -- XXX s2 can be a sorted mutable array and we can use binary
284+ Stream.concatEffect
379− -- search to find. Mark the element deleted, count the deletions
285+ $ do
380− -- and reconsolidate the array when a min number of elements is
286+ -- This may work well if s1 is small
381− -- deleted.
287+ -- If s1 is big we can go through s1, deleting elements from s2 and
382−
288+ -- not emitting an element if it was successfully deleted from s2.
383− -- XXX Use StreamK or list as second argument instead of Stream to avoid
289+ -- we will need a deleteBy that can return whether the element was
384− -- concatEffect?
290+ -- deleted or not.
385− Stream.concatEffect $ do
291+ xs <- Stream.fold Fold.toList s2
386− xs <- Stream.toList s1
292+ let f = Fold.foldl' (flip (List.deleteBy eq)) xs
387− -- It reverses the list but that is fine.
293+ fmap Stream.fromList $ Stream.fold f s1
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
294
403−-- | A more efficient 'deleteFirstsBy' for streams sorted in ascending order.
295+-- | A more efficient 'deleteInStreamGenericBy' for streams sorted in ascending order.
404−--
405−-- Both streams can be infinite.
406--
296--
407-- Space: O(1)
297-- Space: O(1)
408--
298--
409-- /Unimplemented/
299-- /Unimplemented/
410−{-# INLINE sortedDeleteFirstsBy #-}
300+{-# INLINE deleteInStreamAscBy #-}
411−sortedDeleteFirstsBy :: -- (Monad m) =>
301+deleteInStreamAscBy :: -- (Monad m) =>
412 (a -> a -> Ordering) -> Stream m a -> Stream m a -> Stream m a
302 (a -> a -> Ordering) -> Stream m a -> Stream m a -> Stream m a
413−sortedDeleteFirstsBy _eq _s1 _s2 = undefined
303+deleteInStreamAscBy _eq _s1 _s2 = undefined
414
304
415-- XXX Remove the MonadIO constraint. We can just cache one stream and then
305-- XXX Remove the MonadIO constraint. We can just cache one stream and then
416-- implement using differenceEqBy.
306-- implement using differenceEqBy.
417
307
418−-- | Returns the first stream appended with those unique elements from the
308+-- | This essentially appends to the second stream all the occurrences of
419−-- second stream that are not already present in the first stream. Note that
309+-- elements in the first stream that are not already present in the second
420−-- this is not a commutative operation unlike a set union, argument order
310+-- stream.
421−-- matters. The behavior is similar to the 'Data.List.unionBy'.
422--
311--
423-- Equivalent to the following except that @s2@ is evaluated only once:
312-- Equivalent to the following except that @s2@ is evaluated only once:
424--
313--
425−-- >>> unionBy eq s1 s2 = s1 `Stream.append` Stream.deleteFirstsBy eq s1 (Stream.nub s2)
314+-- >>> unionWithStreamGenericBy eq s1 s2 = s2 `Stream.append` (Stream.deleteInStreamGenericBy eq s2 s1)
426--
315--
427-- Example:
316-- Example:
428--
317--
429−-- >>> f s1 s2 = Stream.fold Fold.toList $ Stream.unionBy (==) (Stream.fromList s1) (Stream.fromList s2)
318+-- >>> Stream.fold Fold.toList $ Stream.unionWithStreamGenericBy (==) (Stream.fromList [1,1,2,3]) (Stream.fromList [1,2,2,4])
430−-- >>> f [1,2,2,4] [1,1,2,3,3]
431-- [1,2,2,4,3]
319-- [1,2,2,4,3]
432--
320--
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−--
438-- Space: O(n)
321-- Space: O(n)
439--
322--
440-- Time: O(m x n)
323-- Time: O(m x n)
441--
324--
442-- /Pre-release/
325-- /Pre-release/
443−{-# INLINE unionBy #-}
326+{-# INLINE unionWithStreamGenericBy #-}
444−unionBy :: MonadIO m =>
327+unionWithStreamGenericBy :: MonadIO m =>
445 (a -> a -> Bool) -> Stream m a -> Stream m a -> Stream m a
328 (a -> a -> Bool) -> Stream m a -> Stream m a -> Stream m a
446−unionBy eq s2 s1 =
329+unionWithStreamGenericBy eq s1 s2 =
447 Stream.concatEffect
330 Stream.concatEffect
448 $ do
331 $ 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.
451 xs <- Stream.fold Fold.toList s1
332 xs <- Stream.fold Fold.toList s1
452 -- XXX we can use postscanlMAfter' instead of IORef
333 -- XXX we can use postscanlMAfter' instead of IORef
453 ref <- liftIO $ newIORef $! List.nubBy eq xs
334 ref <- liftIO $ newIORef $! List.nubBy eq xs
@@ -460,21 +341,12 @@ unionBy eq s2 s1 =
460 return $ Stream.fromList xs1
341 return $ Stream.fromList xs1
461 return $ Stream.mapM f s2 `Stream.append` s3
342 return $ Stream.mapM f s2 `Stream.append` s3
462
343
463−-- | A more efficient 'unionBy' for sorted streams.
344+-- | A more efficient 'unionWithStreamGenericBy' 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.
471--
345--
472-- Space: O(1)
346-- Space: O(1)
473--
347--
474−-- Both streams can be infinite.
475−--
476-- /Unimplemented/
348-- /Unimplemented/
477−{-# INLINE sortedUnionBy #-}
349+{-# INLINE unionWithStreamAscBy #-}
478−sortedUnionBy :: -- (Monad m) =>
350+unionWithStreamAscBy :: -- (Monad m) =>
479 (a -> a -> Ordering) -> Stream m a -> Stream m a -> Stream m a
351 (a -> a -> Ordering) -> Stream m a -> Stream m a -> Stream m a
480−sortedUnionBy _eq _s1 _s2 = undefined
352+unionWithStreamAscBy _eq _s1 _s2 = undefined
core/src/Streamly/Internal/Data/Stream/Transform.hs+27−53
@@ -21,6 +21,8 @@ module Streamly.Internal.Data.Stream.Transform
21
21
22 -- * Mapping
22 -- * Mapping
23 -- | Stateless one-to-one maps.
23 -- | Stateless one-to-one maps.
24+ , map
25+ , mapM
24 , sequence
26 , sequence
25
27
26 -- * Mapping Effects
28 -- * Mapping Effects
@@ -73,7 +75,7 @@ module Streamly.Internal.Data.Stream.Transform
73 , scanMaybe
75 , scanMaybe
74 , filter
76 , filter
75 , filterM
77 , filterM
76− , deleteBy -- deleteOnceBy?
78+ , deleteBy
77 , uniqBy
79 , uniqBy
78 , uniq
80 , uniq
79 , prune
81 , prune
@@ -81,8 +83,9 @@ module Streamly.Internal.Data.Stream.Transform
81
83
82 -- * Trimming
84 -- * Trimming
83 -- | Produce a subset of the stream trimmed at ends.
85 -- | Produce a subset of the stream trimmed at ends.
84− , initNonEmpty
86+ , take
85− , tailNonEmpty
87+ , takeWhile
88+ , takeWhileM
86 , takeWhileLast
89 , takeWhileLast
87 , takeWhileAround
90 , takeWhileAround
88 , drop
91 , drop
@@ -129,6 +132,7 @@ module Streamly.Internal.Data.Stream.Transform
129 -- * Searching
132 -- * Searching
130 , findIndices
133 , findIndices
131 , elemIndices
134 , elemIndices
135+ , slicesBy
132
136
133 -- * Rolling map
137 -- * Rolling map
134 -- | Map using the previous element.
138 -- | Map using the previous element.
@@ -1070,55 +1074,6 @@ repeated = undefined
1070-- Trimming
1074-- Trimming
1071------------------------------------------------------------------------------
1075------------------------------------------------------------------------------
1072
1076
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−
1122-- | Take all consecutive elements at the end of the stream for which the
1077-- | Take all consecutive elements at the end of the stream for which the
1123-- predicate is true.
1078-- predicate is true.
1124--
1079--
@@ -1582,7 +1537,7 @@ reverse m = Stream step Nothing
1582{-# INLINE reverseUnbox #-}
1537{-# INLINE reverseUnbox #-}
1583reverseUnbox :: (MonadIO m, Unbox a) => Stream m a -> Stream m a
1538reverseUnbox :: (MonadIO m, Unbox a) => Stream m a -> Stream m a
1584reverseUnbox =
1539reverseUnbox =
1585− A.concatRev -- unfoldMany A.readerRev
1540+ A.flattenArraysRev -- unfoldMany A.readRev
1586 . fromStreamK
1541 . fromStreamK
1587 . K.reverse
1542 . K.reverse
1588 . toStreamK
1543 . toStreamK
@@ -1754,6 +1709,25 @@ findIndices p (Stream step state) = Stream step' (state, 0)
1754elemIndices :: (Monad m, Eq a) => a -> Stream m a -> Stream m Int
1709elemIndices :: (Monad m, Eq a) => a -> Stream m a -> Stream m Int
1755elemIndices a = findIndices (== a)
1710elemIndices a = findIndices (== a)
1756
1711
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+
1757------------------------------------------------------------------------------
1731------------------------------------------------------------------------------
1758-- Rolling map
1732-- Rolling map
1759------------------------------------------------------------------------------
1733------------------------------------------------------------------------------
core/src/Streamly/Internal/Data/Stream/Transformer.hs+1−2
@@ -11,11 +11,10 @@
11
11
12module Streamly.Internal.Data.Stream.Transformer
12module Streamly.Internal.Data.Stream.Transformer
13 (
13 (
14− -- * Fold to Transformer Monad
15 foldlT
14 foldlT
16 , foldrT
15 , foldrT
17
16
18− -- * Inner Monad Operations
17+ -- * Transform Inner Monad
19 , liftInner
18 , liftInner
20 , runReaderT
19 , runReaderT
21 , usingReaderT
20 , usingReaderT
core/src/Streamly/Internal/Data/Stream/Type.hs+3−132
@@ -1,11 +1,5 @@
1{-# LANGUAGE CPP #-}
1{-# 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 #-}
7{-# LANGUAGE UndecidableInstances #-}
2{-# LANGUAGE UndecidableInstances #-}
8−{-# LANGUAGE ViewPatterns #-}
9
3
10-- |
4-- |
11-- Module : Streamly.Internal.Data.Stream.Type
5-- Module : Streamly.Internal.Data.Stream.Type
@@ -22,18 +16,18 @@
22
16
23module Streamly.Internal.Data.Stream.Type
17module Streamly.Internal.Data.Stream.Type
24 (
18 (
25− -- * Type
19+ -- * The stream type
26 Step (..)
20 Step (..)
27 -- XXX UnStream is exported to avoid a performance issue in some
21 -- XXX UnStream is exported to avoid a performance issue in some
28 -- combinators if we use the pattern synonym "Stream".
22 -- combinators if we use the pattern synonym "Stream".
29 , Stream (Stream, UnStream)
23 , Stream (Stream, UnStream)
30
24
31− -- * CrossStream
25+ -- * CrossStream type wrapper
32 , CrossStream
26 , CrossStream
33 , unCross
27 , unCross
34 , mkCross
28 , mkCross
35
29
36− -- * To StreamK
30+ -- * Conversion to StreamK
37 , fromStreamK
31 , fromStreamK
38 , toStreamK
32 , toStreamK
39
33
@@ -91,7 +85,6 @@ module Streamly.Internal.Data.Stream.Type
91
85
92 -- * Combining Two Streams
86 -- * Combining Two Streams
93 -- ** Zipping
87 -- ** Zipping
94− -- | Zip corresponding elements of two streams.
95 , zipWithM
88 , zipWithM
96 , zipWith
89 , zipWith
97
90
@@ -107,8 +100,6 @@ module Streamly.Internal.Data.Stream.Type
107 , unfoldMany
100 , unfoldMany
108
101
109 -- * Concat
102 -- * Concat
110− -- | Generate streams by mapping a stream generator on each element of an
111− -- input stream, append the resulting streams and flatten.
112 , concatEffect
103 , concatEffect
113 , concatMap
104 , concatMap
114 , concatMapM
105 , concatMapM
@@ -132,22 +123,14 @@ module Streamly.Internal.Data.Stream.Type
132 , foldManyPost
123 , foldManyPost
133 , groupsOf
124 , groupsOf
134 , refoldMany
125 , refoldMany
135− , refoldIterateM
136
126
137 -- * Fold Iterate
127 -- * Fold Iterate
138 , reduceIterateBfs
128 , reduceIterateBfs
139 , foldIterateBfs
129 , foldIterateBfs
140
130
141− -- * Splitting
142− , indexOnSuffix
143−
144 -- * Multi-stream folds
131 -- * Multi-stream folds
145− -- | These should probably be expressed using zipping operations.
146 , eqBy
132 , eqBy
147 , cmpBy
133 , cmpBy
148−
149− -- * Deprecated
150− , sliceOnSuffix
151 )
134 )
152where
135where
153
136
@@ -179,7 +162,6 @@ import Streamly.Internal.Data.Maybe.Strict (Maybe'(..), toMaybe)
179import Streamly.Internal.Data.Refold.Type (Refold(..))
162import Streamly.Internal.Data.Refold.Type (Refold(..))
180import Streamly.Internal.Data.Stream.Step (Step (..))
163import Streamly.Internal.Data.Stream.Step (Step (..))
181import Streamly.Internal.Data.SVar.Type (State, adaptState, defState)
164import Streamly.Internal.Data.SVar.Type (State, adaptState, defState)
182−import Streamly.Internal.Data.Tuple.Strict (Tuple'(..))
183import Streamly.Internal.Data.Unfold.Type (Unfold(..))
165import Streamly.Internal.Data.Unfold.Type (Unfold(..))
184
166
185import qualified Streamly.Internal.Data.Fold.Type as FL hiding (foldr)
167import qualified Streamly.Internal.Data.Fold.Type as FL hiding (foldr)
@@ -230,9 +212,6 @@ pattern Stream step state <- (unShare -> UnStream step state)
230nilM :: Applicative m => m b -> Stream m a
212nilM :: Applicative m => m b -> Stream m a
231nilM m = Stream (\_ _ -> m $> Stop) ()
213nilM m = Stream (\_ _ -> m $> Stop) ()
232
214
233−
234−infixr 5 `consM`
235−
236-- | Like 'cons' but fuses an effect instead of a pure value.
215-- | Like 'cons' but fuses an effect instead of a pure value.
237{-# INLINE_NORMAL consM #-}
216{-# INLINE_NORMAL consM #-}
238consM :: Applicative m => m a -> Stream m a -> Stream m a
217consM :: Applicative m => m a -> Stream m a -> Stream m a
@@ -572,12 +551,8 @@ foldAdd f =
572-- >>> Stream.foldrM step (return False) s
551-- >>> Stream.foldrM step (return False) s
573-- True
552-- True
574--
553--
575−-- >>> import Control.Monad (join)
576−-- >>> foldrM f z = join $ Stream.foldr f z
577−--
578{-# INLINE_NORMAL foldrM #-}
554{-# INLINE_NORMAL foldrM #-}
579foldrM :: Monad m => (a -> m b -> m b) -> m b -> Stream m a -> m b
555foldrM :: 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
581foldrM f z (Stream step state) = go SPEC state
556foldrM f z (Stream step state) = go SPEC state
582 where
557 where
583 {-# INLINE_LATE go #-}
558 {-# INLINE_LATE go #-}
@@ -1996,110 +1971,6 @@ refoldMany (Refold fstep inject extract) action (Stream step state) =
1996 step' _ (FoldManyYield b next) = return $ Yield b next
1971 step' _ (FoldManyYield b next) = return $ Yield b next
1997 step' _ FoldManyDone = return Stop
1972 step' _ FoldManyDone = return Stop
1998
1973
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−
2103------------------------------------------------------------------------------
1974------------------------------------------------------------------------------
2104-- Stream with a cross product style monad instance
1975-- Stream with a cross product style monad instance
2105------------------------------------------------------------------------------
1976------------------------------------------------------------------------------
core/src/Streamly/Internal/Data/StreamK.hs+28−19
@@ -16,15 +16,16 @@ module Streamly.Internal.Data.StreamK
16 --
16 --
17 -- $setup
17 -- $setup
18
18
19+ -- * The stream type
19 module Streamly.Internal.Data.StreamK.Type
20 module Streamly.Internal.Data.StreamK.Type
20− -- * Transformer
21 , module Streamly.Internal.Data.StreamK.Transformer
21 , module Streamly.Internal.Data.StreamK.Transformer
22
22
23− -- * From containers
23+ , StreamK(..)
24 , fromList
24 , fromList
25 , fromStream
25 , fromStream
26+ , toStream
26
27
27− -- * Specialized Generation
28+ -- ** Specialized Generation
28 , repeatM
29 , repeatM
29 , replicate
30 , replicate
30 , replicateM
31 , replicateM
@@ -36,6 +37,8 @@ module Streamly.Internal.Data.StreamK
36 -- * Elimination
37 -- * Elimination
37 -- ** General Folds
38 -- ** General Folds
38 , foldr1
39 , foldr1
40+ , foldlM'
41+ , foldlMx'
39 , fold
42 , fold
40 , foldBreak
43 , foldBreak
41 , foldEither
44 , foldEither
@@ -66,13 +69,13 @@ module Streamly.Internal.Data.StreamK
66 , find
69 , find
67 , (!!)
70 , (!!)
68
71
69− -- ** To Containers
70− , toList
71− , toStream
72−
73 -- ** Map and Fold
72 -- ** Map and Fold
74 , mapM_
73 , mapM_
75
74
75+ -- ** Conversions
76+ , toList
77+ , hoist
78+
76 -- * Transformation
79 -- * Transformation
77 -- ** By folding (scans)
80 -- ** By folding (scans)
78 , scanl'
81 , scanl'
@@ -99,7 +102,6 @@ module Streamly.Internal.Data.StreamK
99
102
100 -- ** Reordering
103 -- ** Reordering
101 , sortBy
104 , sortBy
102− , sortOn
103
105
104 -- ** Map and Filter
106 -- ** Map and Filter
105 , mapMaybe
107 , mapMaybe
@@ -115,9 +117,6 @@ module Streamly.Internal.Data.StreamK
115 -- ** Transformation comprehensions
117 -- ** Transformation comprehensions
116 , the
118 , the
117
119
118− -- ** Transforming Inner Monad
119− , hoist
120−
121 -- * Exceptions
120 -- * Exceptions
122 , handle
121 , handle
123
122
@@ -134,7 +133,6 @@ import Control.Exception (mask_, Exception)
134import Control.Monad (void, join)
133import Control.Monad (void, join)
135import Control.Monad.Catch (MonadCatch)
134import Control.Monad.Catch (MonadCatch)
136import Control.Monad.IO.Class (MonadIO(..))
135import Control.Monad.IO.Class (MonadIO(..))
137−import Data.Ord (comparing)
138import Data.Proxy (Proxy(..))
136import Data.Proxy (Proxy(..))
139import GHC.Types (SPEC(..))
137import GHC.Types (SPEC(..))
140import Streamly.Internal.Data.Array.Type (Array(..))
138import Streamly.Internal.Data.Array.Type (Array(..))
@@ -308,6 +306,19 @@ foldr1 step m = do
308 yieldk a r = fmap (step p) (go a r)
306 yieldk a r = fmap (step p) (go a r)
309 in foldStream defState yieldk single stp m1
307 in foldStream defState yieldk single stp m1
310
308
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+
311-- | Fold a stream using the supplied left 'Fold' and reducing the resulting
322-- | Fold a stream using the supplied left 'Fold' and reducing the resulting
312-- expression strictly at each step. The behavior is similar to 'foldl''. A
323-- expression strictly at each step. The behavior is similar to 'foldl''. A
313-- 'Fold' can terminate early without consuming the full stream. See the
324-- 'Fold' can terminate early without consuming the full stream. See the
@@ -459,6 +470,11 @@ foldConcat
459 Stream.Skip s -> go1 SPEC fs s
470 Stream.Skip s -> go1 SPEC fs s
460 Stream.Stop -> return $ Left fs
471 Stream.Stop -> return $ Left fs
461
472
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+
462------------------------------------------------------------------------------
478------------------------------------------------------------------------------
463-- Specialized folds
479-- Specialized folds
464------------------------------------------------------------------------------
480------------------------------------------------------------------------------
@@ -1721,10 +1737,3 @@ sortBy cmp =
1721 . Stream.catRights -- its a non-failing backtracking parser
1737 . Stream.catRights -- its a non-failing backtracking parser
1722 . Stream.parseMany (fmap (either id id) p)
1738 . Stream.parseMany (fmap (either id id) p)
1723 . Stream.fromStreamK
1739 . 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+11−80
@@ -1,8 +1,4 @@
1{-# LANGUAGE CPP #-}
1{-# 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 #-}
6{-# LANGUAGE UndecidableInstances #-}
2{-# LANGUAGE UndecidableInstances #-}
7-- |
3-- |
8-- Module : Streamly.Internal.Data.StreamK.Type
4-- Module : Streamly.Internal.Data.StreamK.Type
@@ -88,8 +84,6 @@ module Streamly.Internal.Data.StreamK.Type
88 -- ** Strict Left Folds
84 -- ** Strict Left Folds
89 , Streamly.Internal.Data.StreamK.Type.foldl'
85 , Streamly.Internal.Data.StreamK.Type.foldl'
90 , foldlx'
86 , foldlx'
91− , foldlMx'
92− , foldlM'
93
87
94 -- ** Lazy Right Folds
88 -- ** Lazy Right Folds
95 , Streamly.Internal.Data.StreamK.Type.foldr
89 , Streamly.Internal.Data.StreamK.Type.foldr
@@ -97,11 +91,8 @@ module Streamly.Internal.Data.StreamK.Type
97 -- ** Specific Folds
91 -- ** Specific Folds
98 , drain
92 , drain
99 , null
93 , null
100− , headNonEmpty
101 , tail
94 , tail
102− , tailNonEmpty
103 , init
95 , init
104− , initNonEmpty
105
96
106 -- * Mapping
97 -- * Mapping
107 , map
98 , map
@@ -826,24 +817,6 @@ foldlx' step begin done m = get $ go m begin
826foldl' :: Monad m => (b -> a -> b) -> b -> StreamK m a -> m b
817foldl' :: Monad m => (b -> a -> b) -> b -> StreamK m a -> m b
827foldl' step begin = foldlx' step begin id
818foldl' step begin = foldlx' step begin id
828
819
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−
847------------------------------------------------------------------------------
820------------------------------------------------------------------------------
848-- Specialized folds
821-- Specialized folds
849------------------------------------------------------------------------------
822------------------------------------------------------------------------------
@@ -1814,42 +1787,14 @@ iterateMWith cns step = go
1814 !next <- s
1787 !next <- s
1815 foldStreamShared st stp sng yld (return next `cns` go (step next))
1788 foldStreamShared st stp sng yld (return next `cns` go (step next))
1816
1789
1817−-- | head for non-empty streams, fails for empty stream case.
1790+{-# INLINE headPartial #-}
1818−--
1791+headPartial :: Monad m => StreamK m a -> m a
1819−{-# INLINE headNonEmpty #-}
1792+headPartial = foldrM (\x _ -> return x) (error "head of nil")
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
1843
1793
1844−-- | tail for non-empty streams, fails for empty stream case.
1794+{-# INLINE tailPartial #-}
1845−--
1795+tailPartial :: StreamK m a -> StreamK m a
1846−-- See also 'tail' for a non-partial version of this function..
1796+tailPartial m = mkStream $ \st yld sng stp ->
1847−--
1797+ let stop = error "tail of nil"
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"
1853 single _ = stp
1798 single _ = stp
1854 yieldk _ r = foldStream st yld sng stp r
1799 yieldk _ r = foldStream st yld sng stp r
1855 in foldStream st yieldk single stop m
1800 in foldStream st yieldk single stop m
@@ -1914,10 +1859,10 @@ mfix f = mkStream $ \st yld sng stp ->
1914 where
1859 where
1915
1860
1916 -- fix the head element of the stream
1861 -- fix the head element of the stream
1917− xs = fix (f . headNonEmpty)
1862+ xs = fix (f . headPartial)
1918
1863
1919 -- now fix the tail recursively
1864 -- now fix the tail recursively
1920− ys = mfix (tailNonEmpty . f)
1865+ ys = mfix (tailPartial . f)
1921
1866
1922-------------------------------------------------------------------------------
1867-------------------------------------------------------------------------------
1923-- Conversions
1868-- Conversions
@@ -1948,15 +1893,6 @@ uncons m =
1948 yieldk a r = pure (Just (a, r))
1893 yieldk a r = pure (Just (a, r))
1949 in foldStream defState yieldk single stop m
1894 in foldStream defState yieldk single stop m
1950
1895
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−--
1960{-# INLINE tail #-}
1896{-# INLINE tail #-}
1961tail :: Applicative m => StreamK m a -> m (Maybe (StreamK m a))
1897tail :: Applicative m => StreamK m a -> m (Maybe (StreamK m a))
1962tail =
1898tail =
@@ -1965,14 +1901,9 @@ tail =
1965 yieldk _ r = pure $ Just r
1901 yieldk _ r = pure $ Just r
1966 in foldStream defState yieldk single stop
1902 in foldStream defState yieldk single stop
1967
1903
1968−-- Note that this is not a StreamK -> StreamK because then we cannot handle the
1904+-- | Extract all but the last element of the stream, if any.
1969−-- empty stream case without making this a partial function.
1970--
1905--
1971−-- XXX How do we implement unsnoc? Make StreamK a monad and return the
1906+-- Note: This will end up buffering the entire stream.
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.
1976--
1907--
1977-- /Pre-release/
1908-- /Pre-release/
1978{-# INLINE init #-}
1909{-# INLINE init #-}
core/src/Streamly/Internal/Data/Time/Units.hs+4
@@ -1,3 +1,7 @@
1+{-# LANGUAGE DataKinds #-}
2+{-# LANGUAGE PolyKinds #-}
3+{-# LANGUAGE UnboxedTuples #-}
4+
1-- |
5-- |
2-- Module : Streamly.Internal.Data.Time.Units
6-- Module : Streamly.Internal.Data.Time.Units
3-- Copyright : (c) 2019 Composewell Technologies
7-- Copyright : (c) 2019 Composewell Technologies
core/src/Streamly/Internal/Data/Unbox.hs+1−4
@@ -1,8 +1,5 @@
1{-# LANGUAGE DataKinds #-}
1{-# LANGUAGE DataKinds #-}
2−{-# LANGUAGE TypeFamilies #-}
2+{-# LANGUAGE DefaultSignatures #-}
3−-- Must come after TypeFamilies, otherwise it is re-enabled.
4−-- MonoLocalBinds enabled by TypeFamilies causes perf regressions in general.
5−{-# LANGUAGE NoMonoLocalBinds #-}
6{-# LANGUAGE UnboxedTuples #-}
3{-# LANGUAGE UnboxedTuples #-}
7{-# LANGUAGE UndecidableInstances #-}
4{-# LANGUAGE UndecidableInstances #-}
8
5
core/src/Streamly/Internal/Data/Unbox/TH.hs+1−4
@@ -241,7 +241,6 @@ mkSizeOfExpr headTy constructors =
241mkPeekExprOne :: Int -> DataCon -> Q Exp
241mkPeekExprOne :: Int -> DataCon -> Q Exp
242mkPeekExprOne tagSize (DataCon cname _ _ fields) =
242mkPeekExprOne tagSize (DataCon cname _ _ fields) =
243 case fields of
243 case fields of
244− -- XXX Should we peek and check if the byte value is 0?
245 [] -> [|pure $(conE cname)|]
244 [] -> [|pure $(conE cname)|]
246 _ ->
245 _ ->
247 letE
246 letE
@@ -337,9 +336,7 @@ mkPokeExpr headTy cons =
337 [|error
336 [|error
338 ("Attempting to poke type with no constructors (" ++
337 ("Attempting to poke type with no constructors (" ++
339 $(lift (pprint headTy)) ++ ")")|]
338 $(lift (pprint headTy)) ++ ")")|]
340− -- XXX We don't gaurentee encoded equivalilty for Unbox. Does it still
339+ [(DataCon _ _ _ [])] -> [|pure ()|]
341− -- make sense to encode a default value for unit constructor?
342− [(DataCon _ _ _ [])] -> [|pure ()|] -- mkPokeExprTag ''Word8 0
343 [(DataCon cname _ _ fields)] ->
340 [(DataCon cname _ _ fields)] ->
344 caseE
341 caseE
345 (varE _val)
342 (varE _val)
core/src/Streamly/Internal/Data/Unfold.hs+4−6
@@ -82,7 +82,7 @@ module Streamly.Internal.Data.Unfold
82 , dropWhileM
82 , dropWhileM
83
83
84 -- ** Cross product
84 -- ** Cross product
85− , innerJoin
85+ , joinInnerGeneric
86
86
87 -- ** Resource Management
87 -- ** Resource Management
88 -- | 'bracket' is the most general resource management operation, all other
88 -- | 'bracket' is the most general resource management operation, all other
@@ -677,12 +677,10 @@ dropWhileM f (Unfold step inject) = Unfold step' inject'
677dropWhile :: Monad m => (b -> Bool) -> Unfold m a b -> Unfold m a b
677dropWhile :: Monad m => (b -> Bool) -> Unfold m a b -> Unfold m a b
678dropWhile f = dropWhileM (return . f)
678dropWhile f = dropWhileM (return . f)
679
679
680−-- | Cross intersection of two unfolds. See
680+{-# INLINE_NORMAL joinInnerGeneric #-}
681−-- 'Streamly.Internal.Data.Stream.innerJoin' for more details.
681+joinInnerGeneric :: Monad m =>
682−{-# INLINE_NORMAL innerJoin #-}
683−innerJoin :: Monad m =>
684 (b -> c -> Bool) -> Unfold m a b -> Unfold m a c -> Unfold m a (b, c)
682 (b -> c -> Bool) -> Unfold m a b -> Unfold m a c -> Unfold m a (b, c)
685−innerJoin eq s1 s2 = filter (\(a, b) -> a `eq` b) $ cross s1 s2
683+joinInnerGeneric eq s1 s2 = filter (\(a, b) -> a `eq` b) $ cross s1 s2
686
684
687------------------------------------------------------------------------------
685------------------------------------------------------------------------------
688-- Exceptions
686-- Exceptions
core/src/Streamly/Internal/FileSystem/File.hs+9−4
@@ -103,14 +103,15 @@ import qualified Control.Monad.Catch as MC
103import qualified System.IO as SIO
103import qualified System.IO as SIO
104
104
105import Streamly.Data.Fold (groupsOf, drain)
105import Streamly.Data.Fold (groupsOf, drain)
106−import Streamly.Internal.Data.Array.Type (Array(..))
106+import Streamly.Internal.Data.Array.Type (Array(..), pinnedWriteNUnsafe)
107import Streamly.Internal.Data.Fold.Type (Fold(..))
107import Streamly.Internal.Data.Fold.Type (Fold(..))
108import Streamly.Data.Stream (Stream)
108import Streamly.Data.Stream (Stream)
109+import Streamly.Internal.Data.Unbox (Unbox)
109import Streamly.Internal.Data.Unfold.Type (Unfold(..))
110import Streamly.Internal.Data.Unfold.Type (Unfold(..))
110-- import Streamly.String (encodeUtf8, decodeUtf8, foldLines)
111-- import Streamly.String (encodeUtf8, decodeUtf8, foldLines)
111import Streamly.Internal.System.IO (defaultChunkSize)
112import Streamly.Internal.System.IO (defaultChunkSize)
112
113
113−import qualified Streamly.Internal.Data.Array as A
114+import qualified Streamly.Data.Array as A
114import qualified Streamly.Data.Stream as S
115import qualified Streamly.Data.Stream as S
115import qualified Streamly.Data.Unfold as UF
116import qualified Streamly.Data.Unfold as UF
116import qualified Streamly.Internal.Data.Array.Type as IA (pinnedChunksOf)
117import qualified Streamly.Internal.Data.Array.Type as IA (pinnedChunksOf)
@@ -338,6 +339,10 @@ readWithBufferOf = readerWith
338reader :: (MonadIO m, MonadCatch m) => Unfold m FilePath Word8
339reader :: (MonadIO m, MonadCatch m) => Unfold m FilePath Word8
339reader = UF.many A.reader (usingFile FH.chunkReader)
340reader = UF.many A.reader (usingFile FH.chunkReader)
340
341
342+{-# INLINE concatChunks #-}
343+concatChunks :: (Monad m, Unbox a) => Stream m (Array a) -> Stream m a
344+concatChunks = S.unfoldMany A.reader
345+
341-- | Generate a stream of bytes from a file specified by path. The stream ends
346-- | Generate a stream of bytes from a file specified by path. The stream ends
342-- when EOF is encountered. File is locked using multiple reader and single
347-- when EOF is encountered. File is locked using multiple reader and single
343-- writer locking mode.
348-- writer locking mode.
@@ -346,7 +351,7 @@ reader = UF.many A.reader (usingFile FH.chunkReader)
346--
351--
347{-# INLINE read #-}
352{-# INLINE read #-}
348read :: (MonadIO m, MonadCatch m) => FilePath -> Stream m Word8
353read :: (MonadIO m, MonadCatch m) => FilePath -> Stream m Word8
349−read file = A.concat $ withFile file ReadMode FH.readChunks
354+read file = concatChunks $ withFile file ReadMode FH.readChunks
350
355
351{-# DEPRECATED toBytes "Please use 'read' instead" #-}
356{-# DEPRECATED toBytes "Please use 'read' instead" #-}
352{-# INLINE toBytes #-}
357{-# INLINE toBytes #-}
@@ -462,7 +467,7 @@ writeChunks path = Fold step initial extract final
462writeWith :: (MonadIO m, MonadCatch m)
467writeWith :: (MonadIO m, MonadCatch m)
463 => Int -> FilePath -> Fold m Word8 ()
468 => Int -> FilePath -> Fold m Word8 ()
464writeWith n path =
469writeWith n path =
465− groupsOf n (A.unsafePinnedCreateOf n) (writeChunks path)
470+ groupsOf n (pinnedWriteNUnsafe n) (writeChunks path)
466
471
467{-# DEPRECATED writeWithBufferOf "Please use 'writeWith' instead" #-}
472{-# DEPRECATED writeWithBufferOf "Please use 'writeWith' instead" #-}
468{-# INLINE writeWithBufferOf #-}
473{-# INLINE writeWithBufferOf #-}
core/src/Streamly/Internal/FileSystem/Handle.hs+28−21
@@ -40,8 +40,8 @@ module Streamly.Internal.FileSystem.Handle
40
40
41 -- * Streams
41 -- * Streams
42 , read
42 , read
43− , readWith -- readConcatChunksOf
43+ , readWith
44− , readChunksWith -- readChunksOf
44+ , readChunksWith
45 , readChunks
45 , readChunks
46
46
47 -- * Unfolds
47 -- * Unfolds
@@ -59,10 +59,10 @@ module Streamly.Internal.FileSystem.Handle
59 -- , writeUtf8ByLines
59 -- , writeUtf8ByLines
60 -- , writeByFrames
60 -- , writeByFrames
61 -- , writeLines
61 -- , writeLines
62− , writeWith -- writeChunksOf
62+ , writeWith
63 , writeChunks
63 , writeChunks
64− , writeChunksWith -- writeCompactChunksOf
64+ , writeChunksWith
65− , writeMaybesWith -- writeCompactJustsOf
65+ , writeMaybesWith
66
66
67 -- * Refolds
67 -- * Refolds
68 , writer
68 , writer
@@ -131,13 +131,16 @@ import Streamly.Internal.Data.Fold (Fold)
131import Streamly.Internal.Data.Refold.Type (Refold(..))
131import Streamly.Internal.Data.Refold.Type (Refold(..))
132import Streamly.Internal.Data.Unfold.Type (Unfold(..))
132import Streamly.Internal.Data.Unfold.Type (Unfold(..))
133import Streamly.Internal.Data.Array.Type
133import Streamly.Internal.Data.Array.Type
134− (Array(..), unsafeFreezeWithShrink, byteLength)
134+ (Array(..), pinnedWriteNUnsafe, unsafeFreezeWithShrink, byteLength)
135import Streamly.Internal.Data.Stream.Type (Stream)
135import Streamly.Internal.Data.Stream.Type (Stream)
136+import Streamly.Internal.Data.Array.Stream (lpackArraysChunksOf)
136-- import Streamly.String (encodeUtf8, decodeUtf8, foldLines)
137-- import Streamly.String (encodeUtf8, decodeUtf8, foldLines)
137import Streamly.Internal.System.IO (defaultChunkSize)
138import Streamly.Internal.System.IO (defaultChunkSize)
138
139
139import qualified Streamly.Data.Fold as FL
140import qualified Streamly.Data.Fold as FL
140−import qualified Streamly.Internal.Data.Array as A
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
141import qualified Streamly.Internal.Data.MutArray.Type as MArray
144import qualified Streamly.Internal.Data.MutArray.Type as MArray
142import qualified Streamly.Internal.Data.Refold.Type as Refold
145import qualified Streamly.Internal.Data.Refold.Type as Refold
143import qualified Streamly.Internal.Data.Fold.Type as FL(refoldMany)
146import qualified Streamly.Internal.Data.Fold.Type as FL(refoldMany)
@@ -166,8 +169,6 @@ import qualified Streamly.Internal.Data.StreamK.Type as K (mkStream)
166-- Array IO (Input)
169-- Array IO (Input)
167-------------------------------------------------------------------------------
170-------------------------------------------------------------------------------
168
171
169−-- XXX add an API that compacts the arrays to an exact size.
170−
171-- | Read a 'ByteArray' consisting of one or more bytes from a file handle. If
172-- | Read a 'ByteArray' consisting of one or more bytes from a file handle. If
172-- no data is available on the handle it blocks until at least one byte becomes
173-- no data is available on the handle it blocks until at least one byte becomes
173-- available. If any data is available then it immediately returns that data
174-- available. If any data is available then it immediately returns that data
@@ -177,9 +178,9 @@ import qualified Streamly.Internal.Data.StreamK.Type as K (mkStream)
177{-# INLINABLE getChunk #-}
178{-# INLINABLE getChunk #-}
178getChunk :: MonadIO m => Int -> Handle -> m (Array Word8)
179getChunk :: MonadIO m => Int -> Handle -> m (Array Word8)
179getChunk size h = liftIO $ do
180getChunk size h = liftIO $ do
180− arr :: MArray.MutArray Word8 <- MArray.pinnedEmptyOf size
181+ arr <- MArray.pinnedNewBytes size
181 -- ptr <- mallocPlainForeignPtrAlignedBytes size (alignment (undefined :: Word8))
182 -- ptr <- mallocPlainForeignPtrAlignedBytes size (alignment (undefined :: Word8))
182− MArray.unsafePinnedAsPtr arr $ \p -> do
183+ MArray.asPtrUnsafe arr $ \p -> do
183 n <- hGetBufSome h p size
184 n <- hGetBufSome h p size
184 -- XXX shrink only if the diff is significant
185 -- XXX shrink only if the diff is significant
185 return $
186 return $
@@ -337,6 +338,10 @@ readerWith = UF.many A.reader chunkReaderWith
337readWithBufferOf :: MonadIO m => Unfold m (Int, Handle) Word8
338readWithBufferOf :: MonadIO m => Unfold m (Int, Handle) Word8
338readWithBufferOf = readerWith
339readWithBufferOf = readerWith
339
340
341+{-# INLINE concatChunks #-}
342+concatChunks :: (Monad m, Unbox a) => Stream m (Array a) -> Stream m a
343+concatChunks = S.unfoldMany A.reader
344+
340-- | @readWith bufsize handle@ reads a byte stream from a file
345-- | @readWith bufsize handle@ reads a byte stream from a file
341-- handle, reads are performed in chunks of up to @bufsize@.
346-- handle, reads are performed in chunks of up to @bufsize@.
342--
347--
@@ -345,7 +350,7 @@ readWithBufferOf = readerWith
345-- /Pre-release/
350-- /Pre-release/
346{-# INLINE readWith #-}
351{-# INLINE readWith #-}
347readWith :: MonadIO m => Int -> Handle -> Stream m Word8
352readWith :: MonadIO m => Int -> Handle -> Stream m Word8
348−readWith size h = A.concat $ readChunksWith size h
353+readWith size h = concatChunks $ readChunksWith size h
349
354
350-- | Unfolds a file handle into a byte stream. IO requests to the device are
355-- | Unfolds a file handle into a byte stream. IO requests to the device are
351-- performed in sizes of
356-- performed in sizes of
@@ -364,7 +369,7 @@ reader = UF.many A.reader chunkReader
364-- /Pre-release/
369-- /Pre-release/
365{-# INLINE read #-}
370{-# INLINE read #-}
366read :: MonadIO m => Handle -> Stream m Word8
371read :: MonadIO m => Handle -> Stream m Word8
367−read = A.concat . readChunks
372+read = concatChunks . readChunks
368
373
369-------------------------------------------------------------------------------
374-------------------------------------------------------------------------------
370-- Writing
375-- Writing
@@ -379,12 +384,12 @@ read = A.concat . readChunks
379{-# INLINABLE putChunk #-}
384{-# INLINABLE putChunk #-}
380putChunk :: MonadIO m => Handle -> Array a -> m ()
385putChunk :: MonadIO m => Handle -> Array a -> m ()
381putChunk _ arr | byteLength arr == 0 = return ()
386putChunk _ arr | byteLength arr == 0 = return ()
382−putChunk h arr = A.unsafePinnedAsPtr arr $ \ptr ->
387+putChunk h arr = A.asPtrUnsafe arr $ \ptr ->
383 liftIO $ hPutBuf h ptr aLen
388 liftIO $ hPutBuf h ptr aLen
384
389
385 where
390 where
386
391
387− -- XXX We should have the length passed by unsafePinnedAsPtr itself.
392+ -- XXX We should have the length passed by asPtrUnsafe itself.
388 aLen = A.byteLength arr
393 aLen = A.byteLength arr
389
394
390-------------------------------------------------------------------------------
395-------------------------------------------------------------------------------
@@ -416,7 +421,7 @@ putChunks h = S.fold (FL.drainMapM (putChunk h))
416{-# INLINE putChunksWith #-}
421{-# INLINE putChunksWith #-}
417putChunksWith :: (MonadIO m, Unbox a)
422putChunksWith :: (MonadIO m, Unbox a)
418 => Int -> Handle -> Stream m (Array a) -> m ()
423 => Int -> Handle -> Stream m (Array a) -> m ()
419−putChunksWith n h xs = putChunks h $ A.compactLE n xs
424+putChunksWith n h xs = putChunks h $ AS.compact n xs
420
425
421-- > putBytesWith n h m = Handle.putChunks h $ A.pinnedChunksOf n m
426-- > putBytesWith n h m = Handle.putChunks h $ A.pinnedChunksOf n m
422
427
@@ -456,6 +461,8 @@ writeChunks h = FL.drainMapM (putChunk h)
456chunkWriter :: MonadIO m => Refold m Handle (Array a) ()
461chunkWriter :: MonadIO m => Refold m Handle (Array a) ()
457chunkWriter = Refold.drainBy putChunk
462chunkWriter = Refold.drainBy putChunk
458
463
464+-- XXX lpackArraysChunksOf should be written idiomatically
465+
459-- | @writeChunksWith bufsize handle@ writes a stream of arrays
466-- | @writeChunksWith bufsize handle@ writes a stream of arrays
460-- to @handle@ after coalescing the adjacent arrays in chunks of @bufsize@.
467-- to @handle@ after coalescing the adjacent arrays in chunks of @bufsize@.
461-- We never split an array, if a single array is bigger than the specified size
468-- We never split an array, if a single array is bigger than the specified size
@@ -465,7 +472,7 @@ chunkWriter = Refold.drainBy putChunk
465{-# INLINE writeChunksWith #-}
472{-# INLINE writeChunksWith #-}
466writeChunksWith :: (MonadIO m, Unbox a)
473writeChunksWith :: (MonadIO m, Unbox a)
467 => Int -> Handle -> Fold m (Array a) ()
474 => Int -> Handle -> Fold m (Array a) ()
468−writeChunksWith n h = A.lCompactGE n (writeChunks h)
475+writeChunksWith n h = lpackArraysChunksOf n (writeChunks h)
469
476
470-- | Same as 'writeChunksWith'
477-- | Same as 'writeChunksWith'
471--
478--
@@ -489,11 +496,11 @@ writeChunksWithBufferOf = writeChunksWith
489-- Bytes in the input stream are collected into a buffer until we have a chunk
496-- Bytes in the input stream are collected into a buffer until we have a chunk
490-- of @reqSize@ and then written to the IO device.
497-- of @reqSize@ and then written to the IO device.
491--
498--
492−-- >>> writeWith n h = Fold.groupsOf n (Array.unsafeCreateOf n) (Handle.writeChunks h)
499+-- >>> writeWith n h = Fold.groupsOf n (Array.writeNUnsafe n) (Handle.writeChunks h)
493--
500--
494{-# INLINE writeWith #-}
501{-# INLINE writeWith #-}
495writeWith :: MonadIO m => Int -> Handle -> Fold m Word8 ()
502writeWith :: MonadIO m => Int -> Handle -> Fold m Word8 ()
496−writeWith n h = FL.groupsOf n (A.unsafePinnedCreateOf n) (writeChunks h)
503+writeWith n h = FL.groupsOf n (pinnedWriteNUnsafe n) (writeChunks h)
497
504
498-- | Same as 'writeWith'
505-- | Same as 'writeWith'
499--
506--
@@ -511,7 +518,7 @@ writeWithBufferOf = writeWith
511writeMaybesWith :: (MonadIO m )
518writeMaybesWith :: (MonadIO m )
512 => Int -> Handle -> Fold m (Maybe Word8) ()
519 => Int -> Handle -> Fold m (Maybe Word8) ()
513writeMaybesWith n h =
520writeMaybesWith n h =
514− let writeNJusts = FL.lmap fromJust $ A.pinnedCreateOf n
521+ let writeNJusts = FL.lmap fromJust $ A.pinnedWriteN n
515 writeOnNothing = FL.takeEndBy_ isNothing writeNJusts
522 writeOnNothing = FL.takeEndBy_ isNothing writeNJusts
516 in FL.many writeOnNothing (writeChunks h)
523 in FL.many writeOnNothing (writeChunks h)
517
524
@@ -521,7 +528,7 @@ writeMaybesWith n h =
521{-# INLINE writerWith #-}
528{-# INLINE writerWith #-}
522writerWith :: MonadIO m => Int -> Refold m Handle Word8 ()
529writerWith :: MonadIO m => Int -> Refold m Handle Word8 ()
523writerWith n =
530writerWith n =
524− FL.refoldMany (FL.take n $ A.unsafePinnedCreateOf n) chunkWriter
531+ FL.refoldMany (FL.take n $ pinnedWriteNUnsafe n) chunkWriter
525
532
526-- | Write a byte stream to a file handle. Accumulates the input in chunks of
533-- | Write a byte stream to a file handle. Accumulates the input in chunks of
527-- up to 'Streamly.Internal.Data.Array.Type.defaultChunkSize' before writing
534-- up to 'Streamly.Internal.Data.Array.Type.defaultChunkSize' before writing
core/src/Streamly/Internal/FileSystem/Path.hsdeleted−515
@@ -1,515 +0,0 @@
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/Parser.hs−3
@@ -300,9 +300,6 @@ data ScientificParseState
300 | SPExponentWithSign !Multiplier !Number !DecimalPlaces !PowerMultiplier
300 | SPExponentWithSign !Multiplier !Number !DecimalPlaces !PowerMultiplier
301 | SPAfterExponent !Multiplier !Number !DecimalPlaces !PowerMultiplier !Power
301 | SPAfterExponent !Multiplier !Number !DecimalPlaces !PowerMultiplier !Power
302
302
303−-- XXX See https://hackage.haskell.org/package/integer-conversion for large
304−-- integers.
305−
306-- | A generic parser for scientific notation of numbers. Returns (mantissa,
303-- | A generic parser for scientific notation of numbers. Returns (mantissa,
307-- exponent) tuple. The result can be mapped to 'Double' or any other number
304-- exponent) tuple. The result can be mapped to 'Double' or any other number
308-- representation e.g. @Scientific@.
305-- representation e.g. @Scientific@.
core/src/Streamly/Internal/Unicode/Stream.hs+3−30
@@ -28,7 +28,6 @@ module Streamly.Internal.Unicode.Stream
28 , decodeUtf8
28 , decodeUtf8
29 , decodeUtf8'
29 , decodeUtf8'
30 , decodeUtf8_
30 , decodeUtf8_
31− , decodeUtf16le'
32
31
33 -- ** Resumable UTF-8 Decoding
32 -- ** Resumable UTF-8 Decoding
34 , DecodeError(..)
33 , DecodeError(..)
@@ -56,7 +55,6 @@ module Streamly.Internal.Unicode.Stream
56 , encodeUtf8'
55 , encodeUtf8'
57 , encodeUtf8_
56 , encodeUtf8_
58 , encodeStrings
57 , encodeStrings
59− , encodeUtf16le'
60 {-
58 {-
61 -- * Operations on character strings
59 -- * Operations on character strings
62 , strip -- (dropAround isSpace)
60 , strip -- (dropAround isSpace)
@@ -99,7 +97,7 @@ import Data.Char (chr, ord)
99#if MIN_VERSION_base(4,17,0)
97#if MIN_VERSION_base(4,17,0)
100import Data.Char (generalCategory, GeneralCategory(Space))
98import Data.Char (generalCategory, GeneralCategory(Space))
101#endif
99#endif
102−import Data.Word (Word8, Word16)
100+import Data.Word (Word8)
103import Foreign.Marshal.Alloc (mallocBytes)
101import Foreign.Marshal.Alloc (mallocBytes)
104import Foreign.Storable (Storable(..))
102import Foreign.Storable (Storable(..))
105#ifndef __GHCJS__
103#ifndef __GHCJS__
@@ -111,7 +109,7 @@ import GHC.IO.Encoding.Failure (isSurrogate)
111import GHC.Ptr (Ptr (..), plusPtr)
109import GHC.Ptr (Ptr (..), plusPtr)
112import System.IO.Unsafe (unsafePerformIO)
110import System.IO.Unsafe (unsafePerformIO)
113import Streamly.Internal.Data.Array.Type (Array(..))
111import Streamly.Internal.Data.Array.Type (Array(..))
114−import Streamly.Internal.Data.MutByteArray.Type (MutByteArray)
112+import Streamly.Internal.Data.MutArray.Type (MutByteArray)
115import Streamly.Internal.Data.Fold (Fold)
113import Streamly.Internal.Data.Fold (Fold)
116import Streamly.Internal.Data.Parser (Parser)
114import Streamly.Internal.Data.Parser (Parser)
117import Streamly.Internal.Data.Stream (Stream)
115import Streamly.Internal.Data.Stream (Stream)
@@ -678,19 +676,6 @@ decodeUtf8_ = decodeUtf8D_
678decodeUtf8Lax :: Monad m => Stream m Word8 -> Stream m Char
676decodeUtf8Lax :: Monad m => Stream m Word8 -> Stream m Char
679decodeUtf8Lax = decodeUtf8
677decodeUtf8Lax = decodeUtf8
680
678
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−
694-------------------------------------------------------------------------------
679-------------------------------------------------------------------------------
695-- Decoding Array Streams
680-- Decoding Array Streams
696-------------------------------------------------------------------------------
681-------------------------------------------------------------------------------
@@ -961,18 +946,6 @@ encodeUtf8_ = encodeUtf8D_
961encodeUtf8Lax :: Monad m => Stream m Char -> Stream m Word8
946encodeUtf8Lax :: Monad m => Stream m Char -> Stream m Word8
962encodeUtf8Lax = encodeUtf8
947encodeUtf8Lax = encodeUtf8
963
948
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−
976-------------------------------------------------------------------------------
949-------------------------------------------------------------------------------
977-- Decoding string literals
950-- Decoding string literals
978-------------------------------------------------------------------------------
951-------------------------------------------------------------------------------
@@ -1057,7 +1030,7 @@ stripHead = Stream.dropWhile isSpace
1057-- >>> Stream.fold Fold.toList $ Unicode.lines Fold.toList (Stream.fromList "lines\nthis\nstring\n\n\n")
1030-- >>> Stream.fold Fold.toList $ Unicode.lines Fold.toList (Stream.fromList "lines\nthis\nstring\n\n\n")
1058-- ["lines","this","string","",""]
1031-- ["lines","this","string","",""]
1059--
1032--
1060−-- >>> lines f = Stream.foldMany (Fold.takeEndBy_ (== '\n') f)
1033+-- > lines = Stream.splitOnSuffix (== '\n')
1061--
1034--
1062-- /Pre-release/
1035-- /Pre-release/
1063{-# INLINE lines #-}
1036{-# INLINE lines #-}
core/src/Streamly/Internal/Unicode/String.hs+7−7
@@ -39,14 +39,14 @@ module Streamly.Internal.Unicode.String
39 --
39 --
40 -- $setup
40 -- $setup
41
41
42− str
42+ str
43 ) where
43 ) where
44
44
45
45
46import Control.Applicative (Alternative(..))
46import Control.Applicative (Alternative(..))
47import Control.Exception (displayException)
47import Control.Exception (displayException)
48import Data.Functor.Identity (runIdentity)
48import Data.Functor.Identity (runIdentity)
49−import Streamly.Internal.Data.Parser (Parser, ParseError)
49+import Streamly.Internal.Data.Parser (Parser)
50
50
51import Language.Haskell.TH
51import Language.Haskell.TH
52import Language.Haskell.TH.Quote
52import Language.Haskell.TH.Quote
@@ -106,12 +106,9 @@ strSegmentExp (StrVar name) = do
106strExp :: [StrSegment] -> Q Exp
106strExp :: [StrSegment] -> Q Exp
107strExp xs = appE [| concat |] $ listE $ map strSegmentExp xs
107strExp xs = appE [| concat |] $ listE $ map strSegmentExp xs
108
108
109−parseStr :: String -> Either ParseError [StrSegment]
110−parseStr = runIdentity . Stream.parse strParser . Stream.fromList
111−
112expandVars :: String -> Q Exp
109expandVars :: String -> Q Exp
113−expandVars input =
110+expandVars ln =
114− case parseStr input of
111+ case runIdentity $ Stream.parse strParser (Stream.fromList ln) of
115 Left e ->
112 Left e ->
116 fail $ "str QuasiQuoter parse error: " ++ displayException e
113 fail $ "str QuasiQuoter parse error: " ++ displayException e
117 Right x ->
114 Right x ->
@@ -144,6 +141,9 @@ expandVars input =
144-- :}
141-- :}
145-- "hello world!"
142-- "hello world!"
146--
143--
144+-- Bugs: because of a bug in parsers, a lone # at the end of input gets
145+-- removed.
146+--
147str :: QuasiQuoter
147str :: QuasiQuoter
148str =
148str =
149 QuasiQuoter
149 QuasiQuoter
core/streamly-core.cabal+14−54
@@ -1,13 +1,11 @@
1cabal-version: 2.2
1cabal-version: 2.2
2name: streamly-core
2name: streamly-core
3−version: 0.2.2
3+version: 0.2.0
4−synopsis: Streaming, parsers, arrays, serialization and more
4+synopsis: Streaming, parsers, arrays and more
5description:
5description:
6 For upgrading to streamly-0.9.0+ please read the
6 For upgrading to streamly-0.9.0+ please read the
7 <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>.
7 <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>.
8 .
8 .
9− Streamly is a standard library for Haskell that focuses on C-like
10− performance, modular combinators, and streaming data flow model.
11 Streamly consists of two packages: "streamly-core" and "streamly".
9 Streamly consists of two packages: "streamly-core" and "streamly".
12 <https://hackage.haskell.org/package/streamly-core streamly-core>
10 <https://hackage.haskell.org/package/streamly-core streamly-core>
13 provides basic features, and depends only on GHC boot libraries (see
11 provides basic features, and depends only on GHC boot libraries (see
@@ -17,41 +15,8 @@ description:
17 and networking. For documentation, visit the
15 and networking. For documentation, visit the
18 <https://streamly.composewell.com Streamly website>.
16 <https://streamly.composewell.com Streamly website>.
19 .
17 .
20− The streamly-core package provides the following functionality:
18+ This package provides streams, arrays, parsers, unicode text, file
21− .
19+ IO, and console IO functionality.
22− * Streams as composable producers of a sequence of values.
23− * Streams provide all the functionality provided by Haskell lists but
24− in an effectful, streaming fashion with better performance.
25− * Streams provide ListT and logic programming functionality as well.
26− * Folds as composable stream consumers that reduce the streams to a
27− single value or reduce segments of streams to transform the stream.
28− * Parsers as more powerful, composable stream consumers supporting
29− standard parser combinators with backtracking but in a streaming
30− fashion.
31− * Arrays with streaming interfaces providing high performance,
32− modularity, and concise interface as all streaming operations can be
33− performed on arrays.
34− * Arrays can be immutable or mutable, unboxed or boxed, pinned or
35− unpinned.
36− * Arrays generalize the functionality provided by @bytestring@ and
37− @text@ packages.
38− * Interoperability with @bytestring@ and @text@ is
39− provided via separate packages.
40− * Arrays and folds provide natural builder functionality so there are no
41− separate builder modules.
42− * High performance binary serialization with configurable JSON like features.
43− * Streaming combinators for unicode text processing, providing
44− functionality equivalent to the @text@ package.
45− * String interpolation for convenient construction of strings.
46− * Streaming console IO (stdin/stdout) operations.
47− * Streaming file and directory IO operations.
48− .
49− This package covers some or all of the functionality covered
50− by @streaming, pipes, conduit, list-t, logic-t, foldl, attoparsec,
51− array, primitive, vector, vector-algorithms, binary, cereal, store,
52− bytestring, text, stringsearch, interpolate@. Streamly provides a
53− consistent, concise, modular and performant interface for all this
54− functionality.
55 .
20 .
56 Note: The dependencies "heaps" and "monad-control" are included in
21 Note: The dependencies "heaps" and "monad-control" are included in
57 the package solely for backward compatibility, and will be removed in
22 the package solely for backward compatibility, and will be removed in
@@ -116,7 +81,6 @@ extra-doc-files:
116 docs/*.md
81 docs/*.md
117 docs/ApiChangelogs/0.1.0.txt
82 docs/ApiChangelogs/0.1.0.txt
118 docs/ApiChangelogs/0.1.0-0.2.0.txt
83 docs/ApiChangelogs/0.1.0-0.2.0.txt
119− docs/ApiChangelogs/0.2.0-0.2.2.txt
120
84
121source-repository head
85source-repository head
122 type: git
86 type: git
@@ -218,6 +182,8 @@ common compile-options
218common default-extensions
182common default-extensions
219 default-extensions:
183 default-extensions:
220 BangPatterns
184 BangPatterns
185+ CApiFFI
186+ CPP
221 ConstraintKinds
187 ConstraintKinds
222 DeriveDataTypeable
188 DeriveDataTypeable
223 DeriveGeneric
189 DeriveGeneric
@@ -228,29 +194,24 @@ common default-extensions
228 GeneralizedNewtypeDeriving
194 GeneralizedNewtypeDeriving
229 InstanceSigs
195 InstanceSigs
230 KindSignatures
196 KindSignatures
197+ LambdaCase
198+ MagicHash
231 MultiParamTypeClasses
199 MultiParamTypeClasses
200+ PatternSynonyms
232 RankNTypes
201 RankNTypes
202+ RecordWildCards
233 ScopedTypeVariables
203 ScopedTypeVariables
234 StandaloneDeriving
204 StandaloneDeriving
235 TupleSections
205 TupleSections
236 TypeApplications
206 TypeApplications
207+ TypeFamilies
237 TypeOperators
208 TypeOperators
209+ ViewPatterns
238
210
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
250 -- MonoLocalBinds, enabled by TypeFamilies, causes performance
211 -- MonoLocalBinds, enabled by TypeFamilies, causes performance
251 -- regressions. Disable it. This must come after TypeFamilies,
212 -- regressions. Disable it. This must come after TypeFamilies,
252 -- otherwise TypeFamilies will enable it again.
213 -- otherwise TypeFamilies will enable it again.
253− -- NoMonoLocalBinds
214+ NoMonoLocalBinds
254
215
255 -- UndecidableInstances -- Does not show any perf impact
216 -- UndecidableInstances -- Does not show any perf impact
256 -- UnboxedTuples -- interferes with (#.)
217 -- UnboxedTuples -- interferes with (#.)
@@ -376,7 +337,6 @@ library
376 , Streamly.Internal.Unicode.Array
337 , Streamly.Internal.Unicode.Array
377
338
378 -- Filesystem/IO
339 -- Filesystem/IO
379− , Streamly.Internal.FileSystem.Path
380 , Streamly.Internal.FileSystem.Handle
340 , Streamly.Internal.FileSystem.Handle
381 , Streamly.Internal.FileSystem.File
341 , Streamly.Internal.FileSystem.File
382 , Streamly.Internal.FileSystem.Dir
342 , Streamly.Internal.FileSystem.Dir
@@ -507,7 +467,7 @@ library
507 , base >= 4.12 && < 4.20
467 , base >= 4.12 && < 4.20
508 , exceptions >= 0.8.0 && < 0.11
468 , exceptions >= 0.8.0 && < 0.11
509 , transformers >= 0.5.5 && < 0.7
469 , transformers >= 0.5.5 && < 0.7
510− , filepath >= 1.4.2 && < 1.6
470+ , filepath >= 1.4.2 && < 1.5
511
471
512 -- streamly-unicode-core
472 -- streamly-unicode-core
513 , template-haskell >= 2.14 && < 2.22
473 , template-haskell >= 2.14 && < 2.22
docs/Developer/optimization-guidelines.md−12
@@ -191,18 +191,6 @@ the stages of a loop, it is often not useful to inline the whole loop
191itself, in fact we may have to occasionally use a `NOINLINE` so that the
191itself, in fact we may have to occasionally use a `NOINLINE` so that the
192compiler does not inline it.
192compiler does not inline it.
193
193
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−
206### NoSpecConstr
194### NoSpecConstr
207
195
208It is not always useful to specialize function calls for all constructors, some
196It is not always useful to specialize function calls for all constructors, some
docs/Developer/paths.rst+2−25
@@ -140,35 +140,12 @@ know exactly how the file system stores the path. Otherwise if we display
140it differently, the user may use the displayed result to find the file
140it differently, the user may use the displayed result to find the file
141and may not find it.
141and may not find it.
142
142
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−
166Type Safety Requirements
143Type Safety Requirements
167------------------------
144------------------------
168
145
169* Safety against using an absolute path where a relative path is to be
146* Safety against using an absolute path where a relative path is to be
170− used and vice-versa.
147+ used and vice-versa.
171−
148+
172 * Validations for absolute or relative path when constructing a path.
149 * Validations for absolute or relative path when constructing a path.
173 * We cannot append an absolute path to another path
150 * We cannot append an absolute path to another path
174* Safety against using a file name where a directory name is to be used and
151* Safety against using a file name where a directory name is to be used and
docs/User/Project/Changelog.md−4
@@ -2,10 +2,6 @@
2
2
3<!-- See rendered changelog at https://streamly.composewell.com -->
3<!-- See rendered changelog at https://streamly.composewell.com -->
4
4
5−## 0.10.1 (Jan 2024)
6−
7−* Fix TH macros in `Streamly.Data.Stream.MkType` for GHC 9.6 and above.
8−
9## 0.10.0 (Nov 2023)
5## 0.10.0 (Nov 2023)
10
6
11See
7See
docs/User/Project/README.md−1
@@ -1,7 +1,6 @@
1# [Streamly][]: Idiomatic Haskell with C-Like Performance
1# [Streamly][]: Idiomatic Haskell with C-Like Performance
2
2
3[![Gitter chat](https://badges.gitter.im/composewell/gitter.svg)](https://gitter.im/composewell/streamly)
3[![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)
5[![Hackage](https://img.shields.io/hackage/v/streamly.svg?style=flat)](https://hackage.haskell.org/package/streamly)
4[![Hackage](https://img.shields.io/hackage/v/streamly.svg?style=flat)](https://hackage.haskell.org/package/streamly)
6
5
7## Upgrading to 0.9.0+
6## Upgrading to 0.9.0+
src/Streamly/Data/Stream/Prelude.hs+15−25
@@ -6,11 +6,6 @@
6-- Stability : experimental
6-- Stability : experimental
7-- Portability : GHC
7-- Portability : GHC
8--
8--
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−--
14-- All Stream related combinators including the streamly-core
9-- All Stream related combinators including the streamly-core
15-- "Streamly.Data.Stream" module, concurrency, time and lifted
10-- "Streamly.Data.Stream" module, concurrency, time and lifted
16-- exception operations. For more pre-release operations also see
11-- exception operations. For more pre-release operations also see
@@ -61,28 +56,25 @@ module Streamly.Data.Stream.Prelude
61 -- | Stream combinators using a concurrent channel.
56 -- | Stream combinators using a concurrent channel.
62
57
63 -- *** Evaluate
58 -- *** Evaluate
64− -- | Evaluate a stream as a whole concurrently with respect to the consumer
59+ -- | Evaluates a serial stream asynchronously using a concurency channel.
65− -- of the stream.
66
60
67 , parEval
61 , parEval
68
62
69 -- *** Generate
63 -- *** Generate
70− -- | Generate a stream by evaluating multiple actions concurrently.
64+ -- | Uses a concurrency channel to evaluate multiple actions concurrently.
71 , parRepeatM
65 , parRepeatM
72 , parReplicateM
66 , parReplicateM
73 , fromCallback
67 , fromCallback
74
68
75 -- *** Map
69 -- *** Map
76− -- | Map actions on a stream such that the mapped actions are evaluated
70+ -- | Uses a concurrency channel to evaluate multiple mapped actions
77− -- concurrently with each other.
71+ -- concurrently.
78
72
79 , parMapM
73 , parMapM
80 , parSequence
74 , parSequence
81
75
82 -- *** Combine two
76 -- *** Combine two
83− -- | Combine two streams such that each stream as a whole is evaluated
77+ -- | Use a channel for each pair.
84− -- concurrently with respect to the other stream as well as the consumer of
85− -- the resulting stream.
86 , parZipWithM
78 , parZipWithM
87 , parZipWith
79 , parZipWith
88 , parMergeByM
80 , parMergeByM
@@ -160,18 +152,16 @@ import Streamly.Internal.Data.Stream.Prelude
160-- 'parConcatMap', and 'parConcatIterate', all concurrency combinators can be
152-- 'parConcatMap', and 'parConcatIterate', all concurrency combinators can be
161-- expressed in terms of these.
153-- expressed in terms of these.
162--
154--
163−-- 'parEval' evaluates a stream as a whole asynchronously with respect to
155+-- 'parEval', evaluates a single stream asynchronously, a worker thread runs
164−-- the consumer of the stream. A worker thread evaluates multiple elements of
156+-- the stream and buffers the results, and the consumer of the stream runs in
165−-- the stream ahead of time and buffers the results; the consumer of the stream
157+-- another thread consuming it from the buffer, thus decoupling the production
166−-- runs in another thread consuming the elements from the buffer, thus
158+-- and consumption of the stream. This can be used to run different stages of a
167−-- decoupling the production and consumption of the stream. 'parEval' can be
159+-- pipeline concurrently.
168−-- used to run different stages of a pipeline concurrently.
160+--
169−--
161+-- 'parConcatMap' is used to evaluate multiple streams concurrently and combine
170−-- 'parConcatMap' is used to evaluate multiple actions in a stream concurrently
162+-- the results. A stream generator function is mapped to the input stream and
171−-- with respect to each other or to evaluate multiple streams concurrently and
163+-- all the generated streams are then evaluated concurrently, and the results
172−-- combine the results. A stream generator function is mapped to the input
164+-- are combined.
173−-- stream and all the generated streams are then evaluated concurrently, and
174−-- the results are combined.
175--
165--
176-- 'parConcatIterate' is like 'parConcatMap' but iterates a stream generator
166-- 'parConcatIterate' is like 'parConcatMap' but iterates a stream generator
177-- function recursively over the stream. This can be used to traverse trees or
167-- function recursively over the stream. This can be used to traverse trees or
src/Streamly/Internal/Data/IsMap/HashMap.hs−5
@@ -1,8 +1,3 @@
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−
6{-# OPTIONS_GHC -Wno-orphans #-}
1{-# OPTIONS_GHC -Wno-orphans #-}
7
2
8-- |
3-- |
src/Streamly/Internal/Data/Stream/Concurrent.hs+11−19
@@ -147,23 +147,15 @@ parEvalD modifier m = D.Stream step Nothing
147 D.Stop -> D.Stop
147 D.Stop -> D.Stop
148-}
148-}
149
149
150−-- | 'parEval' evaluates a stream as a whole asynchronously with respect to
150+-- | Evaluate a stream asynchronously. In a serial stream, each element of the
151−-- the consumer of the stream. A worker thread evaluates multiple elements of
151+-- stream is generated as it is demanded by the consumer. `parEval` evaluates
152−-- the stream ahead of time and buffers the results; the consumer of the stream
152+-- multiple elements of the stream ahead of time and serves the results from a
153−-- runs in another thread consuming the elements from the buffer, thus
153+-- buffer.
154−-- decoupling the production and consumption of the stream. 'parEval' can be
154+--
155−-- used to run different stages of a pipeline concurrently.
155+-- Note that the evaluation requires only one thread as only one stream needs
156−--
156+-- to be evaluated. Therefore, the concurrency options that are relevant to
157−-- It is important to note that 'parEval' does not evaluate individual actions
157+-- multiple streams won't apply here e.g. maxThreads, eager, interleaved,
158−-- in the stream concurrently with respect to each other, it merely evaluates
158+-- ordered, stopWhen options won't have any effect.
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'.
167--
159--
168-- Useful idioms:
160-- Useful idioms:
169--
161--
@@ -654,8 +646,8 @@ parConcatIterate modifier f input =
654
646
655 where
647 where
656
648
657− iterateStream channel =
649+ iterateStream channel stream =
658− parConcatMapChanKGeneric modifier channel (generate channel)
650+ parConcatMapChanKGeneric modifier channel (generate channel) stream
659
651
660 generate channel x =
652 generate channel x =
661 -- XXX The channel q should be FIFO for DFS, otherwise it is BFS
653 -- 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
529-- reverse' s = fromStreamD $ D.reverse' $ toStreamD s
529-- reverse' s = fromStreamD $ D.reverse' $ toStreamD s
530reverse' =
530reverse' =
531 fromStreamD
531 fromStreamD
532− . A.concatRev -- unfoldMany A.readerRev
532+ . A.flattenArraysRev -- unfoldMany A.readRev
533 . D.fromStreamK
533 . D.fromStreamK
534 . K.reverse
534 . K.reverse
535 . D.toStreamK
535 . D.toStreamK
src/Streamly/Internal/Data/Stream/IsStream/Expand.hs−5
@@ -1,8 +1,3 @@
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−
6{-# OPTIONS_GHC -Wno-deprecations #-}
1{-# OPTIONS_GHC -Wno-deprecations #-}
7
2
8-- |
3-- |
src/Streamly/Internal/Data/Stream/IsStream/Reduce.hs+1−1
@@ -1624,4 +1624,4 @@ splitInnerBySuffix
1624 -> t m (f a)
1624 -> t m (f a)
1625 -> t m (f a)
1625 -> t m (f a)
1626splitInnerBySuffix splitter joiner xs =
1626splitInnerBySuffix splitter joiner xs =
1627− fromStreamD $ D.splitInnerBySuffix (== mempty) splitter joiner $ toStreamD xs
1627+ fromStreamD $ D.splitInnerBySuffix splitter joiner $ toStreamD xs
src/Streamly/Internal/Data/Stream/IsStream/Top.hs+1−1
@@ -649,7 +649,7 @@ intersectBySorted :: (IsStream t, Monad m) =>
649 (a -> a -> Ordering) -> t m a -> t m a -> t m a
649 (a -> a -> Ordering) -> t m a -> t m a -> t m a
650intersectBySorted eq s1 =
650intersectBySorted eq s1 =
651 IsStream.fromStreamD
651 IsStream.fromStreamD
652− . StreamD.sortedIntersectBy eq (IsStream.toStreamD s1)
652+ . StreamD.intersectBySorted eq (IsStream.toStreamD s1)
653 . IsStream.toStreamD
653 . IsStream.toStreamD
654
654
655-- Roughly joinLeft s1 s2 = s1 `difference` s2 + s1 `intersection` s2
655-- Roughly joinLeft s1 s2 = s1 `difference` s2 + s1 `intersection` s2
src/Streamly/Internal/Data/Stream/MkType.hs+34−33
@@ -334,14 +334,10 @@ monadthrowInstance _Type =
334 [ clause
334 [ clause
335 []
335 []
336 (normalB
336 (normalB
337− (infixE
337+ (infixE
338− (Just (conE _Type))
338+ (Just (varE _lift))
339− (varE _dotOp)
339+ (varE _dotOp)
340− (Just
340+ (Just (varE _throwM))))
341− (infixE
342− (Just (varE (mkName "Stream.fromEffect")))
343− (varE _dotOp)
344− (Just (varE _throwM))))))
345 []
341 []
346 ]
342 ]
347 ]
343 ]
@@ -508,7 +504,7 @@ flattenDec (ma:mas) = do
508-- >>> expr <- runQ (mkZipType "ZipStream" "zipApply" False)
504-- >>> expr <- runQ (mkZipType "ZipStream" "zipApply" False)
509-- >>> putStrLn $ pprint expr
505-- >>> putStrLn $ pprint expr
510-- newtype ZipStream m a
506-- newtype ZipStream m a
511−-- = ZipStream (Stream.Stream m a)
507+-- = ZipStream (Stream.Stream m a)
512-- deriving Foldable
508-- deriving Foldable
513-- mkZipStream :: Stream.Stream m a -> ZipStream m a
509-- mkZipStream :: Stream.Stream m a -> ZipStream m a
514-- mkZipStream = ZipStream
510-- mkZipStream = ZipStream
@@ -520,17 +516,19 @@ flattenDec (ma:mas) = do
520-- deriving instance GHC.Classes.Eq a => Eq (ZipStream Identity a)
516-- deriving instance GHC.Classes.Eq a => Eq (ZipStream Identity a)
521-- deriving instance GHC.Classes.Ord a => Ord (ZipStream Identity a)
517-- deriving instance GHC.Classes.Ord a => Ord (ZipStream Identity a)
522-- instance Show a => Show (ZipStream Identity a)
518-- instance Show a => Show (ZipStream Identity a)
523−-- where {{-# INLINE show #-}; show (ZipStream strm) = show strm}
519+-- where {-# INLINE show #-}
520+-- show (ZipStream strm) = show strm
524-- instance Read a => Read (ZipStream Identity a)
521-- instance Read a => Read (ZipStream Identity a)
525−-- where {{-# INLINE readPrec #-}; readPrec = fmap ZipStream readPrec}
522+-- where {-# INLINE readPrec #-}
523+-- readPrec = fmap ZipStream readPrec
526-- instance Monad m => Functor (ZipStream m)
524-- instance Monad m => Functor (ZipStream m)
527−-- where {{-# INLINE fmap #-};
525+-- where {-# INLINE fmap #-}
528−-- fmap f (ZipStream strm) = ZipStream (fmap f strm)}
526+-- fmap f (ZipStream strm) = ZipStream (fmap f strm)
529-- instance Monad m => Applicative (ZipStream m)
527-- instance Monad m => Applicative (ZipStream m)
530−-- where {{-# INLINE pure #-};
528+-- where {-# INLINE pure #-}
531−-- pure = ZipStream . Stream.repeat;
529+-- pure = ZipStream . Stream.repeat
532−-- {-# INLINE (<*>) #-};
530+-- {-# INLINE (<*>) #-}
533−-- (<*>) (ZipStream strm1) (ZipStream strm2) = ZipStream (zipApply strm1 strm2)}
531+-- (<*>) (ZipStream strm1) (ZipStream strm2) = ZipStream (zipApply strm1 strm2)
534mkZipType
532mkZipType
535 :: String -- ^ Name of the type
533 :: String -- ^ Name of the type
536 -> String -- ^ Function to use for (\<*\>)
534 -> String -- ^ Function to use for (\<*\>)
@@ -579,25 +577,27 @@ mkZipType dtNameStr apOpStr isConcurrent =
579-- unParallel :: Parallel m a -> Stream.Stream m a
577-- unParallel :: Parallel m a -> Stream.Stream m a
580-- unParallel (Parallel strm) = strm
578-- unParallel (Parallel strm) = strm
581-- instance Monad m => Functor (Parallel m)
579-- instance Monad m => Functor (Parallel m)
582−-- where {{-# INLINE fmap #-};
580+-- where {-# INLINE fmap #-}
583−-- fmap f (Parallel strm) = Parallel (fmap f strm)}
581+-- fmap f (Parallel strm) = Parallel (fmap f strm)
584-- instance Stream.MonadAsync m => Monad (Parallel m)
582-- instance Stream.MonadAsync m => Monad (Parallel m)
585−-- where {{-# INLINE (>>=) #-};
583+-- where {-# INLINE (>>=) #-}
586−-- (>>=) (Parallel strm1) f = let f1 a = unParallel (f a)
584+-- (>>=) (Parallel strm1) f = let f1 a = unParallel (f a)
587−-- in Parallel (parBind strm1 f1)}
585+-- in Parallel (parBind strm1 f1)
588-- instance Stream.MonadAsync m => Applicative (Parallel m)
586-- instance Stream.MonadAsync m => Applicative (Parallel m)
589−-- where {{-# INLINE pure #-};
587+-- where {-# INLINE pure #-}
590−-- pure = Parallel . Stream.fromPure;
588+-- pure = Parallel . Stream.fromPure
591−-- {-# INLINE (<*>) #-};
589+-- {-# INLINE (<*>) #-}
592−-- (<*>) = ap}
590+-- (<*>) = ap
593-- instance (Monad (Parallel m), MonadIO m) => MonadIO (Parallel m)
591-- instance (Monad (Parallel m), MonadIO m) => MonadIO (Parallel m)
594−-- where {{-# INLINE liftIO #-};
592+-- where {-# INLINE liftIO #-}
595−-- liftIO = Parallel . (Stream.fromEffect . liftIO)}
593+-- liftIO = Parallel . (Stream.fromEffect . liftIO)
594+-- instance MonadTrans Parallel
595+-- where {-# INLINE lift #-}
596+-- lift = Parallel . Stream.fromEffect
596-- instance (Monad (Parallel m),
597-- instance (Monad (Parallel m),
597-- MonadThrow m) => MonadThrow (Parallel m)
598-- MonadThrow m) => MonadThrow (Parallel m)
598−-- where {{-# INLINE throwM #-};
599+-- where {-# INLINE throwM #-}
599−-- throwM = Parallel . (Stream.fromEffect . throwM)}
600+-- throwM = lift . throwM
600−
601mkCrossType
601mkCrossType
602 :: String -- ^ Name of the type
602 :: String -- ^ Name of the type
603 -> String -- ^ Function to use for (>>=)
603 -> String -- ^ Function to use for (>>=)
@@ -617,7 +617,7 @@ mkCrossType dtNameStr bindOpStr isConcurrent =
617 , readInstance _Type
617 , readInstance _Type
618 ]
618 ]
619 else []
619 else []
620− , sequence $
620+ , sequence
621 [ functorInstance _Type
621 [ functorInstance _Type
622 , mkStreamMonad dtNameStr classConstraints bindOpStr
622 , mkStreamMonad dtNameStr classConstraints bindOpStr
623 , mkStreamApplicative
623 , mkStreamApplicative
@@ -627,9 +627,10 @@ mkCrossType dtNameStr bindOpStr isConcurrent =
627 "Stream.fromPure"
627 "Stream.fromPure"
628 "ap"
628 "ap"
629 , monadioInstance _Type
629 , monadioInstance _Type
630+ , monadtransInstance _Type
630 , monadthrowInstance _Type
631 , monadthrowInstance _Type
631 -- , monadreaderInstance _Type
632 -- , monadreaderInstance _Type
632− ] ++ [monadtransInstance _Type | not isConcurrent]
633+ ]
633 ]
634 ]
634
635
635 where
636 where
src/Streamly/Internal/Data/Stream/Serial.hs−5
@@ -1,9 +1,4 @@
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{-# LANGUAGE UndecidableInstances #-}
1{-# LANGUAGE UndecidableInstances #-}
6−
7{-# OPTIONS_GHC -Wno-deprecations #-}
2{-# OPTIONS_GHC -Wno-deprecations #-}
8{-# OPTIONS_GHC -Wno-redundant-constraints #-}
3{-# OPTIONS_GHC -Wno-redundant-constraints #-}
9
4
src/Streamly/Internal/Data/Stream/Zip.hs−5
@@ -1,9 +1,4 @@
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{-# LANGUAGE UndecidableInstances #-}
1{-# LANGUAGE UndecidableInstances #-}
6−
7{-# OPTIONS_GHC -Wno-deprecations #-}
2{-# OPTIONS_GHC -Wno-deprecations #-}
8
3
9-- |
4-- |
src/Streamly/Internal/FileSystem/Event.hs−3
@@ -20,9 +20,6 @@
20-- * "Streamly.Internal.FileSystem.Event.Linux"
20-- * "Streamly.Internal.FileSystem.Event.Linux"
21-- * "Streamly.Internal.FileSystem.Event.Windows"
21-- * "Streamly.Internal.FileSystem.Event.Windows"
22
22
23−-- XXX Need to ensure that the signatures of the exported APIs are same on all
24−-- platforms.
25−
26module Streamly.Internal.FileSystem.Event
23module Streamly.Internal.FileSystem.Event
27 (
24 (
28 -- * Creating a Watch
25 -- * Creating a Watch
src/Streamly/Internal/FileSystem/Event/Darwin.hs+1−1
@@ -451,7 +451,7 @@ openWatch Config{..} paths = do
451
451
452 withPathName :: Array Word8 -> (PathName -> IO a) -> IO a
452 withPathName :: Array Word8 -> (PathName -> IO a) -> IO a
453 withPathName arr act = do
453 withPathName arr act = do
454− A.unsafePinnedAsPtr arr $ \ptr ->
454+ A.asPtrUnsafe arr $ \ptr ->
455 let pname = PathName (castPtr ptr) (fromIntegral (A.length arr))
455 let pname = PathName (castPtr ptr) (fromIntegral (A.length arr))
456 in act pname
456 in act pname
457
457
src/Streamly/Internal/FileSystem/Event/Linux.hs+2−2
@@ -188,7 +188,7 @@ import qualified Streamly.FileSystem.Handle as FH
188import qualified Streamly.Unicode.Stream as U
188import qualified Streamly.Unicode.Stream as U
189
189
190import qualified Streamly.Internal.Data.Array as A
190import qualified Streamly.Internal.Data.Array as A
191− ( fromStream, asCStringUnsafe, unsafePinnedAsPtr
191+ ( fromStream, asCStringUnsafe, asPtrUnsafe
192 , getSliceUnsafe, read
192 , getSliceUnsafe, read
193 )
193 )
194import qualified Streamly.Internal.FileSystem.Dir as Dir (readDirs)
194import qualified Streamly.Internal.FileSystem.Dir as Dir (readDirs)
@@ -809,7 +809,7 @@ readOneEvent :: Config -> Watch -> Parser Word8 IO Event
809readOneEvent cfg wt@(Watch _ wdMap) = do
809readOneEvent cfg wt@(Watch _ wdMap) = do
810 let headerLen = sizeOf (undefined :: CInt) + 12
810 let headerLen = sizeOf (undefined :: CInt) + 12
811 arr <- PR.takeEQ headerLen (A.writeN headerLen)
811 arr <- PR.takeEQ headerLen (A.writeN headerLen)
812− (ewd, eflags, cookie, pathLen) <- PR.fromEffect $ A.unsafePinnedAsPtr arr readHeader
812+ (ewd, eflags, cookie, pathLen) <- PR.fromEffect $ A.asPtrUnsafe arr readHeader
813 -- XXX need the "initial" in parsers to return a step type so that "take 0"
813 -- XXX need the "initial" in parsers to return a step type so that "take 0"
814 -- can return without an input. otherwise if pathLen is 0 we will keep
814 -- can return without an input. otherwise if pathLen is 0 we will keep
815 -- waiting to read one more char before we return this event.
815 -- 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
130import Streamly.Data.Array (Array, Unbox)
130import Streamly.Data.Array (Array, Unbox)
131import Streamly.Data.Stream (Stream)
131import Streamly.Data.Stream (Stream)
132
132
133−import Streamly.Internal.Data.Array (byteLength, unsafeFreeze, unsafePinnedAsPtr)
133+import Streamly.Internal.Data.Array (byteLength, unsafeFreeze, asPtrUnsafe)
134import Streamly.Internal.System.IO (defaultChunkSize)
134import Streamly.Internal.System.IO (defaultChunkSize)
135
135
136#if !defined(mingw32_HOST_OS)
136#if !defined(mingw32_HOST_OS)
@@ -147,7 +147,7 @@ import qualified Streamly.Internal.System.IOVec.Type as RawIO
147import qualified Streamly.Data.Array as A
147import qualified Streamly.Data.Array as A
148import qualified Streamly.Data.Fold as FL
148import qualified Streamly.Data.Fold as FL
149import qualified Streamly.Internal.Data.MutArray as MArray
149import qualified Streamly.Internal.Data.MutArray as MArray
150− (MutArray(..), unsafePinnedAsPtr, pinnedNewBytes)
150+ (MutArray(..), asPtrUnsafe, pinnedNewBytes)
151import qualified Streamly.Internal.Data.Array.Stream as AS
151import qualified Streamly.Internal.Data.Array.Stream as AS
152import qualified Streamly.Internal.Data.Stream as S
152import qualified Streamly.Internal.Data.Stream as S
153import qualified Streamly.Internal.Data.Stream as D
153import qualified Streamly.Internal.Data.Stream as D
@@ -220,7 +220,7 @@ readArrayUpto :: Int -> Handle -> IO (Array Word8)
220readArrayUpto size (Handle fd) = do
220readArrayUpto size (Handle fd) = do
221 arr <- MArray.pinnedNewBytes size
221 arr <- MArray.pinnedNewBytes size
222 -- ptr <- mallocPlainForeignPtrAlignedBytes size (alignment (undefined :: Word8))
222 -- ptr <- mallocPlainForeignPtrAlignedBytes size (alignment (undefined :: Word8))
223− MArray.unsafePinnedAsPtr arr $ \p -> do
223+ MArray.asPtrUnsafe arr $ \p -> do
224 -- n <- hGetBufSome h p size
224 -- n <- hGetBufSome h p size
225#if MIN_VERSION_base(4,15,0)
225#if MIN_VERSION_base(4,15,0)
226 n <- RawIO.read fd p 0 size
226 n <- RawIO.read fd p 0 size
@@ -244,7 +244,7 @@ readArrayUpto size (Handle fd) = do
244writeArray :: Unbox a => Handle -> Array a -> IO ()
244writeArray :: Unbox a => Handle -> Array a -> IO ()
245writeArray _ arr | A.length arr == 0 = return ()
245writeArray _ arr | A.length arr == 0 = return ()
246writeArray (Handle fd) arr =
246writeArray (Handle fd) arr =
247− unsafePinnedAsPtr arr $ \p ->
247+ asPtrUnsafe arr $ \p ->
248 -- RawIO.writeAll fd (castPtr p) aLen
248 -- RawIO.writeAll fd (castPtr p) aLen
249#if MIN_VERSION_base(4,15,0)
249#if MIN_VERSION_base(4,15,0)
250 RawIO.write fd (castPtr p) 0 aLen
250 RawIO.write fd (castPtr p) 0 aLen
@@ -270,7 +270,7 @@ writeArray (Handle fd) arr =
270writeIOVec :: Handle -> Array RawIO.IOVec -> IO ()
270writeIOVec :: Handle -> Array RawIO.IOVec -> IO ()
271writeIOVec _ iov | A.length iov == 0 = return ()
271writeIOVec _ iov | A.length iov == 0 = return ()
272writeIOVec (Handle fd) iov =
272writeIOVec (Handle fd) iov =
273− unsafePinnedAsPtr iov $ \p ->
273+ asPtrUnsafe iov $ \p ->
274 RawIO.writevAll fd p (A.length iov)
274 RawIO.writevAll fd p (A.length iov)
275-}
275-}
276#endif
276#endif
src/Streamly/Internal/Network/Inet/TCP.hs+3−3
@@ -116,6 +116,7 @@ import Prelude hiding (read)
116import Streamly.Internal.Control.Concurrent (MonadAsync)
116import Streamly.Internal.Control.Concurrent (MonadAsync)
117import Streamly.Internal.Control.ForkLifted (fork)
117import Streamly.Internal.Control.ForkLifted (fork)
118import Streamly.Data.Array (Array)
118import Streamly.Data.Array (Array)
119+import Streamly.Internal.Data.Array (pinnedWriteNUnsafe)
119import Streamly.Internal.Data.Fold ( Fold(..) )
120import Streamly.Internal.Data.Fold ( Fold(..) )
120import Streamly.Data.Stream (Stream)
121import Streamly.Data.Stream (Stream)
121import Streamly.Internal.Data.Tuple.Strict (Tuple'(..))
122import Streamly.Internal.Data.Tuple.Strict (Tuple'(..))
@@ -131,8 +132,7 @@ import qualified Streamly.Data.Array as A
131import qualified Streamly.Data.Fold as FL
132import qualified Streamly.Data.Fold as FL
132import qualified Streamly.Data.Stream as S
133import qualified Streamly.Data.Stream as S
133import qualified Streamly.Data.Unfold as UF
134import qualified Streamly.Data.Unfold as UF
134−import qualified Streamly.Internal.Data.Array as A
135+import qualified Streamly.Internal.Data.Array as A (pinnedChunksOf)
135− (pinnedChunksOf, unsafePinnedCreateOf)
136import qualified Streamly.Internal.Data.Unfold as UF (bracketIO)
136import qualified Streamly.Internal.Data.Unfold as UF (bracketIO)
137import qualified Streamly.Internal.Data.Fold as FL (Step(..), reduce)
137import qualified Streamly.Internal.Data.Fold as FL (Step(..), reduce)
138
138
@@ -435,7 +435,7 @@ writeWithBufferOf
435 -> PortNumber
435 -> PortNumber
436 -> Fold m Word8 ()
436 -> Fold m Word8 ()
437writeWithBufferOf n addr port =
437writeWithBufferOf n addr port =
438− FL.groupsOf n (A.unsafePinnedCreateOf n) (writeChunks addr port)
438+ FL.groupsOf n (pinnedWriteNUnsafe n) (writeChunks addr port)
439
439
440-- | Write a stream to the supplied IPv4 host address and port number.
440-- | Write a stream to the supplied IPv4 host address and port number.
441--
441--
src/Streamly/Internal/Network/Socket.hs+10−11
@@ -87,6 +87,7 @@ import Prelude hiding (read)
87import qualified Network.Socket as Net
87import qualified Network.Socket as Net
88
88
89import Streamly.Internal.Data.Array (Array(..))
89import Streamly.Internal.Data.Array (Array(..))
90+import Streamly.Internal.Data.Array.Stream (lpackArraysChunksOf)
90import Streamly.Data.Fold (Fold)
91import Streamly.Data.Fold (Fold)
91import Streamly.Data.Stream (Stream)
92import Streamly.Data.Stream (Stream)
92import Streamly.Internal.Data.Unfold (Unfold(..))
93import Streamly.Internal.Data.Unfold (Unfold(..))
@@ -98,10 +99,10 @@ import qualified Streamly.Data.Fold as FL
98import qualified Streamly.Data.Stream as S
99import qualified Streamly.Data.Stream as S
99import qualified Streamly.Data.Unfold as UF
100import qualified Streamly.Data.Unfold as UF
100import qualified Streamly.Internal.Data.Array as A
101import qualified Streamly.Internal.Data.Array as A
101− ( unsafeFreeze, unsafePinnedAsPtr, byteLength, pinnedChunksOf,
102+ ( unsafeFreeze, asPtrUnsafe, byteLength, pinnedChunksOf,
102− pinnedCreateOf, unsafePinnedCreateOf, lCompactGE )
103+ pinnedWriteN, pinnedWriteNUnsafe )
103import qualified Streamly.Internal.Data.MutArray as MArray
104import qualified Streamly.Internal.Data.MutArray as MArray
104− (MutArray(..), unsafePinnedAsPtr, pinnedEmptyOf)
105+ (MutArray(..), asPtrUnsafe, pinnedNewBytes)
105import qualified Streamly.Internal.Data.Stream as S (fromStreamK, Stream(..), Step(..))
106import qualified Streamly.Internal.Data.Stream as S (fromStreamK, Stream(..), Step(..))
106import qualified Streamly.Internal.Data.StreamK as K (mkStream)
107import qualified Streamly.Internal.Data.StreamK as K (mkStream)
107
108
@@ -252,8 +253,6 @@ accept tcpListenQ spec addr =
252-- Array IO (Input)
253-- Array IO (Input)
253-------------------------------------------------------------------------------
254-------------------------------------------------------------------------------
254
255
255−-- XXX add an API that compacts the arrays to an exact size.
256−
257{-# INLINABLE readArrayUptoWith #-}
256{-# INLINABLE readArrayUptoWith #-}
258readArrayUptoWith
257readArrayUptoWith
259 :: (h -> Ptr Word8 -> Int -> IO Int)
258 :: (h -> Ptr Word8 -> Int -> IO Int)
@@ -261,9 +260,9 @@ readArrayUptoWith
261 -> h
260 -> h
262 -> IO (Array Word8)
261 -> IO (Array Word8)
263readArrayUptoWith f size h = do
262readArrayUptoWith f size h = do
264− arr <- MArray.pinnedEmptyOf size
263+ arr <- MArray.pinnedNewBytes size
265 -- ptr <- mallocPlainForeignPtrAlignedBytes size (alignment (undefined :: Word8))
264 -- ptr <- mallocPlainForeignPtrAlignedBytes size (alignment (undefined :: Word8))
266− MArray.unsafePinnedAsPtr arr $ \p -> do
265+ MArray.asPtrUnsafe arr $ \p -> do
267 n <- f h p size
266 n <- f h p size
268 let v = A.unsafeFreeze
267 let v = A.unsafeFreeze
269 $ arr { MArray.arrEnd = n, MArray.arrBound = size }
268 $ arr { MArray.arrEnd = n, MArray.arrBound = size }
@@ -311,7 +310,7 @@ writeArrayWith :: Unbox a
311 -> Array a
310 -> Array a
312 -> IO ()
311 -> IO ()
313writeArrayWith _ _ arr | A.length arr == 0 = return ()
312writeArrayWith _ _ arr | A.length arr == 0 = return ()
314−writeArrayWith f h arr = A.unsafePinnedAsPtr arr $ \ptr -> f h (castPtr ptr) aLen
313+writeArrayWith f h arr = A.asPtrUnsafe arr $ \ptr -> f h (castPtr ptr) aLen
315
314
316 where
315 where
317
316
@@ -474,7 +473,7 @@ writeChunks h = FL.drainMapM (liftIO . putChunk h)
474{-# INLINE writeChunksWith #-}
473{-# INLINE writeChunksWith #-}
475writeChunksWith :: (MonadIO m, Unbox a)
474writeChunksWith :: (MonadIO m, Unbox a)
476 => Int -> Socket -> Fold m (Array a) ()
475 => Int -> Socket -> Fold m (Array a) ()
477−writeChunksWith n h = A.lCompactGE n (writeChunks h)
476+writeChunksWith n h = lpackArraysChunksOf n (writeChunks h)
478
477
479-- | Same as 'writeChunksWith'
478-- | Same as 'writeChunksWith'
480--
479--
@@ -505,7 +504,7 @@ putBytesWith n h m = putChunks h $ A.pinnedChunksOf n m
505--
504--
506{-# INLINE writeWith #-}
505{-# INLINE writeWith #-}
507writeWith :: MonadIO m => Int -> Socket -> Fold m Word8 ()
506writeWith :: MonadIO m => Int -> Socket -> Fold m Word8 ()
508−writeWith n h = FL.groupsOf n (A.unsafePinnedCreateOf n) (writeChunks h)
507+writeWith n h = FL.groupsOf n (A.pinnedWriteNUnsafe n) (writeChunks h)
509
508
510-- | Same as 'writeWith'
509-- | Same as 'writeWith'
511--
510--
@@ -523,7 +522,7 @@ writeWithBufferOf = writeWith
523writeMaybesWith :: (MonadIO m )
522writeMaybesWith :: (MonadIO m )
524 => Int -> Socket -> Fold m (Maybe Word8) ()
523 => Int -> Socket -> Fold m (Maybe Word8) ()
525writeMaybesWith n h =
524writeMaybesWith n h =
526− let writeNJusts = FL.lmap fromJust $ A.pinnedCreateOf n
525+ let writeNJusts = FL.lmap fromJust $ A.pinnedWriteN n
527 writeOnNothing = FL.takeEndBy_ isNothing writeNJusts
526 writeOnNothing = FL.takeEndBy_ isNothing writeNJusts
528 in FL.many writeOnNothing (writeChunks h)
527 in FL.many writeOnNothing (writeChunks h)
529
528
src/Streamly/Internal/Unicode/Char.hs+3−3
@@ -227,13 +227,13 @@ partialComposeD (Stream step state) = Stream step' (ComposeNone state)
227 Stop -> Skip $ YieldList rbuf ComposeStop
227 Stop -> Skip $ YieldList rbuf ComposeStop
228
228
229 {-# INLINE initHangul #-}
229 {-# INLINE initHangul #-}
230− initHangul c = ComposeJamo (Hangul c)
230+ initHangul c st = ComposeJamo (Hangul c) st
231
231
232 {-# INLINE initJamo #-}
232 {-# INLINE initJamo #-}
233− initJamo c = ComposeJamo (Jamo c)
233+ initJamo c st = ComposeJamo (Jamo c) st
234
234
235 {-# INLINE initReg #-}
235 {-# INLINE initReg #-}
236− initReg !c = ComposeReg 0 [c]
236+ initReg !c st = ComposeReg 0 [c] st
237
237
238 {-# INLINE composeNone #-}
238 {-# INLINE composeNone #-}
239 composeNone ch st
239 composeNone ch st
src/Streamly/Network/Inet/TCP.hs−28
@@ -11,34 +11,6 @@
11--
11--
12-- >>> import qualified Streamly.Network.Inet.TCP as TCP
12-- >>> import qualified Streamly.Network.Inet.TCP as TCP
13--
13--
14−-- = Examples
15−--
16−-- Following is a short example of a concurrent echo server.
17−--
18−-- >>> import Control.Monad.Catch (finally)
19−-- >>> import Data.Function ((&))
20−-- >>> import Network.Socket (Socket)
21−-- >>>
22−-- >>> import qualified Network.Socket as Net
23−-- >>> import qualified Streamly.Data.Fold as Fold
24−-- >>> import qualified Streamly.Data.Stream.Prelude as Stream
25−-- >>> import qualified Streamly.Network.Inet.TCP as TCP
26−-- >>> import qualified Streamly.Network.Socket as Socket
27−-- >>>
28−-- >>> :{
29−-- main :: IO ()
30−-- main =
31−-- TCP.accept 8091 -- Stream IO Socket
32−-- & Stream.parMapM id (handleExceptions echo) -- Stream IO ()
33−-- & Stream.fold Fold.drain -- IO ()
34−-- where
35−-- echo :: Socket -> IO ()
36−-- echo sk =
37−-- Socket.readChunksWith 32768 sk -- Stream IO (Array Word8)
38−-- & Stream.fold (Socket.writeChunks sk) -- IO ()
39−-- handleExceptions :: (Socket -> IO ()) -> Socket -> IO ()
40−-- handleExceptions f sk = finally (f sk) (Net.close sk)
41−-- :}
42
14
43module Streamly.Network.Inet.TCP
15module Streamly.Network.Inet.TCP
44 (
16 (
src/Streamly/Network/Socket.hs+4−3
@@ -10,13 +10,13 @@
10-- This module provides socket based streaming APIs to to receive connections
10-- This module provides socket based streaming APIs to to receive connections
11-- from remote hosts, and to read and write from and to network sockets.
11-- from remote hosts, and to read and write from and to network sockets.
12--
12--
13−-- For basic socket types and non-streaming operations please consult the @Network.Socket@
13+-- For basic socket types and operations please consult the @Network.Socket@
14-- module of the <http://hackage.haskell.org/package/network network> package.
14-- module of the <http://hackage.haskell.org/package/network network> package.
15--
15--
16-- = Examples
16-- = Examples
17--
17--
18−-- To write a server, use the 'accept' stream to start listening for
18+-- To write a server, use the 'acceptor' unfold to start listening for
19−-- connections from clients. 'accept' generates a stream of connected
19+-- connections from clients. 'acceptor' generates a stream of connected
20-- sockets. We can map an effectful action on this socket stream to handle the
20-- sockets. We can map an effectful action on this socket stream to handle the
21-- connections. The action would typically use socket reading and writing
21-- connections. The action would typically use socket reading and writing
22-- operations to communicate with the remote host. We can read/write a stream
22-- operations to communicate with the remote host. We can read/write a stream
@@ -33,6 +33,7 @@
33-- >>> import Streamly.Network.Socket (SockSpec(..))
33-- >>> import Streamly.Network.Socket (SockSpec(..))
34-- >>>
34-- >>>
35-- >>> import qualified Streamly.Data.Fold as Fold
35-- >>> import qualified Streamly.Data.Fold as Fold
36+-- >>> import qualified Streamly.Data.Stream as Stream
36-- >>> import qualified Streamly.Data.Stream.Prelude as Stream
37-- >>> import qualified Streamly.Data.Stream.Prelude as Stream
37-- >>> import qualified Streamly.Network.Socket as Socket
38-- >>> import qualified Streamly.Network.Socket as Socket
38-- >>>
39-- >>>
streamly.cabal+15−19
@@ -1,14 +1,12 @@
1cabal-version: 2.2
1cabal-version: 2.2
2name: streamly
2name: streamly
3−version: 0.10.1
3+version: 0.10.0
4synopsis: Streaming, dataflow programming and declarative concurrency
4synopsis: Streaming, dataflow programming and declarative concurrency
5description:
5description:
6 For upgrading to streamly-0.9.0+ please read the
6 For upgrading to streamly-0.9.0+ please read the
7 <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>.
7 <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>.
8 .
8 .
9− Streamly is a standard library for Haskell that focuses on C-like
9+ Streamly comprises two packages, the
10− performance, modular combinators, and streaming data flow model.
11− Streamly consists of two packages, the
12 <https://hackage.haskell.org/package/streamly-core streamly-core> package
10 <https://hackage.haskell.org/package/streamly-core streamly-core> package
13 provides functionality that depends only on boot libraries, and
11 provides functionality that depends only on boot libraries, and
14 the <https://hackage.haskell.org/package/streamly streamly> package
12 the <https://hackage.haskell.org/package/streamly streamly> package
@@ -294,6 +292,8 @@ common compile-options
294common default-extensions
292common default-extensions
295 default-extensions:
293 default-extensions:
296 BangPatterns
294 BangPatterns
295+ CApiFFI
296+ CPP
297 ConstraintKinds
297 ConstraintKinds
298 DeriveDataTypeable
298 DeriveDataTypeable
299 DeriveGeneric
299 DeriveGeneric
@@ -304,25 +304,23 @@ common default-extensions
304 GeneralizedNewtypeDeriving
304 GeneralizedNewtypeDeriving
305 InstanceSigs
305 InstanceSigs
306 KindSignatures
306 KindSignatures
307+ LambdaCase
308+ MagicHash
307 MultiParamTypeClasses
309 MultiParamTypeClasses
310+ PatternSynonyms
308 RankNTypes
311 RankNTypes
312+ RecordWildCards
309 ScopedTypeVariables
313 ScopedTypeVariables
310 TupleSections
314 TupleSections
311 TypeApplications
315 TypeApplications
316+ TypeFamilies
312 TypeOperators
317 TypeOperators
318+ ViewPatterns
313
319
314− -- Not GHC2021
315− CApiFFI
316− CPP
317− LambdaCase
318− MagicHash
319− RecordWildCards
320−
321− -- TypeFamilies -- required by IsHashMap type class
322 -- MonoLocalBinds, enabled by TypeFamilies, causes performance
320 -- MonoLocalBinds, enabled by TypeFamilies, causes performance
323 -- regressions. Disable it. This must come after TypeFamilies,
321 -- regressions. Disable it. This must come after TypeFamilies,
324 -- otherwise TypeFamilies will enable it again.
322 -- otherwise TypeFamilies will enable it again.
325− -- NoMonoLocalBinds
323+ NoMonoLocalBinds
326
324
327 -- UndecidableInstances -- Does not show any perf impact
325 -- UndecidableInstances -- Does not show any perf impact
328 -- UnboxedTuples -- interferes with (#.)
326 -- UnboxedTuples -- interferes with (#.)
@@ -429,11 +427,9 @@ library
429 , Streamly.Internal.FileSystem.FD
427 , Streamly.Internal.FileSystem.FD
430
428
431 if flag(dev)
429 if flag(dev)
432− exposed-modules: Streamly.Internal.Data.SmallArray
430+ exposed-modules: Streamly.Data.SmallArray
433− -- Exposed modules show up on hackage irrespective of the flag, so keep
431+ , Streamly.Internal.Data.SmallArray
434− -- it hidden.
432+ other-modules: Streamly.Internal.Data.SmallArray.Type
435− other-modules: Streamly.Data.SmallArray
436− , Streamly.Internal.Data.SmallArray.Type
437
433
438 if os(windows)
434 if os(windows)
439 exposed-modules: Streamly.Internal.FileSystem.Event.Windows
435 exposed-modules: Streamly.Internal.FileSystem.Event.Windows
@@ -513,7 +509,7 @@ library
513 , template-haskell >= 2.14 && < 2.22
509 , template-haskell >= 2.14 && < 2.22
514
510
515 -- The core streamly package
511 -- The core streamly package
516− , streamly-core == 0.2.2
512+ , streamly-core == 0.2.0
517
513
518 , hashable >= 1.3 && < 1.5
514 , hashable >= 1.3 && < 1.5
519 , unordered-containers >= 0.2 && < 0.3
515 , unordered-containers >= 0.2 && < 0.3
test/Streamly/Test/Data/Array.hs+2−2
@@ -185,7 +185,7 @@ testUnsafeIndxedFromList inp =
185testAsPtrUnsafeMA :: IO ()
185testAsPtrUnsafeMA :: IO ()
186testAsPtrUnsafeMA = do
186testAsPtrUnsafeMA = do
187 arr <- MA.fromList ([0 .. 99] :: [Int])
187 arr <- MA.fromList ([0 .. 99] :: [Int])
188− MA.unsafePinnedAsPtr arr (getList (0 :: Int)) `shouldReturn` [0 .. 99]
188+ MA.asPtrUnsafe arr (getList (0 :: Int)) `shouldReturn` [0 .. 99]
189
189
190 where
190 where
191
191
@@ -229,7 +229,7 @@ main =
229 prop "read . write === id" testFoldUnfold
229 prop "read . write === id" testFoldUnfold
230 prop "fromList" testFromList
230 prop "fromList" testFromList
231 prop "foldMany with writeNUnsafe concats to original"
231 prop "foldMany with writeNUnsafe concats to original"
232− (foldManyWith (\n -> Fold.take n (A.unsafeCreateOf n)))
232+ (foldManyWith (\n -> Fold.take n (A.writeNUnsafe n)))
233 describe "unsafeSlice" $ do
233 describe "unsafeSlice" $ do
234 it "partial" $ unsafeSlice 2 4 [1..10]
234 it "partial" $ unsafeSlice 2 4 [1..10]
235 it "none" $ unsafeSlice 10 0 [1..10]
235 it "none" $ unsafeSlice 10 0 [1..10]
test/Streamly/Test/Data/Fold.hs+6−7
@@ -2,8 +2,7 @@
2
2
3module Main (main) where
3module Main (main) where
4
4
5−import Data.List (sort, sortBy)
5+import Data.List (sort)
6−import Data.Ord (comparing, Down(..))
7import Data.Semigroup (Sum(..), getSum)
6import Data.Semigroup (Sum(..), getSum)
8import Streamly.Test.Common (checkListEqual, listEquals)
7import Streamly.Test.Common (checkListEqual, listEquals)
9import Test.QuickCheck
8import Test.QuickCheck
@@ -413,7 +412,7 @@ partitionByM =
413 where
412 where
414
413
415 action ls = do
414 action ls = do
416− let f x = if odd x then return (Left x) else return (Right x)
415+ let f = \x -> if odd x then return (Left x) else return (Right x)
417 v1 <-
416 v1 <-
418 run
417 run
419 $ Stream.fold (Fold.partitionByM f Fold.length Fold.length)
418 $ Stream.fold (Fold.partitionByM f Fold.length Fold.length)
@@ -430,7 +429,7 @@ partitionByFstM =
430 where
429 where
431
430
432 action _ = do
431 action _ = do
433− let f x = if odd x then return (Left x) else return (Right x)
432+ let f = \x -> if odd x then return (Left x) else return (Right x)
434 v1 <-
433 v1 <-
435 run
434 run
436 $ Stream.fold
435 $ Stream.fold
@@ -448,7 +447,7 @@ partitionByMinM1 =
448 where
447 where
449
448
450 action _ = do
449 action _ = do
451− let f x = if odd x then return (Left x) else return (Right x)
450+ let f = \x -> if odd x then return (Left x) else return (Right x)
452 v1 <-
451 v1 <-
453 run
452 run
454 $ Stream.fold
453 $ Stream.fold
@@ -466,7 +465,7 @@ partitionByMinM2 =
466 where
465 where
467
466
468 action _ = do
467 action _ = do
469− let f x = if odd x then return (Left x) else return (Right x)
468+ let f = \x -> if odd x then return (Left x) else return (Right x)
470 v1 <-
469 v1 <-
471 run
470 run
472 $ Stream.fold
471 $ Stream.fold
@@ -691,7 +690,7 @@ topBy isTop = forAll (listOf (chooseInt (-50, 100))) $ \ls0 ->
691 if isTop
690 if isTop
692 then do
691 then do
693 lst <- Stream.fold (Fold.top n) (Stream.fromList ls) >>= MArray.toList
692 lst <- Stream.fold (Fold.top n) (Stream.fromList ls) >>= MArray.toList
694− assert ((Prelude.take n . sortBy (comparing Down)) ls == lst)
693+ assert ((Prelude.take n . Prelude.reverse . sort) ls == lst)
695 else do
694 else do
696 lst <- Stream.fold (Fold.bottom n) (Stream.fromList ls) >>= MArray.toList
695 lst <- Stream.fold (Fold.bottom n) (Stream.fromList ls) >>= MArray.toList
697 assert ((Prelude.take n . sort) ls == lst)
696 assert ((Prelude.take n . sort) ls == lst)
test/Streamly/Test/Data/Serialize.hs+1−32
@@ -21,8 +21,6 @@ module Streamly.Test.Data.Serialize (main) where
21-- Imports
21-- Imports
22--------------------------------------------------------------------------------
22--------------------------------------------------------------------------------
23
23
24−import Data.Foldable (forM_)
25−import Data.Word (Word8)
26import System.Random (randomRIO)
24import System.Random (randomRIO)
27import Streamly.Internal.Data.MutByteArray (MutByteArray)
25import Streamly.Internal.Data.MutByteArray (MutByteArray)
28import GHC.Generics (Generic)
26import GHC.Generics (Generic)
@@ -109,19 +107,9 @@ $(Serialize.deriveSerializeWith
109 CONF
107 CONF
110 [d|instance Serialize a => Serialize (BinTree a)|])
108 [d|instance Serialize a => Serialize (BinTree a)|])
111
109
112−-- XXX This may not terminate, or could become really large.
113instance Arbitrary a => Arbitrary (BinTree a) where
110instance Arbitrary a => Arbitrary (BinTree a) where
114 arbitrary = oneof [Leaf <$> arbitrary, Tree <$> arbitrary <*> arbitrary]
111 arbitrary = oneof [Leaf <$> arbitrary, Tree <$> arbitrary <*> arbitrary]
115
112
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−
125--------------------------------------------------------------------------------
113--------------------------------------------------------------------------------
126-- Record syntax type
114-- Record syntax type
127--------------------------------------------------------------------------------
115--------------------------------------------------------------------------------
@@ -182,18 +170,8 @@ poke val = do
182 serStartOff = randomOff
170 serStartOff = randomOff
183 serEndOff = randomOff + sz
171 serEndOff = randomOff + sz
184 arr <- Serialize.new arrSize
172 arr <- Serialize.new arrSize
185− arr2 <- Serialize.new arrSize
186− -- Re-initialize the array with random value
187− forM_ [0..(arrSize - 1)] $ \i -> Serialize.pokeAt i arr2 (8 :: Word8)
188
173
189 off1 <- Serialize.serializeAt serStartOff arr val
174 off1 <- Serialize.serializeAt serStartOff arr val
190− off2 <- Serialize.serializeAt 0 arr2 val
191−
192− let slice1 = Array.Array arr serStartOff off1 :: Array.Array Word8
193− slice2 = Array.Array arr2 0 off2 :: Array.Array Word8
194− -- The serialized representation should be the same
195− slice1 `shouldBe` slice2
196−
197 off1 `shouldBe` serEndOff
175 off1 `shouldBe` serEndOff
198 pure (arr, serStartOff, serEndOff)
176 pure (arr, serStartOff, serEndOff)
199
177
@@ -217,10 +195,6 @@ roundtrip
217 -> IO ()
195 -> IO ()
218roundtrip val = do
196roundtrip val = do
219
197
220− -- For debugging large size generated by arbitrary
221− -- let sz = Serialize.addSizeTo 0 val
222− -- putStrLn $ "Size is: " ++ show sz
223−
224 val `shouldBe` Array.deserialize (Array.pinnedSerialize val)
198 val `shouldBe` Array.deserialize (Array.pinnedSerialize val)
225
199
226 res <- poke val
200 res <- poke val
@@ -284,12 +258,7 @@ testCases = do
284 $ \(x :: [CustomDatatype]) -> roundtrip x
258 $ \(x :: [CustomDatatype]) -> roundtrip x
285
259
286 limitQC
260 limitQC
287− $ prop "BinTree"
261+ $ prop "BinTree" $ \(x :: BinTree Int) -> roundtrip x
288− $ forAll (elements [1..15])
289− (\(x :: Int) -> do
290− (r :: BinTree Int) <- mkBinTree x
291− roundtrip r
292− )
293
262
294 where
263 where
295
264
test/Streamly/Test/Data/Serialize/TH.hs+2−2
@@ -26,7 +26,7 @@ import Test.QuickCheck.Gen (oneof)
26genDatatype :: String -> Int -> Q [Dec]
26genDatatype :: String -> Int -> Q [Dec]
27genDatatype tyName numCons =
27genDatatype tyName numCons =
28 sequence
28 sequence
29− [ dataD
29+ ([ dataD
30 (pure [])
30 (pure [])
31 (mkName tyName)
31 (mkName tyName)
32 []
32 []
@@ -49,7 +49,7 @@ genDatatype tyName numCons =
49 []
49 []
50 ]
50 ]
51 ]
51 ]
52− ]
52+ ])
53
53
54 where
54 where
55
55
test/Streamly/Test/Data/Unbox.hs−19
@@ -20,12 +20,6 @@ module Streamly.Test.Data.Unbox (main) where
20-- Imports
20-- Imports
21--------------------------------------------------------------------------------
21--------------------------------------------------------------------------------
22
22
23−#ifdef USE_SERIALIZE
24−import Data.Foldable (forM_)
25−import Data.Word (Word8)
26−import qualified Streamly.Internal.Data.Array as Array
27−#endif
28−
29import Data.Complex (Complex ((:+)))
23import Data.Complex (Complex ((:+)))
30import Data.Functor.Const (Const (..))
24import Data.Functor.Const (Const (..))
31import Data.Functor.Identity (Identity (..))
25import Data.Functor.Identity (Identity (..))
@@ -207,19 +201,6 @@ testSerialization val = do
207#endif
201#endif
208 arr <- MBA.new len
202 arr <- MBA.new len
209 nextOff <- POKE(0, arr, val)
203 nextOff <- POKE(0, arr, val)
210−#ifdef USE_SERIALIZE
211− arr2 <- MBA.new len
212− -- Re-initialize the array with random value
213− forM_ [0..(len - 1)] $ \i -> POKE(i, arr2, (8 :: Word8))
214− _ <- POKE(0, arr2, val)
215− let slice1 = Array.Array arr 0 len :: Array.Array Word8
216− slice2 = Array.Array arr2 0 len :: Array.Array Word8
217− -- The serialized representation should be the same
218− slice1 `shouldBe` slice2
219− -- The serialized representation is not the same for "Unbox" as the Array
220− -- might not be fully utilized in case of "Unbox". This is because different
221− -- constructors might have different lengths.
222−#endif
223 (nextOff1, val1) <- PEEK(0, arr, len)
204 (nextOff1, val1) <- PEEK(0, arr, len)
224 val1 `shouldBe` val
205 val1 `shouldBe` val
225 nextOff1 `shouldBe` len
206 nextOff1 `shouldBe` len
test/Streamly/Test/FileSystem/Handle.hs+2−2
@@ -75,7 +75,7 @@ readFromHandle =
75
75
76readWithBufferFromHandle :: IO (Stream IO Char)
76readWithBufferFromHandle :: IO (Stream IO Char)
77readWithBufferFromHandle =
77readWithBufferFromHandle =
78− let f1 h = (1024, h)
78+ let f1 = (\h -> (1024, h))
79 f2 = Unicode.decodeUtf8 . Stream.unfold Handle.readerWith . f1
79 f2 = Unicode.decodeUtf8 . Stream.unfold Handle.readerWith . f1
80 in executor f2
80 in executor f2
81
81
@@ -88,7 +88,7 @@ readChunksFromHandle =
88
88
89readChunksWithBuffer :: IO (Stream IO Char)
89readChunksWithBuffer :: IO (Stream IO Char)
90readChunksWithBuffer =
90readChunksWithBuffer =
91− let f1 h = (1024, h)
91+ let f1 = (\h -> (1024, h))
92 f2 =
92 f2 =
93 Unicode.decodeUtf8
93 Unicode.decodeUtf8
94 . Stream.concatMap Array.read
94 . Stream.concatMap Array.read
test/Streamly/Test/Unicode/Char.hs+1−1
@@ -54,7 +54,7 @@ checkEqual opName op (mc1, mc2) = do
54checkOp :: String -> NormalizationMode -> [(Text, Text)] -> IO Bool
54checkOp :: String -> NormalizationMode -> [(Text, Text)] -> IO Bool
55checkOp name op pairs = do
55checkOp name op pairs = do
56 res <- mapM (checkEqual name (normalize op)) pairs
56 res <- mapM (checkEqual name (normalize op)) pairs
57− return $ and res
57+ return $ all (== True) res
58
58
59checkNFC :: (Text, Text, Text, Text, Text) -> IO Bool
59checkNFC :: (Text, Text, Text, Text, Text) -> IO Bool
60checkNFC (c1, c2, c3, c4, c5) =
60checkNFC (c1, c2, c3, c4, c5) =