on master help sign in
.circleci/config.yml+73−277
since splitsnapshots
unifiedsplit
@@ -1,7 +1,7 @@
1version: 2.1
1version: 2.1
2
2
3#-----------------------------------------------------------------------------
3#-----------------------------------------------------------------------------
4−# packcheck-0.4.2
4+# packcheck-0.7.0
5# Packcheck global environment variables
5# Packcheck global environment variables
6#-----------------------------------------------------------------------------
6#-----------------------------------------------------------------------------
7
7
@@ -11,6 +11,8 @@ 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
14 CABAL_REINIT_CONFIG: "y"
16 CABAL_REINIT_CONFIG: "y"
15 LC_ALL: "C.UTF-8"
17 LC_ALL: "C.UTF-8"
16
18
@@ -22,7 +24,6 @@ env: &env
22 # DISABLE_DOCS: "y"
24 # DISABLE_DOCS: "y"
23 # DISABLE_SDIST_BUILD: "y"
25 # DISABLE_SDIST_BUILD: "y"
24 # DISABLE_DIST_CHECKS: "y"
26 # DISABLE_DIST_CHECKS: "y"
25− ENABLE_INSTALL: "y"
26
27
27 # ------------------------------------------------------------------------
28 # ------------------------------------------------------------------------
28 # stack options
29 # stack options
@@ -38,15 +39,12 @@ env: &env
38 # cabal options
39 # cabal options
39 # ------------------------------------------------------------------------
40 # ------------------------------------------------------------------------
40 CABAL_CHECK_RELAX: "y"
41 CABAL_CHECK_RELAX: "y"
41− CABAL_NO_SANDBOX: "y"
42− CABAL_HACKAGE_MIRROR: "hackage.haskell.org:http://hackage.fpcomplete.com"
43 CABAL_BUILD_OPTIONS: "-j1 --flag limit-build-mem"
42 CABAL_BUILD_OPTIONS: "-j1 --flag limit-build-mem"
44
43
45 # ------------------------------------------------------------------------
44 # ------------------------------------------------------------------------
46 # Where to find the required tools
45 # Where to find the required tools
47 # ------------------------------------------------------------------------
46 # ------------------------------------------------------------------------
48− PATH: /opt/ghc/bin:/opt/ghcjs/bin:/sbin:/usr/sbin:/bin:/usr/bin
47+ PATH: /sbin:/usr/sbin:/bin:/usr/bin
49− TOOLS_DIR: /opt
50
48
51 # ------------------------------------------------------------------------
49 # ------------------------------------------------------------------------
52 # Location of packcheck.sh (the shell script invoked to perform CI tests ).
50 # Location of packcheck.sh (the shell script invoked to perform CI tests ).
@@ -59,15 +57,15 @@ env: &env
59 # If you have not committed packcheck.sh in your repo at PACKCHECK
57 # If you have not committed packcheck.sh in your repo at PACKCHECK
60 # then it is automatically pulled from this URL.
58 # then it is automatically pulled from this URL.
61 PACKCHECK_GITHUB_URL: "https://raw.githubusercontent.com/composewell/packcheck"
59 PACKCHECK_GITHUB_URL: "https://raw.githubusercontent.com/composewell/packcheck"
62− PACKCHECK_GITHUB_COMMIT: "a302772c8f3791c477edd1764b82f8d1f5b22fdf"
60+ PACKCHECK_GITHUB_COMMIT: "f6cfd7b7a33b4bf6fd63cce6a3fb3d26f5031403"
63
61
64executors:
62executors:
65 amd64-executor:
63 amd64-executor:
66 docker:
64 docker:
67− - image: ubuntu:xenial
65+ - image: ubuntu:latest
68 x86-executor:
66 x86-executor:
69 docker:
67 docker:
70− - image: i386/ubuntu:eoan
68+ - image: i386/ubuntu:focal
71
69
72#-----------------------------------------------------------------------------
70#-----------------------------------------------------------------------------
73# Common utility stuff, not to be modified usually
71# Common utility stuff, not to be modified usually
@@ -79,38 +77,50 @@ preinstall: &preinstall
79 # required for https/cache save and restore
77 # required for https/cache save and restore
80 apt-get install -y ca-certificates
78 apt-get install -y ca-certificates
81
79
82− # required to (re)generate the configure script
80+ # Alternative way of installing ghc and cabal, directly from
83− apt-get install -y autoconf
81+ # haskell.org instead of using ghcup. NOTE: this is for Debian
84−
82+ # only and is debian release specific.
85− # For ghc and cabal-install packages from hvr's ppa
86 # gnupg is required for apt-key to work
83 # gnupg is required for apt-key to work
87− apt-get install -y gnupg
84+ #apt-get install -y gnupg
88− apt-get install -y apt-transport-https
89− apt-key adv --keyserver keyserver.ubuntu.com --recv-keys BA3CBA3FFE22B574
85+ #apt-key adv --keyserver keyserver.ubuntu.com --recv-keys BA3CBA3FFE22B574
90− apt-get install -y software-properties-common
91− add-apt-repository -y ppa:hvr/ghc
92− # echo "deb http://downloads.haskell.org/debian stretch main" >> /etc/apt/sources.list
86+ #echo "deb http://downloads.haskell.org/debian buster 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
87+ #apt-get update
95
88
96− # required for packcheck
89+ # required for outbound https for stack and for stack setup
90+ apt-get install -y netbase xz-utils make
97− apt-get install -y curl
91+ apt-get install -y zlib1g-dev
98
92
93+ # For ghcup to install ghc
94+ if test -n "$GHCUP_VERSION"
95+ then
96+ apt-get install -y gcc
97+ apt-get install -y g++
98+ fi
99+
99− # required for circleci.
100+ # libgmp required by ghc for linking
100− apt-get install -y libcurl4-gnutls-dev
101+ apt-get install -y libgmp-dev
101− apt-get install -y git
102 apt-get install -y libtinfo-dev
102 apt-get install -y libtinfo-dev
103
103
104− # required for outbound https for stack and for stack setup
104+ # Required by cabal when git URL is specified in project file
105+ apt-get install -y git
106+
107+ # Required for and by packcheck
105− apt-get install -y netbase xz-utils make
108+ apt-get install -y curl
106
109
107−project-preinstall: &project-preinstall
108− run: |
109 # Get packcheck if needed
110 # Get packcheck if needed
110− CURL=$(which curl)
111+ if test ! -e "$PACKCHECK"
112+ then
113+ if test -z "$PACKCHECK_GITHUB_COMMIT"
114+ then
115+ die "PACKCHECK_GITHUB_COMMIT is not specified."
116+ fi
111− PACKCHECK_URL=${PACKCHECK_GITHUB_URL}/${PACKCHECK_GITHUB_COMMIT}/packcheck.sh
117+ PACKCHECK_URL=${PACKCHECK_GITHUB_URL}/${PACKCHECK_GITHUB_COMMIT}/packcheck.sh
112− if test ! -e "$PACKCHECK"; then $CURL -sL -o "$PACKCHECK" $PACKCHECK_URL; fi;
118+ curl --fail -sL -o "$PACKCHECK" $PACKCHECK_URL || exit 1
119+ chmod +x $PACKCHECK
120+ elif test ! -x "$PACKCHECK"
121+ then
113− chmod +x $PACKCHECK
122+ chmod +x $PACKCHECK
123+ fi
114
124
115restore: &restore
125restore: &restore
116 # Needs to happen after installing ca-certificates
126 # Needs to happen after installing ca-certificates
@@ -126,271 +136,71 @@ save: &save
126 save_cache:
136 save_cache:
127 key: v1-{{ .Environment.CIRCLE_JOB }}-{{ .Branch }}
137 key: v1-{{ .Environment.CIRCLE_JOB }}-{{ .Branch }}
128 paths:
138 paths:
129− - ~/.cabal
130− - ~/.ghc
131 - ~/.local
139 - ~/.local
140+ - ~/.cabal
132 - ~/.stack
141 - ~/.stack
142+ - ~/.ghcup
133
143
134#-----------------------------------------------------------------------------
144#-----------------------------------------------------------------------------
135# Build matrix
145# Build matrix
136#-----------------------------------------------------------------------------
146#-----------------------------------------------------------------------------
137
147
138jobs:
148jobs:
139− cabal-ghc-8_10_4:
149+ cabal-ghc-8_6_5:
140 <<: *env
150 <<: *env
141 executor: amd64-executor
151 executor: amd64-executor
142 steps:
152 steps:
143− - *preinstall
144 - checkout
153 - checkout
145− - *project-preinstall
146 - *restore
154 - *restore
155+ - *preinstall
147 - run:
156 - run:
148 environment:
157 environment:
158+ GHCVER: "8.6.5"
159+ CABAL_VERSION: "3.6.2.0"
149 CABAL_PROJECT: "cabal.project"
160 CABAL_PROJECT: "cabal.project"
150− DISABLE_TEST: "yes"
151− DISABLE_BENCH: "yes"
152 DISABLE_SDIST_BUILD: "yes"
161 DISABLE_SDIST_BUILD: "yes"
162+ CABAL_BUILD_OPTIONS: "--flag debug --flag -opt"
153 command: |
163 command: |
154− apt-get install -y ghc-8.10.4
155− apt-get install -y cabal-install-3.4
156 bash -c "$PACKCHECK cabal"
164 bash -c "$PACKCHECK cabal"
157 - *save
165 - *save
158− stack-ghc-8_8:
166+ cabal-ghc-9_8_1-docspec:
159 <<: *env
167 <<: *env
160 executor: amd64-executor
168 executor: amd64-executor
161 steps:
169 steps:
162− - *preinstall
163 - checkout
170 - checkout
164− - *project-preinstall
165 - *restore
171 - *restore
166− - run:
167− name: GHC 8.8 + stack lts-16.12
168− environment:
169− BUILD: "stack"
170− RESOLVER: "lts-16.12"
171− SDIST_OPTIONS: "--ignore-check"
172− STACK_BUILD_OPTIONS: "--flag streamly-benchmarks:-opt"
173− command: |
174− apt-get build-dep -y ghc-8.8.4
175− apt-get install -y cabal-install-3.2
176− bash -c "$PACKCHECK $BUILD"
177− - *save
178− cabal-ghc-8_6_5:
179− <<: *env
180− executor: amd64-executor
181− steps:
182 - *preinstall
172 - *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
218 - run:
173 - run:
219 environment:
174 environment:
220− BUILD: "cabal-v2"
175+ GHCVER: "9.8.1"
176+ CABAL_VERSION: "3.10.1.0"
177+ CABAL_PROJECT: "cabal.project.doctest"
178+ DISABLE_SDIST_BUILD: "y"
221− DISABLE_TEST: "yes"
179+ DISABLE_TEST: "y"
222− DISABLE_BENCH: "yes"
180+ DISABLE_BENCH: "y"
223− DISABLE_DOCS: "yes"
181+ DISABLE_DOCS: "y"
224− DISABLE_SDIST_BUILD: "yes"
182+ ENABLE_DOCSPEC: "y"
225− DISABLE_DIST_CHECKS: "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
184+ DOCSPEC_OPTIONS: "--timeout 60 --check-properties --property-variables xs"
226 command: |
185 command: |
227− apt-get install -y ghc-8.2.2
228− apt-get install -y cabal-install-3.2
229− bash -c "$PACKCHECK $BUILD"
230− - *save
231− cabal-ghc-8_0_2:
232− <<: *env
233− executor: amd64-executor
234− steps:
235− - *preinstall
236− - checkout
237− - *project-preinstall
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"
186+ bash -c "$PACKCHECK cabal"
251− - *save
252− cabal-ghc-7_10_3:
253− <<: *env
254− executor: amd64-executor
255− steps:
256− - *preinstall
257− - checkout
258− - *project-preinstall
259− - *restore
260− - run: |
261− apt-get install -y ghc-7.10.3
262− apt-get install -y cabal-install-2.4
263− bash -c "$PACKCHECK cabal-v2"
264 - *save
187 - *save
265− stack-ghc-8_4:
188+ hlint-trailing-spaces:
266 <<: *env
189 <<: *env
267 executor: amd64-executor
190 executor: amd64-executor
268 steps:
191 steps:
269− - *preinstall
270 - checkout
192 - checkout
271− - *project-preinstall
272 - *restore
193 - *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
194 - *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
295 - run:
195 - run:
296− name: GHCJS 8.4+no-test+no-docs
196+ name: hlint and trailing spaces
297 environment:
197 environment:
298− BUILD: cabal-v2
299− ENABLE_GHCJS: "yes"
300− DISABLE_TEST: "yes"
301− ENABLE_INSTALL: ""
302− DISABLE_DOCS: "yes"
303 DISABLE_SDIST_BUILD: "yes"
198 DISABLE_SDIST_BUILD: "yes"
304− command: |
199+ HLINT_VERSION: 3.6.1
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
389 HLINT_OPTIONS: lint
200 HLINT_OPTIONS: lint
390− DISABLE_SDIST_BUILD: "yes"
391 HLINT_TARGETS: core/src src test benchmark
201 HLINT_TARGETS: core/src src test benchmark
392 command: |
202 command: |
393− bash -c "$PACKCHECK $BUILD" || exit 1
203+ bash -c "$PACKCHECK hlint" || exit 1
394 echo "Checking trailing spaces..."
204 echo "Checking trailing spaces..."
395 count=$(find . -name "*.hs" -exec grep -H '\ $' {} \; | tee /dev/tty | wc -l)
205 count=$(find . -name "*.hs" -exec grep -H '\ $' {} \; | tee /dev/tty | wc -l)
396 exit $count
206 exit $count
@@ -400,23 +210,9 @@ workflows:
400 version: 2
210 version: 2
401 build:
211 build:
402 jobs:
212 jobs:
403− - cabal-ghc-8_10_4:
213+ #- cabal-ghc-8_6_5:
404− name: GHC 8.10.4 + sdist + no-test + no-bench
214+ # name: 8.6.5-debug-unoptimized
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
410− #- cabal-ghc-8_0_2:
411− # name: GHC 8.0.2 + no-test + no-bench + no-docs
412− #- cabal-ghc-7.10.3
215+ - cabal-ghc-9_8_1-docspec:
413− # - cabal-ghcjs-8_4:
216+ name: ghc-9.8.1-docspec
414− # name: GHCJS 8.4 + no-test + no-docs
217+ - hlint-trailing-spaces:
415− #- stack-ghc-8.4
218+ name: hlint and trailing spaces
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+61−60
@@ -13,9 +13,15 @@ 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 }}
16 GHCVER: ${{ matrix.ghc_version }}
21 GHCVER: ${{ matrix.ghc_version }}
17 CABALVER: ${{ matrix.cabal_version }}
22 CABALVER: ${{ matrix.cabal_version }}
18 DISABLE_DOCS: ${{ matrix.disable_docs }}
23 DISABLE_DOCS: ${{ matrix.disable_docs }}
24+ ENABLE_DOCSPEC: ${{ matrix.enable_docspec }}
19 DISABLE_TEST: ${{ matrix.disable_test }}
25 DISABLE_TEST: ${{ matrix.disable_test }}
20 DISABLE_BENCH: ${{ matrix.disable_bench }}
26 DISABLE_BENCH: ${{ matrix.disable_bench }}
21 DISABLE_DIST_CHECKS: ${{ matrix.disable_dist_checks }}
27 DISABLE_DIST_CHECKS: ${{ matrix.disable_dist_checks }}
@@ -39,14 +45,18 @@ jobs:
39 STACK_BUILD_OPTIONS: ${{ matrix.stack_build_options }}
45 STACK_BUILD_OPTIONS: ${{ matrix.stack_build_options }}
40
46
41 # packcheck location and revision
47 # packcheck location and revision
42− PACKCHECK_LOCAL_PATH: "./packcheck.sh"
48+ PACKCHECK: "./packcheck.sh"
43 PACKCHECK_GITHUB_URL: "https://raw.githubusercontent.com/composewell/packcheck"
49 PACKCHECK_GITHUB_URL: "https://raw.githubusercontent.com/composewell/packcheck"
44− PACKCHECK_GITHUB_COMMIT: "a302772c8f3791c477edd1764b82f8d1f5b22fdf"
50+ PACKCHECK_GITHUB_COMMIT: "v0.7.0"
45
51
46 # Pull token from "secrets" setting of the github repo
52 # Pull token from "secrets" setting of the github repo
47 COVERALLS_TOKEN: ${{ secrets.COVERALLS_TOKEN }}
53 COVERALLS_TOKEN: ${{ secrets.COVERALLS_TOKEN }}
48 COVERAGE: ${{ matrix.coverage }}
54 COVERAGE: ${{ matrix.coverage }}
49− DOCTEST: ${{ matrix.doctest }}
55+
56+ # hlint
57+ HLINT_VERSION: 3.6.1
58+ HLINT_OPTIONS: "lint"
59+ HLINT_TARGETS: "core/src src test benchmark"
50
60
51 # Subdir
61 # Subdir
52 SUBDIR: ${{ matrix.subdir }}
62 SUBDIR: ${{ matrix.subdir }}
@@ -59,7 +69,7 @@ jobs:
59 # The order is important to optimize fail-fast.
69 # The order is important to optimize fail-fast.
60 name:
70 name:
61 - 9.8.1-Werror
71 - 9.8.1-Werror
62− - 9.2.8-docspec
72+ # - 9.8.1-docspec
63 # - 8.10.7-coverage
73 # - 8.10.7-coverage
64
74
65 # Note: if cabal.project is not specified benchmarks and tests won't
75 # Note: if cabal.project is not specified benchmarks and tests won't
@@ -68,9 +78,19 @@ jobs:
68 include:
78 include:
69 - name: head
79 - name: head
70 ghc_version: head
80 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"
71 runner: ubuntu-latest
91 runner: ubuntu-latest
72 build: cabal
92 build: cabal
73− cabal_version: 3.10.1.0
93+ cabal_version: 3.10.2.0
74 cabal_project: cabal.project.ghc-head
94 cabal_project: cabal.project.ghc-head
75 disable_sdist_build: "y"
95 disable_sdist_build: "y"
76 ignore_error: true
96 ignore_error: true
@@ -83,6 +103,18 @@ jobs:
83 disable_sdist_build: "y"
103 disable_sdist_build: "y"
84 cabal_project: cabal.project.Werror
104 cabal_project: cabal.project.Werror
85 ignore_error: false
105 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
86 - name: 9.8.1-fusion-inspection
118 - name: 9.8.1-fusion-inspection
87 ghc_version: 9.8.1
119 ghc_version: 9.8.1
88 runner: ubuntu-latest
120 runner: ubuntu-latest
@@ -116,14 +148,6 @@ jobs:
116 cabal_version: 3.6.2.0
148 cabal_version: 3.6.2.0
117 disable_sdist_build: "y"
149 disable_sdist_build: "y"
118 ignore_error: false
150 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
127 - name: 9.0.2-streamly-sdist
151 - name: 9.0.2-streamly-sdist
128 ghc_version: 9.0.2
152 ghc_version: 9.0.2
129 runner: ubuntu-latest
153 runner: ubuntu-latest
@@ -170,66 +194,44 @@ jobs:
170 cabal_build_options: "--flag debug --flag -opt"
194 cabal_build_options: "--flag debug --flag -opt"
171 disable_sdist_build: "y"
195 disable_sdist_build: "y"
172 ignore_error: false
196 ignore_error: false
197+ - name: hlint
198+ build: hlint
199+ runner: ubuntu-latest
200+ ignore_error: true
173
201
174 steps:
202 steps:
175 - uses: actions/checkout@v2
203 - uses: actions/checkout@v2
176
204
177 - uses: actions/cache@v1
205 - uses: actions/cache@v1
178− name: Cache ~/.cabal
206+ name: Cache common directories
179 with:
207 with:
208+ path: |
209+ ~/.local
180− path: ~/.cabal
210+ ~/.cabal
211+ ~/.stack
212+ ~/.ghcup
181 # Bump the key version to clear the cache
213 # Bump the key version to clear the cache
182 key: ${{ runner.os }}-${{ matrix.ghc_version }}-cabal-v2
214 key: ${{ runner.os }}-${{ matrix.ghc_version }}-cabal-v2
183
215
184 - name: Download packcheck
216 - name: Download packcheck
185 run: |
217 run: |
186− # Get packcheck if needed
218+ if test ! -e "$PACKCHECK"
187− CURL=$(which curl)
188− PACKCHECK_URL=${PACKCHECK_GITHUB_URL}/${PACKCHECK_GITHUB_COMMIT}/packcheck.sh
189− if test ! -e "$PACKCHECK_LOCAL_PATH"; then $CURL -sL -o "$PACKCHECK_LOCAL_PATH" $PACKCHECK_URL; fi;
190− chmod +x $PACKCHECK_LOCAL_PATH
191−
192− os=$(uname -s -m)
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
202− chmod +x ./ghcup
203− if test "$GHCVER" = "head"
204 then
219 then
205− # The URL may change, to find a working URL go to https://gitlab.haskell.org/ghc/ghc/-/jobs/
220+ if test -z "$PACKCHECK_GITHUB_COMMIT"
206− # Find a debian10 job, click on a passed/failed job, at the
221+ then
207− # end of the output you will find the tar.xz name, put that after
222+ die "PACKCHECK_GITHUB_COMMIT is not specified."
208− # "raw/", and put the job name after "job=".
223+ fi
209− # Also see https://github.com/mpickering/ghc-artefact-nix/blob/master/gitlab-artifact.nix
224+ PACKCHECK_URL=${PACKCHECK_GITHUB_URL}/${PACKCHECK_GITHUB_COMMIT}/packcheck.sh
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
225+ curl --fail -sL -o "$PACKCHECK" $PACKCHECK_URL || exit 1
211− else
226+ chmod +x $PACKCHECK
212− ./ghcup install ghc $GHCVER
227+ elif test ! -x "$PACKCHECK"
228+ then
229+ chmod +x $PACKCHECK
213 fi
230 fi
214− ./ghcup install cabal $CABALVER
215− ./ghcup set ghc $GHCVER
216− ./ghcup set cabal $CABALVER
217
231
218 - name: Run tests
232 - name: Run tests
219 run: |
233 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
232− elif test -n "$COVERAGE"
234+ if test -n "$COVERAGE"
233 then
235 then
234 # Run tests with coverage
236 # Run tests with coverage
235 cabal update
237 cabal update
@@ -242,16 +244,15 @@ jobs:
242 # XXX Uncomment this and fix it properly later
244 # XXX Uncomment this and fix it properly later
243 # hpc-coveralls --repo-token="$COVERALLS_TOKEN" --coverage-mode=StrictlyFullLines
245 # hpc-coveralls --repo-token="$COVERALLS_TOKEN" --coverage-mode=StrictlyFullLines
244 else
246 else
245− export TOOLS_DIR=/opt
246 # /usr/local/opt/curl/bin for macOS
247 # /usr/local/opt/curl/bin for macOS
247 export PATH=$HOME/.local/bin:$HOME/.ghcup/bin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/local/opt/curl/bin
248 export PATH=$HOME/.local/bin:$HOME/.ghcup/bin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/local/opt/curl/bin
248 if test -n "$SUBDIR"
249 if test -n "$SUBDIR"
249 then
250 then
250− mv "$PACKCHECK_LOCAL_PATH" "$SUBDIR/packcheck.sh"
251+ mv "$PACKCHECK" "$SUBDIR/packcheck.sh"
251 # This is required as dist-newstyle will be created on the top level
252 # This is required as dist-newstyle will be created on the top level
252 # directory as it is considered the working dir.
253 # directory as it is considered the working dir.
253 rm cabal.project
254 rm cabal.project
254 cd "$SUBDIR"
255 cd "$SUBDIR"
255 fi
256 fi
256− bash -c "$PACKCHECK_LOCAL_PATH $BUILD"
257+ bash -c "$PACKCHECK $BUILD"
257 fi
258 fi
.hlint.ignore+5
@@ -2,6 +2,9 @@ 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
5test/Streamly/Test/Data/Array.hs
8test/Streamly/Test/Data/Array.hs
6test/Streamly/Test/Data/Parser.hs
9test/Streamly/Test/Data/Parser.hs
7test/Streamly/Test/Data/ParserK.hs
10test/Streamly/Test/Data/ParserK.hs
@@ -10,6 +13,7 @@ test/Streamly/Test/Data/Unfold.hs
10test/Streamly/Test/FileSystem/Event.hs
13test/Streamly/Test/FileSystem/Event.hs
11test/Streamly/Test/Prelude/Concurrent.hs
14test/Streamly/Test/Prelude/Concurrent.hs
12test/Streamly/Test/Data/Stream/Concurrent.hs
15test/Streamly/Test/Data/Stream/Concurrent.hs
16+test/lib/Streamly/Test/Prelude/Common.hs
13test/Streamly/Test/Prelude/Fold.hs
17test/Streamly/Test/Prelude/Fold.hs
14test/Streamly/Test/Prelude/Rate.hs
18test/Streamly/Test/Prelude/Rate.hs
15test/Streamly/Test/Prelude/Serial.hs
19test/Streamly/Test/Prelude/Serial.hs
@@ -27,6 +31,7 @@ benchmark/Streamly/Benchmark/Data/Stream/StreamK.hs
27benchmark/Streamly/Benchmark/Data/Unfold.hs
31benchmark/Streamly/Benchmark/Data/Unfold.hs
28benchmark/Streamly/Benchmark/FileSystem/Handle.hs
32benchmark/Streamly/Benchmark/FileSystem/Handle.hs
29benchmark/Streamly/Benchmark/Prelude/Async.hs
33benchmark/Streamly/Benchmark/Prelude/Async.hs
34+benchmark/Streamly/Benchmark/Prelude/Concurrent.hs
30benchmark/Streamly/Benchmark/Prelude/Merge.hs
35benchmark/Streamly/Benchmark/Prelude/Merge.hs
31benchmark/Streamly/Benchmark/Prelude/Parallel.hs
36benchmark/Streamly/Benchmark/Prelude/Parallel.hs
32benchmark/Streamly/Benchmark/Prelude/Rate.hs
37benchmark/Streamly/Benchmark/Prelude/Rate.hs
benchmark/Streamly/Benchmark/Data/Array/Stream.hs+1−1
@@ -53,7 +53,7 @@ import Streamly.Benchmark.Common.Handle
53import Control.Monad.IO.Class (MonadIO)
53import Control.Monad.IO.Class (MonadIO)
54
54
55#ifdef INSPECTION
55#ifdef INSPECTION
56−import Streamly.Internal.Data.Serialize (Unbox)
56+import Streamly.Internal.Data.MutByteArray (Unbox)
57import Streamly.Internal.Data.Stream (Step(..))
57import Streamly.Internal.Data.Stream (Step(..))
58import Test.Inspection
58import Test.Inspection
59#endif
59#endif
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+22−26
@@ -1,4 +1,5 @@
1{-# LANGUAGE TemplateHaskell #-}
1{-# LANGUAGE TemplateHaskell #-}
2+{- HLINT ignore -}
2
3
3#undef FUSION_CHECK
4#undef FUSION_CHECK
4#ifdef FUSION_CHECK
5#ifdef FUSION_CHECK
@@ -18,13 +19,15 @@ module Main (main) where
18-------------------------------------------------------------------------------
19-------------------------------------------------------------------------------
19
20
20import Control.DeepSeq (NFData(..), deepseq)
21import Control.DeepSeq (NFData(..), deepseq)
22+import Control.Monad (when, void)
21import GHC.Generics (Generic)
23import GHC.Generics (Generic)
22import System.Random (randomRIO)
24import System.Random (randomRIO)
23#ifndef USE_UNBOX
25#ifndef USE_UNBOX
24import Test.QuickCheck (Arbitrary, arbitrary)
26import Test.QuickCheck (Arbitrary, arbitrary)
25#endif
27#endif
26
28
27−import Streamly.Internal.Data.Serialize hiding (encode)
29+import Streamly.Internal.Data.MutByteArray
30+import qualified Streamly.Internal.Data.MutByteArray as MBA
28#ifdef USE_UNBOX
31#ifdef USE_UNBOX
29import Data.Proxy (Proxy(..))
32import Data.Proxy (Proxy(..))
30#else
33#else
@@ -43,13 +46,13 @@ import Streamly.Benchmark.Data.Serialize.RecNonCompatible
43#ifdef USE_UNBOX
46#ifdef USE_UNBOX
44#define SERIALIZE_CLASS Unbox
47#define SERIALIZE_CLASS Unbox
45#define DERIVE_CLASS(typ) $(deriveUnbox [d|instance Unbox typ|])
48#define DERIVE_CLASS(typ) $(deriveUnbox [d|instance Unbox typ|])
46−#define SERIALIZE_OP pokeByteIndex
49+#define SERIALIZE_OP pokeAt
47−#define DESERIALIZE_OP peekByteIndex
50+#define DESERIALIZE_OP peekAt
48#else
51#else
49#define SERIALIZE_CLASS Serialize
52#define SERIALIZE_CLASS Serialize
50#define DERIVE_CLASS(typ) $(deriveSerialize [d|instance Serialize typ|])
53#define DERIVE_CLASS(typ) $(deriveSerialize [d|instance Serialize typ|])
51−#define SERIALIZE_OP serialize
54+#define SERIALIZE_OP serializeAt
52−#define DESERIALIZE_OP deserialize
55+#define DESERIALIZE_OP deserializeAt
53#endif
56#endif
54
57
55-------------------------------------------------------------------------------
58-------------------------------------------------------------------------------
@@ -263,13 +266,14 @@ $(deriveSerialize [d|instance Serialize a => Serialize (BinTree a)|])
263instance NFData a => NFData (BinTree a) where
266instance NFData a => NFData (BinTree a) where
264 {-# INLINE rnf #-}
267 {-# INLINE rnf #-}
265 rnf (Leaf a) = rnf a `seq` ()
268 rnf (Leaf a) = rnf a `seq` ()
266− rnf (Tree l r) = rnf l `seq` rnf r `seq` ()
269+ rnf (Tree l r) = rnf l `seq` rnf r
267
270
271+-- XXX This may not terminate, or could become really large.
268instance Arbitrary a => Arbitrary (BinTree a) where
272instance Arbitrary a => Arbitrary (BinTree a) where
269 arbitrary = oneof [Leaf <$> arbitrary, Tree <$> arbitrary <*> arbitrary]
273 arbitrary = oneof [Leaf <$> arbitrary, Tree <$> arbitrary <*> arbitrary]
270
274
271mkBinTree :: (Arbitrary a) => Int -> IO (BinTree a)
275mkBinTree :: (Arbitrary a) => Int -> IO (BinTree a)
272−mkBinTree = go (generate $ arbitrary)
276+mkBinTree = go (generate arbitrary)
273
277
274 where
278 where
275
279
@@ -287,23 +291,21 @@ getSize :: forall a. SERIALIZE_CLASS a => a -> Int
287#ifdef USE_UNBOX
291#ifdef USE_UNBOX
288getSize _ = sizeOf (Proxy :: Proxy a)
292getSize _ = sizeOf (Proxy :: Proxy a)
289#else
293#else
290−getSize = size 0
294+getSize = addSizeTo 0
291#endif
295#endif
292
296
293-------------------------------------------------------------------------------
297-------------------------------------------------------------------------------
294-- Common helpers
298-- Common helpers
295-------------------------------------------------------------------------------
299-------------------------------------------------------------------------------
296
300
301+{- HLINT ignore "Eta reduce" -}
297-- Parts of "f" that are dependent on val will not be optimized out.
302-- Parts of "f" that are dependent on val will not be optimized out.
298{-# INLINE loop #-}
303{-# INLINE loop #-}
299loop :: Int -> (a -> IO b) -> a -> IO ()
304loop :: Int -> (a -> IO b) -> a -> IO ()
300loop count f val = go count val
305loop count f val = go count val
301 where
306 where
302
307
303− go n x = do
304− if n > 0
305− then f x >> go (n-1) x
308+ go n x = when (n > 0) $ f x >> go (n-1) x
306− else return ()
307
309
308-- The first arg of "f" is the environment which is not threaded around in the
310-- The first arg of "f" is the environment which is not threaded around in the
309-- loop.
311-- loop.
@@ -312,10 +314,7 @@ loopWith :: Int -> (e -> a -> IO b) -> e -> a -> IO ()
312loopWith count f e val = go count val
314loopWith count f e val = go count val
313 where
315 where
314
316
315− go n x = do
316− if n > 0
317− then f e x >> go (n-1) x
317+ go n x = when (n > 0) $ f e x >> go (n-1) x
318− else return ()
319
318
320benchSink :: NFData b => String -> Int -> (Int -> IO b) -> Benchmark
319benchSink :: NFData b => String -> Int -> (Int -> IO b) -> Benchmark
321benchSink name times f = bench name (nfIO (randomRIO (times, times) >>= f))
320benchSink name times f = bench name (nfIO (randomRIO (times, times) >>= f))
@@ -335,27 +334,27 @@ pokeWithSize arr val = do
335pokeTimesWithSize :: SERIALIZE_CLASS a => a -> Int -> IO ()
334pokeTimesWithSize :: SERIALIZE_CLASS a => a -> Int -> IO ()
336pokeTimesWithSize val times = do
335pokeTimesWithSize val times = do
337 let n = getSize val
336 let n = getSize val
338− arr <- newByteArray n
337+ arr <- MBA.new n
339 loopWith times pokeWithSize arr val
338 loopWith times pokeWithSize arr val
340-}
339-}
341
340
342{-# INLINE poke #-}
341{-# INLINE poke #-}
343poke :: SERIALIZE_CLASS a => MutByteArray -> a -> IO ()
342poke :: SERIALIZE_CLASS a => MutByteArray -> a -> IO ()
344−poke arr val = SERIALIZE_OP 0 arr val >> return ()
343+poke arr val = void (SERIALIZE_OP 0 arr val)
345
344
346{-# INLINE pokeTimes #-}
345{-# INLINE pokeTimes #-}
347pokeTimes :: SERIALIZE_CLASS a => a -> Int -> IO ()
346pokeTimes :: SERIALIZE_CLASS a => a -> Int -> IO ()
348pokeTimes val times = do
347pokeTimes val times = do
349 let n = getSize val
348 let n = getSize val
350− arr <- newByteArray n
349+ arr <- MBA.new n
351 loopWith times poke arr val
350 loopWith times poke arr val
352
351
353{-# INLINE encode #-}
352{-# INLINE encode #-}
354encode :: SERIALIZE_CLASS a => a -> IO ()
353encode :: SERIALIZE_CLASS a => a -> IO ()
355encode val = do
354encode val = do
356 let n = getSize val
355 let n = getSize val
357− arr <- newByteArray n
356+ arr <- MBA.new n
358− SERIALIZE_OP 0 arr val >> return ()
357+ void (SERIALIZE_OP 0 arr val)
359
358
360{-# INLINE encodeTimes #-}
359{-# INLINE encodeTimes #-}
361encodeTimes :: SERIALIZE_CLASS a => a -> Int -> IO ()
360encodeTimes :: SERIALIZE_CLASS a => a -> Int -> IO ()
@@ -390,7 +389,7 @@ peek (_val, n) arr = do
390{-# INLINE peekTimes #-}
389{-# INLINE peekTimes #-}
391peekTimes :: (NFData a, SERIALIZE_CLASS a) => Int -> a -> Int -> IO ()
390peekTimes :: (NFData a, SERIALIZE_CLASS a) => Int -> a -> Int -> IO ()
392peekTimes n val times = do
391peekTimes n val times = do
393− arr <- newByteArray n
392+ arr <- MBA.new n
394 _ <- SERIALIZE_OP 0 arr val
393 _ <- SERIALIZE_OP 0 arr val
395 loopWith times peek (val, n) arr
394 loopWith times peek (val, n) arr
396
395
@@ -398,7 +397,7 @@ peekTimes n val times = do
398trip :: forall a. (NFData a, SERIALIZE_CLASS a) => a -> IO ()
397trip :: forall a. (NFData a, SERIALIZE_CLASS a) => a -> IO ()
399trip val = do
398trip val = do
400 let n = getSize val
399 let n = getSize val
401− arr <- newByteArray n
400+ arr <- MBA.new n
402 _ <- SERIALIZE_OP 0 arr val
401 _ <- SERIALIZE_OP 0 arr val
403#ifdef USE_UNBOX
402#ifdef USE_UNBOX
404 (val1 :: a) <- DESERIALIZE_OP 0 arr
403 (val1 :: a) <- DESERIALIZE_OP 0 arr
@@ -561,8 +560,6 @@ main = do
561 let !len = length lInt -- evaluate the list
560 let !len = length lInt -- evaluate the list
562#endif
561#endif
563#ifndef FUSION_CHECK
562#ifndef FUSION_CHECK
564− -- This can take too much memory/CPU, need to restrict the test
565− -- runQC
566#ifdef USE_UNBOX
563#ifdef USE_UNBOX
567 runWithCLIOpts defaultStreamSize allBenchmarks
564 runWithCLIOpts defaultStreamSize allBenchmarks
568#else
565#else
@@ -570,7 +567,6 @@ main = do
570 `seq` runWithCLIOpts
567 `seq` runWithCLIOpts
571 defaultStreamSize
568 defaultStreamSize
572 (allBenchmarks tInt lInt recL recR)
569 (allBenchmarks tInt lInt recL recR)
573−
574#endif
570#endif
575#else
571#else
576 -- Enable FUSION_CHECK macro at the beginning of the file
572 -- Enable FUSION_CHECK macro at the beginning of the file
benchmark/Streamly/Benchmark/Data/Serialize/RecCompatible.hs+3−3
@@ -9,10 +9,10 @@ module Streamly.Benchmark.Data.Serialize.RecCompatible
9-- Imports
9-- Imports
10--------------------------------------------------------------------------------
10--------------------------------------------------------------------------------
11
11
12−import Streamly.Internal.Data.Serialize (Serialize)
12+import Streamly.Internal.Data.MutByteArray (Serialize)
13import Streamly.Benchmark.Data.Serialize.TH (genLargeRecord)
13import Streamly.Benchmark.Data.Serialize.TH (genLargeRecord)
14
14
15−import qualified Streamly.Internal.Data.Serialize as Serialize
15+import qualified Streamly.Internal.Data.MutByteArray as Serialize
16
16
17--------------------------------------------------------------------------------
17--------------------------------------------------------------------------------
18-- Code
18-- Code
@@ -20,5 +20,5 @@ import qualified Streamly.Internal.Data.Serialize as Serialize
20
20
21$(genLargeRecord "RecCompatible" 50)
21$(genLargeRecord "RecCompatible" 50)
22$(Serialize.deriveSerializeWith
22$(Serialize.deriveSerializeWith
23− (Serialize.encodeRecordFields True Serialize.serializeConfig)
23+ (Serialize.encodeRecordFields True)
24 [d|instance Serialize RecCompatible|])
24 [d|instance Serialize RecCompatible|])
benchmark/Streamly/Benchmark/Data/Serialize/RecNonCompatible.hs+1−1
@@ -11,7 +11,7 @@ module Streamly.Benchmark.Data.Serialize.RecNonCompatible
11
11
12import Streamly.Benchmark.Data.Serialize.TH (genLargeRecord)
12import Streamly.Benchmark.Data.Serialize.TH (genLargeRecord)
13
13
14−import qualified Streamly.Internal.Data.Serialize as Serialize
14+import qualified Streamly.Internal.Data.MutByteArray as Serialize
15
15
16--------------------------------------------------------------------------------
16--------------------------------------------------------------------------------
17-- Code
17-- Code
benchmark/Streamly/Benchmark/Data/Serialize/TH.hs+4−4
@@ -8,7 +8,7 @@ module Streamly.Benchmark.Data.Serialize.TH (genLargeRecord) where
8
8
9import Language.Haskell.TH
9import Language.Haskell.TH
10
10
11−import Streamly.Internal.Data.Serialize (makeI)
11+import Streamly.Internal.Data.MutByteArray (makeI)
12import Control.DeepSeq (NFData(..))
12import Control.DeepSeq (NFData(..))
13
13
14--------------------------------------------------------------------------------
14--------------------------------------------------------------------------------
@@ -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− (const (conE '()) <$> [0 .. (numFields - 1)])))
67+ (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/ConcurrentCommon.hs+2−2
@@ -14,12 +14,12 @@ where
14import Stream.Common
14import Stream.Common
15 (composeN, benchIO, benchIOSink, benchIOSrc, sourceUnfoldrM)
15 (composeN, benchIO, benchIOSink, benchIOSrc, sourceUnfoldrM)
16import Streamly.Data.Stream (Stream)
16import Streamly.Data.Stream (Stream)
17−import Streamly.Internal.Data.Stream.Concurrent (MonadAsync, Config)
17+import Streamly.Internal.Data.Stream.Prelude (MonadAsync, Config)
18
18
19import qualified Data.List as List
19import qualified Data.List as List
20import qualified Streamly.Data.Fold as Fold
20import qualified Streamly.Data.Fold as Fold
21import qualified Streamly.Data.Stream as Stream
21import qualified Streamly.Data.Stream as Stream
22−import qualified Streamly.Internal.Data.Stream.Concurrent as Async
22+import qualified Streamly.Internal.Data.Stream.Prelude as Async
23
23
24import Test.Tasty.Bench
24import Test.Tasty.Bench
25import Prelude hiding (mapM)
25import Prelude hiding (mapM)
benchmark/Streamly/Benchmark/Data/Stream/ConcurrentEager.hs+1−1
@@ -9,7 +9,7 @@
9import Stream.ConcurrentCommon (allBenchmarks)
9import Stream.ConcurrentCommon (allBenchmarks)
10import Streamly.Benchmark.Common (runWithCLIOpts, defaultStreamSize)
10import Streamly.Benchmark.Common (runWithCLIOpts, defaultStreamSize)
11
11
12−import qualified Streamly.Internal.Data.Stream.Concurrent as Async
12+import qualified Streamly.Internal.Data.Stream.Prelude as Async
13
13
14moduleName :: String
14moduleName :: String
15moduleName = "Data.Stream.ConcurrentEager"
15moduleName = "Data.Stream.ConcurrentEager"
benchmark/Streamly/Benchmark/Data/Stream/ConcurrentInterleaved.hs+1−1
@@ -9,7 +9,7 @@
9import Stream.ConcurrentCommon (allBenchmarks)
9import Stream.ConcurrentCommon (allBenchmarks)
10import Streamly.Benchmark.Common (runWithCLIOpts, defaultStreamSize)
10import Streamly.Benchmark.Common (runWithCLIOpts, defaultStreamSize)
11
11
12−import qualified Streamly.Internal.Data.Stream.Concurrent as Async
12+import qualified Streamly.Internal.Data.Stream.Prelude as Async
13
13
14moduleName :: String
14moduleName :: String
15moduleName = "Data.Stream.ConcurrentInterleaved"
15moduleName = "Data.Stream.ConcurrentInterleaved"
benchmark/Streamly/Benchmark/Data/Stream/ConcurrentOrdered.hs+1−1
@@ -9,7 +9,7 @@
9import Stream.ConcurrentCommon (allBenchmarks)
9import Stream.ConcurrentCommon (allBenchmarks)
10import Streamly.Benchmark.Common (runWithCLIOpts, defaultStreamSize)
10import Streamly.Benchmark.Common (runWithCLIOpts, defaultStreamSize)
11
11
12−import qualified Streamly.Internal.Data.Stream.Concurrent as Async
12+import qualified Streamly.Internal.Data.Stream.Prelude as Async
13
13
14moduleName :: String
14moduleName :: String
15moduleName = "Data.Stream.ConcurrentOrdered"
15moduleName = "Data.Stream.ConcurrentOrdered"
benchmark/Streamly/Benchmark/Data/Stream/Eliminate.hs+2
@@ -132,12 +132,14 @@ 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"-}
135{-# INLINE foldableAnd #-}
136{-# INLINE foldableAnd #-}
136foldableAnd :: Int -> Int -> Bool
137foldableAnd :: Int -> Int -> Bool
137foldableAnd value n =
138foldableAnd value n =
138 Prelude.and $ fmap
139 Prelude.and $ fmap
139 (<= (value + 1)) (sourceUnfoldr value n :: Stream Identity Int)
140 (<= (value + 1)) (sourceUnfoldr value n :: Stream Identity Int)
140
141
142+{- HLINT ignore "Use any"-}
141{-# INLINE foldableOr #-}
143{-# INLINE foldableOr #-}
142foldableOr :: Int -> Int -> Bool
144foldableOr :: Int -> Int -> Bool
143foldableOr value n =
145foldableOr value n =
benchmark/Streamly/Benchmark/Data/Stream/Exceptions.hs+2−2
@@ -36,13 +36,13 @@ import System.IO (Handle, hClose, hPutChar)
36import qualified Streamly.FileSystem.Handle as FH
36import qualified Streamly.FileSystem.Handle as FH
37import qualified Streamly.Internal.FileSystem.Handle as IFH
37import qualified Streamly.Internal.FileSystem.Handle as IFH
38import qualified Streamly.Internal.Data.Unfold as IUF
38import qualified Streamly.Internal.Data.Unfold as IUF
39−import qualified Streamly.Internal.Data.Unfold.Exception as IUF
39+import qualified Streamly.Internal.Data.Unfold.Prelude as IUF
40
40
41#ifdef USE_PRELUDE
41#ifdef USE_PRELUDE
42import qualified Streamly.Internal.Data.Stream.IsStream as Stream
42import qualified Streamly.Internal.Data.Stream.IsStream as Stream
43#else
43#else
44import qualified Streamly.Internal.Data.Stream as Stream
44import qualified Streamly.Internal.Data.Stream as Stream
45−import qualified Streamly.Internal.Data.Stream.Exception.Lifted as Stream
45+import qualified Streamly.Internal.Data.Stream.Prelude as Stream
46#endif
46#endif
47
47
48import Test.Tasty.Bench hiding (env)
48import Test.Tasty.Bench hiding (env)
benchmark/Streamly/Benchmark/Data/Stream/Reduce.hs+1−1
@@ -48,7 +48,7 @@ import Streamly.Internal.Data.Stream (Stream)
48import qualified Streamly.Internal.Data.Stream as S
48import qualified Streamly.Internal.Data.Stream as S
49#ifndef USE_STREAMLY_CORE
49#ifndef USE_STREAMLY_CORE
50import qualified Streamly.Data.Stream.Prelude as S
50import qualified Streamly.Data.Stream.Prelude as S
51−import qualified Streamly.Internal.Data.Stream.Time as S
51+import qualified Streamly.Internal.Data.Stream.Prelude as S
52#endif
52#endif
53
53
54#ifdef USE_STREAMK
54#ifdef USE_STREAMK
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 $ concat [o_1_space]
455+main = defaultMain [o_1_space]
benchmark/Streamly/Benchmark/Data/Stream/ToStreamK.hs+1−1
@@ -120,7 +120,7 @@ fromFoldable streamLen n = S.fromFoldable [n..n+streamLen]
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 S.nil (Prelude.fmap return [n..n+streamLen])
123+ Prelude.foldr (S.consM . return) S.nil [n .. n + streamLen]
124
124
125{-
125{-
126{-# INLINABLE concatMapFoldableWith #-}
126{-# INLINABLE concatMapFoldableWith #-}
benchmark/Streamly/Benchmark/Data/Stream/Transform.hs+1−1
@@ -46,7 +46,7 @@ import Streamly.Internal.Data.Time.Units
46import Streamly.Internal.Data.Stream (Stream)
46import Streamly.Internal.Data.Stream (Stream)
47import qualified Streamly.Internal.Data.Stream as Stream
47import qualified Streamly.Internal.Data.Stream as Stream
48#ifndef USE_STREAMLY_CORE
48#ifndef USE_STREAMLY_CORE
49−import qualified Streamly.Internal.Data.Stream.Time as Stream
49+import qualified Streamly.Internal.Data.Stream.Prelude as Stream
50#endif
50#endif
51#ifdef USE_STREAMK
51#ifdef USE_STREAMK
52import Control.DeepSeq (NFData(..))
52import Control.DeepSeq (NFData(..))
benchmark/Streamly/Benchmark/Data/Unfold.hs+1−1
@@ -31,7 +31,7 @@ import qualified Prelude
31import qualified Streamly.FileSystem.Handle as FH
31import qualified Streamly.FileSystem.Handle as FH
32import qualified Streamly.Internal.Data.Fold as FL
32import qualified Streamly.Internal.Data.Fold as FL
33import qualified Streamly.Internal.Data.Unfold as UF
33import qualified Streamly.Internal.Data.Unfold as UF
34−import qualified Streamly.Internal.Data.Unfold.Exception as UF
34+import qualified Streamly.Internal.Data.Unfold.Prelude as UF
35import qualified Streamly.Internal.Data.Stream as S
35import qualified Streamly.Internal.Data.Stream as S
36import qualified Streamly.Internal.Data.Stream as D
36import qualified Streamly.Internal.Data.Stream as D
37import qualified Streamly.Internal.Data.StreamK as K
37import qualified Streamly.Internal.Data.StreamK as K
benchmark/Streamly/Benchmark/FileSystem/Handle/Read.hs+7−8
@@ -33,7 +33,6 @@ 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
37import qualified Streamly.Internal.Data.Fold as FL
36import qualified Streamly.Internal.Data.Fold as FL
38import qualified Streamly.Internal.Data.Stream as IP
37import qualified Streamly.Internal.Data.Stream as IP
39import qualified Streamly.Internal.FileSystem.Handle as IFH
38import qualified Streamly.Internal.FileSystem.Handle as IFH
@@ -48,7 +47,7 @@ import Streamly.Benchmark.Common.Handle
48#ifdef INSPECTION
47#ifdef INSPECTION
49import Streamly.Internal.Data.Stream (Step(..), FoldMany)
48import Streamly.Internal.Data.Stream (Step(..), FoldMany)
50
49
51−import qualified Streamly.Internal.Data.MutArray as MA
50+import qualified Streamly.Internal.Data.MutArray as MutArray
52import qualified Streamly.Internal.Data.Stream as D
51import qualified Streamly.Internal.Data.Stream as D
53import qualified Streamly.Internal.Data.Unfold as IUF
52import qualified Streamly.Internal.Data.Unfold as IUF
54
53
@@ -69,7 +68,7 @@ readLast = S.fold Fold.last . S.unfold FH.reader
69inspect $ hasNoTypeClasses 'readLast
68inspect $ hasNoTypeClasses 'readLast
70inspect $ 'readLast `hasNoType` ''Step -- S.unfold
69inspect $ 'readLast `hasNoType` ''Step -- S.unfold
71inspect $ 'readLast `hasNoType` ''IUF.ConcatState -- FH.read/UF.many
70inspect $ 'readLast `hasNoType` ''IUF.ConcatState -- FH.read/UF.many
72−inspect $ 'readLast `hasNoType` ''MA.ArrayUnsafe -- FH.read/A.read
71+inspect $ 'readLast `hasNoType` ''MutArray.ArrayUnsafe -- FH.read/A.read
73#endif
72#endif
74
73
75-- assert that flattenArrays constructors are not present
74-- assert that flattenArrays constructors are not present
@@ -81,7 +80,7 @@ readCountBytes = S.fold Fold.length . S.unfold FH.reader
81inspect $ hasNoTypeClasses 'readCountBytes
80inspect $ hasNoTypeClasses 'readCountBytes
82inspect $ 'readCountBytes `hasNoType` ''Step -- S.unfold
81inspect $ 'readCountBytes `hasNoType` ''Step -- S.unfold
83inspect $ 'readCountBytes `hasNoType` ''IUF.ConcatState -- FH.read/UF.many
82inspect $ 'readCountBytes `hasNoType` ''IUF.ConcatState -- FH.read/UF.many
84−inspect $ 'readCountBytes `hasNoType` ''MA.ArrayUnsafe -- FH.read/A.read
83+inspect $ 'readCountBytes `hasNoType` ''MutArray.ArrayUnsafe -- FH.read/A.read
85#endif
84#endif
86
85
87-- | Count the number of lines in a file.
86-- | Count the number of lines in a file.
@@ -96,7 +95,7 @@ readCountLines =
96inspect $ hasNoTypeClasses 'readCountLines
95inspect $ hasNoTypeClasses 'readCountLines
97inspect $ 'readCountLines `hasNoType` ''Step
96inspect $ 'readCountLines `hasNoType` ''Step
98inspect $ 'readCountLines `hasNoType` ''IUF.ConcatState -- FH.read/UF.many
97inspect $ 'readCountLines `hasNoType` ''IUF.ConcatState -- FH.read/UF.many
99−inspect $ 'readCountLines `hasNoType` ''MA.ArrayUnsafe -- FH.read/A.read
98+inspect $ 'readCountLines `hasNoType` ''MutArray.ArrayUnsafe -- FH.read/A.read
100#endif
99#endif
101
100
102-- | Count the number of words in a file.
101-- | Count the number of words in a file.
@@ -120,7 +119,7 @@ readSumBytes = S.fold Fold.sum . S.unfold FH.reader
120inspect $ hasNoTypeClasses 'readSumBytes
119inspect $ hasNoTypeClasses 'readSumBytes
121inspect $ 'readSumBytes `hasNoType` ''Step
120inspect $ 'readSumBytes `hasNoType` ''Step
122inspect $ 'readSumBytes `hasNoType` ''IUF.ConcatState -- FH.read/UF.many
121inspect $ 'readSumBytes `hasNoType` ''IUF.ConcatState -- FH.read/UF.many
123−inspect $ 'readSumBytes `hasNoType` ''MA.ArrayUnsafe -- FH.read/A.read
122+inspect $ 'readSumBytes `hasNoType` ''MutArray.ArrayUnsafe -- FH.read/A.read
124#endif
123#endif
125
124
126-- XXX When we mark this with INLINE and we have two benchmarks using S.drain
125-- XXX When we mark this with INLINE and we have two benchmarks using S.drain
@@ -236,13 +235,13 @@ groupsOf :: Int -> Handle -> IO Int
236groupsOf n inh =
235groupsOf n inh =
237 -- writeNUnsafe gives 2.5x boost here over writeN.
236 -- writeNUnsafe gives 2.5x boost here over writeN.
238 S.fold Fold.length
237 S.fold Fold.length
239− $ IP.groupsOf n (AT.writeNUnsafe n) (S.unfold FH.reader inh)
238+ $ IP.groupsOf n (A.writeNUnsafe n) (S.unfold FH.reader inh)
240
239
241#ifdef INSPECTION
240#ifdef INSPECTION
242inspect $ hasNoTypeClasses 'groupsOf
241inspect $ hasNoTypeClasses 'groupsOf
243inspect $ 'groupsOf `hasNoType` ''Step
242inspect $ 'groupsOf `hasNoType` ''Step
244inspect $ 'groupsOf `hasNoType` ''FoldMany
243inspect $ 'groupsOf `hasNoType` ''FoldMany
245−inspect $ 'groupsOf `hasNoType` ''AT.ArrayUnsafe -- AT.writeNUnsafe
244+inspect $ 'groupsOf `hasNoType` ''MutArray.ArrayUnsafe -- AT.writeNUnsafe
246 -- FH.read/A.read
245 -- FH.read/A.read
247inspect $ 'groupsOf `hasNoType` ''IUF.ConcatState -- FH.read/UF.many
246inspect $ 'groupsOf `hasNoType` ''IUF.ConcatState -- FH.read/UF.many
248#endif
247#endif
benchmark/Streamly/Benchmark/FileSystem/Handle/ReadWrite.hs+8−9
@@ -42,8 +42,7 @@ 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.Stream as MAS
45+import qualified Streamly.Internal.Data.MutArray as MutArray
46−import qualified Streamly.Internal.Data.Array as AT
47
46
48import Test.Inspection
47import Test.Inspection
49#endif
48#endif
@@ -83,7 +82,7 @@ copyStream inh outh = S.fold (FH.write outh) (S.unfold FH.reader inh)
83inspect $ hasNoTypeClasses 'copyStream
82inspect $ hasNoTypeClasses 'copyStream
84inspect $ 'copyStream `hasNoType` ''Step -- S.unfold
83inspect $ 'copyStream `hasNoType` ''Step -- S.unfold
85inspect $ 'copyStream `hasNoType` ''IUF.ConcatState -- FH.read/UF.many
84inspect $ 'copyStream `hasNoType` ''IUF.ConcatState -- FH.read/UF.many
86−inspect $ 'copyStream `hasNoType` ''AT.ArrayUnsafe -- FH.write/writeNUnsafe
85+inspect $ 'copyStream `hasNoType` ''MutArray.ArrayUnsafe -- FH.write/writeNUnsafe
87 -- FH.read/A.read
86 -- FH.read/A.read
88inspect $ 'copyStream `hasNoType` ''Strict.Tuple3' -- FH.write/chunksOf
87inspect $ 'copyStream `hasNoType` ''Strict.Tuple3' -- FH.write/chunksOf
89#endif
88#endif
@@ -109,8 +108,8 @@ readFromBytesNull inh devNull = IFH.putBytes devNull $ S.unfold FH.reader inh
109#ifdef INSPECTION
108#ifdef INSPECTION
110inspect $ hasNoTypeClasses 'readFromBytesNull
109inspect $ hasNoTypeClasses 'readFromBytesNull
111inspect $ 'readFromBytesNull `hasNoType` ''Step
110inspect $ 'readFromBytesNull `hasNoType` ''Step
112−inspect $ 'readFromBytesNull `hasNoType` ''MAS.SpliceState
111+inspect $ 'readFromBytesNull `hasNoType` ''MutArray.SpliceState
113−inspect $ 'readFromBytesNull `hasNoType` ''AT.ArrayUnsafe -- FH.fromBytes/S.chunksOf
112+inspect $ 'readFromBytesNull `hasNoType` ''MutArray.ArrayUnsafe -- FH.fromBytes/S.chunksOf
114inspect $ 'readFromBytesNull `hasNoType` ''D.FoldMany
113inspect $ 'readFromBytesNull `hasNoType` ''D.FoldMany
115#endif
114#endif
116
115
@@ -123,8 +122,8 @@ readWithFromBytesNull inh devNull =
123#ifdef INSPECTION
122#ifdef INSPECTION
124inspect $ hasNoTypeClasses 'readWithFromBytesNull
123inspect $ hasNoTypeClasses 'readWithFromBytesNull
125inspect $ 'readWithFromBytesNull `hasNoType` ''Step
124inspect $ 'readWithFromBytesNull `hasNoType` ''Step
126−inspect $ 'readWithFromBytesNull `hasNoType` ''MAS.SpliceState
125+inspect $ 'readWithFromBytesNull `hasNoType` ''MutArray.SpliceState
127−inspect $ 'readWithFromBytesNull `hasNoType` ''AT.ArrayUnsafe -- FH.fromBytes/S.chunksOf
126+inspect $ 'readWithFromBytesNull `hasNoType` ''MutArray.ArrayUnsafe -- FH.fromBytes/S.chunksOf
128inspect $ 'readWithFromBytesNull `hasNoType` ''D.FoldMany
127inspect $ 'readWithFromBytesNull `hasNoType` ''D.FoldMany
129#endif
128#endif
130
129
@@ -171,7 +170,7 @@ writeReadWith inh devNull = IUF.fold fld unf (defaultChunkSize, inh)
171inspect $ hasNoTypeClasses 'writeReadWith
170inspect $ hasNoTypeClasses 'writeReadWith
172inspect $ 'writeReadWith `hasNoType` ''Step
171inspect $ 'writeReadWith `hasNoType` ''Step
173inspect $ 'writeReadWith `hasNoType` ''IUF.ConcatState -- FH.read/UF.many
172inspect $ 'writeReadWith `hasNoType` ''IUF.ConcatState -- FH.read/UF.many
174−inspect $ 'writeReadWith `hasNoType` ''AT.ArrayUnsafe -- FH.write/writeNUnsafe
173+inspect $ 'writeReadWith `hasNoType` ''MutArray.ArrayUnsafe -- FH.write/writeNUnsafe
175 -- FH.read/A.read
174 -- FH.read/A.read
176#endif
175#endif
177
176
@@ -188,7 +187,7 @@ writeRead inh devNull = IUF.fold fld unf inh
188inspect $ hasNoTypeClasses 'writeRead
187inspect $ hasNoTypeClasses 'writeRead
189inspect $ 'writeRead `hasNoType` ''Step
188inspect $ 'writeRead `hasNoType` ''Step
190inspect $ 'writeRead `hasNoType` ''IUF.ConcatState -- FH.read/UF.many
189inspect $ 'writeRead `hasNoType` ''IUF.ConcatState -- FH.read/UF.many
191−inspect $ 'writeRead `hasNoType` ''AT.ArrayUnsafe -- FH.write/writeNUnsafe
190+inspect $ 'writeRead `hasNoType` ''MutArray.ArrayUnsafe -- FH.write/writeNUnsafe
192 -- FH.read/A.read
191 -- FH.read/A.read
193#endif
192#endif
194
193
benchmark/Streamly/Benchmark/Unicode/Stream.hs+5−4
@@ -43,11 +43,12 @@ import Streamly.Benchmark.Common
43import Streamly.Benchmark.Common.Handle
43import Streamly.Benchmark.Common.Handle
44
44
45#ifdef INSPECTION
45#ifdef INSPECTION
46−import Streamly.Internal.Data.Serialize (Unbox)
46+import 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
48import qualified Streamly.Internal.Data.Fold as Fold
50import qualified Streamly.Internal.Data.Fold as Fold
49import qualified Streamly.Internal.Data.Tuple.Strict as Strict
51import qualified Streamly.Internal.Data.Tuple.Strict as Strict
50−import qualified Streamly.Internal.Data.Array as Array
51
52
52import Test.Inspection
53import Test.Inspection
53#endif
54#endif
@@ -223,7 +224,7 @@ inspect $ 'copyStreamLatin1' `hasNoType` ''Step
223inspect $ 'copyStreamLatin1' `hasNoType` ''Unfold.ConcatState -- Handle.read/UF.many
224inspect $ 'copyStreamLatin1' `hasNoType` ''Unfold.ConcatState -- Handle.read/UF.many
224
225
225inspect $ 'copyStreamLatin1' `hasNoType` ''Fold.Step
226inspect $ 'copyStreamLatin1' `hasNoType` ''Fold.Step
226−inspect $ 'copyStreamLatin1' `hasNoType` ''Array.ArrayUnsafe -- Handle.write/writeNUnsafe
227+inspect $ 'copyStreamLatin1' `hasNoType` ''MutArray.ArrayUnsafe -- Handle.write/writeNUnsafe
227 -- Handle.read/Array.read
228 -- Handle.read/Array.read
228inspect $ 'copyStreamLatin1' `hasNoType` ''Strict.Tuple3' -- Handle.write/chunksOf
229inspect $ 'copyStreamLatin1' `hasNoType` ''Strict.Tuple3' -- Handle.write/chunksOf
229#endif
230#endif
@@ -244,7 +245,7 @@ inspect $ 'copyStreamLatin1 `hasNoType` ''Unfold.ConcatState -- Handle.read/UF.m
244
245
245inspect $ 'copyStreamLatin1 `hasNoType` ''Fold.ManyState
246inspect $ 'copyStreamLatin1 `hasNoType` ''Fold.ManyState
246inspect $ 'copyStreamLatin1 `hasNoType` ''Fold.Step
247inspect $ 'copyStreamLatin1 `hasNoType` ''Fold.Step
247−inspect $ 'copyStreamLatin1 `hasNoType` ''Array.ArrayUnsafe -- Handle.write/writeNUnsafe
248+inspect $ 'copyStreamLatin1 `hasNoType` ''MutArray.ArrayUnsafe -- Handle.write/writeNUnsafe
248 -- Handle.read/Array.read
249 -- Handle.read/Array.read
249inspect $ 'copyStreamLatin1 `hasNoType` ''Strict.Tuple3' -- Handle.write/chunksOf
250inspect $ 'copyStreamLatin1 `hasNoType` ''Strict.Tuple3' -- Handle.write/chunksOf
250#endif
251#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.5
191+ , filepath >= 1.4.1 && < 1.6
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.0.
3+# Generated by GNU Autoconf 2.71 for streamly 0.10.1.
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.0'
613+PACKAGE_VERSION='0.10.1'
614−PACKAGE_STRING='streamly 0.10.0'
614+PACKAGE_STRING='streamly 0.10.1'
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.0 to adapt to many kinds of systems.
1227+\`configure' configures streamly 0.10.1 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.0:";;
1289+ short | recursive ) echo "Configuration of streamly 0.10.1:";;
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.0
1375+streamly configure 0.10.1
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.0, which was
1503+It was created by streamly $as_me 0.10.1, 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.0, which was
3803+This file was extended by streamly $as_me 0.10.1, 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.0
3859+streamly config.status 0.10.1
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.0], [streamly@composewell.com], [streamly], [https://streamly.composewell.com])
6+AC_INIT([streamly], [0.10.1], [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.0.
3+# Generated by GNU Autoconf 2.71 for streamly-core 0.2.2.
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.0'
613+PACKAGE_VERSION='0.2.2'
614−PACKAGE_STRING='streamly-core 0.2.0'
614+PACKAGE_STRING='streamly-core 0.2.2'
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.0 to adapt to many kinds of systems.
1259+\`configure' configures streamly-core 0.2.2 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.0:";;
1321+ short | recursive ) echo "Configuration of streamly-core 0.2.2:";;
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.0
1407+streamly-core configure 0.2.2
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.0, which was
1625+It was created by streamly-core $as_me 0.2.2, 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.0, which was
3835+This file was extended by streamly-core $as_me 0.2.2, 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.0
3891+streamly-core config.status 0.2.2
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.0], [streamly@composewell.com], [streamly-core], [https://streamly.composewell.com])
6+AC_INIT([streamly-core], [0.2.2], [streamly@composewell.com], [streamly-core], [https://streamly.composewell.com])
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.1.0-0.2.0.txtnew file+1471
@@ -0,0 +1,1471 @@
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.Unicode.Parser
17+ [A] double :: Monad m => Parser Char m Double
18+[C] Streamly.FileSystem.Handle
19+ [A] readWith :: MonadIO m => Int -> Handle -> Stream m Word8
20+ [A] readChunksWith :: MonadIO m => Int -> Handle -> Stream m (Array Word8)
21+[C] Streamly.FileSystem.Dir
22+ [C] readEither
23+ [O] readEither :: MonadIO m => FilePath -> Stream m (Either FilePath FilePath)
24+ [N] readEither :: (MonadIO m, MonadCatch m) => FilePath -> Stream m (Either FilePath FilePath)
25+ [C] read
26+ [O] read :: MonadIO m => FilePath -> Stream m FilePath
27+ [N] read :: (MonadIO m, MonadCatch m) => FilePath -> Stream m FilePath
28+[C] Streamly.Data.Unfold
29+ [A] class Enum a => Enumerable a
30+ [A] second :: b -> Unfold m (a, b) c -> Unfold m a c
31+ [A] first :: a -> Unfold m (a, b) c -> Unfold m b c
32+ [A] enumerateFromTo :: (Enumerable a, Monad m) => Unfold m (a, a) a
33+ [A] enumerateFromThenTo :: (Enumerable a, Monad m) => Unfold m (a, a, a) a
34+ [A] enumerateFromThen :: (Enumerable a, Monad m) => Unfold m (a, a) a
35+ [A] enumerateFrom :: (Enumerable a, Monad m) => Unfold m a a
36+[C] Streamly.Data.StreamK
37+ [C] parseChunks
38+ [O] parseChunks :: (Monad m, Unbox a) => ParserK a m b -> StreamK m (Array a) -> m (Either ParseError b)
39+ [N] parseChunks :: (Monad m, Unbox a) => ParserK (Array a) m b -> StreamK m (Array a) -> m (Either ParseError b)
40+ [C] parseBreakChunks
41+ [O] parseBreakChunks :: (Monad m, Unbox a) => ParserK a m b -> StreamK m (Array a) -> m (Either ParseError b, StreamK m (Array a))
42+ [N] parseBreakChunks :: (Monad m, Unbox a) => ParserK (Array a) m b -> StreamK m (Array a) -> m (Either ParseError b, StreamK m (Array a))
43+ [A] parseBreak :: forall m a b. Monad m => ParserK a m b -> StreamK m a -> m (Either ParseError b, StreamK m a)
44+ [A] parse :: Monad m => ParserK a m b -> StreamK m a -> m (Either ParseError b)
45+ [A] handle :: (MonadCatch m, Exception e) => (e -> m (StreamK m a)) -> StreamK m a -> StreamK m a
46+ [A] bracketIO :: (MonadIO m, MonadCatch m) => IO b -> (b -> IO c) -> (b -> StreamK m a) -> StreamK m a
47+[C] Streamly.Data.Stream
48+ [A] wordsBy :: Monad m => (a -> Bool) -> Fold m a b -> Stream m a -> Stream m b
49+ [A] splitOn :: Monad m => (a -> Bool) -> Fold m a b -> Stream m a -> Stream m b
50+ [C] handle
51+ [O] handle :: (MonadCatch m, Exception e) => (e -> Stream m a) -> Stream m a -> Stream m a
52+ [N] handle :: (MonadCatch m, Exception e) => (e -> m (Stream m a)) -> Stream m a -> Stream m a
53+ [A] groupsOf :: Monad m => Int -> Fold m a b -> Stream m a -> Stream m b
54+[C] Streamly.Data.ParserK
55+ [D] fromParser :: (MonadIO m, Unbox a) => Parser a m b -> ParserK (Array a) m b
56+ [D] fromFold :: (MonadIO m, Unbox a) => Fold m a b -> ParserK (Array a) m b
57+ [A] adaptCG :: Monad m => Parser a m b -> ParserK (Array a) m b
58+ [A] adaptC :: (Monad m, Unbox a) => Parser a m b -> ParserK (Array a) m b
59+ [A] adapt :: Monad m => Parser a m b -> ParserK a m b
60+[C] Streamly.Data.Parser
61+ [A] groupByRollingEither :: Monad m => (a -> a -> Bool) -> Fold m a b -> Fold m a c -> Parser a m (Either b c)
62+ [A] groupByRolling :: Monad m => (a -> a -> Bool) -> Fold m a b -> Parser a m b
63+[A] Streamly.Data.MutByteArray
64+ [A] class Unbox a
65+ [A] class Serialize a
66+ [A] SerializeConfig
67+ [A] MutByteArray
68+ [A] unpin :: MutByteArray -> IO MutByteArray
69+ [A] sizeOf :: (Unbox a, SizeOfRep (Rep a)) => Proxy a -> Int
70+ [A] serializeAt :: Serialize a => Int -> MutByteArray -> a -> IO Int
71+ [A] pokeAt :: (Unbox a, Generic a, PokeRep (Rep a)) => Int -> MutByteArray -> a -> IO ()
72+ [A] pinnedNew :: Int -> IO MutByteArray
73+ [A] pin :: MutByteArray -> IO MutByteArray
74+ [A] peekAt :: (Unbox a, Generic a, PeekRep (Rep a)) => Int -> MutByteArray -> IO a
75+ [A] new :: Int -> IO MutByteArray
76+ [A] isPinned :: MutByteArray -> Bool
77+ [A] inlineSerializeAt :: Maybe Inline -> SerializeConfig -> SerializeConfig
78+ [A] inlineDeserializeAt :: Maybe Inline -> SerializeConfig -> SerializeConfig
79+ [A] inlineAddSizeTo :: Maybe Inline -> SerializeConfig -> SerializeConfig
80+ [A] deserializeAt :: Serialize a => Int -> MutByteArray -> Int -> IO (Int, a)
81+ [A] deriveUnbox :: Q [Dec] -> Q [Dec]
82+ [A] deriveSerializeWith :: (SerializeConfig -> SerializeConfig) -> Q [Dec] -> Q [Dec]
83+ [A] deriveSerialize :: Q [Dec] -> Q [Dec]
84+ [A] addSizeTo :: Serialize a => Int -> a -> Int
85+[C] Streamly.Data.MutArray.Generic
86+ [A] write :: MonadIO m => Fold m a (MutArray a)
87+ [A] readRev :: MonadIO m => MutArray a -> Stream m a
88+ [A] read :: MonadIO m => MutArray a -> Stream m a
89+ [A] putIndexUnsafe :: forall m a. MonadIO m => Int -> MutArray a -> a -> m ()
90+ [A] modifyIndexUnsafe :: MonadIO m => Int -> MutArray a -> (a -> (a, b)) -> m b
91+ [A] length :: MutArray a -> Int
92+ [A] getIndexUnsafe :: MonadIO m => Int -> MutArray a -> m a
93+ [C] getIndex
94+ [O] getIndex :: MonadIO m => Int -> MutArray a -> m a
95+ [N] getIndex :: MonadIO m => Int -> MutArray a -> m (Maybe a)
96+ [A] fromListN :: MonadIO m => Int -> [a] -> m (MutArray a)
97+ [A] fromList :: MonadIO m => [a] -> m (MutArray a)
98+[C] Streamly.Data.MutArray
99+ [A] unpin :: MutArray a -> IO (MutArray a)
100+ [A] readRev :: forall m a. (MonadIO m, Unbox a) => MutArray a -> Stream m a
101+ [A] read :: forall m a. (MonadIO m, Unbox a) => MutArray a -> Stream m a
102+ [A] putIndexUnsafe :: forall m a. (MonadIO m, Unbox a) => Int -> MutArray a -> a -> m ()
103+ [D] pokeByteIndex :: Unbox a => Int -> MutByteArray -> a -> IO ()
104+ [A] pokeAt :: (Unbox a, Generic a, PokeRep (Rep a)) => Int -> MutByteArray -> a -> IO ()
105+ [A] pinnedNew :: forall m a. (MonadIO m, Unbox a) => Int -> m (MutArray a)
106+ [A] pin :: MutArray a -> IO (MutArray a)
107+ [D] peekByteIndex :: Unbox a => Int -> MutByteArray -> IO a
108+ [A] peekAt :: (Unbox a, Generic a, PeekRep (Rep a)) => Int -> MutByteArray -> IO a
109+ [D] newPinned :: forall m a. (MonadIO m, Unbox a) => Int -> m (MutArray a)
110+ [A] modifyIndexUnsafe :: forall m a b. (MonadIO m, Unbox a) => Int -> MutArray a -> (a -> (a, b)) -> m b
111+ [A] modifyIndex :: forall m a b. (MonadIO m, Unbox a) => Int -> MutArray a -> (a -> (a, b)) -> m b
112+ [A] modify :: forall m a. (MonadIO m, Unbox a) => MutArray a -> (a -> a) -> m ()
113+ [A] isPinned :: MutArray a -> Bool
114+ [A] getIndexUnsafe :: forall m a. (MonadIO m, Unbox a) => Int -> MutArray a -> m a
115+ [C] getIndex
116+ [O] getIndex :: forall m a. (MonadIO m, Unbox a) => Int -> MutArray a -> m a
117+ [N] getIndex :: forall m a. (MonadIO m, Unbox a) => Int -> MutArray a -> m (Maybe a)
118+[C] Streamly.Data.Array.Generic
119+ [A] toList :: Array a -> [a]
120+ [A] getIndex :: Int -> Array a -> Maybe a
121+[C] Streamly.Data.Array
122+ [A] unpin :: Array a -> IO (Array a)
123+ [D] pokeByteIndex :: Unbox a => Int -> MutByteArray -> a -> IO ()
124+ [A] pokeAt :: (Unbox a, Generic a, PokeRep (Rep a)) => Int -> MutByteArray -> a -> IO ()
125+ [A] pin :: Array a -> IO (Array a)
126+ [D] peekByteIndex :: Unbox a => Int -> MutByteArray -> IO a
127+ [A] peekAt :: (Unbox a, Generic a, PeekRep (Rep a)) => Int -> MutByteArray -> IO a
128+ [A] isPinned :: Array a -> Bool
129+[C] Streamly.Console.Stdio
130+
131+---------------------------------
132+Internal API diff
133+---------------------------------
134+
135+[C] Streamly.Internal.Unicode.Stream
136+ [C] writeCharUtf8'
137+ [O] writeCharUtf8' :: Monad m => Fold m Word8 Char
138+ [N] writeCharUtf8' :: Monad m => Parser Word8 m Char
139+[C] Streamly.Internal.Unicode.Parser
140+ [A] number :: Monad m => Parser Char m (Integer, Int)
141+ [A] mkDouble :: Integer -> Int -> Double
142+ [A] doubleParser :: Monad m => Parser Char m (Int, Int)
143+ [C] double
144+ [O] double :: Parser Char m Double
145+ [N] double :: Monad m => Parser Char m Double
146+[R] Streamly.Internal.Serialize.ToBytes
147+[R] Streamly.Internal.Serialize.FromBytes
148+[C] Streamly.Internal.FileSystem.File
149+ [A] writeAppendWith :: (MonadIO m, MonadCatch m) => Int -> FilePath -> Stream m Word8 -> m ()
150+ [A] writeAppendChunks :: (MonadIO m, MonadCatch m) => FilePath -> Stream m (Array a) -> m ()
151+ [A] writeAppendArray :: FilePath -> Array a -> IO ()
152+ [A] writeAppend :: (MonadIO m, MonadCatch m) => FilePath -> Stream m Word8 -> m ()
153+ [R] appendWith :: (MonadIO m, MonadCatch m) => Int -> FilePath -> Stream m Word8 -> m ()
154+ [R] appendChunks :: (MonadIO m, MonadCatch m) => FilePath -> Stream m (Array a) -> m ()
155+ [R] appendArray :: FilePath -> Array a -> IO ()
156+ [R] append :: (MonadIO m, MonadCatch m) => FilePath -> Stream m Word8 -> m ()
157+[C] Streamly.Internal.FileSystem.Dir
158+ [C] reader
159+ [O] reader :: MonadIO m => Unfold m FilePath FilePath
160+ [N] reader :: (MonadIO m, MonadCatch m) => Unfold m FilePath FilePath
161+ [C] readFiles
162+ [O] readFiles :: MonadIO m => FilePath -> Stream m FilePath
163+ [N] readFiles :: (MonadIO m, MonadCatch m) => FilePath -> Stream m FilePath
164+ [C] readEitherPaths
165+ [O] readEitherPaths :: MonadIO m => FilePath -> Stream m (Either FilePath FilePath)
166+ [N] readEitherPaths :: (MonadIO m, MonadCatch m) => FilePath -> Stream m (Either FilePath FilePath)
167+ [C] readEither
168+ [O] readEither :: MonadIO m => FilePath -> Stream m (Either FilePath FilePath)
169+ [N] readEither :: (MonadIO m, MonadCatch m) => FilePath -> Stream m (Either FilePath FilePath)
170+ [C] readDirs
171+ [O] readDirs :: MonadIO m => FilePath -> Stream m FilePath
172+ [N] readDirs :: (MonadIO m, MonadCatch m) => FilePath -> Stream m FilePath
173+ [C] read
174+ [O] read :: MonadIO m => FilePath -> Stream m FilePath
175+ [N] read :: (MonadIO m, MonadCatch m) => FilePath -> Stream m FilePath
176+ [C] fileReader
177+ [O] fileReader :: MonadIO m => Unfold m FilePath FilePath
178+ [N] fileReader :: (MonadIO m, MonadCatch m) => Unfold m FilePath FilePath
179+ [C] eitherReaderPaths
180+ [O] eitherReaderPaths :: MonadIO m => Unfold m FilePath (Either FilePath FilePath)
181+ [N] eitherReaderPaths :: (MonadIO m, MonadCatch m) => Unfold m FilePath (Either FilePath FilePath)
182+ [C] eitherReader
183+ [O] eitherReader :: MonadIO m => Unfold m FilePath (Either FilePath FilePath)
184+ [N] eitherReader :: (MonadIO m, MonadCatch m) => Unfold m FilePath (Either FilePath FilePath)
185+ [C] dirReader
186+ [O] dirReader :: MonadIO m => Unfold m FilePath FilePath
187+ [N] dirReader :: (MonadIO m, MonadCatch m) => Unfold m FilePath FilePath
188+[R] Streamly.Internal.Data.Unfold.Type
189+[R] Streamly.Internal.Data.Unfold.Enumeration
190+[C] Streamly.Internal.Data.Unfold
191+ [C] Unfold
192+ [A] Unfold :: (s -> m (Step s b)) -> (a -> m s) -> Unfold m a b
193+ [A] takeWhileMWithInput :: Monad m => (a -> b -> m Bool) -> Unfold m a b -> Unfold m a b
194+ [A] manyInterleave :: Monad m => Unfold m a b -> Unfold m c a -> Unfold m c b
195+ [A] enumerateFromStepIntegral :: (Monad m, Integral a) => Unfold m (a, a) a
196+ [A] crossApplySnd :: Unfold m a b -> Unfold m a c -> Unfold m a c
197+ [A] crossApplyFst :: Unfold m a b -> Unfold m a c -> Unfold m a b
198+ [A] concatMap :: Monad m => (b -> Unfold m a c) -> Unfold m a b -> Unfold m a c
199+[R] Streamly.Internal.Data.Unboxed
200+[C] Streamly.Internal.Data.Time.Units
201+ [R] Streamly.Internal.Data.Unboxed.Unbox
202+ [A] Streamly.Internal.Data.Unbox.Unbox
203+ [A] instance Streamly.Internal.Data.Unbox.Unbox Streamly.Internal.Data.Time.Units.NanoSecond64
204+ [A] instance Streamly.Internal.Data.Unbox.Unbox Streamly.Internal.Data.Time.Units.MilliSecond64
205+ [A] instance Streamly.Internal.Data.Unbox.Unbox Streamly.Internal.Data.Time.Units.MicroSecond64
206+[R] Streamly.Internal.Data.Time.Clock.Type
207+[A] Streamly.Internal.Data.StreamK
208+ [A] CrossStreamK
209+ [A] (FixityR,6)
210+ [A] (FixityR,6)
211+ [A] (FixityR,6)
212+ [A] (FixityR,5)
213+ [A] (FixityR,5)
214+ [A] (FixityR,6)
215+ [A] (FixityR,5)
216+ [A] StreamK
217+ [A] MkStream :: (forall r. State StreamK m a -> (a -> StreamK m a -> m r) -> (a -> m r) -> m r -> m r) -> StreamK m a
218+ [A] zipWithM :: Monad m => (a -> b -> m c) -> StreamK m a -> StreamK m b -> StreamK m c
219+ [A] zipWith :: Monad m => (a -> b -> c) -> StreamK m a -> StreamK m b -> StreamK m c
220+ [A] unfoldrMWith :: Monad m => (m a -> StreamK m a -> StreamK m a) -> (b -> m (Maybe (a, b))) -> b -> StreamK m a
221+ [A] unfoldrM :: Monad m => (b -> m (Maybe (a, b))) -> b -> StreamK m a
222+ [A] unfoldr :: (b -> Maybe (a, b)) -> b -> StreamK m a
223+ [A] uncons :: Applicative m => StreamK m a -> m (Maybe (a, StreamK m a))
224+ [A] unShare :: StreamK m a -> StreamK m a
225+ [A] unCross :: CrossStreamK m a -> StreamK m a
226+ [A] toStream :: Applicative m => StreamK m a -> Stream m a
227+ [A] toList :: Monad m => StreamK m a -> m [a]
228+ [A] the :: (Eq a, Monad m) => StreamK m a -> m (Maybe a)
229+ [A] takeWhile :: (a -> Bool) -> StreamK m a -> StreamK m a
230+ [A] take :: Int -> StreamK m a -> StreamK m a
231+ [A] tail :: Applicative m => StreamK m a -> m (Maybe (StreamK m a))
232+ [A] sortBy :: Monad m => (a -> a -> Ordering) -> StreamK m a -> StreamK m a
233+ [A] sequence :: Monad m => StreamK m (m a) -> StreamK m a
234+ [A] scanlx' :: (x -> a -> x) -> x -> (x -> b) -> StreamK m a -> StreamK m b
235+ [A] scanl' :: (b -> a -> b) -> b -> StreamK m a -> StreamK m b
236+ [A] reverse :: StreamK m a -> StreamK m a
237+ [A] replicateMWith :: (m a -> StreamK m a -> StreamK m a) -> Int -> m a -> StreamK m a
238+ [A] replicateM :: Monad m => Int -> m a -> StreamK m a
239+ [A] replicate :: Int -> a -> StreamK m a
240+ [A] repeatMWith :: (m a -> t m a -> t m a) -> m a -> t m a
241+ [A] repeatM :: Monad m => m a -> StreamK m a
242+ [A] repeat :: a -> StreamK m a
243+ [A] parseDBreak :: Monad m => Parser a m b -> StreamK m a -> m (Either ParseError b, StreamK m a)
244+ [A] parseD :: Monad m => Parser a m b -> StreamK m a -> m (Either ParseError b)
245+ [A] parseChunksGeneric :: Monad m => ParserK (Array a) m b -> StreamK m (Array a) -> m (Either ParseError b)
246+ [A] parseChunks :: (Monad m, Unbox a) => ParserK (Array a) m b -> StreamK m (Array a) -> m (Either ParseError b)
247+ [A] parseBreakChunksGeneric :: forall m a b. Monad m => ParserK (Array a) m b -> StreamK m (Array a) -> m (Either ParseError b, StreamK m (Array a))
248+ [A] parseBreakChunks :: (Monad m, Unbox a) => ParserK (Array a) m b -> StreamK m (Array a) -> m (Either ParseError b, StreamK m (Array a))
249+ [A] parseBreak :: forall m a b. Monad m => ParserK a m b -> StreamK m a -> m (Either ParseError b, StreamK m a)
250+ [A] parse :: Monad m => ParserK a m b -> StreamK m a -> m (Either ParseError b)
251+ [A] null :: Monad m => StreamK m a -> m Bool
252+ [A] notElem :: (Monad m, Eq a) => a -> StreamK m a -> m Bool
253+ [A] nilM :: Applicative m => m b -> StreamK m a
254+ [A] nil :: StreamK m a
255+ [A] mkStream :: (forall r. State StreamK m a -> (a -> StreamK m a -> m r) -> (a -> m r) -> m r -> m r) -> StreamK m a
256+ [A] mkCross :: StreamK m a -> CrossStreamK m a
257+ [A] minimumBy :: Monad m => (a -> a -> Ordering) -> StreamK m a -> m (Maybe a)
258+ [A] minimum :: (Monad m, Ord a) => StreamK m a -> m (Maybe a)
259+ [A] mfix :: Monad m => (m a -> StreamK m a) -> StreamK m a
260+ [A] mergeMapWith :: (StreamK m b -> StreamK m b -> StreamK m b) -> (a -> StreamK m b) -> StreamK m a -> StreamK m b
261+ [A] mergeIterateWith :: (StreamK m a -> StreamK m a -> StreamK m a) -> (a -> StreamK m a) -> StreamK m a -> StreamK m a
262+ [A] mergeByM :: Monad m => (a -> a -> m Ordering) -> StreamK m a -> StreamK m a -> StreamK m a
263+ [A] mergeBy :: (a -> a -> Ordering) -> StreamK m a -> StreamK m a -> StreamK m a
264+ [A] maximumBy :: Monad m => (a -> a -> Ordering) -> StreamK m a -> m (Maybe a)
265+ [A] maximum :: (Monad m, Ord a) => StreamK m a -> m (Maybe a)
266+ [A] mapMaybe :: (a -> Maybe b) -> StreamK m a -> StreamK m b
267+ [A] mapM_ :: Monad m => (a -> m b) -> StreamK m a -> m ()
268+ [A] mapMWith :: (m b -> StreamK m b -> StreamK m b) -> (a -> m b) -> StreamK m a -> StreamK m b
269+ [A] mapMSerial :: Monad m => (a -> m b) -> StreamK m a -> StreamK m b
270+ [A] mapM :: Monad m => (a -> m b) -> StreamK m a -> StreamK m b
271+ [A] map :: (a -> b) -> StreamK m a -> StreamK m b
272+ [A] lookup :: (Monad m, Eq a) => a -> StreamK m (a, b) -> m (Maybe b)
273+ [A] liftInner :: (Monad m, MonadTrans t, Monad (t m)) => StreamK m a -> StreamK (t m) a
274+ [A] last :: Monad m => StreamK m a -> m (Maybe a)
275+ [A] iterateMWith :: Monad m => (m a -> StreamK m a -> StreamK m a) -> (a -> m a) -> m a -> StreamK m a
276+ [A] iterateM :: Monad m => (a -> m a) -> m a -> StreamK m a
277+ [A] iterate :: (a -> a) -> a -> StreamK m a
278+ [A] intersperseM :: Monad m => m a -> StreamK m a -> StreamK m a
279+ [A] intersperse :: Monad m => a -> StreamK m a -> StreamK m a
280+ [A] interleaveMin :: StreamK m a -> StreamK m a -> StreamK m a
281+ [A] interleaveFst :: StreamK m a -> StreamK m a -> StreamK m a
282+ [A] interleave :: StreamK m a -> StreamK m a -> StreamK m a
283+ [A] insertBy :: (a -> a -> Ordering) -> a -> StreamK m a -> StreamK m a
284+ [A] init :: Applicative m => StreamK m a -> m (Maybe (StreamK m a))
285+ [A] hoist :: (Monad m, Monad n) => (forall x. m x -> n x) -> StreamK m a -> StreamK n a
286+ [A] head :: Monad m => StreamK m a -> m (Maybe a)
287+ [A] handle :: (MonadCatch m, Exception e) => (e -> m (StreamK m a)) -> StreamK m a -> StreamK m a
288+ [A] fromYieldK :: YieldK m a -> StreamK m a
289+ [A] fromStream :: Monad m => Stream m a -> StreamK m a
290+ [A] fromStopK :: StopK m -> StreamK m a
291+ [A] fromPure :: a -> StreamK m a
292+ [A] fromList :: [a] -> StreamK m a
293+ [A] fromIndicesMWith :: (m a -> StreamK m a -> StreamK m a) -> (Int -> m a) -> StreamK m a
294+ [A] fromIndicesM :: Monad m => (Int -> m a) -> StreamK m a
295+ [A] fromIndices :: (Int -> a) -> StreamK m a
296+ [A] fromFoldableM :: (Foldable f, Monad m) => f (m a) -> StreamK m a
297+ [A] fromFoldable :: Foldable f => f a -> StreamK m a
298+ [A] fromEffect :: Monad m => m a -> StreamK m a
299+ [A] foldrT :: (Monad m, Monad (s m), MonadTrans s) => (a -> s m b -> s m b) -> s m b -> StreamK m a -> s m b
300+ [A] foldrSShared :: (a -> StreamK m b -> StreamK m b) -> StreamK m b -> StreamK m a -> StreamK m b
301+ [A] foldrSM :: Monad m => (m a -> StreamK m b -> StreamK m b) -> StreamK m b -> StreamK m a -> StreamK m b
302+ [A] foldrS :: (a -> StreamK m b -> StreamK m b) -> StreamK m b -> StreamK m a -> StreamK m b
303+ [A] foldrM :: (a -> m b -> m b) -> m b -> StreamK m a -> m b
304+ [A] foldr1 :: Monad m => (a -> a -> a) -> StreamK m a -> m (Maybe a)
305+ [A] foldr :: Monad m => (a -> b -> b) -> b -> StreamK m a -> m b
306+ [A] foldlx' :: forall m a b x. Monad m => (x -> a -> x) -> x -> (x -> b) -> StreamK m a -> m b
307+ [A] foldlT :: (Monad m, Monad (s m), MonadTrans s) => (s m b -> a -> s m b) -> s m b -> StreamK m a -> s m b
308+ [A] foldlS :: (StreamK m b -> a -> StreamK m b) -> StreamK m b -> StreamK m a -> StreamK m b
309+ [A] foldlMx' :: Monad m => (x -> a -> m x) -> m x -> (x -> m b) -> StreamK m a -> m b
310+ [A] foldlM' :: Monad m => (b -> a -> m b) -> m b -> StreamK m a -> m b
311+ [A] foldl' :: Monad m => (b -> a -> b) -> b -> StreamK m a -> m b
312+ [A] foldStreamShared :: State StreamK m a -> (a -> StreamK m a -> m r) -> (a -> m r) -> m r -> StreamK m a -> m r
313+ [A] foldStream :: State StreamK m a -> (a -> StreamK m a -> m r) -> (a -> m r) -> m r -> StreamK m a -> m r
314+ [A] foldEither :: Monad m => Fold m a b -> StreamK m a -> m (Either (Fold m a b) (b, StreamK m a))
315+ [A] foldConcat :: Monad m => Producer m a b -> Fold m b c -> StreamK m a -> m (c, StreamK m a)
316+ [A] foldBreak :: Monad m => Fold m a b -> StreamK m a -> m (b, StreamK m a)
317+ [A] fold :: Monad m => Fold m a b -> StreamK m a -> m b
318+ [A] findM :: Monad m => (a -> m Bool) -> StreamK m a -> m (Maybe a)
319+ [A] findIndices :: (a -> Bool) -> StreamK m a -> StreamK m Int
320+ [A] find :: Monad m => (a -> Bool) -> StreamK m a -> m (Maybe a)
321+ [A] filter :: (a -> Bool) -> StreamK m a -> StreamK m a
322+ [A] evalStateT :: Monad m => m s -> StreamK (StateT s m) a -> StreamK m a
323+ [A] elem :: (Monad m, Eq a) => a -> StreamK m a -> m Bool
324+ [A] dropWhile :: (a -> Bool) -> StreamK m a -> StreamK m a
325+ [A] drop :: Int -> StreamK m a -> StreamK m a
326+ [A] drain :: Monad m => StreamK m a -> m ()
327+ [A] deleteBy :: (a -> a -> Bool) -> a -> StreamK m a -> StreamK m a
328+ [A] crossWith :: Monad m => (a -> b -> c) -> StreamK m a -> StreamK m b -> StreamK m c
329+ [A] crossApplyWith :: (StreamK m b -> StreamK m b -> StreamK m b) -> StreamK m (a -> b) -> StreamK m a -> StreamK m b
330+ [A] crossApplySnd :: StreamK m a -> StreamK m b -> StreamK m b
331+ [A] crossApplyFst :: StreamK m a -> StreamK m b -> StreamK m a
332+ [A] crossApply :: StreamK m (a -> b) -> StreamK m a -> StreamK m b
333+ [A] cross :: Monad m => StreamK m a -> StreamK m b -> StreamK m (a, b)
334+ [A] consMBy :: Monad m => (StreamK m a -> StreamK m a -> StreamK m a) -> m a -> StreamK m a -> StreamK m a
335+ [A] consM :: Monad m => m a -> StreamK m a -> StreamK m a
336+ [A] consK :: YieldK m a -> StreamK m a -> StreamK m a
337+ [A] cons :: a -> StreamK m a -> StreamK m a
338+ [A] conjoin :: Monad m => StreamK m a -> StreamK m a -> StreamK m a
339+ [A] concatMapWith :: (StreamK m b -> StreamK m b -> StreamK m b) -> (a -> StreamK m b) -> StreamK m a -> StreamK m b
340+ [A] concatMapEffect :: Monad m => (b -> StreamK m a) -> m b -> StreamK m a
341+ [A] concatMap :: (a -> StreamK m b) -> StreamK m a -> StreamK m b
342+ [A] concatIterateWith :: (StreamK m a -> StreamK m a -> StreamK m a) -> (a -> StreamK m a) -> StreamK m a -> StreamK m a
343+ [A] concatIterateScanWith :: Monad m => (StreamK m a -> StreamK m a -> StreamK m a) -> (b -> a -> m (b, StreamK m a)) -> m b -> StreamK m a -> StreamK m a
344+ [A] concatIterateLeftsWith :: b ~ Either a c => (StreamK m b -> StreamK m b -> StreamK m b) -> (a -> StreamK m b) -> StreamK m b -> StreamK m b
345+ [A] concatEffect :: Monad m => m (StreamK m a) -> StreamK m a
346+ [A] buildSM :: Monad m => ((m a -> StreamK m a -> StreamK m a) -> StreamK m a -> StreamK m a) -> StreamK m a
347+ [A] buildS :: ((a -> StreamK m a -> StreamK m a) -> StreamK m a -> StreamK m a) -> StreamK m a
348+ [A] buildM :: Monad m => (forall r. (a -> StreamK m a -> m r) -> (a -> m r) -> m r -> m r) -> StreamK m a
349+ [A] build :: forall m a. (forall b. (a -> b -> b) -> b -> b) -> StreamK m a
350+ [A] bracketIO :: (MonadIO m, MonadCatch m) => IO b -> (b -> IO c) -> (b -> StreamK m a) -> StreamK m a
351+ [A] bindWith :: (StreamK m b -> StreamK m b -> StreamK m b) -> StreamK m a -> (a -> StreamK m b) -> StreamK m b
352+ [A] before :: Monad m => m b -> StreamK m a -> StreamK m a
353+ [A] augmentSM :: Monad m => ((m a -> StreamK m a -> StreamK m a) -> StreamK m a -> StreamK m a) -> StreamK m a -> StreamK m a
354+ [A] augmentS :: ((a -> StreamK m a -> StreamK m a) -> StreamK m a -> StreamK m a) -> StreamK m a -> StreamK m a
355+ [A] append :: StreamK m a -> StreamK m a -> StreamK m a
356+ [A] any :: Monad m => (a -> Bool) -> StreamK m a -> m Bool
357+ [A] all :: Monad m => (a -> Bool) -> StreamK m a -> m Bool
358+ [A] (.:) :: a -> StreamK m a -> StreamK m a
359+ [A] (!!) :: Monad m => StreamK m a -> Int -> m (Maybe a)
360+[R] Streamly.Internal.Data.Stream.StreamK.Type
361+[R] Streamly.Internal.Data.Stream.StreamK.Transformer
362+[R] Streamly.Internal.Data.Stream.StreamK
363+[R] Streamly.Internal.Data.Stream.StreamD.Type
364+[R] Streamly.Internal.Data.Stream.StreamD.Transformer
365+[R] Streamly.Internal.Data.Stream.StreamD.Transform
366+[R] Streamly.Internal.Data.Stream.StreamD.Top
367+[R] Streamly.Internal.Data.Stream.StreamD.Step
368+[R] Streamly.Internal.Data.Stream.StreamD.Nesting
369+[R] Streamly.Internal.Data.Stream.StreamD.Lift
370+[R] Streamly.Internal.Data.Stream.StreamD.Generate
371+[R] Streamly.Internal.Data.Stream.StreamD.Exception
372+[R] Streamly.Internal.Data.Stream.StreamD.Eliminate
373+[R] Streamly.Internal.Data.Stream.StreamD.Container
374+[D] Streamly.Internal.Data.Stream.StreamD
375+[R] Streamly.Internal.Data.Stream.Common
376+[R] Streamly.Internal.Data.Stream.Chunked
377+[C] Streamly.Internal.Data.Stream
378+ [A] class Enum a => Enumerable a
379+ [A] Stream
380+ [A] UnStream :: (State StreamK m a -> s -> m (Step s a)) -> s -> Stream m a
381+ [A] Step
382+ [A] Yield :: a -> s -> Step s a
383+ [A] Stop :: Step s a
384+ [A] Skip :: s -> Step s a
385+ [A] InterleaveState
386+ [A] InterleaveSecondOnly :: s2 -> InterleaveState s1 s2
387+ [A] InterleaveSecond :: s1 -> s2 -> InterleaveState s1 s2
388+ [A] InterleaveFirstOnly :: s1 -> InterleaveState s1 s2
389+ [A] InterleaveFirst :: s1 -> s2 -> InterleaveState s1 s2
390+ [A] FoldManyPost
391+ [A] FoldManyPostYield :: b -> FoldManyPost s fs b a -> FoldManyPost s fs b a
392+ [A] FoldManyPostStart :: s -> FoldManyPost s fs b a
393+ [A] FoldManyPostLoop :: s -> fs -> FoldManyPost s fs b a
394+ [A] FoldManyPostDone :: FoldManyPost s fs b a
395+ [A] FoldMany
396+ [A] FoldManyYield :: b -> FoldMany s fs b a -> FoldMany s fs b a
397+ [A] FoldManyStart :: s -> FoldMany s fs b a
398+ [A] FoldManyLoop :: s -> fs -> FoldMany s fs b a
399+ [A] FoldManyFirst :: fs -> s -> FoldMany s fs b a
400+ [A] FoldManyDone :: FoldMany s fs b a
401+ [A] CrossStream
402+ [A] ConcatUnfoldInterleaveState
403+ [A] ConcatUnfoldInterleaveOuter :: o -> [i] -> ConcatUnfoldInterleaveState o i
404+ [A] ConcatUnfoldInterleaveInnerR :: [i] -> [i] -> ConcatUnfoldInterleaveState o i
405+ [A] ConcatUnfoldInterleaveInnerL :: [i] -> [i] -> ConcatUnfoldInterleaveState o i
406+ [A] ConcatUnfoldInterleaveInner :: o -> [i] -> ConcatUnfoldInterleaveState o i
407+ [A] ConcatMapUState
408+ [A] ConcatMapUOuter :: o -> ConcatMapUState o i
409+ [A] ConcatMapUInner :: o -> i -> ConcatMapUState o i
410+ [A] AppendState
411+ [A] AppendSecond :: s2 -> AppendState s1 s2
412+ [A] AppendFirst :: s1 -> AppendState s1 s2
413+ [A] pattern Stream :: (State StreamK m a -> s -> m (Step s a)) -> s -> Stream m a
414+ [A] zipWithM :: Monad m => (a -> b -> m c) -> Stream m a -> Stream m b -> Stream m c
415+ [A] zipWith :: Monad m => (a -> b -> c) -> Stream m a -> Stream m b -> Stream m c
416+ [A] wordsBy :: Monad m => (a -> Bool) -> Fold m a b -> Stream m a -> Stream m b
417+ [A] with :: Monad m => (Stream m a -> Stream m (s, a)) -> (((s, a) -> b) -> Stream m (s, a) -> Stream m (s, a)) -> ((s, a) -> b) -> Stream m a -> Stream m a
418+ [A] usingStateT :: Monad m => m s -> (Stream (StateT s m) a -> Stream (StateT s m) a) -> Stream m a -> Stream m a
419+ [A] usingReaderT :: Monad m => m r -> (Stream (ReaderT r m) a -> Stream (ReaderT r m) a) -> Stream m a -> Stream m a
420+ [A] uniqBy :: Monad m => (a -> a -> Bool) -> Stream m a -> Stream m a
421+ [A] uniq :: (Eq a, Monad m) => Stream m a -> Stream m a
422+ [A] unionWithStreamGenericBy :: MonadIO m => (a -> a -> Bool) -> Stream m a -> Stream m a -> Stream m a
423+ [A] unionWithStreamAscBy :: (a -> a -> Ordering) -> Stream m a -> Stream m a -> Stream m a
424+ [A] unfoldrM :: Monad m => (s -> m (Maybe (a, s))) -> s -> Stream m a
425+ [A] unfoldr :: Monad m => (s -> Maybe (a, s)) -> s -> Stream m a
426+ [A] unfoldRoundRobin :: Monad m => Unfold m a b -> Stream m a -> Stream m b
427+ [A] unfoldMany :: Monad m => Unfold m a b -> Stream m a -> Stream m b
428+ [A] unfoldIterateDfs :: Monad m => Unfold m a a -> Stream m a -> Stream m a
429+ [A] unfoldIterateBfsRev :: Monad m => Unfold m a a -> Stream m a -> Stream m a
430+ [A] unfoldIterateBfs :: Monad m => Unfold m a a -> Stream m a -> Stream m a
431+ [A] unfoldInterleave :: Monad m => Unfold m a b -> Stream m a -> Stream m b
432+ [A] unfold :: Applicative m => Unfold m a b -> a -> Stream m b
433+ [A] uncons :: Monad m => Stream m a -> m (Maybe (a, Stream m a))
434+ [A] unCross :: CrossStream m a -> Stream m a
435+ [A] transform :: Monad m => Pipe m a b -> Stream m a -> Stream m b
436+ [A] trace_ :: Monad m => m b -> Stream m a -> Stream m a
437+ [A] trace :: Monad m => (a -> m b) -> Stream m a -> Stream m a
438+ [A] toStreamK :: Monad m => Stream m a -> StreamK m a
439+ [A] toListRev :: Monad m => Stream m a -> m [a]
440+ [A] toList :: Monad m => Stream m a -> m [a]
441+ [A] timestamped :: MonadIO m => Stream m a -> Stream m (AbsTime, a)
442+ [A] timestampWith :: MonadIO m => Double -> Stream m a -> Stream m (AbsTime, a)
443+ [A] timesWith :: MonadIO m => Double -> Stream m (AbsTime, RelTime64)
444+ [A] times :: MonadIO m => Stream m (AbsTime, RelTime64)
445+ [A] timeout :: AbsTime -> t m ()
446+ [A] timeIndexed :: MonadIO m => Stream m a -> Stream m (RelTime64, a)
447+ [A] timeIndexWith :: MonadIO m => Double -> Stream m a -> Stream m (RelTime64, a)
448+ [A] the :: (Eq a, Monad m) => Stream m a -> m (Maybe a)
449+ [A] tapOffsetEvery :: Monad m => Int -> Int -> Fold m a b -> Stream m a -> Stream m a
450+ [A] tap :: Monad m => Fold m a b -> Stream m a -> Stream m a
451+ [A] takeWhileM :: Monad m => (a -> m Bool) -> Stream m a -> Stream m a
452+ [A] takeWhileLast :: (a -> Bool) -> Stream m a -> Stream m a
453+ [A] takeWhileAround :: (a -> Bool) -> Stream m a -> Stream m a
454+ [A] takeWhile :: Monad m => (a -> Bool) -> Stream m a -> Stream m a
455+ [A] takeEndByM :: Monad m => (a -> m Bool) -> Stream m a -> Stream m a
456+ [A] takeEndBy :: Monad m => (a -> Bool) -> Stream m a -> Stream m a
457+ [A] take :: Applicative m => Int -> Stream m a -> Stream m a
458+ [A] tail :: Monad m => Stream m a -> m (Maybe (Stream m a))
459+ [A] stripSuffixUnbox :: (MonadIO m, Eq a, Unbox a) => Stream m a -> Stream m a -> m (Maybe (Stream m a))
460+ [A] stripSuffix :: (Monad m, Eq a) => Stream m a -> Stream m a -> m (Maybe (Stream m a))
461+ [A] stripPrefix :: (Monad m, Eq a) => Stream m a -> Stream m a -> m (Maybe (Stream m a))
462+ [A] strideFromThen :: Monad m => Int -> Int -> Stream m a -> Stream m a
463+ [A] splitOnSuffixSeqAny :: [Array a] -> Fold m a b -> Stream m a -> Stream m b
464+ [A] splitOnSuffixSeq :: forall m a b. (MonadIO m, Storable a, Unbox a, Enum a, Eq a) => Bool -> Array a -> Fold m a b -> Stream m a -> Stream m b
465+ [A] splitOnSeq :: forall m a b. (MonadIO m, Storable a, Unbox a, Enum a, Eq a) => Array a -> Fold m a b -> Stream m a -> Stream m b
466+ [A] splitOnPrefix :: (a -> Bool) -> Fold m a b -> Stream m a -> Stream m b
467+ [A] splitOnAny :: [Array a] -> Fold m a b -> Stream m a -> Stream m b
468+ [A] splitOn :: Monad m => (a -> Bool) -> Fold m a b -> Stream m a -> Stream m b
469+ [A] 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)
470+ [A] splitInnerBy :: Monad m => (f a -> m (f a, Maybe (f a))) -> (f a -> f a -> m (f a)) -> Stream m (f a) -> Stream m (f a)
471+ [A] slicesBy :: Monad m => (a -> Bool) -> Stream m a -> Stream m (Int, Int)
472+ [A] sliceOnSuffix :: Monad m => (a -> Bool) -> Stream m a -> Stream m (Int, Int)
473+ [A] sequence :: Monad m => Stream m (m a) -> Stream m a
474+ [A] scanlx' :: Monad m => (x -> a -> x) -> x -> (x -> b) -> Stream m a -> Stream m b
475+ [A] scanlMx' :: Monad m => (x -> a -> m x) -> m x -> (x -> m b) -> Stream m a -> Stream m b
476+ [A] scanlMAfter' :: Monad m => (b -> a -> m b) -> m b -> (b -> m b) -> Stream m a -> Stream m b
477+ [A] scanlM' :: Monad m => (b -> a -> m b) -> m b -> Stream m a -> Stream m b
478+ [A] scanlM :: Monad m => (b -> a -> m b) -> m b -> Stream m a -> Stream m b
479+ [A] scanl1M' :: Monad m => (a -> a -> m a) -> Stream m a -> Stream m a
480+ [A] scanl1M :: Monad m => (a -> a -> m a) -> Stream m a -> Stream m a
481+ [A] scanl1' :: Monad m => (a -> a -> a) -> Stream m a -> Stream m a
482+ [A] scanl1 :: Monad m => (a -> a -> a) -> Stream m a -> Stream m a
483+ [A] scanl' :: Monad m => (b -> a -> b) -> b -> Stream m a -> Stream m b
484+ [A] scanl :: Monad m => (b -> a -> b) -> b -> Stream m a -> Stream m b
485+ [A] scanMaybe :: Monad m => Fold m a (Maybe b) -> Stream m a -> Stream m b
486+ [A] scanMany :: Monad m => Fold m a b -> Stream m a -> Stream m b
487+ [A] scan :: Monad m => Fold m a b -> Stream m a -> Stream m b
488+ [A] runStateT :: Monad m => m s -> Stream (StateT s m) a -> Stream m (s, a)
489+ [A] runReaderT :: Monad m => m s -> Stream (ReaderT s m) a -> Stream m a
490+ [A] runInnerWithState :: Monad m => (forall b. s -> t m b -> m (b, s)) -> m s -> Stream (t m) a -> Stream m (s, a)
491+ [A] runInnerWith :: Monad m => (forall b. t m b -> m b) -> Stream (t m) a -> Stream m a
492+ [A] roundRobin :: Monad m => Stream m a -> Stream m a -> Stream m a
493+ [A] rollingMapM :: Monad m => (Maybe a -> a -> m b) -> Stream m a -> Stream m b
494+ [A] rollingMap2 :: Monad m => (a -> a -> b) -> Stream m a -> Stream m b
495+ [A] rollingMap :: Monad m => (Maybe a -> a -> b) -> Stream m a -> Stream m b
496+ [A] reverseUnbox :: (MonadIO m, Unbox a) => Stream m a -> Stream m a
497+ [A] reverse :: Monad m => Stream m a -> Stream m a
498+ [A] replicateM :: Monad m => Int -> m a -> Stream m a
499+ [A] replicate :: Monad m => Int -> a -> Stream m a
500+ [A] repeated :: Stream m a -> Stream m a
501+ [A] repeatM :: Monad m => m a -> Stream m a
502+ [A] repeat :: Monad m => a -> Stream m a
503+ [A] relTimesWith :: MonadIO m => Double -> Stream m RelTime64
504+ [A] relTimes :: MonadIO m => Stream m RelTime64
505+ [A] refoldMany :: Monad m => Refold m x a b -> m x -> Stream m a -> Stream m b
506+ [A] refoldIterateM :: Monad m => Refold m b a b -> m b -> Stream m a -> Stream m b
507+ [A] reduceIterateBfs :: Monad m => (a -> a -> m a) -> Stream m a -> m (Maybe a)
508+ [A] reassembleBy :: Fold m a b -> (a -> a -> Int) -> Stream m a -> Stream m b
509+ [A] prune :: (a -> Bool) -> Stream m a -> Stream m a
510+ [A] prescanlM' :: Monad m => (b -> a -> m b) -> m b -> Stream m a -> Stream m b
511+ [A] prescanl' :: Monad m => (b -> a -> b) -> b -> Stream m a -> Stream m b
512+ [A] postscanlx' :: Monad m => (x -> a -> x) -> x -> (x -> b) -> Stream m a -> Stream m b
513+ [A] postscanlMx' :: Monad m => (x -> a -> m x) -> m x -> (x -> m b) -> Stream m a -> Stream m b
514+ [A] postscanlMAfter' :: Monad m => (b -> a -> m b) -> m b -> (b -> m b) -> Stream m a -> Stream m b
515+ [A] postscanlM' :: Monad m => (b -> a -> m b) -> m b -> Stream m a -> Stream m b
516+ [A] postscanlM :: Monad m => (b -> a -> m b) -> m b -> Stream m a -> Stream m b
517+ [A] postscanl' :: Monad m => (a -> b -> a) -> a -> Stream m b -> Stream m a
518+ [A] postscanl :: Monad m => (a -> b -> a) -> a -> Stream m b -> Stream m a
519+ [A] postscan :: Monad m => Fold m a b -> Stream m a -> Stream m b
520+ [A] parseSequence :: Stream m (Parser a m b) -> Stream m a -> Stream m b
521+ [A] parseManyTill :: Parser a m b -> Parser a m x -> Stream m a -> Stream m b
522+ [A] parseManyD :: Monad m => Parser a m b -> Stream m a -> Stream m (Either ParseError b)
523+ [A] parseMany :: Monad m => Parser a m b -> Stream m a -> Stream m (Either ParseError b)
524+ [A] parseIterateD :: Monad m => (b -> Parser a m b) -> b -> Stream m a -> Stream m (Either ParseError b)
525+ [A] parseIterate :: Monad m => (b -> Parser a m b) -> b -> Stream m a -> Stream m (Either ParseError b)
526+ [A] parseD :: Monad m => Parser a m b -> Stream m a -> m (Either ParseError b)
527+ [A] parseBreakD :: Monad m => Parser a m b -> Stream m a -> m (Either ParseError b, Stream m a)
528+ [A] parseBreak :: Monad m => Parser a m b -> Stream m a -> m (Either ParseError b, Stream m a)
529+ [A] parse :: Monad m => Parser a m b -> Stream m a -> m (Either ParseError b)
530+ [A] onException :: MonadCatch m => m b -> Stream m a -> Stream m a
531+ [A] null :: Monad m => Stream m a -> m Bool
532+ [A] nub :: (Monad m, Ord a) => Stream m a -> Stream m a
533+ [A] notElem :: (Monad m, Eq a) => a -> Stream m a -> m Bool
534+ [A] nilM :: Applicative m => m b -> Stream m a
535+ [A] nil :: Applicative m => Stream m a
536+ [A] morphInner :: Monad n => (forall x. m x -> n x) -> Stream m a -> Stream n a
537+ [A] mkCross :: Stream m a -> CrossStream m a
538+ [A] minimumBy :: Monad m => (a -> a -> Ordering) -> Stream m a -> m (Maybe a)
539+ [A] minimum :: (Monad m, Ord a) => Stream m a -> m (Maybe a)
540+ [A] mergeMinBy :: (a -> a -> m Ordering) -> Stream m a -> Stream m a -> Stream m a
541+ [A] mergeFstBy :: (a -> a -> m Ordering) -> Stream m a -> Stream m a -> Stream m a
542+ [A] mergeByM :: Monad m => (a -> a -> m Ordering) -> Stream m a -> Stream m a -> Stream m a
543+ [A] mergeBy :: Monad m => (a -> a -> Ordering) -> Stream m a -> Stream m a -> Stream m a
544+ [A] maximumBy :: Monad m => (a -> a -> Ordering) -> Stream m a -> m (Maybe a)
545+ [A] maximum :: (Monad m, Ord a) => Stream m a -> m (Maybe a)
546+ [A] mapMaybeM :: Monad m => (a -> m (Maybe b)) -> Stream m a -> Stream m b
547+ [A] mapMaybe :: Monad m => (a -> Maybe b) -> Stream m a -> Stream m b
548+ [A] mapM_ :: Monad m => (a -> m b) -> Stream m a -> m ()
549+ [A] mapM :: Monad m => (a -> m b) -> Stream m a -> Stream m b
550+ [A] map :: Monad m => (a -> b) -> Stream m a -> Stream m b
551+ [A] lookup :: (Monad m, Eq a) => a -> Stream m (a, b) -> m (Maybe b)
552+ [A] liftInnerWith :: Monad (t m) => (forall b. m b -> t m b) -> Stream m a -> Stream (t m) a
553+ [A] liftInner :: (Monad m, MonadTrans t, Monad (t m)) => Stream m a -> Stream (t m) a
554+ [A] last :: Monad m => Stream m a -> m (Maybe a)
555+ [A] joinOuterGeneric :: MonadIO m => (a -> b -> Bool) -> Stream m a -> Stream m b -> Stream m (Maybe a, Maybe b)
556+ [A] joinOuterAscBy :: (a -> b -> Ordering) -> Stream m a -> Stream m b -> Stream m (Maybe a, Maybe b)
557+ [A] joinOuter :: (Ord k, MonadIO m) => Stream m (k, a) -> Stream m (k, b) -> Stream m (k, Maybe a, Maybe b)
558+ [A] joinLeftGeneric :: Monad m => (a -> b -> Bool) -> Stream m a -> Stream m b -> Stream m (a, Maybe b)
559+ [A] joinLeftAscBy :: (a -> b -> Ordering) -> Stream m a -> Stream m b -> Stream m (a, Maybe b)
560+ [A] joinLeft :: (Ord k, Monad m) => Stream m (k, a) -> Stream m (k, b) -> Stream m (k, a, Maybe b)
561+ [A] joinInnerGeneric :: Monad m => (a -> b -> Bool) -> Stream m a -> Stream m b -> Stream m (a, b)
562+ [A] joinInnerAscBy :: (a -> b -> Ordering) -> Stream m a -> Stream m b -> Stream m (a, b)
563+ [A] joinInner :: (Monad m, Ord k) => Stream m (k, a) -> Stream m (k, b) -> Stream m (k, a, b)
564+ [A] iterateM :: Monad m => (a -> m a) -> m a -> Stream m a
565+ [A] iterate :: Monad m => (a -> a) -> a -> Stream m a
566+ [A] isSuffixOfUnbox :: (MonadIO m, Eq a, Unbox a) => Stream m a -> Stream m a -> m Bool
567+ [A] isSuffixOf :: (Monad m, Eq a) => Stream m a -> Stream m a -> m Bool
568+ [A] isSubsequenceOf :: (Monad m, Eq a) => Stream m a -> Stream m a -> m Bool
569+ [A] isPrefixOf :: (Monad m, Eq a) => Stream m a -> Stream m a -> m Bool
570+ [A] isInfixOf :: (MonadIO m, Eq a, Enum a, Storable a, Unbox a) => Stream m a -> Stream m a -> m Bool
571+ [A] intersperseM_ :: Monad m => m b -> Stream m a -> Stream m a
572+ [A] intersperseMWith :: Int -> m a -> Stream m a -> Stream m a
573+ [A] intersperseMSuffix_ :: Monad m => m b -> Stream m a -> Stream m a
574+ [A] intersperseMSuffixWith :: forall m a. Monad m => Int -> m a -> Stream m a -> Stream m a
575+ [A] intersperseMSuffix :: forall m a. Monad m => m a -> Stream m a -> Stream m a
576+ [A] intersperseMPrefix_ :: Monad m => m b -> Stream m a -> Stream m a
577+ [A] intersperseM :: Monad m => m a -> Stream m a -> Stream m a
578+ [A] intersperse :: Monad m => a -> Stream m a -> Stream m a
579+ [A] intersectBySorted :: Monad m => (a -> a -> Ordering) -> Stream m a -> Stream m a -> Stream m a
580+ [A] interposeSuffixM :: Monad m => m c -> Unfold m b c -> Stream m b -> Stream m c
581+ [A] interposeSuffix :: Monad m => c -> Unfold m b c -> Stream m b -> Stream m c
582+ [A] interposeM :: Monad m => m c -> Unfold m b c -> Stream m b -> Stream m c
583+ [A] interpose :: Monad m => c -> Unfold m b c -> Stream m b -> Stream m c
584+ [A] interleaveMin :: Monad m => Stream m a -> Stream m a -> Stream m a
585+ [A] interleaveFstSuffix :: Monad m => Stream m a -> Stream m a -> Stream m a
586+ [A] interleaveFst :: Monad m => Stream m a -> Stream m a -> Stream m a
587+ [A] interleave :: Monad m => Stream m a -> Stream m a -> Stream m a
588+ [A] intercalateSuffix :: Monad m => Unfold m b c -> b -> Stream m b -> Stream m c
589+ [A] intercalate :: Monad m => Unfold m b c -> b -> Stream m b -> Stream m c
590+ [A] insertBy :: Monad m => (a -> a -> Ordering) -> a -> Stream m a -> Stream m a
591+ [A] indexedR :: Monad m => Int -> Stream m a -> Stream m (Int, a)
592+ [A] indexed :: Monad m => Stream m a -> Stream m (Int, a)
593+ [A] headElse :: Monad m => a -> Stream m a -> m a
594+ [A] head :: Monad m => Stream m a -> m (Maybe a)
595+ [A] handle :: (MonadCatch m, Exception e) => (e -> m (Stream m a)) -> Stream m a -> Stream m a
596+ [A] groupsWhile :: Monad m => (a -> a -> Bool) -> Fold m a b -> Stream m a -> Stream m b
597+ [A] groupsRollingBy :: Monad m => (a -> a -> Bool) -> Fold m a b -> Stream m a -> Stream m b
598+ [A] groupsOf :: Monad m => Int -> Fold m a b -> Stream m a -> Stream m b
599+ [A] gintercalateSuffix :: Monad m => Unfold m a c -> Stream m a -> Unfold m b c -> Stream m b -> Stream m c
600+ [A] gintercalate :: Monad m => Unfold m a c -> Stream m a -> Unfold m b c -> Stream m b -> Stream m c
601+ [A] ghandle :: (MonadCatch m, Exception e) => (e -> Stream m a -> m (Stream m a)) -> Stream m a -> Stream m a
602+ [A] generateM :: Monad m => Int -> (Int -> m a) -> Stream m a
603+ [A] generate :: Monad m => Int -> (Int -> a) -> Stream m a
604+ [A] generalizeInner :: Monad m => Stream Identity a -> Stream m a
605+ [A] gbracket_ :: Monad m => m c -> (c -> m d) -> (c -> e -> Stream m b -> m (Stream m b)) -> (forall s. m s -> m (Either e s)) -> (c -> Stream m b) -> Stream m b
606+ [A] gbracket :: MonadIO m => IO c -> (c -> IO d1) -> (c -> e -> Stream m b -> IO (Stream m b)) -> (c -> IO d2) -> (forall s. m s -> m (Either e s)) -> (c -> Stream m b) -> Stream m b
607+ [A] fromStreamK :: Applicative m => StreamK m a -> Stream m a
608+ [A] fromPure :: Applicative m => a -> Stream m a
609+ [A] fromPtrN :: (Monad m, Storable a) => Int -> Ptr a -> Stream m a
610+ [A] fromPtr :: forall m a. (Monad m, Storable a) => Ptr a -> Stream m a
611+ [A] fromListM :: Monad m => [m a] -> Stream m a
612+ [A] fromList :: Applicative m => [a] -> Stream m a
613+ [A] fromIndicesM :: Monad m => (Int -> m a) -> Stream m a
614+ [A] fromIndices :: Monad m => (Int -> a) -> Stream m a
615+ [A] fromFoldableM :: (Monad m, Foldable f) => f (m a) -> Stream m a
616+ [A] fromFoldable :: (Monad m, Foldable f) => f a -> Stream m a
617+ [A] fromEffect :: Applicative m => m a -> Stream m a
618+ [A] fromByteStr# :: Monad m => Addr# -> Stream m Word8
619+ [A] foldrT :: (Monad m, Monad (t m), MonadTrans t) => (a -> t m b -> t m b) -> t m b -> Stream m a -> t m b
620+ [A] foldrS :: Monad m => (a -> Stream m b -> Stream m b) -> Stream m b -> Stream m a -> Stream m b
621+ [A] foldrMx :: Monad m => (a -> m x -> m x) -> m x -> (m x -> m b) -> Stream m a -> m b
622+ [A] foldrM :: Monad m => (a -> m b -> m b) -> m b -> Stream m a -> m b
623+ [A] foldr1 :: Monad m => (a -> a -> a) -> Stream m a -> m (Maybe a)
624+ [A] foldr :: Monad m => (a -> b -> b) -> b -> Stream m a -> m b
625+ [A] foldlx' :: Monad m => (x -> a -> x) -> x -> (x -> b) -> Stream m a -> m b
626+ [A] foldlT :: (Monad m, Monad (s m), MonadTrans s) => (s m b -> a -> s m b) -> s m b -> Stream m a -> s m b
627+ [A] foldlS :: Monad m => (Stream m b -> a -> Stream m b) -> Stream m b -> Stream m a -> Stream m b
628+ [A] foldlMx' :: Monad m => (x -> a -> m x) -> m x -> (x -> m b) -> Stream m a -> m b
629+ [A] foldlM' :: Monad m => (b -> a -> m b) -> m b -> Stream m a -> m b
630+ [A] foldl' :: Monad m => (b -> a -> b) -> b -> Stream m a -> m b
631+ [A] foldSequence :: Stream m (Fold m a b) -> Stream m a -> Stream m b
632+ [A] foldManyPost :: Monad m => Fold m a b -> Stream m a -> Stream m b
633+ [A] foldMany :: Monad m => Fold m a b -> Stream m a -> Stream m b
634+ [A] foldIterateM :: Monad m => (b -> m (Fold m a b)) -> m b -> Stream m a -> Stream m b
635+ [A] foldIterateBfs :: Fold m a (Either a a) -> Stream m a -> m (Maybe a)
636+ [A] foldEither :: Monad m => Fold m a b -> Stream m a -> m (Either (Fold m a b) (b, Stream m a))
637+ [A] foldBreak :: Monad m => Fold m a b -> Stream m a -> m (b, Stream m a)
638+ [A] foldAddLazy :: Monad m => Fold m a b -> Stream m a -> Fold m a b
639+ [A] foldAdd :: Monad m => Fold m a b -> Stream m a -> m (Fold m a b)
640+ [A] fold :: Monad m => Fold m a b -> Stream m a -> m b
641+ [A] findM :: Monad m => (a -> m Bool) -> Stream m a -> m (Maybe a)
642+ [A] findIndices :: Monad m => (a -> Bool) -> Stream m a -> Stream m Int
643+ [A] find :: Monad m => (a -> Bool) -> Stream m a -> m (Maybe a)
644+ [A] finallyUnsafe :: MonadCatch m => m b -> Stream m a -> Stream m a
645+ [A] finallyIO :: (MonadIO m, MonadCatch m) => IO b -> Stream m a -> Stream m a
646+ [A] filterM :: Monad m => (a -> m Bool) -> Stream m a -> Stream m a
647+ [A] filterInStreamGenericBy :: Monad m => (a -> a -> Bool) -> Stream m a -> Stream m a -> Stream m a
648+ [A] filterInStreamAscBy :: Monad m => (a -> a -> Ordering) -> Stream m a -> Stream m a -> Stream m a
649+ [A] filter :: Monad m => (a -> Bool) -> Stream m a -> Stream m a
650+ [A] evalStateT :: Monad m => m s -> Stream (StateT s m) a -> Stream m a
651+ [A] eqBy :: Monad m => (a -> b -> Bool) -> Stream m a -> Stream m b -> m Bool
652+ [A] enumerateTo :: (Monad m, Bounded a, Enumerable a) => a -> Stream m a
653+ [A] enumerateFromToSmall :: (Monad m, Enum a) => a -> a -> Stream m a
654+ [A] enumerateFromToIntegral :: (Monad m, Integral a) => a -> a -> Stream m a
655+ [A] enumerateFromToFractional :: (Monad m, Fractional a, Ord a) => a -> a -> Stream m a
656+ [A] enumerateFromTo :: (Enumerable a, Monad m) => a -> a -> Stream m a
657+ [A] enumerateFromThenToSmall :: (Monad m, Enum a) => a -> a -> a -> Stream m a
658+ [A] enumerateFromThenToIntegral :: (Monad m, Integral a) => a -> a -> a -> Stream m a
659+ [A] enumerateFromThenToFractional :: (Monad m, Fractional a, Ord a) => a -> a -> a -> Stream m a
660+ [A] enumerateFromThenTo :: (Enumerable a, Monad m) => a -> a -> a -> Stream m a
661+ [A] enumerateFromThenSmallBounded :: (Monad m, Enumerable a, Bounded a) => a -> a -> Stream m a
662+ [A] enumerateFromThenNum :: (Monad m, Num a) => a -> a -> Stream m a
663+ [A] enumerateFromThenIntegral :: (Monad m, Integral a, Bounded a) => a -> a -> Stream m a
664+ [A] enumerateFromThenFractional :: (Monad m, Fractional a) => a -> a -> Stream m a
665+ [A] enumerateFromThen :: (Enumerable a, Monad m) => a -> a -> Stream m a
666+ [A] enumerateFromStepNum :: (Monad m, Num a) => a -> a -> Stream m a
667+ [A] enumerateFromStepIntegral :: (Integral a, Monad m) => a -> a -> Stream m a
668+ [A] enumerateFromNum :: (Monad m, Num a) => a -> Stream m a
669+ [A] enumerateFromIntegral :: (Monad m, Integral a, Bounded a) => a -> Stream m a
670+ [A] enumerateFromFractional :: (Monad m, Fractional a) => a -> Stream m a
671+ [A] enumerateFromBounded :: (Monad m, Enumerable a, Bounded a) => a -> Stream m a
672+ [A] enumerateFrom :: (Enumerable a, Monad m) => a -> Stream m a
673+ [A] enumerate :: (Monad m, Bounded a, Enumerable a) => Stream m a
674+ [A] elemIndices :: (Monad m, Eq a) => a -> Stream m a -> Stream m Int
675+ [A] elem :: (Monad m, Eq a) => a -> Stream m a -> m Bool
676+ [A] durations :: Double -> t m RelTime64
677+ [A] dropWhileM :: Monad m => (a -> m Bool) -> Stream m a -> Stream m a
678+ [A] dropWhileLast :: (a -> Bool) -> Stream m a -> Stream m a
679+ [A] dropWhileAround :: (a -> Bool) -> Stream m a -> Stream m a
680+ [A] dropWhile :: Monad m => (a -> Bool) -> Stream m a -> Stream m a
681+ [A] dropSuffix :: Stream m a -> Stream m a -> Stream m a
682+ [A] dropPrefix :: Stream m a -> Stream m a -> Stream m a
683+ [A] dropLast :: Int -> Stream m a -> Stream m a
684+ [A] dropInfix :: Stream m a -> Stream m a -> Stream m a
685+ [A] drop :: Monad m => Int -> Stream m a -> Stream m a
686+ [A] drain :: Monad m => Stream m a -> m ()
687+ [A] deleteInStreamGenericBy :: Monad m => (a -> a -> Bool) -> Stream m a -> Stream m a -> Stream m a
688+ [A] deleteInStreamAscBy :: (a -> a -> Ordering) -> Stream m a -> Stream m a -> Stream m a
689+ [A] deleteBy :: Monad m => (a -> a -> Bool) -> a -> Stream m a -> Stream m a
690+ [A] delayPre :: MonadIO m => Double -> Stream m a -> Stream m a
691+ [A] delayPost :: MonadIO m => Double -> Stream m a -> Stream m a
692+ [A] delay :: MonadIO m => Double -> Stream m a -> Stream m a
693+ [A] crossWith :: Monad m => (a -> b -> c) -> Stream m a -> Stream m b -> Stream m c
694+ [A] crossApplySnd :: Functor f => Stream f a -> Stream f b -> Stream f b
695+ [A] crossApplyFst :: Functor f => Stream f a -> Stream f b -> Stream f a
696+ [A] crossApply :: Functor f => Stream f (a -> b) -> Stream f a -> Stream f b
697+ [A] cross :: Monad m => Stream m a -> Stream m b -> Stream m (a, b)
698+ [A] consM :: Applicative m => m a -> Stream m a -> Stream m a
699+ [A] cons :: Applicative m => a -> Stream m a -> Stream m a
700+ [A] concatMapM :: Monad m => (a -> m (Stream m b)) -> Stream m a -> Stream m b
701+ [A] concatMap :: Monad m => (a -> Stream m b) -> Stream m a -> Stream m b
702+ [A] concatIterateScan :: Monad m => (b -> a -> m b) -> (b -> m (Maybe (b, Stream m a))) -> b -> Stream m a
703+ [A] concatIterateDfs :: Monad m => (a -> Maybe (Stream m a)) -> Stream m a -> Stream m a
704+ [A] concatIterateBfsRev :: Monad m => (a -> Maybe (Stream m a)) -> Stream m a -> Stream m a
705+ [A] concatIterateBfs :: Monad m => (a -> Maybe (Stream m a)) -> Stream m a -> Stream m a
706+ [A] concatEffect :: Monad m => m (Stream m a) -> Stream m a
707+ [A] concat :: Monad m => Stream m (Stream m a) -> Stream m a
708+ [A] cmpBy :: Monad m => (a -> b -> Ordering) -> Stream m a -> Stream m b -> m Ordering
709+ [A] catRights :: Monad m => Stream m (Either a b) -> Stream m b
710+ [A] catMaybes :: Monad m => Stream m (Maybe a) -> Stream m a
711+ [A] catLefts :: Monad m => Stream m (Either a b) -> Stream m a
712+ [A] catEithers :: Monad m => Stream m (Either a a) -> Stream m a
713+ [A] bracketUnsafe :: MonadCatch m => m b -> (b -> m c) -> (b -> Stream m a) -> Stream m a
714+ [A] bracketIO3 :: (MonadIO m, MonadCatch m) => IO b -> (b -> IO c) -> (b -> IO d) -> (b -> IO e) -> (b -> Stream m a) -> Stream m a
715+ [A] bracketIO :: (MonadIO m, MonadCatch m) => IO b -> (b -> IO c) -> (b -> Stream m a) -> Stream m a
716+ [A] before :: Monad m => m b -> Stream m a -> Stream m a
717+ [A] append :: Monad m => Stream m a -> Stream m a -> Stream m a
718+ [A] any :: Monad m => (a -> Bool) -> Stream m a -> m Bool
719+ [A] all :: Monad m => (a -> Bool) -> Stream m a -> m Bool
720+ [A] afterUnsafe :: Monad m => m b -> Stream m a -> Stream m a
721+ [A] afterIO :: MonadIO m => IO b -> Stream m a -> Stream m a
722+ [A] absTimesWith :: MonadIO m => Double -> Stream m AbsTime
723+ [A] absTimes :: MonadIO m => Stream m AbsTime
724+ [A] (!!) :: Monad m => Stream m a -> Int -> m (Maybe a)
725+[R] Streamly.Internal.Data.Ring.Unboxed
726+[A] Streamly.Internal.Data.Ring.Generic
727+ [A] Ring
728+ [A] [ringMax] :: Ring a -> !Int
729+ [A] [ringHead] :: Ring a -> !Int
730+ [A] [ringArr] :: Ring a -> MutArray a
731+ [A] Ring :: MutArray a -> !Int -> !Int -> Ring a
732+ [A] writeLastN :: MonadIO m => Int -> Fold m a (Ring a)
733+ [A] unsafeInsertRingWith :: Ring a -> a -> IO Int
734+ [A] toStreamWith :: Int -> Ring a -> Stream m a
735+ [A] toMutArray :: MonadIO m => Int -> Int -> Ring a -> m (MutArray a)
736+ [A] seek :: MonadIO m => Int -> Ring a -> m (Ring a)
737+ [A] createRing :: MonadIO m => Int -> m (Ring a)
738+ [A] copyToMutArray :: MonadIO m => Int -> Int -> Ring a -> m (MutArray a)
739+[C] Streamly.Internal.Data.Ring
740+ [C] Ring
741+ [A] [ringStart] :: Ring a -> {-# UNPACK #-} !ForeignPtr a
742+ [R] [ringMax] :: Ring a -> !Int
743+ [R] [ringHead] :: Ring a -> !Int
744+ [A] [ringBound] :: Ring a -> {-# UNPACK #-} !Ptr a
745+ [R] [ringArr] :: Ring a -> MutArray a
746+ [C] Ring
747+ [O] Ring :: MutArray a -> !Int -> !Int -> Ring a
748+ [N] Ring :: {-# UNPACK #-} !ForeignPtr a -> {-# UNPACK #-} !Ptr a -> Ring a
749+ [A] GHC.Show.Show
750+ [A] instance (GHC.Show.Show a, GHC.Show.Show b, GHC.Show.Show c, GHC.Show.Show d) => GHC.Show.Show (Streamly.Internal.Data.Ring.Tuple4' a b c d)
751+ [A] writeN :: Int -> Fold m a (Ring a)
752+ [R] writeLastN :: MonadIO m => Int -> Fold m a (Ring a)
753+ [R] unsafeInsertRingWith :: Ring a -> a -> IO Int
754+ [A] unsafeInsert :: Storable a => Ring a -> Ptr a -> a -> IO (Ptr a)
755+ [A] unsafeFoldRingNM :: forall m a b. (MonadIO m, Storable a) => Int -> Ptr a -> (b -> a -> m b) -> b -> Ring a -> m b
756+ [A] unsafeFoldRingM :: forall m a b. (MonadIO m, Storable a) => Ptr a -> (b -> a -> m b) -> b -> Ring a -> m b
757+ [A] unsafeFoldRingFullM :: forall m a b. (MonadIO m, Storable a) => Ptr a -> (b -> a -> m b) -> b -> Ring a -> m b
758+ [A] unsafeFoldRing :: forall a b. Storable a => Ptr a -> (b -> a -> b) -> b -> Ring a -> b
759+ [A] unsafeEqArrayN :: Ring a -> Ptr a -> Array a -> Int -> Bool
760+ [A] unsafeEqArray :: Ring a -> Ptr a -> Array a -> Bool
761+ [R] toStreamWith :: Int -> Ring a -> Stream m a
762+ [R] toMutArray :: MonadIO m => Int -> Int -> Ring a -> m (MutArray a)
763+ [A] startOf :: Ring a -> Ptr a
764+ [A] slidingWindowWith :: forall m a b. (MonadIO m, Storable a, Unbox a) => Int -> Fold m ((a, Maybe a), m (MutArray a)) b -> Fold m a b
765+ [A] slidingWindow :: forall m a b. (MonadIO m, Storable a, Unbox a) => Int -> Fold m (a, Maybe a) b -> Fold m a b
766+ [A] slide :: Ring a -> a -> m (Ring a)
767+ [R] seek :: MonadIO m => Int -> Ring a -> m (Ring a)
768+ [A] ringsOf :: Int -> Stream m a -> Stream m (MutArray a)
769+ [A] readRev :: Unfold m (MutArray a) a
770+ [A] read :: forall m a. (MonadIO m, Storable a) => Unfold m (Ring a, Ptr a, Int) a
771+ [A] putIndex :: Ring a -> Int -> a -> m ()
772+ [A] newRing :: Int -> m (Ring a)
773+ [A] new :: forall a. Storable a => Int -> IO (Ring a, Ptr a)
774+ [A] moveBy :: forall a. Storable a => Int -> Ring a -> Ptr a -> Ptr a
775+ [A] modifyIndex :: Ring a -> Int -> (a -> (a, b)) -> m b
776+ [A] length :: Ring a -> Int
777+ [A] getIndexUnsafe :: Ring a -> Int -> m a
778+ [A] getIndexRev :: Ring a -> Int -> m a
779+ [A] getIndex :: Ring a -> Int -> m a
780+ [A] fromArray :: MutArray a -> Ring a
781+ [R] createRing :: MonadIO m => Int -> m (Ring a)
782+ [A] castUnsafe :: Ring a -> Ring b
783+ [A] cast :: forall a b. Storable b => Ring a -> Maybe (Ring b)
784+ [A] bytesFree :: Ring a -> Int
785+ [A] byteLength :: Ring a -> Int
786+ [A] byteCapacity :: Ring a -> Int
787+ [A] asBytes :: Ring a -> Ring Word8
788+ [A] advance :: forall a. Storable a => Ring a -> Ptr a -> Ptr a
789+[R] Streamly.Internal.Data.Producer.Type
790+[R] Streamly.Internal.Data.Producer.Source
791+[C] Streamly.Internal.Data.Producer
792+ [A] Source
793+ [A] unread :: [b] -> Source a b -> Source a b
794+ [A] translate :: Functor m => (a -> c) -> (c -> a) -> Producer m c b -> Producer m a b
795+ [A] source :: Maybe a -> Source a b
796+ [A] producer :: Monad m => Producer m a b -> Producer m (Source a b) b
797+ [A] parseManyD :: Monad m => Parser a m b -> Producer m (Source x a) a -> Producer m (Source x a) (Either ParseError b)
798+ [A] parseMany :: Monad m => Parser a m b -> Producer m (Source x a) a -> Producer m (Source x a) (Either ParseError b)
799+ [A] parse :: Monad m => Parser a m b -> Producer m (Source s a) a -> Source s a -> m (Either ParseError b, Source s a)
800+ [A] lmap :: (a -> a) -> Producer m a b -> Producer m a b
801+ [A] isEmpty :: Source a b -> Bool
802+[R] Streamly.Internal.Data.Pipe.Type
803+[C] Streamly.Internal.Data.Pipe
804+ [A] Step
805+ [A] Yield :: a -> s -> Step s a
806+ [A] Continue :: s -> Step s a
807+ [A] PipeState
808+ [A] Produce :: s2 -> PipeState s1 s2
809+ [A] Consume :: s1 -> PipeState s1 s2
810+ [C] Pipe
811+ [A] Pipe :: (s1 -> a -> m (Step (PipeState s1 s2) b)) -> (s2 -> m (Step (PipeState s1 s2) b)) -> s1 -> Pipe m a b
812+[A] Streamly.Internal.Data.ParserK
813+ [A] Step
814+ [A] Partial :: !Int -> (Input a -> m (Step a m r)) -> Step a m r
815+ [A] Error :: !Int -> String -> Step a m r
816+ [A] Done :: !Int -> r -> Step a m r
817+ [A] Continue :: !Int -> (Input a -> m (Step a m r)) -> Step a m r
818+ [A] ParseResult
819+ [A] Success :: !Int -> !b -> ParseResult b
820+ [A] Failure :: !Int -> !String -> ParseResult b
821+ [A] Input
822+ [A] None :: Input a
823+ [A] Chunk :: a -> Input a
824+ [A] ParserK
825+ [A] [runParser] :: ParserK a m b -> forall r. (ParseResult b -> Int -> Input a -> m (Step a m r)) -> Int -> Int -> Input a -> m (Step a m r)
826+ [A] MkParser :: (forall r. (ParseResult b -> Int -> Input a -> m (Step a m r)) -> Int -> Int -> Input a -> m (Step a m r)) -> ParserK a m b
827+ [A] fromPure :: b -> ParserK a m b
828+ [A] fromEffect :: Monad m => m b -> ParserK a m b
829+ [A] die :: String -> ParserK a m b
830+ [A] adaptCG :: Monad m => Parser a m b -> ParserK (Array a) m b
831+ [A] adaptC :: (Monad m, Unbox a) => Parser a m b -> ParserK (Array a) m b
832+ [A] adapt :: Monad m => Parser a m b -> ParserK a m b
833+[R] Streamly.Internal.Data.Parser.ParserK.Type
834+[R] Streamly.Internal.Data.Parser.ParserD.Type
835+[R] Streamly.Internal.Data.Parser.ParserD.Tee
836+[R] Streamly.Internal.Data.Parser.ParserD
837+[C] Streamly.Internal.Data.Parser
838+ [A] Step
839+ [A] Partial :: !Int -> !s -> Step s b
840+ [A] Error :: !String -> Step s b
841+ [A] Done :: !Int -> !b -> Step s b
842+ [A] Continue :: !Int -> !s -> Step s b
843+ [A] Parser
844+ [A] Parser :: (s -> a -> m (Step s b)) -> m (Initial s b) -> (s -> m (Step s b)) -> Parser a m b
845+ [A] Initial
846+ [A] IPartial :: !s -> Initial s b
847+ [A] IError :: !String -> Initial s b
848+ [A] IDone :: !b -> Initial s b
849+ [A] GHC.Show.Show
850+ [A] instance (GHC.Show.Show a, GHC.Show.Show b) => GHC.Show.Show (Streamly.Internal.Data.Parser.Tuple'Fused a b)
851+ [A] ParseError
852+ [A] ParseError :: String -> ParseError
853+ [A] zipWithM :: Monad m => (a -> b -> m c) -> Stream m a -> Fold m c x -> Parser b m x
854+ [A] zip :: Monad m => Stream m a -> Fold m (a, b) x -> Parser b m x
855+ [A] wordWithQuotes :: (Monad m, Eq a) => Bool -> (a -> a -> Maybe a) -> a -> (a -> Maybe a) -> (a -> Bool) -> Fold m a b -> Parser a m b
856+ [A] wordProcessQuotes :: (Monad m, Eq a) => a -> (a -> Maybe a) -> (a -> Bool) -> Fold m a b -> Parser a m b
857+ [A] wordKeepQuotes :: (Monad m, Eq a) => a -> (a -> Maybe a) -> (a -> Bool) -> Fold m a b -> Parser a m b
858+ [A] wordFramedBy :: Monad m => (a -> Bool) -> (a -> Bool) -> (a -> Bool) -> (a -> Bool) -> Fold m a b -> Parser a m b
859+ [A] wordBy :: Monad m => (a -> Bool) -> Fold m a b -> Parser a m b
860+ [A] toFold :: Monad m => Parser a m b -> Fold m a b
861+ [A] takeWhileP :: Monad m => (a -> Bool) -> Parser a m b -> Parser a m b
862+ [A] takeWhile1 :: Monad m => (a -> Bool) -> Fold m a b -> Parser a m b
863+ [A] takeWhile :: Monad m => (a -> Bool) -> Fold m a b -> Parser a m b
864+ [A] takeStartBy_ :: Monad m => (a -> Bool) -> Fold m a b -> Parser a m b
865+ [A] takeStartBy :: Monad m => (a -> Bool) -> Fold m a b -> Parser a m b
866+ [A] takeP :: Monad m => Int -> Parser a m b -> Parser a m b
867+ [A] takeGE :: Monad m => Int -> Fold m a b -> Parser a m b
868+ [A] takeFramedBy_ :: Monad m => (a -> Bool) -> (a -> Bool) -> Fold m a b -> Parser a m b
869+ [A] takeFramedByGeneric :: Monad m => Maybe (a -> Bool) -> Maybe (a -> Bool) -> Maybe (a -> Bool) -> Fold m a b -> Parser a m b
870+ [A] takeFramedByEsc_ :: Monad m => (a -> Bool) -> (a -> Bool) -> (a -> Bool) -> Fold m a b -> Parser a m b
871+ [A] takeEndBy_ :: (a -> Bool) -> Parser a m b -> Parser a m b
872+ [A] takeEndByEsc :: Monad m => (a -> Bool) -> (a -> Bool) -> Parser a m b -> Parser a m b
873+ [A] takeEndBy :: Monad m => (a -> Bool) -> Parser a m b -> Parser a m b
874+ [A] takeEitherSepBy :: (a -> Bool) -> Fold m (Either a b) c -> Parser a m c
875+ [A] takeEQ :: Monad m => Int -> Fold m a b -> Parser a m b
876+ [A] takeBetween :: Monad m => Int -> Int -> Fold m a b -> Parser a m b
877+ [A] subsequenceBy :: (a -> a -> Bool) -> Stream m a -> Parser a m ()
878+ [A] streamEqBy :: Monad m => (a -> a -> Bool) -> Stream m a -> Parser a m ()
879+ [A] split_ :: Monad m => Parser x m a -> Parser x m b -> Parser x m b
880+ [A] splitWith :: Monad m => (a -> b -> c) -> Parser x m a -> Parser x m b -> Parser x m c
881+ [A] splitSome :: Monad m => Parser a m b -> Fold m b c -> Parser a m c
882+ [A] splitManyPost :: Monad m => Parser a m b -> Fold m b c -> Parser a m c
883+ [A] splitMany :: Monad m => Parser a m b -> Fold m b c -> Parser a m c
884+ [A] spanByRolling :: Monad m => (a -> a -> Bool) -> Fold m a b -> Fold m a c -> Parser a m (b, c)
885+ [A] spanBy :: Monad m => (a -> a -> Bool) -> Fold m a b -> Fold m a c -> Parser a m (b, c)
886+ [A] span :: Monad m => (a -> Bool) -> Fold m a b -> Fold m a c -> Parser a m (b, c)
887+ [A] some :: Monad m => Parser a m b -> Fold m b c -> Parser a m c
888+ [A] sequence :: Monad m => Stream m (Parser a m b) -> Fold m b c -> Parser a m c
889+ [A] sepByAll :: Monad m => Parser a m b -> Parser a m x -> Fold m b c -> Parser a m c
890+ [A] sepBy1 :: Monad m => Parser a m b -> Parser a m x -> Fold m b c -> Parser a m c
891+ [A] sepBy :: Monad m => Parser a m b -> Parser a m x -> Fold m b c -> Parser a m c
892+ [A] satisfy :: Monad m => (a -> Bool) -> Parser a m a
893+ [A] sampleFromthen :: Monad m => Int -> Int -> Fold m a b -> Parser a m b
894+ [A] roundRobin :: t (Parser a m b) -> Fold m b c -> Parser a m c
895+ [A] rmapM :: Monad m => (b -> m c) -> Parser a m b -> Parser a m c
896+ [A] retryMaxTotal :: Int -> Parser a m b -> Fold m b c -> Parser a m c
897+ [A] retryMaxSuccessive :: Int -> Parser a m b -> Fold m b c -> Parser a m c
898+ [A] retry :: Parser a m b -> Parser a m b
899+ [A] postscan :: Fold m a b -> Parser b m c -> Parser a m c
900+ [A] peek :: Monad m => Parser a m a
901+ [A] oneOf :: (Monad m, Eq a, Foldable f) => f a -> Parser a m a
902+ [A] oneNotEq :: (Monad m, Eq a) => a -> Parser a m a
903+ [A] oneEq :: (Monad m, Eq a) => a -> Parser a m a
904+ [A] one :: Monad m => Parser a m a
905+ [A] noneOf :: (Monad m, Eq a, Foldable f) => f a -> Parser a m a
906+ [A] noErrorUnsafeSplit_ :: Monad m => Parser x m a -> Parser x m b -> Parser x m b
907+ [A] noErrorUnsafeSplitWith :: Monad m => (a -> b -> c) -> Parser x m a -> Parser x m b -> Parser x m c
908+ [A] noErrorUnsafeConcatMap :: Monad m => (b -> Parser a m c) -> Parser a m b -> Parser a m c
909+ [A] maybe :: Monad m => (a -> Maybe b) -> Parser a m b
910+ [A] manyTillP :: Parser a m b -> Parser a m x -> Parser b m c -> Parser a m c
911+ [A] manyTill :: Monad m => Parser a m b -> Parser a m x -> Fold m b c -> Parser a m c
912+ [A] manyThen :: Parser a m b -> Parser a m x -> Fold m b c -> Parser a m c
913+ [A] manyP :: Parser a m b -> Parser b m c -> Parser a m c
914+ [A] many :: Monad m => Parser a m b -> Fold m b c -> Parser a m c
915+ [A] makeIndexFilter :: (Fold m (s, a) b -> Parser a m b) -> (((s, a) -> Bool) -> Fold m (s, a) b -> Fold m (s, a) b) -> ((s, a) -> Bool) -> Fold m a b -> Parser a m b
916+ [A] lookAhead :: Monad m => Parser a m b -> Parser a m b
917+ [A] lmapM :: Monad m => (a -> m b) -> Parser b m r -> Parser a m r
918+ [A] lmap :: (a -> b) -> Parser b m r -> Parser a m r
919+ [A] listEqBy :: Monad m => (a -> a -> Bool) -> [a] -> Parser a m [a]
920+ [A] listEq :: (Monad m, Eq a) => [a] -> Parser a m [a]
921+ [A] indexed :: forall m a b. Monad m => Fold m (Int, a) b -> Parser a m b
922+ [A] groupByRollingEither :: Monad m => (a -> a -> Bool) -> Fold m a b -> Fold m a c -> Parser a m (Either b c)
923+ [A] groupByRolling :: Monad m => (a -> a -> Bool) -> Fold m a b -> Parser a m b
924+ [A] groupBy :: Monad m => (a -> a -> Bool) -> Fold m a b -> Parser a m b
925+ [A] fromPure :: Monad m => b -> Parser a m b
926+ [A] fromFoldMaybe :: Monad m => String -> Fold m a (Maybe b) -> Parser a m b
927+ [A] fromFold :: Monad m => Fold m a b -> Parser a m b
928+ [A] fromEffect :: Monad m => m b -> Parser a m b
929+ [A] filter :: Monad m => (a -> Bool) -> Parser a m b -> Parser a m b
930+ [A] extractStep :: Monad m => (s -> m (Step s1 b)) -> Step s b -> m (Step s1 b)
931+ [A] eof :: Monad m => Parser a m ()
932+ [A] either :: Monad m => (a -> Either String b) -> Parser a m b
933+ [A] dropWhile :: Monad m => (a -> Bool) -> Parser a m ()
934+ [A] dieM :: Monad m => m String -> Parser a m b
935+ [A] die :: Monad m => String -> Parser a m b
936+ [A] deintercalateAll :: Monad m => Parser a m x -> Parser a m y -> Fold m (Either x y) z -> Parser a m z
937+ [A] deintercalate1 :: Monad m => Parser a m x -> Parser a m y -> Fold m (Either x y) z -> Parser a m z
938+ [A] deintercalate :: Monad m => Parser a m x -> Parser a m y -> Fold m (Either x y) z -> Parser a m z
939+ [A] countBetween :: Int -> Int -> Parser a m b -> Fold m b c -> Parser a m c
940+ [A] count :: Int -> Parser a m b -> Fold m b c -> Parser a m c
941+ [A] concatMap :: Monad m => (b -> Parser a m c) -> Parser a m b -> Parser a m c
942+ [A] blockWithQuotes :: (Monad m, Eq a) => (a -> Bool) -> (a -> Bool) -> a -> a -> Fold m a b -> Parser a m b
943+ [A] bimapOverrideCount :: Int -> (s -> s1) -> (b -> b1) -> Step s b -> Step s1 b1
944+ [A] alt :: Monad m => Parser x m a -> Parser x m a -> Parser x m a
945+[A] Streamly.Internal.Data.MutByteArray
946+ [A] class Unbox a
947+ [A] class SizeOfRep (f :: Type -> Type)
948+ [A] class Serialize a
949+ [A] TypeOfType
950+ [A] UnitType :: Name -> TypeOfType
951+ [A] TheType :: SimpleDataCon -> TypeOfType
952+ [A] MultiType :: [SimpleDataCon] -> TypeOfType
953+ [A] SimpleDataCon
954+ [A] SimpleDataCon :: Name -> [Field] -> SimpleDataCon
955+ [A] SerializeConfig
956+ [A] [cfgRecordSyntaxWithHeader] :: SerializeConfig -> Bool
957+ [A] [cfgInlineSize] :: SerializeConfig -> Maybe Inline
958+ [A] [cfgInlineSerialize] :: SerializeConfig -> Maybe Inline
959+ [A] [cfgInlineDeserialize] :: SerializeConfig -> Maybe Inline
960+ [A] [cfgConstructorTagAsString] :: SerializeConfig -> Bool
961+ [A] SerializeConfig :: Maybe Inline -> Maybe Inline -> Maybe Inline -> Bool -> Bool -> SerializeConfig
962+ [A] PinnedState
963+ [A] Unpinned :: PinnedState
964+ [A] Pinned :: PinnedState
965+ [A] MutByteArray
966+ [A] MutByteArray :: MutableByteArray# RealWorld -> MutByteArray
967+ [A] DataType
968+ [A] [dtTvs] :: DataType -> [Name]
969+ [A] [dtName] :: DataType -> Name
970+ [A] [dtCxt] :: DataType -> Cxt
971+ [A] [dtCons] :: DataType -> [DataCon]
972+ [A] DataType :: Name -> [Name] -> Cxt -> [DataCon] -> DataType
973+ [A] DataCon
974+ [A] [dcTvs] :: DataCon -> [Name]
975+ [A] [dcName] :: DataCon -> Name
976+ [A] [dcFields] :: DataCon -> [(Maybe Name, Type)]
977+ [A] [dcCxt] :: DataCon -> Cxt
978+ [A] DataCon :: Name -> [Name] -> Cxt -> [(Maybe Name, Type)] -> DataCon
979+ [A] BoundedPtr
980+ [A] BoundedPtr :: MutByteArray -> Int -> Int -> BoundedPtr
981+ [A] Streamly.Internal.Data.Serialize.Type.Serialize
982+ [A] instance Streamly.Internal.Data.Serialize.Type.Serialize Streamly.Internal.Data.MutByteArray.LiftedInteger
983+ [A] instance Streamly.Internal.Data.Serialize.Type.Serialize GHC.Num.Integer.Integer
984+ [A] instance Streamly.Internal.Data.Serialize.Type.Serialize a => Streamly.Internal.Data.Serialize.Type.Serialize (GHC.Maybe.Maybe a)
985+ [A] instance Streamly.Internal.Data.Serialize.Type.Serialize (Data.Proxy.Proxy a)
986+ [A] instance (Streamly.Internal.Data.Serialize.Type.Serialize a, Streamly.Internal.Data.Serialize.Type.Serialize b) => Streamly.Internal.Data.Serialize.Type.Serialize (Data.Either.Either a b)
987+ [A] Peeker
988+ [A] Peeker :: Builder BoundedPtr IO a -> Peeker a
989+ [A] type Field = (Maybe Name, Type)
990+ [A] xorCmp :: [Word8] -> Name -> Name -> Q Exp
991+ [A] wListToString :: [Word8] -> String
992+ [A] w8_int :: Word8 -> Int
993+ [A] w32_int :: Word32 -> Int
994+ [A] unpin :: MutByteArray -> IO MutByteArray
995+ [A] typeOfType :: Type -> [DataCon] -> TypeOfType
996+ [A] skipByte :: Peeker ()
997+ [A] sizeOfRep :: SizeOfRep f => f x -> Int
998+ [A] sizeOfMutableByteArray :: MutByteArray -> IO Int
999+ [A] sizeOf :: (Unbox a, SizeOfRep (Rep a)) => Proxy a -> Int
1000+ [A] simplifyDataCon :: DataCon -> SimpleDataCon
1001+ [A] serializeW8List :: Name -> Name -> [Word8] -> Q Exp
1002+ [A] serializeConfig :: SerializeConfig
1003+ [A] serializeAt :: Serialize a => Int -> MutByteArray -> a -> IO Int
1004+ [A] runPeeker :: Peeker a -> BoundedPtr -> IO a
1005+ [A] reifyDataType :: Name -> Q DataType
1006+ [A] readUnsafe :: Unbox a => Peeker a
1007+ [A] read :: Unbox a => Peeker a
1008+ [A] putSliceUnsafe :: MonadIO m => MutByteArray -> Int -> MutByteArray -> Int -> Int -> m ()
1009+ [A] pokeRep :: PokeRep f => f a -> BoundedPtr -> IO BoundedPtr
1010+ [A] pokeBoundedPtrUnsafe :: forall a. Unbox a => a -> BoundedPtr -> IO BoundedPtr
1011+ [A] pokeBoundedPtr :: forall a. Unbox a => a -> BoundedPtr -> IO BoundedPtr
1012+ [A] pokeAt :: (Unbox a, Generic a, PokeRep (Rep a)) => Int -> MutByteArray -> a -> IO ()
1013+ [A] pinnedNewAlignedBytes :: Int -> Int -> IO MutByteArray
1014+ [A] pinnedNew :: Int -> IO MutByteArray
1015+ [A] pinnedCloneSliceUnsafe :: MonadIO m => Int -> Int -> MutByteArray -> m MutByteArray
1016+ [A] pin :: MutByteArray -> IO MutByteArray
1017+ [A] peekRep :: PeekRep f => Peeker (f x)
1018+ [A] peekAt :: (Unbox a, Generic a, PeekRep (Rep a)) => Int -> MutByteArray -> IO a
1019+ [A] openConstructor :: Name -> Int -> Q Pat
1020+ [A] nil :: MutByteArray
1021+ [A] newBytesAs :: PinnedState -> Int -> IO MutByteArray
1022+ [A] new :: Int -> IO MutByteArray
1023+ [A] mkSerializeExprFields :: Name -> [Field] -> Q Exp
1024+ [A] mkRecSizeOfExpr :: SimpleDataCon -> Q Exp
1025+ [A] mkRecSerializeExpr :: Name -> SimpleDataCon -> Q Exp
1026+ [A] mkRecDeserializeExpr :: Name -> Name -> Name -> SimpleDataCon -> Q Exp
1027+ [A] mkFieldName :: Int -> Name
1028+ [A] mkDeserializeKeysDec :: Name -> Name -> SimpleDataCon -> Q [Dec]
1029+ [A] mkDeserializeExprOne :: Name -> SimpleDataCon -> Q Exp
1030+ [A] matchConstructor :: Name -> Int -> Q Exp -> Q Match
1031+ [A] makeN :: Int -> Name
1032+ [A] makeI :: Int -> Name
1033+ [A] makeA :: Int -> Name
1034+ [A] litProxy :: Unbox a => Proxy a -> Q Exp
1035+ [A] litIntegral :: Integral a => a -> Q Exp
1036+ [A] isUnitType :: [DataCon] -> Bool
1037+ [A] isRecordSyntax :: SimpleDataCon -> Bool
1038+ [A] isPinned :: MutByteArray -> Bool
1039+ [A] int_w8 :: Int -> Word8
1040+ [A] int_w32 :: Int -> Word32
1041+ [A] inlineSerializeAt :: Maybe Inline -> SerializeConfig -> SerializeConfig
1042+ [A] inlineDeserializeAt :: Maybe Inline -> SerializeConfig -> SerializeConfig
1043+ [A] inlineAddSizeTo :: Maybe Inline -> SerializeConfig -> SerializeConfig
1044+ [A] getMutableByteArray# :: MutByteArray -> MutableByteArray# RealWorld
1045+ [A] genericSizeOf :: forall a. SizeOfRep (Rep a) => Proxy a -> Int
1046+ [A] genericPokeByteIndex :: (Generic a, PokeRep (Rep a)) => MutByteArray -> Int -> a -> IO ()
1047+ [A] genericPeekByteIndex :: (Generic a, PeekRep (Rep a)) => MutByteArray -> Int -> IO a
1048+ [A] errorUnsupported :: String -> a
1049+ [A] errorUnimplemented :: a
1050+ [A] encodeRecordFields :: Bool -> SerializeConfig -> SerializeConfig
1051+ [A] encodeConstrNames :: Bool -> SerializeConfig -> SerializeConfig
1052+ [A] deserializeAt :: Serialize a => Int -> MutByteArray -> Int -> IO (Int, a)
1053+ [A] deriveUnbox :: Q [Dec] -> Q [Dec]
1054+ [A] deriveSerializeWith :: (SerializeConfig -> SerializeConfig) -> Q [Dec] -> Q [Dec]
1055+ [A] deriveSerialize :: Q [Dec] -> Q [Dec]
1056+ [A] conUpdateFuncDec :: Name -> [Field] -> Q [Dec]
1057+ [A] cloneSliceUnsafeAs :: MonadIO m => PinnedState -> Int -> Int -> MutByteArray -> m MutByteArray
1058+ [A] cloneSliceUnsafe :: MonadIO m => Int -> Int -> MutByteArray -> m MutByteArray
1059+ [A] c2w :: Char -> Word8
1060+ [A] asPtrUnsafe :: MonadIO m => MutByteArray -> (Ptr a -> m b) -> m b
1061+ [A] addSizeTo :: Serialize a => Int -> a -> Int
1062+ [A] _x :: Name
1063+ [A] _val :: Name
1064+ [A] _tag :: Name
1065+ [A] _initialOffset :: Name
1066+ [A] _endOffset :: Name
1067+ [A] _arr :: Name
1068+ [A] _acc :: Name
1069+[A] Streamly.Internal.Data.MutArray.Stream
1070+ [A] SpliceState
1071+ [A] SpliceYielding :: arr -> SpliceState s arr -> SpliceState s arr
1072+ [A] SpliceInitial :: s -> SpliceState s arr
1073+ [A] SpliceFinish :: SpliceState s arr
1074+ [A] SpliceBuffering :: s -> arr -> SpliceState s arr
1075+ [A] writeChunks :: (MonadIO m, Unbox a) => Int -> Fold m a (StreamK n (MutArray a))
1076+ [A] splitOn :: (MonadIO m, Unbox a) => (a -> Bool) -> MutArray a -> Stream m (MutArray a)
1077+ [A] pinnedChunksOf :: forall m a. (MonadIO m, Unbox a) => Int -> Stream m a -> Stream m (MutArray a)
1078+ [A] packArraysChunksOf :: (MonadIO m, Unbox a) => Int -> Stream m (MutArray a) -> Stream m (MutArray a)
1079+ [A] lpackArraysChunksOf :: (MonadIO m, Unbox a) => Int -> Fold m (MutArray a) () -> Fold m (MutArray a) ()
1080+ [A] fromArrayStreamK :: (Unbox a, MonadIO m) => StreamK m (MutArray a) -> m (MutArray a)
1081+ [A] flattenArraysRev :: forall m a. (MonadIO m, Unbox a) => Stream m (MutArray a) -> Stream m a
1082+ [A] flattenArrays :: forall m a. (MonadIO m, Unbox a) => Stream m (MutArray a) -> Stream m a
1083+ [A] compactLE :: (MonadIO m, Unbox a) => Int -> Stream m (MutArray a) -> Stream m (Either ParseError (MutArray a))
1084+ [A] compactGE :: (MonadIO m, Unbox a) => Int -> Stream m (MutArray a) -> Stream m (MutArray a)
1085+ [A] compactEQ :: Int -> Stream m (MutArray a) -> Stream m (MutArray a)
1086+ [A] compact :: (MonadIO m, Unbox a) => Int -> Stream m (MutArray a) -> Stream m (MutArray a)
1087+ [A] chunksOf :: forall m a. (MonadIO m, Unbox a) => Int -> Stream m a -> Stream m (MutArray a)
1088+[A] Streamly.Internal.Data.MutArray.Generic
1089+ [A] MutArray
1090+ [A] [arrTrueLen] :: MutArray a -> {-# UNPACK #-} !Int
1091+ [A] [arrStart] :: MutArray a -> {-# UNPACK #-} !Int
1092+ [A] [arrLen] :: MutArray a -> {-# UNPACK #-} !Int
1093+ [A] [arrContents#] :: MutArray a -> MutableArray# RealWorld a
1094+ [A] MutArray :: MutableArray# RealWorld a -> {-# UNPACK #-} !Int -> {-# UNPACK #-} !Int -> {-# UNPACK #-} !Int -> MutArray a
1095+ [A] writeWith :: MonadIO m => Int -> Fold m a (MutArray a)
1096+ [A] writeNUnsafe :: MonadIO m => Int -> Fold m a (MutArray a)
1097+ [A] writeN :: MonadIO m => Int -> Fold m a (MutArray a)
1098+ [A] write :: MonadIO m => Fold m a (MutArray a)
1099+ [A] uninit :: MonadIO m => MutArray a -> Int -> m (MutArray a)
1100+ [A] toStreamK :: MonadIO m => MutArray a -> StreamK m a
1101+ [A] toList :: MonadIO m => MutArray a -> m [a]
1102+ [A] strip :: MonadIO m => (a -> Bool) -> MutArray a -> m (MutArray a)
1103+ [A] snocWith :: MonadIO m => (Int -> Int) -> MutArray a -> a -> m (MutArray a)
1104+ [A] snocUnsafe :: MonadIO m => MutArray a -> a -> m (MutArray a)
1105+ [A] snoc :: MonadIO m => MutArray a -> a -> m (MutArray a)
1106+ [A] realloc :: MonadIO m => Int -> MutArray a -> m (MutArray a)
1107+ [A] reader :: MonadIO m => Unfold m (MutArray a) a
1108+ [A] readRev :: MonadIO m => MutArray a -> Stream m a
1109+ [A] read :: MonadIO m => MutArray a -> Stream m a
1110+ [A] putSliceUnsafe :: MonadIO m => MutArray a -> Int -> MutArray a -> Int -> Int -> m ()
1111+ [A] putIndices :: MonadIO m => MutArray a -> Fold m (Int, a) ()
1112+ [A] putIndexUnsafe :: forall m a. MonadIO m => Int -> MutArray a -> a -> m ()
1113+ [A] putIndex :: MonadIO m => Int -> MutArray a -> a -> m ()
1114+ [A] producerWith :: Monad m => (forall b. IO b -> m b) -> Producer m (MutArray a) a
1115+ [A] producer :: MonadIO m => Producer m (MutArray a) a
1116+ [A] nil :: MonadIO m => m (MutArray a)
1117+ [A] new :: MonadIO m => Int -> m (MutArray a)
1118+ [A] modifyIndexUnsafe :: MonadIO m => Int -> MutArray a -> (a -> (a, b)) -> m b
1119+ [A] modifyIndex :: MonadIO m => Int -> MutArray a -> (a -> (a, b)) -> m b
1120+ [A] length :: MutArray a -> Int
1121+ [A] getSliceUnsafe :: Int -> Int -> MutArray a -> MutArray a
1122+ [A] getSlice :: Int -> Int -> MutArray a -> MutArray a
1123+ [A] getIndexUnsafeWith :: MonadIO m => MutableArray# RealWorld a -> Int -> m a
1124+ [A] getIndexUnsafe :: MonadIO m => Int -> MutArray a -> m a
1125+ [A] getIndex :: MonadIO m => Int -> MutArray a -> m (Maybe a)
1126+ [A] fromStreamN :: MonadIO m => Int -> Stream m a -> m (MutArray a)
1127+ [A] fromStream :: MonadIO m => Stream m a -> m (MutArray a)
1128+ [A] fromPureStream :: MonadIO m => Stream Identity a -> m (MutArray a)
1129+ [A] fromListN :: MonadIO m => Int -> [a] -> m (MutArray a)
1130+ [A] fromList :: MonadIO m => [a] -> m (MutArray a)
1131+ [A] eq :: (MonadIO m, Eq a) => MutArray a -> MutArray a -> m Bool
1132+ [A] cmp :: (MonadIO m, Ord a) => MutArray a -> MutArray a -> m Ordering
1133+ [A] clone :: MonadIO m => MutArray a -> m (MutArray a)
1134+ [A] chunksOf :: forall m a. MonadIO m => Int -> Stream m a -> Stream m (MutArray a)
1135+[A] Streamly.Internal.Data.MutArray
1136+ [A] MutByteArray
1137+ [A] MutArray
1138+ [A] [arrStart] :: MutArray a -> {-# UNPACK #-} !Int
1139+ [A] [arrEnd] :: MutArray a -> {-# UNPACK #-} !Int
1140+ [A] [arrContents] :: MutArray a -> {-# UNPACK #-} !MutByteArray
1141+ [A] [arrBound] :: MutArray a -> {-# UNPACK #-} !Int
1142+ [A] MutArray :: {-# UNPACK #-} !MutByteArray -> {-# UNPACK #-} !Int -> {-# UNPACK #-} !Int -> {-# UNPACK #-} !Int -> MutArray a
1143+ [A] IORef
1144+ [A] ArrayUnsafe
1145+ [A] ArrayUnsafe :: {-# UNPACK #-} !MutByteArray -> {-# UNPACK #-} !Int -> {-# UNPACK #-} !Int -> ArrayUnsafe a
1146+ [A] writeWith :: forall m a. (MonadIO m, Unbox a) => Int -> Fold m a (MutArray a)
1147+ [A] writeRevN :: forall m a. (MonadIO m, Unbox a) => Int -> Fold m a (MutArray a)
1148+ [A] writeNWithUnsafe :: forall m a. (MonadIO m, Unbox a) => (Int -> m (MutArray a)) -> Int -> Fold m a (MutArray a)
1149+ [A] writeNWith :: forall m a. (MonadIO m, Unbox a) => (Int -> m (MutArray a)) -> Int -> Fold m a (MutArray a)
1150+ [A] writeNUnsafe :: forall m a. (MonadIO m, Unbox a) => Int -> Fold m a (MutArray a)
1151+ [A] writeN :: forall m a. (MonadIO m, Unbox a) => Int -> Fold m a (MutArray a)
1152+ [A] writeIORef :: Unbox a => IORef a -> a -> IO ()
1153+ [A] writeChunks :: (MonadIO m, Unbox a) => Int -> Fold m a (StreamK n (MutArray a))
1154+ [A] writeAppendWith :: forall m a. (MonadIO m, Unbox a) => (Int -> Int) -> m (MutArray a) -> Fold m a (MutArray a)
1155+ [A] writeAppendNUnsafe :: forall m a. (MonadIO m, Unbox a) => Int -> m (MutArray a) -> Fold m a (MutArray a)
1156+ [A] writeAppendN :: forall m a. (MonadIO m, Unbox a) => Int -> m (MutArray a) -> Fold m a (MutArray a)
1157+ [A] writeAppend :: forall m a. (MonadIO m, Unbox a) => m (MutArray a) -> Fold m a (MutArray a)
1158+ [A] write :: forall m a. (MonadIO m, Unbox a) => Fold m a (MutArray a)
1159+ [A] unsafeSwapIndices :: forall m a. (MonadIO m, Unbox a) => Int -> Int -> MutArray a -> m ()
1160+ [A] unpin :: MutArray a -> IO (MutArray a)
1161+ [A] toStreamKWith :: forall m a. (Monad m, Unbox a) => (forall b. IO b -> m b) -> MutArray a -> StreamK m a
1162+ [A] toStreamKRevWith :: forall m a. (Monad m, Unbox a) => (forall b. IO b -> m b) -> MutArray a -> StreamK m a
1163+ [A] toStreamKRev :: forall m a. (MonadIO m, Unbox a) => MutArray a -> StreamK m a
1164+ [A] toStreamK :: forall m a. (MonadIO m, Unbox a) => MutArray a -> StreamK m a
1165+ [A] toStreamDWith :: forall m a. (Monad m, Unbox a) => (forall b. IO b -> m b) -> MutArray a -> Stream m a
1166+ [A] toStreamDRevWith :: forall m a. (Monad m, Unbox a) => (forall b. IO b -> m b) -> MutArray a -> Stream m a
1167+ [A] toList :: forall m a. (MonadIO m, Unbox a) => MutArray a -> m [a]
1168+ [A] swapIndices :: forall m a. (MonadIO m, Unbox a) => Int -> Int -> MutArray a -> m ()
1169+ [A] strip :: forall a m. (Unbox a, MonadIO m) => (a -> Bool) -> MutArray a -> m (MutArray a)
1170+ [A] splitOn :: (MonadIO m, Unbox a) => (a -> Bool) -> MutArray a -> Stream m (MutArray a)
1171+ [A] splitAt :: forall a. Unbox a => Int -> MutArray a -> (MutArray a, MutArray a)
1172+ [A] spliceWith :: forall m a. (MonadIO m, Unbox a) => (Int -> Int -> Int) -> MutArray a -> MutArray a -> m (MutArray a)
1173+ [A] spliceUnsafe :: MonadIO m => MutArray a -> MutArray a -> m (MutArray a)
1174+ [A] spliceExp :: (MonadIO m, Unbox a) => MutArray a -> MutArray a -> m (MutArray a)
1175+ [A] spliceCopy :: forall m a. MonadIO m => MutArray a -> MutArray a -> m (MutArray a)
1176+ [A] splice :: (MonadIO m, Unbox a) => MutArray a -> MutArray a -> m (MutArray a)
1177+ [A] snocWith :: forall m a. (MonadIO m, Unbox a) => (Int -> Int) -> MutArray a -> a -> m (MutArray a)
1178+ [A] snocUnsafe :: forall m a. (MonadIO m, Unbox a) => MutArray a -> a -> m (MutArray a)
1179+ [A] snocMay :: forall m a. (MonadIO m, Unbox a) => MutArray a -> a -> m (Maybe (MutArray a))
1180+ [A] snocLinear :: forall m a. (MonadIO m, Unbox a) => MutArray a -> a -> m (MutArray a)
1181+ [A] snoc :: forall m a. (MonadIO m, Unbox a) => MutArray a -> a -> m (MutArray a)
1182+ [A] shuffleBy :: (a -> a -> m Bool) -> MutArray a -> MutArray a -> m ()
1183+ [A] roundUpToPower2 :: Int -> Int
1184+ [A] rightSize :: forall m a. (MonadIO m, Unbox a) => MutArray a -> m (MutArray a)
1185+ [A] reverse :: forall m a. (MonadIO m, Unbox a) => MutArray a -> m ()
1186+ [A] resizeExp :: forall m a. (MonadIO m, Unbox a) => Int -> MutArray a -> m (MutArray a)
1187+ [A] resize :: forall m a. (MonadIO m, Unbox a) => Int -> MutArray a -> m (MutArray a)
1188+ [A] realloc :: forall m a. (MonadIO m, Unbox a) => Int -> MutArray a -> m (MutArray a)
1189+ [A] readerRevWith :: forall m a. (Monad m, Unbox a) => (forall b. IO b -> m b) -> Unfold m (MutArray a) a
1190+ [A] readerRev :: forall m a. (MonadIO m, Unbox a) => Unfold m (MutArray a) a
1191+ [A] reader :: forall m a. (MonadIO m, Unbox a) => Unfold m (MutArray a) a
1192+ [A] readRev :: forall m a. (MonadIO m, Unbox a) => MutArray a -> Stream m a
1193+ [A] readIORef :: Unbox a => IORef a -> IO a
1194+ [A] read :: forall m a. (MonadIO m, Unbox a) => MutArray a -> Stream m a
1195+ [A] putIndices :: forall m a. (MonadIO m, Unbox a) => MutArray a -> Fold m (Int, a) ()
1196+ [A] putIndexUnsafe :: forall m a. (MonadIO m, Unbox a) => Int -> MutArray a -> a -> m ()
1197+ [A] putIndex :: forall m a. (MonadIO m, Unbox a) => Int -> MutArray a -> a -> m ()
1198+ [A] producerWith :: forall m a. (Monad m, Unbox a) => (forall b. IO b -> m b) -> Producer m (MutArray a) a
1199+ [A] producer :: forall m a. (MonadIO m, Unbox a) => Producer m (MutArray a) a
1200+ [A] pollIntIORef :: (MonadIO m, Unbox a) => IORef a -> Stream m a
1201+ [A] pinnedWriteNUnsafe :: forall m a. (MonadIO m, Unbox a) => Int -> Fold m a (MutArray a)
1202+ [A] pinnedWriteNAligned :: forall m a. (MonadIO m, Unbox a) => Int -> Int -> Fold m a (MutArray a)
1203+ [A] pinnedWriteN :: forall m a. (MonadIO m, Unbox a) => Int -> Fold m a (MutArray a)
1204+ [A] pinnedWrite :: forall m a. (MonadIO m, Unbox a) => Fold m a (MutArray a)
1205+ [A] pinnedNewBytes :: MonadIO m => Int -> m (MutArray a)
1206+ [A] pinnedNewAligned :: (MonadIO m, Unbox a) => Int -> Int -> m (MutArray a)
1207+ [A] pinnedNew :: forall m a. (MonadIO m, Unbox a) => Int -> m (MutArray a)
1208+ [A] pinnedFromListN :: (MonadIO m, Unbox a) => Int -> [a] -> m (MutArray a)
1209+ [A] pinnedFromList :: (MonadIO m, Unbox a) => [a] -> m (MutArray a)
1210+ [A] pinnedClone :: MonadIO m => MutArray a -> m (MutArray a)
1211+ [A] pinnedChunksOf :: forall m a. (MonadIO m, Unbox a) => Int -> Stream m a -> Stream m (MutArray a)
1212+ [A] pin :: MutArray a -> IO (MutArray a)
1213+ [A] permute :: MutArray a -> m Bool
1214+ [A] partitionBy :: forall m a. (MonadIO m, Unbox a) => (a -> Bool) -> MutArray a -> m (MutArray a, MutArray a)
1215+ [A] nil :: MutArray a
1216+ [A] newIORef :: forall a. Unbox a => a -> IO (IORef a)
1217+ [A] newArrayWith :: forall m a. (MonadIO m, Unbox a) => (Int -> Int -> m MutByteArray) -> Int -> Int -> m (MutArray a)
1218+ [A] new :: (MonadIO m, Unbox a) => Int -> m (MutArray a)
1219+ [A] modifyIndices :: forall m a. (MonadIO m, Unbox a) => MutArray a -> (Int -> a -> a) -> Fold m Int ()
1220+ [A] modifyIndexUnsafe :: forall m a b. (MonadIO m, Unbox a) => Int -> MutArray a -> (a -> (a, b)) -> m b
1221+ [A] modifyIndex :: forall m a b. (MonadIO m, Unbox a) => Int -> MutArray a -> (a -> (a, b)) -> m b
1222+ [A] modifyIORef' :: Unbox a => IORef a -> (a -> a) -> IO ()
1223+ [A] modify :: forall m a. (MonadIO m, Unbox a) => MutArray a -> (a -> a) -> m ()
1224+ [A] mergeBy :: Int -> (MutArray a -> MutArray a -> m ()) -> MutArray a -> m ()
1225+ [A] memcpy :: Ptr Word8 -> Ptr Word8 -> Int -> IO ()
1226+ [A] memcmp :: Ptr Word8 -> Ptr Word8 -> Int -> IO Bool
1227+ [A] length :: forall a. Unbox a => MutArray a -> Int
1228+ [A] isPinned :: MutArray a -> Bool
1229+ [A] getSlicesFromLen :: forall m a. (Monad m, Unbox a) => Int -> Int -> Unfold m (MutArray a) (MutArray a)
1230+ [A] getSliceUnsafe :: forall a. Unbox a => Int -> Int -> MutArray a -> MutArray a
1231+ [A] getSlice :: forall a. Unbox a => Int -> Int -> MutArray a -> MutArray a
1232+ [A] getIndicesD :: (Monad m, Unbox a) => (forall b. IO b -> m b) -> Stream m Int -> Unfold m (MutArray a) a
1233+ [A] getIndices :: (MonadIO m, Unbox a) => Stream m Int -> Unfold m (MutArray a) a
1234+ [A] getIndexUnsafe :: forall m a. (MonadIO m, Unbox a) => Int -> MutArray a -> m a
1235+ [A] getIndexRev :: forall m a. (MonadIO m, Unbox a) => Int -> MutArray a -> m a
1236+ [A] getIndex :: forall m a. (MonadIO m, Unbox a) => Int -> MutArray a -> m (Maybe a)
1237+ [A] genSlicesFromLen :: forall m a. (Monad m, Unbox a) => Int -> Int -> Unfold m (MutArray a) (Int, Int)
1238+ [A] fromStreamDN :: forall m a. (MonadIO m, Unbox a) => Int -> Stream m a -> m (MutArray a)
1239+ [A] fromStreamD :: (MonadIO m, Unbox a) => Stream m a -> m (MutArray a)
1240+ [A] fromStream :: (MonadIO m, Unbox a) => Stream m a -> m (MutArray a)
1241+ [A] fromPureStream :: (MonadIO m, Unbox a) => Stream Identity a -> m (MutArray a)
1242+ [A] fromListRevN :: (MonadIO m, Unbox a) => Int -> [a] -> m (MutArray a)
1243+ [A] fromListRev :: (MonadIO m, Unbox a) => [a] -> m (MutArray a)
1244+ [A] fromListN :: (MonadIO m, Unbox a) => Int -> [a] -> m (MutArray a)
1245+ [A] fromList :: (MonadIO m, Unbox a) => [a] -> m (MutArray a)
1246+ [A] fromArrayStreamK :: (Unbox a, MonadIO m) => StreamK m (MutArray a) -> m (MutArray a)
1247+ [A] foldr :: (MonadIO m, Unbox a) => (a -> b -> b) -> b -> MutArray a -> m b
1248+ [A] foldl' :: (MonadIO m, Unbox a) => (b -> a -> b) -> b -> MutArray a -> m b
1249+ [A] flattenArraysRev :: forall m a. (MonadIO m, Unbox a) => Stream m (MutArray a) -> Stream m a
1250+ [A] flattenArrays :: forall m a. (MonadIO m, Unbox a) => Stream m (MutArray a) -> Stream m a
1251+ [A] divideBy :: Int -> (MutArray a -> m (MutArray a, MutArray a)) -> MutArray a -> m ()
1252+ [A] cmp :: MonadIO m => MutArray a -> MutArray a -> m Ordering
1253+ [A] clone :: MonadIO m => MutArray a -> m (MutArray a)
1254+ [A] chunksOf :: forall m a. (MonadIO m, Unbox a) => Int -> Stream m a -> Stream m (MutArray a)
1255+ [A] castUnsafe :: MutArray a -> MutArray b
1256+ [A] cast :: forall a b. Unbox b => MutArray a -> Maybe (MutArray b)
1257+ [A] c_memchr :: Ptr Word8 -> Word8 -> CSize -> IO (Ptr Word8)
1258+ [A] bytesFree :: MutArray a -> Int
1259+ [A] byteLength :: MutArray a -> Int
1260+ [A] byteCapacity :: MutArray a -> Int
1261+ [A] bubble :: (MonadIO m, Unbox a) => (a -> a -> Ordering) -> MutArray a -> m ()
1262+ [A] breakOn :: MonadIO m => Word8 -> MutArray Word8 -> m (MutArray Word8, Maybe (MutArray Word8))
1263+ [A] blockSize :: Int
1264+ [A] asPtrUnsafe :: MonadIO m => MutArray a -> (Ptr a -> m b) -> m b
1265+ [A] asBytes :: MutArray a -> MutArray Word8
1266+ [A] arrayChunkBytes :: Int
1267+ [A] allocBytesToElemCount :: Unbox a => a -> Int -> Int
1268+[C] Streamly.Internal.Data.IsMap
1269+ [A] mapTraverseWithKey :: (IsMap f, Applicative t) => (Key f -> a -> t b) -> f a -> t (f b)
1270+[R] Streamly.Internal.Data.IORef.Unboxed
1271+[R] Streamly.Internal.Data.Fold.Window
1272+[R] Streamly.Internal.Data.Fold.Type
1273+[R] Streamly.Internal.Data.Fold.Tee
1274+[R] Streamly.Internal.Data.Fold.Step
1275+[R] Streamly.Internal.Data.Fold.Container
1276+[D] Streamly.Internal.Data.Fold.Chunked
1277+[C] Streamly.Internal.Data.Fold
1278+ [A] ManyState
1279+ [C] Fold
1280+ [C] Fold
1281+ [O] Fold :: (s -> a -> m (Step s b)) -> m (Step s b) -> (s -> m b) -> Fold m a b
1282+ [N] Fold :: (s -> a -> m (Step s b)) -> m (Step s b) -> (s -> m b) -> (s -> m b) -> Fold m a b
1283+ [A] windowSumInt :: forall m a. (Monad m, Integral a) => Fold m (a, Maybe a) a
1284+ [A] windowSum :: forall m a. (Monad m, Num a) => Fold m (a, Maybe a) a
1285+ [A] windowRollingMapM :: Monad m => (Maybe a -> a -> m (Maybe b)) -> Fold m (a, Maybe a) (Maybe b)
1286+ [A] windowRollingMap :: Monad m => (Maybe a -> a -> Maybe b) -> Fold m (a, Maybe a) (Maybe b)
1287+ [A] windowRange :: (MonadIO m, Storable a, Ord a) => Int -> Fold m a (Maybe (a, a))
1288+ [A] windowPowerSumFrac :: (Monad m, Floating a) => a -> Fold m (a, Maybe a) a
1289+ [A] windowPowerSum :: (Monad m, Num a) => Int -> Fold m (a, Maybe a) a
1290+ [A] windowMinimum :: (MonadIO m, Storable a, Ord a) => Int -> Fold m a (Maybe a)
1291+ [A] windowMean :: forall m a. (Monad m, Fractional a) => Fold m (a, Maybe a) a
1292+ [A] windowMaximum :: (MonadIO m, Storable a, Ord a) => Int -> Fold m a (Maybe a)
1293+ [A] windowLmap :: (c -> a) -> Fold m (a, Maybe a) b -> Fold m (c, Maybe c) b
1294+ [A] windowLength :: (Monad m, Num b) => Fold m (a, Maybe a) b
1295+ [A] toSet :: (Monad m, Ord a) => Fold m a (Set a)
1296+ [A] toMapIO :: (MonadIO m, Ord k) => (a -> k) -> Fold m a b -> Fold m a (Map k b)
1297+ [A] toMap :: (Monad m, Ord k) => (a -> k) -> Fold m a b -> Fold m a (Map k b)
1298+ [A] toIntSet :: Monad m => Fold m Int IntSet
1299+ [A] toContainerIO :: (MonadIO m, IsMap f, Traversable f, Ord (Key f)) => (a -> Key f) -> Fold m a b -> Fold m a (f b)
1300+ [A] toContainer :: (Monad m, IsMap f, Traversable f, Ord (Key f)) => (a -> Key f) -> Fold m a b -> Fold m a (f b)
1301+ [A] nubInt :: Monad m => Fold m Int (Maybe Int)
1302+ [A] nub :: (Monad m, Ord a) => Fold m a (Maybe a)
1303+ [A] mapMStep :: Applicative m => (a -> m b) -> Step s a -> m (Step s b)
1304+ [A] kvToMap :: (Monad m, Ord k) => Fold m a b -> Fold m (k, a) (Map k b)
1305+ [A] frequency :: (Monad m, Ord a) => Fold m a (Map a Int)
1306+ [A] demuxToMapIO :: (MonadIO m, Ord k) => (a -> k) -> (a -> m (Fold m a b)) -> Fold m a (Map k b)
1307+ [A] demuxToMap :: (Monad m, Ord k) => (a -> k) -> (a -> m (Fold m a b)) -> Fold m a (Map k b)
1308+ [A] demuxToContainerIO :: (MonadIO m, IsMap f, Traversable f) => (a -> Key f) -> (a -> m (Fold m a b)) -> Fold m a (f b)
1309+ [A] demuxToContainer :: (Monad m, IsMap f, Traversable f) => (a -> Key f) -> (a -> m (Fold m a b)) -> Fold m a (f b)
1310+ [A] demuxKvToMap :: (Monad m, Ord k) => (k -> m (Fold m a b)) -> Fold m (k, a) (Map k b)
1311+ [A] demuxKvToContainer :: (Monad m, IsMap f, Traversable f) => (Key f -> m (Fold m a b)) -> Fold m (Key f, a) (f b)
1312+ [A] demuxIO :: (MonadIO m, Ord k) => (a -> k) -> (a -> m (Fold m a b)) -> Fold m a (m (Map k b), Maybe (k, b))
1313+ [A] demuxGenericIO :: (MonadIO m, IsMap f, Traversable f) => (a -> Key f) -> (a -> m (Fold m a b)) -> Fold m a (m (f b), Maybe (Key f, b))
1314+ [A] demuxGeneric :: (Monad m, IsMap f, Traversable f) => (a -> Key f) -> (a -> m (Fold m a b)) -> Fold m a (m (f b), Maybe (Key f, b))
1315+ [A] demux :: (Monad m, Ord k) => (a -> k) -> (a -> m (Fold m a b)) -> Fold m a (m (Map k b), Maybe (k, b))
1316+ [A] cumulative :: Fold m (a, Maybe a) b -> Fold m a b
1317+ [A] countDistinctInt :: Monad m => Fold m Int Int
1318+ [A] countDistinct :: (Monad m, Ord a) => Fold m a Int
1319+ [A] classifyIO :: (MonadIO m, Ord k) => (a -> k) -> Fold m a b -> Fold m a (m (Map k b), Maybe (k, b))
1320+ [A] classifyGenericIO :: (MonadIO m, IsMap f, Traversable f, Ord (Key f)) => (a -> Key f) -> Fold m a b -> Fold m a (m (f b), Maybe (Key f, b))
1321+ [A] classifyGeneric :: (Monad m, IsMap f, Traversable f, Ord (Key f)) => (a -> Key f) -> Fold m a b -> Fold m a (m (f b), Maybe (Key f, b))
1322+ [A] classify :: (Monad m, Ord k) => (a -> k) -> Fold m a b -> Fold m a (m (Map k b), Maybe (k, b))
1323+ [A] chainStepM :: Applicative m => (s1 -> m s2) -> (a -> m (Step s2 b)) -> Step s1 a -> m (Step s2 b)
1324+[C] Streamly.Internal.Data.Builder
1325+ [C] Builder
1326+ [C] Builder
1327+ [O] Builder :: (s -> m (s, a)) -> Builder s m a
1328+ [N] Builder :: (s -> m (a, s)) -> Builder s m a
1329+[A] Streamly.Internal.Data.Binary.Stream
1330+ [A] class ToBytes a
1331+ [A] word8 :: Applicative m => Word8 -> Stream m Word8
1332+ [A] word64le :: Monad m => Word64 -> Stream m Word8
1333+ [A] word64host :: Monad m => Word64 -> Stream m Word8
1334+ [A] word64be :: Monad m => Word64 -> Stream m Word8
1335+ [A] word32le :: Monad m => Word32 -> Stream m Word8
1336+ [A] word32be :: Monad m => Word32 -> Stream m Word8
1337+ [A] word16le :: Monad m => Word16 -> Stream m Word8
1338+ [A] word16be :: Monad m => Word16 -> Stream m Word8
1339+ [A] unit :: Applicative m => Stream m Word8
1340+ [A] toBytes :: ToBytes a => a -> Stream m Word8
1341+ [A] ordering :: Applicative m => Ordering -> Stream m Word8
1342+ [A] int8 :: Applicative m => Int8 -> Stream m Word8
1343+ [A] int64le :: Monad m => Int64 -> Stream m Word8
1344+ [A] int64be :: Monad m => Int64 -> Stream m Word8
1345+ [A] int32le :: Monad m => Int32 -> Stream m Word8
1346+ [A] int32be :: Monad m => Int32 -> Stream m Word8
1347+ [A] int16le :: Monad m => Int16 -> Stream m Word8
1348+ [A] int16be :: Monad m => Int16 -> Stream m Word8
1349+ [A] float32le :: Monad m => Float -> Stream m Word8
1350+ [A] float32be :: Monad m => Float -> Stream m Word8
1351+ [A] double64le :: Monad m => Double -> Stream m Word8
1352+ [A] double64be :: Monad m => Double -> Stream m Word8
1353+ [A] charUtf8 :: Monad m => Char -> Stream m Word8
1354+ [A] charLatin1 :: Applicative m => Char -> Stream m Word8
1355+ [A] bool :: Applicative m => Bool -> Stream m Word8
1356+[A] Streamly.Internal.Data.Binary.Parser
1357+ [A] class FromBytes a
1358+ [A] word8 :: Monad m => Parser Word8 m Word8
1359+ [A] word64le :: Monad m => Parser Word8 m Word64
1360+ [A] word64host :: MonadIO m => Parser Word8 m Word64
1361+ [A] word64be :: Monad m => Parser Word8 m Word64
1362+ [A] word32le :: Monad m => Parser Word8 m Word32
1363+ [A] word32be :: Monad m => Parser Word8 m Word32
1364+ [A] word16le :: Monad m => Parser Word8 m Word16
1365+ [A] word16be :: Monad m => Parser Word8 m Word16
1366+ [A] unit :: Monad m => Parser Word8 m ()
1367+ [A] ordering :: Monad m => Parser Word8 m Ordering
1368+ [A] int8 :: Monad m => Parser Word8 m Int8
1369+ [A] int64le :: Monad m => Parser Word8 m Int64
1370+ [A] int64be :: Monad m => Parser Word8 m Int64
1371+ [A] int32le :: Monad m => Parser Word8 m Int32
1372+ [A] int32be :: Monad m => Parser Word8 m Int32
1373+ [A] int16le :: Monad m => Parser Word8 m Int16
1374+ [A] int16be :: Monad m => Parser Word8 m Int16
1375+ [A] fromBytes :: FromBytes a => Parser Word8 m a
1376+ [A] float32le :: MonadIO m => Parser Word8 m Float
1377+ [A] float32be :: MonadIO m => Parser Word8 m Float
1378+ [A] eqWord8 :: Monad m => Word8 -> Parser Word8 m Word8
1379+ [A] double64le :: MonadIO m => Parser Word8 m Double
1380+ [A] double64be :: MonadIO m => Parser Word8 m Double
1381+ [A] charLatin1 :: Monad m => Parser Word8 m Char
1382+ [A] bool :: Monad m => Parser Word8 m Bool
1383+[R] Streamly.Internal.Data.Array.Type
1384+[A] Streamly.Internal.Data.Array.Stream
1385+ [A] unlines :: forall m a. (MonadIO m, Unbox a) => a -> Stream m (Array a) -> Stream m a
1386+ [A] toArray :: (MonadIO m, Unbox a) => Stream m (Array a) -> m (Array a)
1387+ [A] splitOnSuffix :: MonadIO m => Word8 -> Stream m (Array Word8) -> Stream m (Array Word8)
1388+ [A] splitOn :: MonadIO m => Word8 -> Stream m (Array Word8) -> Stream m (Array Word8)
1389+ [A] runArrayParserDBreak :: forall m a b. (MonadIO m, Unbox a) => Parser (Array a) m b -> Stream m (Array a) -> m (Either ParseError b, Stream m (Array a))
1390+ [A] runArrayFoldMany :: (Monad m, Unbox a) => ChunkFold m a b -> StreamK m (Array a) -> StreamK m (Either ParseError b)
1391+ [A] runArrayFoldBreak :: (MonadIO m, Unbox a) => ChunkFold m a b -> StreamK m (Array a) -> m (Either ParseError b, StreamK m (Array a))
1392+ [A] runArrayFold :: (MonadIO m, Unbox a) => ChunkFold m a b -> StreamK m (Array a) -> m (Either ParseError b)
1393+ [A] pinnedChunksOf :: forall m a. (MonadIO m, Unbox a) => Int -> Stream m a -> Stream m (Array a)
1394+ [A] parseChunks :: (Monad m, Unbox a) => ParserK (Array a) m b -> StreamK m (Array a) -> m (Either ParseError b)
1395+ [A] parseBreakChunks :: (Monad m, Unbox a) => ParserK (Array a) m b -> StreamK m (Array a) -> m (Either ParseError b, StreamK m (Array a))
1396+ [A] parseBreak :: (MonadIO m, Unbox a) => Parser a m b -> StreamK m (Array a) -> m (Either ParseError b, StreamK m (Array a))
1397+ [A] lpackArraysChunksOf :: (MonadIO m, Unbox a) => Int -> Fold m (Array a) () -> Fold m (Array a) ()
1398+ [A] interposeSuffix :: (Monad m, Unbox a) => a -> Stream m (Array a) -> Stream m a
1399+ [A] interpose :: (Monad m, Unbox a) => a -> Stream m (Array a) -> Stream m a
1400+ [A] intercalateSuffix :: (Monad m, Unbox a) => Array a -> Stream m (Array a) -> Stream m a
1401+ [A] foldBreakD :: forall m a b. (MonadIO m, Unbox a) => Fold m a b -> Stream m (Array a) -> m (b, Stream m (Array a))
1402+ [A] foldBreak :: (MonadIO m, Unbox a) => Fold m a b -> StreamK m (Array a) -> m (b, StreamK m (Array a))
1403+ [A] flattenArraysRev :: forall m a. (MonadIO m, Unbox a) => Stream m (Array a) -> Stream m a
1404+ [A] flattenArrays :: forall m a. (MonadIO m, Unbox a) => Stream m (Array a) -> Stream m a
1405+ [A] concatRev :: (Monad m, Unbox a) => Stream m (Array a) -> Stream m a
1406+ [A] concat :: (Monad m, Unbox a) => Stream m (Array a) -> Stream m a
1407+ [A] compact :: (MonadIO m, Unbox a) => Int -> Stream m (Array a) -> Stream m (Array a)
1408+ [A] chunksOf :: forall m a. (MonadIO m, Unbox a) => Int -> Stream m a -> Stream m (Array a)
1409+ [A] bufferChunks :: (MonadIO m, Unbox a) => Stream m a -> m (StreamK m (Array a))
1410+[R] Streamly.Internal.Data.Array.Mut.Type
1411+[R] Streamly.Internal.Data.Array.Mut.Stream
1412+[R] Streamly.Internal.Data.Array.Mut
1413+[R] Streamly.Internal.Data.Array.Generic.Mut.Type
1414+[C] Streamly.Internal.Data.Array.Generic
1415+ [A] getIndex :: Int -> Array a -> Maybe a
1416+ [A] fromPureStream :: Stream Identity a -> Array a
1417+ [A] fromByteStr# :: Addr# -> Array Word8
1418+ [A] chunksOf :: forall m a. MonadIO m => Int -> Stream m a -> Stream m (Array a)
1419+[C] Streamly.Internal.Data.Array
1420+ [A] ArrayUnsafe
1421+ [A] ArrayUnsafe :: {-# UNPACK #-} !MutByteArray -> {-# UNPACK #-} !Int -> {-# UNPACK #-} !Int -> ArrayUnsafe a
1422+ [C] Array
1423+ [A] [arrStart] :: Array a -> {-# UNPACK #-} !Int
1424+ [A] [arrEnd] :: Array a -> {-# UNPACK #-} !Int
1425+ [A] [arrContents] :: Array a -> {-# UNPACK #-} !MutByteArray
1426+ [A] Array :: {-# UNPACK #-} !MutByteArray -> {-# UNPACK #-} !Int -> {-# UNPACK #-} !Int -> Array a
1427+ [A] writeWith :: forall m a. (MonadIO m, Unbox a) => Int -> Fold m a (Array a)
1428+ [A] writeNUnsafe :: forall m a. (MonadIO m, Unbox a) => Int -> Fold m a (Array a)
1429+ [R] writeNAligned :: forall m a. (MonadIO m, Unbox a) => Int -> Int -> Fold m a (Array a)
1430+ [A] unsafeMakePure :: Monad m => Fold IO a b -> Fold m a b
1431+ [A] unsafeIndexIO :: forall a. Unbox a => Int -> Array a -> IO a
1432+ [D] unsafeIndex :: forall a. Unbox a => Int -> Array a -> a
1433+ [A] unsafeFreezeWithShrink :: Unbox a => MutArray a -> Array a
1434+ [A] unpin :: Array a -> IO (Array a)
1435+ [A] toStreamKRev :: forall m a. (Monad m, Unbox a) => Array a -> StreamK m a
1436+ [A] toStreamK :: forall m a. (Monad m, Unbox a) => Array a -> StreamK m a
1437+ [A] toStreamDRev :: forall m a. (Monad m, Unbox a) => Array a -> Stream m a
1438+ [A] toStreamD :: forall m a. (Monad m, Unbox a) => Array a -> Stream m a
1439+ [A] splitAt :: Unbox a => Int -> Array a -> (Array a, Array a)
1440+ [A] splice :: (MonadIO m, Unbox a) => Array a -> Array a -> m (Array a)
1441+ [A] serialize :: Serialize a => a -> Array Word8
1442+ [A] pinnedWriteNUnsafe :: forall m a. (MonadIO m, Unbox a) => Int -> Fold m a (Array a)
1443+ [A] pinnedWriteNAligned :: forall m a. (MonadIO m, Unbox a) => Int -> Int -> Fold m a (Array a)
1444+ [A] pinnedWriteN :: forall m a. (MonadIO m, Unbox a) => Int -> Fold m a (Array a)
1445+ [A] pinnedWrite :: forall m a. (MonadIO m, Unbox a) => Fold m a (Array a)
1446+ [A] pinnedSerialize :: Serialize a => a -> Array Word8
1447+ [A] pinnedFromListN :: Unbox a => Int -> [a] -> Array a
1448+ [A] pinnedFromList :: Unbox a => [a] -> Array a
1449+ [A] pinnedClone :: MonadIO m => Array a -> m (Array a)
1450+ [A] pinnedChunksOf :: forall m a. (MonadIO m, Unbox a) => Int -> Stream m a -> Stream m (Array a)
1451+ [A] pin :: Array a -> IO (Array a)
1452+ [A] nil :: Array a
1453+ [A] isPinned :: Array a -> Bool
1454+ [A] getIndexUnsafe :: forall a. Unbox a => Int -> Array a -> a
1455+ [A] fromStreamDN :: forall m a. (MonadIO m, Unbox a) => Int -> Stream m a -> m (Array a)
1456+ [A] fromStreamD :: forall m a. (MonadIO m, Unbox a) => Stream m a -> m (Array a)
1457+ [A] fromPureStream :: Unbox a => Stream Identity a -> Array a
1458+ [A] fromListRevN :: Unbox a => Int -> [a] -> Array a
1459+ [A] fromListRev :: Unbox a => [a] -> Array a
1460+ [A] fromByteStr# :: Addr# -> Array Word8
1461+ [A] foldr :: Unbox a => (a -> b -> b) -> b -> Array a -> b
1462+ [A] foldl' :: forall a b. Unbox a => (b -> a -> b) -> b -> Array a -> b
1463+ [A] flattenArraysRev :: forall m a. (MonadIO m, Unbox a) => Stream m (Array a) -> Stream m a
1464+ [A] flattenArrays :: forall m a. (MonadIO m, Unbox a) => Stream m (Array a) -> Stream m a
1465+ [A] encodeAs :: forall a. Serialize a => PinnedState -> a -> Array Word8
1466+ [A] deserialize :: Serialize a => Array Word8 -> a
1467+ [A] clone :: MonadIO m => Array a -> m (Array a)
1468+ [A] chunksOf :: forall m a. (MonadIO m, Unbox a) => Int -> Stream m a -> Stream m (Array a)
1469+ [A] byteLength :: Array a -> Int
1470+ [A] bufferChunks :: (MonadIO m, Unbox a) => Stream m a -> m (StreamK m (Array a))
1471+ [A] breakOn :: MonadIO m => Word8 -> Array Word8 -> m (Array Word8, Maybe (Array Word8))
core/docs/ApiChangelogs/0.2.0-0.2.2.txtnew file+306
@@ -0,0 +1,306 @@
1+---------------------------------
2+API Annotations
3+---------------------------------
4+
5+[A] : Added
6+[R] : Removed
7+[C] : Changed
8+[O] : Old definition
9+[N] : New definition
10+[D] : Deprecated
11+
12+---------------------------------
13+API diff
14+---------------------------------
15+
16+[C] Streamly.Data.Stream
17+ [A] (FixityR,5)
18+ [A] (FixityR,5)
19+[C] Streamly.Data.MutArray.Generic
20+ [A] emptyOf :: MonadIO m => Int -> m (MutArray a)
21+ [A] createOf :: MonadIO m => Int -> Fold m a (MutArray a)
22+ [A] create :: MonadIO m => Fold m a (MutArray a)
23+[C] Streamly.Data.MutArray
24+ [A] pinnedEmptyOf :: forall m a. (MonadIO m, Unbox a) => Int -> m (MutArray a)
25+ [A] emptyOf :: (MonadIO m, Unbox a) => Int -> m (MutArray a)
26+ [A] createOf :: forall m a. (MonadIO m, Unbox a) => Int -> Fold m a (MutArray a)
27+ [A] create :: forall m a. (MonadIO m, Unbox a) => Fold m a (MutArray a)
28+ [A] appendN :: forall m a. (MonadIO m, Unbox a) => Int -> m (MutArray a) -> Fold m a (MutArray a)
29+ [A] append :: forall m a. (MonadIO m, Unbox a) => m (MutArray a) -> Fold m a (MutArray a)
30+[C] Streamly.Data.Array.Generic
31+ [A] createOf :: MonadIO m => Int -> Fold m a (Array a)
32+ [A] create :: MonadIO m => Fold m a (Array a)
33+[C] Streamly.Data.Array
34+ [A] class Serialize a
35+ [A] serializeAt :: Serialize a => Int -> MutByteArray -> a -> IO Int
36+ [A] pinnedSerialize :: Serialize a => a -> Array Word8
37+ [A] deserializeAt :: Serialize a => Int -> MutByteArray -> Int -> IO (Int, a)
38+ [A] deserialize :: Serialize a => Array Word8 -> a
39+ [A] createOf :: forall m a. (MonadIO m, Unbox a) => Int -> Fold m a (Array a)
40+ [A] create :: forall m a. (MonadIO m, Unbox a) => Fold m a (Array a)
41+ [A] addSizeTo :: Serialize a => Int -> a -> Int
42+
43+---------------------------------
44+Internal API diff
45+---------------------------------
46+
47+[C] Streamly.Internal.Unicode.Stream
48+ [A] encodeUtf16le' :: Stream m Char -> Stream m Word16
49+ [A] decodeUtf16le' :: Stream m Word16 -> Stream m Char
50+[A] Streamly.Internal.FileSystem.Path
51+ [A] class IsPath a
52+ [A] Rel
53+ [A] File
54+ [A] Dir
55+ [A] Abs
56+ [A] Streamly.Internal.FileSystem.Path.IsPath
57+ [A] instance Streamly.Internal.FileSystem.Path.IsPath Streamly.Internal.FileSystem.Path.Path
58+ [A] instance Streamly.Internal.FileSystem.Path.IsPath (Streamly.Internal.FileSystem.Path.Rel Streamly.Internal.FileSystem.Path.Path)
59+ [A] instance Streamly.Internal.FileSystem.Path.IsPath (Streamly.Internal.FileSystem.Path.Rel (Streamly.Internal.FileSystem.Path.File Streamly.Internal.FileSystem.Path.Path))
60+ [A] instance Streamly.Internal.FileSystem.Path.IsPath (Streamly.Internal.FileSystem.Path.Rel (Streamly.Internal.FileSystem.Path.Dir Streamly.Internal.FileSystem.Path.Path))
61+ [A] instance Streamly.Internal.FileSystem.Path.IsPath (Streamly.Internal.FileSystem.Path.File Streamly.Internal.FileSystem.Path.Path)
62+ [A] instance Streamly.Internal.FileSystem.Path.IsPath (Streamly.Internal.FileSystem.Path.Dir Streamly.Internal.FileSystem.Path.Path)
63+ [A] instance Streamly.Internal.FileSystem.Path.IsPath (Streamly.Internal.FileSystem.Path.Abs Streamly.Internal.FileSystem.Path.Path)
64+ [A] instance Streamly.Internal.FileSystem.Path.IsPath (Streamly.Internal.FileSystem.Path.Abs (Streamly.Internal.FileSystem.Path.File Streamly.Internal.FileSystem.Path.Path))
65+ [A] instance Streamly.Internal.FileSystem.Path.IsPath (Streamly.Internal.FileSystem.Path.Abs (Streamly.Internal.FileSystem.Path.Dir Streamly.Internal.FileSystem.Path.Path))
66+ [A] GHC.Show.Show
67+ [A] instance GHC.Show.Show Streamly.Internal.FileSystem.Path.PathException
68+ [A] GHC.Exception.Type.Exception
69+ [A] instance GHC.Exception.Type.Exception Streamly.Internal.FileSystem.Path.PathException
70+ [A] GHC.Classes.Eq
71+ [A] instance GHC.Classes.Eq Streamly.Internal.FileSystem.Path.PathException
72+ [A] Path
73+ [A] Path :: Array Word8 -> Path
74+ [A] toString :: Path -> [Char]
75+ [A] toPath :: IsPath a => a -> Path
76+ [A] toChunk :: Path -> Array Word8
77+ [A] toChars :: Monad m => Path -> Stream m Char
78+ [A] relfile :: QuasiQuoter
79+ [A] reldir :: QuasiQuoter
80+ [A] rel :: QuasiQuoter
81+ [A] primarySeparator :: Char
82+ [A] path :: QuasiQuoter
83+ [A] mkRelFile :: String -> Q Exp
84+ [A] mkRelDir :: String -> Q Exp
85+ [A] mkRel :: String -> Q Exp
86+ [A] mkPath :: String -> Q Exp
87+ [A] mkFile :: String -> Q Exp
88+ [A] mkDir :: String -> Q Exp
89+ [A] mkAbsFile :: String -> Q Exp
90+ [A] mkAbsDir :: String -> Q Exp
91+ [A] mkAbs :: String -> Q Exp
92+ [A] isSeparator :: Char -> Bool
93+ [A] fromString :: MonadThrow m => [Char] -> m Path
94+ [A] fromPathUnsafe :: IsPath a => Path -> a
95+ [A] fromPath :: (IsPath a, MonadThrow m) => Path -> m a
96+ [A] fromChunkUnsafe :: Array Word8 -> Path
97+ [A] fromChunk :: MonadThrow m => Array Word8 -> m Path
98+ [A] fromChars :: MonadThrow m => Stream Identity Char -> m Path
99+ [A] file :: QuasiQuoter
100+ [A] extendPath :: Path -> Path -> Path
101+ [A] extendDir :: (IsPath (a (Dir Path)), IsPath b, IsPath (a b)) => a (Dir Path) -> Rel b -> a b
102+ [A] dir :: QuasiQuoter
103+ [A] adaptPath :: (MonadThrow m, IsPath a, IsPath b) => a -> m b
104+ [A] absfile :: QuasiQuoter
105+ [A] absdir :: QuasiQuoter
106+ [A] abs :: QuasiQuoter
107+[C] Streamly.Internal.Data.Stream
108+ [A] (FixityR,5)
109+ [A] (FixityR,5)
110+ [C] splitInnerBySuffix
111+ [O] splitInnerBySuffix :: (Monad m, Eq (f a), Monoid (f a)) => (f a -> m (f a, Maybe (f a))) -> (f a -> f a -> m (f a)) -> Stream m (f a) -> Stream m (f a)
112+ [N] splitInnerBySuffix :: Monad m => (f a -> Bool) -> (f a -> m (f a, Maybe (f a))) -> (f a -> f a -> m (f a)) -> Stream m (f a) -> Stream m (f a)
113+ [D] sliceOnSuffix :: Monad m => (a -> Bool) -> Stream m a -> Stream m (Int, Int)
114+ [A] indexOnSuffix :: Monad m => (a -> Bool) -> Stream m a -> Stream m (Int, Int)
115+[C] Streamly.Internal.Data.MutByteArray
116+ [A] unsafePinnedAsPtr :: MonadIO m => MutByteArray -> (Ptr a -> m b) -> m b
117+ [A] unsafeAsPtr :: MonadIO m => MutByteArray -> (Ptr a -> m b) -> m b
118+ [D] nil :: MutByteArray
119+ [A] empty :: MutByteArray
120+ [D] asPtrUnsafe :: MonadIO m => MutByteArray -> (Ptr a -> m b) -> m b
121+[D] Streamly.Internal.Data.MutArray.Stream
122+ [D] writeChunks :: (MonadIO m, Unbox a) => Int -> Fold m a (StreamK n (MutArray a))
123+ [D] fromArrayStreamK :: (Unbox a, MonadIO m) => StreamK m (MutArray a) -> m (MutArray a)
124+ [D] flattenArraysRev :: forall m a. (MonadIO m, Unbox a) => Stream m (MutArray a) -> Stream m a
125+ [D] flattenArrays :: forall m a. (MonadIO m, Unbox a) => Stream m (MutArray a) -> Stream m a
126+[C] Streamly.Internal.Data.MutArray.Generic
127+ [D] writeWith :: MonadIO m => Int -> Fold m a (MutArray a)
128+ [D] writeNUnsafe :: MonadIO m => Int -> Fold m a (MutArray a)
129+ [A] unsafeCreateOf :: MonadIO m => Int -> Fold m a (MutArray a)
130+ [A] emptyOf :: MonadIO m => Int -> m (MutArray a)
131+ [A] createWith :: MonadIO m => Int -> Fold m a (MutArray a)
132+ [A] createOf :: MonadIO m => Int -> Fold m a (MutArray a)
133+ [A] create :: MonadIO m => Fold m a (MutArray a)
134+[C] Streamly.Internal.Data.MutArray
135+ [A] SpliceState
136+ [A] SpliceYielding :: arr -> SpliceState s arr -> SpliceState s arr
137+ [A] SpliceInitial :: s -> SpliceState s arr
138+ [A] SpliceFinish :: SpliceState s arr
139+ [A] SpliceBuffering :: s -> arr -> SpliceState s arr
140+ [R] MutByteArray
141+ [D] writeWith :: forall m a. (MonadIO m, Unbox a) => Int -> Fold m a (MutArray a)
142+ [D] writeRevN :: forall m a. (MonadIO m, Unbox a) => Int -> Fold m a (MutArray a)
143+ [D] writeNWithUnsafe :: forall m a. (MonadIO m, Unbox a) => (Int -> m (MutArray a)) -> Int -> Fold m a (MutArray a)
144+ [D] writeNUnsafe :: forall m a. (MonadIO m, Unbox a) => Int -> Fold m a (MutArray a)
145+ [D] writeChunks :: (MonadIO m, Unbox a) => Int -> Fold m a (StreamK n (MutArray a))
146+ [D] writeAppendWith :: forall m a. (MonadIO m, Unbox a) => (Int -> Int) -> m (MutArray a) -> Fold m a (MutArray a)
147+ [D] writeAppendNUnsafe :: forall m a. (MonadIO m, Unbox a) => Int -> m (MutArray a) -> Fold m a (MutArray a)
148+ [A] unsafePinnedCreateOf :: forall m a. (MonadIO m, Unbox a) => Int -> Fold m a (MutArray a)
149+ [A] unsafePinnedAsPtr :: MonadIO m => MutArray a -> (Ptr a -> m b) -> m b
150+ [A] unsafeCreateOfWith :: forall m a. (MonadIO m, Unbox a) => (Int -> m (MutArray a)) -> Int -> Fold m a (MutArray a)
151+ [A] unsafeCreateOf :: forall m a. (MonadIO m, Unbox a) => Int -> Fold m a (MutArray a)
152+ [A] unsafeAsPtr :: MonadIO m => MutArray a -> (Ptr a -> m b) -> m b
153+ [A] unsafeAppendN :: forall m a. (MonadIO m, Unbox a) => Int -> m (MutArray a) -> Fold m a (MutArray a)
154+ [A] toStreamWith :: forall m a. (Monad m, Unbox a) => (forall b. IO b -> m b) -> MutArray a -> Stream m a
155+ [A] toStreamRevWith :: forall m a. (Monad m, Unbox a) => (forall b. IO b -> m b) -> MutArray a -> Stream m a
156+ [R] toStreamDWith :: forall m a. (Monad m, Unbox a) => (forall b. IO b -> m b) -> MutArray a -> Stream m a
157+ [R] toStreamDRevWith :: forall m a. (Monad m, Unbox a) => (forall b. IO b -> m b) -> MutArray a -> Stream m a
158+ [A] slicerFromLen :: forall m a. (Monad m, Unbox a) => Int -> Int -> Unfold m (MutArray a) (MutArray a)
159+ [A] sliceIndexerFromLen :: forall m a. (Monad m, Unbox a) => Int -> Int -> Unfold m (MutArray a) (Int, Int)
160+ [A] revCreateOf :: forall m a. (MonadIO m, Unbox a) => Int -> Fold m a (MutArray a)
161+ [D] resizeExp :: forall m a. (MonadIO m, Unbox a) => Int -> MutArray a -> m (MutArray a)
162+ [D] resize :: forall m a. (MonadIO m, Unbox a) => Int -> MutArray a -> m (MutArray a)
163+ [A] pokeSkipUnsafe :: Int -> MutArray Word8 -> MutArray Word8
164+ [A] pokeAppendMay :: forall m a. (MonadIO m, Unbox a) => MutArray Word8 -> a -> m (Maybe (MutArray Word8))
165+ [A] pokeAppend :: forall m a. (MonadIO m, Unbox a) => MutArray Word8 -> a -> m (MutArray Word8)
166+ [D] pinnedWriteNUnsafe :: forall m a. (MonadIO m, Unbox a) => Int -> Fold m a (MutArray a)
167+ [D] pinnedWriteN :: forall m a. (MonadIO m, Unbox a) => Int -> Fold m a (MutArray a)
168+ [D] pinnedWrite :: forall m a. (MonadIO m, Unbox a) => Fold m a (MutArray a)
169+ [D] pinnedNewBytes :: MonadIO m => Int -> m (MutArray a)
170+ [A] pinnedEmptyOf :: forall m a. (MonadIO m, Unbox a) => Int -> m (MutArray a)
171+ [A] pinnedCreateOf :: forall m a. (MonadIO m, Unbox a) => Int -> Fold m a (MutArray a)
172+ [A] pinnedCreate :: forall m a. (MonadIO m, Unbox a) => Fold m a (MutArray a)
173+ [A] pinnedCompactLE :: forall m a. (MonadIO m, Unbox a) => Int -> Stream m (MutArray a) -> Stream m (MutArray a)
174+ [A] peekUnconsUnsafe :: forall m a. (MonadIO m, Unbox a) => MutArray Word8 -> m (a, MutArray Word8)
175+ [A] peekUncons :: forall m a. (MonadIO m, Unbox a) => MutArray Word8 -> m (Maybe a, MutArray Word8)
176+ [A] peekSkipUnsafe :: Int -> MutArray Word8 -> MutArray Word8
177+ [A] pPinnedCompactLE :: forall m a. (MonadIO m, Unbox a) => Int -> Parser (MutArray a) m (MutArray a)
178+ [A] pCompactLE :: forall m a. (MonadIO m, Unbox a) => Int -> Parser (MutArray a) m (MutArray a)
179+ [D] nil :: MutArray a
180+ [A] lPinnedCompactGE :: forall m a. (MonadIO m, Unbox a) => Int -> Fold m (MutArray a) () -> Fold m (MutArray a) ()
181+ [A] lCompactGE :: forall m a. (MonadIO m, Unbox a) => Int -> Fold m (MutArray a) () -> Fold m (MutArray a) ()
182+ [A] indexReaderWith :: (Monad m, Unbox a) => (forall b. IO b -> m b) -> Stream m Int -> Unfold m (MutArray a) a
183+ [A] indexReader :: (MonadIO m, Unbox a) => Stream m Int -> Unfold m (MutArray a) a
184+ [A] growExp :: forall m a. (MonadIO m, Unbox a) => Int -> MutArray a -> m (MutArray a)
185+ [A] grow :: forall m a. (MonadIO m, Unbox a) => Int -> MutArray a -> m (MutArray a)
186+ [D] getSlicesFromLen :: forall m a. (Monad m, Unbox a) => Int -> Int -> Unfold m (MutArray a) (MutArray a)
187+ [R] getIndicesD :: (Monad m, Unbox a) => (forall b. IO b -> m b) -> Stream m Int -> Unfold m (MutArray a) a
188+ [D] getIndices :: (MonadIO m, Unbox a) => Stream m Int -> Unfold m (MutArray a) a
189+ [D] genSlicesFromLen :: forall m a. (Monad m, Unbox a) => Int -> Int -> Unfold m (MutArray a) (Int, Int)
190+ [A] fromStreamN :: forall m a. (MonadIO m, Unbox a) => Int -> Stream m a -> m (MutArray a)
191+ [D] fromStreamDN :: forall m a. (MonadIO m, Unbox a) => Int -> Stream m a -> m (MutArray a)
192+ [D] fromStreamD :: (MonadIO m, Unbox a) => Stream m a -> m (MutArray a)
193+ [A] fromPureStreamN :: (MonadIO m, Unbox a) => Int -> Stream Identity a -> m (MutArray a)
194+ [A] fromPtrN :: MonadIO m => Int -> Ptr Word8 -> m (MutArray Word8)
195+ [A] fromChunksRealloced :: forall m a. (MonadIO m, Unbox a) => Stream m (MutArray a) -> m (MutArray a)
196+ [A] fromChunksK :: (Unbox a, MonadIO m) => StreamK m (MutArray a) -> m (MutArray a)
197+ [A] fromByteStr# :: MonadIO m => Addr# -> m (MutArray Word8)
198+ [D] fromArrayStreamK :: (Unbox a, MonadIO m) => StreamK m (MutArray a) -> m (MutArray a)
199+ [D] flattenArraysRev :: forall m a. (MonadIO m, Unbox a) => Stream m (MutArray a) -> Stream m a
200+ [D] flattenArrays :: forall m a. (MonadIO m, Unbox a) => Stream m (MutArray a) -> Stream m a
201+ [A] fPinnedCompactGE :: forall m a. (MonadIO m, Unbox a) => Int -> Fold m (MutArray a) (MutArray a)
202+ [A] fCompactGE :: forall m a. (MonadIO m, Unbox a) => Int -> Fold m (MutArray a) (MutArray a)
203+ [A] emptyOf :: (MonadIO m, Unbox a) => Int -> m (MutArray a)
204+ [A] empty :: MutArray a
205+ [A] createWith :: forall m a. (MonadIO m, Unbox a) => Int -> Fold m a (MutArray a)
206+ [A] createOfWith :: forall m a. (MonadIO m, Unbox a) => (Int -> m (MutArray a)) -> Int -> Fold m a (MutArray a)
207+ [A] createOf :: forall m a. (MonadIO m, Unbox a) => Int -> Fold m a (MutArray a)
208+ [A] create :: forall m a. (MonadIO m, Unbox a) => Fold m a (MutArray a)
209+ [A] concatWith :: forall m a. (Monad m, Unbox a) => (forall b. IO b -> m b) -> Stream m (MutArray a) -> Stream m a
210+ [A] concatRevWith :: forall m a. (Monad m, Unbox a) => (forall b. IO b -> m b) -> Stream m (MutArray a) -> Stream m a
211+ [A] concatRev :: forall m a. (MonadIO m, Unbox a) => Stream m (MutArray a) -> Stream m a
212+ [A] concat :: forall m a. (MonadIO m, Unbox a) => Stream m (MutArray a) -> Stream m a
213+ [A] compactOnByteSuffix :: MonadIO m => Word8 -> Stream m (MutArray Word8) -> Stream m (MutArray Word8)
214+ [A] compactOnByte :: MonadIO m => Word8 -> Stream m (MutArray Word8) -> Stream m (MutArray Word8)
215+ [A] compactLeAs :: forall m a. (MonadIO m, Unbox a) => PinnedState -> Int -> Stream m (MutArray a) -> Stream m (MutArray a)
216+ [A] compactLE :: (MonadIO m, Unbox a) => Int -> Stream m (MutArray a) -> Stream m (MutArray a)
217+ [A] compactGE :: (MonadIO m, Unbox a) => Int -> Stream m (MutArray a) -> Stream m (MutArray a)
218+ [A] compactEQ :: Int -> Stream m (MutArray a) -> Stream m (MutArray a)
219+ [D] cmp :: MonadIO m => MutArray a -> MutArray a -> m Ordering
220+ [A] byteEq :: MonadIO m => MutArray a -> MutArray a -> m Bool
221+ [A] byteCmp :: MonadIO m => MutArray a -> MutArray a -> m Ordering
222+ [A] buildChunks :: (MonadIO m, Unbox a) => Int -> Fold m a (StreamK n (MutArray a))
223+ [D] asPtrUnsafe :: MonadIO m => MutArray a -> (Ptr a -> m b) -> m b
224+ [A] appendWith :: forall m a. (MonadIO m, Unbox a) => (Int -> Int) -> m (MutArray a) -> Fold m a (MutArray a)
225+ [A] appendN :: forall m a. (MonadIO m, Unbox a) => Int -> m (MutArray a) -> Fold m a (MutArray a)
226+ [A] append :: forall m a. (MonadIO m, Unbox a) => m (MutArray a) -> Fold m a (MutArray a)
227+[D] Streamly.Internal.Data.Array.Stream
228+ [C] interposeSuffix
229+ [O] interposeSuffix :: (Monad m, Unbox a) => a -> Stream m (Array a) -> Stream m a
230+ [N] interposeSuffix :: forall m a. (Monad m, Unbox a) => a -> Stream m (Array a) -> Stream m a
231+ [D] flattenArraysRev :: forall m a. (MonadIO m, Unbox a) => Stream m (Array a) -> Stream m a
232+ [D] flattenArrays :: forall m a. (MonadIO m, Unbox a) => Stream m (Array a) -> Stream m a
233+ [C] concatRev
234+ [O] concatRev :: (Monad m, Unbox a) => Stream m (Array a) -> Stream m a
235+ [N] concatRev :: forall m a. (Monad m, Unbox a) => Stream m (Array a) -> Stream m a
236+ [D] bufferChunks :: (MonadIO m, Unbox a) => Stream m a -> m (StreamK m (Array a))
237+[C] Streamly.Internal.Data.Array.Generic
238+ [A] createOf :: MonadIO m => Int -> Fold m a (Array a)
239+ [A] create :: MonadIO m => Fold m a (Array a)
240+[C] Streamly.Internal.Data.Array
241+ [R] ArrayUnsafe
242+ [D] writeWith :: forall m a. (MonadIO m, Unbox a) => Int -> Fold m a (Array a)
243+ [D] writeNUnsafe :: forall m a. (MonadIO m, Unbox a) => Int -> Fold m a (Array a)
244+ [A] unsafePinnedCreateOf :: forall m a. (MonadIO m, Unbox a) => Int -> Fold m a (Array a)
245+ [A] unsafePinnedAsPtr :: MonadIO m => Array a -> (Ptr a -> m b) -> m b
246+ [A] unsafeCreateOf :: forall m a. (MonadIO m, Unbox a) => Int -> Fold m a (Array a)
247+ [D] toStreamDRev :: forall m a. (Monad m, Unbox a) => Array a -> Stream m a
248+ [D] toStreamD :: forall m a. (Monad m, Unbox a) => Array a -> Stream m a
249+ [C] splice
250+ [O] splice :: (MonadIO m, Unbox a) => Array a -> Array a -> m (Array a)
251+ [N] splice :: MonadIO m => Array a -> Array a -> m (Array a)
252+ [A] slicerFromLen :: forall m a. (Monad m, Unbox a) => Int -> Int -> Unfold m (Array a) (Array a)
253+ [A] sliceIndexerFromLen :: forall m a. (Monad m, Unbox a) => Int -> Int -> Unfold m (Array a) (Int, Int)
254+ [D] pinnedWriteNUnsafe :: forall m a. (MonadIO m, Unbox a) => Int -> Fold m a (Array a)
255+ [D] pinnedWriteNAligned :: forall m a. (MonadIO m, Unbox a) => Int -> Int -> Fold m a (Array a)
256+ [D] pinnedWriteN :: forall m a. (MonadIO m, Unbox a) => Int -> Fold m a (Array a)
257+ [D] pinnedWrite :: forall m a. (MonadIO m, Unbox a) => Fold m a (Array a)
258+ [A] pinnedCreateOf :: forall m a. (MonadIO m, Unbox a) => Int -> Fold m a (Array a)
259+ [A] pinnedCreate :: forall m a. (MonadIO m, Unbox a) => Fold m a (Array a)
260+ [A] pinnedCompactLE :: (MonadIO m, Unbox a) => Int -> Stream m (Array a) -> Stream m (Array a)
261+ [A] parseBreakChunksK :: forall m a b. (MonadIO m, Unbox a) => Parser a m b -> StreamK m (Array a) -> m (Either ParseError b, StreamK m (Array a))
262+ [D] nil :: Array a
263+ [A] lPinnedCompactGE :: (MonadIO m, Unbox a) => Int -> Fold m (Array a) () -> Fold m (Array a) ()
264+ [A] lCompactGE :: (MonadIO m, Unbox a) => Int -> Fold m (Array a) () -> Fold m (Array a) ()
265+ [A] interposeSuffix :: forall m a. (Monad m, Unbox a) => a -> Stream m (Array a) -> Stream m a
266+ [A] interpose :: (Monad m, Unbox a) => a -> Stream m (Array a) -> Stream m a
267+ [A] intercalateSuffix :: (Monad m, Unbox a) => Array a -> Stream m (Array a) -> Stream m a
268+ [A] indexReaderFromThenTo :: Unfold m (Int, Int, Int, Array a) a
269+ [A] indexReader :: (Monad m, Unbox a) => Stream m Int -> Unfold m (Array a) a
270+ [A] indexFinder :: (a -> Bool) -> Unfold Identity (Array a) Int
271+ [D] getSlicesFromLen :: forall m a. (Monad m, Unbox a) => Int -> Int -> Unfold m (Array a) (Array a)
272+ [R] getIndicesFromThenTo :: Unfold m (Int, Int, Int, Array a) a
273+ [D] getIndices :: (Monad m, Unbox a) => Stream m Int -> Unfold m (Array a) a
274+ [D] genSlicesFromLen :: forall m a. (Monad m, Unbox a) => Int -> Int -> Unfold m (Array a) (Int, Int)
275+ [D] fromStreamDN :: forall m a. (MonadIO m, Unbox a) => Int -> Stream m a -> m (Array a)
276+ [D] fromStreamD :: forall m a. (MonadIO m, Unbox a) => Stream m a -> m (Array a)
277+ [A] fromPureStreamN :: Unbox a => Int -> Stream Identity a -> Array a
278+ [A] fromPtrN :: Int -> Ptr Word8 -> Array Word8
279+ [A] fromChunksK :: (MonadIO m, Unbox a) => StreamK m (Array a) -> m (Array a)
280+ [A] fromChunks :: (MonadIO m, Unbox a) => Stream m (Array a) -> m (Array a)
281+ [A] fromByteStr :: Ptr Word8 -> Array Word8
282+ [A] foldChunks :: (MonadIO m, Unbox a) => Fold m a b -> Stream m (Array a) -> m b
283+ [A] foldBreakChunksK :: forall m a b. (MonadIO m, Unbox a) => Fold m a b -> StreamK m (Array a) -> m (b, StreamK m (Array a))
284+ [A] foldBreakChunks :: forall m a b. (MonadIO m, Unbox a) => Fold m a b -> Stream m (Array a) -> m (b, Stream m (Array a))
285+ [D] flattenArraysRev :: forall m a. (MonadIO m, Unbox a) => Stream m (Array a) -> Stream m a
286+ [D] flattenArrays :: forall m a. (MonadIO m, Unbox a) => Stream m (Array a) -> Stream m a
287+ [C] findIndicesOf
288+ [O] findIndicesOf :: (a -> Bool) -> Unfold Identity (Array a) Int
289+ [N] findIndicesOf :: (a -> Bool) -> Array a -> Stream Identity Int
290+ [A] fPinnedCompactGE :: (MonadIO m, Unbox a) => Int -> Fold m (Array a) (Array a)
291+ [A] fCompactGE :: (MonadIO m, Unbox a) => Int -> Fold m (Array a) (Array a)
292+ [A] empty :: Array a
293+ [A] createWith :: forall m a. (MonadIO m, Unbox a) => Int -> Fold m a (Array a)
294+ [A] createOf :: forall m a. (MonadIO m, Unbox a) => Int -> Fold m a (Array a)
295+ [A] create :: forall m a. (MonadIO m, Unbox a) => Fold m a (Array a)
296+ [A] concatRev :: forall m a. (Monad m, Unbox a) => Stream m (Array a) -> Stream m a
297+ [A] concat :: (Monad m, Unbox a) => Stream m (Array a) -> Stream m a
298+ [A] compactOnByteSuffix :: MonadIO m => Word8 -> Stream m (Array Word8) -> Stream m (Array Word8)
299+ [A] compactOnByte :: MonadIO m => Word8 -> Stream m (Array Word8) -> Stream m (Array Word8)
300+ [A] compactLE :: (MonadIO m, Unbox a) => Int -> Stream m (Array a) -> Stream m (Array a)
301+ [A] compactGE :: (MonadIO m, Unbox a) => Int -> Stream m (Array a) -> Stream m (Array a)
302+ [A] byteEq :: Array a -> Array a -> Bool
303+ [A] byteCmp :: Array a -> Array a -> Ordering
304+ [A] buildChunks :: (MonadIO m, Unbox a) => Stream m a -> m (StreamK m (Array a))
305+ [D] bufferChunks :: (MonadIO m, Unbox a) => Stream m a -> m (StreamK m (Array a))
306+ [D] asPtrUnsafe :: MonadIO m => Array a -> (Ptr a -> m b) -> m b
core/docs/Changelog.md+46−29
@@ -1,56 +1,73 @@
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+
3## 0.2.0 (Nov 2023)
16## 0.2.0 (Nov 2023)
4
17
18+See [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)
19+for a full list of API changes in this release. Only a few significant
20+changes are mentioned here.
21+
5−### Breaking
22+### Breaking Changes
6
23
7* `ParserK` in `Streamly.Data.ParserK` is not implicitly specialized
24* `ParserK` in `Streamly.Data.ParserK` is not implicitly specialized
8 to arrays anymore. To adapt to the new code, change `ParserK a m
25 to arrays anymore. To adapt to the new code, change `ParserK a m
9 b` to `ParserK (Array a) m b` where the `Array` type comes from
26 b` to `ParserK (Array a) m b` where the `Array` type comes from
10− `Streamly.Data.Array`. This change also changed the signatures of
27+ `Streamly.Data.Array`. This change also affected the signatures of
11 `parseChunks` and `parseBreakChunks`.
28 `parseChunks` and `parseBreakChunks`.
12* Changed the signature of 'Streamly.Data.Stream.handle' to make the
29* Changed the signature of 'Streamly.Data.Stream.handle' to make the
13 exception handler monadic.
30 exception handler monadic.
14−* `getIndex` signature changed in `Streamly.Data.MutArray` and
15− `Streamly.Data.MutArray.Generic`.
16* Behavior change: Exceptions are now rethrown promptly in `bracketIO`.
31* Behavior change: Exceptions are now rethrown promptly in `bracketIO`.
17
32
33+### Enhancements
34+
35+* __Serialization__: Added a `Streamly.Data.MutByteArray` module with a
36+ `Serialize` type class for fast binary serialization. The Data.Array
37+ module supplies the `serialize` and `deserialize` operations for arrays.
38+* __Unpinned Arrays__: Unboxed arrays are now created unpinned by default,
39+ they were created pinned earlier. During IO operations, unpinned arrays
40+ are automatically copied to pinned memory. When arrays are directly
41+ passed to IO operations programmers can choose to create them pinned to
42+ avoid a copy. To create pinned arrays, use the internal APIs with the
43+ `pinned*` prefix.
44+* StreamK now supports native exception handling routines (handle, bracketIO).
45+ Earlier we had to convert it to the `Stream` type for exception handling.
46+
47+### Deprecations
48+
49+See [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)
50+for a full list of deprecations.
51+
18−### Internal Changes
52+### Internal API Changes
19
53
20* Fold constructor has changed, added a `final` field to support
54* Fold constructor has changed, added a `final` field to support
21 finalization and cleanup of a chain of folds. The `extract` field is
55 finalization and cleanup of a chain of folds. The `extract` field is
22 now used only for mapping the fold internal state to fold result for
56 now used only for mapping the fold internal state to fold result for
23− scanning purposes.
57+ scanning purposes. If your fold does not require cleanup you can just use
58+ your existing `extract` function as `final` as well to adapt to this change.
24−* Some low level internal modules have been removed, they are entirely
59+* Many low level internal modules have been removed, they are entirely
25 exported from higher level internal modules. If you were importing any
60 exported from higher level internal modules. If you were importing any
26− of the missing modules then import the higher level modules now.
61+ of the missing low level modules then import the higher level modules instead.
27* Internal module changes:
62* Internal module changes:
28− * Streamly.Internal.Serialize.FromBytes -> Streamly.Internal.Data.Serialize.Parser
63+ * Streamly.Internal.Serialize.FromBytes -> Streamly.Internal.Data.Binary.Parser
29− * Streamly.Internal.Serialize.ToBytes -> Streamly.Internal.Data.Serialize.Stream
64+ * Streamly.Internal.Serialize.ToBytes -> Streamly.Internal.Data.Binary.Stream
30 * Streamly.Internal.Data.Unbox is now exported via Streamly.Internal.Data.Serialize
65 * Streamly.Internal.Data.Unbox is now exported via Streamly.Internal.Data.Serialize
31 * Streamly.Internal.Data.IORef.Unboxed is now exported via Streamly.Internal.Data.Serialize
66 * Streamly.Internal.Data.IORef.Unboxed is now exported via Streamly.Internal.Data.Serialize
32
67
33−### Enhancements
34−
35−* Unboxed arrays are now created unpinned by default, they were
36− created pinned earlier. During IO operations, unpinned arrays are
37− automatically copied to pinned memory. When arrays are directly passed
38− to IO operations programmers can choose to create them pinned to avoid a
39− copy. To create pinned arrays, use the internal APIs with the `pinned*`
40− prefix.
41−* Added a `Streamly.Data.Serialize` module for fast binary serialization.
42−* Added exception handling routines in StreamK (handle, bracketIO).
43−* More APIs added in `Streamly.Data.MutArray` and
44− `Streamly.Data.MutArray.Generic`.
45−
46−### Deprecations
47−
48−* `Streamly.Data.MutArray.newUnpinned` is renamed to
49− `Streamly.Data.MutArray.pinnedNew`
50−
51## 0.1.0 (March 2023)
68## 0.1.0 (March 2023)
52
69
53−Also see [streamly-core-0.1.0 API Changelog](/core/docs/ApiChangelogs/0.1.0.txt) or
70+Also see [streamly-core-0.1.0 API Changelog](https://github.com/composewell/streamly/blob/streamly-0.10.0/core/docs/ApiChangelogs/0.1.0.txt) or
54https://hackage.haskell.org/package/streamly-core-0.1.0/docs/docs/ApiChangelogs/0.1.0.txt
71https://hackage.haskell.org/package/streamly-core-0.1.0/docs/docs/ApiChangelogs/0.1.0.txt
55
72
56`streamly` package is split into two packages, (1) `streamly-core` that
73`streamly` package is split into two packages, (1) `streamly-core` that
core/src/DocTestDataArray.hs+3
@@ -11,9 +11,12 @@
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
14>>> import qualified Streamly.Data.Stream as Stream
15>>> import qualified Streamly.Data.Stream as Stream
16+>>> import qualified Streamly.Data.StreamK as StreamK
15
17
16For APIs that have not been released yet.
18For APIs that have not been released yet.
17
19
18>>> import qualified Streamly.Internal.Data.Array as Array
20>>> import qualified Streamly.Internal.Data.Array as Array
21+>>> import qualified Streamly.Internal.Data.Stream as Stream
19-}
22-}
core/src/DocTestDataStream.hs+2
@@ -6,6 +6,7 @@
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)
9>>> import Data.Either (fromLeft, fromRight, isLeft, isRight, either)
10>>> import Data.Either (fromLeft, fromRight, isLeft, isRight, either)
10>>> import Data.Maybe (fromJust, isJust)
11>>> import Data.Maybe (fromJust, isJust)
11>>> import Data.Function (fix, (&))
12>>> import Data.Function (fix, (&))
@@ -32,6 +33,7 @@ For APIs that have not been released yet.
32>>> import qualified Streamly.Internal.Data.Fold as Fold
33>>> import qualified Streamly.Internal.Data.Fold as Fold
33>>> import qualified Streamly.Internal.Data.Parser as Parser
34>>> import qualified Streamly.Internal.Data.Parser as Parser
34>>> import qualified Streamly.Internal.Data.Stream as Stream
35>>> import qualified Streamly.Internal.Data.Stream as Stream
36+>>> import qualified Streamly.Internal.Data.StreamK as StreamK
35>>> import qualified Streamly.Internal.Data.Unfold as Unfold
37>>> import qualified Streamly.Internal.Data.Unfold as Unfold
36>>> import qualified Streamly.Internal.FileSystem.Dir as Dir
38>>> import qualified Streamly.Internal.FileSystem.Dir as Dir
37-}
39-}
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 (writeNUnsafe)
11+>>> import qualified Streamly.Internal.Data.Array as Array (unsafeCreateOf)
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+41−21
@@ -31,28 +31,45 @@ 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+
34 -- * Construction
47 -- * Construction
35 -- | When performance matters, the fastest way to generate an array is
48 -- | When performance matters, the fastest way to generate an array is
36− -- 'writeN'. 'IsList' and 'IsString' instances can be
49+ -- 'createOf'. 'IsList' and 'IsString' instances can be
37 -- used to conveniently construct arrays from literal values.
50 -- used to conveniently construct arrays from literal values.
38 -- 'OverloadedLists' extension or 'fromList' can be used to construct an
51 -- 'OverloadedLists' extension or 'fromList' can be used to construct an
39 -- array from a list literal. Similarly, 'OverloadedStrings' extension or
52 -- array from a list literal. Similarly, 'OverloadedStrings' extension or
40 -- 'fromList' can be used to construct an array from a string literal.
53 -- 'fromList' can be used to construct an array from a string literal.
41
54
55+ -- ** From Stream
56+ , createOf
57+ , create
58+ , writeLastN -- drop old (ring buffer)
59+
42− -- Pure List APIs
60+ -- ** From List
43 , fromListN
61 , fromListN
44 , fromList
62 , fromList
45
63
46− -- Monadic APIs
64+ -- * To List
47− , writeN -- drop new
48− , write -- full buffer
49− , writeLastN -- drop old (ring buffer)
50−
51− -- * Conversion
52 -- 'GHC.Exts.toList' from "GHC.Exts" can be used to convert an array to a
65 -- 'GHC.Exts.toList' from "GHC.Exts" can be used to convert an array to a
53 -- list.
66 -- list.
54 , toList
67 , toList
55
68
69+ -- * To Stream
70+ , read
71+ , readRev
72+
56 -- * Unfolds
73 -- * Unfolds
57 , reader
74 , reader
58 , readerRev
75 , readerRev
@@ -66,20 +83,24 @@ module Streamly.Data.Array
66 -- , (!!)
83 -- , (!!)
67 , getIndex
84 , getIndex
68
85
69− -- * Unbox Type Class
86+ -- * Serialization
87+ , pinnedSerialize
88+ , deserialize
89+
90+ -- * Re-exports
70 , Unbox (..)
91 , Unbox (..)
92+ , Serialize(..)
71
93
72− -- * Pinning & Unpinning
94+ -- * Deprecated
73− , pin
95+ , writeN -- drop new
74− , unpin
96+ , write -- full buffer
75− , isPinned
76 )
97 )
77where
98where
78
99
79#include "inline.hs"
100#include "inline.hs"
80
101
81−import Streamly.Internal.Data.Array hiding (read, readRev)
102+import Streamly.Internal.Data.Array
82−import Streamly.Internal.Data.Unbox (Unbox (..))
103+import Streamly.Internal.Data.MutByteArray (Unbox(..), Serialize(..))
83
104
84import Prelude hiding (read, length)
105import Prelude hiding (read, length)
85
106
@@ -97,7 +118,7 @@ import Prelude hiding (read, length)
97--
118--
98-- Convert array to stream, and fold the stream:
119-- Convert array to stream, and fold the stream:
99--
120--
100−-- >>> fold f arr = Stream.unfold Array.reader arr & Stream.fold f
121+-- >>> fold f arr = Array.read arr & Stream.fold f
101-- >>> fold Fold.sum (Array.fromList [1,2,3::Int])
122-- >>> fold Fold.sum (Array.fromList [1,2,3::Int])
102-- 6
123-- 6
103--
124--
@@ -105,7 +126,7 @@ import Prelude hiding (read, length)
105--
126--
106-- Convert array to stream, transform, and fold back to array:
127-- Convert array to stream, transform, and fold back to array:
107--
128--
108−-- >>> amap f arr = Stream.unfold Array.reader arr & fmap f & Stream.fold Array.write
129+-- >>> amap f arr = Array.read arr & fmap f & Stream.fold (Array.createOf (Array.length arr))
109-- >>> amap (+1) (Array.fromList [1,2,3::Int])
130-- >>> amap (+1) (Array.fromList [1,2,3::Int])
110-- fromList [2,3,4]
131-- fromList [2,3,4]
111--
132--
@@ -132,12 +153,11 @@ import Prelude hiding (read, length)
132-- See the 'fromPureStream' unreleased API to generate an array from an
153-- See the 'fromPureStream' unreleased API to generate an array from an
133-- Identity stream safely without using MonadIO constraint.
154-- Identity stream safely without using MonadIO constraint.
134--
155--
135−--
136-- Note that 'Identity' streams can be generalized to IO streams:
156-- Note that 'Identity' streams can be generalized to IO streams:
137--
157--
138−-- >>> pure = Stream.fromList [1,2,3] :: Stream Identity Int
158+-- >>> stream = Stream.fromList [1,2,3] :: Stream Identity Int
139−-- >>> generally = Stream.morphInner (return . runIdentity)
159+-- >>> pure2IO s = Stream.morphInner (return . runIdentity) s :: Stream IO Int
140−-- >>> Stream.fold Array.write (generally pure :: Stream IO Int)
160+-- >>> Stream.fold Array.create (pure2IO stream)
141-- fromList [1,2,3]
161-- fromList [1,2,3]
142--
162--
143-- == Programming Tips
163-- == Programming Tips
core/src/Streamly/Data/Array/Generic.hs+16−13
@@ -15,34 +15,37 @@ module Streamly.Data.Array.Generic
15 ( Array
15 ( Array
16
16
17 -- * Construction
17 -- * Construction
18− , A.fromListN
18+ , fromListN
19− , A.fromList
19+ , fromList
20
20
21 -- MonadicAPIs
21 -- MonadicAPIs
22− , A.writeN
22+ , createOf
23− , A.write
23+ , create
24
24
25 -- * Conversion
25 -- * Conversion
26− , A.toList
26+ , toList
27
27
28 -- * Streams
28 -- * Streams
29− , A.read
29+ , read
30− , A.readRev
30+ , readRev
31
31
32 -- * Unfolds
32 -- * Unfolds
33− , A.reader
33+ , reader
34 -- , A.readerRev
34 -- , A.readerRev
35
35
36 -- * Random Access
36 -- * Random Access
37− , A.length
37+ , length
38− , A.getIndex
38+ , 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
43 )
47 )
44where
48where
45
49
46−import Streamly.Internal.Data.Array.Generic (Array)
50+import Streamly.Internal.Data.Array.Generic
47−
51+import Prelude hiding (length, read)
48−import qualified Streamly.Internal.Data.Array.Generic as A
core/src/Streamly/Data/MutArray.hs+18−12
@@ -32,22 +32,27 @@ module Streamly.Data.MutArray
32 -- * Construction
32 -- * Construction
33
33
34 -- Uninitialized Arrays
34 -- Uninitialized Arrays
35− , new
35+ , emptyOf
36− , pinnedNew
36+ , pinnedEmptyOf
37
37
38 -- From containers
38 -- From containers
39 , fromListN
39 , fromListN
40 , fromList
40 , fromList
41− , writeN -- drop new
41+ , createOf
42− , write -- full buffer
42+ , create
43 -- writeLastN
43 -- writeLastN
44
44
45+ -- * Pinning & Unpinning
46+ , pin
47+ , unpin
48+ , isPinned
49+
45 -- * Appending elements
50 -- * Appending elements
46 , snoc
51 , snoc
47
52
48 -- * Appending streams
53 -- * Appending streams
49− , writeAppendN -- XXX Rename to writeSnocN?
54+ , appendN
50− , writeAppend -- XXX Rename to writeSnoc?
55+ , append
51
56
52 -- * Inplace mutation
57 -- * Inplace mutation
53 , putIndex
58 , putIndex
@@ -78,16 +83,17 @@ module Streamly.Data.MutArray
78 -- * Size
83 -- * Size
79 , length
84 , length
80
85
81− -- * Unbox Type Class
86+ -- * Re-exports
82 , Unbox (..)
87 , Unbox (..)
83
88
84− -- * Pinning & Unpinning
85− , pin
86− , unpin
87− , isPinned
88−
89 -- * Deprecated
89 -- * Deprecated
90 , newPinned
90 , newPinned
91+ , new
92+ , pinnedNew
93+ , writeN
94+ , write
95+ , writeAppendN
96+ , writeAppend
91 )
97 )
92where
98where
93
99
core/src/Streamly/Data/MutArray/Generic.hs+8−3
@@ -23,11 +23,11 @@ module Streamly.Data.MutArray.Generic
23 MutArray
23 MutArray
24
24
25 -- * Construction
25 -- * Construction
26− , new
26+ , emptyOf
27 , fromListN
27 , fromListN
28 , fromList
28 , fromList
29− , writeN
29+ , createOf
30− , write
30+ , create
31
31
32 -- * Appending elements
32 -- * Appending elements
33 , snoc
33 , snoc
@@ -56,6 +56,11 @@ module Streamly.Data.MutArray.Generic
56
56
57 -- * Size
57 -- * Size
58 , length
58 , length
59+
60+ -- * Deprecated
61+ , new
62+ , writeN
63+ , write
59 )
64 )
60where
65where
61
66
core/src/Streamly/Data/MutByteArray.hsnew file+106
@@ -0,0 +1,106 @@
1+-- |
2+-- Module : Streamly.Data.MutByteArray
3+-- Copyright : (c) 2023 Composewell Technologies
4+-- License : BSD3-3-Clause
5+-- Maintainer : streamly@composewell.com
6+-- Portability : GHC
7+--
8+-- A low level byte Array type 'MutByteArray', along with type classes 'Unbox'
9+-- and 'Serialize' for fast binary serialization and deserialization of Haskell
10+-- values. Serialization, deserialization performance is similar to, and in
11+-- some cases many times better than the store package. Conceptually, the
12+-- 'Serialize' type class works in the same way as store.
13+--
14+-- Serialize instances are configurable to use constructor names (see
15+-- 'Streamly.Internal.Data.MutByteArray.encodeConstrNames'), record field names (see
16+-- 'Streamly.Internal.Data.MutByteArray.encodeRecordFields') instead of binary
17+-- encoded values. This is an experimental feature which allows JSON like
18+-- properties with faster speed. For example, you can change the order of
19+-- constructors or record fields without affecting serialized value.
20+--
21+-- Higher level unboxed array modules "Streamly.Data.Array" and
22+-- "Streamly.Data.MutArray" are built on top of this module. Unboxed arrays are
23+-- essentially serialized Haskell values. Array modules provide higher level
24+-- serialization routines like 'Streamly.Internal.Data.Array.pinnedSerialize'
25+-- and 'Streamly.Internal.Data.Array.deserialize' from the
26+-- "Streamly.Internal.Data.Array" module.
27+--
28+-- == Mutable Byte Array
29+--
30+-- 'MutByteArray' is a primitive mutable array in the IO monad. 'Unbox' and
31+-- 'Serialize' type classes use this primitive array to serialize data to and
32+-- deserialize it from. This array is used to build higher level unboxed
33+-- array types 'Streamly.Data.MutArray.MutArray' and 'Streamly.Data.Array.Array'.
34+--
35+-- == Using Unbox
36+--
37+-- The 'Unbox' type class is simple and used to serialize non-recursive fixed
38+-- size data types. This type class is primarily used to implement unboxed
39+-- arrays. Unboxed arrays are just a sequence of serialized fixed length
40+-- Haskell data types. Instances of this type class can be derived using
41+-- 'Generic' or template haskell based deriving functions provided in this
42+-- module.
43+--
44+-- Writing a data type to an array using the array creation routines in
45+-- "Streamly.Data.Array" or "Streamly.Data.MutArray" (e.g. @writeN@ or
46+-- @fromListN@), serializes the type to the array. Similarly, reading the data
47+-- type from the array deserializes it. You can also serialize and deserialize
48+-- directly to and from a 'MutByteArray', using the type class methods.
49+--
50+-- == Using Serialize
51+--
52+-- The 'Serialize' type class is a superset of the 'Unbox' type class, it can
53+-- serialize variable length data types as well e.g. Haskell lists. Use
54+-- 'deriveSerialize' to derive the instances of the type class automatically
55+-- and then use the type class methods to serialize and deserialize to and from
56+-- a 'MutByteArray'.
57+--
58+-- See 'Streamly.Internal.Data.Array.pinnedSerialize' and
59+-- 'Streamly.Internal.Data.Array.deserialize' for 'Array' type based
60+-- serialization.
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+--
71+module Streamly.Data.MutByteArray
72+ (
73+
74+ -- * Mutable Byte Array
75+ -- | The standard way to read from or write to a 'MutByteArray' is by using
76+ -- the 'Unbox' or 'Serialize' type class methods.
77+ MutByteArray
78+ , isPinned
79+ , pin
80+ , unpin
81+ , new
82+ , pinnedNew
83+
84+ -- * Unbox
85+ , Unbox(..)
86+ , deriveUnbox
87+
88+ -- * Serialize
89+ , Serialize(..)
90+
91+ -- ** Instance Config
92+ , SerializeConfig
93+ , inlineAddSizeTo
94+ , inlineSerializeAt
95+ , inlineDeserializeAt
96+
97+ -- ** Instance Deriving
98+ , deriveSerialize
99+ , deriveSerializeWith
100+ ) where
101+
102+--------------------------------------------------------------------------------
103+-- Imports
104+--------------------------------------------------------------------------------
105+
106+import Streamly.Internal.Data.MutByteArray
core/src/Streamly/Data/Serialize.hsdeleted−79
@@ -1,79 +0,0 @@
1−-- |
2−-- Module : Streamly.Data.Serialize
3−-- Copyright : (c) 2023 Composewell Technologies
4−-- License : BSD3-3-Clause
5−-- Maintainer : streamly@composewell.com
6−-- Portability : GHC
7−--
8−-- Fast binary serialization and deserialization of Haskell values to and from
9−-- arrays. This module provides two type classes for serialization, 'Unbox' and
10−-- 'Serialize'. The speed is similar to, and in some cases many times faster
11−-- than the store package. Conceptually, the 'Serialize' type class works in
12−-- the same way as store.
13−--
14−-- == Using Unbox
15−--
16−-- The 'Unbox' type class is simple and used to serialize non-recursive fixed
17−-- size data types. This type class is primarily used to implement unboxed
18−-- arrays. Unboxed arrays are just serialization of fixed length Haskell data
19−-- types. Instances of this type class can be derived using 'Generic' or
20−-- template haskell based deriving functions provided in this module. Read the
21−-- type class documentation for more details.
22−--
23−-- Writing a data type to an array using the array creation routines in
24−-- "Streamly.Data.Array" or "Streamly.Data.MutArray", serializes the type to
25−-- the array. Similarly, reading the data type from the array deserializes it.
26−--
27−-- There are no encode/decode routines provided to serialize or deserialize
28−-- using this type class but you can write those easily. Just create a mutable
29−-- byte array and use 'pokeByteIndex' to serialize a type, and use
30−-- 'peekByteIndex' to deserialize it.
31−--
32−-- == Using Serialize
33−--
34−-- The 'Serialize' type class is a superset of the 'Unbox' type class, it can
35−-- serialize variable length data types as well e.g. Haskell lists. Use
36−-- 'deriveSerialize' to derive the instances of the type class automatically
37−-- and then use 'pinnedEncode', 'decode' to serialize and deserialize the type
38−-- respectively. You can also create the array yourself and use the type class
39−-- functions directly.
40−--
41−module Streamly.Data.Serialize
42− (
43−
44− -- * MutableByteArray
45− MutByteArray
46− , isPinned
47− , pin
48− , unpin
49− , newByteArray
50− , pinnedNewByteArray
51−
52− -- * Unbox
53− , Unbox(..)
54− , deriveUnbox
55−
56− -- * Serialize
57− , Serialize(..)
58−
59− -- Deriving instances
60− , SerializeConfig
61− , serializeConfig
62− , inlineSize
63− , inlineSerialize
64− , inlineDeserialize
65−
66− , deriveSerialize
67− , deriveSerializeWith
68−
69− -- Encoding and Decoding
70− -- , encode
71− , pinnedEncode
72− , decode
73− ) where
74−
75−--------------------------------------------------------------------------------
76−-- Imports
77−--------------------------------------------------------------------------------
78−
79−import Streamly.Internal.Data.Serialize
core/src/Streamly/Data/Stream.hs+129−20
@@ -70,6 +70,15 @@ 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+ --
73 , fromPure
82 , fromPure
74 , fromEffect
83 , fromEffect
75 , repeat
84 , repeat
@@ -212,21 +221,57 @@ module Streamly.Data.Stream
212 -- operations like mapping a function over the stream.
221 -- operations like mapping a function over the stream.
213 , foldrM
222 , foldrM
214 , foldr
223 , foldr
224+ -- foldr1
215
225
216 -- ** Specific Folds
226 -- ** Specific Folds
217− -- | Usually you can use the folds in "Streamly.Data.Fold". However, some
227+ -- | Streams are folded using folds in "Streamly.Data.Fold". Here are some
218− -- folds that may be commonly used or may have an edge in performance in
228+ -- idioms and equivalents of Data.List APIs using folds:
219− -- some cases are provided here.
220− --
221− -- Useful idioms:
222 --
229 --
223 -- >>> foldlM' f a = Stream.fold (Fold.foldlM' f a)
230 -- >>> foldlM' f a = Stream.fold (Fold.foldlM' f a)
224− -- >>> foldl1 f = Stream.fold (Fold.foldl1' f)
231+ -- >>> foldl1' f = Stream.fold (Fold.foldl1' f)
225 -- >>> foldl' f a = Stream.fold (Fold.foldl' f a)
232 -- >>> foldl' f a = Stream.fold (Fold.foldl' f a)
226 -- >>> drain = Stream.fold Fold.drain
233 -- >>> drain = Stream.fold Fold.drain
227 -- >>> mapM_ f = Stream.fold (Fold.drainMapM f)
234 -- >>> mapM_ f = Stream.fold (Fold.drainMapM f)
228 -- >>> length = Stream.fold Fold.length
235 -- >>> length = Stream.fold Fold.length
236+ -- >>> genericLength = Stream.fold Fold.lengthGeneric
229 -- >>> head = Stream.fold Fold.one
237 -- >>> 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
230 , toList
275 , toList
231
276
232 -- * Mapping
277 -- * Mapping
@@ -280,6 +325,15 @@ module Streamly.Data.Stream
280 , scan
325 , scan
281 , postscan
326 , postscan
282 -- XXX postscan1 can be implemented using Monoids or Refolds.
327 -- 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
283
337
284 -- ** Specific scans
338 -- ** Specific scans
285 -- Indexing can be considered as a special type of zipping where we zip a
339 -- Indexing can be considered as a special type of zipping where we zip a
@@ -288,6 +342,8 @@ module Streamly.Data.Stream
288
342
289 -- * Insertion
343 -- * Insertion
290 -- | Add elements to the stream.
344 -- | Add elements to the stream.
345+ --
346+ -- >>> insert = Stream.insertBy compare
291
347
292 -- Inserting elements is a special case of interleaving/merging streams.
348 -- Inserting elements is a special case of interleaving/merging streams.
293 , insertBy
349 , insertBy
@@ -323,12 +379,15 @@ module Streamly.Data.Stream
323 -- "Streamly.Internal.Data.Fold" can be used along with 'scanMaybe' to
379 -- "Streamly.Internal.Data.Fold" can be used along with 'scanMaybe' to
324 -- perform stateful filtering operations in general.
380 -- perform stateful filtering operations in general.
325 --
381 --
326− -- Useful idioms:
382+ -- Idioms and equivalents of Data.List APIs:
327 --
383 --
328 -- >>> deleteBy cmp x = Stream.scanMaybe (Fold.deleteBy cmp x)
384 -- >>> deleteBy cmp x = Stream.scanMaybe (Fold.deleteBy cmp x)
385+ -- >>> deleteBy = Stream.deleteBy -- unreleased API
386+ -- >>> delete = deleteBy (==)
329 -- >>> findIndices p = Stream.scanMaybe (Fold.findIndices p)
387 -- >>> findIndices p = Stream.scanMaybe (Fold.findIndices p)
330 -- >>> elemIndices a = findIndices (== a)
388 -- >>> elemIndices a = findIndices (== a)
331 -- >>> uniq = Stream.scanMaybe (Fold.uniqBy (==))
389 -- >>> uniq = Stream.scanMaybe (Fold.uniqBy (==))
390+ -- >>> partition p = Stream.fold (Fold.partition Fold.toList Fold.toList) . fmap (if p then Left else Right)
332 , scanMaybe
391 , scanMaybe
333 , take
392 , take
334 , takeWhile
393 , takeWhile
@@ -336,6 +395,8 @@ module Streamly.Data.Stream
336 , drop
395 , drop
337 , dropWhile
396 , dropWhile
338 , dropWhileM
397 , dropWhileM
398+ -- XXX write to an array in reverse and then read in reverse
399+ -- > dropWhileEnd = reverse . dropWhile p . reverse
339
400
340 -- XXX These are available as scans in folds. We need to check the
401 -- XXX These are available as scans in folds. We need to check the
341 -- performance though. If these are common and we need convenient stream
402 -- performance though. If these are common and we need convenient stream
@@ -372,6 +433,9 @@ module Streamly.Data.Stream
372 -- >>> s = s1 `Stream.append` s2
433 -- >>> s = s1 `Stream.append` s2
373
434
374 -- ** Appending
435 -- ** Appending
436+ -- | Equivalent of Data.List append:
437+ --
438+ -- >>> (++) = Stream.append
375 , append
439 , append
376
440
377 -- ** Interleaving
441 -- ** Interleaving
@@ -382,8 +446,14 @@ module Streamly.Data.Stream
382 , mergeByM
446 , mergeByM
383
447
384 -- ** Zipping
448 -- ** Zipping
449+ -- | Idioms and equivalents of Data.List APIs:
450+ --
451+ -- >>> zip = Stream.zipWith (,)
452+ -- >>> unzip = Stream.fold (Fold.unzip Fold.toList Fold.toList)
385 , zipWith
453 , zipWith
386 , zipWithM
454 , zipWithM
455+ -- XXX zipWith3,4,5,6,7
456+ -- XXX unzip3,4,5,6,7
387 -- , ZipStream (..)
457 -- , ZipStream (..)
388
458
389 -- ** Cross Product
459 -- ** Cross Product
@@ -416,48 +486,89 @@ module Streamly.Data.Stream
416 -- One dimension loops are just a special case of nested loops. For
486 -- One dimension loops are just a special case of nested loops. For
417 -- example, 'concatMap' can degenerate to a simple map operation:
487 -- example, 'concatMap' can degenerate to a simple map operation:
418 --
488 --
419− -- > map f m = S.concatMap (\x -> S.fromPure (f x)) m
489+ -- >>> map f m = Stream.concatMap (\x -> Stream.fromPure (f x)) m
420 --
490 --
421 -- Similarly, 'concatMap' can perform filtering by mapping an element to a
491 -- Similarly, 'concatMap' can perform filtering by mapping an element to a
422 -- 'nil' stream:
492 -- 'nil' stream:
423 --
493 --
424− -- > filter p m = S.concatMap (\x -> if p x then S.fromPure x else S.nil) m
494+ -- >>> filter p m = Stream.concatMap (\x -> if p x then Stream.fromPure x else Stream.nil) m
495+ --
496+ -- Idioms and equivalents of Data.List APIs:
425 --
497 --
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 " "
426
503
427 , concatEffect
504 , concatEffect
428 , concatMap
505 , concatMap
429 , concatMapM
506 , concatMapM
430
507
431 -- * Repeated Fold
508 -- * Repeated Fold
432− -- | Useful idioms:
509+ -- | Idioms and equivalents of Data.List APIs:
433 --
510 --
434− -- >>> splitWithSuffix p f = Stream.foldMany (Fold.takeEndBy p f)
511+ -- >>> groupsOf n = Stream.foldMany (Fold.take n Fold.toList)
435− -- >>> splitOnSuffix p f = Stream.foldMany (Fold.takeEndBy_ p f)
512+ -- >>> groupBy eq = Stream.groupsWhile eq Fold.toList
436− -- >>> groupsBy eq f = Stream.parseMany (Parser.groupBy eq f)
513+ -- >>> groupBy eq = Stream.parseMany (Parser.groupBy eq Fold.toList)
437− -- >>> groupsByRolling eq f = Stream.parseMany (Parser.groupByRolling eq f)
514+ -- >>> groupsByRolling eq = Stream.parseMany (Parser.groupByRolling eq Fold.toList)
438− -- >>> groupsOf n f = Stream.foldMany (Fold.take n f)
515+ -- >>> groups = groupBy (==)
439 , foldMany -- XXX Rename to foldRepeat
516 , foldMany -- XXX Rename to foldRepeat
440 , groupsOf
517 , groupsOf
441 , parseMany
518 , parseMany
442
519
443 -- * Splitting
520 -- * 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)
444 , splitOn
530 , splitOn
445 , wordsBy
531 , wordsBy
446
532
447 -- * Buffered Operations
533 -- * Buffered Operations
448 -- | Operations that require buffering of the stream.
534 -- | Operations that require buffering of the stream.
449 -- Reverse is essentially a left fold followed by an unfold.
535 -- 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+ --
450 , reverse
550 , 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
451
557
452 -- * Multi-Stream folds
558 -- * Multi-Stream folds
453 -- | Operations that consume multiple streams at the same time.
559 -- | Operations that consume multiple streams at the same time.
454 , eqBy
560 , eqBy
455 , cmpBy
561 , cmpBy
456 , isPrefixOf
562 , isPrefixOf
563+ , isInfixOf
564+ -- , isSuffixOf
565+ -- , isSuffixOfUnbox
457 , isSubsequenceOf
566 , isSubsequenceOf
458
567
459 -- trimming sequences
568 -- trimming sequences
460− , stripPrefix
569+ -- , stripPrefix
570+ -- , stripSuffix
571+ -- , stripSuffixUnbox
461
572
462 -- Exceptions and resource management depend on the "exceptions" package
573 -- Exceptions and resource management depend on the "exceptions" package
463 -- XXX We can have IO Stream operations not depending on "exceptions"
574 -- XXX We can have IO Stream operations not depending on "exceptions"
@@ -521,10 +632,8 @@ where
521import Streamly.Internal.Data.Stream
632import Streamly.Internal.Data.Stream
522import Prelude
633import Prelude
523 hiding (filter, drop, dropWhile, take, takeWhile, zipWith, foldr,
634 hiding (filter, drop, dropWhile, take, takeWhile, zipWith, foldr,
524− foldl, map, mapM, mapM_, sequence, all, any, sum, product, elem,
525− notElem, maximum, minimum, head, last, tail, length, null,
526− reverse, iterate, init, and, or, lookup, foldr1, (!!),
635+ mapM, sequence, reverse, iterate, foldr1, repeat, replicate,
527− scanl, scanl1, repeat, replicate, concatMap, span)
636+ concatMap)
528
637
529import qualified Streamly.Internal.Data.Array.Type as Array
638import qualified Streamly.Internal.Data.Array.Type as Array
530
639
core/src/Streamly/Data/Unfold.hs+2
@@ -65,6 +65,8 @@ module Streamly.Data.Unfold
65 -- ** Mapping on Input
65 -- ** Mapping on Input
66 , lmap
66 , lmap
67 , lmapM
67 , lmapM
68+ , first
69+ , second
68
70
69 -- ** Mapping on Output
71 -- ** Mapping on Output
70 , mapM
72 , mapM
core/src/Streamly/FileSystem/Handle.hs+7−6
@@ -81,13 +81,13 @@ module Streamly.FileSystem.Handle
81 -- position of the file handle. The stream ends as soon as EOF is
81 -- position of the file handle. The stream ends as soon as EOF is
82 -- encountered.
82 -- encountered.
83
83
84− -- -- *** Streams
84+ -- *** Streams
85− -- , read
85+ , read
86− -- , readWith
86+ , readWith
87− -- , readChunks
87+ , readChunks
88− -- , readChunksWith
88+ , readChunksWith
89
89
90− -- -- *** Unfolds
90+ -- *** Unfolds
91 , reader
91 , reader
92 , readerWith
92 , readerWith
93 , chunkReader
93 , chunkReader
@@ -112,5 +112,6 @@ module Streamly.FileSystem.Handle
112where
112where
113
113
114import Streamly.Internal.FileSystem.Handle
114import Streamly.Internal.FileSystem.Handle
115+import Prelude hiding (read)
115
116
116#include "DocTestFileSystemHandle.hs"
117#include "DocTestFileSystemHandle.hs"
core/src/Streamly/Internal/Data/Array.hs+608−135
@@ -2,7 +2,6 @@
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−--
6-- License : BSD3
5-- License : BSD3
7-- Maintainer : streamly@composewell.com
6-- Maintainer : streamly@composewell.com
8-- Stability : experimental
7-- Stability : experimental
@@ -20,33 +19,21 @@ module Streamly.Internal.Data.Array
20 module Streamly.Internal.Data.Array.Type
19 module Streamly.Internal.Data.Array.Type
21
20
22 -- * Construction
21 -- * Construction
23− -- Stream Folds
24− , fromStreamN
25− , fromStream
26−
27 -- Monadic Folds
22 -- Monadic Folds
28 , writeLastN
23 , writeLastN
29
24
30− -- * Elimination
31− -- ** Conversion
32−
33− -- ** Unfolds
34− , reader
35− , readerUnsafe
36− , producer -- experimental
37−
38 -- * Random Access
25 -- * Random Access
39 -- , (!!)
26 -- , (!!)
40 , getIndex
27 , getIndex
41 , getIndexRev
28 , getIndexRev
42− , last -- XXX getIndexLast?
29+ , last -- XXX getLastIndex?
43− , getIndices
44− , getIndicesFromThenTo
45 -- , getIndicesFrom -- read from a given position to the end of file
30 -- , getIndicesFrom -- read from a given position to the end of file
46 -- , getIndicesUpto -- read from beginning up to the given position
31 -- , getIndicesUpto -- read from beginning up to the given position
47 -- , getIndicesFromTo
32 -- , getIndicesFromTo
48 -- , getIndicesFromRev -- read from a given position to the beginning of file
33 -- , getIndicesFromRev -- read from a given position to the beginning of file
49 -- , getIndicesUptoRev -- read from end to the given position in file
34 -- , getIndicesUptoRev -- read from end to the given position in file
35+ , indexReader
36+ , indexReaderFromThenTo
50
37
51 -- * Size
38 -- * Size
52 , null
39 , null
@@ -54,6 +41,8 @@ module Streamly.Internal.Data.Array
54 -- * Search
41 -- * Search
55 , binarySearch
42 , binarySearch
56 , findIndicesOf
43 , findIndicesOf
44+ -- getIndicesOf
45+ , indexFinder -- see splitOn
57 -- , findIndexOf
46 -- , findIndexOf
58 -- , find
47 -- , find
59
48
@@ -66,51 +55,89 @@ module Streamly.Internal.Data.Array
66 -- * Subarrays
55 -- * Subarrays
67 , getSliceUnsafe
56 , getSliceUnsafe
68 -- , getSlice
57 -- , getSlice
69− , genSlicesFromLen
58+ , sliceIndexerFromLen
70− , getSlicesFromLen
59+ , slicerFromLen
71 , splitOn
60 , splitOn
72
61
73 -- * Streaming Operations
62 -- * Streaming Operations
74 , streamTransform
63 , streamTransform
75
64
76− -- ** Folding
65+ -- * Folding
77 , streamFold
66 , streamFold
78 , fold
67 , fold
68+
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
88+ , encodeAs
89+ , serialize
90+ , pinnedSerialize
91+ , deserialize
92+
93+ -- * Deprecated
94+ , genSlicesFromLen
95+ , getSlicesFromLen
96+ , getIndices
79 )
97 )
80where
98where
81
99
100+#include "assert.hs"
82#include "inline.hs"
101#include "inline.hs"
83#include "ArrayMacros.h"
102#include "ArrayMacros.h"
84
103
85−import Control.Exception (assert)
86−import Control.Monad (when)
87import Control.Monad.IO.Class (MonadIO(..))
104import Control.Monad.IO.Class (MonadIO(..))
105+-- import Data.Bifunctor (first)
106+-- import Data.Either (fromRight)
88import Data.Functor.Identity (Identity)
107import Data.Functor.Identity (Identity)
89import Data.Proxy (Proxy(..))
108import Data.Proxy (Proxy(..))
90import Data.Word (Word8)
109import Data.Word (Word8)
91import Foreign.C.String (CString)
110import Foreign.C.String (CString)
92import Foreign.Ptr (castPtr)
111import Foreign.Ptr (castPtr)
93import Foreign.Storable (Storable)
112import Foreign.Storable (Storable)
113+import GHC.Types (SPEC(..))
94import Streamly.Internal.Data.Unbox (Unbox(..))
114import Streamly.Internal.Data.Unbox (Unbox(..))
95import Prelude hiding (length, null, last, map, (!!), read, concat)
115import Prelude hiding (length, null, last, map, (!!), read, concat)
96
116
117+import Streamly.Internal.Data.MutByteArray.Type (PinnedState(..), MutByteArray)
118+import Streamly.Internal.Data.Serialize.Type (Serialize)
97import Streamly.Internal.Data.Fold.Type (Fold(..))
119import Streamly.Internal.Data.Fold.Type (Fold(..))
98−import Streamly.Internal.Data.Producer.Type (Producer(..))
120+import Streamly.Internal.Data.Parser (Parser(..), Initial(..), ParseError(..))
99−import Streamly.Internal.Data.Stream (Stream)
121+import Streamly.Internal.Data.Stream (Stream(..))
122+import Streamly.Internal.Data.StreamK (StreamK)
123+import Streamly.Internal.Data.SVar.Type (adaptState, defState)
100−import Streamly.Internal.Data.Tuple.Strict (Tuple3Fused'(..))
124+import Streamly.Internal.Data.Tuple.Strict (Tuple'(..), Tuple3Fused'(..))
101import Streamly.Internal.Data.Unfold.Type (Unfold(..))
125import Streamly.Internal.Data.Unfold.Type (Unfold(..))
102import Streamly.Internal.System.IO (unsafeInlineIO)
126import Streamly.Internal.System.IO (unsafeInlineIO)
103
127
104−import qualified Streamly.Internal.Data.MutArray.Type as MA
128+import qualified Streamly.Internal.Data.Fold.Type as Fold
129+import qualified Streamly.Internal.Data.Serialize.Type as Serialize
130+import qualified Streamly.Internal.Data.MutByteArray.Type as MBA
105import qualified Streamly.Internal.Data.MutArray as MA
131import qualified Streamly.Internal.Data.MutArray as MA
106−import qualified Streamly.Internal.Data.Array.Type as A
107import qualified Streamly.Internal.Data.Fold as FL
132import qualified Streamly.Internal.Data.Fold as FL
108−import qualified Streamly.Internal.Data.Producer.Type as Producer
109−import qualified Streamly.Internal.Data.Producer as Producer
110import qualified Streamly.Internal.Data.Ring as RB
133import 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
111import qualified Streamly.Internal.Data.Stream as D
136import qualified Streamly.Internal.Data.Stream as D
112import qualified Streamly.Internal.Data.Stream as Stream
137import qualified Streamly.Internal.Data.Stream as Stream
138+import qualified Streamly.Internal.Data.StreamK as StreamK
113import qualified Streamly.Internal.Data.Unfold as Unfold
139import qualified Streamly.Internal.Data.Unfold as Unfold
140+import qualified Prelude
114
141
115import Streamly.Internal.Data.Array.Type
142import Streamly.Internal.Data.Array.Type
116
143
@@ -139,82 +166,10 @@ import Streamly.Internal.Data.Array.Type
139-- if we use one array for each chunk. This is still significant to add
166-- if we use one array for each chunk. This is still significant to add
140-- pressure to GC.
167-- pressure to GC.
141
168
142−-------------------------------------------------------------------------------
143−-- Construction
144−-------------------------------------------------------------------------------
145−
146−-- | Create an 'Array' from the first N elements of a stream. The array is
147−-- allocated to size N, if the stream terminates before N elements then the
148−-- array may hold less than N elements.
149−--
150−-- /Pre-release/
151−{-# INLINE fromStreamN #-}
152−fromStreamN :: (MonadIO m, Unbox a) => Int -> Stream m a -> m (Array a)
153−fromStreamN n m = do
154− when (n < 0) $ error "writeN: negative write count specified"
155− A.fromStreamDN n m
156−
157−-- | Create an 'Array' from a stream. This is useful when we want to create a
158−-- single array from a stream of unknown size. 'writeN' is at least twice
159−-- as efficient when the size is already known.
160−--
161−-- Note that if the input stream is too large memory allocation for the array
162−-- may fail. When the stream size is not known, `chunksOf` followed by
163−-- processing of indvidual arrays in the resulting stream should be preferred.
164−--
165−-- /Pre-release/
166−{-# INLINE fromStream #-}
167−fromStream :: (MonadIO m, Unbox a) => Stream m a -> m (Array a)
168−fromStream = Stream.fold A.write
169−-- write m = A.fromStreamD $ D.fromStreamK m
170−
171-------------------------------------------------------------------------------
169-------------------------------------------------------------------------------
172-- Elimination
170-- Elimination
173-------------------------------------------------------------------------------
171-------------------------------------------------------------------------------
174
172
175−{-# INLINE_NORMAL producer #-}
176−producer :: forall m a. (Monad m, Unbox a) => Producer m (Array a) a
177−producer =
178− Producer.translate A.unsafeThaw A.unsafeFreeze
179− $ MA.producerWith (return . unsafeInlineIO)
180−
181−-- | Unfold an array into a stream.
182−--
183−{-# INLINE_NORMAL reader #-}
184−reader :: forall m a. (Monad m, Unbox a) => Unfold m (Array a) a
185−reader = Producer.simplify producer
186−
187−-- | Unfold an array into a stream, does not check the end of the array, the
188−-- user is responsible for terminating the stream within the array bounds. For
189−-- high performance application where the end condition can be determined by
190−-- a terminating fold.
191−--
192−-- Written in the hope that it may be faster than "read", however, in the case
193−-- for which this was written, "read" proves to be faster even though the core
194−-- generated with unsafeRead looks simpler.
195−--
196−-- /Pre-release/
197−--
198−{-# INLINE_NORMAL readerUnsafe #-}
199−readerUnsafe :: forall m a. (Monad m, Unbox a) => Unfold m (Array a) a
200−readerUnsafe = Unfold step inject
201− where
202−
203− inject (Array contents start end) =
204− return (ArrayUnsafe contents end start)
205−
206− {-# INLINE_LATE step #-}
207− step (ArrayUnsafe contents end p) = do
208− -- unsafeInlineIO allows us to run this in Identity monad for pure
209− -- toList/foldr case which makes them much faster due to not
210− -- accumulating the list and fusing better with the pure consumers.
211− --
212− -- This should be safe as the array contents are guaranteed to be
213− -- evaluated/written to before we peek at them.
214− let !x = unsafeInlineIO $ peekByteIndex p contents
215− let !p1 = INDEX_NEXT(p,a)
216− return $ D.Yield x (ArrayUnsafe contents end p1)
217−
218-- |
173-- |
219--
174--
220-- >>> null arr = Array.byteLength arr == 0
175-- >>> null arr = Array.byteLength arr == 0
@@ -222,7 +177,7 @@ readerUnsafe = Unfold step inject
222-- /Pre-release/
177-- /Pre-release/
223{-# INLINE null #-}
178{-# INLINE null #-}
224null :: Array a -> Bool
179null :: Array a -> Bool
225−null arr = A.byteLength arr == 0
180+null arr = byteLength arr == 0
226
181
227-- | Like 'getIndex' but indexes the array in reverse from the end.
182-- | Like 'getIndex' but indexes the array in reverse from the end.
228--
183--
@@ -234,7 +189,7 @@ getIndexRev i arr =
234 $ do
189 $ do
235 let elemPtr = RINDEX_OF(arrEnd arr, i, a)
190 let elemPtr = RINDEX_OF(arrEnd arr, i, a)
236 if i >= 0 && elemPtr >= arrStart arr
191 if i >= 0 && elemPtr >= arrStart arr
237− then Just <$> peekByteIndex elemPtr (arrContents arr)
192+ then Just <$> peekAt elemPtr (arrContents arr)
238 else return Nothing
193 else return Nothing
239
194
240-- |
195-- |
@@ -250,6 +205,8 @@ last = getIndexRev 0
250-- Folds with Array as the container
205-- Folds with Array as the container
251-------------------------------------------------------------------------------
206-------------------------------------------------------------------------------
252
207
208+-- XXX We should generate this from Ring.
209+
253-- | @writeLastN n@ folds a maximum of @n@ elements from the end of the input
210-- | @writeLastN n@ folds a maximum of @n@ elements from the end of the input
254-- stream to an 'Array'.
211-- stream to an 'Array'.
255--
212--
@@ -258,7 +215,7 @@ writeLastN ::
258 (Storable a, Unbox a, MonadIO m) => Int -> Fold m a (Array a)
215 (Storable a, Unbox a, MonadIO m) => Int -> Fold m a (Array a)
259writeLastN n
216writeLastN n
260 | n <= 0 = fmap (const mempty) FL.drain
217 | n <= 0 = fmap (const mempty) FL.drain
261− | otherwise = A.unsafeFreeze <$> Fold step initial done done
218+ | otherwise = unsafeFreeze <$> Fold step initial done done
262
219
263 where
220 where
264
221
@@ -271,11 +228,9 @@ writeLastN n
271 in fmap f $ liftIO $ RB.new n
228 in fmap f $ liftIO $ RB.new n
272
229
273 done (Tuple3Fused' rb rh i) = do
230 done (Tuple3Fused' rb rh i) = do
274− arr <- liftIO $ MA.new n
231+ arr <- MA.new n
275− foldFunc i rh snoc' arr rb
276−
277− -- XXX We should write a read unfold for ring.
232+ -- XXX We should write a read unfold for ring.
278− snoc' b a = liftIO $ MA.snocUnsafe b a
233+ foldFunc i rh MA.snocUnsafe arr rb
279
234
280 foldFunc i
235 foldFunc i
281 | i < n = RB.unsafeFoldRingM
236 | i < n = RB.unsafeFoldRingM
@@ -301,19 +256,26 @@ binarySearch = undefined
301-- compared to streams by using SIMD instructions.
256-- compared to streams by using SIMD instructions.
302-- We can also return a bit array instead.
257-- We can also return a bit array instead.
303
258
259+-- Can use SIMD.
260+
304-- | Perform a linear search to find all the indices where a given element is
261-- | Perform a linear search to find all the indices where a given element is
305-- present in an array.
262-- present in an array.
306--
263--
307-- /Unimplemented/
264-- /Unimplemented/
308−findIndicesOf :: (a -> Bool) -> Unfold Identity (Array a) Int
265+indexFinder :: (a -> Bool) -> Unfold Identity (Array a) Int
309−findIndicesOf = undefined
266+indexFinder = undefined
267+
268+-- |
269+-- /Unimplemented/
270+findIndicesOf :: (a -> Bool) -> Array a -> Stream Identity Int
271+findIndicesOf p = Stream.unfold (indexFinder p)
310
272
311{-
273{-
312findIndexOf :: (a -> Bool) -> Array a -> Maybe Int
274findIndexOf :: (a -> Bool) -> Array a -> Maybe Int
313−findIndexOf p = Unfold.fold Fold.one . Stream.unfold (findIndicesOf p)
275+findIndexOf p = Unfold.fold Fold.one . Stream.unfold (indexFinder p)
314
276
315find :: (a -> Bool) -> Array a -> Bool
277find :: (a -> Bool) -> Array a -> Bool
316−find = Unfold.fold Fold.null . Stream.unfold (findIndicesOf p)
278+find = Unfold.fold Fold.null . Stream.unfold (indexFinder p)
317-}
279-}
318
280
319-------------------------------------------------------------------------------
281-------------------------------------------------------------------------------
@@ -355,29 +317,45 @@ splitOn :: (Monad m, Unbox a) =>
355 (a -> Bool) -> Array a -> Stream m (Array a)
317 (a -> Bool) -> Array a -> Stream m (Array a)
356splitOn predicate arr =
318splitOn predicate arr =
357 fmap (\(i, len) -> getSliceUnsafe i len arr)
319 fmap (\(i, len) -> getSliceUnsafe i len arr)
358− $ D.sliceOnSuffix predicate (A.toStreamD arr)
320+ $ D.indexOnSuffix predicate (read 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)
359
329
330+{-# DEPRECATED genSlicesFromLen "Please use sliceIndexerFromLen instead." #-}
360{-# INLINE genSlicesFromLen #-}
331{-# INLINE genSlicesFromLen #-}
361genSlicesFromLen :: forall m a. (Monad m, Unbox a)
332genSlicesFromLen :: forall m a. (Monad m, Unbox a)
362 => Int -- ^ from index
333 => Int -- ^ from index
363 -> Int -- ^ length of the slice
334 -> Int -- ^ length of the slice
364 -> Unfold m (Array a) (Int, Int)
335 -> Unfold m (Array a) (Int, Int)
365−genSlicesFromLen from len =
336+genSlicesFromLen = sliceIndexerFromLen
366− Unfold.lmap A.unsafeThaw (MA.genSlicesFromLen from len)
367
337
368-- | Generate a stream of slices of specified length from an array, starting
338-- | Generate a stream of slices of specified length from an array, starting
369-- from the supplied array index. The last slice may be shorter than the
339-- from the supplied array index. The last slice may be shorter than the
370-- requested length.
340-- requested length.
371--
341--
372-- /Pre-release//
342-- /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." #-}
373{-# INLINE getSlicesFromLen #-}
353{-# INLINE getSlicesFromLen #-}
374getSlicesFromLen :: forall m a. (Monad m, Unbox a)
354getSlicesFromLen :: forall m a. (Monad m, Unbox a)
375 => Int -- ^ from index
355 => Int -- ^ from index
376 -> Int -- ^ length of the slice
356 -> Int -- ^ length of the slice
377 -> Unfold m (Array a) (Array a)
357 -> Unfold m (Array a) (Array a)
378−getSlicesFromLen from len =
358+getSlicesFromLen = slicerFromLen
379− fmap A.unsafeFreeze
380− $ Unfold.lmap A.unsafeThaw (MA.getSlicesFromLen from len)
381
359
382-------------------------------------------------------------------------------
360-------------------------------------------------------------------------------
383-- Random reads and writes
361-- Random reads and writes
@@ -395,7 +373,7 @@ getIndex i arr =
395 $ do
373 $ do
396 let elemPtr = INDEX_OF(arrStart arr, i, a)
374 let elemPtr = INDEX_OF(arrStart arr, i, a)
397 if i >= 0 && INDEX_VALID(elemPtr, arrEnd arr, a)
375 if i >= 0 && INDEX_VALID(elemPtr, arrEnd arr, a)
398− then Just <$> peekByteIndex elemPtr (arrContents arr)
376+ then Just <$> peekAt elemPtr (arrContents arr)
399 else return Nothing
377 else return Nothing
400
378
401-- | Given a stream of array indices, read the elements on those indices from
379-- | Given a stream of array indices, read the elements on those indices from
@@ -407,19 +385,25 @@ getIndex i arr =
407-- @
385-- @
408-- read =
386-- read =
409-- let u = lmap (\arr -> (0, length arr - 1)) Unfold.enumerateFromTo
387-- let u = lmap (\arr -> (0, length arr - 1)) Unfold.enumerateFromTo
410−-- in Unfold.lmap f (getIndices arr)
388+-- in Unfold.lmap f (indexReader arr)
411--
389--
412-- readRev =
390-- readRev =
413-- let i = length arr - 1
391-- let i = length arr - 1
414−-- in Unfold.lmap f (getIndicesFromThenTo i (i - 1) 0)
392+-- in Unfold.lmap f (indexReaderFromThenTo i (i - 1) 0)
415-- @
393-- @
416--
394--
417-- /Pre-release/
395-- /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." #-}
418{-# INLINE getIndices #-}
404{-# INLINE getIndices #-}
419getIndices :: (Monad m, Unbox a) => Stream m Int -> Unfold m (Array a) a
405getIndices :: (Monad m, Unbox a) => Stream m Int -> Unfold m (Array a) a
420−getIndices m =
406+getIndices = indexReader
421− let unf = MA.getIndicesD (return . unsafeInlineIO) m
422− in Unfold.lmap A.unsafeThaw unf
423
407
424-- | Unfolds @(from, then, to, array)@ generating a finite stream whose first
408-- | Unfolds @(from, then, to, array)@ generating a finite stream whose first
425-- element is the array value from the index @from@ and the successive elements
409-- element is the array value from the index @from@ and the successive elements
@@ -435,9 +419,9 @@ getIndices m =
435-- @
419-- @
436--
420--
437-- /Unimplemented/
421-- /Unimplemented/
438−{-# INLINE getIndicesFromThenTo #-}
422+{-# INLINE indexReaderFromThenTo #-}
439−getIndicesFromThenTo :: Unfold m (Int, Int, Int, Array a) a
423+indexReaderFromThenTo :: Unfold m (Int, Int, Int, Array a) a
440−getIndicesFromThenTo = undefined
424+indexReaderFromThenTo = undefined
441
425
442-------------------------------------------------------------------------------
426-------------------------------------------------------------------------------
443-- Transform via stream operations
427-- Transform via stream operations
@@ -454,7 +438,7 @@ getIndicesFromThenTo = undefined
454{-# INLINE runPipe #-}
438{-# INLINE runPipe #-}
455runPipe :: (MonadIO m, Unbox a, Unbox b)
439runPipe :: (MonadIO m, Unbox a, Unbox b)
456 => Pipe m a b -> Array a -> m (Array b)
440 => Pipe m a b -> Array a -> m (Array b)
457−runPipe f arr = P.runPipe (toArrayMinChunk (length arr)) $ f (A.read arr)
441+runPipe f arr = P.runPipe (toArrayMinChunk (length arr)) $ f (read arr)
458-}
442-}
459
443
460-- XXX For transformations that cannot change the number of elements e.g. "map"
444-- XXX For transformations that cannot change the number of elements e.g. "map"
@@ -468,7 +452,7 @@ runPipe f arr = P.runPipe (toArrayMinChunk (length arr)) $ f (A.read arr)
468streamTransform :: forall m a b. (MonadIO m, Unbox a, Unbox b)
452streamTransform :: forall m a b. (MonadIO m, Unbox a, Unbox b)
469 => (Stream m a -> Stream m b) -> Array a -> m (Array b)
453 => (Stream m a -> Stream m b) -> Array a -> m (Array b)
470streamTransform f arr =
454streamTransform f arr =
471− Stream.fold (A.writeWith (length arr)) $ f (A.read arr)
455+ Stream.fold (createWith (length arr)) $ f (read arr)
472
456
473-------------------------------------------------------------------------------
457-------------------------------------------------------------------------------
474-- Casts
458-- Casts
@@ -502,7 +486,7 @@ asBytes = castUnsafe
502--
486--
503cast :: forall a b. (Unbox b) => Array a -> Maybe (Array b)
487cast :: forall a b. (Unbox b) => Array a -> Maybe (Array b)
504cast arr =
488cast arr =
505− let len = A.byteLength arr
489+ let len = byteLength arr
506 r = len `mod` SIZE_OF(b)
490 r = len `mod` SIZE_OF(b)
507 in if r /= 0
491 in if r /= 0
508 then Nothing
492 then Nothing
@@ -520,9 +504,9 @@ cast arr =
520--
504--
521asCStringUnsafe :: Array a -> (CString -> IO b) -> IO b
505asCStringUnsafe :: Array a -> (CString -> IO b) -> IO b
522asCStringUnsafe arr act = do
506asCStringUnsafe arr act = do
523− let arr1 = asBytes arr <> A.fromList [0]
507+ let arr1 = asBytes arr <> fromList [0]
524− -- asPtrUnsafe makes sure the array is pinned
508+ -- unsafePinnedAsPtr makes sure the array is pinned
525− asPtrUnsafe arr1 $ \ptr -> act (castPtr ptr)
509+ unsafePinnedAsPtr arr1 $ \ptr -> act (castPtr ptr)
526
510
527-------------------------------------------------------------------------------
511-------------------------------------------------------------------------------
528-- Folds
512-- Folds
@@ -535,11 +519,500 @@ asCStringUnsafe arr act = do
535-- /Pre-release/
519-- /Pre-release/
536{-# INLINE fold #-}
520{-# INLINE fold #-}
537fold :: forall m a b. (Monad m, Unbox a) => Fold m a b -> Array a -> m b
521fold :: forall m a b. (Monad m, Unbox a) => Fold m a b -> Array a -> m b
538−fold f arr = Stream.fold f (A.read arr)
522+fold f arr = Stream.fold f (read arr)
539
523
540-- | Fold an array using a stream fold operation.
524-- | Fold an array using a stream fold operation.
541--
525--
542-- /Pre-release/
526-- /Pre-release/
543{-# INLINE streamFold #-}
527{-# INLINE streamFold #-}
544streamFold :: (Monad m, Unbox a) => (Stream m a -> m b) -> Array a -> m b
528streamFold :: (Monad m, Unbox a) => (Stream m a -> m b) -> Array a -> m b
545−streamFold f arr = f (A.read arr)
529+streamFold f arr = f (read arr)
530+
531+--------------------------------------------------------------------------------
532+-- Serialization
533+--------------------------------------------------------------------------------
534+
535+{-# INLINE encodeAs #-}
536+encodeAs :: forall a. Serialize a => PinnedState -> a -> Array Word8
537+encodeAs ps a =
538+ unsafeInlineIO $ do
539+ let len = Serialize.addSizeTo 0 a
540+ mbarr <- MBA.newBytesAs ps len
541+ off <- Serialize.serializeAt 0 mbarr a
542+ assertM(len == off)
543+ pure $ Array mbarr 0 off
544+
545+-- |
546+-- Properties:
547+-- 1. Identity: @deserialize . serialize == id@
548+-- 2. Encoded equivalence: @serialize a == serialize a@
549+{-# INLINE serialize #-}
550+serialize :: Serialize a => a -> Array Word8
551+serialize = encodeAs Unpinned
552+
553+-- | 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
555+-- 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 #-}
561+pinnedSerialize :: Serialize a => a -> Array Word8
562+pinnedSerialize = encodeAs Pinned
563+
564+-- | Decode a Haskell type from a byte array containing its serialized
565+-- representation.
566+{-# INLINE deserialize #-}
567+deserialize :: Serialize a => Array Word8 -> a
568+deserialize arr@(Array {..}) = unsafeInlineIO $ do
569+ let lenArr = length arr
570+ (off, val) <-
571+ Serialize.deserializeAt arrStart arrContents (arrStart + lenArr)
572+ assertM(off == arrStart + lenArr)
573+ pure val
574+
575+-------------------------------------------------------------------------------
576+-- Streams of Arrays
577+-------------------------------------------------------------------------------
578+
579+-- TODO: efficiently compare two streams of arrays. Two streams can have chunks
580+-- of different sizes, we can handle that in the stream comparison abstraction.
581+-- This could be useful e.g. to fast compare whether two files differ.
582+
583+-- | Insert the given element between arrays and flatten.
584+--
585+-- >>> interpose x = Stream.interpose x Array.reader
586+--
587+{-# INLINE interpose #-}
588+interpose :: (Monad m, Unbox a) => a -> Stream m (Array a) -> Stream m a
589+interpose x = D.interpose x reader
590+
591+data FlattenState s =
592+ OuterLoop s
593+ | InnerLoop s !MutByteArray !Int !Int
594+
595+-- | Insert the given element after each array and flatten. This is similar to
596+-- unlines.
597+--
598+-- >>> interposeSuffix x = Stream.interposeSuffix x Array.reader
599+--
600+{-# INLINE_NORMAL interposeSuffix #-}
601+interposeSuffix :: forall m a. (Monad m, Unbox a)
602+ => a -> Stream m (Array a) -> Stream m a
603+-- This does not require MonadIO constraint.
604+-- interposeSuffix x = D.interposeSuffix x reader
605+interposeSuffix sep (D.Stream step state) = D.Stream step' (OuterLoop state)
606+
607+ where
608+
609+ {-# INLINE_LATE step' #-}
610+ step' gst (OuterLoop st) = do
611+ r <- step (adaptState gst) st
612+ return $ case r of
613+ D.Yield Array{..} s ->
614+ D.Skip (InnerLoop s arrContents arrStart arrEnd)
615+ D.Skip s -> D.Skip (OuterLoop s)
616+ D.Stop -> D.Stop
617+
618+ step' _ (InnerLoop st _ p end) | p == end =
619+ return $ D.Yield sep $ OuterLoop st
620+
621+ step' _ (InnerLoop st contents p end) = do
622+ x <- pure $ unsafeInlineIO $ peekAt p contents
623+ return $ D.Yield x (InnerLoop st contents (INDEX_NEXT(p,a)) end)
624+
625+-- | Insert the given array after each array and flatten.
626+--
627+-- >>> intercalateSuffix = Stream.intercalateSuffix Array.reader
628+--
629+{-# INLINE intercalateSuffix #-}
630+intercalateSuffix :: (Monad m, Unbox a)
631+ => Array a -> Stream m (Array a) -> Stream m a
632+intercalateSuffix = D.intercalateSuffix reader
633+
634+-- | @compactLE n@ coalesces adjacent arrays in the input stream
635+-- only if the combined size would be less than or equal to n.
636+--
637+-- Generates unpinned arrays irrespective of the pinning status of input
638+-- arrays.
639+{-# INLINE_NORMAL compactLE #-}
640+compactLE :: (MonadIO m, Unbox a)
641+ => Int -> Stream m (Array a) -> Stream m (Array a)
642+compactLE n stream =
643+ D.map unsafeFreeze $ MA.compactLE n $ D.map unsafeThaw stream
644+
645+-- | Pinned version of 'compactLE'.
646+{-# INLINE_NORMAL pinnedCompactLE #-}
647+pinnedCompactLE :: (MonadIO m, Unbox a)
648+ => Int -> Stream m (Array a) -> Stream m (Array a)
649+pinnedCompactLE n stream =
650+ D.map unsafeFreeze $ MA.pinnedCompactLE n $ D.map unsafeThaw stream
651+
652+-- | Split a stream of arrays on a given separator byte, dropping the separator
653+-- and coalescing all the arrays between two separators into a single array.
654+--
655+{-# INLINE compactOnByte #-}
656+compactOnByte
657+ :: (MonadIO m)
658+ => Word8
659+ -> Stream m (Array Word8)
660+ -> Stream m (Array Word8)
661+compactOnByte byte =
662+ fmap unsafeFreeze . MA.compactOnByte byte . fmap unsafeThaw
663+
664+-- | Like 'compactOnByte' considers the separator in suffix position instead of
665+-- infix position.
666+{-# INLINE compactOnByteSuffix #-}
667+compactOnByteSuffix
668+ :: (MonadIO m)
669+ => Word8
670+ -> Stream m (Array Word8)
671+ -> Stream m (Array Word8)
672+compactOnByteSuffix byte =
673+ fmap unsafeFreeze . MA.compactOnByteSuffix byte . fmap unsafeThaw
674+
675+-------------------------------------------------------------------------------
676+-- Folding Streams of Arrays
677+-------------------------------------------------------------------------------
678+
679+-- XXX This should not be used for breaking a stream as the D.cons used in
680+-- reconstructing the stream could be very bad for performance. This can only
681+-- be useful in folding without breaking.
682+{-# INLINE_NORMAL foldBreakChunks #-}
683+foldBreakChunks :: forall m a b. (MonadIO m, Unbox a) =>
684+ Fold m a b -> Stream m (Array a) -> m (b, Stream m (Array a))
685+foldBreakChunks (Fold fstep initial _ final) stream@(Stream step state) = do
686+ res <- initial
687+ case res of
688+ Fold.Partial fs -> go SPEC state fs
689+ Fold.Done fb -> return $! (fb, stream)
690+
691+ where
692+
693+ {-# INLINE go #-}
694+ go !_ st !fs = do
695+ r <- step defState st
696+ case r of
697+ Stream.Yield (Array contents start end) s ->
698+ let fp = Tuple' end contents
699+ in goArray SPEC s fp start fs
700+ Stream.Skip s -> go SPEC s fs
701+ Stream.Stop -> do
702+ b <- final fs
703+ return (b, D.nil)
704+
705+ goArray !_ s (Tuple' end _) !cur !fs
706+ | cur == end = do
707+ go SPEC s fs
708+ goArray !_ st fp@(Tuple' end contents) !cur !fs = do
709+ x <- liftIO $ peekAt cur contents
710+ res <- fstep fs x
711+ let next = INDEX_NEXT(cur,a)
712+ case res of
713+ Fold.Done b -> do
714+ let arr = Array contents next end
715+ return $! (b, D.cons arr (D.Stream step st))
716+ Fold.Partial fs1 -> goArray SPEC st fp next fs1
717+
718+-- This may be more robust wrt fusion compared to unfoldMany?
719+
720+-- | Fold a stream of arrays using a 'Fold'. This is equivalent to the
721+-- following:
722+--
723+-- >>> foldChunks f = Stream.fold f . Stream.unfoldMany Array.reader
724+--
725+foldChunks :: (MonadIO m, Unbox a) => Fold m a b -> Stream m (Array a) -> m b
726+foldChunks f s = fmap fst (foldBreakChunks f s)
727+-- foldStream f = Stream.fold f . Stream.unfoldMany reader
728+
729+-- | Fold a stream of arrays using a 'Fold' and return the remaining stream.
730+--
731+-- The following alternative to this function allows composing the fold using
732+-- the parser Monad:
733+--
734+-- @
735+-- foldBreakStreamK f s =
736+-- fmap (first (fromRight undefined))
737+-- $ StreamK.parseBreakChunks (ParserK.adaptC (Parser.fromFold f)) s
738+-- @
739+--
740+-- We can compare perf and remove this one or define it in terms of that.
741+--
742+foldBreakChunksK :: forall m a b. (MonadIO m, Unbox a) =>
743+ Fold m a b -> StreamK m (Array a) -> m (b, StreamK m (Array a))
744+{-
745+foldBreakChunksK f s =
746+ fmap (first (fromRight undefined))
747+ $ StreamK.parseBreakChunks (ParserK.adaptC (Parser.fromFold f)) s
748+-}
749+foldBreakChunksK (Fold fstep initial _ final) stream = do
750+ res <- initial
751+ case res of
752+ Fold.Partial fs -> go fs stream
753+ Fold.Done fb -> return (fb, stream)
754+
755+ where
756+
757+ {-# INLINE go #-}
758+ go !fs st = do
759+ let stop = (, StreamK.nil) <$> final fs
760+ single a = yieldk a StreamK.nil
761+ yieldk (Array contents start end) r =
762+ let fp = Tuple' end contents
763+ in goArray fs r fp start
764+ in StreamK.foldStream defState yieldk single stop st
765+
766+ goArray !fs st (Tuple' end _) !cur
767+ | cur == end = do
768+ go fs st
769+ goArray !fs st fp@(Tuple' end contents) !cur = do
770+ x <- liftIO $ peekAt cur contents
771+ res <- fstep fs x
772+ let next = INDEX_NEXT(cur,a)
773+ case res of
774+ Fold.Done b -> do
775+ let arr = Array contents next end
776+ return $! (b, StreamK.cons arr st)
777+ Fold.Partial fs1 -> goArray fs1 st fp next
778+
779+{-
780+-- This can be generalized to any type provided it can be unfolded to a stream
781+-- and it can be combined using a semigroup operation.
782+--
783+{-# INLINE_NORMAL parseBreakD #-}
784+parseBreakD ::
785+ forall m a b. (MonadIO m, MonadThrow m, Unbox a)
786+ => PRD.Parser a m b
787+ -> D.Stream m (Array.Array a)
788+ -> m (b, D.Stream m (Array.Array a))
789+parseBreakD
790+ (PRD.Parser pstep initial extract) stream@(D.Stream step state) = do
791+
792+ res <- initial
793+ case res of
794+ PRD.IPartial s -> go SPEC state (List []) s
795+ PRD.IDone b -> return (b, stream)
796+ PRD.IError err -> throwM $ ParseError err
797+
798+ where
799+
800+ -- "backBuf" contains last few items in the stream that we may have to
801+ -- backtrack to.
802+ --
803+ -- XXX currently we are using a dumb list based approach for backtracking
804+ -- buffer. This can be replaced by a sliding/ring buffer using Data.Array.
805+ -- That will allow us more efficient random back and forth movement.
806+ go !_ st backBuf !pst = do
807+ r <- step defState st
808+ case r of
809+ D.Yield (Array contents start end) s ->
810+ gobuf SPEC s backBuf
811+ (Tuple' end contents) start pst
812+ D.Skip s -> go SPEC s backBuf pst
813+ D.Stop -> do
814+ b <- extract pst
815+ return (b, D.nil)
816+
817+ -- Use strictness on "cur" to keep it unboxed
818+ gobuf !_ s backBuf (Tuple' end _) !cur !pst
819+ | cur == end = do
820+ go SPEC s backBuf pst
821+ gobuf !_ s backBuf fp@(Tuple' end contents) !cur !pst = do
822+ x <- liftIO $ peekByteIndex contents cur
823+ pRes <- pstep pst x
824+ let next = INDEX_NEXT(cur,a)
825+ case pRes of
826+ PR.Partial 0 pst1 ->
827+ gobuf SPEC s (List []) fp next pst1
828+ PR.Partial n pst1 -> do
829+ assert (n <= Prelude.length (x:getList backBuf)) (return ())
830+ let src0 = Prelude.take n (x:getList backBuf)
831+ arr0 = A.fromListN n (Prelude.reverse src0)
832+ arr1 = Array contents next end
833+ src = arr0 <> arr1
834+ let !(Array cont1 start end1) = src
835+ fp1 = Tuple' end1 cont1
836+ gobuf SPEC s (List []) fp1 start pst1
837+ PR.Continue 0 pst1 ->
838+ gobuf SPEC s (List (x:getList backBuf)) fp next pst1
839+ PR.Continue n pst1 -> do
840+ assert (n <= Prelude.length (x:getList backBuf)) (return ())
841+ let (src0, buf1) = splitAt n (x:getList backBuf)
842+ arr0 = A.fromListN n (Prelude.reverse src0)
843+ arr1 = Array contents next end
844+ src = arr0 <> arr1
845+ let !(Array cont1 start end1) = src
846+ fp1 = Tuple' end1 cont1
847+ gobuf SPEC s (List buf1) fp1 start pst1
848+ PR.Done 0 b -> do
849+ let arr = Array contents next end
850+ return (b, D.cons arr (D.Stream step s))
851+ PR.Done n b -> do
852+ assert (n <= Prelude.length (x:getList backBuf)) (return ())
853+ let src0 = Prelude.take n (x:getList backBuf)
854+ -- XXX create the array in reverse instead
855+ arr0 = A.fromListN n (Prelude.reverse src0)
856+ arr1 = Array contents next end
857+ -- XXX Use StreamK to avoid adding arbitrary layers of
858+ -- constructors every time.
859+ str = D.cons arr0 (D.cons arr1 (D.Stream step s))
860+ return (b, str)
861+ PR.Error err -> throwM $ ParseError err
862+-}
863+
864+-- | Parse an array stream using the supplied 'Parser'. Returns the parse
865+-- result and the unconsumed stream. Throws 'ParseError' if the parse fails.
866+--
867+-- The following alternative to this function allows composing the parser using
868+-- the parser Monad:
869+--
870+-- >>> parseBreakStreamK p = StreamK.parseBreakChunks (ParserK.adaptC p)
871+--
872+-- We can compare perf and remove this one or define it in terms of that.
873+--
874+-- /Internal/
875+--
876+{-# INLINE_NORMAL parseBreakChunksK #-}
877+parseBreakChunksK ::
878+ forall m a b. (MonadIO m, Unbox a)
879+ => Parser a m b
880+ -> StreamK m (Array a)
881+ -> m (Either ParseError b, StreamK m (Array a))
882+-- parseBreakStreamK p = StreamK.parseBreakChunks (ParserK.adaptC p)
883+parseBreakChunksK (Parser pstep initial extract) stream = do
884+ res <- initial
885+ case res of
886+ IPartial s -> go s stream []
887+ IDone b -> return (Right b, stream)
888+ IError err -> return (Left (ParseError err), stream)
889+
890+ where
891+
892+ -- "backBuf" contains last few items in the stream that we may have to
893+ -- backtrack to.
894+ --
895+ -- XXX currently we are using a dumb list based approach for backtracking
896+ -- buffer. This can be replaced by a sliding/ring buffer using Data.Array.
897+ -- That will allow us more efficient random back and forth movement.
898+ go !pst st backBuf = do
899+ let stop = goStop pst backBuf -- (, K.nil) <$> extract pst
900+ single a = yieldk a StreamK.nil
901+ yieldk arr r = goArray pst backBuf r arr
902+ in StreamK.foldStream defState yieldk single stop st
903+
904+ -- Use strictness on "cur" to keep it unboxed
905+ goArray !pst backBuf st (Array _ cur end) | cur == end = go pst st backBuf
906+ goArray !pst backBuf st (Array contents cur end) = do
907+ x <- liftIO $ peekAt cur contents
908+ pRes <- pstep pst x
909+ let next = INDEX_NEXT(cur,a)
910+ case pRes of
911+ Parser.Partial 0 s ->
912+ goArray s [] st (Array contents next end)
913+ Parser.Partial n s -> do
914+ assert (n <= Prelude.length (x:backBuf)) (return ())
915+ let src0 = Prelude.take n (x:backBuf)
916+ arr0 = fromListN n (Prelude.reverse src0)
917+ arr1 = Array contents next end
918+ src = arr0 <> arr1
919+ goArray s [] st src
920+ Parser.Continue 0 s ->
921+ goArray s (x:backBuf) st (Array contents next end)
922+ Parser.Continue n s -> do
923+ assert (n <= Prelude.length (x:backBuf)) (return ())
924+ let (src0, buf1) = Prelude.splitAt n (x:backBuf)
925+ arr0 = fromListN n (Prelude.reverse src0)
926+ arr1 = Array contents next end
927+ src = arr0 <> arr1
928+ goArray s buf1 st src
929+ Parser.Done 0 b -> do
930+ let arr = Array contents next end
931+ return (Right b, StreamK.cons arr st)
932+ Parser.Done n b -> do
933+ assert (n <= Prelude.length (x:backBuf)) (return ())
934+ let src0 = Prelude.take n (x:backBuf)
935+ -- XXX Use fromListRevN once implemented
936+ -- arr0 = A.fromListRevN n src0
937+ arr0 = fromListN n (Prelude.reverse src0)
938+ arr1 = Array contents next end
939+ str = StreamK.cons arr0 (StreamK.cons arr1 st)
940+ return (Right b, str)
941+ Parser.Error err -> do
942+ let n = Prelude.length backBuf
943+ arr0 = fromListN n (Prelude.reverse backBuf)
944+ arr1 = Array contents cur end
945+ str = StreamK.cons arr0 (StreamK.cons arr1 stream)
946+ return (Left (ParseError err), str)
947+
948+ -- This is a simplified goArray
949+ goExtract !pst backBuf (Array _ cur end)
950+ | cur == end = goStop pst backBuf
951+ goExtract !pst backBuf (Array contents cur end) = do
952+ x <- liftIO $ peekAt cur contents
953+ pRes <- pstep pst x
954+ let next = INDEX_NEXT(cur,a)
955+ case pRes of
956+ Parser.Partial 0 s ->
957+ goExtract s [] (Array contents next end)
958+ Parser.Partial n s -> do
959+ assert (n <= Prelude.length (x:backBuf)) (return ())
960+ let src0 = Prelude.take n (x:backBuf)
961+ arr0 = fromListN n (Prelude.reverse src0)
962+ arr1 = Array contents next end
963+ src = arr0 <> arr1
964+ goExtract s [] src
965+ Parser.Continue 0 s ->
966+ goExtract s backBuf (Array contents next end)
967+ Parser.Continue n s -> do
968+ assert (n <= Prelude.length (x:backBuf)) (return ())
969+ let (src0, buf1) = Prelude.splitAt n (x:backBuf)
970+ arr0 = fromListN n (Prelude.reverse src0)
971+ arr1 = Array contents next end
972+ src = arr0 <> arr1
973+ goExtract s buf1 src
974+ Parser.Done 0 b -> do
975+ let arr = Array contents next end
976+ return (Right b, StreamK.fromPure arr)
977+ Parser.Done n b -> do
978+ assert (n <= Prelude.length backBuf) (return ())
979+ let src0 = Prelude.take n backBuf
980+ -- XXX Use fromListRevN once implemented
981+ -- arr0 = A.fromListRevN n src0
982+ arr0 = fromListN n (Prelude.reverse src0)
983+ arr1 = Array contents next end
984+ str = StreamK.cons arr0 (StreamK.fromPure arr1)
985+ return (Right b, str)
986+ Parser.Error err -> do
987+ let n = Prelude.length backBuf
988+ arr0 = fromListN n (Prelude.reverse backBuf)
989+ arr1 = Array contents cur end
990+ str = StreamK.cons arr0 (StreamK.cons arr1 stream)
991+ return (Left (ParseError err), str)
992+
993+ -- This is a simplified goExtract
994+ {-# INLINE goStop #-}
995+ goStop !pst backBuf = do
996+ pRes <- extract pst
997+ case pRes of
998+ Parser.Partial _ _ -> error "Bug: parseBreak: Partial in extract"
999+ Parser.Continue 0 s ->
1000+ goStop s backBuf
1001+ Parser.Continue n s -> do
1002+ assert (n <= Prelude.length backBuf) (return ())
1003+ let (src0, buf1) = Prelude.splitAt n backBuf
1004+ arr = fromListN n (Prelude.reverse src0)
1005+ goExtract s buf1 arr
1006+ Parser.Done 0 b ->
1007+ return (Right b, StreamK.nil)
1008+ Parser.Done n b -> do
1009+ assert (n <= Prelude.length backBuf) (return ())
1010+ let src0 = Prelude.take n backBuf
1011+ -- XXX Use fromListRevN once implemented
1012+ -- arr0 = A.fromListRevN n src0
1013+ arr0 = fromListN n (Prelude.reverse src0)
1014+ return (Right b, StreamK.fromPure arr0)
1015+ Parser.Error err -> do
1016+ let n = Prelude.length backBuf
1017+ arr0 = fromListN n (Prelude.reverse backBuf)
1018+ return (Left (ParseError err), StreamK.fromPure arr0)
core/src/Streamly/Internal/Data/Array/Generic.hs+22−6
@@ -11,8 +11,8 @@ module Streamly.Internal.Data.Array.Generic
11
11
12 -- * Construction
12 -- * Construction
13 , nil
13 , nil
14− , writeN
14+ , createOf
15− , write
15+ , create
16 , writeWith
16 , writeWith
17 , writeLastN
17 , writeLastN
18
18
@@ -44,6 +44,10 @@ 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
47 )
51 )
48where
52where
49
53
@@ -104,21 +108,33 @@ nil = unsafePerformIO $ unsafeFreeze <$> MArray.nil
104-- Construction - Folds
108-- Construction - Folds
105-------------------------------------------------------------------------------
109-------------------------------------------------------------------------------
106
110
107−{-# INLINE_NORMAL writeN #-}
111+{-# INLINE_NORMAL createOf #-}
112+createOf :: MonadIO m => Int -> Fold m a (Array a)
113+createOf = fmap unsafeFreeze <$> MArray.createOf
114+
115+-- XXX Deprecate in major
116+-- {-# DEPRECATED writeN "Please use createOf instead." #-}
117+{-# INLINE writeN #-}
108writeN :: MonadIO m => Int -> Fold m a (Array a)
118writeN :: MonadIO m => Int -> Fold m a (Array a)
109−writeN = fmap unsafeFreeze <$> MArray.writeN
119+writeN = createOf
110
120
111{-# INLINE_NORMAL writeWith #-}
121{-# INLINE_NORMAL writeWith #-}
112writeWith :: MonadIO m => Int -> Fold m a (Array a)
122writeWith :: MonadIO m => Int -> Fold m a (Array a)
113−writeWith elemCount = unsafeFreeze <$> MArray.writeWith elemCount
123+writeWith elemCount = unsafeFreeze <$> MArray.createWith elemCount
114
124
115-- | Fold the whole input to a single array.
125-- | Fold the whole input to a single array.
116--
126--
117-- /Caution! Do not use this on infinite streams./
127-- /Caution! Do not use this on infinite streams./
118--
128--
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." #-}
119{-# INLINE write #-}
135{-# INLINE write #-}
120write :: MonadIO m => Fold m a (Array a)
136write :: MonadIO m => Fold m a (Array a)
121−write = fmap unsafeFreeze MArray.write
137+write = create
122
138
123fromPureStream :: Stream Identity a -> Array a
139fromPureStream :: Stream Identity a -> Array a
124fromPureStream x =
140fromPureStream x =
core/src/Streamly/Internal/Data/Array/Stream.hs+57−564
@@ -12,6 +12,7 @@
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." #-}
15 (
16 (
16 -- * Creation
17 -- * Creation
17 Array.chunksOf
18 Array.chunksOf
@@ -19,13 +20,13 @@ module Streamly.Internal.Data.Array.Stream
19 , Array.bufferChunks
20 , Array.bufferChunks
20
21
21 -- * Flattening to elements
22 -- * Flattening to elements
22− , concat
23+ , Array.concat
23 , Array.flattenArrays
24 , Array.flattenArrays
24− , concatRev
25+ , Array.concatRev
25 , Array.flattenArraysRev
26 , Array.flattenArraysRev
26− , interpose
27+ , Array.interpose
27− , interposeSuffix
28+ , Array.interposeSuffix
28− , intercalateSuffix
29+ , Array.intercalateSuffix
29 , unlines
30 , unlines
30
31
31 -- * Elimination
32 -- * Elimination
@@ -36,10 +37,11 @@ module Streamly.Internal.Data.Array.Stream
36 -- could also be implemented using parseBreak.
37 -- could also be implemented using parseBreak.
37 , foldBreak
38 , foldBreak
38 , foldBreakD
39 , foldBreakD
39− -- The byte level parseBreak cannot work efficiently. Because the stream
40+ -- This is chunked parseBreak. A byte level parseBreak cannot work
41+ -- efficiently. Because the stream will have to be a StreamK for
40− -- will have to be a StreamK for backtracking, StreamK at byte level would
42+ -- backtracking, StreamK at byte level would not be efficient.
41− -- not be efficient.
42− , parseBreak -- StreamK.parseBreakChunks
43+ -- parseBreak p = K.parseBreakChunks (ParserK.adaptC p)
44+ , parseBreak
43 -- , parseBreakD
45 -- , parseBreakD
44 -- , foldManyChunks
46 -- , foldManyChunks
45 -- , parseManyChunks
47 -- , parseManyChunks
@@ -77,6 +79,8 @@ where
77import Data.Bifunctor (second)
79import Data.Bifunctor (second)
78import Control.Exception (assert)
80import Control.Exception (assert)
79import Control.Monad.IO.Class (MonadIO(..))
81import Control.Monad.IO.Class (MonadIO(..))
82+-- import Data.Bifunctor (first)
83+-- import Data.Either (fromRight)
80import Data.Proxy (Proxy(..))
84import Data.Proxy (Proxy(..))
81import Data.Word (Word8)
85import Data.Word (Word8)
82import Streamly.Internal.Data.Unbox (Unbox(..))
86import Streamly.Internal.Data.Unbox (Unbox(..))
@@ -92,127 +96,32 @@ import Streamly.Internal.Data.Parser (ParseError(..))
92import Streamly.Internal.Data.Stream (Stream)
96import Streamly.Internal.Data.Stream (Stream)
93import Streamly.Internal.Data.StreamK (StreamK, fromStream, toStream)
97import Streamly.Internal.Data.StreamK (StreamK, fromStream, toStream)
94import Streamly.Internal.Data.SVar.Type (adaptState, defState)
98import Streamly.Internal.Data.SVar.Type (adaptState, defState)
95−import Streamly.Internal.Data.Tuple.Strict (Tuple'(..))
96
99
97−import qualified Streamly.Data.Fold as FL
98import qualified Streamly.Internal.Data.Array as A
100import qualified Streamly.Internal.Data.Array as A
99import qualified Streamly.Internal.Data.Array as Array
101import 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(..))
103import qualified Streamly.Internal.Data.Parser as PR
102import qualified Streamly.Internal.Data.Parser as PR
104import qualified Streamly.Internal.Data.Parser as PRD
103import qualified Streamly.Internal.Data.Parser as PRD
105 (Parser(..), Initial(..))
104 (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−
144-------------------------------------------------------------------------------
112-------------------------------------------------------------------------------
145-- Intersperse and append
113-- Intersperse and append
146-------------------------------------------------------------------------------
114-------------------------------------------------------------------------------
147
115
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
177{-# INLINE_NORMAL unlines #-}
116{-# INLINE_NORMAL unlines #-}
178unlines :: forall m a. (MonadIO m, Unbox a)
117unlines :: forall m a. (MonadIO m, Unbox a)
179 => a -> D.Stream m (Array a) -> D.Stream m a
118 => a -> D.Stream m (Array a) -> D.Stream m a
180−unlines sep (D.Stream step state) = D.Stream step' (OuterLoop state)
119+unlines = Array.interposeSuffix
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)
197
120
198-------------------------------------------------------------------------------
121-------------------------------------------------------------------------------
199-- Compact
122-- Compact
200-------------------------------------------------------------------------------
123-------------------------------------------------------------------------------
201
124
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−
216-- XXX instead of writing two different versions of this operation, we should
125-- XXX instead of writing two different versions of this operation, we should
217-- write it as a pipe.
126-- write it as a pipe.
218--
127--
@@ -220,8 +129,7 @@ packArraysChunksOf n str =
220{-# INLINE_NORMAL lpackArraysChunksOf #-}
129{-# INLINE_NORMAL lpackArraysChunksOf #-}
221lpackArraysChunksOf :: (MonadIO m, Unbox a)
130lpackArraysChunksOf :: (MonadIO m, Unbox a)
222 => Int -> Fold m (Array a) () -> Fold m (Array a) ()
131 => Int -> Fold m (Array a) () -> Fold m (Array a) ()
223−lpackArraysChunksOf n fld =
132+lpackArraysChunksOf = Array.lCompactGE
224− FL.lmap A.unsafeThaw $ AS.lpackArraysChunksOf n (FL.lmap A.unsafeFreeze fld)
225
133
226-- | Coalesce adjacent arrays in incoming stream to form bigger arrays of a
134-- | Coalesce adjacent arrays in incoming stream to form bigger arrays of a
227-- maximum specified size in bytes.
135-- maximum specified size in bytes.
@@ -230,68 +138,19 @@ lpackArraysChunksOf n fld =
230{-# INLINE compact #-}
138{-# INLINE compact #-}
231compact :: (MonadIO m, Unbox a)
139compact :: (MonadIO m, Unbox a)
232 => Int -> Stream m (Array a) -> Stream m (Array a)
140 => Int -> Stream m (Array a) -> Stream m (Array a)
233−compact = packArraysChunksOf
141+compact = Array.compactLE
234
142
235−-------------------------------------------------------------------------------
143+-- | Given a stream of arrays, splice them all together to generate a single
236−-- Split
144+-- array. The stream must be /finite/.
237−-------------------------------------------------------------------------------
238−
239−data SplitState s arr
240− = Initial s
241− | Buffering s arr
242− | Splitting s arr
243− | Yielding arr (SplitState s arr)
244− | Finishing
245−
246−-- | Split a stream of arrays on a given separator byte, dropping the separator
247−-- and coalescing all the arrays between two separators into a single array.
248--
145--
249-- @since 0.7.0
146-- @since 0.7.0
250−{-# INLINE_NORMAL _splitOn #-}
147+{-# INLINE toArray #-}
251−_splitOn
252− :: MonadIO m
253− => Word8
254− -> D.Stream m (Array Word8)
148+toArray :: (MonadIO m, Unbox a) => Stream m (Array a) -> m (Array a)
255− -> D.Stream m (Array Word8)
149+toArray = Array.fromChunks
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
150
293− step' _ (Yielding arr next) = return $ D.Yield arr next
151+-------------------------------------------------------------------------------
294− step' _ Finishing = return D.Stop
152+-- Split
153+-------------------------------------------------------------------------------
295
154
296-- XXX Remove MonadIO constraint.
155-- XXX Remove MonadIO constraint.
297-- | Split a stream of arrays on a given separator byte, dropping the separator
156-- | Split a stream of arrays on a given separator byte, dropping the separator
@@ -304,7 +163,7 @@ splitOn
304 => Word8
163 => Word8
305 -> Stream m (Array Word8)
164 -> Stream m (Array Word8)
306 -> Stream m (Array Word8)
165 -> Stream m (Array Word8)
307−splitOn byte = D.splitInnerBy (A.breakOn byte) A.splice
166+splitOn = Array.compactOnByte
308
167
309{-# INLINE splitOnSuffix #-}
168{-# INLINE splitOnSuffix #-}
310splitOnSuffix
169splitOnSuffix
@@ -312,8 +171,7 @@ splitOnSuffix
312 => Word8
171 => Word8
313 -> Stream m (Array Word8)
172 -> Stream m (Array Word8)
314 -> Stream m (Array Word8)
173 -> Stream m (Array Word8)
315−-- splitOn byte s = fromStreamD $ A.splitOn byte $ toStreamD s
174+splitOnSuffix = Array.compactOnByteSuffix
316−splitOnSuffix byte = D.splitInnerBySuffix (A.breakOn byte) A.splice
317
175
318-------------------------------------------------------------------------------
176-------------------------------------------------------------------------------
319-- Elimination - Running folds
177-- Elimination - Running folds
@@ -322,77 +180,24 @@ splitOnSuffix byte = D.splitInnerBySuffix (A.breakOn byte) A.splice
322{-# INLINE_NORMAL foldBreakD #-}
180{-# INLINE_NORMAL foldBreakD #-}
323foldBreakD :: forall m a b. (MonadIO m, Unbox a) =>
181foldBreakD :: forall m a b. (MonadIO m, Unbox a) =>
324 Fold m a b -> D.Stream m (Array a) -> m (b, D.Stream m (Array a))
182 Fold m a b -> D.Stream m (Array a) -> m (b, D.Stream m (Array a))
325−foldBreakD (FL.Fold fstep initial _ final) stream@(D.Stream step state) = do
183+foldBreakD = Array.foldBreakChunks
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
390
184
391-- | Fold an array stream using the supplied 'Fold'. Returns the fold result
185-- | Fold an array stream using the supplied 'Fold'. Returns the fold result
392-- and the unconsumed stream.
186-- and the unconsumed stream.
393--
187--
394-- > foldBreak f = runArrayFoldBreak (ChunkFold.fromFold f)
188-- > foldBreak f = runArrayFoldBreak (ChunkFold.fromFold f)
395--
189--
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+--
396-- /Internal/
201-- /Internal/
397--
202--
398{-# INLINE_NORMAL foldBreak #-}
203{-# INLINE_NORMAL foldBreak #-}
@@ -401,14 +206,20 @@ foldBreak ::
401 => Fold m a b
206 => Fold m a b
402 -> StreamK m (A.Array a)
207 -> StreamK m (A.Array a)
403 -> m (b, StreamK m (A.Array a))
208 -> m (b, StreamK m (A.Array a))
209+foldBreak = Array.foldBreakChunksK
210+--
404-- foldBreak f s = fmap fromStreamD <$> foldBreakD f (toStreamD s)
211-- foldBreak f s = fmap fromStreamD <$> foldBreakD f (toStreamD s)
212+--
405−foldBreak = foldBreakK
213+-- foldBreak f s =
214+-- fmap (first (fromRight undefined))
215+-- $ K.parseBreakChunks (ParserK.adaptC (PR.fromFold f)) s
216+--
406-- If foldBreak performs better than runArrayFoldBreak we can use a rewrite
217-- If foldBreak performs better than runArrayFoldBreak we can use a rewrite
407-- rule to rewrite runArrayFoldBreak to fold.
218-- rule to rewrite runArrayFoldBreak to fold.
408-- foldBreak f = runArrayFoldBreak (ChunkFold.fromFold f)
219-- foldBreak f = runArrayFoldBreak (ChunkFold.fromFold f)
409
220
410-------------------------------------------------------------------------------
221-------------------------------------------------------------------------------
411−-- Fold to a single Array
222+-- Elimination - running element parsers
412-------------------------------------------------------------------------------
223-------------------------------------------------------------------------------
413
224
414-- When we have to take an array partially, take the last part of the array.
225-- When we have to take an array partially, take the last part of the array.
@@ -454,341 +265,22 @@ splitAtArrayListRev n ls
454 arr1 = Array contents end1 end
265 arr1 = Array contents end1 end
455 in ([arr1], arr2:xs)
266 in ([arr1], arr2:xs)
456
267
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−
554-- GHC parser does not accept {-# ANN type [] NoSpecConstr #-}, so we need
268-- GHC parser does not accept {-# ANN type [] NoSpecConstr #-}, so we need
555-- to make a newtype.
269-- to make a newtype.
556{-# ANN type List NoSpecConstr #-}
270{-# ANN type List NoSpecConstr #-}
557newtype List a = List {getList :: [a]}
271newtype List a = List {getList :: [a]}
558
272
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−
789-- | Parse an array stream using the supplied 'Parser'. Returns the parse
273-- | Parse an array stream using the supplied 'Parser'. Returns the parse
790-- result and the unconsumed stream. Throws 'ParseError' if the parse fails.
274-- result and the unconsumed stream. Throws 'ParseError' if the parse fails.
791--
275--
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+--
792-- /Internal/
284-- /Internal/
793--
285--
794{-# INLINE_NORMAL parseBreak #-}
286{-# INLINE_NORMAL parseBreak #-}
@@ -801,7 +293,8 @@ parseBreak ::
801parseBreak p s =
293parseBreak p s =
802 fmap fromStreamD <$> parseBreakD (PRD.fromParserK p) (toStreamD s)
294 fmap fromStreamD <$> parseBreakD (PRD.fromParserK p) (toStreamD s)
803-}
295-}
804−parseBreak = parseBreakK
296+parseBreak = Array.parseBreakChunksK
297+-- parseBreak p = K.parseBreakChunks (ParserK.adaptC p)
805
298
806-------------------------------------------------------------------------------
299-------------------------------------------------------------------------------
807-- Elimination - Running Array Folds and parsers
300-- Elimination - Running Array Folds and parsers
core/src/Streamly/Internal/Data/Array/Type.hs+514−107
@@ -1,8 +1,11 @@
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 #-}
2-- |
6-- |
3-- Module : Streamly.Internal.Data.Array.Type
7-- Module : Streamly.Internal.Data.Array.Type
4-- Copyright : (c) 2020 Composewell Technologies
8-- Copyright : (c) 2020 Composewell Technologies
5−--
6-- License : BSD3-3-Clause
9-- License : BSD3-3-Clause
7-- Maintainer : streamly@composewell.com
10-- Maintainer : streamly@composewell.com
8-- Stability : experimental
11-- Stability : experimental
@@ -10,86 +13,146 @@
10--
13--
11-- See notes in "Streamly.Internal.Data.MutArray.Type"
14-- See notes in "Streamly.Internal.Data.MutArray.Type"
12--
15--
16+
13module Streamly.Internal.Data.Array.Type
17module Streamly.Internal.Data.Array.Type
14 (
18 (
19+ -- ** Type
15 -- $arrayNotes
20 -- $arrayNotes
16 Array (..)
21 Array (..)
17− , asPtrUnsafe
18− , nil
19
22
20− -- * Freezing and Thawing
23+ -- ** Conversion
24+ -- *** Mutable and Immutable
21 , unsafeFreeze
25 , unsafeFreeze
22 , unsafeFreezeWithShrink
26 , unsafeFreezeWithShrink
23 , unsafeThaw
27 , unsafeThaw
24
28
25− -- * Pinning and Unpinning
29+ -- *** Pinned and Unpinned
26 , pin
30 , pin
27 , unpin
31 , unpin
28 , isPinned
32 , isPinned
29
33
30− -- * Construction
34+ -- *** Casting
31− , splice
35+ , unsafePinnedAsPtr
32
36
33− , fromList
37+ -- ** Construction
34− , pinnedFromList
38+ , empty
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+
50+ -- *** Stream Folds
51+ , unsafeMakePure
52+ , createOf
53+ , pinnedCreateOf
54+ , unsafeCreateOf
55+ , unsafePinnedCreateOf
56+ , create
57+ , pinnedCreate
58+ , createWith
59+
60+ -- *** From containers
35 , fromListN
61 , fromListN
36 , pinnedFromListN
62 , pinnedFromListN
37− , fromListRev
63+ , fromList
64+ , pinnedFromList
38 , fromListRevN
65 , fromListRevN
39− , fromStreamDN
66+ , fromListRev
40− , fromStreamD
67+ , fromStreamN
68+ , fromStream
69+ , fromPureStreamN
41 , fromPureStream
70 , fromPureStream
42 , fromByteStr#
71 , fromByteStr#
72+ , fromByteStr
73+ , fromPtrN
74+ , fromChunks
75+ , fromChunksK
43
76
44− -- * Split
77+ -- ** Reading
45− , breakOn
46
78
47− -- ** Cloning arrays
79+ -- *** Indexing
48− , clone
80+ , unsafeIndexIO -- XXX unsafeGetIndexIO
49− , pinnedClone
81+ , getIndexUnsafe -- XXX unsafeGetIndex
50
82
51− -- * Elimination
83+ -- *** To Streams
52− , unsafeIndexIO
84+ , read
53− , getIndexUnsafe
85+ , readRev
54− , byteLength
86+ , toStreamK
87+ , toStreamKRev
88+
89+ -- *** To Containers
90+ , toList
91+
92+ -- *** Unfolds
93+ , producer -- experimental
94+ , readerUnsafe
95+ , reader
96+ , readerRev
97+
98+ -- *** Size
55 , length
99 , length
100+ , byteLength
56
101
102+ -- ** Folding
57 , foldl'
103 , foldl'
58 , foldr
104 , foldr
59− , splitAt
105+ , byteCmp
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
60
117
118+ -- *** Split
119+ -- | Split an array into slices.
120+
121+ -- *** Concat
122+ -- | Append the arrays in a stream to form a stream of elements.
123+ , concat
124+ , concatRev
125+
126+ -- *** Compact
127+ -- | Append the arrays in a stream to form a stream of larger arrays.
128+ , fCompactGE
129+ , fPinnedCompactGE
130+ , lCompactGE
131+ , lPinnedCompactGE
132+ , compactGE
133+
134+ -- ** Deprecated
135+ , asPtrUnsafe
136+ , unsafeIndex
137+ , bufferChunks
138+ , flattenArrays
139+ , flattenArraysRev
140+ , fromArrayStreamK
141+ , fromStreamDN
142+ , fromStreamD
61 , toStreamD
143 , toStreamD
62 , toStreamDRev
144 , toStreamDRev
63− , toStreamK
64− , toStreamKRev
65 , toStream
145 , toStream
66 , toStreamRev
146 , toStreamRev
67− , read
147+ , nil
68− , readRev
69− , readerRev
70− , toList
71−
72− -- * Folds
73 , writeWith
148 , writeWith
74 , writeN
149 , writeN
75 , pinnedWriteN
150 , pinnedWriteN
76 , writeNUnsafe
151 , writeNUnsafe
77 , pinnedWriteNUnsafe
152 , pinnedWriteNUnsafe
78− , MA.ArrayUnsafe (..)
79 , pinnedWriteNAligned
153 , pinnedWriteNAligned
80 , write
154 , write
81 , pinnedWrite
155 , pinnedWrite
82− , unsafeMakePure
83−
84− -- * Streams of arrays
85− , chunksOf
86− , pinnedChunksOf
87− , bufferChunks
88− , flattenArrays
89− , flattenArraysRev
90−
91− -- * Deprecated
92− , unsafeIndex
93 )
156 )
94where
157where
95
158
@@ -97,31 +160,36 @@ where
97#include "inline.hs"
160#include "inline.hs"
98
161
99import Control.Exception (assert)
162import Control.Exception (assert)
100−import Control.Monad (replicateM)
163+import Control.Monad (replicateM, when)
101import Control.Monad.IO.Class (MonadIO(..))
164import Control.Monad.IO.Class (MonadIO(..))
102import Data.Functor.Identity (Identity(..))
165import Data.Functor.Identity (Identity(..))
166+import Data.Int (Int8, Int16, Int32, Int64)
103import Data.Proxy (Proxy(..))
167import Data.Proxy (Proxy(..))
104−import Data.Word (Word8)
168+import Data.Word (Word8, Word16, Word32, Word64)
105import GHC.Base (build)
169import GHC.Base (build)
106import GHC.Exts (IsList, IsString(..), Addr#)
170import GHC.Exts (IsList, IsString(..), Addr#)
107
171
108import GHC.IO (unsafePerformIO)
172import GHC.IO (unsafePerformIO)
109import GHC.Ptr (Ptr(..))
173import GHC.Ptr (Ptr(..))
110−import Streamly.Internal.Data.MutArray.Type (MutArray(..), MutByteArray)
174+import Streamly.Internal.Data.Producer.Type (Producer(..))
175+import Streamly.Internal.Data.MutArray.Type (MutArray(..))
176+import Streamly.Internal.Data.MutByteArray.Type (MutByteArray)
111import Streamly.Internal.Data.Fold.Type (Fold(..))
177import Streamly.Internal.Data.Fold.Type (Fold(..))
112import Streamly.Internal.Data.Stream.Type (Stream)
178import Streamly.Internal.Data.Stream.Type (Stream)
179+import Streamly.Internal.Data.StreamK.Type (StreamK)
113import Streamly.Internal.Data.Unbox (Unbox(..))
180import Streamly.Internal.Data.Unbox (Unbox(..))
114import Streamly.Internal.Data.Unfold.Type (Unfold(..))
181import Streamly.Internal.Data.Unfold.Type (Unfold(..))
115import Text.Read (readPrec)
182import Text.Read (readPrec)
116
183
117−import Prelude hiding (Foldable(..), read, unlines, splitAt)
184+import Prelude hiding (Foldable(..), concat, read, unlines, splitAt)
118
185
119import qualified GHC.Exts as Exts
186import qualified GHC.Exts as Exts
187+import qualified Streamly.Internal.Data.Fold.Type as Fold
120import qualified Streamly.Internal.Data.MutArray.Type as MA
188import qualified Streamly.Internal.Data.MutArray.Type as MA
121import qualified Streamly.Internal.Data.Stream.Type as D
189import qualified Streamly.Internal.Data.Stream.Type as D
122−import qualified Streamly.Internal.Data.Stream.Generate as D
123import qualified Streamly.Internal.Data.StreamK.Type as K
190import qualified Streamly.Internal.Data.StreamK.Type as K
124−import qualified Streamly.Internal.Data.MutByteArray as Unboxed
191+import qualified Streamly.Internal.Data.MutByteArray.Type as Unboxed
192+import qualified Streamly.Internal.Data.Producer as Producer
125import qualified Streamly.Internal.Data.Unfold.Type as Unfold
193import qualified Streamly.Internal.Data.Unfold.Type as Unfold
126import qualified Text.ParserCombinators.ReadPrec as ReadPrec
194import qualified Text.ParserCombinators.ReadPrec as ReadPrec
127
195
@@ -175,15 +243,20 @@ data Array a =
175
243
176-- | Use an @Array a@ as @Ptr a@.
244-- | Use an @Array a@ as @Ptr a@.
177--
245--
178−-- See 'MA.asPtrUnsafe' in the Mutable array module for more details.
246+-- See 'MA.unsafePinnedAsPtr' in the Mutable array module for more details.
179--
247--
180-- /Unsafe/
248-- /Unsafe/
181--
249--
182-- /Pre-release/
250-- /Pre-release/
183--
251--
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." #-}
184{-# INLINE asPtrUnsafe #-}
257{-# INLINE asPtrUnsafe #-}
185asPtrUnsafe :: MonadIO m => Array a -> (Ptr a -> m b) -> m b
258asPtrUnsafe :: MonadIO m => Array a -> (Ptr a -> m b) -> m b
186−asPtrUnsafe arr = MA.asPtrUnsafe (unsafeThaw arr)
259+asPtrUnsafe = unsafePinnedAsPtr
187
260
188-------------------------------------------------------------------------------
261-------------------------------------------------------------------------------
189-- Freezing and Thawing
262-- Freezing and Thawing
@@ -230,18 +303,19 @@ unsafeThaw (Array ac as ae) = MutArray ac as ae ae
230-- Pinning & Unpinning
303-- Pinning & Unpinning
231-------------------------------------------------------------------------------
304-------------------------------------------------------------------------------
232
305
233−-- | Copy the 'Array' to pinned memory if unpinned, else do nothing. The
306+-- | Return a copy of the 'Array' in pinned memory if unpinned, else return the
234−-- overhead is a copy if the input array is unpinned.
307+-- original array.
235{-# INLINE pin #-}
308{-# INLINE pin #-}
236pin :: Array a -> IO (Array a)
309pin :: Array a -> IO (Array a)
237pin = fmap unsafeFreeze . MA.pin . unsafeThaw
310pin = fmap unsafeFreeze . MA.pin . unsafeThaw
238
311
239−-- | Copy the 'Array' to unpinned memory if pinned, else do nothing. The
312+-- | Return a copy of the 'Array' in unpinned memory if pinned, else return the
240−-- overhead is a copy if the input array is pinned.
313+-- original array.
241{-# INLINE unpin #-}
314{-# INLINE unpin #-}
242unpin :: Array a -> IO (Array a)
315unpin :: Array a -> IO (Array a)
243unpin = fmap unsafeFreeze . MA.unpin . unsafeThaw
316unpin = fmap unsafeFreeze . MA.unpin . unsafeThaw
244
317
318+-- | Return 'True' if the array is allocated in pinned memory.
245{-# INLINE isPinned #-}
319{-# INLINE isPinned #-}
246isPinned :: Array a -> Bool
320isPinned :: Array a -> Bool
247isPinned = MA.isPinned . unsafeThaw
321isPinned = MA.isPinned . unsafeThaw
@@ -250,11 +324,14 @@ isPinned = MA.isPinned . unsafeThaw
250-- Construction
324-- Construction
251-------------------------------------------------------------------------------
325-------------------------------------------------------------------------------
252
326
253−-- Splice two immutable arrays creating a new array.
327+-- | Copy two immutable arrays into a new array. If you want to splice more
328+-- than two arrays then this operation would be highly inefficient because it
329+-- would make a copy on every splice operation, instead use the
330+-- 'fromChunksK' operation to combine n immutable arrays.
254{-# INLINE splice #-}
331{-# INLINE splice #-}
255−splice :: (MonadIO m, Unbox a) => Array a -> Array a -> m (Array a)
332+splice :: MonadIO m => Array a -> Array a -> m (Array a)
256splice arr1 arr2 =
333splice arr1 arr2 =
257− unsafeFreeze <$> MA.splice (unsafeThaw arr1) (unsafeThaw arr2)
334+ unsafeFreeze <$> MA.spliceCopy (unsafeThaw arr1) (unsafeThaw arr2)
258
335
259-- | Create an 'Array' from the first N elements of a list. The array is
336-- | Create an 'Array' from the first N elements of a list. The array is
260-- allocated to size N, if the list terminates before N elements then the
337-- allocated to size N, if the list terminates before N elements then the
@@ -298,36 +375,83 @@ pinnedFromList xs = unsafePerformIO $ unsafeFreeze <$> MA.pinnedFromList xs
298fromListRev :: Unbox a => [a] -> Array a
375fromListRev :: Unbox a => [a] -> Array a
299fromListRev xs = unsafePerformIO $ unsafeFreeze <$> MA.fromListRev xs
376fromListRev xs = unsafePerformIO $ unsafeFreeze <$> MA.fromListRev xs
300
377
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/
301−{-# INLINE_NORMAL fromStreamDN #-}
385+{-# INLINE_NORMAL fromStreamN #-}
386+fromStreamN :: (MonadIO m, Unbox a) => Int -> Stream m a -> m (Array a)
387+fromStreamN n m = do
388+ when (n < 0) $ error "writeN: negative write count specified"
389+ unsafeFreeze <$> MA.fromStreamN n m
390+-- fromStreamN n = D.fold (writeN n)
391+
392+{-# DEPRECATED fromStreamDN "Please use fromStreamN instead." #-}
302fromStreamDN :: forall m a. (MonadIO m, Unbox a)
393fromStreamDN :: forall m a. (MonadIO m, Unbox a)
303 => Int -> D.Stream m a -> m (Array a)
394 => Int -> D.Stream m a -> m (Array a)
304−fromStreamDN limit str = unsafeFreeze <$> MA.fromStreamDN limit str
395+fromStreamDN = fromStreamN
305
396
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/
306{-# INLINE_NORMAL fromStreamD #-}
408{-# 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." #-}
307fromStreamD :: forall m a. (MonadIO m, Unbox a)
414fromStreamD :: forall m a. (MonadIO m, Unbox a)
308 => D.Stream m a -> m (Array a)
415 => D.Stream m a -> m (Array a)
309−fromStreamD str = unsafeFreeze <$> MA.fromStreamD str
416+fromStreamD = fromStream
310
417
311-------------------------------------------------------------------------------
418-------------------------------------------------------------------------------
312-- Cloning
419-- Cloning
313-------------------------------------------------------------------------------
420-------------------------------------------------------------------------------
314
421
315{-# INLINE clone #-}
422{-# INLINE clone #-}
423+clone ::
424+ ( MonadIO m
425+#ifdef DEVBUILD
426+ , Unbox a
427+#endif
428+ )
316−clone :: MonadIO m => Array a -> m (Array a)
429+ => Array a -> m (Array a)
317clone = fmap unsafeFreeze . MA.clone . unsafeThaw
430clone = fmap unsafeFreeze . MA.clone . unsafeThaw
318
431
319{-# INLINE pinnedClone #-}
432{-# INLINE pinnedClone #-}
433+pinnedClone ::
434+ ( MonadIO m
435+#ifdef DEVBUILD
436+ , Unbox a
437+#endif
438+ )
320−pinnedClone :: MonadIO m => Array a -> m (Array a)
439+ => Array a -> m (Array a)
321pinnedClone = fmap unsafeFreeze . MA.pinnedClone . unsafeThaw
440pinnedClone = fmap unsafeFreeze . MA.pinnedClone . unsafeThaw
322
441
323-------------------------------------------------------------------------------
442-------------------------------------------------------------------------------
324-- Streams of arrays
443-- Streams of arrays
325-------------------------------------------------------------------------------
444-------------------------------------------------------------------------------
326
445
327−{-# INLINE bufferChunks #-}
446+{-# INLINE buildChunks #-}
447+buildChunks :: (MonadIO m, Unbox a) =>
448+ D.Stream m a -> m (K.StreamK m (Array a))
449+buildChunks m = D.foldr K.cons K.nil $ chunksOf defaultChunkSize m
450+
451+{-# DEPRECATED bufferChunks "Please use buildChunks instead." #-}
328bufferChunks :: (MonadIO m, Unbox a) =>
452bufferChunks :: (MonadIO m, Unbox a) =>
329 D.Stream m a -> m (K.StreamK m (Array a))
453 D.Stream m a -> m (K.StreamK m (Array a))
330−bufferChunks m = D.foldr K.cons K.nil $ chunksOf defaultChunkSize m
454+bufferChunks = buildChunks
331
455
332-- | @chunksOf n stream@ groups the elements in the input stream into arrays of
456-- | @chunksOf n stream@ groups the elements in the input stream into arrays of
333-- @n@ elements each.
457-- @n@ elements each.
@@ -348,27 +472,100 @@ pinnedChunksOf :: forall m a. (MonadIO m, Unbox a)
348 => Int -> D.Stream m a -> D.Stream m (Array a)
472 => Int -> D.Stream m a -> D.Stream m (Array a)
349pinnedChunksOf n str = D.map unsafeFreeze $ MA.pinnedChunksOf n str
473pinnedChunksOf n str = D.map unsafeFreeze $ MA.pinnedChunksOf n str
350
474
351−-- | Use the "read" unfold instead.
475+-- | Convert a stream of arrays into a stream of their elements.
352--
476--
353−-- @flattenArrays = unfoldMany read@
477+-- >>> concat = Stream.unfoldMany Array.reader
354--
478--
355−-- We can try this if there are any fusion issues in the unfold.
479+{-# INLINE_NORMAL concat #-}
356−--
480+concat :: (Monad m, Unbox a) => Stream m (Array a) -> Stream m a
481+-- XXX this requires MonadIO whereas the unfoldMany version does not
482+concat = MA.concatWith (pure . unsafeInlineIO) . D.map unsafeThaw
483+-- concat = D.unfoldMany reader
484+
485+{-# DEPRECATED flattenArrays "Please use \"unfoldMany reader\" instead." #-}
357−{-# INLINE_NORMAL flattenArrays #-}
486+{-# INLINE flattenArrays #-}
358flattenArrays :: forall m a. (MonadIO m, Unbox a)
487flattenArrays :: forall m a. (MonadIO m, Unbox a)
359 => D.Stream m (Array a) -> D.Stream m a
488 => D.Stream m (Array a) -> D.Stream m a
360−flattenArrays = MA.flattenArrays . D.map unsafeThaw
489+flattenArrays = concat
361
490
362−-- | Use the "readRev" unfold instead.
491+-- | Convert a stream of arrays into a stream of their elements reversing the
363−--
492+-- contents of each array before flattening.
364−-- @flattenArrays = unfoldMany readRev@
365--
493--
366−-- We can try this if there are any fusion issues in the unfold.
494+-- >>> concatRev = Stream.unfoldMany Array.readerRev
367--
495--
496+{-# INLINE_NORMAL concatRev #-}
497+concatRev :: forall m a. (Monad m, Unbox a)
498+ => D.Stream m (Array a) -> D.Stream m a
499+-- XXX this requires MonadIO whereas the unfoldMany version does not
500+concatRev = MA.concatRevWith (pure . unsafeInlineIO) . D.map unsafeThaw
501+-- concatRev = D.unfoldMany readerRev
502+
503+{-# DEPRECATED flattenArraysRev "Please use \"unfoldMany readerRev\" instead." #-}
368−{-# INLINE_NORMAL flattenArraysRev #-}
504+{-# INLINE flattenArraysRev #-}
369flattenArraysRev :: forall m a. (MonadIO m, Unbox a)
505flattenArraysRev :: forall m a. (MonadIO m, Unbox a)
370 => D.Stream m (Array a) -> D.Stream m a
506 => D.Stream m (Array a) -> D.Stream m a
371−flattenArraysRev = MA.flattenArraysRev . D.map unsafeThaw
507+flattenArraysRev = concatRev
508+
509+-------------------------------------------------------------------------------
510+-- Compact
511+-------------------------------------------------------------------------------
512+
513+-- XXX Note that this thaws immutable arrays for appending, that may be
514+-- problematic if multiple users do the same thing, however, thawed immutable
515+-- arrays would have no capacity to append, therefore, a copy will be forced
516+-- anyway.
517+
518+-- | Fold @fCompactGE n@ coalesces adjacent arrays in the input stream
519+-- until the size becomes greater than or equal to n.
520+--
521+-- Generates unpinned arrays irrespective of the pinning status of input
522+-- arrays.
523+{-# INLINE_NORMAL fCompactGE #-}
524+fCompactGE :: (MonadIO m, Unbox a) => Int -> Fold m (Array a) (Array a)
525+fCompactGE n = fmap unsafeFreeze $ Fold.lmap unsafeThaw $ MA.fCompactGE n
526+
527+-- | PInned version of 'fCompactGE'.
528+{-# INLINE_NORMAL fPinnedCompactGE #-}
529+fPinnedCompactGE :: (MonadIO m, Unbox a) => Int -> Fold m (Array a) (Array a)
530+fPinnedCompactGE n =
531+ fmap unsafeFreeze $ Fold.lmap unsafeThaw $ MA.fPinnedCompactGE n
532+
533+-- | @compactGE n stream@ coalesces adjacent arrays in the @stream@ until
534+-- the size becomes greater than or equal to @n@.
535+--
536+-- >>> compactGE n = Stream.foldMany (Array.fCompactGE n)
537+--
538+-- Generates unpinned arrays irrespective of the pinning status of input
539+-- arrays.
540+{-# INLINE compactGE #-}
541+compactGE ::
542+ (MonadIO m, Unbox a)
543+ => Int -> Stream m (Array a) -> Stream m (Array a)
544+compactGE n stream =
545+ D.map unsafeFreeze $ MA.compactGE n $ D.map unsafeThaw stream
546+
547+-- | Like 'compactGE' but for transforming folds instead of stream.
548+--
549+-- >>> lCompactGE n = Fold.many (Array.fCompactGE n)
550+--
551+-- Generates unpinned arrays irrespective of the pinning status of input
552+-- arrays.
553+{-# INLINE_NORMAL lCompactGE #-}
554+lCompactGE :: (MonadIO m, Unbox a)
555+ => Int -> Fold m (Array a) () -> Fold m (Array a) ()
556+lCompactGE n fld =
557+ Fold.lmap unsafeThaw $ MA.lCompactGE n (Fold.lmap unsafeFreeze fld)
558+
559+-- | Pinned version of 'lCompactGE'.
560+{-# INLINE_NORMAL lPinnedCompactGE #-}
561+lPinnedCompactGE :: (MonadIO m, Unbox a)
562+ => Int -> Fold m (Array a) () -> Fold m (Array a) ()
563+lPinnedCompactGE n fld =
564+ Fold.lmap unsafeThaw $ MA.lPinnedCompactGE n (Fold.lmap unsafeFreeze fld)
565+
566+-------------------------------------------------------------------------------
567+-- Splitting
568+-------------------------------------------------------------------------------
372
569
373-- Drops the separator byte
570-- Drops the separator byte
374{-# INLINE breakOn #-}
571{-# INLINE breakOn #-}
@@ -412,24 +609,68 @@ byteLength = MA.byteLength . unsafeThaw
412length :: Unbox a => Array a -> Int
609length :: Unbox a => Array a -> Int
413length arr = MA.length (unsafeThaw arr)
610length arr = MA.length (unsafeThaw arr)
414
611
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+
415-- | Unfold an array into a stream in reverse order.
655-- | Unfold an array into a stream in reverse order.
416--
656--
417{-# INLINE_NORMAL readerRev #-}
657{-# INLINE_NORMAL readerRev #-}
418readerRev :: forall m a. (Monad m, Unbox a) => Unfold m (Array a) a
658readerRev :: forall m a. (Monad m, Unbox a) => Unfold m (Array a) a
419readerRev = Unfold.lmap unsafeThaw $ MA.readerRevWith (return . unsafeInlineIO)
659readerRev = Unfold.lmap unsafeThaw $ MA.readerRevWith (return . unsafeInlineIO)
420
660
661+{-# DEPRECATED toStreamD "Please use 'read' instead." #-}
421{-# INLINE_NORMAL toStreamD #-}
662{-# INLINE_NORMAL toStreamD #-}
422toStreamD :: forall m a. (Monad m, Unbox a) => Array a -> D.Stream m a
663toStreamD :: forall m a. (Monad m, Unbox a) => Array a -> D.Stream m a
423−toStreamD arr = MA.toStreamDWith (return . unsafeInlineIO) (unsafeThaw arr)
664+toStreamD = read
424
665
425{-# INLINE toStreamK #-}
666{-# INLINE toStreamK #-}
426toStreamK :: forall m a. (Monad m, Unbox a) => Array a -> K.StreamK m a
667toStreamK :: forall m a. (Monad m, Unbox a) => Array a -> K.StreamK m a
427toStreamK arr = MA.toStreamKWith (return . unsafeInlineIO) (unsafeThaw arr)
668toStreamK arr = MA.toStreamKWith (return . unsafeInlineIO) (unsafeThaw arr)
428
669
670+{-# DEPRECATED toStreamDRev "Please use 'readRev' instead." #-}
429{-# INLINE_NORMAL toStreamDRev #-}
671{-# INLINE_NORMAL toStreamDRev #-}
430toStreamDRev :: forall m a. (Monad m, Unbox a) => Array a -> D.Stream m a
672toStreamDRev :: forall m a. (Monad m, Unbox a) => Array a -> D.Stream m a
431−toStreamDRev arr =
673+toStreamDRev = readRev
432− MA.toStreamDRevWith (return . unsafeInlineIO) (unsafeThaw arr)
433
674
434{-# INLINE toStreamKRev #-}
675{-# INLINE toStreamKRev #-}
435toStreamKRev :: forall m a. (Monad m, Unbox a) => Array a -> K.StreamK m a
676toStreamKRev :: forall m a. (Monad m, Unbox a) => Array a -> K.StreamK m a
@@ -441,7 +682,7 @@ toStreamKRev arr =
441-- /Pre-release/
682-- /Pre-release/
442{-# INLINE_EARLY read #-}
683{-# INLINE_EARLY read #-}
443read :: (Monad m, Unbox a) => Array a -> Stream m a
684read :: (Monad m, Unbox a) => Array a -> Stream m a
444−read = toStreamD
685+read arr = MA.toStreamWith (return . unsafeInlineIO) (unsafeThaw arr)
445
686
446-- | Same as 'read'
687-- | Same as 'read'
447--
688--
@@ -458,7 +699,7 @@ toStream = read
458-- /Pre-release/
699-- /Pre-release/
459{-# INLINE_EARLY readRev #-}
700{-# INLINE_EARLY readRev #-}
460readRev :: (Monad m, Unbox a) => Array a -> Stream m a
701readRev :: (Monad m, Unbox a) => Array a -> Stream m a
461−readRev = toStreamDRev
702+readRev arr = MA.toStreamRevWith (return . unsafeInlineIO) (unsafeThaw arr)
462
703
463-- | Same as 'readRev'
704-- | Same as 'readRev'
464--
705--
@@ -482,6 +723,7 @@ foldr f z arr = runIdentity $ D.foldr f z $ toStreamD arr
482-- | Create two slices of an array without copying the original array. The
723-- | Create two slices of an array without copying the original array. The
483-- specified index @i@ is the first index of the second slice.
724-- specified index @i@ is the first index of the second slice.
484--
725--
726+{-# INLINE splitAt #-}
485splitAt :: Unbox a => Int -> Array a -> (Array a, Array a)
727splitAt :: Unbox a => Int -> Array a -> (Array a, Array a)
486splitAt i arr = (unsafeFreeze a, unsafeFreeze b)
728splitAt i arr = (unsafeFreeze a, unsafeFreeze b)
487 where
729 where
@@ -501,8 +743,8 @@ toListFB c n Array{..} = go arrStart
501 -- accumulating the list and fusing better with the pure consumers.
743 -- accumulating the list and fusing better with the pure consumers.
502 --
744 --
503 -- This should be safe as the array contents are guaranteed to be
745 -- This should be safe as the array contents are guaranteed to be
504− -- evaluated/written to before we peekByteIndex at them.
746+ -- evaluated/written to before we peekAt at them.
505− let !x = unsafeInlineIO $ peekByteIndex p arrContents
747+ let !x = unsafeInlineIO $ peekAt p arrContents
506 in c x (go (INDEX_NEXT(p,a)))
748 in c x (go (INDEX_NEXT(p,a)))
507
749
508-- | Convert an 'Array' into a list.
750-- | Convert an 'Array' into a list.
@@ -515,17 +757,28 @@ toList s = build (\c n -> toListFB c n s)
515-- Folds
757-- Folds
516-------------------------------------------------------------------------------
758-------------------------------------------------------------------------------
517
759
518−-- | @writeN n@ folds a maximum of @n@ elements from the input stream to an
760+-- | @createOf n@ folds a maximum of @n@ elements from the input stream to an
519-- 'Array'.
761-- 'Array'.
520--
762--
521−{-# INLINE_NORMAL writeN #-}
763+{-# INLINE_NORMAL createOf #-}
764+createOf :: forall m a. (MonadIO m, Unbox a) => Int -> Fold m a (Array a)
765+createOf = fmap unsafeFreeze . MA.createOf
766+
767+-- XXX Deprecate in major
768+-- {-# DEPRECATED writeN "Please use createOf instead." #-}
769+{-# INLINE writeN #-}
522writeN :: forall m a. (MonadIO m, Unbox a) => Int -> Fold m a (Array a)
770writeN :: forall m a. (MonadIO m, Unbox a) => Int -> Fold m a (Array a)
523−writeN = fmap unsafeFreeze . MA.writeN
771+writeN = createOf
524
772
525−-- | Like 'fromListN' but creates a pinned array.
773+-- | Like 'createOf' but creates a pinned array.
774+{-# INLINE_NORMAL pinnedCreateOf #-}
775+pinnedCreateOf :: forall m a. (MonadIO m, Unbox a) => Int -> Fold m a (Array a)
776+pinnedCreateOf = fmap unsafeFreeze . MA.pinnedCreateOf
777+
778+{-# DEPRECATED pinnedWriteN "Please use pinnedCreateOf instead." #-}
526−{-# INLINE_NORMAL pinnedWriteN #-}
779+{-# INLINE pinnedWriteN #-}
527pinnedWriteN :: forall m a. (MonadIO m, Unbox a) => Int -> Fold m a (Array a)
780pinnedWriteN :: forall m a. (MonadIO m, Unbox a) => Int -> Fold m a (Array a)
528−pinnedWriteN = fmap unsafeFreeze . MA.pinnedWriteN
781+pinnedWriteN = pinnedCreateOf
529
782
530-- | @pinnedWriteNAligned alignment n@ folds a maximum of @n@ elements from the input
783-- | @pinnedWriteNAligned alignment n@ folds a maximum of @n@ elements from the input
531-- stream to an 'Array' aligned to the given size.
784-- stream to an 'Array' aligned to the given size.
@@ -533,44 +786,74 @@ pinnedWriteN = fmap unsafeFreeze . MA.pinnedWriteN
533-- /Pre-release/
786-- /Pre-release/
534--
787--
535{-# INLINE_NORMAL pinnedWriteNAligned #-}
788{-# INLINE_NORMAL pinnedWriteNAligned #-}
789+{-# DEPRECATED pinnedWriteNAligned "To be removed." #-}
536pinnedWriteNAligned :: forall m a. (MonadIO m, Unbox a)
790pinnedWriteNAligned :: forall m a. (MonadIO m, Unbox a)
537 => Int -> Int -> Fold m a (Array a)
791 => Int -> Int -> Fold m a (Array a)
538pinnedWriteNAligned alignSize = fmap unsafeFreeze . MA.pinnedWriteNAligned alignSize
792pinnedWriteNAligned alignSize = fmap unsafeFreeze . MA.pinnedWriteNAligned alignSize
539
793
540−-- | Like 'writeN' but does not check the array bounds when writing. The fold
794+-- | Like 'createOf' but does not check the array bounds when writing. The fold
541-- driver must not call the step function more than 'n' times otherwise it will
795-- driver must not call the step function more than 'n' times otherwise it will
542-- corrupt the memory and crash. This function exists mainly because any
796-- corrupt the memory and crash. This function exists mainly because any
543-- conditional in the step function blocks fusion causing 10x performance
797-- conditional in the step function blocks fusion causing 10x performance
544-- slowdown.
798-- slowdown.
545--
799--
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." #-}
546−{-# INLINE_NORMAL writeNUnsafe #-}
806+{-# INLINE writeNUnsafe #-}
547writeNUnsafe :: forall m a. (MonadIO m, Unbox a)
807writeNUnsafe :: forall m a. (MonadIO m, Unbox a)
548 => Int -> Fold m a (Array a)
808 => Int -> Fold m a (Array a)
549−writeNUnsafe n = unsafeFreeze <$> MA.writeNUnsafe n
809+writeNUnsafe = unsafeCreateOf
550
810
811+{-# INLINE_NORMAL unsafePinnedCreateOf #-}
812+unsafePinnedCreateOf :: forall m a. (MonadIO m, Unbox a)
813+ => Int -> Fold m a (Array a)
814+unsafePinnedCreateOf n = unsafeFreeze <$> MA.unsafePinnedCreateOf n
815+
816+{-# DEPRECATED pinnedWriteNUnsafe "Please use unsafePinnedCreateOf instead." #-}
551−{-# INLINE_NORMAL pinnedWriteNUnsafe #-}
817+{-# INLINE pinnedWriteNUnsafe #-}
552pinnedWriteNUnsafe :: forall m a. (MonadIO m, Unbox a)
818pinnedWriteNUnsafe :: forall m a. (MonadIO m, Unbox a)
553 => Int -> Fold m a (Array a)
819 => Int -> Fold m a (Array a)
554−pinnedWriteNUnsafe n = unsafeFreeze <$> MA.pinnedWriteNUnsafe n
820+pinnedWriteNUnsafe = unsafePinnedCreateOf
821+
822+{-# INLINE_NORMAL createWith #-}
823+createWith :: forall m a. (MonadIO m, Unbox a)
824+ => Int -> Fold m a (Array a)
825+-- createWith n = FL.rmapM spliceArrays $ toArraysOf n
826+createWith elemCount = unsafeFreeze <$> MA.createWith elemCount
555
827
828+{-# DEPRECATED writeWith "Please use createWith instead." #-}
556−{-# INLINE_NORMAL writeWith #-}
829+{-# INLINE writeWith #-}
557writeWith :: forall m a. (MonadIO m, Unbox a)
830writeWith :: forall m a. (MonadIO m, Unbox a)
558 => Int -> Fold m a (Array a)
831 => Int -> Fold m a (Array a)
559−-- writeWith n = FL.rmapM spliceArrays $ toArraysOf n
832+writeWith = createWith
560−writeWith elemCount = unsafeFreeze <$> MA.writeWith elemCount
561
833
562-- | Fold the whole input to a single array.
834-- | Fold the whole input to a single array.
563--
835--
564-- /Caution! Do not use this on infinite streams./
836-- /Caution! Do not use this on infinite streams./
565--
837--
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." #-}
566{-# INLINE write #-}
844{-# INLINE write #-}
567write :: forall m a. (MonadIO m, Unbox a) => Fold m a (Array a)
845write :: forall m a. (MonadIO m, Unbox a) => Fold m a (Array a)
568−write = fmap unsafeFreeze MA.write
846+write = create
569
847
570−-- | Like 'write' but creates a pinned array.
848+-- | Like 'create' but creates a pinned array.
849+{-# INLINE pinnedCreate #-}
850+pinnedCreate :: forall m a. (MonadIO m, Unbox a) => Fold m a (Array a)
851+pinnedCreate = fmap unsafeFreeze MA.pinnedCreate
852+
853+{-# DEPRECATED pinnedWrite "Please use pinnedCreate instead." #-}
571{-# INLINE pinnedWrite #-}
854{-# INLINE pinnedWrite #-}
572pinnedWrite :: forall m a. (MonadIO m, Unbox a) => Fold m a (Array a)
855pinnedWrite :: forall m a. (MonadIO m, Unbox a) => Fold m a (Array a)
573−pinnedWrite = fmap unsafeFreeze MA.pinnedWrite
856+pinnedWrite = pinnedCreate
574
857
575-- | Fold "step" has a dependency on "initial", and each step is dependent on
858-- | Fold "step" has a dependency on "initial", and each step is dependent on
576-- the previous invocation of step due to state passing, finally extract
859-- the previous invocation of step due to state passing, finally extract
@@ -602,6 +885,11 @@ unsafeMakePure (Fold step initial extract final) =
602 (\s -> return $! unsafeInlineIO $ extract s)
885 (\s -> return $! unsafeInlineIO $ extract s)
603 (\s -> return $! unsafeInlineIO $ final s)
886 (\s -> return $! unsafeInlineIO $ final s)
604
887
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+
605-- | Convert a pure stream in Identity monad to an immutable array.
893-- | Convert a pure stream in Identity monad to an immutable array.
606--
894--
607-- Same as the following but with better performance:
895-- Same as the following but with better performance:
@@ -613,6 +901,19 @@ fromPureStream x = unsafePerformIO $ fmap (unsafeFreeze) (MA.fromPureStream x)
613-- fromPureStream = runIdentity . D.fold (unsafeMakePure write)
901-- fromPureStream = runIdentity . D.fold (unsafeMakePure write)
614-- fromPureStream = fromList . runIdentity . D.toList
902-- fromPureStream = fromList . runIdentity . D.toList
615
903
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+
616-- | Copy a null terminated immutable 'Addr#' Word8 sequence into an array.
917-- | Copy a null terminated immutable 'Addr#' Word8 sequence into an array.
617--
918--
618-- /Unsafe:/ The caller is responsible for safe addressing.
919-- /Unsafe:/ The caller is responsible for safe addressing.
@@ -623,8 +924,46 @@ fromPureStream x = unsafePerformIO $ fmap (unsafeFreeze) (MA.fromPureStream x)
623-- >>> Array.toList $ Array.fromByteStr# "\1\2\3\0"#
924-- >>> Array.toList $ Array.fromByteStr# "\1\2\3\0"#
624-- [1,2,3]
925-- [1,2,3]
625--
926--
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.
626fromByteStr# :: Addr# -> Array Word8
929fromByteStr# :: Addr# -> Array Word8
627−fromByteStr# addr = fromPureStream (D.fromByteStr# addr)
930+fromByteStr# addr = unsafePerformIO $ fmap unsafeFreeze (MA.fromByteStr# addr)
931+
932+-- XXX This should be monadic.
933+
934+-- | Note that this should be evaluated strictly to ensure that we do not hold
935+-- the reference to the pointer in a lazy thunk.
936+fromByteStr :: Ptr Word8 -> Array Word8
937+fromByteStr (Ptr addr#) = fromByteStr# addr#
938+
939+-- XXX implement fromChunks/fromChunkList instead?
940+
941+-- | Convert an array stream to an array. Note that this requires peak memory
942+-- that is double the size of the array stream.
943+--
944+{-# INLINE fromChunksK #-}
945+fromChunksK :: (MonadIO m, Unbox a) => StreamK m (Array a) -> m (Array a)
946+fromChunksK stream =
947+ -- We buffer the entire stream and then allocate the target array of the
948+ -- same size, thus requiring double the memory.
949+ fmap unsafeFreeze $ MA.fromChunksK $ fmap unsafeThaw stream
950+
951+{-# DEPRECATED fromArrayStreamK "Please use fromChunksK instead." #-}
952+fromArrayStreamK :: (Unbox a, MonadIO m) => StreamK m (Array a) -> m (Array a)
953+fromArrayStreamK = fromChunksK
954+
955+-- | Given a stream of arrays, splice them all together to generate a single
956+-- array. The stream must be /finite/.
957+--
958+{-# INLINE fromChunks #-}
959+fromChunks :: (MonadIO m, Unbox a) => Stream m (Array a) -> m (Array a)
960+fromChunks s =
961+ -- XXX Check which implementation is better
962+ -- This may also require double the memory as we double the space every
963+ -- time, when copying the last array we may have reallocated almost double
964+ -- the space required before we right size it.
965+ fmap unsafeFreeze $ MA.fromChunksRealloced (fmap unsafeThaw s)
966+ -- fromChunkStreamK $ D.toStreamK s
628
967
629-------------------------------------------------------------------------------
968-------------------------------------------------------------------------------
630-- Instances
969-- Instances
@@ -656,12 +995,66 @@ instance Unbox a => IsList (Array a) where
656 {-# INLINE toList #-}
995 {-# INLINE toList #-}
657 toList = toList
996 toList = toList
658
997
659−-- XXX we are assuming that Unboxed equality means element equality. This may
998+-- | Byte compare two arrays. Compare the length of the arrays. If the length
660−-- or may not be correct? arrcmp is 40% faster compared to stream equality.
999+-- is equal, compare the lexicographical ordering of two underlying byte arrays
1000+-- otherwise return the result of length comparison.
1001+--
1002+-- /Unsafe/: Note that the 'Unbox' instance of sum types with constructors of
1003+-- different sizes may leave some memory uninitialized which can make byte
1004+-- comparison unreliable.
1005+--
1006+-- /Pre-release/
1007+{-# INLINE byteCmp #-}
1008+byteCmp :: Array a -> Array a -> Ordering
1009+byteCmp arr1 arr2 =
1010+ -- unsafePerformIO?
1011+ unsafeInlineIO $! unsafeThaw arr1 `MA.byteCmp` unsafeThaw arr2
1012+
1013+-- | Byte equality of two arrays.
1014+--
1015+-- >>> byteEq arr1 arr2 = (==) EQ $ Array.byteCmp arr1 arr2
1016+--
1017+-- /Unsafe/: See 'byteCmp'.
1018+{-# INLINE byteEq #-}
1019+byteEq :: Array a -> Array a -> Bool
1020+byteEq arr1 arr2 = (==) EQ $ byteCmp arr1 arr2
1021+
1022+#define MK_EQ_INSTANCE(typ) \
1023+instance {-# OVERLAPPING #-} Eq (Array typ) where { \
1024+; {-# INLINE (==) #-} \
1025+; (==) = byteEq \
1026+}
1027+
1028+MK_EQ_INSTANCE(Char)
1029+MK_EQ_INSTANCE(Word8)
1030+MK_EQ_INSTANCE(Word16)
1031+MK_EQ_INSTANCE(Word32)
1032+
1033+-- XXX The Word64 default instance should be as fast because we are comparing
1034+-- 64-bit at a time.
1035+MK_EQ_INSTANCE(Word64)
1036+MK_EQ_INSTANCE(Int)
1037+MK_EQ_INSTANCE(Int8)
1038+MK_EQ_INSTANCE(Int16)
1039+MK_EQ_INSTANCE(Int32)
1040+
1041+-- XXX The Int64 default instance should be as fast.
1042+MK_EQ_INSTANCE(Int64)
1043+
1044+-- | If the type allows a byte-by-byte comparison this instance can be
1045+-- overlapped by a more specific instance that uses 'byteCmp'. Byte comparison
1046+-- can be significantly faster.
1047+--
661−instance (Unbox a, Eq a) => Eq (Array a) where
1048+instance {-# OVERLAPPABLE #-} (Unbox a, Eq a) => Eq (Array a) where
662 {-# INLINE (==) #-}
1049 {-# INLINE (==) #-}
663 arr1 == arr2 =
1050 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.
664− (==) EQ $ unsafeInlineIO $! unsafeThaw arr1 `MA.cmp` unsafeThaw arr2
1056+ -- (==) EQ $ unsafeInlineIO $! unsafeThaw arr1 `MA.cmp` unsafeThaw arr2
1057+ (toStreamD arr1 :: Stream Identity a) == toStreamD arr2
665
1058
666instance (Unbox a, Ord a) => Ord (Array a) where
1059instance (Unbox a, Ord a) => Ord (Array a) where
667 {-# INLINE compare #-}
1060 {-# INLINE compare #-}
@@ -703,7 +1096,7 @@ _toStreamD_ size Array{..} = D.Stream step arrStart
703 {-# INLINE_LATE step #-}
1096 {-# INLINE_LATE step #-}
704 step _ p | p == arrEnd = return D.Stop
1097 step _ p | p == arrEnd = return D.Stop
705 step _ p = liftIO $ do
1098 step _ p = liftIO $ do
706− x <- peekByteIndex p arrContents
1099+ x <- peekAt p arrContents
707 return $ D.Yield x (p + size)
1100 return $ D.Yield x (p + size)
708
1101
709{-
1102{-
@@ -726,15 +1119,29 @@ instance Foldable Array where
726-- Semigroup and Monoid
1119-- Semigroup and Monoid
727-------------------------------------------------------------------------------
1120-------------------------------------------------------------------------------
728
1121
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.
729instance Unbox a => Semigroup (Array a) where
1128instance Unbox a => Semigroup (Array a) where
730 arr1 <> arr2 = unsafePerformIO $ splice arr1 arr2
1129 arr1 <> arr2 = unsafePerformIO $ splice arr1 arr2
731
1130
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." #-}
732nil ::
1139nil ::
733#ifdef DEVBUILD
1140#ifdef DEVBUILD
734 Unbox a =>
1141 Unbox a =>
735#endif
1142#endif
736 Array a
1143 Array a
737−nil = Array Unboxed.nil 0 0
1144+nil = empty
738
1145
739instance Unbox a => Monoid (Array a) where
1146instance Unbox a => Monoid (Array a) where
740 mempty = nil
1147 mempty = nil
core/src/Streamly/Internal/Data/Serialize/Parser.hs → core/src/Streamly/Internal/Data/Binary/Parser.hsrenamed+2−2
@@ -1,5 +1,5 @@
1-- |
1-- |
2−-- Module : Streamly.Internal.Data.Serialize.Parser
2+-- Module : Streamly.Internal.Data.Binary.Parser
3-- Copyright : (c) 2020 Composewell Technologies
3-- Copyright : (c) 2020 Composewell Technologies
4-- License : BSD-3-Clause
4-- License : BSD-3-Clause
5-- Maintainer : streamly@composewell.com
5-- Maintainer : streamly@composewell.com
@@ -13,7 +13,7 @@
13-- representing Haskell data types is to write them to arrays and deserialize
13-- representing Haskell data types is to write them to arrays and deserialize
14-- the array using the Serialize type class.
14-- the array using the Serialize type class.
15
15
16−module Streamly.Internal.Data.Serialize.Parser
16+module Streamly.Internal.Data.Binary.Parser
17 (
17 (
18 -- * Type class
18 -- * Type class
19 FromBytes (..)
19 FromBytes (..)
core/src/Streamly/Internal/Data/Serialize/Stream.hs → core/src/Streamly/Internal/Data/Binary/Stream.hsrenamed+2−2
@@ -1,5 +1,5 @@
1-- |
1-- |
2−-- Module : Streamly.Internal.Data.Serialize.Stream
2+-- Module : Streamly.Internal.Data.Binary.Stream
3-- Copyright : (c) 2022 Composewell Technologies
3-- Copyright : (c) 2022 Composewell Technologies
4-- License : BSD-3-Clause
4-- License : BSD-3-Clause
5-- Maintainer : streamly@composewell.com
5-- Maintainer : streamly@composewell.com
@@ -17,7 +17,7 @@
17
17
18-- XXX remove unit, bool, ordering, and the type class as well
18-- XXX remove unit, bool, ordering, and the type class as well
19
19
20−module Streamly.Internal.Data.Serialize.Stream
20+module Streamly.Internal.Data.Binary.Stream
21 (
21 (
22 -- * Type class
22 -- * Type class
23 ToBytes (..)
23 ToBytes (..)
core/src/Streamly/Internal/Data/Fold/Chunked.hs+2−2
@@ -124,7 +124,7 @@ fromFold (Fold.Fold fstep finitial _ ffinal) =
124 assert (cur == end) (return ())
124 assert (cur == end) (return ())
125 return $ Partial 0 fs
125 return $ Partial 0 fs
126 goArray !_ !cur !fs = do
126 goArray !_ !cur !fs = do
127− x <- liftIO $ peekByteIndex cur contents
127+ x <- liftIO $ peekAt cur contents
128 res <- fstep fs x
128 res <- fstep fs x
129 let elemSize = SIZE_OF(a)
129 let elemSize = SIZE_OF(a)
130 next = INDEX_NEXT(cur,a)
130 next = INDEX_NEXT(cur,a)
@@ -163,7 +163,7 @@ fromParserD (ParserD.Parser step1 initial1 extract1) =
163 else return $ st (arrRem + n) fs1
163 else return $ st (arrRem + n) fs1
164
164
165 goArray !_ !cur !fs = do
165 goArray !_ !cur !fs = do
166− x <- liftIO $ peekByteIndex cur contents
166+ x <- liftIO $ peekAt cur contents
167 res <- step1 fs x
167 res <- step1 fs x
168 let elemSize = SIZE_OF(a)
168 let elemSize = SIZE_OF(a)
169 next = INDEX_NEXT(cur,a)
169 next = INDEX_NEXT(cur,a)
core/src/Streamly/Internal/Data/Fold/Combinators.hs−92
@@ -30,8 +30,6 @@ module Streamly.Internal.Data.Fold.Combinators
30 -- *** Reducers
30 -- *** Reducers
31 , drainMapM
31 , drainMapM
32 , the
32 , the
33− , length
34− , lengthGeneric
35 , mean
33 , mean
36 , rollingHash
34 , rollingHash
37 , defaultSalt
35 , defaultSalt
@@ -166,8 +164,6 @@ module Streamly.Internal.Data.Fold.Combinators
166 -- ** Trimming
164 -- ** Trimming
167
165
168 -- By elements
166 -- By elements
169− , takeEndBy
170− , takeEndBy_
171 , takeEndBySeq
167 , takeEndBySeq
172 , takeEndBySeq_
168 , takeEndBySeq_
173 {-
169 {-
@@ -709,30 +705,6 @@ the = foldt' step initial id
709-- To Summary
705-- To Summary
710------------------------------------------------------------------------------
706------------------------------------------------------------------------------
711
707
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−
736-- | Determine the sum of all elements of a stream of numbers. Returns additive
708-- | Determine the sum of all elements of a stream of numbers. Returns additive
737-- identity (@0@) when the stream is empty. Note that this is not numerically
709-- identity (@0@) when the stream is empty. Note that this is not numerically
738-- stable for floating point numbers.
710-- stable for floating point numbers.
@@ -1510,70 +1482,6 @@ droppingWhileM p = Fold step initial extract extract
1510droppingWhile :: Monad m => (a -> Bool) -> Fold m a (Maybe a)
1482droppingWhile :: Monad m => (a -> Bool) -> Fold m a (Maybe a)
1511droppingWhile p = droppingWhileM (return . p)
1483droppingWhile p = droppingWhileM (return . p)
1512
1484
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−
1577------------------------------------------------------------------------------
1485------------------------------------------------------------------------------
1578-- Binary splitting on a separator
1486-- Binary splitting on a separator
1579------------------------------------------------------------------------------
1487------------------------------------------------------------------------------
core/src/Streamly/Internal/Data/Fold/Type.hs+91
@@ -367,6 +367,8 @@ module Streamly.Internal.Data.Fold.Type
367 , toList
367 , toList
368 , toStreamK
368 , toStreamK
369 , toStreamKRev
369 , toStreamKRev
370+ , lengthGeneric
371+ , length
370
372
371 -- * Combinators
373 -- * Combinators
372
374
@@ -391,6 +393,8 @@ module Streamly.Internal.Data.Fold.Type
391 -- ** Trimming
393 -- ** Trimming
392 , take
394 , take
393 , taking
395 , taking
396+ , takeEndBy_
397+ , takeEndBy
394 , dropping
398 , dropping
395
399
396 -- ** Sequential application
400 -- ** Sequential application
@@ -767,6 +771,29 @@ toStreamKRev = foldl' (flip K.cons) K.nil
767toStreamK :: Monad m => Fold m a (K.StreamK n a)
771toStreamK :: Monad m => Fold m a (K.StreamK n a)
768toStreamK = foldr K.cons K.nil
772toStreamK = foldr K.cons K.nil
769
773
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+
770------------------------------------------------------------------------------
797------------------------------------------------------------------------------
771-- Instances
798-- Instances
772------------------------------------------------------------------------------
799------------------------------------------------------------------------------
@@ -1517,6 +1544,70 @@ take n (Fold fstep finitial fextract ffinal) = Fold step initial extract final
1517
1544
1518 final (Tuple'Fused _ r) = ffinal r
1545 final (Tuple'Fused _ r) = ffinal r
1519
1546
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+
1520------------------------------------------------------------------------------
1611------------------------------------------------------------------------------
1521-- Nesting
1612-- Nesting
1522------------------------------------------------------------------------------
1613------------------------------------------------------------------------------
core/src/Streamly/Internal/Data/IORef/Unboxed.hs+6−5
@@ -40,9 +40,10 @@ where
40
40
41import Control.Monad.IO.Class (MonadIO(..))
41import Control.Monad.IO.Class (MonadIO(..))
42import Data.Proxy (Proxy(..))
42import Data.Proxy (Proxy(..))
43−import Streamly.Internal.Data.MutByteArray (MutByteArray(..), newByteArray)
43+import Streamly.Internal.Data.MutByteArray.Type (MutByteArray)
44import Streamly.Internal.Data.Unbox (Unbox(..), sizeOf)
44import Streamly.Internal.Data.Unbox (Unbox(..), sizeOf)
45
45
46+import qualified Streamly.Internal.Data.MutByteArray.Type as MBA
46import qualified Streamly.Internal.Data.Stream.Type as D
47import qualified Streamly.Internal.Data.Stream.Type as D
47
48
48-- | An 'IORef' holds a single 'Unbox'-able value.
49-- | An 'IORef' holds a single 'Unbox'-able value.
@@ -54,8 +55,8 @@ newtype IORef a = IORef MutByteArray
54{-# INLINE newIORef #-}
55{-# INLINE newIORef #-}
55newIORef :: forall a. Unbox a => a -> IO (IORef a)
56newIORef :: forall a. Unbox a => a -> IO (IORef a)
56newIORef x = do
57newIORef x = do
57− var <- newByteArray (sizeOf (Proxy :: Proxy a))
58+ var <- MBA.new (sizeOf (Proxy :: Proxy a))
58− pokeByteIndex 0 var x
59+ pokeAt 0 var x
59 return $ IORef var
60 return $ IORef var
60
61
61-- | Write a value to an 'IORef'.
62-- | Write a value to an 'IORef'.
@@ -63,14 +64,14 @@ newIORef x = do
63-- /Pre-release/
64-- /Pre-release/
64{-# INLINE writeIORef #-}
65{-# INLINE writeIORef #-}
65writeIORef :: Unbox a => IORef a -> a -> IO ()
66writeIORef :: Unbox a => IORef a -> a -> IO ()
66−writeIORef (IORef var) = pokeByteIndex 0 var
67+writeIORef (IORef var) = pokeAt 0 var
67
68
68-- | Read a value from an 'IORef'.
69-- | Read a value from an 'IORef'.
69--
70--
70-- /Pre-release/
71-- /Pre-release/
71{-# INLINE readIORef #-}
72{-# INLINE readIORef #-}
72readIORef :: Unbox a => IORef a -> IO a
73readIORef :: Unbox a => IORef a -> IO a
73−readIORef (IORef var) = peekByteIndex 0 var
74+readIORef (IORef var) = peekAt 0 var
74
75
75-- | Modify the value of an 'IORef' using a function with strict application.
76-- | Modify the value of an 'IORef' using a function with strict application.
76--
77--
core/src/Streamly/Internal/Data/IsMap.hs+4
@@ -1,3 +1,7 @@
1+{-# LANGUAGE TypeFamilies #-}
2+-- Must come after TypeFamilies, otherwise it is re-enabled.
3+-- MonoLocalBinds enabled by TypeFamilies causes perf regressions in general.
4+{-# LANGUAGE NoMonoLocalBinds #-}
1-- |
5-- |
2-- Module : Streamly.Internal.Data.IsMap
6-- Module : Streamly.Internal.Data.IsMap
3-- Copyright : (c) 2022 Composewell Technologies
7-- Copyright : (c) 2022 Composewell Technologies
core/src/Streamly/Internal/Data/MutArray.hs+151−33
@@ -8,82 +8,200 @@
8--
8--
9module Streamly.Internal.Data.MutArray
9module Streamly.Internal.Data.MutArray
10 (
10 (
11+ -- * MutArray.Type module
11 module Streamly.Internal.Data.MutArray.Type
12 module Streamly.Internal.Data.MutArray.Type
13+ -- * MutArray module
14+ , sliceIndexerFromLen
15+ , slicerFromLen
16+ , compactLE
17+ , pinnedCompactLE
18+ , compactOnByte
19+ , compactOnByteSuffix
12 -- * Unboxed IORef
20 -- * Unboxed IORef
13 , module Streamly.Internal.Data.IORef.Unboxed
21 , module Streamly.Internal.Data.IORef.Unboxed
14− , splitOn
22+
23+ -- * Deprecated
15 , genSlicesFromLen
24 , genSlicesFromLen
16 , getSlicesFromLen
25 , getSlicesFromLen
17− , fromStream
18 )
26 )
19where
27where
20
28
21#include "inline.hs"
29#include "inline.hs"
22
30
23import Control.Monad.IO.Class (MonadIO(..))
31import 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)
24import Streamly.Internal.Data.Unbox (Unbox)
35import Streamly.Internal.Data.Unbox (Unbox)
25−import Streamly.Internal.Data.Stream (Stream)
26import Streamly.Internal.Data.Unfold.Type (Unfold(..))
36import Streamly.Internal.Data.Unfold.Type (Unfold(..))
27
37
28−import qualified Streamly.Internal.Data.Stream as D
38+import qualified Streamly.Internal.Data.Stream.Type as Stream
39+import qualified Streamly.Internal.Data.Stream.Nesting as Stream
40+-- import qualified Streamly.Internal.Data.Stream.Transform as Stream
29import qualified Streamly.Internal.Data.Unfold as Unfold
41import qualified Streamly.Internal.Data.Unfold as Unfold
30
42
31import Prelude hiding (foldr, length, read, splitAt)
43import Prelude hiding (foldr, length, read, splitAt)
32import Streamly.Internal.Data.MutArray.Type
44import Streamly.Internal.Data.MutArray.Type
33import Streamly.Internal.Data.IORef.Unboxed
45import Streamly.Internal.Data.IORef.Unboxed
34
46
35−-- | Split the array into a stream of slices using a predicate. The element
36−-- matching the predicate is dropped.
37−--
38−-- /Pre-release/
39−{-# INLINE splitOn #-}
40−splitOn :: (MonadIO m, Unbox a) =>
41− (a -> Bool) -> MutArray a -> Stream m (MutArray a)
42−splitOn predicate arr =
43− fmap (\(i, len) -> getSliceUnsafe i len arr)
44− $ D.sliceOnSuffix predicate (read arr)
45−
46-- | Generate a stream of array slice descriptors ((index, len)) of specified
47-- | Generate a stream of array slice descriptors ((index, len)) of specified
47-- length from an array, starting from the supplied array index. The last slice
48-- length from an array, starting from the supplied array index. The last slice
48-- may be shorter than the requested length depending on the array length.
49-- may be shorter than the requested length depending on the array length.
49--
50--
50-- /Pre-release/
51-- /Pre-release/
51−{-# INLINE genSlicesFromLen #-}
52+{-# INLINE sliceIndexerFromLen #-}
52−genSlicesFromLen :: forall m a. (Monad m, Unbox a)
53+sliceIndexerFromLen :: forall m a. (Monad m, Unbox a)
53 => Int -- ^ from index
54 => Int -- ^ from index
54 -> Int -- ^ length of the slice
55 -> Int -- ^ length of the slice
55 -> Unfold m (MutArray a) (Int, Int)
56 -> Unfold m (MutArray a) (Int, Int)
56−genSlicesFromLen from len =
57+sliceIndexerFromLen from len =
57 let fromThenTo n = (from, from + len, n - 1)
58 let fromThenTo n = (from, from + len, n - 1)
58 mkSlice n i = return (i, min len (n - i))
59 mkSlice n i = return (i, min len (n - i))
59 in Unfold.lmap length
60 in Unfold.lmap length
60 $ Unfold.mapM2 mkSlice
61 $ Unfold.mapM2 mkSlice
61 $ Unfold.lmap fromThenTo Unfold.enumerateFromThenTo
62 $ Unfold.lmap fromThenTo Unfold.enumerateFromThenTo
62
63
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+
63-- | Generate a stream of slices of specified length from an array, starting
71-- | Generate a stream of slices of specified length from an array, starting
64-- from the supplied array index. The last slice may be shorter than the
72-- from the supplied array index. The last slice may be shorter than the
65-- requested length depending on the array length.
73-- requested length depending on the array length.
66--
74--
67-- /Pre-release/
75-- /Pre-release/
68−{-# INLINE getSlicesFromLen #-}
76+{-# INLINE slicerFromLen #-}
69−getSlicesFromLen :: forall m a. (Monad m, Unbox a)
77+slicerFromLen :: forall m a. (Monad m, Unbox a)
70 => Int -- ^ from index
78 => Int -- ^ from index
71 -> Int -- ^ length of the slice
79 -> Int -- ^ length of the slice
72 -> Unfold m (MutArray a) (MutArray a)
80 -> Unfold m (MutArray a) (MutArray a)
73−getSlicesFromLen from len =
81+slicerFromLen from len =
74 let mkSlice arr (i, n) = return $ getSliceUnsafe i n arr
82 let mkSlice arr (i, n) = return $ getSliceUnsafe i n arr
75− in Unfold.mapM2 mkSlice (genSlicesFromLen from len)
83+ in Unfold.mapM2 mkSlice (sliceIndexerFromLen from len)
84+
85+{-# DEPRECATED getSlicesFromLen "Please use slicerFromLen instead." #-}
86+getSlicesFromLen :: forall m a. (Monad m, Unbox a)
87+ => Int -- ^ from index
88+ -> Int -- ^ length of the slice
89+ -> Unfold m (MutArray a) (MutArray a)
90+getSlicesFromLen = slicerFromLen
76
91
77−-- | Create an 'Array' from a stream. This is useful when we want to create a
92+-------------------------------------------------------------------------------
78−-- single array from a stream of unknown size. 'writeN' is at least twice
93+-- Compacting Streams of Arrays
79−-- as efficient when the size is already known.
94+-------------------------------------------------------------------------------
95+
96+-- | @compactLE maxElems@ coalesces adjacent arrays in the input stream
97+-- only if the combined size would be less than or equal to @maxElems@
98+-- elements. Note that it won't split an array if the original array is already
99+-- larger than maxElems.
80--
100--
81−-- Note that if the input stream is too large memory allocation for the array
101+-- @maxElems@ must be greater than 0.
82−-- may fail. When the stream size is not known, `chunksOf` followed by
83−-- processing of indvidual arrays in the resulting stream should be preferred.
84--
102--
85−-- /Pre-release/
103+-- Generates unpinned arrays irrespective of the pinning status of input
86−{-# INLINE fromStream #-}
104+-- arrays.
105+{-# INLINE compactLE #-}
106+compactLE :: (MonadIO m, Unbox a) =>
87−fromStream :: (MonadIO m, Unbox a) => Stream m a -> m (MutArray a)
107+ Int -> Stream m (MutArray a) -> Stream m (MutArray a)
88−fromStream = fromStreamD
108+-- XXX compactLE can be moved to MutArray/Type if we are not using the parser
89−-- fromStream (Stream m) = P.fold write m
109+-- to implement it.
110+compactLE = compactLeAs Unpinned
111+-- The parser version turns out to be a little bit slower.
112+-- compactLE n = Stream.catRights . Stream.parseManyD (pCompactLE n)
113+
114+-- | Pinned version of 'compactLE'.
115+{-# INLINE pinnedCompactLE #-}
116+pinnedCompactLE :: forall m a. (MonadIO m, Unbox a)
117+ => Int -> Stream m (MutArray a) -> Stream m (MutArray a)
118+pinnedCompactLE = compactLeAs Pinned
119+-- pinnedCompactLE n = Stream.catRights . Stream.parseManyD (pPinnedCompactLE n)
120+
121+data SplitState s arr
122+ = Initial s
123+ | Buffering s arr
124+ | Splitting s arr
125+ | Yielding arr (SplitState s arr)
126+ | Finishing
127+
128+-- | Split a stream of arrays on a given separator byte, dropping the separator
129+-- and coalescing all the arrays between two separators into a single array.
130+--
131+{-# INLINE_NORMAL _compactOnByteCustom #-}
132+_compactOnByteCustom
133+ :: MonadIO m
134+ => Word8
135+ -> Stream m (MutArray Word8)
136+ -> Stream m (MutArray Word8)
137+_compactOnByteCustom byte (Stream.Stream step state) =
138+ Stream.Stream step' (Initial state)
139+
140+ where
141+
142+ {-# INLINE_LATE step' #-}
143+ step' gst (Initial st) = do
144+ r <- step gst st
145+ case r of
146+ Stream.Yield arr s -> do
147+ (arr1, marr2) <- breakOn byte arr
148+ return $ case marr2 of
149+ Nothing -> Stream.Skip (Buffering s arr1)
150+ Just arr2 -> Stream.Skip (Yielding arr1 (Splitting s arr2))
151+ Stream.Skip s -> return $ Stream.Skip (Initial s)
152+ Stream.Stop -> return Stream.Stop
153+
154+ step' gst (Buffering st buf) = do
155+ r <- step gst st
156+ case r of
157+ Stream.Yield arr s -> do
158+ (arr1, marr2) <- breakOn byte arr
159+ -- XXX Use spliceExp instead and then rightSize?
160+ buf1 <- splice buf arr1
161+ return $ case marr2 of
162+ Nothing -> Stream.Skip (Buffering s buf1)
163+ Just x -> Stream.Skip (Yielding buf1 (Splitting s x))
164+ Stream.Skip s -> return $ Stream.Skip (Buffering s buf)
165+ Stream.Stop -> return $
166+ if byteLength buf == 0
167+ then Stream.Stop
168+ else Stream.Skip (Yielding buf Finishing)
169+
170+ step' _ (Splitting st buf) = do
171+ (arr1, marr2) <- breakOn byte buf
172+ return $ case marr2 of
173+ Nothing -> Stream.Skip $ Buffering st arr1
174+ Just arr2 -> Stream.Skip $ Yielding arr1 (Splitting st arr2)
175+
176+ step' _ (Yielding arr next) = return $ Stream.Yield arr next
177+ step' _ Finishing = return Stream.Stop
178+
179+-- XXX implement predicate based version of this
180+-- XXX Naming of predicate based vs custom version
181+
182+-- | Split a stream of arrays on a given separator byte, dropping the separator
183+-- and coalescing all the arrays between two separators into a single array.
184+--
185+{-# INLINE compactOnByte #-}
186+compactOnByte
187+ :: (MonadIO m)
188+ => Word8
189+ -> Stream m (MutArray Word8)
190+ -> Stream m (MutArray Word8)
191+-- XXX compare perf of custom vs idiomatic version
192+-- compactOnByte = _compactOnByteCustom
193+-- XXX use spliceExp and rightSize?
194+compactOnByte byte = Stream.splitInnerBy (breakOn byte) splice
195+
196+-- | Like 'compactOnByte' considers the separator in suffix position instead of
197+-- infix position.
198+{-# INLINE compactOnByteSuffix #-}
199+compactOnByteSuffix
200+ :: (MonadIO m)
201+ => Word8
202+ -> Stream m (MutArray Word8)
203+ -> Stream m (MutArray Word8)
204+compactOnByteSuffix byte =
205+ -- XXX use spliceExp and rightSize?
206+ Stream.splitInnerBySuffix
207+ (\arr -> byteLength arr == 0) (breakOn byte) splice
core/src/Streamly/Internal/Data/MutArray/Generic.hs+65−30
@@ -19,14 +19,14 @@ module Streamly.Internal.Data.MutArray.Generic
19 , nil
19 , nil
20
20
21 -- *** Uninitialized Arrays
21 -- *** Uninitialized Arrays
22− , new
22+ , emptyOf
23 -- , newArrayWith
23 -- , newArrayWith
24
24
25 -- *** From streams
25 -- *** From streams
26− , writeNUnsafe
26+ , unsafeCreateOf
27− , writeN
27+ , createOf
28− , writeWith
28+ , createWith -- createOfMin/createMin/createGE?
29− , write
29+ , create
30 , fromStreamN
30 , fromStreamN
31 , fromStream
31 , fromStream
32 , fromPureStream
32 , fromPureStream
@@ -160,6 +160,13 @@ 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
163 )
170 )
164where
171where
165
172
@@ -236,13 +243,13 @@ bottomElement =
236-- XXX Would be nice if GHC can provide something like newUninitializedArray# so
243-- XXX Would be nice if GHC can provide something like newUninitializedArray# so
237-- that we do not have to write undefined or error in the whole array.
244-- that we do not have to write undefined or error in the whole array.
238
245
239−-- | @new count@ allocates a zero length array that can be extended to hold
246+-- | @emptyOf count@ allocates a zero length array that can be extended to hold
240-- up to 'count' items without reallocating.
247-- up to 'count' items without reallocating.
241--
248--
242-- /Pre-release/
249-- /Pre-release/
243−{-# INLINE new #-}
250+{-# INLINE emptyOf #-}
244−new :: MonadIO m => Int -> m (MutArray a)
251+emptyOf :: MonadIO m => Int -> m (MutArray a)
245−new n@(I# n#) =
252+emptyOf n@(I# n#) =
246 liftIO
253 liftIO
247 $ IO
254 $ IO
248 $ \s# ->
255 $ \s# ->
@@ -251,6 +258,12 @@ new n@(I# n#) =
251 let ma = MutArray arr# 0 0 n
258 let ma = MutArray arr# 0 0 n
252 in (# s1#, ma #)
259 in (# s1#, ma #)
253
260
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+
254-- XXX This could be pure?
267-- XXX This could be pure?
255
268
256-- |
269-- |
@@ -318,7 +331,7 @@ putIndices arr = FL.foldlM' step (return ())
318
331
319 where
332 where
320
333
321− step () (i, x) = liftIO (putIndex i arr x)
334+ step () (i, x) = putIndex i arr x
322
335
323-- | Modify a given index of an array using a modifier function without checking
336-- | Modify a given index of an array using a modifier function without checking
324-- the bounds.
337-- the bounds.
@@ -589,16 +602,16 @@ readRev arr@MutArray{..} =
589arrayChunkSize :: Int
602arrayChunkSize :: Int
590arrayChunkSize = 1024
603arrayChunkSize = 1024
591
604
592−-- | Like 'writeN' but does not check the array bounds when writing. The fold
605+-- | Like 'createOf' but does not check the array bounds when writing. The fold
593-- driver must not call the step function more than 'n' times otherwise it will
606-- driver must not call the step function more than 'n' times otherwise it will
594-- corrupt the memory and crash. This function exists mainly because any
607-- corrupt the memory and crash. This function exists mainly because any
595-- conditional in the step function blocks fusion causing 10x performance
608-- conditional in the step function blocks fusion causing 10x performance
596-- slowdown.
609-- slowdown.
597--
610--
598-- /Pre-release/
611-- /Pre-release/
599−{-# INLINE_NORMAL writeNUnsafe #-}
612+{-# INLINE_NORMAL unsafeCreateOf #-}
600−writeNUnsafe :: MonadIO m => Int -> Fold m a (MutArray a)
613+unsafeCreateOf :: MonadIO m => Int -> Fold m a (MutArray a)
601−writeNUnsafe n = Fold step initial return return
614+unsafeCreateOf n = Fold step initial return return
602
615
603 where
616 where
604
617
@@ -606,59 +619,81 @@ writeNUnsafe n = Fold step initial return return
606
619
607 step arr x = FL.Partial <$> snocUnsafe arr x
620 step arr x = FL.Partial <$> snocUnsafe arr x
608
621
622+{-# DEPRECATED writeNUnsafe "Please use unsafeCreateOf instead." #-}
623+{-# INLINE writeNUnsafe #-}
624+writeNUnsafe :: MonadIO m => Int -> Fold m a (MutArray a)
625+writeNUnsafe = unsafeCreateOf
626+
609−-- | @writeN n@ folds a maximum of @n@ elements from the input stream to an
627+-- | @createOf n@ folds a maximum of @n@ elements from the input stream to an
610-- 'Array'.
628-- 'Array'.
611--
629--
612−-- >>> writeN n = Fold.take n (MutArray.writeNUnsafe n)
630+-- >>> createOf n = Fold.take n (MutArray.unsafeCreateOf n)
613--
631--
614-- /Pre-release/
632-- /Pre-release/
615−{-# INLINE_NORMAL writeN #-}
633+{-# INLINE_NORMAL createOf #-}
634+createOf :: MonadIO m => Int -> Fold m a (MutArray a)
635+createOf n = FL.take n $ unsafeCreateOf n
636+
637+-- XXX Deprecate in major
638+-- {-# DEPRECATED writeN "Please use createOf instead." #-}
639+{-# INLINE writeN #-}
616writeN :: MonadIO m => Int -> Fold m a (MutArray a)
640writeN :: MonadIO m => Int -> Fold m a (MutArray a)
617−writeN n = FL.take n $ writeNUnsafe n
641+writeN = createOf
618
642
619-- >>> f n = MutArray.writeAppendWith (* 2) (MutArray.pinnedNew n)
643-- >>> f n = MutArray.writeAppendWith (* 2) (MutArray.pinnedNew n)
620-- >>> writeWith n = Fold.rmapM MutArray.rightSize (f n)
644-- >>> writeWith n = Fold.rmapM MutArray.rightSize (f n)
621-- >>> writeWith n = Fold.rmapM MutArray.fromArrayStreamK (MutArray.writeChunks n)
645-- >>> writeWith n = Fold.rmapM MutArray.fromArrayStreamK (MutArray.writeChunks n)
622
646
623−-- | @writeWith minCount@ folds the whole input to a single array. The array
647+-- | @createWith minCount@ folds the whole input to a single array. The array
624-- starts at a size big enough to hold minCount elements, the size is doubled
648-- starts at a size big enough to hold minCount elements, the size is doubled
625-- every time the array needs to be grown.
649-- every time the array needs to be grown.
626--
650--
627-- /Caution! Do not use this on infinite streams./
651-- /Caution! Do not use this on infinite streams./
628--
652--
629-- /Pre-release/
653-- /Pre-release/
630−{-# INLINE_NORMAL writeWith #-}
654+{-# INLINE_NORMAL createWith #-}
631−writeWith :: MonadIO m => Int -> Fold m a (MutArray a)
655+createWith :: MonadIO m => Int -> Fold m a (MutArray a)
632-- writeWith n = FL.rmapM rightSize $ writeAppendWith (* 2) (pinnedNew n)
656-- writeWith n = FL.rmapM rightSize $ writeAppendWith (* 2) (pinnedNew n)
633−writeWith elemCount = FL.rmapM extract $ FL.foldlM' step initial
657+createWith elemCount = FL.rmapM extract $ FL.foldlM' step initial
634
658
635 where
659 where
636
660
637 initial = do
661 initial = do
638− when (elemCount < 0) $ error "writeWith: elemCount is negative"
662+ when (elemCount < 0) $ error "createWith: elemCount is negative"
639− liftIO $ new elemCount
663+ new elemCount
640
664
641 step arr@(MutArray _ start end bound) x
665 step arr@(MutArray _ start end bound) x
642 | end == bound = do
666 | end == bound = do
643 let oldSize = end - start
667 let oldSize = end - start
644 newSize = max (oldSize * 2) 1
668 newSize = max (oldSize * 2) 1
645− arr1 <- liftIO $ realloc newSize arr
669+ arr1 <- realloc newSize arr
646 snocUnsafe arr1 x
670 snocUnsafe arr1 x
647 step arr x = snocUnsafe arr x
671 step arr x = snocUnsafe arr x
648
672
649− -- extract = liftIO . rightSize
673+ -- extract = rightSize
650 extract = return
674 extract = return
651
675
676+{-# DEPRECATED writeWith "Please use createWith instead." #-}
677+{-# INLINE writeWith #-}
678+writeWith :: MonadIO m => Int -> Fold m a (MutArray a)
679+writeWith = createWith
680+
652-- | Fold the whole input to a single array.
681-- | Fold the whole input to a single array.
653--
682--
654−-- Same as 'writeWith' using an initial array size of 'arrayChunkSize' bytes
683+-- Same as 'createWith' using an initial array size of 'arrayChunkSize' bytes
655-- rounded up to the element size.
684-- rounded up to the element size.
656--
685--
657-- /Caution! Do not use this on infinite streams./
686-- /Caution! Do not use this on infinite streams./
658--
687--
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." #-}
659{-# INLINE write #-}
694{-# INLINE write #-}
660write :: MonadIO m => Fold m a (MutArray a)
695write :: MonadIO m => Fold m a (MutArray a)
661−write = writeWith arrayChunkSize
696+write = create
662
697
663-- | Create a 'MutArray' from the first @n@ elements of a stream. The
698-- | Create a 'MutArray' from the first @n@ elements of a stream. The
664-- array is allocated to size @n@, if the stream terminates before @n@
699-- array is allocated to size @n@, if the stream terminates before @n@
@@ -683,7 +718,7 @@ fromList xs = fromStream $ D.fromList xs
683{-# INLINABLE fromPureStream #-}
718{-# INLINABLE fromPureStream #-}
684fromPureStream :: MonadIO m => Stream Identity a -> m (MutArray a)
719fromPureStream :: MonadIO m => Stream Identity a -> m (MutArray a)
685fromPureStream xs =
720fromPureStream xs =
686− liftIO $ D.fold write $ D.morphInner (return . runIdentity) xs
721+ D.fold write $ D.morphInner (return . runIdentity) xs
687
722
688-------------------------------------------------------------------------------
723-------------------------------------------------------------------------------
689-- Chunking
724-- Chunking
@@ -815,7 +850,7 @@ putSliceUnsafe src srcStart dst dstStart len = liftIO $ do
815
850
816{-# INLINE clone #-}
851{-# INLINE clone #-}
817clone :: MonadIO m => MutArray a -> m (MutArray a)
852clone :: MonadIO m => MutArray a -> m (MutArray a)
818−clone src = liftIO $ do
853+clone src = do
819 let len = arrLen src
854 let len = arrLen src
820 dst <- new len
855 dst <- new len
821 putSliceUnsafe src 0 dst 0 len
856 putSliceUnsafe src 0 dst 0 len
core/src/Streamly/Internal/Data/MutArray/Stream.hs+11−195
@@ -12,6 +12,7 @@
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." #-}
15 (
16 (
16 -- * Generation
17 -- * Generation
17 MArray.chunksOf
18 MArray.chunksOf
@@ -21,7 +22,7 @@ module Streamly.Internal.Data.MutArray.Stream
21
22
22 -- * Compaction
23 -- * Compaction
23 , packArraysChunksOf
24 , packArraysChunksOf
24− , SpliceState (..)
25+ , MArray.SpliceState (..)
25 , lpackArraysChunksOf
26 , lpackArraysChunksOf
26 , compact -- chunksCompact
27 , compact -- chunksCompact
27 , compactLE
28 , compactLE
@@ -35,19 +36,12 @@ module Streamly.Internal.Data.MutArray.Stream
35 )
36 )
36where
37where
37
38
38−#include "inline.hs"
39−#include "ArrayMacros.h"
40−
41import Control.Monad.IO.Class (MonadIO(..))
39import Control.Monad.IO.Class (MonadIO(..))
42−import Control.Monad (when)
43−import Data.Bifunctor (first)
44−import Data.Proxy (Proxy(..))
45−import Streamly.Internal.Data.Unbox (Unbox, sizeOf)
40+import Streamly.Internal.Data.Unbox (Unbox)
46import Streamly.Internal.Data.MutArray.Type (MutArray(..))
41import Streamly.Internal.Data.MutArray.Type (MutArray(..))
47import Streamly.Internal.Data.Fold.Type (Fold(..))
42import Streamly.Internal.Data.Fold.Type (Fold(..))
48import Streamly.Internal.Data.Parser (ParseError)
43import Streamly.Internal.Data.Parser (ParseError)
49import Streamly.Internal.Data.Stream.Type (Stream)
44import Streamly.Internal.Data.Stream.Type (Stream)
50−import Streamly.Internal.Data.Tuple.Strict (Tuple'(..))
51
45
52import qualified Streamly.Internal.Data.MutArray as MArray
46import qualified Streamly.Internal.Data.MutArray as MArray
53import qualified Streamly.Internal.Data.Fold.Type as FL
47import qualified Streamly.Internal.Data.Fold.Type as FL
@@ -58,12 +52,6 @@ import qualified Streamly.Internal.Data.Stream as D
58-- Compact
52-- Compact
59-------------------------------------------------------------------------------
53-------------------------------------------------------------------------------
60
54
61−data SpliceState s arr
62− = SpliceInitial s
63− | SpliceBuffering s arr
64− | SpliceYielding arr (SpliceState s arr)
65− | SpliceFinish
66−
67-- XXX This can be removed once compactLEFold/compactLE are implemented.
55-- XXX This can be removed once compactLEFold/compactLE are implemented.
68--
56--
69-- | This mutates the first array (if it has space) to append values from the
57-- | This mutates the first array (if it has space) to append values from the
@@ -78,117 +66,18 @@ data SpliceState s arr
78-- actual array size may be less than the specified chunk size.
66-- actual array size may be less than the specified chunk size.
79--
67--
80-- @since 0.7.0
68-- @since 0.7.0
81−{-# INLINE_NORMAL packArraysChunksOf #-}
69+{-# INLINE packArraysChunksOf #-}
82packArraysChunksOf :: (MonadIO m, Unbox a)
70packArraysChunksOf :: (MonadIO m, Unbox a)
83 => Int -> D.Stream m (MutArray a) -> D.Stream m (MutArray a)
71 => Int -> D.Stream m (MutArray a) -> D.Stream m (MutArray a)
84−packArraysChunksOf n (D.Stream step state) =
72+packArraysChunksOf = MArray.compactLE
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
125
73
126-- XXX Remove this once compactLEFold is implemented
74-- XXX Remove this once compactLEFold is implemented
127-- lpackArraysChunksOf = Fold.many compactLEFold
75-- lpackArraysChunksOf = Fold.many compactLEFold
128--
76--
129−{-# INLINE_NORMAL lpackArraysChunksOf #-}
77+{-# INLINE lpackArraysChunksOf #-}
130lpackArraysChunksOf :: (MonadIO m, Unbox a)
78lpackArraysChunksOf :: (MonadIO m, Unbox a)
131 => Int -> Fold m (MutArray a) () -> Fold m (MutArray a) ()
79 => Int -> Fold m (MutArray a) () -> Fold m (MutArray a) ()
132−lpackArraysChunksOf n (Fold step1 initial1 _ final1) =
80+lpackArraysChunksOf = MArray.lCompactGE
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 ()
192
81
193-- XXX Same as compactLE, to be removed once that is implemented.
82-- XXX Same as compactLE, to be removed once that is implemented.
194--
83--
@@ -208,48 +97,11 @@ compact = packArraysChunksOf
208-- actual array size may be less than the specified chunk size.
97-- actual array size may be less than the specified chunk size.
209--
98--
210-- /Internal/
99-- /Internal/
211−{-# INLINE_NORMAL compactLEParserD #-}
100+{-# INLINE compactLEParserD #-}
212compactLEParserD ::
101compactLEParserD ::
213 forall m a. (MonadIO m, Unbox a)
102 forall m a. (MonadIO m, Unbox a)
214 => Int -> ParserD.Parser (MutArray a) m (MutArray a)
103 => Int -> ParserD.Parser (MutArray a) m (MutArray a)
215−compactLEParserD n = ParserD.Parser step initial extract
104+compactLEParserD = MArray.pCompactLE
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"
253
105
254-- | Coalesce adjacent arrays in incoming stream to form bigger arrays of a
106-- | Coalesce adjacent arrays in incoming stream to form bigger arrays of a
255-- minimum specified size. Note that if all the arrays in the stream together
107-- minimum specified size. Note that if all the arrays in the stream together
@@ -258,47 +110,11 @@ compactLEParserD n = ParserD.Parser step initial extract
258-- the specified size we stop coalescing further.
110-- the specified size we stop coalescing further.
259--
111--
260-- /Internal/
112-- /Internal/
261−{-# INLINE_NORMAL compactGEFold #-}
113+{-# INLINE compactGEFold #-}
262compactGEFold ::
114compactGEFold ::
263 forall m a. (MonadIO m, Unbox a)
115 forall m a. (MonadIO m, Unbox a)
264 => Int -> FL.Fold m (MutArray a) (MutArray a)
116 => Int -> FL.Fold m (MutArray a) (MutArray a)
265−compactGEFold n = Fold step initial extract extract
117+compactGEFold = MArray.fCompactGE
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"
302
118
303-- | Coalesce adjacent arrays in incoming stream to form bigger arrays of a
119-- | Coalesce adjacent arrays in incoming stream to form bigger arrays of a
304-- maximum specified size in bytes.
120-- maximum specified size in bytes.
core/src/Streamly/Internal/Data/MutArray/Type.hs+1252−394
@@ -28,57 +28,80 @@
28
28
29module Streamly.Internal.Data.MutArray.Type
29module Streamly.Internal.Data.MutArray.Type
30 (
30 (
31− -- * Type
31+ -- ** Type
32 -- $arrayNotes
32 -- $arrayNotes
33 MutArray (..)
33 MutArray (..)
34− , MutByteArray
34+
35− , MutableByteArray
35+ -- ** Conversion
36+ -- *** Pinned and Unpinned
36 , pin
37 , pin
37 , unpin
38 , unpin
38 , isPinned
39 , isPinned
39
40
40− -- * Constructing and Writing
41+ -- ** Casting
42+ , cast
43+ , castUnsafe -- XXX unsafeCast
44+ , asBytes
45+ , unsafePinnedAsPtr
46+ , unsafeAsPtr
47+
41 -- ** Construction
48 -- ** Construction
42− , nil
49+ , empty
50+
51+ -- *** New
52+ -- | New arrays are always empty arrays with some reserve capacity to
53+ -- extend the length without reallocating.
54+ , emptyOf
55+ , newArrayWith -- emptyAlignedWith
56+ , pinnedEmptyOf
57+ , pinnedNewAligned -- XXX not required
58+ -- , new -- uninitialized array of specified length
59+
60+ -- *** Cloning
61+ , clone
62+ , pinnedClone
43
63
44− -- *** Uninitialized Arrays
64+ -- *** Slicing
45− , pinnedNew
65+ -- | Get a subarray without copying
46− , pinnedNewBytes
66+ , getSliceUnsafe -- XXX unsafeGetSlice
47− , pinnedNewAligned
67+ , getSlice
48− , new
68+ , splitAt -- XXX should be able to express using getSlice
49− , newArrayWith
69+ , breakOn
50
70
51− -- *** From streams
71+ -- *** Stream Folds
52 , ArrayUnsafe (..)
72 , ArrayUnsafe (..)
53− , writeNWithUnsafe
73+ , unsafeCreateOfWith
54− , writeNWith
74+ , unsafeCreateOf
55− , writeNUnsafe
75+ , unsafePinnedCreateOf
56− , pinnedWriteNUnsafe
76+ , pinnedCreateOf
57− , writeN
77+ , createOfWith
58− , pinnedWriteN
78+ , createOf
59− , pinnedWriteNAligned
79+ , revCreateOf
60−
80+
61− , writeWith
81+ , pinnedCreate
62− , write
82+ , createWith
63− , pinnedWrite
83+ , create
64−
84+ -- , revCreate
65− , writeRevN
85+
66− -- , writeRev
67−
68− -- ** From containers
86+ -- *** From containers
69 , fromListN
87 , fromListN
70 , pinnedFromListN
88 , pinnedFromListN
71 , fromList
89 , fromList
72 , pinnedFromList
90 , pinnedFromList
73 , fromListRevN
91 , fromListRevN
74 , fromListRev
92 , fromListRev
75− , fromStreamDN
93+ , fromStreamN
76− , fromStreamD
94+ , fromStream
95+ , fromPureStreamN
77 , fromPureStream
96 , fromPureStream
97+ , fromByteStr#
98+ , fromPtrN
99+ , fromChunksK
100+ , fromChunksRealloced -- fromSmallChunks
78
101
79− -- * Random writes
102+ -- ** Random writes
80 , putIndex
103 , putIndex
81− , putIndexUnsafe
104+ , putIndexUnsafe -- XXX unsafePutIndex
82 , putIndices
105 , putIndices
83 -- , putFromThenTo
106 -- , putFromThenTo
84 -- , putFrom -- start writing at the given position
107 -- , putFrom -- start writing at the given position
@@ -86,78 +109,71 @@ module Streamly.Internal.Data.MutArray.Type
86 -- , putFromTo
109 -- , putFromTo
87 -- , putFromRev
110 -- , putFromRev
88 -- , putUptoRev
111 -- , putUptoRev
89− , modifyIndexUnsafe
112+ , modifyIndexUnsafe -- XXX unsafeModifyIndex
90 , modifyIndex
113 , modifyIndex
91 , modifyIndices
114 , modifyIndices
92 , modify
115 , modify
93 , swapIndices
116 , swapIndices
94 , unsafeSwapIndices
117 , unsafeSwapIndices
95
118
96− -- * Growing and Shrinking
119+ -- ** Reading
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.
100−
101− -- ** Appending elements
102− , snocWith
103− , snoc
104− , snocLinear
105− , snocMay
106− , snocUnsafe
107−
108− -- ** Appending streams
109− , writeAppendNUnsafe
110− , writeAppendN
111− , writeAppendWith
112− , writeAppend
113
120
114− -- * Eliminating and Reading
121+ -- *** Indexing
115−
122+ , getIndex
116− -- ** To streams
123+ , getIndexUnsafe -- XXX unsafeGetIndex
117− , reader
124+ -- , getFromThenTo
118− , readerRevWith
125+ , getIndexRev -- getRevIndex?
119− , readerRev
126+ , indexReader
127+ , indexReaderWith
120
128
121− -- ** To containers
129+ -- *** To Streams
122− , toStreamDWith
123− , toStreamDRevWith
124− , toStreamKWith
125− , toStreamKRevWith
126 , read
130 , read
127 , readRev
131 , readRev
132+ , toStreamWith
133+ , toStreamRevWith
128 , toStreamK
134 , toStreamK
135+ , toStreamKWith
129 , toStreamKRev
136 , toStreamKRev
137+ , toStreamKRevWith
138+
139+ -- *** To Containers
130 , toList
140 , toList
131
141
142+ -- *** Unfolds
132 -- experimental
143 -- experimental
133 , producerWith
144 , producerWith
134 , producer
145 , producer
135
146
136− -- ** Random reads
147+ , reader
137− , getIndex
148+ , readerRevWith
138− , getIndexUnsafe
149+ , readerRev
139− , getIndices
140− , getIndicesD
141− -- , getFromThenTo
142− , getIndexRev
143−
144− -- * Memory Management
145− , blockSize
146− , arrayChunkBytes
147− , allocBytesToElemCount
148− , realloc
149− , resize
150− , resizeExp
151− , rightSize
152
150
151+ -- ** Size and Capacity
153− -- * Size
152+ -- *** Size
154 , length
153 , length
155 , byteLength
154 , byteLength
155+
156+ -- *** Capacity
156 -- , capacity
157 -- , capacity
157 , byteCapacity
158 , byteCapacity
158 , bytesFree
159 , bytesFree
159
160
161+ -- *** Capacity Management
162+ , blockSize
163+ , arrayChunkBytes
164+ , allocBytesToElemCount
165+ , realloc -- this is "resize"
166+ , grow
167+ , growExp
168+ , rightSize
169+
170+ -- ** Folding
171+ , foldl'
172+ , foldr
173+ , byteCmp
174+ , byteEq
175+
160− -- * In-place Mutation Algorithms
176+ -- ** In-place Mutation Algorithms
161 , strip
177 , strip
162 , reverse
178 , reverse
163 , permute
179 , permute
@@ -167,62 +183,121 @@ module Streamly.Internal.Data.MutArray.Type
167 , mergeBy
183 , mergeBy
168 , bubble
184 , bubble
169
185
170− -- * Casting
186+ -- ** Growing and Shrinking
171− , cast
187+ -- | Arrays grow only at the end, though it is possible to grow on both
172− , castUnsafe
188+ -- sides and therefore have a cons as well as snoc. But that will require
173− , asBytes
189+ -- both lower and upper bound in the array representation.
174− , asPtrUnsafe
175−
176− -- * Folding
177− , foldl'
178− , foldr
179− , cmp
180−
181− -- * Arrays of arrays
182− -- We can add dimensionality parameter to the array type to get
183− -- multidimensional arrays. Multidimensional arrays would just be a
184− -- convenience wrapper on top of single dimensional arrays.
185
190
186− -- | Operations dealing with multiple arrays, streams of arrays or
191+ -- *** Appending elements
187− -- multidimensional array representations.
192+ , snocWith
188−
193+ , snoc
189− -- ** Construct from streams
194+ , snocLinear
190− , chunksOf
195+ , snocMay
191− , pinnedChunksOf
196+ , snocUnsafe -- XXX unsafeSnoc
192− , writeChunks
193−
194− -- ** Eliminate to streams
195− , flattenArrays
196− , flattenArraysRev
197− , fromArrayStreamK
198
197
199− -- ** Construct from arrays
198+ -- *** Appending streams
200− -- get chunks without copying
199+ , unsafeAppendN
201− , getSliceUnsafe
200+ , appendN
202− , getSlice
201+ , appendWith
203− -- , getSlicesFromLenN
202+ , append
204− , splitAt -- XXX should be able to express using getSlice
205− , breakOn
206
203
207− -- ** Cloning arrays
208− , clone
209− , pinnedClone
210−
211− -- ** Appending arrays
204+ -- *** Appending arrays
212 , spliceCopy
205 , spliceCopy
213 , spliceWith
206 , spliceWith
214 , splice
207 , splice
215 , spliceExp
208 , spliceExp
216− , spliceUnsafe
209+ , spliceUnsafe -- XXX unsafeSplice
217 -- , putSlice
210 -- , putSlice
218 -- , appendSlice
211 -- , appendSlice
219 -- , appendSliceFrom
212 -- , appendSliceFrom
220
213
214+ -- ** Serialization using Unbox
215+ , pokeAppend
216+ , pokeAppendMay
217+ , pokeSkipUnsafe -- unsafePokeSkip
218+
219+ -- ** Deserialization using Unbox
220+ , peekUncons
221+ , peekUnconsUnsafe -- unsafePeekUncons
222+ , peekSkipUnsafe -- unsafePeekSkip
223+
224+ -- Arrays of arrays
225+ -- We can add dimensionality parameter to the array type to get
226+ -- multidimensional arrays. Multidimensional arrays would just be a
227+ -- convenience wrapper on top of single dimensional arrays.
228+
229+ -- ** Streams of Arrays
230+ -- *** Chunk
231+ -- | Group a stream into arrays.
232+ , chunksOf
233+ , pinnedChunksOf
234+ , buildChunks
235+
236+ -- *** Split
237+ -- | Split an array into slices.
238+
239+ -- , getSlicesFromLenN
240+ , splitOn
241+ -- , slicesOf
242+
243+ -- *** Concat
244+ -- | Append the arrays in a stream to form a stream of elements.
245+ , concatWith
246+ , concatRevWith
247+ , concat
248+ , concatRev
249+
250+ -- *** Compact
251+ -- | Append the arrays in a stream to form a stream of larger arrays.
252+ , SpliceState (..)
253+ , pCompactLE
254+ , pPinnedCompactLE
255+ , compactLeAs
256+ , fCompactGE
257+ , fPinnedCompactGE
258+ , lCompactGE
259+ , lPinnedCompactGE
260+ , compactGE
261+ , compactEQ
262+
221− -- * Utilities
263+ -- ** Utilities
222 , roundUpToPower2
264 , roundUpToPower2
223 , memcpy
265 , memcpy
224 , memcmp
266 , memcmp
225 , c_memchr
267 , 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
226 )
301 )
227where
302where
228
303
@@ -233,15 +308,15 @@ where
233
308
234import Control.Monad (when, void)
309import Control.Monad (when, void)
235import Control.Monad.IO.Class (MonadIO(..))
310import Control.Monad.IO.Class (MonadIO(..))
311+import Data.Bifunctor (first)
236import Data.Bits (shiftR, (.|.), (.&.))
312import Data.Bits (shiftR, (.|.), (.&.))
237import Data.Functor.Identity (Identity(..))
313import Data.Functor.Identity (Identity(..))
238import Data.Proxy (Proxy(..))
314import Data.Proxy (Proxy(..))
239import Data.Word (Word8)
315import Data.Word (Word8)
240import Foreign.C.Types (CSize(..), CInt(..))
316import Foreign.C.Types (CSize(..), CInt(..))
241import Foreign.Ptr (plusPtr, minusPtr, nullPtr)
317import Foreign.Ptr (plusPtr, minusPtr, nullPtr)
242−import Streamly.Internal.Data.MutByteArray
318+import Streamly.Internal.Data.MutByteArray.Type
243 ( MutByteArray(..)
319 ( MutByteArray(..)
244− , MutableByteArray
245 , PinnedState(..)
320 , PinnedState(..)
246 , getMutableByteArray#
321 , getMutableByteArray#
247 , putSliceUnsafe
322 , putSliceUnsafe
@@ -254,19 +329,23 @@ import GHC.Base
254 , copyMutableByteArray#
329 , copyMutableByteArray#
255 )
330 )
256import GHC.Base (noinline)
331import GHC.Base (noinline)
257−import GHC.Exts (unsafeCoerce#)
332+import GHC.Exts (unsafeCoerce#, Addr#)
258import GHC.Ptr (Ptr(..))
333import GHC.Ptr (Ptr(..))
259
334
260import Streamly.Internal.Data.Fold.Type (Fold(..))
335import Streamly.Internal.Data.Fold.Type (Fold(..))
261import Streamly.Internal.Data.Producer.Type (Producer (..))
336import Streamly.Internal.Data.Producer.Type (Producer (..))
262import Streamly.Internal.Data.Stream.Type (Stream)
337import Streamly.Internal.Data.Stream.Type (Stream)
338+import Streamly.Internal.Data.Parser.Type (Parser (..))
263import Streamly.Internal.Data.StreamK.Type (StreamK)
339import Streamly.Internal.Data.StreamK.Type (StreamK)
264import Streamly.Internal.Data.SVar.Type (adaptState, defState)
340import Streamly.Internal.Data.SVar.Type (adaptState, defState)
341+import Streamly.Internal.Data.Tuple.Strict (Tuple'(..))
265import Streamly.Internal.Data.Unfold.Type (Unfold(..))
342import Streamly.Internal.Data.Unfold.Type (Unfold(..))
266import Streamly.Internal.System.IO (arrayPayloadSize, defaultChunkSize)
343import Streamly.Internal.System.IO (arrayPayloadSize, defaultChunkSize)
267
344
268import qualified Streamly.Internal.Data.Fold.Type as FL
345import qualified Streamly.Internal.Data.Fold.Type as FL
269−import qualified Streamly.Internal.Data.MutByteArray as Unboxed
346+import 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
270import qualified Streamly.Internal.Data.Producer as Producer
349import qualified Streamly.Internal.Data.Producer as Producer
271import qualified Streamly.Internal.Data.Stream.Type as D
350import qualified Streamly.Internal.Data.Stream.Type as D
272import qualified Streamly.Internal.Data.Stream.Lift as D
351import qualified Streamly.Internal.Data.Stream.Lift as D
@@ -274,7 +353,7 @@ import qualified Streamly.Internal.Data.StreamK.Type as K
274import qualified Prelude
353import qualified Prelude
275
354
276import Prelude hiding
355import Prelude hiding
277− (Foldable(..), read, unlines, splitAt, reverse, truncate)
356+ (Foldable(..), concat, read, unlines, splitAt, reverse, truncate)
278
357
279#include "DocTestDataMutArray.hs"
358#include "DocTestDataMutArray.hs"
280
359
@@ -282,6 +361,7 @@ import Prelude hiding
282-- Foreign helpers
361-- Foreign helpers
283-------------------------------------------------------------------------------
362-------------------------------------------------------------------------------
284
363
364+-- NOTE: Have to be "ccall unsafe" so that we can pass unpinned memory to these
285foreign import ccall unsafe "string.h memcpy" c_memcpy
365foreign import ccall unsafe "string.h memcpy" c_memcpy
286 :: Ptr Word8 -> Ptr Word8 -> CSize -> IO (Ptr Word8)
366 :: Ptr Word8 -> Ptr Word8 -> CSize -> IO (Ptr Word8)
287
367
@@ -291,6 +371,9 @@ foreign import ccall unsafe "string.h memchr" c_memchr
291foreign import ccall unsafe "string.h memcmp" c_memcmp
371foreign import ccall unsafe "string.h memcmp" c_memcmp
292 :: Ptr Word8 -> Ptr Word8 -> CSize -> IO CInt
372 :: Ptr Word8 -> Ptr Word8 -> CSize -> IO CInt
293
373
374+foreign import ccall unsafe "string.h strlen" c_strlen
375+ :: Ptr Word8 -> IO CSize
376+
294-- | Given an 'Unboxed' type (unused first arg) and a number of bytes, return
377-- | Given an 'Unboxed' type (unused first arg) and a number of bytes, return
295-- how many elements of that type will completely fit in those bytes.
378-- how many elements of that type will completely fit in those bytes.
296--
379--
@@ -357,8 +440,8 @@ data MutArray a =
357-- Pinning & Unpinning
440-- Pinning & Unpinning
358-------------------------------------------------------------------------------
441-------------------------------------------------------------------------------
359
442
360−-- | Copy the 'MutArray' to pinned memory if unpinned, else do nothing. The
443+-- | Return a copy of the array in pinned memory if unpinned, else return the
361−-- overhead is a copy if the input array is unpinned.
444+-- original array.
362{-# INLINE pin #-}
445{-# INLINE pin #-}
363pin :: MutArray a -> IO (MutArray a)
446pin :: MutArray a -> IO (MutArray a)
364pin arr@MutArray{..} =
447pin arr@MutArray{..} =
@@ -366,8 +449,8 @@ pin arr@MutArray{..} =
366 then pure arr
449 then pure arr
367 else pinnedClone arr
450 else pinnedClone arr
368
451
369−-- | Copy the 'MutArray' to unpinned memory if pinned, else do nothing. The
452+-- | Return a copy of the array in unpinned memory if pinned, else return the
370−-- overhead is a copy if the input array is pinned.
453+-- original array.
371{-# INLINE unpin #-}
454{-# INLINE unpin #-}
372unpin :: MutArray a -> IO (MutArray a)
455unpin :: MutArray a -> IO (MutArray a)
373unpin arr@MutArray{..} =
456unpin arr@MutArray{..} =
@@ -375,6 +458,7 @@ unpin arr@MutArray{..} =
375 then clone arr
458 then clone arr
376 else pure arr
459 else pure arr
377
460
461+-- | Return 'True' if the array is allocated in pinned memory.
378{-# INLINE isPinned #-}
462{-# INLINE isPinned #-}
379isPinned :: MutArray a -> Bool
463isPinned :: MutArray a -> Bool
380isPinned MutArray{..} = Unboxed.isPinned arrContents
464isPinned MutArray{..} = Unboxed.isPinned arrContents
@@ -395,6 +479,8 @@ isPinned MutArray{..} = Unboxed.isPinned arrContents
395-- we need more than that. See stg_pinnedNewAlignedByteArrayzh and
479-- we need more than that. See stg_pinnedNewAlignedByteArrayzh and
396-- allocatePinned in GHC source.
480-- allocatePinned in GHC source.
397
481
482+-- XXX Rename to emptyAlignedWith, alignSize should be first arg.
483+
398-- | @newArrayWith allocator alignment count@ allocates a new array of zero
484-- | @newArrayWith allocator alignment count@ allocates a new array of zero
399-- length and with a capacity to hold @count@ elements, using @allocator
485-- length and with a capacity to hold @count@ elements, using @allocator
400-- size alignment@ as the memory allocator function.
486-- size alignment@ as the memory allocator function.
@@ -418,12 +504,25 @@ newArrayWith alloc alignSize count = do
418 , arrBound = size
504 , arrBound = size
419 }
505 }
420
506
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." #-}
421nil ::
520nil ::
422#ifdef DEVBUILD
521#ifdef DEVBUILD
423 Unbox a =>
522 Unbox a =>
424#endif
523#endif
425 MutArray a
524 MutArray a
426−nil = MutArray Unboxed.nil 0 0 0
525+nil = empty
427
526
428{-# INLINE newBytesAs #-}
527{-# INLINE newBytesAs #-}
429newBytesAs :: MonadIO m =>
528newBytesAs :: MonadIO m =>
@@ -440,12 +539,13 @@ newBytesAs ps bytes = do
440 , arrBound = bytes
539 , arrBound = bytes
441 }
540 }
442
541
443−-- | Allocates a pinned empty array that can hold 'count' items. The memory of
542+-- | Allocates a pinned empty array that with a reserved capacity of bytes.
444−-- the array is uninitialized and the allocation is aligned as per the
543+-- The memory of the array is uninitialized and the allocation is aligned as
445−-- 'Unboxed' instance of the type.
544+-- per the 'Unboxed' instance of the type.
446--
545--
447-- /Pre-release/
546-- /Pre-release/
448{-# INLINE pinnedNewBytes #-}
547{-# INLINE pinnedNewBytes #-}
548+{-# DEPRECATED pinnedNewBytes "Please use pinnedEmptyOf with appropriate calculation" #-}
449pinnedNewBytes :: MonadIO m =>
549pinnedNewBytes :: MonadIO m =>
450#ifdef DEVBUILD
550#ifdef DEVBUILD
451 Unbox a =>
551 Unbox a =>
@@ -470,21 +570,31 @@ newAs ps =
470 (error "new: alignment is not used in unpinned arrays.")
570 (error "new: alignment is not used in unpinned arrays.")
471
571
472-- XXX can unaligned allocation be more efficient when alignment is not needed?
572-- XXX can unaligned allocation be more efficient when alignment is not needed?
473−--
573+
474−-- | Allocates an empty pinned array that can hold 'count' items. The memory of
574+-- | Allocates a pinned array of zero length but growable to the specified
475−-- the array is uninitialized and the allocation is aligned as per the 'Unboxed'
575+-- capacity without reallocation.
476−-- instance of the type.
576+{-# INLINE pinnedEmptyOf #-}
477−--
577+pinnedEmptyOf :: forall m a. (MonadIO m, Unbox a) => Int -> m (MutArray a)
578+pinnedEmptyOf = newAs Pinned
579+
580+-- XXX Deprecate in major
581+-- {-# DEPRECATED pinnedNew "Please use pinnedEmptyOf instead." #-}
478{-# INLINE pinnedNew #-}
582{-# INLINE pinnedNew #-}
479pinnedNew :: forall m a. (MonadIO m, Unbox a) => Int -> m (MutArray a)
583pinnedNew :: forall m a. (MonadIO m, Unbox a) => Int -> m (MutArray a)
480−pinnedNew = newAs Pinned
584+pinnedNew = pinnedEmptyOf
481
585
482−-- | Allocates an empty unpinned array that can hold 'count' items. The memory
586+-- | Allocates an unpinned array of zero length but growable to the specified
483−-- of the array is uninitialized.
587+-- capacity without reallocation.
484--
588--
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." #-}
485{-# INLINE new #-}
595{-# INLINE new #-}
486new :: (MonadIO m, Unbox a) => Int -> m (MutArray a)
596new :: (MonadIO m, Unbox a) => Int -> m (MutArray a)
487−new = newAs Unpinned
597+new = emptyOf
488
598
489-------------------------------------------------------------------------------
599-------------------------------------------------------------------------------
490-- Random writes
600-- Random writes
@@ -500,7 +610,7 @@ putIndexUnsafe :: forall m a. (MonadIO m, Unbox a)
500putIndexUnsafe i MutArray{..} x = do
610putIndexUnsafe i MutArray{..} x = do
501 let index = INDEX_OF(arrStart, i, a)
611 let index = INDEX_OF(arrStart, i, a)
502 assert (i >= 0 && INDEX_VALID(index, arrEnd, a)) (return ())
612 assert (i >= 0 && INDEX_VALID(index, arrEnd, a)) (return ())
503− liftIO $ pokeByteIndex index arrContents x
613+ liftIO $ pokeAt index arrContents x
504
614
505invalidIndex :: String -> Int -> a
615invalidIndex :: String -> Int -> a
506invalidIndex label i =
616invalidIndex label i =
@@ -518,7 +628,7 @@ putIndex :: forall m a. (MonadIO m, Unbox a) => Int -> MutArray a -> a -> m ()
518putIndex i MutArray{..} x = do
628putIndex i MutArray{..} x = do
519 let index = INDEX_OF(arrStart,i,a)
629 let index = INDEX_OF(arrStart,i,a)
520 if i >= 0 && INDEX_VALID(index,arrEnd,a)
630 if i >= 0 && INDEX_VALID(index,arrEnd,a)
521− then liftIO $ pokeByteIndex index arrContents x
631+ then liftIO $ pokeAt index arrContents x
522 else invalidIndex "putIndex" i
632 else invalidIndex "putIndex" i
523
633
524-- | Write an input stream of (index, value) pairs to an array. Throws an
634-- | Write an input stream of (index, value) pairs to an array. Throws an
@@ -532,7 +642,7 @@ putIndices arr = FL.foldlM' step (return ())
532
642
533 where
643 where
534
644
535− step () (i, x) = liftIO (putIndex i arr x)
645+ step () (i, x) = putIndex i arr x
536
646
537-- | Modify a given index of an array using a modifier function.
647-- | Modify a given index of an array using a modifier function.
538--
648--
@@ -544,9 +654,9 @@ modifyIndexUnsafe :: forall m a b. (MonadIO m, Unbox a) =>
544modifyIndexUnsafe i MutArray{..} f = liftIO $ do
654modifyIndexUnsafe i MutArray{..} f = liftIO $ do
545 let index = INDEX_OF(arrStart,i,a)
655 let index = INDEX_OF(arrStart,i,a)
546 assert (i >= 0 && INDEX_NEXT(index,a) <= arrEnd) (return ())
656 assert (i >= 0 && INDEX_NEXT(index,a) <= arrEnd) (return ())
547− r <- peekByteIndex index arrContents
657+ r <- peekAt index arrContents
548 let (x, res) = f r
658 let (x, res) = f r
549− pokeByteIndex index arrContents x
659+ pokeAt index arrContents x
550 return res
660 return res
551
661
552-- | Modify a given index of an array using a modifier function.
662-- | Modify a given index of an array using a modifier function.
@@ -558,9 +668,9 @@ modifyIndex i MutArray{..} f = do
558 let index = INDEX_OF(arrStart,i,a)
668 let index = INDEX_OF(arrStart,i,a)
559 if i >= 0 && INDEX_VALID(index,arrEnd,a)
669 if i >= 0 && INDEX_VALID(index,arrEnd,a)
560 then liftIO $ do
670 then liftIO $ do
561− r <- peekByteIndex index arrContents
671+ r <- peekAt index arrContents
562 let (x, res) = f r
672 let (x, res) = f r
563− pokeByteIndex index arrContents x
673+ pokeAt index arrContents x
564 return res
674 return res
565 else invalidIndex "modifyIndex" i
675 else invalidIndex "modifyIndex" i
566
676
@@ -594,8 +704,8 @@ modify MutArray{..} f = liftIO $
594
704
595 go i =
705 go i =
596 when (INDEX_VALID(i,arrEnd,a)) $ do
706 when (INDEX_VALID(i,arrEnd,a)) $ do
597− r <- peekByteIndex i arrContents
707+ r <- peekAt i arrContents
598− pokeByteIndex i arrContents (f r)
708+ pokeAt i arrContents (f r)
599 go (INDEX_NEXT(i,a))
709 go (INDEX_NEXT(i,a))
600
710
601-- XXX We could specify the number of bytes to swap instead of Proxy. Need
711-- XXX We could specify the number of bytes to swap instead of Proxy. Need
@@ -609,10 +719,10 @@ swapArrayByteIndices ::
609 -> Int
719 -> Int
610 -> IO ()
720 -> IO ()
611swapArrayByteIndices _ arrContents i1 i2 = do
721swapArrayByteIndices _ arrContents i1 i2 = do
612− r1 <- peekByteIndex i1 arrContents
722+ r1 <- peekAt i1 arrContents
613− r2 <- peekByteIndex i2 arrContents
723+ r2 <- peekAt i2 arrContents
614− pokeByteIndex i1 arrContents (r2 :: a)
724+ pokeAt i1 arrContents (r2 :: a)
615− pokeByteIndex i2 arrContents (r1 :: a)
725+ pokeAt i2 arrContents (r1 :: a)
616
726
617-- | Swap the elements at two indices without validating the indices.
727-- | Swap the elements at two indices without validating the indices.
618--
728--
@@ -728,24 +838,21 @@ arrayChunkBytes = 1024
728roundDownTo :: Int -> Int -> Int
838roundDownTo :: Int -> Int -> Int
729roundDownTo elemSize size = size - (size `mod` elemSize)
839roundDownTo elemSize size = size - (size `mod` elemSize)
730
840
731−-- XXX See if resizing can be implemented by reading the old array as a stream
732−-- and then using writeN to the new array.
733−--
734-- NOTE: we are passing elemSize explicitly to avoid an Unboxed constraint.
841-- NOTE: we are passing elemSize explicitly to avoid an Unboxed constraint.
735-- Since this is not inlined Unboxed consrraint leads to dictionary passing
842-- Since this is not inlined Unboxed consrraint leads to dictionary passing
736-- which complicates some inspection tests.
843-- which complicates some inspection tests.
737--
844--
738−{-# NOINLINE reallocExplicit #-}
845+{-# NOINLINE reallocExplicitAs #-}
739−reallocExplicit :: Int -> Int -> MutArray a -> IO (MutArray a)
846+reallocExplicitAs :: PinnedState -> Int -> Int -> MutArray a -> IO (MutArray a)
740−reallocExplicit elemSize newCapacityInBytes MutArray{..} = do
847+reallocExplicitAs ps elemSize newCapacityInBytes MutArray{..} = do
741 assertM(arrEnd <= arrBound)
848 assertM(arrEnd <= arrBound)
742
849
743 -- Allocate new array
850 -- Allocate new array
744 let newCapMaxInBytes = roundUpLargeArray newCapacityInBytes
851 let newCapMaxInBytes = roundUpLargeArray newCapacityInBytes
745 contents <-
852 contents <-
746− if Unboxed.isPinned arrContents
853+ if ps == Pinned
747− then Unboxed.pinnedNewByteArray newCapMaxInBytes
854+ then Unboxed.pinnedNew newCapMaxInBytes
748− else Unboxed.newByteArray newCapMaxInBytes
855+ else Unboxed.new newCapMaxInBytes
749 let !(MutByteArray mbarrFrom#) = arrContents
856 let !(MutByteArray mbarrFrom#) = arrContents
750 !(MutByteArray mbarrTo#) = contents
857 !(MutByteArray mbarrTo#) = contents
751
858
@@ -768,6 +875,10 @@ reallocExplicit elemSize newCapacityInBytes MutArray{..} = do
768 , arrBound = newCapInBytes
875 , arrBound = newCapInBytes
769 }
876 }
770
877
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+
771-- | @realloc newCapacity array@ reallocates the array to the specified
882-- | @realloc newCapacity array@ reallocates the array to the specified
772-- capacity in bytes.
883-- capacity in bytes.
773--
884--
@@ -779,7 +890,12 @@ reallocExplicit elemSize newCapacityInBytes MutArray{..} = do
779-- If the original array is pinned, the newly allocated array is also pinned.
890-- If the original array is pinned, the newly allocated array is also pinned.
780{-# INLINABLE realloc #-}
891{-# INLINABLE realloc #-}
781realloc :: forall m a. (MonadIO m, Unbox a) => Int -> MutArray a -> m (MutArray a)
892realloc :: 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
782−realloc bytes arr = liftIO $ reallocExplicit (SIZE_OF(a)) bytes arr
898+ in liftIO $ reallocExplicitAs ps (SIZE_OF(a)) bytes arr
783
899
784-- | @reallocWith label capSizer minIncrBytes array@. The label is used
900-- | @reallocWith label capSizer minIncrBytes array@. The label is used
785-- in error messages and the capSizer is used to determine the capacity of the
901-- in error messages and the capSizer is used to determine the capacity of the
@@ -802,14 +918,14 @@ reallocWith label capSizer minIncrBytes arr = do
802 where
918 where
803
919
804 badSize newSize =
920 badSize newSize =
805− concat
921+ Prelude.concat
806 [ label
922 [ label
807 , ": new array size (in bytes) is less than required size "
923 , ": new array size (in bytes) is less than required size "
808 , show newSize
924 , show newSize
809 , ". Please check the sizing function passed."
925 , ". Please check the sizing function passed."
810 ]
926 ]
811
927
812−-- | @resize newCapacity array@ changes the total capacity of the array so that
928+-- | @grow newCapacity array@ changes the total capacity of the array so that
813-- it is enough to hold the specified number of elements. Nothing is done if
929-- it is enough to hold the specified number of elements. Nothing is done if
814-- the specified capacity is less than the length of the array.
930-- the specified capacity is less than the length of the array.
815--
931--
@@ -817,34 +933,46 @@ reallocWith label capSizer minIncrBytes arr = do
817-- the block size (4K).
933-- the block size (4K).
818--
934--
819-- /Pre-release/
935-- /Pre-release/
820−{-# INLINE resize #-}
936+{-# INLINE grow #-}
821−resize :: forall m a. (MonadIO m, Unbox a) =>
937+grow :: forall m a. (MonadIO m, Unbox a) =>
822 Int -> MutArray a -> m (MutArray a)
938 Int -> MutArray a -> m (MutArray a)
823−resize nElems arr@MutArray{..} = do
939+grow nElems arr@MutArray{..} = do
824 let req = SIZE_OF(a) * nElems
940 let req = SIZE_OF(a) * nElems
825− len = arrEnd - arrStart
941+ cap = arrBound - arrStart
826− if req < len
942+ if req < cap
827 then return arr
943 then return arr
828 else realloc req arr
944 else realloc req arr
829
945
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+
830−-- | Like 'resize' but if the byte capacity is more than 'largeObjectThreshold'
952+-- | Like 'grow' but if the requested byte capacity is more than
831−-- then it is rounded up to the closest power of 2.
953+-- 'largeObjectThreshold' then it is rounded up to the closest power of 2.
832--
954--
833-- /Pre-release/
955-- /Pre-release/
834−{-# INLINE resizeExp #-}
956+{-# INLINE growExp #-}
835−resizeExp :: forall m a. (MonadIO m, Unbox a) =>
957+growExp :: forall m a. (MonadIO m, Unbox a) =>
836 Int -> MutArray a -> m (MutArray a)
958 Int -> MutArray a -> m (MutArray a)
837−resizeExp nElems arr@MutArray{..} = do
959+growExp nElems arr@MutArray{..} = do
838 let req = roundUpLargeArray (SIZE_OF(a) * nElems)
960 let req = roundUpLargeArray (SIZE_OF(a) * nElems)
839 req1 =
961 req1 =
840 if req > largeObjectThreshold
962 if req > largeObjectThreshold
841 then roundUpToPower2 req
963 then roundUpToPower2 req
842 else req
964 else req
843− len = arrEnd - arrStart
965+ cap = arrBound - arrStart
844− if req1 < len
966+ if req1 < cap
845 then return arr
967 then return arr
846 else realloc req1 arr
968 else realloc req1 arr
847
969
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+
848-- | Resize the allocated memory to drop any reserved free space at the end of
976-- | Resize the allocated memory to drop any reserved free space at the end of
849-- the array and reallocate it to reduce wastage.
977-- the array and reallocate it to reduce wastage.
850--
978--
@@ -889,7 +1017,7 @@ rightSize arr@MutArray{..} = do
889snocNewEnd :: (MonadIO m, Unbox a) => Int -> MutArray a -> a -> m (MutArray a)
1017snocNewEnd :: (MonadIO m, Unbox a) => Int -> MutArray a -> a -> m (MutArray a)
890snocNewEnd newEnd arr@MutArray{..} x = liftIO $ do
1018snocNewEnd newEnd arr@MutArray{..} x = liftIO $ do
891 assert (newEnd <= arrBound) (return ())
1019 assert (newEnd <= arrBound) (return ())
892− pokeByteIndex arrEnd arrContents x
1020+ pokeAt arrEnd arrContents x
893 return $ arr {arrEnd = newEnd}
1021 return $ arr {arrEnd = newEnd}
894
1022
895-- | Really really unsafe, appends the element into the first array, may
1023-- | Really really unsafe, appends the element into the first array, may
@@ -909,7 +1037,7 @@ snocUnsafe arr@MutArray{..} = snocNewEnd (INDEX_NEXT(arrEnd,a)) arr
909{-# INLINE snocMay #-}
1037{-# INLINE snocMay #-}
910snocMay :: forall m a. (MonadIO m, Unbox a) =>
1038snocMay :: forall m a. (MonadIO m, Unbox a) =>
911 MutArray a -> a -> m (Maybe (MutArray a))
1039 MutArray a -> a -> m (Maybe (MutArray a))
912−snocMay arr@MutArray{..} x = liftIO $ do
1040+snocMay arr@MutArray{..} x = do
913 let newEnd = INDEX_NEXT(arrEnd,a)
1041 let newEnd = INDEX_NEXT(arrEnd,a)
914 if newEnd <= arrBound
1042 if newEnd <= arrBound
915 then Just <$> snocNewEnd newEnd arr x
1043 then Just <$> snocNewEnd newEnd arr x
@@ -923,7 +1051,7 @@ snocWithRealloc :: forall m a. (MonadIO m, Unbox a) =>
923 -> a
1051 -> a
924 -> m (MutArray a)
1052 -> m (MutArray a)
925snocWithRealloc sizer arr x = do
1053snocWithRealloc sizer arr x = do
926− arr1 <- liftIO $ reallocWith "snocWith" sizer (SIZE_OF(a)) arr
1054+ arr1 <- reallocWith "snocWith" sizer (SIZE_OF(a)) arr
927 snocUnsafe arr1 x
1055 snocUnsafe arr1 x
928
1056
929-- | @snocWith sizer arr elem@ mutates @arr@ to append @elem@. The length of
1057-- | @snocWith sizer arr elem@ mutates @arr@ to append @elem@. The length of
@@ -945,7 +1073,7 @@ snocWith :: forall m a. (MonadIO m, Unbox a) =>
945 -> MutArray a
1073 -> MutArray a
946 -> a
1074 -> a
947 -> m (MutArray a)
1075 -> m (MutArray a)
948−snocWith allocSize arr x = liftIO $ do
1076+snocWith allocSize arr x = do
949 let newEnd = INDEX_NEXT(arrEnd arr,a)
1077 let newEnd = INDEX_NEXT(arrEnd arr,a)
950 if newEnd <= arrBound arr
1078 if newEnd <= arrBound arr
951 then snocNewEnd newEnd arr x
1079 then snocNewEnd newEnd arr x
@@ -989,6 +1117,126 @@ snoc = snocWith f
989 then oldSize * 2
1117 then oldSize * 2
990 else roundUpToPower2 oldSize * 2
1118 else roundUpToPower2 oldSize * 2
991
1119
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+
992-------------------------------------------------------------------------------
1240-------------------------------------------------------------------------------
993-- Random reads
1241-- Random reads
994-------------------------------------------------------------------------------
1242-------------------------------------------------------------------------------
@@ -1003,7 +1251,7 @@ getIndexUnsafe :: forall m a. (MonadIO m, Unbox a) => Int -> MutArray a -> m a
1003getIndexUnsafe i MutArray{..} = do
1251getIndexUnsafe i MutArray{..} = do
1004 let index = INDEX_OF(arrStart,i,a)
1252 let index = INDEX_OF(arrStart,i,a)
1005 assert (i >= 0 && INDEX_VALID(index,arrEnd,a)) (return ())
1253 assert (i >= 0 && INDEX_VALID(index,arrEnd,a)) (return ())
1006− liftIO $ peekByteIndex index arrContents
1254+ liftIO $ peekAt index arrContents
1007
1255
1008-- | /O(1)/ Lookup the element at the given index. Index starts from 0.
1256-- | /O(1)/ Lookup the element at the given index. Index starts from 0.
1009--
1257--
@@ -1012,7 +1260,7 @@ getIndex :: forall m a. (MonadIO m, Unbox a) => Int -> MutArray a -> m (Maybe a)
1012getIndex i MutArray{..} = do
1260getIndex i MutArray{..} = do
1013 let index = INDEX_OF(arrStart,i,a)
1261 let index = INDEX_OF(arrStart,i,a)
1014 if i >= 0 && INDEX_VALID(index,arrEnd,a)
1262 if i >= 0 && INDEX_VALID(index,arrEnd,a)
1015− then liftIO $ Just <$> peekByteIndex index arrContents
1263+ then liftIO $ Just <$> peekAt index arrContents
1016 else return Nothing
1264 else return Nothing
1017
1265
1018-- | /O(1)/ Lookup the element at the given index from the end of the array.
1266-- | /O(1)/ Lookup the element at the given index from the end of the array.
@@ -1025,21 +1273,16 @@ getIndexRev :: forall m a. (MonadIO m, Unbox a) => Int -> MutArray a -> m a
1025getIndexRev i MutArray{..} = do
1273getIndexRev i MutArray{..} = do
1026 let index = RINDEX_OF(arrEnd,i,a)
1274 let index = RINDEX_OF(arrEnd,i,a)
1027 if i >= 0 && index >= arrStart
1275 if i >= 0 && index >= arrStart
1028− then liftIO $ peekByteIndex index arrContents
1276+ then liftIO $ peekAt index arrContents
1029 else invalidIndex "getIndexRev" i
1277 else invalidIndex "getIndexRev" i
1030
1278
1031data GetIndicesState contents start end st =
1279data GetIndicesState contents start end st =
1032 GetIndicesState contents start end st
1280 GetIndicesState contents start end st
1033
1281
1034−-- | Given an unfold that generates array indices, read the elements on those
1282+{-# INLINE indexReaderWith #-}
1035−-- indices from the supplied MutArray. An error is thrown if an index is out of
1036−-- bounds.
1037−--
1038−-- /Pre-release/
1039−{-# INLINE getIndicesD #-}
1040−getIndicesD :: (Monad m, Unbox a) =>
1283+indexReaderWith :: (Monad m, Unbox a) =>
1041 (forall b. IO b -> m b) -> D.Stream m Int -> Unfold m (MutArray a) a
1284 (forall b. IO b -> m b) -> D.Stream m Int -> Unfold m (MutArray a) a
1042−getIndicesD liftio (D.Stream stepi sti) = Unfold step inject
1285+indexReaderWith liftio (D.Stream stepi sti) = Unfold step inject
1043
1286
1044 where
1287 where
1045
1288
@@ -1058,9 +1301,26 @@ getIndicesD liftio (D.Stream stepi sti) = Unfold step inject
1058 D.Skip s -> return $ D.Skip (GetIndicesState contents start end s)
1301 D.Skip s -> return $ D.Skip (GetIndicesState contents start end s)
1059 D.Stop -> return D.Stop
1302 D.Stop -> return D.Stop
1060
1303
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." #-}
1061{-# INLINE getIndices #-}
1321{-# INLINE getIndices #-}
1062getIndices :: (MonadIO m, Unbox a) => Stream m Int -> Unfold m (MutArray a) a
1322getIndices :: (MonadIO m, Unbox a) => Stream m Int -> Unfold m (MutArray a) a
1063−getIndices = getIndicesD liftIO
1323+getIndices = indexReader
1064
1324
1065-------------------------------------------------------------------------------
1325-------------------------------------------------------------------------------
1066-- Subarrays
1326-- Subarrays
@@ -1165,7 +1425,7 @@ partitionBy f arr@MutArray{..} = liftIO $ do
1165
1425
1166 -- Invariant low < high on entry, and on return as well
1426 -- Invariant low < high on entry, and on return as well
1167 moveHigh low high = do
1427 moveHigh low high = do
1168− h <- peekByteIndex high arrContents
1428+ h <- peekAt high arrContents
1169 if f h
1429 if f h
1170 then
1430 then
1171 -- Correctly classified, continue the loop
1431 -- Correctly classified, continue the loop
@@ -1185,7 +1445,7 @@ partitionBy f arr@MutArray{..} = liftIO $ do
1185 -- low <= high
1445 -- low <= high
1186 -- Both low and high are valid locations within the array
1446 -- Both low and high are valid locations within the array
1187 go low high = do
1447 go low high = do
1188− l <- peekByteIndex low arrContents
1448+ l <- peekAt low arrContents
1189 if f l
1449 if f l
1190 then
1450 then
1191 -- low is wrongly classified
1451 -- low is wrongly classified
@@ -1196,8 +1456,8 @@ partitionBy f arr@MutArray{..} = liftIO $ do
1196 case r of
1456 case r of
1197 Nothing -> return low
1457 Nothing -> return low
1198 Just (high1, h) -> do -- low < high1
1458 Just (high1, h) -> do -- low < high1
1199− pokeByteIndex low arrContents h
1459+ pokeAt low arrContents h
1200− pokeByteIndex high1 arrContents l
1460+ pokeAt high1 arrContents l
1201 let low1 = INDEX_NEXT(low,a)
1461 let low1 = INDEX_NEXT(low,a)
1202 high2 = INDEX_PREV(high1,a)
1462 high2 = INDEX_PREV(high1,a)
1203 if low1 <= high2
1463 if low1 <= high2
@@ -1331,7 +1591,7 @@ chunksOfAs ps n (D.Stream step state) =
1331 r <- step (adaptState gst) st
1591 r <- step (adaptState gst) st
1332 case r of
1592 case r of
1333 D.Yield x s -> do
1593 D.Yield x s -> do
1334− liftIO $ pokeByteIndex end contents x
1594+ liftIO $ pokeAt end contents x
1335 let end1 = INDEX_NEXT(end,a)
1595 let end1 = INDEX_NEXT(end,a)
1336 return $
1596 return $
1337 if end1 >= bound
1597 if end1 >= bound
@@ -1355,7 +1615,7 @@ chunksOfAs ps n (D.Stream step state) =
1355--
1615--
1356-- Same as the following but may be more efficient:
1616-- Same as the following but may be more efficient:
1357--
1617--
1358−-- >>> chunksOf n = Stream.foldMany (MutArray.writeN n)
1618+-- >>> chunksOf n = Stream.foldMany (MutArray.createOf n)
1359--
1619--
1360-- /Pre-release/
1620-- /Pre-release/
1361{-# INLINE_NORMAL chunksOf #-}
1621{-# INLINE_NORMAL chunksOf #-}
@@ -1364,17 +1624,29 @@ chunksOf :: forall m a. (MonadIO m, Unbox a)
1364-- XXX the idiomatic implementation leads to large regression in the D.reverse'
1624-- XXX the idiomatic implementation leads to large regression in the D.reverse'
1365-- benchmark. It seems it has difficulty producing optimized code when
1625-- benchmark. It seems it has difficulty producing optimized code when
1366-- converting to StreamK. Investigate GHC optimizations.
1626-- converting to StreamK. Investigate GHC optimizations.
1367−-- chunksOf n = D.foldMany (writeN n)
1627+-- chunksOf n = D.foldMany (createOf n)
1368chunksOf = chunksOfAs Unpinned
1628chunksOf = chunksOfAs Unpinned
1369
1629
1370-- | Like 'chunksOf' but creates pinned arrays.
1630-- | Like 'chunksOf' but creates pinned arrays.
1371{-# INLINE_NORMAL pinnedChunksOf #-}
1631{-# INLINE_NORMAL pinnedChunksOf #-}
1372pinnedChunksOf :: forall m a. (MonadIO m, Unbox a)
1632pinnedChunksOf :: forall m a. (MonadIO m, Unbox a)
1373 => Int -> D.Stream m a -> D.Stream m (MutArray a)
1633 => Int -> D.Stream m a -> D.Stream m (MutArray a)
1374−-- pinnedChunksOf n = D.foldMany (pinnedWriteN n)
1634+-- pinnedChunksOf n = D.foldMany (pinnedCreateOf n)
1375pinnedChunksOf = chunksOfAs Pinned
1635pinnedChunksOf = chunksOfAs Pinned
1376
1636
1377−-- XXX This should take a PinnedState
1637+-- | When we are buffering a stream of unknown size into an array we do not
1638+-- know how much space to pre-allocate. So we start with the min size and emit
1639+-- the array then keep on doubling the size every time. Thus we do not need to
1640+-- guess the optimum chunk size.
1641+--
1642+-- We can incorporate this in chunksOfAs if the additional size parameter does
1643+-- not impact perf.
1644+--
1645+{-# INLINE _chunksOfRange #-}
1646+_chunksOfRange :: -- (MonadIO m, Unbox a) =>
1647+ PinnedState -> Int -> Int -> D.Stream m a -> D.Stream m (MutArray a)
1648+_chunksOfRange _ps _low _hi = undefined
1649+
1378-- XXX buffer to a list instead?
1650-- XXX buffer to a list instead?
1379-- | Buffer the stream into arrays in memory.
1651-- | Buffer the stream into arrays in memory.
1380{-# INLINE arrayStreamKFromStreamDAs #-}
1652{-# INLINE arrayStreamKFromStreamDAs #-}
@@ -1392,16 +1664,10 @@ data FlattenState s contents a =
1392 OuterLoop s
1664 OuterLoop s
1393 | InnerLoop s contents !Int !Int
1665 | InnerLoop s contents !Int !Int
1394
1666
1395−-- | Use the "reader" unfold instead.
1396−--
1397−-- @flattenArrays = unfoldMany reader@
1398−--
1399−-- We can try this if there are any fusion issues in the unfold.
1400−--
1401−{-# INLINE_NORMAL flattenArrays #-}
1667+{-# INLINE_NORMAL concatWith #-}
1402−flattenArrays :: forall m a. (MonadIO m, Unbox a)
1668+concatWith :: forall m a. (Monad m, Unbox a)
1403− => D.Stream m (MutArray a) -> D.Stream m a
1669+ => (forall b. IO b -> m b) -> D.Stream m (MutArray a) -> D.Stream m a
1404−flattenArrays (D.Stream step state) = D.Stream step' (OuterLoop state)
1670+concatWith liftio (D.Stream step state) = D.Stream step' (OuterLoop state)
1405
1671
1406 where
1672 where
1407
1673
@@ -1418,19 +1684,30 @@ flattenArrays (D.Stream step state) = D.Stream step' (OuterLoop state)
1418 return $ D.Skip $ OuterLoop st
1684 return $ D.Skip $ OuterLoop st
1419
1685
1420 step' _ (InnerLoop st contents p end) = do
1686 step' _ (InnerLoop st contents p end) = do
1421− x <- liftIO $ peekByteIndex p contents
1687+ x <- liftio $ peekAt p contents
1422 return $ D.Yield x (InnerLoop st contents (INDEX_NEXT(p,a)) end)
1688 return $ D.Yield x (InnerLoop st contents (INDEX_NEXT(p,a)) end)
1423
1689
1424−-- | Use the "readerRev" unfold instead.
1690+-- | Use the "reader" unfold instead.
1425--
1691--
1426−-- @flattenArrays = unfoldMany readerRev@
1692+-- @concat = unfoldMany reader@
1427--
1693--
1428-- We can try this if there are any fusion issues in the unfold.
1694-- We can try this if there are any fusion issues in the unfold.
1429--
1695--
1430−{-# INLINE_NORMAL flattenArraysRev #-}
1696+{-# INLINE_NORMAL concat #-}
1431−flattenArraysRev :: forall m a. (MonadIO m, Unbox a)
1697+concat :: forall m a. (MonadIO m, Unbox a)
1432 => D.Stream m (MutArray a) -> D.Stream m 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)
1704+ => 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
1433−flattenArraysRev (D.Stream step state) = D.Stream step' (OuterLoop state)
1710+concatRevWith liftio (D.Stream step state) = D.Stream step' (OuterLoop state)
1434
1711
1435 where
1712 where
1436
1713
@@ -1448,10 +1725,27 @@ flattenArraysRev (D.Stream step state) = D.Stream step' (OuterLoop state)
1448 return $ D.Skip $ OuterLoop st
1725 return $ D.Skip $ OuterLoop st
1449
1726
1450 step' _ (InnerLoop st contents p start) = do
1727 step' _ (InnerLoop st contents p start) = do
1451− x <- liftIO $ peekByteIndex p contents
1728+ x <- liftio $ peekAt p contents
1452 let cur = INDEX_PREV(p,a)
1729 let cur = INDEX_PREV(p,a)
1453 return $ D.Yield x (InnerLoop st contents cur start)
1730 return $ D.Yield x (InnerLoop st contents cur start)
1454
1731
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+
1455-------------------------------------------------------------------------------
1749-------------------------------------------------------------------------------
1456-- Unfolds
1750-- Unfolds
1457-------------------------------------------------------------------------------
1751-------------------------------------------------------------------------------
@@ -1487,7 +1781,7 @@ producerWith liftio = Producer step (return . toArrayUnsafe) extract
1487 -- few actions for correctness and execution order sanity. We want
1781 -- few actions for correctness and execution order sanity. We want
1488 -- the peek to occur right here and not lazily at some later point
1782 -- the peek to occur right here and not lazily at some later point
1489 -- because we want the peek to be ordered with respect to the touch.
1783 -- because we want the peek to be ordered with respect to the touch.
1490− !x <- liftio $ peekByteIndex cur contents
1784+ !x <- liftio $ peekAt cur contents
1491 return $ D.Yield x (ArrayUnsafe contents (INDEX_NEXT(cur,a)) end)
1785 return $ D.Yield x (ArrayUnsafe contents (INDEX_NEXT(cur,a)) end)
1492
1786
1493 extract = return . fromArrayUnsafe
1787 extract = return . fromArrayUnsafe
@@ -1518,7 +1812,7 @@ readerRevWith liftio = Unfold step inject
1518 {-# INLINE_LATE step #-}
1812 {-# INLINE_LATE step #-}
1519 step (ArrayUnsafe _ start p) | p < start = return D.Stop
1813 step (ArrayUnsafe _ start p) | p < start = return D.Stop
1520 step (ArrayUnsafe contents start p) = do
1814 step (ArrayUnsafe contents start p) = do
1521− !x <- liftio $ peekByteIndex p contents
1815+ !x <- liftio $ peekAt p contents
1522 return $ D.Yield x (ArrayUnsafe contents start (INDEX_PREV(p,a)))
1816 return $ D.Yield x (ArrayUnsafe contents start (INDEX_PREV(p,a)))
1523
1817
1524-- | Unfold an array into a stream in reverse order.
1818-- | Unfold an array into a stream in reverse order.
@@ -1549,7 +1843,7 @@ toListFB c n MutArray{..} = go arrStart
1549 -- evaluated/written to before we peek at them.
1843 -- evaluated/written to before we peek at them.
1550 -- XXX
1844 -- XXX
1551 let !x = unsafeInlineIO $ do
1845 let !x = unsafeInlineIO $ do
1552− r <- peekByteIndex arrContents p
1846+ r <- peekAt arrContents p
1553 return r
1847 return r
1554 in c x (go (PTR_NEXT(p,a)))
1848 in c x (go (PTR_NEXT(p,a)))
1555-}
1849-}
@@ -1566,21 +1860,21 @@ toList MutArray{..} = liftIO $ go arrStart
1566
1860
1567 go p | assert (p <= arrEnd) (p == arrEnd) = return []
1861 go p | assert (p <= arrEnd) (p == arrEnd) = return []
1568 go p = do
1862 go p = do
1569− x <- peekByteIndex p arrContents
1863+ x <- peekAt p arrContents
1570 (:) x <$> go (INDEX_NEXT(p,a))
1864 (:) x <$> go (INDEX_NEXT(p,a))
1571
1865
1572−{-# INLINE_NORMAL toStreamDWith #-}
1866+{-# INLINE_NORMAL toStreamWith #-}
1573−toStreamDWith ::
1867+toStreamWith ::
1574 forall m a. (Monad m, Unbox a)
1868 forall m a. (Monad m, Unbox a)
1575 => (forall b. IO b -> m b) -> MutArray a -> D.Stream m a
1869 => (forall b. IO b -> m b) -> MutArray a -> D.Stream m a
1576−toStreamDWith liftio MutArray{..} = D.Stream step arrStart
1870+toStreamWith liftio MutArray{..} = D.Stream step arrStart
1577
1871
1578 where
1872 where
1579
1873
1580 {-# INLINE_LATE step #-}
1874 {-# INLINE_LATE step #-}
1581 step _ p | assert (p <= arrEnd) (p == arrEnd) = return D.Stop
1875 step _ p | assert (p <= arrEnd) (p == arrEnd) = return D.Stop
1582 step _ p = liftio $ do
1876 step _ p = liftio $ do
1583− r <- peekByteIndex p arrContents
1877+ r <- peekAt p arrContents
1584 return $ D.Yield r (INDEX_NEXT(p,a))
1878 return $ D.Yield r (INDEX_NEXT(p,a))
1585
1879
1586-- | Convert a 'MutArray' into a stream.
1880-- | Convert a 'MutArray' into a stream.
@@ -1589,7 +1883,7 @@ toStreamDWith liftio MutArray{..} = D.Stream step arrStart
1589--
1883--
1590{-# INLINE_NORMAL read #-}
1884{-# INLINE_NORMAL read #-}
1591read :: forall m a. (MonadIO m, Unbox a) => MutArray a -> D.Stream m a
1885read :: forall m a. (MonadIO m, Unbox a) => MutArray a -> D.Stream m a
1592−read = toStreamDWith liftIO
1886+read = toStreamWith liftIO
1593
1887
1594{-# INLINE toStreamKWith #-}
1888{-# INLINE toStreamKWith #-}
1595toStreamKWith ::
1889toStreamKWith ::
@@ -1601,18 +1895,18 @@ toStreamKWith liftio MutArray{..} = go arrStart
1601
1895
1602 go p | assert (p <= arrEnd) (p == arrEnd) = K.nil
1896 go p | assert (p <= arrEnd) (p == arrEnd) = K.nil
1603 | otherwise =
1897 | otherwise =
1604− let elemM = peekByteIndex p arrContents
1898+ let elemM = peekAt p arrContents
1605 in liftio elemM `K.consM` go (INDEX_NEXT(p,a))
1899 in liftio elemM `K.consM` go (INDEX_NEXT(p,a))
1606
1900
1607{-# INLINE toStreamK #-}
1901{-# INLINE toStreamK #-}
1608toStreamK :: forall m a. (MonadIO m, Unbox a) => MutArray a -> StreamK m a
1902toStreamK :: forall m a. (MonadIO m, Unbox a) => MutArray a -> StreamK m a
1609toStreamK = toStreamKWith liftIO
1903toStreamK = toStreamKWith liftIO
1610
1904
1611−{-# INLINE_NORMAL toStreamDRevWith #-}
1905+{-# INLINE_NORMAL toStreamRevWith #-}
1612−toStreamDRevWith ::
1906+toStreamRevWith ::
1613 forall m a. (Monad m, Unbox a)
1907 forall m a. (Monad m, Unbox a)
1614 => (forall b. IO b -> m b) -> MutArray a -> D.Stream m a
1908 => (forall b. IO b -> m b) -> MutArray a -> D.Stream m a
1615−toStreamDRevWith liftio MutArray{..} =
1909+toStreamRevWith liftio MutArray{..} =
1616 let p = INDEX_PREV(arrEnd,a)
1910 let p = INDEX_PREV(arrEnd,a)
1617 in D.Stream step p
1911 in D.Stream step p
1618
1912
@@ -1621,7 +1915,7 @@ toStreamDRevWith liftio MutArray{..} =
1621 {-# INLINE_LATE step #-}
1915 {-# INLINE_LATE step #-}
1622 step _ p | p < arrStart = return D.Stop
1916 step _ p | p < arrStart = return D.Stop
1623 step _ p = liftio $ do
1917 step _ p = liftio $ do
1624− r <- peekByteIndex p arrContents
1918+ r <- peekAt p arrContents
1625 return $ D.Yield r (INDEX_PREV(p,a))
1919 return $ D.Yield r (INDEX_PREV(p,a))
1626
1920
1627-- | Convert a 'MutArray' into a stream in reverse order.
1921-- | Convert a 'MutArray' into a stream in reverse order.
@@ -1630,7 +1924,7 @@ toStreamDRevWith liftio MutArray{..} =
1630--
1924--
1631{-# INLINE_NORMAL readRev #-}
1925{-# INLINE_NORMAL readRev #-}
1632readRev :: forall m a. (MonadIO m, Unbox a) => MutArray a -> D.Stream m a
1926readRev :: forall m a. (MonadIO m, Unbox a) => MutArray a -> D.Stream m a
1633−readRev = toStreamDRevWith liftIO
1927+readRev = toStreamRevWith liftIO
1634
1928
1635{-# INLINE toStreamKRevWith #-}
1929{-# INLINE toStreamKRevWith #-}
1636toStreamKRevWith ::
1930toStreamKRevWith ::
@@ -1644,7 +1938,7 @@ toStreamKRevWith liftio MutArray {..} =
1644
1938
1645 go p | p < arrStart = K.nil
1939 go p | p < arrStart = K.nil
1646 | otherwise =
1940 | otherwise =
1647− let elemM = peekByteIndex p arrContents
1941+ let elemM = peekAt p arrContents
1648 in liftio elemM `K.consM` go (INDEX_PREV(p,a))
1942 in liftio elemM `K.consM` go (INDEX_PREV(p,a))
1649
1943
1650{-# INLINE toStreamKRev #-}
1944{-# INLINE toStreamKRev #-}
@@ -1679,7 +1973,7 @@ foldr f z arr = D.foldr f z $ read arr
1679
1973
1680-- XXX Keep the bound intact to not lose any free space? Perf impact?
1974-- XXX Keep the bound intact to not lose any free space? Perf impact?
1681
1975
1682−-- | @writeAppendNUnsafe n arr@ appends up to @n@ input items to the supplied
1976+-- | @unsafeAppendN n arr@ appends up to @n@ input items to the supplied
1683-- array.
1977-- array.
1684--
1978--
1685-- Unsafe: Do not drive the fold beyond @n@ elements, it will lead to memory
1979-- Unsafe: Do not drive the fold beyond @n@ elements, it will lead to memory
@@ -1688,13 +1982,12 @@ foldr f z arr = D.foldr f z $ read arr
1688-- Any free space left in the array after appending @n@ elements is lost.
1982-- Any free space left in the array after appending @n@ elements is lost.
1689--
1983--
1690-- /Internal/
1984-- /Internal/
1691−{-# INLINE_NORMAL writeAppendNUnsafe #-}
1985+{-# INLINE_NORMAL unsafeAppendN #-}
1692−writeAppendNUnsafe :: forall m a. (MonadIO m, Unbox a) =>
1986+unsafeAppendN :: forall m a. (MonadIO m, Unbox a) =>
1693 Int
1987 Int
1694 -> m (MutArray a)
1988 -> m (MutArray a)
1695 -> Fold m a (MutArray a)
1989 -> Fold m a (MutArray a)
1696−writeAppendNUnsafe n action =
1697− fmap fromArrayUnsafe $ FL.foldlM' step initial
1990+unsafeAppendN n action = fmap fromArrayUnsafe $ FL.foldlM' step initial
1698
1991
1699 where
1992 where
1700
1993
@@ -1707,38 +2000,59 @@ writeAppendNUnsafe n action =
1707 -- otherwise we lose that space.
2000 -- otherwise we lose that space.
1708 arr1 <-
2001 arr1 <-
1709 if free < needed
2002 if free < needed
1710− then noinline reallocWith "writeAppendNUnsafeWith" (+ needed) needed arr
2003+ then noinline reallocWith "unsafeAppendN" (+ needed) needed arr
1711 else return arr
2004 else return arr
1712 return $ toArrayUnsafe arr1
2005 return $ toArrayUnsafe arr1
1713
2006
1714 step (ArrayUnsafe contents start end) x = do
2007 step (ArrayUnsafe contents start end) x = do
1715− liftIO $ pokeByteIndex end contents x
2008+ liftIO $ pokeAt end contents x
1716 return $ ArrayUnsafe contents start (INDEX_NEXT(end,a))
2009 return $ ArrayUnsafe contents start (INDEX_NEXT(end,a))
1717
2010
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+
1718-- | Append @n@ elements to an existing array. Any free space left in the array
2019-- | Append @n@ elements to an existing array. Any free space left in the array
1719-- after appending @n@ elements is lost.
2020-- after appending @n@ elements is lost.
1720--
2021--
1721−-- >>> writeAppendN n initial = Fold.take n (MutArray.writeAppendNUnsafe n initial)
2022+-- >>> appendN n initial = Fold.take n (MutArray.unsafeAppendN n initial)
1722--
2023--
1723−{-# INLINE_NORMAL writeAppendN #-}
2024+{-# INLINE_NORMAL appendN #-}
2025+appendN :: forall m a. (MonadIO m, Unbox a) =>
2026+ Int -> m (MutArray a) -> Fold m a (MutArray a)
2027+appendN n initial = FL.take n (unsafeAppendN n initial)
2028+
2029+-- XXX Deprecate in major
2030+-- {-# DEPRECATED writeAppendN "Please use appendN instead." #-}
2031+{-# INLINE writeAppendN #-}
1724writeAppendN :: forall m a. (MonadIO m, Unbox a) =>
2032writeAppendN :: forall m a. (MonadIO m, Unbox a) =>
1725 Int -> m (MutArray a) -> Fold m a (MutArray a)
2033 Int -> m (MutArray a) -> Fold m a (MutArray a)
1726−writeAppendN n initial = FL.take n (writeAppendNUnsafe n initial)
2034+writeAppendN = appendN
1727
2035
1728−-- | @writeAppendWith realloc action@ mutates the array generated by @action@ to
2036+-- | @appendWith realloc action@ mutates the array generated by @action@ to
1729-- append the input stream. If there is no reserved space available in the
2037-- append the input stream. If there is no reserved space available in the
1730-- array it is reallocated to a size in bytes determined by @realloc oldSize@,
2038-- array it is reallocated to a size in bytes determined by @realloc oldSize@,
1731-- where @oldSize@ is the current size of the array in bytes.
2039-- where @oldSize@ is the current size of the array in bytes.
1732--
2040--
1733-- Note that the returned array may be a mutated version of original array.
2041-- Note that the returned array may be a mutated version of original array.
1734--
2042--
1735−-- >>> writeAppendWith sizer = Fold.foldlM' (MutArray.snocWith sizer)
2043+-- >>> appendWith sizer = Fold.foldlM' (MutArray.snocWith sizer)
1736--
2044--
1737-- /Pre-release/
2045-- /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." #-}
1738{-# INLINE writeAppendWith #-}
2052{-# INLINE writeAppendWith #-}
1739writeAppendWith :: forall m a. (MonadIO m, Unbox a) =>
2053writeAppendWith :: forall m a. (MonadIO m, Unbox a) =>
1740 (Int -> Int) -> m (MutArray a) -> Fold m a (MutArray a)
2054 (Int -> Int) -> m (MutArray a) -> Fold m a (MutArray a)
1741−writeAppendWith sizer = FL.foldlM' (snocWith sizer)
2055+writeAppendWith = appendWith
1742
2056
1743-- | @append action@ mutates the array generated by @action@ to append the
2057-- | @append action@ mutates the array generated by @action@ to append the
1744-- input stream. If there is no reserved space available in the array it is
2058-- input stream. If there is no reserved space available in the array it is
@@ -1746,70 +2060,101 @@ writeAppendWith sizer = FL.foldlM' (snocWith sizer)
1746--
2060--
1747-- Note that the returned array may be a mutated version of original array.
2061-- Note that the returned array may be a mutated version of original array.
1748--
2062--
1749−-- >>> writeAppend = MutArray.writeAppendWith (* 2)
2063+-- >>> append = MutArray.appendWith (* 2)
1750--
2064--
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." #-}
1751{-# INLINE writeAppend #-}
2072{-# INLINE writeAppend #-}
1752writeAppend :: forall m a. (MonadIO m, Unbox a) =>
2073writeAppend :: forall m a. (MonadIO m, Unbox a) =>
1753 m (MutArray a) -> Fold m a (MutArray a)
2074 m (MutArray a) -> Fold m a (MutArray a)
1754−writeAppend = writeAppendWith (* 2)
2075+writeAppend = append
1755
2076
1756-- XXX We can carry bound as well in the state to make sure we do not lose the
2077-- XXX We can carry bound as well in the state to make sure we do not lose the
1757-- remaining capacity. Need to check perf impact.
2078-- remaining capacity. Need to check perf impact.
1758--
2079--
1759−-- | Like 'writeNUnsafe' but takes a new array allocator @alloc size@ function
2080+-- | Like 'unsafeCreateOf' but takes a new array allocator @alloc size@
1760−-- as argument.
2081+-- function as argument.
1761--
2082--
1762−-- >>> writeNWithUnsafe alloc n = MutArray.writeAppendNUnsafe (alloc n) n
2083+-- >>> unsafeCreateOfWith alloc n = MutArray.unsafeAppendN (alloc n) n
1763--
2084--
1764-- /Pre-release/
2085-- /Pre-release/
1765−{-# INLINE_NORMAL writeNWithUnsafe #-}
2086+{-# INLINE_NORMAL unsafeCreateOfWith #-}
1766−writeNWithUnsafe :: forall m a. (MonadIO m, Unbox a)
2087+unsafeCreateOfWith :: forall m a. (MonadIO m, Unbox a)
1767 => (Int -> m (MutArray a)) -> Int -> Fold m a (MutArray a)
2088 => (Int -> m (MutArray a)) -> Int -> Fold m a (MutArray a)
1768−writeNWithUnsafe alloc n = fromArrayUnsafe <$> FL.foldlM' step initial
2089+unsafeCreateOfWith alloc n = fromArrayUnsafe <$> FL.foldlM' step initial
1769
2090
1770 where
2091 where
1771
2092
1772 initial = toArrayUnsafe <$> alloc (max n 0)
2093 initial = toArrayUnsafe <$> alloc (max n 0)
1773
2094
1774 step (ArrayUnsafe contents start end) x = do
2095 step (ArrayUnsafe contents start end) x = do
1775− liftIO $ pokeByteIndex end contents x
2096+ liftIO $ pokeAt end contents x
1776 return
2097 return
1777 $ ArrayUnsafe contents start (INDEX_NEXT(end,a))
2098 $ ArrayUnsafe contents start (INDEX_NEXT(end,a))
1778
2099
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+
1779{-# INLINE_NORMAL writeNUnsafeAs #-}
2106{-# INLINE_NORMAL writeNUnsafeAs #-}
1780writeNUnsafeAs :: forall m a. (MonadIO m, Unbox a)
2107writeNUnsafeAs :: forall m a. (MonadIO m, Unbox a)
1781 => PinnedState -> Int -> Fold m a (MutArray a)
2108 => PinnedState -> Int -> Fold m a (MutArray a)
1782−writeNUnsafeAs ps = writeNWithUnsafe (newAs ps)
2109+writeNUnsafeAs ps = unsafeCreateOfWith (newAs ps)
1783
2110
1784−-- | Like 'writeN' but does not check the array bounds when writing. The fold
2111+-- | Like 'createOf' but does not check the array bounds when writing. The fold
1785-- driver must not call the step function more than 'n' times otherwise it will
2112-- driver must not call the step function more than 'n' times otherwise it will
1786-- corrupt the memory and crash. This function exists mainly because any
2113-- corrupt the memory and crash. This function exists mainly because any
1787-- conditional in the step function blocks fusion causing 10x performance
2114-- conditional in the step function blocks fusion causing 10x performance
1788-- slowdown.
2115-- slowdown.
1789--
2116--
1790−-- >>> writeNUnsafe = MutArray.writeNWithUnsafe MutArray.new
2117+-- >>> unsafeCreateOf = MutArray.unsafeCreateOfWith MutArray.emptyOf
1791--
2118--
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." #-}
1792−{-# INLINE_NORMAL writeNUnsafe #-}
2125+{-# INLINE writeNUnsafe #-}
1793writeNUnsafe :: forall m a. (MonadIO m, Unbox a)
2126writeNUnsafe :: forall m a. (MonadIO m, Unbox a)
1794 => Int -> Fold m a (MutArray a)
2127 => Int -> Fold m a (MutArray a)
1795−writeNUnsafe = writeNUnsafeAs Unpinned
2128+writeNUnsafe = unsafeCreateOf
1796
2129
1797−-- | Like 'writeNUnsafe' but creates a pinned array.
2130+-- | Like 'unsafeCreateOf' but creates a pinned array.
2131+{-# INLINE_NORMAL unsafePinnedCreateOf #-}
2132+unsafePinnedCreateOf :: forall m a. (MonadIO m, Unbox a)
2133+ => Int -> Fold m a (MutArray a)
2134+unsafePinnedCreateOf = writeNUnsafeAs Pinned
2135+
2136+{-# DEPRECATED pinnedWriteNUnsafe "Please use unsafePinnedCreateOf instead." #-}
1798−{-# INLINE_NORMAL pinnedWriteNUnsafe #-}
2137+{-# INLINE pinnedWriteNUnsafe #-}
1799pinnedWriteNUnsafe :: forall m a. (MonadIO m, Unbox a)
2138pinnedWriteNUnsafe :: forall m a. (MonadIO m, Unbox a)
1800 => Int -> Fold m a (MutArray a)
2139 => Int -> Fold m a (MutArray a)
1801−pinnedWriteNUnsafe = writeNUnsafeAs Pinned
2140+pinnedWriteNUnsafe = unsafePinnedCreateOf
1802
2141
1803−-- | @writeNWith alloc n@ folds a maximum of @n@ elements into an array
2142+-- | @createOfWith alloc n@ folds a maximum of @n@ elements into an array
1804-- allocated using the @alloc@ function.
2143-- allocated using the @alloc@ function.
1805--
2144--
1806−-- >>> writeNWith alloc n = Fold.take n (MutArray.writeNWithUnsafe alloc n)
2145+-- >>> createOfWith alloc n = Fold.take n (MutArray.unsafeCreateOfWith alloc n)
1807−-- >>> writeNWith alloc n = MutArray.writeAppendN (alloc n) n
2146+-- >>> createOfWith alloc n = MutArray.appendN (alloc n) n
1808--
2147--
1809−{-# INLINE_NORMAL writeNWith #-}
2148+{-# INLINE_NORMAL createOfWith #-}
2149+createOfWith :: forall m a. (MonadIO m, Unbox a)
2150+ => (Int -> m (MutArray a)) -> Int -> Fold m a (MutArray a)
2151+createOfWith alloc n = FL.take n (unsafeCreateOfWith alloc n)
2152+
2153+-- {-# DEPRECATED writeNWith "Please use createOfWith instead." #-}
2154+{-# INLINE writeNWith #-}
1810writeNWith :: forall m a. (MonadIO m, Unbox a)
2155writeNWith :: forall m a. (MonadIO m, Unbox a)
1811 => (Int -> m (MutArray a)) -> Int -> Fold m a (MutArray a)
2156 => (Int -> m (MutArray a)) -> Int -> Fold m a (MutArray a)
1812−writeNWith alloc n = FL.take n (writeNWithUnsafe alloc n)
2157+writeNWith = createOfWith
1813
2158
1814{-# INLINE_NORMAL writeNAs #-}
2159{-# INLINE_NORMAL writeNAs #-}
1815writeNAs ::
2160writeNAs ::
@@ -1817,28 +2162,42 @@ writeNAs ::
1817 => PinnedState
2162 => PinnedState
1818 -> Int
2163 -> Int
1819 -> Fold m a (MutArray a)
2164 -> Fold m a (MutArray a)
1820−writeNAs ps = writeNWith (newAs ps)
2165+writeNAs ps = createOfWith (newAs ps)
1821
2166
1822−-- | @writeN n@ folds a maximum of @n@ elements from the input stream to an
2167+-- | @createOf n@ folds a maximum of @n@ elements from the input stream to an
1823-- 'MutArray'.
2168-- 'MutArray'.
1824--
2169--
1825−-- >>> writeN = MutArray.writeNWith MutArray.new
2170+-- >>> createOf = MutArray.createOfWith MutArray.new
1826−-- >>> writeN n = Fold.take n (MutArray.writeNUnsafe n)
2171+-- >>> createOf n = Fold.take n (MutArray.unsafeCreateOf n)
1827−-- >>> writeN n = MutArray.writeAppendN n (MutArray.new n)
2172+-- >>> createOf n = MutArray.appendN n (MutArray.emptyOf n)
1828--
2173--
1829−{-# INLINE_NORMAL writeN #-}
2174+{-# INLINE_NORMAL createOf #-}
2175+createOf :: forall m a. (MonadIO m, Unbox a) => Int -> Fold m a (MutArray a)
2176+createOf = writeNAs Unpinned
2177+
2178+-- XXX Deprecate in major
2179+-- {-# DEPRECATED writeN "Please use createOf instead." #-}
2180+{-# INLINE writeN #-}
1830writeN :: forall m a. (MonadIO m, Unbox a) => Int -> Fold m a (MutArray a)
2181writeN :: forall m a. (MonadIO m, Unbox a) => Int -> Fold m a (MutArray a)
1831−writeN = writeNAs Unpinned
2182+writeN = createOf
2183+
2184+-- | Like 'createOf' but creates a pinned array.
2185+{-# INLINE_NORMAL pinnedCreateOf #-}
2186+pinnedCreateOf ::
2187+ forall m a. (MonadIO m, Unbox a)
2188+ => Int
2189+ -> Fold m a (MutArray a)
2190+pinnedCreateOf = writeNAs Pinned
1832
2191
1833−-- | Like 'writeN' but creates a pinned array.
2192+{-# DEPRECATED pinnedWriteN "Please use pinnedCreateOf instead." #-}
1834−{-# INLINE_NORMAL pinnedWriteN #-}
2193+{-# INLINE pinnedWriteN #-}
1835pinnedWriteN ::
2194pinnedWriteN ::
1836 forall m a. (MonadIO m, Unbox a)
2195 forall m a. (MonadIO m, Unbox a)
1837 => Int
2196 => Int
1838 -> Fold m a (MutArray a)
2197 -> Fold m a (MutArray a)
1839−pinnedWriteN = writeNAs Pinned
2198+pinnedWriteN = pinnedCreateOf
1840
2199
1841−-- | Like writeNWithUnsafe but writes the array in reverse order.
2200+-- | Like unsafeCreateOfWith but writes the array in reverse order.
1842--
2201--
1843-- /Internal/
2202-- /Internal/
1844{-# INLINE_NORMAL writeRevNWithUnsafe #-}
2203{-# INLINE_NORMAL writeRevNWithUnsafe #-}
@@ -1855,11 +2214,11 @@ writeRevNWithUnsafe alloc n = fromArrayUnsafe <$> FL.foldlM' step initial
1855
2214
1856 step (ArrayUnsafe contents start end) x = do
2215 step (ArrayUnsafe contents start end) x = do
1857 let ptr = INDEX_PREV(start,a)
2216 let ptr = INDEX_PREV(start,a)
1858− liftIO $ pokeByteIndex ptr contents x
2217+ liftIO $ pokeAt ptr contents x
1859 return
2218 return
1860 $ ArrayUnsafe contents ptr end
2219 $ ArrayUnsafe contents ptr end
1861
2220
1862−-- | Like writeNWith but writes the array in reverse order.
2221+-- | Like createOfWith but writes the array in reverse order.
1863--
2222--
1864-- /Internal/
2223-- /Internal/
1865{-# INLINE_NORMAL writeRevNWith #-}
2224{-# INLINE_NORMAL writeRevNWith #-}
@@ -1867,31 +2226,36 @@ writeRevNWith :: forall m a. (MonadIO m, Unbox a)
1867 => (Int -> m (MutArray a)) -> Int -> Fold m a (MutArray a)
2226 => (Int -> m (MutArray a)) -> Int -> Fold m a (MutArray a)
1868writeRevNWith alloc n = FL.take n (writeRevNWithUnsafe alloc n)
2227writeRevNWith alloc n = FL.take n (writeRevNWithUnsafe alloc n)
1869
2228
1870−-- | Like writeN but writes the array in reverse order.
2229+-- | Like 'createOf' but writes the array in reverse order.
1871--
2230--
1872-- /Pre-release/
2231-- /Pre-release/
1873−{-# INLINE_NORMAL writeRevN #-}
2232+{-# INLINE_NORMAL revCreateOf #-}
2233+revCreateOf :: forall m a. (MonadIO m, Unbox a) => Int -> Fold m a (MutArray a)
2234+revCreateOf = writeRevNWith new
2235+
2236+{-# DEPRECATED writeRevN "Please use revCreateOf instead." #-}
2237+{-# INLINE writeRevN #-}
1874writeRevN :: forall m a. (MonadIO m, Unbox a) => Int -> Fold m a (MutArray a)
2238writeRevN :: forall m a. (MonadIO m, Unbox a) => Int -> Fold m a (MutArray a)
1875−writeRevN = writeRevNWith new
2239+writeRevN = revCreateOf
1876
2240
1877-- | @pinnedWriteNAligned align n@ folds a maximum of @n@ elements from the
2241-- | @pinnedWriteNAligned align n@ folds a maximum of @n@ elements from the
1878-- input stream to a 'MutArray' aligned to the given size.
2242-- input stream to a 'MutArray' aligned to the given size.
1879--
2243--
1880−-- >>> pinnedWriteNAligned align = MutArray.writeNWith (MutArray.pinnedNewAligned align)
2244+-- >>> pinnedWriteNAligned align = MutArray.createOfWith (MutArray.pinnedNewAligned align)
1881−-- >>> pinnedWriteNAligned align n = MutArray.writeAppendN n (MutArray.pinnedNewAligned align n)
2245+-- >>> pinnedWriteNAligned align n = MutArray.appendN n (MutArray.pinnedNewAligned align n)
1882--
2246--
1883-- /Pre-release/
2247-- /Pre-release/
1884--
2248--
1885{-# INLINE_NORMAL pinnedWriteNAligned #-}
2249{-# INLINE_NORMAL pinnedWriteNAligned #-}
1886pinnedWriteNAligned :: forall m a. (MonadIO m, Unbox a)
2250pinnedWriteNAligned :: forall m a. (MonadIO m, Unbox a)
1887 => Int -> Int -> Fold m a (MutArray a)
2251 => Int -> Int -> Fold m a (MutArray a)
1888−pinnedWriteNAligned align = writeNWith (pinnedNewAligned align)
2252+pinnedWriteNAligned align = createOfWith (pinnedNewAligned align)
1889
2253
1890-- XXX Buffer to a list instead?
2254-- XXX Buffer to a list instead?
1891
2255
1892-- | Buffer a stream into a stream of arrays.
2256-- | Buffer a stream into a stream of arrays.
1893--
2257--
1894−-- >>> writeChunks n = Fold.many (MutArray.writeN n) Fold.toStreamK
2258+-- >>> buildChunks n = Fold.many (MutArray.createOf n) Fold.toStreamK
1895--
2259--
1896-- Breaking an array into an array stream can be useful to consume a large
2260-- Breaking an array into an array stream can be useful to consume a large
1897-- array sequentially such that memory of the array is released incrementatlly.
2261-- array sequentially such that memory of the array is released incrementatlly.
@@ -1900,37 +2264,43 @@ pinnedWriteNAligned align = writeNWith (pinnedNewAligned align)
1900--
2264--
1901-- /Unimplemented/
2265-- /Unimplemented/
1902--
2266--
1903−{-# INLINE_NORMAL writeChunks #-}
2267+{-# INLINE_NORMAL buildChunks #-}
2268+buildChunks :: (MonadIO m, Unbox a) =>
2269+ Int -> Fold m a (StreamK n (MutArray a))
2270+buildChunks n = FL.many (createOf n) FL.toStreamK
2271+
2272+{-# DEPRECATED writeChunks "Please use buildChunks instead." #-}
2273+{-# INLINE writeChunks #-}
1904writeChunks :: (MonadIO m, Unbox a) =>
2274writeChunks :: (MonadIO m, Unbox a) =>
1905 Int -> Fold m a (StreamK n (MutArray a))
2275 Int -> Fold m a (StreamK n (MutArray a))
1906−writeChunks n = FL.many (writeN n) FL.toStreamK
2276+writeChunks = buildChunks
1907
2277
1908{-# INLINE_NORMAL writeWithAs #-}
2278{-# INLINE_NORMAL writeWithAs #-}
1909writeWithAs :: forall m a. (MonadIO m, Unbox a)
2279writeWithAs :: forall m a. (MonadIO m, Unbox a)
1910 => PinnedState -> Int -> Fold m a (MutArray a)
2280 => PinnedState -> Int -> Fold m a (MutArray a)
1911−-- writeWithAs ps n = FL.rmapM rightSize $ writeAppendWith (* 2) (newAs ps n)
2281+-- writeWithAs ps n = FL.rmapM rightSize $ appendWith (* 2) (newAs ps n)
1912writeWithAs ps elemCount =
2282writeWithAs ps elemCount =
1913 FL.rmapM extract $ FL.foldlM' step initial
2283 FL.rmapM extract $ FL.foldlM' step initial
1914
2284
1915 where
2285 where
1916
2286
1917 initial = do
2287 initial = do
1918− when (elemCount < 0) $ error "writeWith: elemCount is negative"
2288+ when (elemCount < 0) $ error "createWith: elemCount is negative"
1919− liftIO $ newAs ps elemCount
2289+ newAs ps elemCount
1920
2290
1921 step arr@(MutArray _ start end bound) x
2291 step arr@(MutArray _ start end bound) x
1922 | INDEX_NEXT(end,a) > bound = do
2292 | INDEX_NEXT(end,a) > bound = do
1923 let oldSize = end - start
2293 let oldSize = end - start
1924 newSize = max (oldSize * 2) 1
2294 newSize = max (oldSize * 2) 1
1925− arr1 <- liftIO $ reallocExplicit (SIZE_OF(a)) newSize arr
2295+ arr1 <- liftIO $ reallocExplicitAs ps (SIZE_OF(a)) newSize arr
1926 snocUnsafe arr1 x
2296 snocUnsafe arr1 x
1927 step arr x = snocUnsafe arr x
2297 step arr x = snocUnsafe arr x
1928
2298
1929 extract = liftIO . rightSize
2299 extract = liftIO . rightSize
1930
2300
1931−-- XXX Compare writeWith with fromStreamD which uses an array of streams
2301+-- XXX Compare createWith with fromStreamD which uses an array of streams
1932−-- implementation. We can write this using writeChunks above if that is faster.
2302+-- implementation. We can write this using buildChunks above if that is faster.
1933−-- If writeWith is faster then we should use that to implement
2303+-- If createWith is faster then we should use that to implement
1934-- fromStreamD.
2304-- fromStreamD.
1935--
2305--
1936-- XXX The realloc based implementation needs to make one extra copy if we use
2306-- XXX The realloc based implementation needs to make one extra copy if we use
@@ -1945,39 +2315,56 @@ writeWithAs ps elemCount =
1945-- XXX check if GHC's memory allocator is efficient enough. We can try the C
2315-- XXX check if GHC's memory allocator is efficient enough. We can try the C
1946-- malloc to compare against.
2316-- malloc to compare against.
1947
2317
1948−-- | @writeWith minCount@ folds the whole input to a single array. The array
2318+-- | @createWith minCount@ folds the whole input to a single array. The array
1949-- starts at a size big enough to hold minCount elements, the size is doubled
2319-- starts at a size big enough to hold minCount elements, the size is doubled
1950-- every time the array needs to be grown.
2320-- every time the array needs to be grown.
1951--
2321--
1952-- /Caution! Do not use this on infinite streams./
2322-- /Caution! Do not use this on infinite streams./
1953--
2323--
1954−-- >>> f n = MutArray.writeAppendWith (* 2) (MutArray.new n)
2324+-- >>> f n = MutArray.appendWith (* 2) (MutArray.emptyOf n)
1955−-- >>> writeWith n = Fold.rmapM MutArray.rightSize (f n)
2325+-- >>> createWith n = Fold.rmapM MutArray.rightSize (f n)
1956−-- >>> writeWith n = Fold.rmapM MutArray.fromArrayStreamK (MutArray.writeChunks n)
2326+-- >>> createWith n = Fold.rmapM MutArray.fromChunksK (MutArray.buildChunks n)
1957--
2327--
1958-- /Pre-release/
2328-- /Pre-release/
1959−{-# INLINE_NORMAL writeWith #-}
2329+{-# INLINE_NORMAL createWith #-}
2330+createWith :: forall m a. (MonadIO m, Unbox a)
2331+ => Int -> Fold m a (MutArray a)
2332+-- createWith n = FL.rmapM rightSize $ appendWith (* 2) (emptyOf n)
2333+createWith = writeWithAs Unpinned
2334+
2335+{-# DEPRECATED writeWith "Please use createWith instead." #-}
2336+{-# INLINE writeWith #-}
1960writeWith :: forall m a. (MonadIO m, Unbox a)
2337writeWith :: forall m a. (MonadIO m, Unbox a)
1961 => Int -> Fold m a (MutArray a)
2338 => Int -> Fold m a (MutArray a)
1962−-- writeWith n = FL.rmapM rightSize $ writeAppendWith (* 2) (new n)
2339+writeWith = createWith
1963−writeWith = writeWithAs Unpinned
1964
2340
1965-- | Fold the whole input to a single array.
2341-- | Fold the whole input to a single array.
1966--
2342--
1967−-- Same as 'writeWith' using an initial array size of 'arrayChunkBytes' bytes
2343+-- Same as 'createWith' using an initial array size of 'arrayChunkBytes' bytes
1968-- rounded up to the element size.
2344-- rounded up to the element size.
1969--
2345--
1970-- /Caution! Do not use this on infinite streams./
2346-- /Caution! Do not use this on infinite streams./
1971--
2347--
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." #-}
1972{-# INLINE write #-}
2354{-# INLINE write #-}
1973write :: forall m a. (MonadIO m, Unbox a) => Fold m a (MutArray a)
2355write :: forall m a. (MonadIO m, Unbox a) => Fold m a (MutArray a)
1974−write = writeWith (allocBytesToElemCount (undefined :: a) arrayChunkBytes)
2356+write = create
1975
2357
1976−-- | Like 'write' but creates a pinned array.
2358+-- | Like 'create' but creates a pinned array.
2359+{-# INLINE pinnedCreate #-}
2360+pinnedCreate :: forall m a. (MonadIO m, Unbox a) => Fold m a (MutArray a)
2361+pinnedCreate =
2362+ writeWithAs Pinned (allocBytesToElemCount (undefined :: a) arrayChunkBytes)
2363+
2364+{-# DEPRECATED pinnedWrite "Please use pinnedCreate instead." #-}
1977{-# INLINE pinnedWrite #-}
2365{-# INLINE pinnedWrite #-}
1978pinnedWrite :: forall m a. (MonadIO m, Unbox a) => Fold m a (MutArray a)
2366pinnedWrite :: forall m a. (MonadIO m, Unbox a) => Fold m a (MutArray a)
1979−pinnedWrite =
2367+pinnedWrite = pinnedCreate
1980− writeWithAs Pinned (allocBytesToElemCount (undefined :: a) arrayChunkBytes)
1981
2368
1982-------------------------------------------------------------------------------
2369-------------------------------------------------------------------------------
1983-- construct from streams, known size
2370-- construct from streams, known size
@@ -1987,25 +2374,31 @@ pinnedWrite =
1987fromStreamDNAs :: forall m a. (MonadIO m, Unbox a)
2374fromStreamDNAs :: forall m a. (MonadIO m, Unbox a)
1988 => PinnedState -> Int -> D.Stream m a -> m (MutArray a)
2375 => PinnedState -> Int -> D.Stream m a -> m (MutArray a)
1989fromStreamDNAs ps limit str = do
2376fromStreamDNAs ps limit str = do
1990− (arr :: MutArray a) <- liftIO $ newAs ps limit
2377+ (arr :: MutArray a) <- newAs ps limit
1991 end <- D.foldlM' (fwrite (arrContents arr)) (return $ arrEnd arr) $ D.take limit str
2378 end <- D.foldlM' (fwrite (arrContents arr)) (return $ arrEnd arr) $ D.take limit str
1992 return $ arr {arrEnd = end}
2379 return $ arr {arrEnd = end}
1993
2380
1994 where
2381 where
1995
2382
1996 fwrite arrContents ptr x = do
2383 fwrite arrContents ptr x = do
1997− liftIO $ pokeByteIndex ptr arrContents x
2384+ liftIO $ pokeAt ptr arrContents x
1998 return $ INDEX_NEXT(ptr,a)
2385 return $ INDEX_NEXT(ptr,a)
1999
2386
2000−-- | Use the 'writeN' fold instead.
2387+-- | Use the 'createOf' fold instead.
2001--
2388--
2002−-- >>> fromStreamDN n = Stream.fold (MutArray.writeN n)
2389+-- >>> fromStreamN n = Stream.fold (MutArray.createOf n)
2003--
2390--
2391+{-# INLINE_NORMAL fromStreamN #-}
2392+fromStreamN :: forall m a. (MonadIO m, Unbox a)
2393+ => Int -> D.Stream m a -> m (MutArray a)
2394+-- fromStreamDN n = D.fold (createOf n)
2395+fromStreamN = fromStreamDNAs Unpinned
2396+
2397+{-# DEPRECATED fromStreamDN "Please use fromStreamN instead." #-}
2004−{-# INLINE_NORMAL fromStreamDN #-}
2398+{-# INLINE fromStreamDN #-}
2005fromStreamDN :: forall m a. (MonadIO m, Unbox a)
2399fromStreamDN :: forall m a. (MonadIO m, Unbox a)
2006 => Int -> D.Stream m a -> m (MutArray a)
2400 => Int -> D.Stream m a -> m (MutArray a)
2007−-- fromStreamDN n = D.fold (writeN n)
2401+fromStreamDN = fromStreamN
2008−fromStreamDN = fromStreamDNAs Unpinned
2009
2402
2010-- | Create a 'MutArray' from the first N elements of a list. The array is
2403-- | Create a 'MutArray' from the first N elements of a list. The array is
2011-- allocated to size N, if the list terminates before N elements then the
2404-- allocated to size N, if the list terminates before N elements then the
@@ -2025,16 +2418,70 @@ pinnedFromListN n xs = fromStreamDNAs Pinned n $ D.fromList xs
2025-- /Pre-release/
2418-- /Pre-release/
2026{-# INLINE fromListRevN #-}
2419{-# INLINE fromListRevN #-}
2027fromListRevN :: (MonadIO m, Unbox a) => Int -> [a] -> m (MutArray a)
2420fromListRevN :: (MonadIO m, Unbox a) => Int -> [a] -> m (MutArray a)
2028−fromListRevN n xs = D.fold (writeRevN n) $ D.fromList xs
2421+fromListRevN n xs = D.fold (revCreateOf n) $ D.fromList xs
2422+
2423+-- | Convert a pure stream in Identity monad to a mutable array.
2424+{-# INLINABLE fromPureStreamN #-}
2425+fromPureStreamN :: (MonadIO m, Unbox a) =>
2426+ Int -> Stream Identity a -> m (MutArray a)
2427+fromPureStreamN n xs =
2428+ D.fold (createOf n) $ D.morphInner (return . runIdentity) xs
2029
2429
2030-- | Convert a pure stream in Identity monad to a mutable array.
2430-- | Convert a pure stream in Identity monad to a mutable array.
2031{-# INLINABLE fromPureStream #-}
2431{-# INLINABLE fromPureStream #-}
2032fromPureStream :: (MonadIO m, Unbox a) => Stream Identity a -> m (MutArray a)
2432fromPureStream :: (MonadIO m, Unbox a) => Stream Identity a -> m (MutArray a)
2033fromPureStream xs =
2433fromPureStream xs =
2034− liftIO $ D.fold write $ D.morphInner (return . runIdentity) xs
2434+ D.fold create $ D.morphInner (return . runIdentity) xs
2435+
2436+{-# INLINABLE fromPtrN #-}
2437+fromPtrN :: MonadIO m => Int -> Ptr Word8 -> m (MutArray Word8)
2438+fromPtrN len addr = do
2439+ -- memcpy is better than stream copy when the size is known.
2440+ -- XXX We can implement a stream copy in a similar way by streaming Word64
2441+ -- first and then remaining Word8.
2442+ arr <- new len
2443+ _ <- unsafeAsPtr arr
2444+ (\ptr -> liftIO $ c_memcpy ptr addr (fromIntegral len))
2445+ return (arr {arrEnd = len})
2446+
2447+{-# INLINABLE fromByteStr# #-}
2448+fromByteStr# :: MonadIO m => Addr# -> m (MutArray Word8)
2449+fromByteStr# addr = do
2450+ -- It is better to count the size first and allocate exact space.
2451+ -- Also, memcpy is better than stream copy when the size is known.
2452+ -- C strlen compares 4 bytes at a time, so is better than the stream
2453+ -- version. https://github.com/bminor/glibc/blob/master/string/strlen.c
2454+ -- XXX We can possibly use a stream of Word64 to do the same.
2455+ -- fromByteStr# addr = fromPureStream (D.fromByteStr# addr)
2456+ len <- liftIO $ c_strlen (Ptr addr)
2457+ let lenInt = fromIntegral len
2458+ arr <- new lenInt
2459+ _ <- unsafeAsPtr arr (\ptr -> liftIO $ c_memcpy ptr (Ptr addr) len)
2460+ return (arr {arrEnd = lenInt})
2035
2461
2036-------------------------------------------------------------------------------
2462-------------------------------------------------------------------------------
2463+-- convert a stream of arrays to a single array by reallocating and copying
2464+-------------------------------------------------------------------------------
2465+
2466+-- XXX Both of these implementations of splicing seem to perform equally well.
2467+-- We need to perform benchmarks over a range of sizes though.
2468+
2469+-- | Also see 'fromChunksK'.
2470+{-# INLINE fromChunksRealloced #-}
2471+fromChunksRealloced :: forall m a. (MonadIO m, Unbox a)
2472+ => Stream m (MutArray a) -> m (MutArray a)
2473+fromChunksRealloced s = do
2474+ res <- D.uncons s
2475+ case res of
2476+ Just (a, strm) -> do
2477+ arr <- D.foldlM' spliceExp (pure a) strm
2478+ -- Reallocation is exponential so there may be 50% empty space in
2479+ -- worst case. One more reallocation to reclaim the space.
2480+ rightSize arr
2481+ Nothing -> pure nil
2482+
2483+-------------------------------------------------------------------------------
2037−-- convert stream to a single array
2484+-- convert a stream of arrays to a single array by buffering arrays first
2038-------------------------------------------------------------------------------
2485-------------------------------------------------------------------------------
2039
2486
2040{-# INLINE arrayStreamKLength #-}
2487{-# INLINE arrayStreamKLength #-}
@@ -2044,34 +2491,72 @@ arrayStreamKLength as = K.foldl' (+) 0 (K.map length as)
2044-- | Convert an array stream to an array. Note that this requires peak memory
2491-- | Convert an array stream to an array. Note that this requires peak memory
2045-- that is double the size of the array stream.
2492-- that is double the size of the array stream.
2046--
2493--
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." #-}
2047{-# INLINE fromArrayStreamK #-}
2518{-# INLINE fromArrayStreamK #-}
2048fromArrayStreamK :: (Unbox a, MonadIO m) =>
2519fromArrayStreamK :: (Unbox a, MonadIO m) =>
2049 StreamK m (MutArray a) -> m (MutArray a)
2520 StreamK m (MutArray a) -> m (MutArray a)
2050−fromArrayStreamK as = do
2521+fromArrayStreamK = fromChunksK
2051− len <- arrayStreamKLength as
2052− fromStreamDN len $ D.unfoldMany reader $ D.fromStreamK as
2053
2522
2054{-# INLINE fromStreamDAs #-}
2523{-# INLINE fromStreamDAs #-}
2055fromStreamDAs ::
2524fromStreamDAs ::
2056 (MonadIO m, Unbox a) => PinnedState -> D.Stream m a -> m (MutArray a)
2525 (MonadIO m, Unbox a) => PinnedState -> D.Stream m a -> m (MutArray a)
2526+fromStreamDAs ps m =
2057−fromStreamDAs ps m = arrayStreamKFromStreamDAs ps m >>= fromArrayStreamK
2527+ arrayStreamKFromStreamDAs Unpinned m >>= fromChunkskAs ps
2058
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+
2542+-- fromStream (Stream m) = P.fold create m
2059-- CAUTION: a very large number (millions) of arrays can degrade performance
2543-- CAUTION: a very large number (millions) of arrays can degrade performance
2060-- due to GC overhead because we need to buffer the arrays before we flatten
2544-- due to GC overhead because we need to buffer the arrays before we flatten
2061-- all the arrays.
2545-- all the arrays.
2062--
2546--
2063−-- XXX Compare if this is faster or "fold write".
2547+-- XXX Compare if this is faster or "fold create".
2064--
2548--
2065-- | We could take the approach of doubling the memory allocation on each
2549-- | We could take the approach of doubling the memory allocation on each
2066-- overflow. This would result in more or less the same amount of copying as in
2550-- overflow. This would result in more or less the same amount of copying as in
2067-- the chunking approach. However, if we have to shrink in the end then it may
2551-- the chunking approach. However, if we have to shrink in the end then it may
2068-- result in an extra copy of the entire data.
2552-- result in an extra copy of the entire data.
2069--
2553--
2070−-- >>> fromStreamD = StreamD.fold MutArray.write
2554+-- >>> fromStreamD = StreamD.fold MutArray.create
2071--
2555--
2072{-# INLINE fromStreamD #-}
2556{-# INLINE fromStreamD #-}
2557+{-# DEPRECATED fromStreamD "Please use fromStream instead." #-}
2073fromStreamD :: (MonadIO m, Unbox a) => D.Stream m a -> m (MutArray a)
2558fromStreamD :: (MonadIO m, Unbox a) => D.Stream m a -> m (MutArray a)
2074−fromStreamD = fromStreamDAs Unpinned
2559+fromStreamD = fromStream
2075
2560
2076-- | Create a 'MutArray' from a list. The list must be of finite size.
2561-- | Create a 'MutArray' from a list. The list must be of finite size.
2077--
2562--
@@ -2096,22 +2581,44 @@ fromListRev xs = fromListRevN (Prelude.length xs) xs
2096-- Cloning
2581-- Cloning
2097-------------------------------------------------------------------------------
2582-------------------------------------------------------------------------------
2098
2583
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+
2099{-# INLINE cloneAs #-}
2588{-# INLINE cloneAs #-}
2589+cloneAs ::
2590+ ( MonadIO m
2591+#ifdef DEVBUILD
2592+ , Unbox a
2593+#endif
2594+ )
2100−cloneAs :: MonadIO m => PinnedState -> MutArray a -> m (MutArray a)
2595+ => PinnedState -> MutArray a -> m (MutArray a)
2101cloneAs ps src =
2596cloneAs ps src =
2102− liftIO $ do
2597+ do
2103 let startSrc = arrStart src
2598 let startSrc = arrStart src
2104 srcLen = arrEnd src - startSrc
2599 srcLen = arrEnd src - startSrc
2105− newArrContents <- Unboxed.newBytesAs ps srcLen
2600+ newArrContents <-
2106− putSliceUnsafe (arrContents src) startSrc newArrContents 0 srcLen
2601+ Unboxed.cloneSliceUnsafeAs ps startSrc srcLen (arrContents src)
2107 return $ MutArray newArrContents 0 srcLen srcLen
2602 return $ MutArray newArrContents 0 srcLen srcLen
2108
2603
2109{-# INLINE clone #-}
2604{-# INLINE clone #-}
2605+clone ::
2606+ ( MonadIO m
2607+#ifdef DEVBUILD
2608+ , Unbox a
2609+#endif
2610+ )
2110−clone :: MonadIO m => MutArray a -> m (MutArray a)
2611+ => MutArray a -> m (MutArray a)
2111clone = cloneAs Unpinned
2612clone = cloneAs Unpinned
2112
2613
2113{-# INLINE pinnedClone #-}
2614{-# INLINE pinnedClone #-}
2615+pinnedClone ::
2616+ ( MonadIO m
2617+#ifdef DEVBUILD
2618+ , Unbox a
2619+#endif
2620+ )
2114−pinnedClone :: MonadIO m => MutArray a -> m (MutArray a)
2621+ => MutArray a -> m (MutArray a)
2115pinnedClone = cloneAs Pinned
2622pinnedClone = cloneAs Pinned
2116
2623
2117-------------------------------------------------------------------------------
2624-------------------------------------------------------------------------------
@@ -2126,17 +2633,16 @@ spliceCopy :: forall m a. MonadIO m =>
2126 Unbox a =>
2633 Unbox a =>
2127#endif
2634#endif
2128 MutArray a -> MutArray a -> m (MutArray a)
2635 MutArray a -> MutArray a -> m (MutArray a)
2129−spliceCopy arr1 arr2 = liftIO $ do
2636+spliceCopy arr1 arr2 = do
2130 let start1 = arrStart arr1
2637 let start1 = arrStart arr1
2131 start2 = arrStart arr2
2638 start2 = arrStart arr2
2132 len1 = arrEnd arr1 - start1
2639 len1 = arrEnd arr1 - start1
2133 len2 = arrEnd arr2 - start2
2640 len2 = arrEnd arr2 - start2
2134− let newLen = len1 + len2
2641+ let len = len1 + len2
2135 newArrContents <-
2642 newArrContents <-
2136 if Unboxed.isPinned (arrContents arr1)
2643 if Unboxed.isPinned (arrContents arr1)
2137− then Unboxed.pinnedNewByteArray newLen
2644+ then liftIO $ Unboxed.pinnedNew len
2138− else Unboxed.newByteArray newLen
2645+ else liftIO $ Unboxed.new len
2139− let len = len1 + len2
2140 putSliceUnsafe (arrContents arr1) start1 newArrContents 0 len1
2646 putSliceUnsafe (arrContents arr1) start1 newArrContents 0 len1
2141 putSliceUnsafe (arrContents arr2) start2 newArrContents len1 len2
2647 putSliceUnsafe (arrContents arr2) start2 newArrContents len1 len2
2142 return $ MutArray newArrContents 0 len len
2648 return $ MutArray newArrContents 0 len len
@@ -2148,7 +2654,7 @@ spliceCopy arr1 arr2 = liftIO $ do
2148spliceUnsafe :: MonadIO m =>
2654spliceUnsafe :: MonadIO m =>
2149 MutArray a -> MutArray a -> m (MutArray a)
2655 MutArray a -> MutArray a -> m (MutArray a)
2150spliceUnsafe dst src =
2656spliceUnsafe dst src =
2151− liftIO $ do
2657+ do
2152 let startSrc = arrStart src
2658 let startSrc = arrStart src
2153 srcLen = arrEnd src - startSrc
2659 srcLen = arrEnd src - startSrc
2154 endDst = arrEnd dst
2660 endDst = arrEnd dst
@@ -2170,7 +2676,7 @@ spliceWith :: forall m a. (MonadIO m, Unbox a) =>
2170 (Int -> Int -> Int) -> MutArray a -> MutArray a -> m (MutArray a)
2676 (Int -> Int -> Int) -> MutArray a -> MutArray a -> m (MutArray a)
2171spliceWith sizer dst@(MutArray _ start end bound) src = do
2677spliceWith sizer dst@(MutArray _ start end bound) src = do
2172{-
2678{-
2173− let f = writeAppendWith (`sizer` byteLength src) (return dst)
2679+ let f = appendWith (`sizer` byteLength src) (return dst)
2174 in D.fold f (toStreamD src)
2680 in D.fold f (toStreamD src)
2175-}
2681-}
2176 assert (end <= bound) (return ())
2682 assert (end <= bound) (return ())
@@ -2186,7 +2692,7 @@ spliceWith sizer dst@(MutArray _ start end bound) src = do
2186 $ "splice: newSize is less than the total size "
2692 $ "splice: newSize is less than the total size "
2187 ++ "of arrays being appended. Please check the "
2693 ++ "of arrays being appended. Please check the "
2188 ++ "sizer function passed."
2694 ++ "sizer function passed."
2189− liftIO $ realloc newSizeInBytes dst
2695+ realloc newSizeInBytes dst
2190 else return dst
2696 else return dst
2191 spliceUnsafe dst1 src
2697 spliceUnsafe dst1 src
2192
2698
@@ -2223,14 +2729,24 @@ spliceExp = spliceWith (\l1 l2 -> max (l1 * 2) (l1 + l2))
2223-- Splitting
2729-- Splitting
2224-------------------------------------------------------------------------------
2730-------------------------------------------------------------------------------
2225
2731
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+
2226-- | Drops the separator byte
2743-- | Drops the separator byte
2227{-# INLINE breakOn #-}
2744{-# INLINE breakOn #-}
2228breakOn :: MonadIO m
2745breakOn :: MonadIO m
2229 => Word8 -> MutArray Word8 -> m (MutArray Word8, Maybe (MutArray Word8))
2746 => Word8 -> MutArray Word8 -> m (MutArray Word8, Maybe (MutArray Word8))
2230−breakOn sep arr@MutArray{..} = asPtrUnsafe arr $ \p -> liftIO $ do
2747+breakOn sep arr@MutArray{..} = unsafeAsPtr arr $ \p -> liftIO $ do
2231− -- XXX Instead of using asPtrUnsafe (pinning memory) we can pass unlifted
2232− -- Addr# to memchr and it should be safe (from ghc 8.4).
2233 -- XXX We do not need memchr here, we can use a Haskell equivalent.
2748 -- XXX We do not need memchr here, we can use a Haskell equivalent.
2749+ -- Need efficient stream based primitives that work on Word64.
2234 loc <- c_memchr p sep (fromIntegral $ byteLength arr)
2750 loc <- c_memchr p sep (fromIntegral $ byteLength arr)
2235 let sepIndex = loc `minusPtr` p
2751 let sepIndex = loc `minusPtr` p
2236 return $
2752 return $
@@ -2251,32 +2767,41 @@ breakOn sep arr@MutArray{..} = asPtrUnsafe arr $ \p -> liftIO $ do
2251 }
2767 }
2252 )
2768 )
2253
2769
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+
2254-- | Create two slices of an array without copying the original array. The
2792-- | Create two slices of an array without copying the original array. The
2255-- specified index @i@ is the first index of the second slice.
2793-- specified index @i@ is the first index of the second slice.
2256--
2794--
2795+{-# INLINE splitAt #-}
2257splitAt :: forall a. Unbox a => Int -> MutArray a -> (MutArray a, MutArray a)
2796splitAt :: forall a. Unbox a => Int -> MutArray a -> (MutArray a, MutArray a)
2258−splitAt i arr@MutArray{..} =
2797+splitAt i arr =
2259 let maxIndex = length arr - 1
2798 let maxIndex = length arr - 1
2260 in if i < 0
2799 in if i < 0
2261 then error "sliceAt: negative array index"
2800 then error "sliceAt: negative array index"
2262 else if i > maxIndex
2801 else if i > maxIndex
2263 then error $ "sliceAt: specified array index " ++ show i
2802 then error $ "sliceAt: specified array index " ++ show i
2264 ++ " is beyond the maximum index " ++ show maxIndex
2803 ++ " is beyond the maximum index " ++ show maxIndex
2265− else let off = i * SIZE_OF(a)
2804+ else unsafeSplitAt i arr
2266− p = arrStart + off
2267− in ( MutArray
2268− { arrContents = arrContents
2269− , arrStart = arrStart
2270− , arrEnd = p
2271− , arrBound = p
2272− }
2273− , MutArray
2274− { arrContents = arrContents
2275− , arrStart = p
2276− , arrEnd = arrEnd
2277− , arrBound = arrBound
2278− }
2279− )
2280
2805
2281-------------------------------------------------------------------------------
2806-------------------------------------------------------------------------------
2282-- Casting
2807-- Casting
@@ -2326,9 +2851,13 @@ cast arr =
2326-- guarantees that unsafe calls will be performed in the calling thread. Making
2851-- guarantees that unsafe calls will be performed in the calling thread. Making
2327-- it safe to pass heap-allocated objects to unsafe functions.
2852-- it safe to pass heap-allocated objects to unsafe functions.
2328
2853
2329−-- Should we just name it asPtr, the unsafety is implicit for any pointer
2854+-- XXX Should we just name it asPtr, the unsafety is implicit for any pointer
2330-- operations. And we are safe from Haskell perspective because we will be
2855-- operations. And we are safe from Haskell perspective because we will be
2331-- pinning the memory.
2856-- 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.
2332
2861
2333-- | Use a @MutArray a@ as @Ptr a@. This is useful when we want to pass an
2862-- | Use a @MutArray a@ as @Ptr a@. This is useful when we want to pass an
2334-- array as a pointer to some operating system call or to a "safe" FFI call.
2863-- array as a pointer to some operating system call or to a "safe" FFI call.
@@ -2346,48 +2875,377 @@ cast arr =
2346--
2875--
2347-- /Pre-release/
2876-- /Pre-release/
2348--
2877--
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." #-}
2349{-# INLINE asPtrUnsafe #-}
2885{-# INLINE asPtrUnsafe #-}
2350asPtrUnsafe :: MonadIO m => MutArray a -> (Ptr a -> m b) -> m b
2886asPtrUnsafe :: MonadIO m => MutArray a -> (Ptr a -> m b) -> m b
2351−asPtrUnsafe arr f =
2887+asPtrUnsafe = unsafePinnedAsPtr
2352− Unboxed.asPtrUnsafe
2888+
2889+{-# INLINE unsafeAsPtr #-}
2890+unsafeAsPtr :: MonadIO m => MutArray a -> (Ptr a -> m b) -> m b
2891+unsafeAsPtr arr f =
2892+ Unboxed.unsafeAsPtr
2353 (arrContents arr) (\ptr -> f (ptr `plusPtr` arrStart arr))
2893 (arrContents arr) (\ptr -> f (ptr `plusPtr` arrStart arr))
2354
2894
2355-------------------------------------------------------------------------------
2895-------------------------------------------------------------------------------
2356-- Equality
2896-- Equality
2357-------------------------------------------------------------------------------
2897-------------------------------------------------------------------------------
2358
2898
2359−-- | Compare the length of the arrays. If the length is equal, compare the
2899+-- | Byte compare two arrays. Compare the length of the arrays. If the length
2360−-- lexicographical ordering of two underlying byte arrays otherwise return the
2900+-- is equal, compare the lexicographical ordering of two underlying byte arrays
2361−-- result of length comparison.
2901+-- otherwise return the result of length comparison.
2902+--
2903+-- /Unsafe/: Note that the 'Unbox' instance of sum types with constructors of
2904+-- different sizes may leave some memory uninitialized which can make byte
2905+-- comparison unreliable.
2362--
2906--
2363-- /Pre-release/
2907-- /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+
2364{-# INLINE cmp #-}
2931{-# INLINE cmp #-}
2932+{-# DEPRECATED cmp "Please use byteCmp instead." #-}
2365cmp :: MonadIO m => MutArray a -> MutArray a -> m Ordering
2933cmp :: MonadIO m => MutArray a -> MutArray a -> m Ordering
2366−cmp arr1 arr2 =
2934+cmp = byteCmp
2367− liftIO
2368− $ do
2369− let marr1 = getMutableByteArray# (arrContents arr1)
2370− marr2 = getMutableByteArray# (arrContents arr2)
2371− !(I# st1#) = arrStart arr1
2372− !(I# st2#) = arrStart arr2
2373− !(I# len#) = byteLength arr1
2374− case compare (byteLength arr1) (byteLength arr2) of
2375− EQ -> do
2376− r <- liftIO $ IO $ \s# ->
2377− let res =
2378− I#
2379− (compareByteArrays#
2380− (unsafeCoerce# marr1)
2381− st1#
2382− (unsafeCoerce# marr2)
2383− st2#
2384− len#)
2385− in (# s#, res #)
2386− return $ compare r 0
2387− x -> return x
2388
2935
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
2389-------------------------------------------------------------------------------
2947-------------------------------------------------------------------------------
2390−-- NFData
2948+
2949+-- Note: LE versions avoid an extra copy compared to GE. LE parser trades
2950+-- backtracking one array in lieu of avoiding a copy. However, LE and GE both
2951+-- can leave some memory unused. They can split the last array to fit it
2952+-- exactly in the space.
2953+
2954+{-# INLINE_NORMAL pCompactLeAs #-}
2955+pCompactLeAs ::
2956+ forall m a. (MonadIO m, Unbox a)
2957+ => PinnedState -> Int -> Parser (MutArray a) m (MutArray a)
2958+pCompactLeAs ps maxElems = Parser step initial extract
2959+
2960+ where
2961+
2962+ maxBytes = maxElems * SIZE_OF(a)
2963+
2964+ functionName = "Streamly.Internal.Data.MutArray.pCompactLE"
2965+
2966+ initial =
2967+ return
2968+ $ if maxElems <= 0
2969+ then error
2970+ $ functionName
2971+ ++ ": the size of arrays ["
2972+ ++ show maxElems ++ "] must be a natural number"
2973+ else Parser.IPartial Nothing
2974+
2975+ step Nothing arr =
2976+ return
2977+ $ let len = byteLength arr
2978+ in if len >= maxBytes
2979+ then Parser.Done 0 arr
2980+ else Parser.Partial 0 (Just arr)
2981+ -- XXX Split the last array to use the space more compactly.
2982+ step (Just buf) arr =
2983+ let len = byteLength buf + byteLength arr
2984+ in if len > maxBytes
2985+ then return $ Parser.Done 1 buf
2986+ else do
2987+ buf1 <-
2988+ if byteCapacity buf < maxBytes
2989+ then liftIO $ reallocExplicitAs
2990+ ps (SIZE_OF(a)) maxBytes buf
2991+ else return buf
2992+ buf2 <- spliceUnsafe buf1 arr
2993+ return $ Parser.Partial 0 (Just buf2)
2994+
2995+ extract Nothing = return $ Parser.Done 0 nil
2996+ extract (Just buf) = return $ Parser.Done 0 buf
2997+
2998+-- | Parser @pCompactLE maxElems@ coalesces adjacent arrays in the input stream
2999+-- only if the combined size would be less than or equal to @maxElems@
3000+-- elements. Note that it won't split an array if the original array is already
3001+-- larger than maxElems.
3002+--
3003+-- @maxElems@ must be greater than 0.
3004+--
3005+-- Generates unpinned arrays irrespective of the pinning status of input
3006+-- arrays.
3007+--
3008+-- /Internal/
3009+{-# INLINE pCompactLE #-}
3010+pCompactLE ::
3011+ forall m a. (MonadIO m, Unbox a)
3012+ => Int -> Parser (MutArray a) m (MutArray a)
3013+pCompactLE = pCompactLeAs Unpinned
3014+
3015+-- | Pinned version of 'pCompactLE'.
3016+{-# INLINE pPinnedCompactLE #-}
3017+pPinnedCompactLE ::
3018+ forall m a. (MonadIO m, Unbox a)
3019+ => Int -> Parser (MutArray a) m (MutArray a)
3020+pPinnedCompactLE = pCompactLeAs Pinned
3021+
3022+data SpliceState s arr
3023+ = SpliceInitial s
3024+ | SpliceBuffering s arr
3025+ | SpliceYielding arr (SpliceState s arr)
3026+ | SpliceFinish
3027+
3028+-- This mutates the first array (if it has space) to append values from the
3029+-- second one. This would work for immutable arrays as well because an
3030+-- immutable array never has additional space so a new array is allocated
3031+-- instead of mutating it.
3032+
3033+{-# INLINE_NORMAL compactLeAs #-}
3034+compactLeAs :: forall m a. (MonadIO m, Unbox a)
3035+ => PinnedState -> Int -> D.Stream m (MutArray a) -> D.Stream m (MutArray a)
3036+compactLeAs ps maxElems (D.Stream step state) =
3037+ D.Stream step' (SpliceInitial state)
3038+
3039+ where
3040+
3041+ maxBytes = maxElems * SIZE_OF(a)
3042+
3043+ functionName = "Streamly.Internal.Data.MutArray.rCompactLE"
3044+
3045+ {-# INLINE_LATE step' #-}
3046+ step' gst (SpliceInitial st) = do
3047+ when (maxElems <= 0) $
3048+ -- XXX we can pass the module string from the higher level API
3049+ error $ functionName ++ ": the size of arrays [" ++ show maxElems
3050+ ++ "] must be a natural number"
3051+ r <- step gst st
3052+ case r of
3053+ D.Yield arr s -> return $
3054+ let len = byteLength arr
3055+ in if len >= maxBytes
3056+ then D.Skip (SpliceYielding arr (SpliceInitial s))
3057+ else D.Skip (SpliceBuffering s arr)
3058+ D.Skip s -> return $ D.Skip (SpliceInitial s)
3059+ D.Stop -> return D.Stop
3060+
3061+ -- XXX Split the last array to use the space more compactly.
3062+ step' gst (SpliceBuffering st buf) = do
3063+ r <- step gst st
3064+ case r of
3065+ D.Yield arr s -> do
3066+ let len = byteLength buf + byteLength arr
3067+ if len > maxBytes
3068+ then return $
3069+ D.Skip (SpliceYielding buf (SpliceBuffering s arr))
3070+ else do
3071+ buf1 <- if byteCapacity buf < maxBytes
3072+ then liftIO $ reallocExplicitAs
3073+ ps (SIZE_OF(a)) maxBytes buf
3074+ else return buf
3075+ buf2 <- spliceUnsafe buf1 arr
3076+ return $ D.Skip (SpliceBuffering s buf2)
3077+ D.Skip s -> return $ D.Skip (SpliceBuffering s buf)
3078+ D.Stop -> return $ D.Skip (SpliceYielding buf SpliceFinish)
3079+
3080+ step' _ SpliceFinish = return D.Stop
3081+
3082+ step' _ (SpliceYielding arr next) = return $ D.Yield arr next
3083+
3084+
3085+{-# INLINE_NORMAL fCompactGeAs #-}
3086+fCompactGeAs ::
3087+ forall m a. (MonadIO m, Unbox a)
3088+ => PinnedState -> Int -> FL.Fold m (MutArray a) (MutArray a)
3089+fCompactGeAs ps minElems = Fold step initial extract extract
3090+
3091+ where
3092+
3093+ minBytes = minElems * SIZE_OF(a)
3094+
3095+ functionName = "Streamly.Internal.Data.MutArray.fCompactGE"
3096+
3097+ initial =
3098+ return
3099+ $ if minElems < 0
3100+ then error
3101+ $ functionName
3102+ ++ ": the size of arrays ["
3103+ ++ show minElems ++ "] must be a natural number"
3104+ else FL.Partial Nothing
3105+
3106+ step Nothing arr =
3107+ return
3108+ $ let len = byteLength arr
3109+ in if len >= minBytes
3110+ then FL.Done arr
3111+ else FL.Partial (Just arr)
3112+ -- XXX Buffer arrays as a list to avoid copy and reallocations
3113+ step (Just buf) arr = do
3114+ let len = byteLength buf + byteLength arr
3115+ buf1 <-
3116+ if byteCapacity buf < len
3117+ then liftIO $ reallocExplicitAs
3118+ ps (SIZE_OF(a)) (max minBytes len) buf
3119+ else return buf
3120+ buf2 <- spliceUnsafe buf1 arr
3121+ if len >= minBytes
3122+ then return $ FL.Done buf2
3123+ else return $ FL.Partial (Just buf2)
3124+
3125+ extract Nothing = return nil
3126+ extract (Just buf) = return buf
3127+
3128+-- | Fold @fCompactGE minElems@ coalesces adjacent arrays in the input stream
3129+-- until the size becomes greater than or equal to @minElems@.
3130+--
3131+-- Generates unpinned arrays irrespective of the pinning status of input
3132+-- arrays.
3133+{-# INLINE fCompactGE #-}
3134+fCompactGE ::
3135+ forall m a. (MonadIO m, Unbox a)
3136+ => Int -> FL.Fold m (MutArray a) (MutArray a)
3137+fCompactGE = fCompactGeAs Unpinned
3138+
3139+-- | Pinned version of 'fCompactGE'.
3140+{-# INLINE fPinnedCompactGE #-}
3141+fPinnedCompactGE ::
3142+ forall m a. (MonadIO m, Unbox a)
3143+ => Int -> FL.Fold m (MutArray a) (MutArray a)
3144+fPinnedCompactGE = fCompactGeAs Pinned
3145+
3146+{-# INLINE_NORMAL lCompactGeAs #-}
3147+lCompactGeAs :: forall m a. (MonadIO m, Unbox a)
3148+ => PinnedState -> Int -> Fold m (MutArray a) () -> Fold m (MutArray a) ()
3149+-- The fold version turns out to be a little bit slower.
3150+-- lCompactGeAs ps n = FL.many (fCompactGeAs ps n)
3151+lCompactGeAs ps minElems (Fold step1 initial1 _ final1) =
3152+ Fold step initial extract final
3153+
3154+ where
3155+
3156+ minBytes = minElems * SIZE_OF(a)
3157+
3158+ functionName = "Streamly.Internal.Data.MutArray.lCompactGE"
3159+
3160+ initial = do
3161+ when (minElems <= 0) $
3162+ -- XXX we can pass the module string from the higher level API
3163+ error $ functionName ++ ": the size of arrays ["
3164+ ++ show minElems ++ "] must be a natural number"
3165+
3166+ r <- initial1
3167+ return $ first (Tuple' Nothing) r
3168+
3169+ {-# INLINE runInner #-}
3170+ runInner len acc buf =
3171+ if len >= minBytes
3172+ then do
3173+ r <- step1 acc buf
3174+ case r of
3175+ FL.Done _ -> return $ FL.Done ()
3176+ FL.Partial s -> do
3177+ _ <- final1 s
3178+ res <- initial1
3179+ return $ first (Tuple' Nothing) res
3180+ else return $ FL.Partial $ Tuple' (Just buf) acc
3181+
3182+ step (Tuple' Nothing r1) arr =
3183+ runInner (byteLength arr) r1 arr
3184+
3185+ -- XXX Buffer arrays as a list to avoid copy and reallocations
3186+ step (Tuple' (Just buf) r1) arr = do
3187+ let len = byteLength buf + byteLength arr
3188+ buf1 <- if byteCapacity buf < len
3189+ then liftIO $ reallocExplicitAs
3190+ ps (SIZE_OF(a)) (max minBytes len) buf
3191+ else return buf
3192+ buf2 <- spliceUnsafe buf1 arr
3193+ runInner len r1 buf2
3194+
3195+ -- XXX Several folds do extract >=> final, therefore, we need to make final
3196+ -- return "m b" rather than using extract post it if we want extract to be
3197+ -- partial.
3198+ --
3199+ -- extract forces the pending buffer to be sent to the fold which is not
3200+ -- what we want.
3201+ extract _ = error "lCompactGE: not designed for scanning"
3202+
3203+ final (Tuple' Nothing r1) = final1 r1
3204+ final (Tuple' (Just buf) r1) = do
3205+ r <- step1 r1 buf
3206+ case r of
3207+ FL.Partial rr -> final1 rr
3208+ FL.Done _ -> return ()
3209+
3210+-- | Like 'compactGE' but for transforming folds instead of stream.
3211+--
3212+-- >>> lCompactGE n = Fold.many (MutArray.fCompactGE n)
3213+--
3214+-- Generates unpinned arrays irrespective of the pinning status of input
3215+-- arrays.
3216+{-# INLINE lCompactGE #-}
3217+lCompactGE :: forall m a. (MonadIO m, Unbox a)
3218+ => Int -> Fold m (MutArray a) () -> Fold m (MutArray a) ()
3219+lCompactGE = lCompactGeAs Unpinned
3220+
3221+-- | Pinned version of 'lCompactGE'.
3222+{-# INLINE lPinnedCompactGE #-}
3223+lPinnedCompactGE :: forall m a. (MonadIO m, Unbox a)
3224+ => Int -> Fold m (MutArray a) () -> Fold m (MutArray a) ()
3225+lPinnedCompactGE = lCompactGeAs Pinned
3226+
3227+-- | @compactGE n stream@ coalesces adjacent arrays in the @stream@ until
3228+-- the size becomes greater than or equal to @n@.
3229+--
3230+-- >>> compactGE n = Stream.foldMany (MutArray.fCompactGE n)
3231+--
3232+{-# INLINE compactGE #-}
3233+compactGE ::
3234+ (MonadIO m, Unbox a)
3235+ => Int -> Stream m (MutArray a) -> Stream m (MutArray a)
3236+compactGE n = D.foldMany (fCompactGE n)
3237+
3238+-- | 'compactEQ n' coalesces adajacent arrays in the input stream to
3239+-- arrays of exact size @n@.
3240+--
3241+-- /Unimplemented/
3242+{-# INLINE compactEQ #-}
3243+compactEQ :: -- (MonadIO m, Unbox a) =>
3244+ Int -> Stream m (MutArray a) -> Stream m (MutArray a)
3245+compactEQ _n = undefined -- D.parseManyD (pCompactEQ n)
3246+
3247+-------------------------------------------------------------------------------
3248+-- In-place mutation algorithms
2391-------------------------------------------------------------------------------
3249-------------------------------------------------------------------------------
2392
3250
2393-- | Strip elements which match with predicate from both ends.
3251-- | Strip elements which match with predicate from both ends.
@@ -2416,7 +3274,7 @@ strip eq arr@MutArray{..} = liftIO $ do
2416 getStart cur = do
3274 getStart cur = do
2417 if cur < arrEnd
3275 if cur < arrEnd
2418 then do
3276 then do
2419− r <- peekByteIndex cur arrContents
3277+ r <- peekAt cur arrContents
2420 if eq r
3278 if eq r
2421 then getStart (INDEX_NEXT(cur,a))
3279 then getStart (INDEX_NEXT(cur,a))
2422 else return cur
3280 else return cur
@@ -2426,7 +3284,7 @@ strip eq arr@MutArray{..} = liftIO $ do
2426 if cur > low
3284 if cur > low
2427 then do
3285 then do
2428 let prev = INDEX_PREV(cur,a)
3286 let prev = INDEX_PREV(cur,a)
2429− r <- peekByteIndex prev arrContents
3287+ r <- peekAt prev arrContents
2430 if eq r
3288 if eq r
2431 then getLast prev low
3289 then getLast prev low
2432 else return cur
3290 else return cur
core/src/Streamly/Internal/Data/MutByteArray.hs+221−219
@@ -1,249 +1,251 @@
1−{-# LANGUAGE UnboxedTuples #-}
1+{-# LANGUAGE TemplateHaskell #-}
2+
3+-- This is required as all the instances in this module are orphan instances.
4+{-# OPTIONS_GHC -fno-warn-orphans #-}
2
5
3-- |
6-- |
4-- Module : Streamly.Internal.Data.MutByteArray
7-- Module : Streamly.Internal.Data.MutByteArray
5-- Copyright : (c) 2023 Composewell Technologies
8-- Copyright : (c) 2023 Composewell Technologies
6-- License : BSD3-3-Clause
9-- License : BSD3-3-Clause
7-- Maintainer : streamly@composewell.com
10-- Maintainer : streamly@composewell.com
8−-- Stability : experimental
9-- Portability : GHC
11-- Portability : GHC
10--
12--
13+
11module Streamly.Internal.Data.MutByteArray
14module Streamly.Internal.Data.MutByteArray
12 (
15 (
13− -- ** MutByteArray
16+ -- * MutByteArray
14− MutByteArray(..)
17+ module Streamly.Internal.Data.MutByteArray.Type
15− , MutableByteArray
18+ -- * Unbox
16− , getMutableByteArray#
19+ , module Streamly.Internal.Data.Unbox
17−
20+ , module Streamly.Internal.Data.Unbox.TH
18− -- ** Pinning
21+ -- * Serialize
19− , PinnedState(..)
22+ , module Streamly.Internal.Data.Serialize.Type
20− , isPinned
23+ -- * Serialize TH
21− , pin
24+ , module Streamly.Internal.Data.Serialize.TH
22− , unpin
23−
24− -- ** Allocation
25− , nil
26− , newBytesAs
27− , newByteArray
28− , pinnedNewByteArray
29− , pinnedNewAlignedBytes
30−
31− -- ** Access
32− , sizeOfMutableByteArray
33− , putSliceUnsafe
34− , asPtrUnsafe
35 ) where
25 ) where
36
26
37−import Control.Monad.IO.Class (MonadIO(..))
27+--------------------------------------------------------------------------------
38−#ifdef DEBUG
28+-- Imports
39−import Debug.Trace (trace)
29+--------------------------------------------------------------------------------
30+
31+import Data.Proxy (Proxy(..))
32+import Streamly.Internal.Data.Array (Array(..))
33+import GHC.Exts (Int(..), sizeofByteArray#, unsafeCoerce#)
34+import GHC.Word (Word8)
35+
36+#if __GLASGOW_HASKELL__ >= 900
37+import GHC.Num.Integer (Integer(..))
38+#else
39+import GHC.Integer.GMP.Internals (Integer(..), BigNat(..))
40#endif
40#endif
41−import GHC.Base (IO(..))
42−import System.IO.Unsafe (unsafePerformIO)
43
41
44−import GHC.Exts
42+import Streamly.Internal.Data.MutByteArray.Type
43+import Streamly.Internal.Data.Serialize.TH
44+import Streamly.Internal.Data.Serialize.Type
45+import Streamly.Internal.Data.Unbox
46+import Streamly.Internal.Data.Unbox.TH
45
47
46--------------------------------------------------------------------------------
48--------------------------------------------------------------------------------
47−-- The ArrayContents type
49+-- Common instances
48--------------------------------------------------------------------------------
50--------------------------------------------------------------------------------
49
51
50−data PinnedState
52+-- Note
51− = Pinned
53+-- ====
52− | Unpinned
53−
54−-- XXX can use UnliftedNewtypes
55−
56−-- | A lifted mutable byte array type wrapping @MutableByteArray# RealWorld@.
57−-- This is a low level array used to back high level unboxed arrays and
58−-- serialized data.
59−data MutByteArray = MutByteArray (MutableByteArray# RealWorld)
60−
61−{-# DEPRECATED MutableByteArray "Please use MutByteArray instead" #-}
62−type MutableByteArray = MutByteArray
63−
64−{-# INLINE getMutableByteArray# #-}
65−getMutableByteArray# :: MutByteArray -> MutableByteArray# RealWorld
66−getMutableByteArray# (MutByteArray mbarr) = mbarr
67−
68−-- | Return the size of the array in bytes.
69−{-# INLINE sizeOfMutableByteArray #-}
70−sizeOfMutableByteArray :: MutByteArray -> IO Int
71−sizeOfMutableByteArray (MutByteArray arr) =
72− IO $ \s ->
73− case getSizeofMutableByteArray# arr s of
74− (# s1, i #) -> (# s1, I# i #)
75−
76−{-# INLINE touch #-}
77−touch :: MutByteArray -> IO ()
78−touch (MutByteArray contents) =
79− IO $ \s -> case touch# contents s of s' -> (# s', () #)
80−
81−-- XXX We can provide another API for "unsafe" FFI calls passing an unlifted
82−-- pointer to the FFI call. For unsafe calls we do not need to pin the array.
83−-- We can pass an unlifted pointer to the FFI routine to avoid GC kicking in
84−-- before the pointer is wrapped.
85--
54--
86−-- From the GHC manual:
55+-- Even a non-functional change such as changing the order of constructors will
56+-- change the instance derivation.
87--
57--
88−-- GHC, since version 8.4, guarantees that garbage collection will never occur
58+-- This will not pose a problem if both, encode, and decode are done by the same
89−-- during an unsafe call, even in the bytecode interpreter, and further
59+-- version of the application. There *might* be a problem if version that
90−-- guarantees that unsafe calls will be performed in the calling thread. Making
60+-- encodes differs from the version that decodes.
91−-- it safe to pass heap-allocated objects to unsafe functions.
61+--
92−
62+-- We need to add some compatibility tests using different versions of
93−-- | Use a @MutByteArray@ as @Ptr a@. This is useful when we want to pass
63+-- dependencies.
94−-- an array as a pointer to some operating system call or to a "safe" FFI call.
95--
64--
96−-- If the array is not pinned it is copied to pinned memory before passing it
65+-- Although such chages for the most basic types won't happen we need to detect
97−-- to the monadic action.
66+-- if it ever happens.
98--
67--
99−-- /Performance Notes:/ Forces a copy if the array is not pinned. It is advised
68+-- Should we worry about these kind of changes and this kind of compatibility?
100−-- that the programmer keeps this in mind and creates a pinned array
69+-- This is a problem for all types of derivations that depend on the order of
101−-- opportunistically before this operation occurs, to avoid the cost of a copy
70+-- constructors, for example, Enum.
102−-- if possible.
71+
72+-- Note on Windows build
73+-- =====================
74+--
75+-- On Windows, having template haskell splices here fail the build with the
76+-- following error:
103--
77--
104−-- /Unsafe/ because of direct pointer operations. The user must ensure that
78+-- @
105−-- they are writing within the legal bounds of the array.
79+-- addLibrarySearchPath: C:\... (Win32 error 3): The system cannot find the path specified.
80+-- @
106--
81--
107−-- /Pre-release/
82+-- The error might be irrelavant but having these splices triggers it. We should
83+-- either fix the problem or avoid the use to template haskell splices in this
84+-- file.
108--
85--
109−{-# INLINE asPtrUnsafe #-}
86+-- Similar issue: https://github.com/haskell/cabal/issues/4741
110−asPtrUnsafe :: MonadIO m => MutByteArray -> (Ptr a -> m b) -> m b
87+
111−asPtrUnsafe arr f = do
88+-- $(Serialize.deriveSerialize ''Maybe)
112− contents <- liftIO $ pin arr
89+instance Serialize a => Serialize (Maybe a) where
113− let !ptr = Ptr (byteArrayContents#
90+
114− (unsafeCoerce# (getMutableByteArray# contents)))
91+ {-# INLINE addSizeTo #-}
115− r <- f ptr
92+ addSizeTo acc x =
116− liftIO $ touch contents
93+ case x of
117− return r
94+ Nothing -> (acc + 1)
95+ Just field0 -> (addSizeTo (acc + 1)) field0
96+
97+ {-# INLINE deserializeAt #-}
98+ deserializeAt initialOffset arr endOffset = do
99+ (i0, tag) <- ((deserializeAt initialOffset) arr) endOffset
100+ case tag :: Word8 of
101+ 0 -> pure (i0, Nothing)
102+ 1 -> do (i1, a0) <- ((deserializeAt i0) arr) endOffset
103+ pure (i1, Just a0)
104+ _ -> error "Found invalid tag while peeking (Maybe a)"
105+
106+ {-# INLINE serializeAt #-}
107+ serializeAt initialOffset arr val =
108+ case val of
109+ Nothing -> do
110+ i0 <- ((serializeAt initialOffset) arr) (0 :: Word8)
111+ pure i0
112+ Just field0 -> do
113+ i0 <- ((serializeAt initialOffset) arr) (1 :: Word8)
114+ i1 <- ((serializeAt i0) arr) field0
115+ pure i1
116+
117+-- $(Serialize.deriveSerialize ''Either)
118+instance (Serialize a, Serialize b) => Serialize (Either a b) where
119+
120+ {-# INLINE addSizeTo #-}
121+ addSizeTo acc x =
122+ case x of
123+ Left field0 -> (addSizeTo (acc + 1)) field0
124+ Right field0 -> (addSizeTo (acc + 1)) field0
125+
126+ {-# INLINE deserializeAt #-}
127+ deserializeAt initialOffset arr endOffset = do
128+ (i0, tag) <- ((deserializeAt initialOffset) arr) endOffset
129+ case tag :: Word8 of
130+ 0 -> do (i1, a0) <- ((deserializeAt i0) arr) endOffset
131+ pure (i1, Left a0)
132+ 1 -> do (i1, a0) <- ((deserializeAt i0) arr) endOffset
133+ pure (i1, Right a0)
134+ _ -> error "Found invalid tag while peeking (Either a b)"
135+
136+ {-# INLINE serializeAt #-}
137+ serializeAt initialOffset arr val =
138+ case val of
139+ Left field0 -> do
140+ i0 <- ((serializeAt initialOffset) arr) (0 :: Word8)
141+ i1 <- ((serializeAt i0) arr) field0
142+ pure i1
143+ Right field0 -> do
144+ i0 <- ((serializeAt initialOffset) arr) (1 :: Word8)
145+ i1 <- ((serializeAt i0) arr) field0
146+ pure i1
147+
148+instance Serialize (Proxy a) where
149+
150+ {-# INLINE addSizeTo #-}
151+ addSizeTo acc _ = (acc + 1)
152+
153+ {-# INLINE deserializeAt #-}
154+ deserializeAt initialOffset _ _ = pure ((initialOffset + 1), Proxy)
155+
156+ {-# INLINE serializeAt #-}
157+ serializeAt initialOffset _ _ = pure (initialOffset + 1)
118
158
119--------------------------------------------------------------------------------
159--------------------------------------------------------------------------------
120−-- Creation
160+-- Integer
121--------------------------------------------------------------------------------
161--------------------------------------------------------------------------------
122
162
123−{-# NOINLINE nil #-}
163+data LiftedInteger
124−nil :: MutByteArray
164+ = LIS Int
125−nil = unsafePerformIO $ newByteArray 0
165+ | LIP (Array Word)
126−
166+ | LIN (Array Word)
127−{-# INLINE newByteArray #-}
167+
128−newByteArray :: Int -> IO MutByteArray
168+-- $(Serialize.deriveSerialize ''LiftedInteger)
129−newByteArray nbytes | nbytes < 0 =
169+instance Serialize LiftedInteger where
130− errorWithoutStackTrace "newByteArray: size must be >= 0"
170+
131−newByteArray (I# nbytes) = IO $ \s ->
171+ {-# INLINE addSizeTo #-}
132− case newByteArray# nbytes s of
172+ addSizeTo acc x =
133− (# s', mbarr# #) ->
173+ case x of
134− let c = MutByteArray mbarr#
174+ LIS field0 -> (addSizeTo (acc + 1)) field0
135− in (# s', c #)
175+ LIP field0 -> (addSizeTo (acc + 1)) field0
136−
176+ LIN field0 -> (addSizeTo (acc + 1)) field0
137−{-# INLINE pinnedNewByteArray #-}
177+
138−pinnedNewByteArray :: Int -> IO MutByteArray
178+ {-# INLINE deserializeAt #-}
139−pinnedNewByteArray nbytes | nbytes < 0 =
179+ deserializeAt initialOffset arr endOffset = do
140− errorWithoutStackTrace "pinnedNewByteArray: size must be >= 0"
180+ (i0, tag) <- ((deserializeAt initialOffset) arr) endOffset
141−pinnedNewByteArray (I# nbytes) = IO $ \s ->
181+ case tag :: Word8 of
142− case newPinnedByteArray# nbytes s of
182+ 0 -> do (i1, a0) <- ((deserializeAt i0) arr) endOffset
143− (# s', mbarr# #) ->
183+ pure (i1, LIS a0)
144− let c = MutByteArray mbarr#
184+ 1 -> do (i1, a0) <- ((deserializeAt i0) arr) endOffset
145− in (# s', c #)
185+ pure (i1, LIP a0)
146−
186+ 2 -> do (i1, a0) <- ((deserializeAt i0) arr) endOffset
147−{-# INLINE pinnedNewAlignedBytes #-}
187+ pure (i1, LIN a0)
148−pinnedNewAlignedBytes :: Int -> Int -> IO MutByteArray
188+ _ -> error "Found invalid tag while peeking (LiftedInteger)"
149−pinnedNewAlignedBytes nbytes _align | nbytes < 0 =
189+
150− errorWithoutStackTrace "pinnedNewAlignedBytes: size must be >= 0"
190+ {-# INLINE serializeAt #-}
151−pinnedNewAlignedBytes (I# nbytes) (I# align) = IO $ \s ->
191+ serializeAt initialOffset arr val =
152− case newAlignedPinnedByteArray# nbytes align s of
192+ case val of
153− (# s', mbarr# #) ->
193+ LIS field0 -> do
154− let c = MutByteArray mbarr#
194+ i0 <- ((serializeAt initialOffset) arr) (0 :: Word8)
155− in (# s', c #)
195+ i1 <- ((serializeAt i0) arr) field0
156−
196+ pure i1
157−{-# INLINE newBytesAs #-}
197+ LIP field0 -> do
158−newBytesAs :: PinnedState -> Int -> IO MutByteArray
198+ i0 <- ((serializeAt initialOffset) arr) (1 :: Word8)
159−newBytesAs Unpinned = newByteArray
199+ i1 <- ((serializeAt i0) arr) field0
160−newBytesAs Pinned = pinnedNewByteArray
200+ pure i1
161−
201+ LIN field0 -> do
162−-------------------------------------------------------------------------------
202+ i0 <- ((serializeAt initialOffset) arr) (2 :: Word8)
163−-- Copying
203+ i1 <- ((serializeAt i0) arr) field0
164−-------------------------------------------------------------------------------
204+ pure i1
165−
205+
166−-- | Put a sub range of a source array into a subrange of a destination array.
206+#if __GLASGOW_HASKELL__ >= 900
167−-- This is not safe as it does not check the bounds.
207+
168−{-# INLINE putSliceUnsafe #-}
208+{-# INLINE liftInteger #-}
169−putSliceUnsafe ::
209+liftInteger :: Integer -> LiftedInteger
170− MonadIO m
210+liftInteger (IS x) = LIS (I# x)
171− => MutByteArray
211+liftInteger (IP x) =
172− -> Int
212+ LIP (Array (MutByteArray (unsafeCoerce# x)) 0 (I# (sizeofByteArray# x)))
173− -> MutByteArray
213+liftInteger (IN x) =
174− -> Int
214+ LIN (Array (MutByteArray (unsafeCoerce# x)) 0 (I# (sizeofByteArray# x)))
175− -> Int
215+
176− -> m ()
216+{-# INLINE unliftInteger #-}
177−putSliceUnsafe src srcStartBytes dst dstStartBytes lenBytes = liftIO $ do
217+unliftInteger :: LiftedInteger -> Integer
178−#ifdef DEBUG
218+unliftInteger (LIS (I# x)) = IS x
179− srcLen <- sizeOfMutableByteArray src
219+unliftInteger (LIP (Array (MutByteArray x) _ _)) = IP (unsafeCoerce# x)
180− dstLen <- sizeOfMutableByteArray src
220+unliftInteger (LIN (Array (MutByteArray x) _ _)) = IN (unsafeCoerce# x)
181− when (srcLen - srcStartBytes < lenBytes) $ error "SRC: Insufficient length."
221+
182− when (dstLen - dstStartBytes < lenBytes) $ error "DST: Insufficient length."
222+#else
183−#endif
223+
184− let !(I# srcStartBytes#) = srcStartBytes
185− !(I# dstStartBytes#) = dstStartBytes
186− !(I# lenBytes#) = lenBytes
187− let arrS# = getMutableByteArray# src
188− arrD# = getMutableByteArray# dst
189− IO $ \s# -> (# copyMutableByteArray#
190− arrS# srcStartBytes# arrD# dstStartBytes# lenBytes# s#
191− , () #)
192−
193−-------------------------------------------------------------------------------
194−-- Pinning & Unpinning
195−-------------------------------------------------------------------------------
196−
197−{-# INLINE isPinned #-}
198−isPinned :: MutByteArray -> Bool
199−isPinned (MutByteArray arr#) =
200− let pinnedInt = I# (isMutableByteArrayPinned# arr#)
201− in pinnedInt /= 0
202−
203−
204−{-# INLINE cloneMutableArrayWith# #-}
205−cloneMutableArrayWith#
206− :: (Int# -> State# RealWorld -> (# State# RealWorld
207− , MutableByteArray# RealWorld #))
208− -> MutableByteArray# RealWorld
209− -> State# RealWorld
210− -> (# State# RealWorld, MutableByteArray# RealWorld #)
211−cloneMutableArrayWith# alloc# arr# s# =
212− case getSizeofMutableByteArray# arr# s# of
213− (# s1#, i# #) ->
214− case alloc# i# s1# of
215− (# s2#, arr1# #) ->
216− case copyMutableByteArray# arr# 0# arr1# 0# i# s2# of
217− s3# -> (# s3#, arr1# #)
218−
219−{-# INLINE pin #-}
224+{-# INLINE liftInteger #-}
220−pin :: MutByteArray -> IO MutByteArray
225+liftInteger :: Integer -> LiftedInteger
221−pin arr@(MutByteArray marr#) =
226+liftInteger (S# x) = LIS (I# x)
222− if isPinned arr
227+liftInteger (Jp# (BN# x)) =
223− then return arr
228+ LIP (Array (MutByteArray (unsafeCoerce# x)) 0 (I# (sizeofByteArray# x)))
224− else
229+liftInteger (Jn# (BN# x)) =
225−#ifdef DEBUG
230+ LIN (Array (MutByteArray (unsafeCoerce# x)) 0 (I# (sizeofByteArray# x)))
226− do
231+
227− -- XXX dump stack trace
228− trace ("pin: Copying array") (return ())
229−#endif
230− IO
231− $ \s# ->
232− case cloneMutableArrayWith# newPinnedByteArray# marr# s# of
233− (# s1#, marr1# #) -> (# s1#, MutByteArray marr1# #)
234−
235−{-# INLINE unpin #-}
232+{-# INLINE unliftInteger #-}
236−unpin :: MutByteArray -> IO MutByteArray
233+unliftInteger :: LiftedInteger -> Integer
237−unpin arr@(MutByteArray marr#) =
234+unliftInteger (LIS (I# x)) = S# x
238− if not (isPinned arr)
235+unliftInteger (LIP (Array (MutByteArray x) _ _)) =
239− then return arr
236+ Jp# (BN# (unsafeCoerce# x))
240− else
237+unliftInteger (LIN (Array (MutByteArray x) _ _)) =
241−#ifdef DEBUG
238+ Jn# (BN# (unsafeCoerce# x))
242− do
239+
243− -- XXX dump stack trace
244− trace ("unpin: Copying array") (return ())
245#endif
240#endif
246− IO
241+
247− $ \s# ->
242+instance Serialize Integer where
248− case cloneMutableArrayWith# newByteArray# marr# s# of
243+ {-# INLINE addSizeTo #-}
249− (# s1#, marr1# #) -> (# s1#, MutByteArray marr1# #)
244+ addSizeTo i a = addSizeTo i (liftInteger a)
245+
246+ {-# INLINE deserializeAt #-}
247+ deserializeAt off arr end =
248+ fmap unliftInteger <$> deserializeAt off arr end
249+
250+ {-# INLINE serializeAt #-}
251+ serializeAt off arr val = serializeAt off arr (liftInteger val)
core/src/Streamly/Internal/Data/MutByteArray/Type.hsnew file+321
@@ -0,0 +1,321 @@
1+{-# LANGUAGE UnboxedTuples #-}
2+
3+-- |
4+-- Module : Streamly.Internal.Data.MutByteArray.Type
5+-- Copyright : (c) 2023 Composewell Technologies
6+-- License : BSD3-3-Clause
7+-- Maintainer : streamly@composewell.com
8+-- Stability : experimental
9+-- Portability : GHC
10+--
11+module Streamly.Internal.Data.MutByteArray.Type
12+ (
13+ -- ** MutByteArray
14+ MutByteArray(..)
15+ , MutableByteArray
16+ , getMutableByteArray# -- XXX getMutByteArray#
17+
18+ -- ** Pinning
19+ , PinnedState(..)
20+ , isPinned
21+ , pin
22+ , unpin
23+
24+ -- ** Allocation
25+ , empty
26+ , newBytesAs -- XXX should be removed
27+ , new
28+ , pinnedNew
29+ , pinnedNewAlignedBytes -- XXX should be removed
30+
31+ -- ** Access
32+ , sizeOfMutableByteArray -- XXX length
33+ , putSliceUnsafe
34+ , cloneSliceUnsafeAs
35+ , cloneSliceUnsafe
36+ , pinnedCloneSliceUnsafe
37+ , unsafePinnedAsPtr
38+ , unsafeAsPtr
39+
40+ -- ** Deprecated
41+ , asPtrUnsafe
42+ , nil
43+ ) where
44+
45+import Control.Monad.IO.Class (MonadIO(..))
46+#ifdef DEBUG
47+import Control.Monad (when)
48+import Debug.Trace (trace)
49+#endif
50+import GHC.Base (IO(..))
51+import System.IO.Unsafe (unsafePerformIO)
52+
53+import GHC.Exts
54+
55+--------------------------------------------------------------------------------
56+-- The ArrayContents type
57+--------------------------------------------------------------------------------
58+
59+data PinnedState
60+ = Pinned
61+ | Unpinned deriving (Show, Eq)
62+
63+-- XXX can use UnliftedNewtypes
64+
65+-- | A lifted mutable byte array type wrapping @MutableByteArray# RealWorld@.
66+-- This is a low level array used to back high level unboxed arrays and
67+-- serialized data.
68+data MutByteArray = MutByteArray (MutableByteArray# RealWorld)
69+
70+{-# DEPRECATED MutableByteArray "Please use MutByteArray instead" #-}
71+type MutableByteArray = MutByteArray
72+
73+{-# INLINE getMutableByteArray# #-}
74+getMutableByteArray# :: MutByteArray -> MutableByteArray# RealWorld
75+getMutableByteArray# (MutByteArray mbarr) = mbarr
76+
77+-- | Return the size of the array in bytes.
78+{-# INLINE sizeOfMutableByteArray #-}
79+sizeOfMutableByteArray :: MutByteArray -> IO Int
80+sizeOfMutableByteArray (MutByteArray arr) =
81+ IO $ \s ->
82+ case getSizeofMutableByteArray# arr s of
83+ (# s1, i #) -> (# s1, I# i #)
84+
85+{-# INLINE touch #-}
86+touch :: MutByteArray -> IO ()
87+touch (MutByteArray contents) =
88+ IO $ \s -> case touch# contents s of s' -> (# s', () #)
89+
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
94+-- 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
96+-- before the pointer is wrapped.
97+--
98+-- From the GHC manual:
99+--
100+-- GHC, since version 8.4, guarantees that garbage collection will never occur
101+-- during an unsafe call, even in the bytecode interpreter, and further
102+-- guarantees that unsafe calls will be performed in the calling thread. Making
103+-- it safe to pass heap-allocated objects to unsafe functions.
104+
105+-- | Use a @MutByteArray@ as @Ptr a@. This is useful when we want to pass
106+-- an array as a pointer to some operating system call or to a "safe" FFI call.
107+--
108+-- If the array is not pinned it is copied to pinned memory before passing it
109+-- to the monadic action.
110+--
111+-- /Performance Notes:/ Forces a copy if the array is not pinned. It is advised
112+-- that the programmer keeps this in mind and creates a pinned array
113+-- opportunistically before this operation occurs, to avoid the cost of a copy
114+-- if possible.
115+--
116+-- /Unsafe/ because of direct pointer operations. The user must ensure that
117+-- they are writing within the legal bounds of the array.
118+--
119+-- /Pre-release/
120+--
121+{-# INLINE unsafePinnedAsPtr #-}
122+unsafePinnedAsPtr :: MonadIO m => MutByteArray -> (Ptr a -> m b) -> m b
123+unsafePinnedAsPtr arr f = do
124+ contents <- liftIO $ pin arr
125+ let !ptr = Ptr (byteArrayContents#
126+ (unsafeCoerce# (getMutableByteArray# contents)))
127+ r <- f ptr
128+ liftIO $ touch contents
129+ return r
130+
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+--------------------------------------------------------------------------------
147+-- Creation
148+--------------------------------------------------------------------------------
149+
150+{-# NOINLINE empty #-}
151+empty :: MutByteArray
152+empty = unsafePerformIO $ new 0
153+
154+{-# DEPRECATED nil "Please use empty instead" #-}
155+nil :: MutByteArray
156+nil = empty
157+
158+-- XXX add "newRounded" to round up the large size to the next page boundary
159+-- and return the allocated size.
160+{-# INLINE new #-}
161+new :: Int -> IO MutByteArray
162+new nbytes | nbytes < 0 =
163+ errorWithoutStackTrace "newByteArray: size must be >= 0"
164+new (I# nbytes) = IO $ \s ->
165+ case newByteArray# nbytes s of
166+ (# s', mbarr# #) ->
167+ let c = MutByteArray mbarr#
168+ in (# s', c #)
169+
170+{-# INLINE pinnedNew #-}
171+pinnedNew :: Int -> IO MutByteArray
172+pinnedNew nbytes | nbytes < 0 =
173+ errorWithoutStackTrace "pinnedNew: size must be >= 0"
174+pinnedNew (I# nbytes) = IO $ \s ->
175+ case newPinnedByteArray# nbytes s of
176+ (# s', mbarr# #) ->
177+ let c = MutByteArray mbarr#
178+ in (# s', c #)
179+
180+{-# INLINE pinnedNewAlignedBytes #-}
181+pinnedNewAlignedBytes :: Int -> Int -> IO MutByteArray
182+pinnedNewAlignedBytes nbytes _align | nbytes < 0 =
183+ errorWithoutStackTrace "pinnedNewAlignedBytes: size must be >= 0"
184+pinnedNewAlignedBytes (I# nbytes) (I# align) = IO $ \s ->
185+ case newAlignedPinnedByteArray# nbytes align s of
186+ (# s', mbarr# #) ->
187+ let c = MutByteArray mbarr#
188+ in (# s', c #)
189+
190+{-# INLINE newBytesAs #-}
191+newBytesAs :: PinnedState -> Int -> IO MutByteArray
192+newBytesAs Unpinned = new
193+newBytesAs Pinned = pinnedNew
194+
195+-------------------------------------------------------------------------------
196+-- Copying
197+-------------------------------------------------------------------------------
198+
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.
206+-- This is not safe as it does not check the bounds of neither the src array
207+-- nor the destination array.
208+{-# INLINE putSliceUnsafe #-}
209+putSliceUnsafe ::
210+ MonadIO m
211+ => MutByteArray
212+ -> Int
213+ -> MutByteArray
214+ -> Int
215+ -> Int
216+ -> m ()
217+putSliceUnsafe src srcStartBytes dst dstStartBytes lenBytes = liftIO $ do
218+#ifdef DEBUG
219+ srcLen <- sizeOfMutableByteArray src
220+ dstLen <- sizeOfMutableByteArray dst
221+ when (srcLen - srcStartBytes < lenBytes)
222+ $ error $ "putSliceUnsafe: src overflow: start" ++ show srcStartBytes
223+ ++ " end " ++ show srcLen ++ " len " ++ show lenBytes
224+ when (dstLen - dstStartBytes < lenBytes)
225+ $ error $ "putSliceUnsafe: dst overflow: start" ++ show dstStartBytes
226+ ++ " end " ++ show dstLen ++ " len " ++ show lenBytes
227+#endif
228+ let !(I# srcStartBytes#) = srcStartBytes
229+ !(I# dstStartBytes#) = dstStartBytes
230+ !(I# lenBytes#) = lenBytes
231+ let arrS# = getMutableByteArray# src
232+ arrD# = getMutableByteArray# dst
233+ IO $ \s# -> (# copyMutableByteArray#
234+ arrS# srcStartBytes# arrD# dstStartBytes# lenBytes# s#
235+ , () #)
236+
237+-- | Unsafe as it does not check whether the start offset and length supplied
238+-- are valid inside the array.
239+{-# INLINE cloneSliceUnsafeAs #-}
240+cloneSliceUnsafeAs :: MonadIO m =>
241+ PinnedState -> Int -> Int -> MutByteArray -> m MutByteArray
242+cloneSliceUnsafeAs ps srcOff srcLen src =
243+ liftIO $ do
244+ mba <- newBytesAs ps srcLen
245+ putSliceUnsafe src srcOff mba 0 srcLen
246+ return mba
247+
248+-- | @cloneSliceUnsafe offset len arr@ clones a slice of the supplied array
249+-- starting at the given offset and equal to the given length.
250+{-# INLINE cloneSliceUnsafe #-}
251+cloneSliceUnsafe :: MonadIO m => Int -> Int -> MutByteArray -> m MutByteArray
252+cloneSliceUnsafe = cloneSliceUnsafeAs Unpinned
253+
254+-- | @pinnedCloneSliceUnsafe offset len arr@
255+{-# INLINE pinnedCloneSliceUnsafe #-}
256+pinnedCloneSliceUnsafe :: MonadIO m =>
257+ Int -> Int -> MutByteArray -> m MutByteArray
258+pinnedCloneSliceUnsafe = cloneSliceUnsafeAs Pinned
259+
260+-------------------------------------------------------------------------------
261+-- Pinning & Unpinning
262+-------------------------------------------------------------------------------
263+
264+-- | Return 'True' if the array is allocated in pinned memory.
265+{-# INLINE isPinned #-}
266+isPinned :: MutByteArray -> Bool
267+isPinned (MutByteArray arr#) =
268+ let pinnedInt = I# (isMutableByteArrayPinned# arr#)
269+ in pinnedInt /= 0
270+
271+
272+{-# INLINE cloneMutableArrayWith# #-}
273+cloneMutableArrayWith#
274+ :: (Int# -> State# RealWorld -> (# State# RealWorld
275+ , MutableByteArray# RealWorld #))
276+ -> MutableByteArray# RealWorld
277+ -> State# RealWorld
278+ -> (# State# RealWorld, MutableByteArray# RealWorld #)
279+cloneMutableArrayWith# alloc# arr# s# =
280+ case getSizeofMutableByteArray# arr# s# of
281+ (# s1#, i# #) ->
282+ case alloc# i# s1# of
283+ (# s2#, arr1# #) ->
284+ case copyMutableByteArray# arr# 0# arr1# 0# i# s2# of
285+ s3# -> (# s3#, arr1# #)
286+
287+-- | Return a copy of the array in pinned memory if unpinned, else return the
288+-- original array.
289+{-# INLINE pin #-}
290+pin :: MutByteArray -> IO MutByteArray
291+pin arr@(MutByteArray marr#) =
292+ if isPinned arr
293+ then return arr
294+ else
295+#ifdef DEBUG
296+ do
297+ -- XXX dump stack trace
298+ trace ("pin: Copying array") (return ())
299+#endif
300+ IO
301+ $ \s# ->
302+ case cloneMutableArrayWith# newPinnedByteArray# marr# s# of
303+ (# s1#, marr1# #) -> (# s1#, MutByteArray marr1# #)
304+
305+-- | Return a copy of the array in unpinned memory if pinned, else return the
306+-- original array.
307+{-# INLINE unpin #-}
308+unpin :: MutByteArray -> IO MutByteArray
309+unpin arr@(MutByteArray marr#) =
310+ if not (isPinned arr)
311+ then return arr
312+ else
313+#ifdef DEBUG
314+ do
315+ -- XXX dump stack trace
316+ trace ("unpin: Copying array") (return ())
317+#endif
318+ IO
319+ $ \s# ->
320+ case cloneMutableArrayWith# newByteArray# marr# s# of
321+ (# s1#, marr1# #) -> (# s1#, MutByteArray marr1# #)
core/src/Streamly/Internal/Data/ParserK/Type.hs+1−1
@@ -404,7 +404,7 @@ adaptCWith pstep initial extract cont !offset0 !usedCount !input = do
404 go !_ !cur !pst | cur >= end =
404 go !_ !cur !pst | cur >= end =
405 onContinue ((end - start) `div` SIZE_OF(a)) pst
405 onContinue ((end - start) `div` SIZE_OF(a)) pst
406 go !_ !cur !pst = do
406 go !_ !cur !pst = do
407− let !x = unsafeInlineIO $ peekByteIndex cur contents
407+ let !x = unsafeInlineIO $ peekAt cur contents
408 pRes <- pstep pst x
408 pRes <- pstep pst x
409 let elemSize = SIZE_OF(a)
409 let elemSize = SIZE_OF(a)
410 next = INDEX_NEXT(cur,a)
410 next = INDEX_NEXT(cur,a)
core/src/Streamly/Internal/Data/Ring.hs+3−3
@@ -79,7 +79,7 @@ import Foreign.Storable
79import Foreign.ForeignPtr (ForeignPtr, withForeignPtr, touchForeignPtr)
79import Foreign.ForeignPtr (ForeignPtr, withForeignPtr, touchForeignPtr)
80import Foreign.ForeignPtr.Unsafe (unsafeForeignPtrToPtr)
80import Foreign.ForeignPtr.Unsafe (unsafeForeignPtrToPtr)
81import Foreign.Ptr (plusPtr, minusPtr, castPtr)
81import Foreign.Ptr (plusPtr, minusPtr, castPtr)
82−import Streamly.Internal.Data.Unbox as Unboxed (Unbox(peekByteIndex))
82+import Streamly.Internal.Data.Unbox as Unboxed (Unbox(peekAt))
83import GHC.ForeignPtr (mallocPlainForeignPtrAlignedBytes)
83import GHC.ForeignPtr (mallocPlainForeignPtrAlignedBytes)
84import GHC.Ptr (Ptr(..))
84import GHC.Ptr (Ptr(..))
85import Streamly.Internal.Data.MutArray.Type (MutArray)
85import Streamly.Internal.Data.MutArray.Type (MutArray)
@@ -411,7 +411,7 @@ unsafeEqArrayN Ring{..} rh A.Array{..} nBytes
411
411
412 check p i = do
412 check p i = do
413 (relem :: Word8) <- peek p
413 (relem :: Word8) <- peek p
414− aelem <- peekByteIndex i w8Contents
414+ aelem <- peekAt i w8Contents
415 if relem == aelem
415 if relem == aelem
416 then go (p `plusPtr` 1) (i + 1)
416 then go (p `plusPtr` 1) (i + 1)
417 else return False
417 else return False
@@ -445,7 +445,7 @@ unsafeEqArray Ring{..} rh A.Array{..} =
445
445
446 check p i = do
446 check p i = do
447 (relem :: Word8) <- peek p
447 (relem :: Word8) <- peek p
448− aelem <- peekByteIndex i w8Contents
448+ aelem <- peekAt i w8Contents
449 if relem == aelem
449 if relem == aelem
450 then go (p `plusPtr` 1) (i + 1)
450 then go (p `plusPtr` 1) (i + 1)
451 else return False
451 else return False
core/src/Streamly/Internal/Data/Serialize.hsdeleted−250
@@ -1,250 +0,0 @@
1−{-# LANGUAGE TemplateHaskell #-}
2−
3−-- This is required as all the instances in this module are orphan instances.
4−{-# OPTIONS_GHC -fno-warn-orphans #-}
5−
6−-- |
7−-- Module : Streamly.Internal.Data.Serialize
8−-- Copyright : (c) 2023 Composewell Technologies
9−-- License : BSD3-3-Clause
10−-- Maintainer : streamly@composewell.com
11−-- Portability : GHC
12−--
13−
14−module Streamly.Internal.Data.Serialize
15− (
16− -- * MutByteArray
17− module Streamly.Internal.Data.MutByteArray
18− -- * Unbox
19− , module Streamly.Internal.Data.Unbox
20− , module Streamly.Internal.Data.Unbox.TH
21− -- * Serialize
22− , module Streamly.Internal.Data.Serialize.Type
23− -- * Serialize TH
24− , module Streamly.Internal.Data.Serialize.TH
25− ) where
26−
27−--------------------------------------------------------------------------------
28−-- Imports
29−--------------------------------------------------------------------------------
30−
31−import Data.Proxy (Proxy(..))
32−import Streamly.Internal.Data.Array (Array(..))
33−import GHC.Exts (Int(..), sizeofByteArray#, unsafeCoerce#)
34−import GHC.Word (Word8)
35−
36−#if __GLASGOW_HASKELL__ >= 900
37−import GHC.Num.Integer (Integer(..))
38−#else
39−import GHC.Integer.GMP.Internals (Integer(..), BigNat(..))
40−#endif
41−
42−import Streamly.Internal.Data.MutByteArray
43−import Streamly.Internal.Data.Serialize.TH
44−import Streamly.Internal.Data.Serialize.Type
45−import Streamly.Internal.Data.Unbox
46−import Streamly.Internal.Data.Unbox.TH
47−
48−--------------------------------------------------------------------------------
49−-- Common instances
50−--------------------------------------------------------------------------------
51−
52−-- Note
53−-- ====
54−--
55−-- Even a non-functional change such as changing the order of constructors will
56−-- change the instance derivation.
57−--
58−-- This will not pose a problem if both, encode, and decode are done by the same
59−-- version of the application. There *might* be a problem if version that
60−-- encodes differs from the version that decodes.
61−--
62−-- We need to add some compatibility tests using different versions of
63−-- dependencies.
64−--
65−-- Although such chages for the most basic types won't happen we need to detect
66−-- if it ever happens.
67−--
68−-- Should we worry about these kind of changes and this kind of compatibility?
69−-- This is a problem for all types of derivations that depend on the order of
70−-- constructors, for example, Enum.
71−
72−-- Note on Windows build
73−-- =====================
74−--
75−-- On Windows, having template haskell splices here fail the build with the
76−-- following error:
77−--
78−-- @
79−-- addLibrarySearchPath: C:\... (Win32 error 3): The system cannot find the path specified.
80−-- @
81−--
82−-- The error might be irrelavant but having these splices triggers it. We should
83−-- either fix the problem or avoid the use to template haskell splices in this
84−-- file.
85−--
86−-- Similar issue: https://github.com/haskell/cabal/issues/4741
87−
88−-- $(Serialize.deriveSerialize ''Maybe)
89−instance Serialize a => Serialize (Maybe a) where
90−
91− {-# INLINE size #-}
92− size acc x =
93− case x of
94− Nothing -> (acc + 1)
95− Just field0 -> (size (acc + 1)) field0
96−
97− {-# INLINE deserialize #-}
98− deserialize initialOffset arr endOffset = do
99− (i0, tag) <- ((deserialize initialOffset) arr) endOffset
100− case tag :: Word8 of
101− 0 -> pure (i0, Nothing)
102− 1 -> do (i1, a0) <- ((deserialize i0) arr) endOffset
103− pure (i1, Just a0)
104− _ -> error "Found invalid tag while peeking (Maybe a)"
105−
106− {-# INLINE serialize #-}
107− serialize initialOffset arr val =
108− case val of
109− Nothing -> do
110− i0 <- ((serialize initialOffset) arr) (0 :: Word8)
111− pure i0
112− Just field0 -> do
113− i0 <- ((serialize initialOffset) arr) (1 :: Word8)
114− i1 <- ((serialize i0) arr) field0
115− pure i1
116−
117−-- $(Serialize.deriveSerialize ''Either)
118−instance (Serialize a, Serialize b) => Serialize (Either a b) where
119−
120− {-# INLINE size #-}
121− size acc x =
122− case x of
123− Left field0 -> (size (acc + 1)) field0
124− Right field0 -> (size (acc + 1)) field0
125−
126− {-# INLINE deserialize #-}
127− deserialize initialOffset arr endOffset = do
128− (i0, tag) <- ((deserialize initialOffset) arr) endOffset
129− case tag :: Word8 of
130− 0 -> do (i1, a0) <- ((deserialize i0) arr) endOffset
131− pure (i1, Left a0)
132− 1 -> do (i1, a0) <- ((deserialize i0) arr) endOffset
133− pure (i1, Right a0)
134− _ -> error "Found invalid tag while peeking (Either a b)"
135−
136− {-# INLINE serialize #-}
137− serialize initialOffset arr val =
138− case val of
139− Left field0 -> do
140− i0 <- ((serialize initialOffset) arr) (0 :: Word8)
141− i1 <- ((serialize i0) arr) field0
142− pure i1
143− Right field0 -> do
144− i0 <- ((serialize initialOffset) arr) (1 :: Word8)
145− i1 <- ((serialize i0) arr) field0
146− pure i1
147−
148−instance Serialize (Proxy a) where
149−
150− {-# INLINE size #-}
151− size acc _ = (acc + 1)
152−
153− {-# INLINE deserialize #-}
154− deserialize initialOffset _ _ = pure ((initialOffset + 1), Proxy)
155−
156− {-# INLINE serialize #-}
157− serialize initialOffset _ _ = pure (initialOffset + 1)
158−
159−--------------------------------------------------------------------------------
160−-- Integer
161−--------------------------------------------------------------------------------
162−
163−data LiftedInteger
164− = LIS Int
165− | LIP (Array Word)
166− | LIN (Array Word)
167−
168−-- $(Serialize.deriveSerialize ''LiftedInteger)
169−instance Serialize LiftedInteger where
170−
171− {-# INLINE size #-}
172− size acc x =
173− case x of
174− LIS field0 -> (size (acc + 1)) field0
175− LIP field0 -> (size (acc + 1)) field0
176− LIN field0 -> (size (acc + 1)) field0
177−
178− {-# INLINE deserialize #-}
179− deserialize initialOffset arr endOffset = do
180− (i0, tag) <- ((deserialize initialOffset) arr) endOffset
181− case tag :: Word8 of
182− 0 -> do (i1, a0) <- ((deserialize i0) arr) endOffset
183− pure (i1, LIS a0)
184− 1 -> do (i1, a0) <- ((deserialize i0) arr) endOffset
185− pure (i1, LIP a0)
186− 2 -> do (i1, a0) <- ((deserialize i0) arr) endOffset
187− pure (i1, LIN a0)
188− _ -> error "Found invalid tag while peeking (LiftedInteger)"
189−
190− {-# INLINE serialize #-}
191− serialize initialOffset arr val =
192− case val of
193− LIS field0 -> do
194− i0 <- ((serialize initialOffset) arr) (0 :: Word8)
195− i1 <- ((serialize i0) arr) field0
196− pure i1
197− LIP field0 -> do
198− i0 <- ((serialize initialOffset) arr) (1 :: Word8)
199− i1 <- ((serialize i0) arr) field0
200− pure i1
201− LIN field0 -> do
202− i0 <- ((serialize initialOffset) arr) (2 :: Word8)
203− i1 <- ((serialize i0) arr) field0
204− pure i1
205−
206−#if __GLASGOW_HASKELL__ >= 900
207−
208−{-# INLINE liftInteger #-}
209−liftInteger :: Integer -> LiftedInteger
210−liftInteger (IS x) = LIS (I# x)
211−liftInteger (IP x) =
212− LIP (Array (MutByteArray (unsafeCoerce# x)) 0 (I# (sizeofByteArray# x)))
213−liftInteger (IN x) =
214− LIN (Array (MutByteArray (unsafeCoerce# x)) 0 (I# (sizeofByteArray# x)))
215−
216−{-# INLINE unliftInteger #-}
217−unliftInteger :: LiftedInteger -> Integer
218−unliftInteger (LIS (I# x)) = IS x
219−unliftInteger (LIP (Array (MutByteArray x) _ _)) = IP (unsafeCoerce# x)
220−unliftInteger (LIN (Array (MutByteArray x) _ _)) = IN (unsafeCoerce# x)
221−
222−#else
223−
224−{-# INLINE liftInteger #-}
225−liftInteger :: Integer -> LiftedInteger
226−liftInteger (S# x) = LIS (I# x)
227−liftInteger (Jp# (BN# x)) =
228− LIP (Array (MutByteArray (unsafeCoerce# x)) 0 (I# (sizeofByteArray# x)))
229−liftInteger (Jn# (BN# x)) =
230− LIN (Array (MutByteArray (unsafeCoerce# x)) 0 (I# (sizeofByteArray# x)))
231−
232−{-# INLINE unliftInteger #-}
233−unliftInteger :: LiftedInteger -> Integer
234−unliftInteger (LIS (I# x)) = S# x
235−unliftInteger (LIP (Array (MutByteArray x) _ _)) =
236− Jp# (BN# (unsafeCoerce# x))
237−unliftInteger (LIN (Array (MutByteArray x) _ _)) =
238− Jn# (BN# (unsafeCoerce# x))
239−
240−#endif
241−
242−instance Serialize Integer where
243− {-# INLINE size #-}
244− size i a = size i (liftInteger a)
245−
246− {-# INLINE deserialize #-}
247− deserialize off arr end = fmap unliftInteger <$> deserialize off arr end
248−
249− {-# INLINE serialize #-}
250− serialize off arr val = serialize off arr (liftInteger val)
core/src/Streamly/Internal/Data/Serialize/TH.hs+44−31
@@ -7,7 +7,11 @@
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+
11module Streamly.Internal.Data.Serialize.TH
15module Streamly.Internal.Data.Serialize.TH
12 (
16 (
13 -- Deriving
17 -- Deriving
@@ -50,7 +54,7 @@ import Streamly.Internal.Data.Serialize.TH.RecHeader
50--------------------------------------------------------------------------------
54--------------------------------------------------------------------------------
51
55
52exprGetSize :: Q Exp -> (Int, Type) -> Q Exp
56exprGetSize :: Q Exp -> (Int, Type) -> Q Exp
53−exprGetSize acc (i, _) = [|size $(acc) $(varE (mkFieldName i))|]
57+exprGetSize acc (i, _) = [|addSizeTo $(acc) $(varE (mkFieldName i))|]
54
58
55getTagSize :: Int -> Int
59getTagSize :: Int -> Int
56getTagSize numConstructors
60getTagSize numConstructors
@@ -170,9 +174,9 @@ mkSizeDec (SerializeConfig {..}) headTy cons = do
170 pure
174 pure
171 ( maybe
175 ( maybe
172 []
176 []
173− (\x -> [PragmaD (InlineP 'size x FunLike AllPhases)])
177+ (\x -> [PragmaD (InlineP 'addSizeTo x FunLike AllPhases)])
174 cfgInlineSize
178 cfgInlineSize
175− ++ [FunD 'size [Clause [] (NormalB sizeOfMethod) []]]
179+ ++ [FunD 'addSizeTo [Clause [] (NormalB sizeOfMethod) []]]
176 )
180 )
177
181
178--------------------------------------------------------------------------------
182--------------------------------------------------------------------------------
@@ -192,7 +196,7 @@ mkDeserializeExpr True False headTy tyOfTy =
192 conLen <- newName "conLen"
196 conLen <- newName "conLen"
193 off1 <- newName "off1"
197 off1 <- newName "off1"
194 [|do ($(varP off1), $(varP conLen) :: Word8) <-
198 [|do ($(varP off1), $(varP conLen) :: Word8) <-
195− deserialize
199+ deserializeAt
196 $(varE _initialOffset)
200 $(varE _initialOffset)
197 $(varE _arr)
201 $(varE _arr)
198 $(varE _endOffset)
202 $(varE _endOffset)
@@ -212,18 +216,19 @@ mkDeserializeExpr True False headTy tyOfTy =
212 [|($(litIntegral lenCname) == $(varE conLen))
216 [|($(litIntegral lenCname) == $(varE conLen))
213 && $(xorCmp tag off _arr)|]
217 && $(xorCmp tag off _arr)|]
214 [|let $(varP (makeI 0)) = $(varE off) + $(litIntegral lenCname)
218 [|let $(varP (makeI 0)) = $(varE off) + $(litIntegral lenCname)
215− in $(mkDeserializeExprOne 'deserialize con)|]
219+ in $(mkDeserializeExprOne 'deserializeAt con)|]
216
220
217mkDeserializeExpr False False headTy tyOfTy =
221mkDeserializeExpr False False headTy tyOfTy =
218 case tyOfTy of
222 case tyOfTy of
219 -- Unit constructor
223 -- Unit constructor
224+ -- XXX Should we peek and check if the byte value is 0?
220 UnitType cname ->
225 UnitType cname ->
221 [|pure ($(varE _initialOffset) + 1, $(conE cname))|]
226 [|pure ($(varE _initialOffset) + 1, $(conE cname))|]
222 -- Product type
227 -- Product type
223 TheType con ->
228 TheType con ->
224 letE
229 letE
225 [valD (varP (mkName "i0")) (normalB (varE _initialOffset)) []]
230 [valD (varP (mkName "i0")) (normalB (varE _initialOffset)) []]
226− (mkDeserializeExprOne 'deserialize con)
231+ (mkDeserializeExprOne 'deserializeAt con)
227 -- Sum type
232 -- Sum type
228 MultiType cons -> do
233 MultiType cons -> do
229 let lenCons = length cons
234 let lenCons = length cons
@@ -231,7 +236,7 @@ mkDeserializeExpr False False headTy tyOfTy =
231 doE
236 doE
232 [ bindS
237 [ bindS
233 (tupP [varP (mkName "i0"), varP _tag])
238 (tupP [varP (mkName "i0"), varP _tag])
234− [|deserialize $(varE _initialOffset) $(varE _arr) $(varE _endOffset)|]
239+ [|deserializeAt $(varE _initialOffset) $(varE _arr) $(varE _endOffset)|]
235 , noBindS
240 , noBindS
236 (caseE
241 (caseE
237 (sigE (varE _tag) (conT tagType))
242 (sigE (varE _tag) (conT tagType))
@@ -241,7 +246,7 @@ mkDeserializeExpr False False headTy tyOfTy =
241 peekMatch (i, con) =
246 peekMatch (i, con) =
242 match
247 match
243 (litP (IntegerL i))
248 (litP (IntegerL i))
244− (normalB (mkDeserializeExprOne 'deserialize con)) []
249+ (normalB (mkDeserializeExprOne 'deserializeAt con)) []
245 peekErr =
250 peekErr =
246 match
251 match
247 wildP
252 wildP
@@ -279,11 +284,11 @@ mkDeserializeDec (SerializeConfig {..}) headTy cons = do
279 pure
284 pure
280 ( maybe
285 ( maybe
281 []
286 []
282− (\x -> [PragmaD (InlineP 'deserialize x FunLike AllPhases)])
287+ (\x -> [PragmaD (InlineP 'deserializeAt x FunLike AllPhases)])
283 cfgInlineDeserialize
288 cfgInlineDeserialize
284 ++
289 ++
285 [ FunD
290 [ FunD
286− 'deserialize
291+ 'deserializeAt
287 [ Clause
292 [ Clause
288 (if isUnitType cons && not cfgConstructorTagAsString
293 (if isUnitType cons && not cfgConstructorTagAsString
289 then [VarP _initialOffset, WildP, WildP]
294 then [VarP _initialOffset, WildP, WildP]
@@ -300,7 +305,7 @@ mkDeserializeDec (SerializeConfig {..}) headTy cons = do
300
305
301mkSerializeExprTag :: Name -> Int -> Q Exp
306mkSerializeExprTag :: Name -> Int -> Q Exp
302mkSerializeExprTag tagType tagVal =
307mkSerializeExprTag tagType tagVal =
303− [|serialize
308+ [|serializeAt
304 $(varE _initialOffset)
309 $(varE _initialOffset)
305 $(varE _arr)
310 $(varE _arr)
306 $((sigE (litE (IntegerL (fromIntegral tagVal))) (conT tagType)))|]
311 $((sigE (litE (IntegerL (fromIntegral tagVal))) (conT tagType)))|]
@@ -335,13 +340,14 @@ mkSerializeExpr True False tyOfTy =
335 (doE [ bindS
340 (doE [ bindS
336 (varP (mkName "i0"))
341 (varP (mkName "i0"))
337 (serializeW8List _initialOffset _arr conEnc)
342 (serializeW8List _initialOffset _arr conEnc)
338− , noBindS (mkSerializeExprFields 'serialize fields)
343+ , noBindS (mkSerializeExprFields 'serializeAt fields)
339 ])
344 ])
340
345
341mkSerializeExpr False False tyOfTy =
346mkSerializeExpr False False tyOfTy =
342 case tyOfTy of
347 case tyOfTy of
343 -- Unit type
348 -- Unit type
349+ UnitType _ ->
344− UnitType _ -> [|pure ($(varE _initialOffset) + 1)|]
350+ [|serializeAt $(varE _initialOffset) $(varE _arr) (0 :: Word8)|]
345 -- Product type
351 -- Product type
346 (TheType (SimpleDataCon cname fields)) ->
352 (TheType (SimpleDataCon cname fields)) ->
347 letE
353 letE
@@ -351,7 +357,7 @@ mkSerializeExpr False False tyOfTy =
351 [ matchConstructor
357 [ matchConstructor
352 cname
358 cname
353 (length fields)
359 (length fields)
354− (mkSerializeExprFields 'serialize fields)
360+ (mkSerializeExprFields 'serializeAt fields)
355 ])
361 ])
356 -- Sum type
362 -- Sum type
357 (MultiType cons) -> do
363 (MultiType cons) -> do
@@ -368,7 +374,7 @@ mkSerializeExpr False False tyOfTy =
368 (mkSerializeExprTag tagType tagVal)
374 (mkSerializeExprTag tagType tagVal)
369 , noBindS
375 , noBindS
370 (mkSerializeExprFields
376 (mkSerializeExprFields
371− 'serialize
377+ 'serializeAt
372 fields)
378 fields)
373 ]))
379 ]))
374 (zip [0 ..] cons))
380 (zip [0 ..] cons))
@@ -388,14 +394,14 @@ mkSerializeDec (SerializeConfig {..}) headTy cons = do
388 pure
394 pure
389 ( maybe
395 ( maybe
390 []
396 []
391− (\x -> [PragmaD (InlineP 'serialize x FunLike AllPhases)])
397+ (\x -> [PragmaD (InlineP 'serializeAt x FunLike AllPhases)])
392 cfgInlineSerialize
398 cfgInlineSerialize
393 ++
399 ++
394 [FunD
400 [FunD
395− 'serialize
401+ 'serializeAt
396 [ Clause
402 [ Clause
397 (if isUnitType cons && not cfgConstructorTagAsString
403 (if isUnitType cons && not cfgConstructorTagAsString
398− then [VarP _initialOffset, WildP, WildP]
404+ then [VarP _initialOffset, VarP _arr, WildP]
399 else [VarP _initialOffset, VarP _arr, VarP _val])
405 else [VarP _initialOffset, VarP _arr, VarP _val])
400 (NormalB pokeMethod)
406 (NormalB pokeMethod)
401 []
407 []
@@ -446,29 +452,36 @@ deriveSerializeInternal conf headTy cons next = do
446 let methods = concat [sizeDec, peekDec, pokeDec]
452 let methods = concat [sizeDec, peekDec, pokeDec]
447 next methods
453 next methods
448
454
449−-- | @deriveSerializeWith config instance-dec@ generates a template Haskell
455+-- | @deriveSerializeWith config-modifier instance-dec@ generates a template
450−-- splice consisting of a declaration of a 'Serialize' instance. @instance-dec@
456+-- Haskell splice consisting of a declaration of a 'Serialize' instance.
451−-- is a template Haskell declaration splice consisting of a standard Haskell
457+-- @instance-dec@ is a template Haskell declaration splice consisting of a
452−-- instance declaration without the type class methods (e.g. @[d|instance
458+-- standard Haskell instance declaration without the type class methods (e.g.
453−-- Serialize a => Serialize (Maybe a)|]@). The type class methods for the given
459+-- @[d|instance Serialize a => Serialize (Maybe a)|]@).
460+--
454−-- instance are generated according to the supplied @config@ parameter.
461+-- The type class methods for the given instance are generated according to the
462+-- supplied @config-modifier@ parameter. See 'SerializeConfig' for default
463+-- configuration settings.
455--
464--
456-- Usage:
465-- Usage:
457--
466--
458-- @
467-- @
459-- \$(deriveSerializeWith
468-- \$(deriveSerializeWith
460−-- serializeConfig
469+-- ( inlineSerializeAt (Just NoInline)
470+-- . inlineDeserializeAt (Just NoInline)
471+-- )
461-- [d|instance Serialize a => Serialize (Maybe a)|])
472-- [d|instance Serialize a => Serialize (Maybe a)|])
462-- @
473-- @
463−deriveSerializeWith :: SerializeConfig -> Q [Dec] -> Q [Dec]
474+deriveSerializeWith ::
475+ (SerializeConfig -> SerializeConfig) -> Q [Dec] -> Q [Dec]
464−deriveSerializeWith conf mDecs = do
476+deriveSerializeWith modifier mDecs = do
465 dec <- mDecs
477 dec <- mDecs
466 case dec of
478 case dec of
467 [InstanceD mo preds headTyWC []] -> do
479 [InstanceD mo preds headTyWC []] -> do
468 let headTy = unwrap dec headTyWC
480 let headTy = unwrap dec headTyWC
469 dt <- reifyDataType (getMainTypeName dec headTy)
481 dt <- reifyDataType (getMainTypeName dec headTy)
470 let cons = dtCons dt
482 let cons = dtCons dt
471− deriveSerializeInternal conf headTy cons (next mo preds headTyWC)
483+ deriveSerializeInternal
484+ (modifier serializeConfig) headTy cons (next mo preds headTyWC)
472 _ -> errorMessage dec
485 _ -> errorMessage dec
473
486
474 where
487 where
@@ -505,7 +518,7 @@ deriveSerializeWith conf mDecs = do
505-- @[d|instance Serialize a => Serialize (Maybe a)|]@), generate an instance
518-- @[d|instance Serialize a => Serialize (Maybe a)|]@), generate an instance
506-- declaration including all the type class method implementations.
519-- declaration including all the type class method implementations.
507--
520--
508−-- >>> deriveSerialize = deriveSerializeWith serializeConfig
521+-- >>> deriveSerialize = deriveSerializeWith id
509--
522--
510-- Usage:
523-- Usage:
511--
524--
@@ -514,4 +527,4 @@ deriveSerializeWith conf mDecs = do
514-- [d|instance Serialize a => Serialize (Maybe a)|])
527-- [d|instance Serialize a => Serialize (Maybe a)|])
515-- @
528-- @
516deriveSerialize :: Q [Dec] -> Q [Dec]
529deriveSerialize :: Q [Dec] -> Q [Dec]
517−deriveSerialize = deriveSerializeWith serializeConfig
530+deriveSerialize = deriveSerializeWith id
core/src/Streamly/Internal/Data/Serialize/TH/Bottom.hs+50−44
@@ -13,9 +13,9 @@ module Streamly.Internal.Data.Serialize.TH.Bottom
13 -- ** Config
13 -- ** Config
14 SerializeConfig(..)
14 SerializeConfig(..)
15 , serializeConfig
15 , serializeConfig
16− , inlineSize
16+ , inlineAddSizeTo
17− , inlineSerialize
17+ , inlineSerializeAt
18− , inlineDeserialize
18+ , inlineDeserializeAt
19 , encodeConstrNames
19 , encodeConstrNames
20 , encodeRecordFields
20 , encodeRecordFields
21
21
@@ -80,11 +80,22 @@ import Streamly.Internal.Data.Unbox.TH (DataCon(..))
80-- no inline specific pragma let's GHC decide whether to put the code in
80-- no inline specific pragma let's GHC decide whether to put the code in
81-- interface file or not.
81-- interface file or not.
82
82
83−-- | Configuration to control how the 'Serialize' instance is generated. Use
83+-- | Configuration to control how the 'Serialize' instance is generated. The
84−-- 'serializeConfig' and config setter functions to generate desired Config. For
84+-- configuration is opaque and is modified by composing config modifier
85−-- example:
85+-- functions, for example:
86--
86--
87−-- >>> (inlineSize (Just Inline)) . (inlineSerialize (Just Inlinable)) serializeConfig
87+-- >>> (inlineSerializeAt (Just NoInline)) . (inlineSerializeAt (Just Inlinable))
88+--
89+-- The default configuration settings are:
90+--
91+-- * 'inlineAddSizeTo' Nothing
92+-- * 'inlineSerializeAt' (Just Inline)
93+-- * 'inlineDeserializeAt' (Just Inline)
94+--
95+-- The following experimental options are also available:
96+--
97+-- * 'encodeConstrNames' False
98+-- * 'encodeRecordFields' False
88--
99--
89data SerializeConfig =
100data SerializeConfig =
90 SerializeConfig
101 SerializeConfig
@@ -95,51 +106,52 @@ data SerializeConfig =
95 , cfgRecordSyntaxWithHeader :: Bool
106 , cfgRecordSyntaxWithHeader :: Bool
96 }
107 }
97
108
98−-- | How should we inline the 'size' function? The default in 'serializeConfig'
109+-- | How should we inline the 'addSizeTo' function? The default is 'Nothing'
99−-- is 'Nothing' which means left to the compiler. Forcing inline on @size@
110+-- which means left to the compiler. Forcing inline on @addSizeTo@ function
100−-- function actually worsens some benchmarks and improves none.
111+-- actually worsens some benchmarks and improves none.
101−inlineSize :: Maybe Inline -> SerializeConfig -> SerializeConfig
112+inlineAddSizeTo :: Maybe Inline -> SerializeConfig -> SerializeConfig
102−inlineSize v cfg = cfg {cfgInlineSize = v}
113+inlineAddSizeTo v cfg = cfg {cfgInlineSize = v}
103
114
104-- XXX Should we make the default Inlinable instead?
115-- XXX Should we make the default Inlinable instead?
105
116
106−-- | How should we inline the 'serialize' function? The default in
117+-- | How should we inline the 'serialize' function? The default 'Just Inline'.
107−-- 'serializeConfig' is 'Just Inline'. However, aggressive inlining can bloat
118+-- However, aggressive inlining can bloat the code and increase in compilation
108−-- the code and increase in compilation times when there are big functions and
119+-- times when there are big functions and too many nesting levels so you can
109−-- too many nesting levels so you can change it accordingly. A 'Nothing' value
120+-- change it accordingly. A 'Nothing' value leaves the decision to the
110−-- leaves the decision to the compiler.
121+-- compiler.
111−inlineSerialize :: Maybe Inline -> SerializeConfig -> SerializeConfig
122+inlineSerializeAt :: Maybe Inline -> SerializeConfig -> SerializeConfig
112−inlineSerialize v cfg = cfg {cfgInlineSerialize = v}
123+inlineSerializeAt v cfg = cfg {cfgInlineSerialize = v}
113
124
114-- XXX Should we make the default Inlinable instead?
125-- XXX Should we make the default Inlinable instead?
115
126
116-- | How should we inline the 'deserialize' function? See guidelines in
127-- | How should we inline the 'deserialize' function? See guidelines in
117−-- 'inlineSerialize'.
128+-- 'inlineSerializeAt'.
118−inlineDeserialize :: Maybe Inline -> SerializeConfig -> SerializeConfig
129+inlineDeserializeAt :: Maybe Inline -> SerializeConfig -> SerializeConfig
119−inlineDeserialize v cfg = cfg {cfgInlineDeserialize = v}
130+inlineDeserializeAt v cfg = cfg {cfgInlineDeserialize = v}
120
131
121-- | __Experimental__
132-- | __Experimental__
122--
133--
123-- In sum types, use Latin-1 encoded original constructor names rather than
134-- In sum types, use Latin-1 encoded original constructor names rather than
124−-- binary values to identify constructors. This option is not useful on a
135+-- binary values to identify constructors. This option is not applicable to
125−-- product type.
136+-- product types.
126--
137--
127−-- This option enables the following backward compatible behavior:
138+-- This option enables the following behavior:
128--
139--
129-- * __Reordering__: Order of the fields can be changed without affecting
140-- * __Reordering__: Order of the fields can be changed without affecting
130-- serialization.
141-- serialization.
131-- * __Addition__: If a field is added in the new version, the old version of
142-- * __Addition__: If a field is added in the new version, the old version of
132-- the data type can still be deserialized by the new version. The new value
143-- the data type can still be deserialized by the new version. The new value
133-- would never occur in the old one.
144-- would never occur in the old one.
134−-- * __Deletion__: If a field is deleted in the new version and deserialization
145+-- * __Deletion__: If a field is deleted in the new version, deserialization
135-- of the old version will result in an error. TBD: We can possibly designate a
146-- of the old version will result in an error. TBD: We can possibly designate a
136−-- catch-all field to handle this case.
147+-- catch-all case to handle this scenario.
137--
148--
138-- Note that if you change a type, change the semantics of a type, or delete a
149-- Note that if you change a type, change the semantics of a type, or delete a
139-- field and add a new field with the same name, deserialization of old data
150-- field and add a new field with the same name, deserialization of old data
140-- may result in silent unexpected behavior.
151-- may result in silent unexpected behavior.
141--
152--
142-- This option has to be the same on both encoding and decoding side.
153-- This option has to be the same on both encoding and decoding side.
154+--
143-- The default is 'False'.
155-- The default is 'False'.
144--
156--
145encodeConstrNames :: Bool -> SerializeConfig -> SerializeConfig
157encodeConstrNames :: Bool -> SerializeConfig -> SerializeConfig
@@ -155,11 +167,11 @@ encodeConstrNames v cfg = cfg {cfgConstructorTagAsString = v}
155-- | __Experimental__
167-- | __Experimental__
156--
168--
157-- In explicit record types, use Latin-1 encoded record field names rather than
169-- In explicit record types, use Latin-1 encoded record field names rather than
158−-- binary values to identify the record fields. Note that this option cannot be
170+-- binary values to identify the record fields. Note that this option is not
159−-- used on a sum type. Also, it does not work on a product type which is not a
171+-- applicable to sum types. Also, it does not work on a product type which is
160−-- record, because there are no field names to begin with.
172+-- not a record, because there are no field names to begin with.
161--
173--
162−-- This option enables the following backward compatible behavior:
174+-- This option enables the following behavior:
163--
175--
164-- * __Reordering__: Order of the fields can be changed without affecting
176-- * __Reordering__: Order of the fields can be changed without affecting
165-- serialization.
177-- serialization.
@@ -167,10 +179,10 @@ encodeConstrNames v cfg = cfg {cfgConstructorTagAsString = v}
167-- version of the data type can still be deserialized by the new version, the
179-- version of the data type can still be deserialized by the new version, the
168-- field value in the older version is assumed to be 'Nothing'. If any other
180-- field value in the older version is assumed to be 'Nothing'. If any other
169-- type of field is added, deserialization of the older version results in an
181-- type of field is added, deserialization of the older version results in an
170−-- error but only when that field is accessed in the deserialized record.
182+-- error but only when that field is actually accessed in the deserialized
183+-- record.
171-- * __Deletion__: If a field is deleted in the new version and it is
184-- * __Deletion__: If a field is deleted in the new version and it is
172-- encountered in a previously serialized version then the field is discarded.
185-- encountered in a previously serialized version then the field is discarded.
173−-- TBD: We can possibly designate a catch-all field to handle this case.
174--
186--
175-- This option has to be the same on both encoding and decoding side.
187-- This option has to be the same on both encoding and decoding side.
176--
188--
@@ -182,12 +194,6 @@ encodeConstrNames v cfg = cfg {cfgConstructorTagAsString = v}
182encodeRecordFields :: Bool -> SerializeConfig -> SerializeConfig
194encodeRecordFields :: Bool -> SerializeConfig -> SerializeConfig
183encodeRecordFields v cfg = cfg {cfgRecordSyntaxWithHeader = v}
195encodeRecordFields v cfg = cfg {cfgRecordSyntaxWithHeader = v}
184
196
185−-- | The default configuration settings are:
186−--
187−-- * 'inlineSize' 'Nothing'
188−-- * 'inlineSerialize' (Just Inline)
189−-- * 'inlineDeserialize' (Just Inline)
190−--
191serializeConfig :: SerializeConfig
197serializeConfig :: SerializeConfig
192serializeConfig =
198serializeConfig =
193 SerializeConfig
199 SerializeConfig
@@ -341,7 +347,7 @@ xorCmp tag off arr =
341 go8 i = do
347 go8 i = do
342 let wIntegral = litIntegral i
348 let wIntegral = litIntegral i
343 [|xor (unsafeInlineIO
349 [|xor (unsafeInlineIO
344− (Unbox.peekByteIndex
350+ (Unbox.peekAt
345 ($(varE off) + $(litIntegral i))
351 ($(varE off) + $(litIntegral i))
346 $(varE arr)))
352 $(varE arr)))
347 ($(wIntegral) :: Word8) .|.
353 ($(wIntegral) :: Word8) .|.
@@ -355,7 +361,7 @@ xorCmp tag off arr =
355 litIntegral
361 litIntegral
356 (shiftAdd w8_w16 [tag !! i, tag !! (i + 1)] :: Word16)
362 (shiftAdd w8_w16 [tag !! i, tag !! (i + 1)] :: Word16)
357 [|xor (unsafeInlineIO
363 [|xor (unsafeInlineIO
358− (Unbox.peekByteIndex
364+ (Unbox.peekAt
359 ($(varE off) + $(litIntegral i))
365 ($(varE off) + $(litIntegral i))
360 $(varE arr)))
366 $(varE arr)))
361 ($(wIntegral) :: Word16) .|.
367 ($(wIntegral) :: Word16) .|.
@@ -375,7 +381,7 @@ xorCmp tag off arr =
375 , tag !! (i + 3)
381 , tag !! (i + 3)
376 ] :: Word32)
382 ] :: Word32)
377 [|xor (unsafeInlineIO
383 [|xor (unsafeInlineIO
378− (Unbox.peekByteIndex
384+ (Unbox.peekAt
379 ($(varE off) + $(litIntegral i))
385 ($(varE off) + $(litIntegral i))
380 $(varE arr)))
386 $(varE arr)))
381 ($(wIntegral) :: Word32) .|.
387 ($(wIntegral) :: Word32) .|.
@@ -399,7 +405,7 @@ xorCmp tag off arr =
399 , tag !! (i + 7)
405 , tag !! (i + 7)
400 ])
406 ])
401 [|xor (unsafeInlineIO
407 [|xor (unsafeInlineIO
402− (Unbox.peekByteIndex
408+ (Unbox.peekAt
403 ($(varE off) + $(litIntegral i))
409 ($(varE off) + $(litIntegral i))
404 $(varE arr)))
410 $(varE arr)))
405 ($(wIntegral) :: Word64) .|.
411 ($(wIntegral) :: Word64) .|.
@@ -425,7 +431,7 @@ serializeW8List off arr w8List = do
425 makeBind i =
431 makeBind i =
426 bindS
432 bindS
427 (varP (makeN (i + 1)))
433 (varP (makeN (i + 1)))
428− [|$(varE 'serialize)
434+ [|$(varE 'serializeAt)
429 $(varE (makeN i))
435 $(varE (makeN i))
430 $(varE arr)
436 $(varE arr)
431 ($(litIntegral (w8List !! i)) :: Word8)|]
437 ($(litIntegral (w8List !! i)) :: Word8)|]
core/src/Streamly/Internal/Data/Serialize/TH/RecHeader.hs+26−26
@@ -27,7 +27,7 @@ import Data.Maybe (fromJust)
27import Language.Haskell.TH
27import Language.Haskell.TH
28import Streamly.Internal.Data.Serialize.Type (Serialize(..))
28import Streamly.Internal.Data.Serialize.Type (Serialize(..))
29import Data.Foldable (foldlM)
29import Data.Foldable (foldlM)
30−import Streamly.Internal.Data.MutByteArray (MutByteArray)
30+import Streamly.Internal.Data.MutByteArray.Type (MutByteArray)
31import Data.Proxy (Proxy(..))
31import Data.Proxy (Proxy(..))
32
32
33import qualified Streamly.Internal.Data.Unbox as Unbox
33import qualified Streamly.Internal.Data.Unbox as Unbox
@@ -103,36 +103,36 @@ newtype CompactList a =
103-- the list is 255.
103-- the list is 255.
104instance forall a. Serialize a => Serialize (CompactList a) where
104instance forall a. Serialize a => Serialize (CompactList a) where
105
105
106− -- {-# INLINE size #-}
106+ -- {-# INLINE addSizeTo #-}
107− size acc (CompactList xs) =
107+ addSizeTo acc (CompactList xs) =
108− foldl' size (acc + (Unbox.sizeOf (Proxy :: Proxy Word8))) xs
108+ foldl' addSizeTo (acc + (Unbox.sizeOf (Proxy :: Proxy Word8))) xs
109
109
110 -- Inlining this causes large compilation times for tests
110 -- Inlining this causes large compilation times for tests
111− {-# INLINABLE deserialize #-}
111+ {-# INLINABLE deserializeAt #-}
112− deserialize off arr sz = do
112+ deserializeAt off arr sz = do
113− (off1, len8) <- deserialize off arr sz :: IO (Int, Word8)
113+ (off1, len8) <- deserializeAt off arr sz :: IO (Int, Word8)
114 let len = w8_int len8
114 let len = w8_int len8
115 peekList f o i | i >= 3 = do
115 peekList f o i | i >= 3 = do
116 -- Unfold the loop three times
116 -- Unfold the loop three times
117− (o1, x1) <- deserialize o arr sz
117+ (o1, x1) <- deserializeAt o arr sz
118− (o2, x2) <- deserialize o1 arr sz
118+ (o2, x2) <- deserializeAt o1 arr sz
119− (o3, x3) <- deserialize o2 arr sz
119+ (o3, x3) <- deserializeAt o2 arr sz
120 peekList (f . (\xs -> x1:x2:x3:xs)) o3 (i - 3)
120 peekList (f . (\xs -> x1:x2:x3:xs)) o3 (i - 3)
121 peekList f o 0 = pure (o, f [])
121 peekList f o 0 = pure (o, f [])
122 peekList f o i = do
122 peekList f o i = do
123− (o1, x) <- deserialize o arr sz
123+ (o1, x) <- deserializeAt o arr sz
124 peekList (f . (x:)) o1 (i - 1)
124 peekList (f . (x:)) o1 (i - 1)
125 (nextOff, lst) <- peekList id off1 len
125 (nextOff, lst) <- peekList id off1 len
126 pure (nextOff, CompactList lst)
126 pure (nextOff, CompactList lst)
127
127
128 -- Inlining this causes large compilation times for tests
128 -- Inlining this causes large compilation times for tests
129− {-# INLINABLE serialize #-}
129+ {-# INLINABLE serializeAt #-}
130− serialize off arr (CompactList val) = do
130+ serializeAt off arr (CompactList val) = do
131− void $ serialize off arr (int_w8 (length val) :: Word8)
131+ void $ serializeAt off arr (int_w8 (length val) :: Word8)
132 let off1 = off + Unbox.sizeOf (Proxy :: Proxy Word8)
132 let off1 = off + Unbox.sizeOf (Proxy :: Proxy Word8)
133 let pokeList o [] = pure o
133 let pokeList o [] = pure o
134 pokeList o (x:xs) = do
134 pokeList o (x:xs) = do
135− o1 <- serialize o arr x
135+ o1 <- serializeAt o arr x
136 pokeList o1 xs
136 pokeList o1 xs
137 pokeList off1 val
137 pokeList off1 val
138
138
@@ -154,7 +154,7 @@ isMaybeType _ = False
154-- We add 4 here because we use 'serializeWithSize' for serializing.
154-- We add 4 here because we use 'serializeWithSize' for serializing.
155exprGetSize :: Q Exp -> (Int, Type) -> Q Exp
155exprGetSize :: Q Exp -> (Int, Type) -> Q Exp
156exprGetSize acc (i, _) =
156exprGetSize acc (i, _) =
157− [|size $(acc) $(varE (mkFieldName i)) + 4|]
157+ [|addSizeTo $(acc) $(varE (mkFieldName i)) + 4|]
158
158
159sizeOfHeader :: SimpleDataCon -> Int
159sizeOfHeader :: SimpleDataCon -> Int
160sizeOfHeader (SimpleDataCon _ fields) =
160sizeOfHeader (SimpleDataCon _ fields) =
@@ -227,8 +227,8 @@ headerValue (SimpleDataCon _ fields) =
227{-# INLINE serializeWithSize #-}
227{-# INLINE serializeWithSize #-}
228serializeWithSize :: Serialize a => Int -> MutByteArray -> a -> IO Int
228serializeWithSize :: Serialize a => Int -> MutByteArray -> a -> IO Int
229serializeWithSize off arr val = do
229serializeWithSize off arr val = do
230− off1 <- serialize (off + 4) arr val
230+ off1 <- serializeAt (off + 4) arr val
231− Unbox.pokeByteIndex off arr (int_w32 (off1 - off - 4) :: Word32)
231+ Unbox.pokeAt off arr (int_w32 (off1 - off - 4) :: Word32)
232 pure off1
232 pure off1
233
233
234mkRecSerializeExpr :: Name -> SimpleDataCon -> Q Exp
234mkRecSerializeExpr :: Name -> SimpleDataCon -> Q Exp
@@ -238,14 +238,14 @@ mkRecSerializeExpr initialOffset (con@(SimpleDataCon cname fields)) = do
238 -- We first compare the header length encoded and the current header
238 -- We first compare the header length encoded and the current header
239 -- length. Only if the header lengths match, we compare the headers.
239 -- length. Only if the header lengths match, we compare the headers.
240 [|do $(varP afterHLen) <-
240 [|do $(varP afterHLen) <-
241− serialize
241+ serializeAt
242 ($(varE initialOffset) + 4)
242 ($(varE initialOffset) + 4)
243 $(varE _arr)
243 $(varE _arr)
244 ($(litIntegral hlen) :: Word32)
244 ($(litIntegral hlen) :: Word32)
245 $(varP (makeI 0)) <- $(serializeW8List afterHLen _arr hval)
245 $(varP (makeI 0)) <- $(serializeW8List afterHLen _arr hval)
246 let $(openConstructor cname (length fields)) = $(varE _val)
246 let $(openConstructor cname (length fields)) = $(varE _val)
247 finalOff <- $(mkSerializeExprFields 'serializeWithSize fields)
247 finalOff <- $(mkSerializeExprFields 'serializeWithSize fields)
248− Unbox.pokeByteIndex
248+ Unbox.pokeAt
249 $(varE initialOffset)
249 $(varE initialOffset)
250 $(varE _arr)
250 $(varE _arr)
251 ((fromIntegral :: Int -> Word32)
251 ((fromIntegral :: Int -> Word32)
@@ -264,7 +264,7 @@ mkRecSerializeExpr initialOffset (con@(SimpleDataCon cname fields)) = do
264{-# INLINE deserializeWithSize #-}
264{-# INLINE deserializeWithSize #-}
265deserializeWithSize ::
265deserializeWithSize ::
266 Serialize a => Int -> MutByteArray -> Int -> IO (Int, a)
266 Serialize a => Int -> MutByteArray -> Int -> IO (Int, a)
267−deserializeWithSize off arr endOff = deserialize (off + 4) arr endOff
267+deserializeWithSize off arr endOff = deserializeAt (off + 4) arr endOff
268
268
269conUpdateFuncDec :: Name -> [Field] -> Q [Dec]
269conUpdateFuncDec :: Name -> [Field] -> Q [Dec]
270conUpdateFuncDec funcName fields = do
270conUpdateFuncDec funcName fields = do
@@ -282,7 +282,7 @@ conUpdateFuncDec funcName fields = do
282 wildP
282 wildP
283 (normalB
283 (normalB
284 [|do (valOff, valLen :: Word32) <-
284 [|do (valOff, valLen :: Word32) <-
285− deserialize
285+ deserializeAt
286 $(varE curOff)
286 $(varE curOff)
287 $(varE arr)
287 $(varE arr)
288 $(varE endOff)
288 $(varE endOff)
@@ -317,7 +317,7 @@ conUpdateFuncDec funcName fields = do
317 (litP fnameLit)
317 (litP fnameLit)
318 (normalB
318 (normalB
319 [|do (valOff, valLen :: Word32) <-
319 [|do (valOff, valLen :: Word32) <-
320− deserialize
320+ deserializeAt
321 $(varE currOff)
321 $(varE currOff)
322 $(varE arr)
322 $(varE arr)
323 $(varE endOff)
323 $(varE endOff)
@@ -345,7 +345,7 @@ mkDeserializeKeysDec funcName updateFunc (SimpleDataCon cname fields) = do
345 errorUnsupported "The datatype should use record syntax."
345 errorUnsupported "The datatype should use record syntax."
346 method <-
346 method <-
347 [|do (dataOff, hlist :: CompactList (CompactList Word8)) <-
347 [|do (dataOff, hlist :: CompactList (CompactList Word8)) <-
348− deserialize $(varE hOff) $(varE arr) $(varE endOff)
348+ deserializeAt $(varE hOff) $(varE arr) $(varE endOff)
349 let keys = wListToString . unCompactList <$> unCompactList hlist
349 let keys = wListToString . unCompactList <$> unCompactList hlist
350 ($(varP kvEncoded), _) <-
350 ($(varP kvEncoded), _) <-
351 foldlM
351 foldlM
@@ -390,9 +390,9 @@ mkRecDeserializeExpr initialOff endOff deserializeWithKeys con = do
390 sizeForHeaderLength = 4 -- Word32
390 sizeForHeaderLength = 4 -- Word32
391 sizePreData = sizeForFinalOff + sizeForHeaderLength + hlen
391 sizePreData = sizeForFinalOff + sizeForHeaderLength + hlen
392 [|do (hlenOff, encLen :: Word32) <-
392 [|do (hlenOff, encLen :: Word32) <-
393− deserialize $(varE initialOff) $(varE _arr) $(varE endOff)
393+ deserializeAt $(varE initialOff) $(varE _arr) $(varE endOff)
394 ($(varP hOff), hlen1 :: Word32) <-
394 ($(varP hOff), hlen1 :: Word32) <-
395− deserialize hlenOff $(varE _arr) $(varE endOff)
395+ deserializeAt hlenOff $(varE _arr) $(varE endOff)
396 if (hlen1 == $(litIntegral hlen)) && $(xorCmp hval hOff _arr)
396 if (hlen1 == $(litIntegral hlen)) && $(xorCmp hval hOff _arr)
397 then do
397 then do
398 let $(varP (makeI 0)) =
398 let $(varP (makeI 0)) =
core/src/Streamly/Internal/Data/Serialize/Type.hs+94−127
@@ -9,35 +9,25 @@
9module Streamly.Internal.Data.Serialize.Type
9module Streamly.Internal.Data.Serialize.Type
10 (
10 (
11 Serialize(..)
11 Serialize(..)
12− , encode
13− , encodeAs
14− , pinnedEncode
15− , decode
16 ) where
12 ) where
17
13
18--------------------------------------------------------------------------------
14--------------------------------------------------------------------------------
19-- Imports
15-- Imports
20--------------------------------------------------------------------------------
16--------------------------------------------------------------------------------
21
17
22−#include "assert.hs"
23−
24−#ifdef DEBUG
25−import Control.Exception (assert)
18+import Control.Monad (when)
26−#endif
27−
28import Data.List (foldl')
19import Data.List (foldl')
29import Data.Proxy (Proxy (..))
20import Data.Proxy (Proxy (..))
30import Streamly.Internal.Data.Unbox (Unbox)
21import Streamly.Internal.Data.Unbox (Unbox)
31−import Streamly.Internal.Data.MutByteArray (MutByteArray(..), PinnedState(..))
22+import Streamly.Internal.Data.MutByteArray.Type (MutByteArray(..))
32import Streamly.Internal.Data.Array.Type (Array(..))
23import Streamly.Internal.Data.Array.Type (Array(..))
33−import Streamly.Internal.System.IO (unsafeInlineIO)
34import GHC.Int (Int16(..), Int32(..), Int64(..), Int8(..))
24import GHC.Int (Int16(..), Int32(..), Int64(..), Int8(..))
35import GHC.Word (Word16(..), Word32(..), Word64(..), Word8(..))
25import GHC.Word (Word16(..), Word32(..), Word64(..), Word8(..))
36import GHC.Stable (StablePtr(..))
26import GHC.Stable (StablePtr(..))
37
27
38−import qualified Streamly.Internal.Data.MutByteArray as MBA
28+import qualified Streamly.Internal.Data.MutByteArray.Type as MBA
39import qualified Streamly.Internal.Data.Unbox as Unbox
29import qualified Streamly.Internal.Data.Unbox as Unbox
40−import qualified Streamly.Internal.Data.Array as Array
30+import qualified Streamly.Internal.Data.Array.Type as Array
41import qualified Streamly.Internal.Data.MutArray as MutArray
31import qualified Streamly.Internal.Data.MutArray as MutArray
42
32
43import GHC.Exts
33import GHC.Exts
@@ -69,14 +59,14 @@ import GHC.Exts
69-- particular constructor. For variable length data types the length is encoded
59-- particular constructor. For variable length data types the length is encoded
70-- along with the data.
60-- along with the data.
71--
61--
72−-- The 'deserialize' operation reads bytes from the mutable byte array and
62+-- The 'deserializeAt' operation reads bytes from the mutable byte array and
73-- builds a Haskell data type from these bytes, the number of bytes it reads
63-- builds a Haskell data type from these bytes, the number of bytes it reads
74−-- depends on the type and the encoded value it is reading. 'serialize'
64+-- depends on the type and the encoded value it is reading. 'serializeAt'
75-- operation converts a Haskell data type to its binary representation which
65-- operation converts a Haskell data type to its binary representation which
76−-- must consist of as many bytes as returned by the @size@ operation on that
66+-- must consist of as many bytes as added by the @addSizeTo@ operation for that
77-- value and then stores these bytes into the mutable byte array. The
67-- value and then stores these bytes into the mutable byte array. The
78−-- programmer is expected to use the @size@ operation and allocate an array of
68+-- programmer is expected to use the @addSizeTo@ operation and allocate an
79−-- sufficient length before calling 'serialize'.
69+-- array of sufficient length before calling 'serializeAt'.
80--
70--
81-- IMPORTANT: The serialized data's byte ordering remains the same as the host
71-- IMPORTANT: The serialized data's byte ordering remains the same as the host
82-- machine's byte order. Therefore, it can not be deserialized from host
72-- machine's byte order. Therefore, it can not be deserialized from host
@@ -95,29 +85,29 @@ import GHC.Exts
95-- :}
85-- :}
96--
86--
97-- @
87-- @
98−-- import Streamly.Data.Serialize (deriveSerialize)
88+-- import Streamly.Data.MutByteArray (deriveSerialize)
99-- \$(deriveSerialize [d|instance Serialize Object|])
89-- \$(deriveSerialize [d|instance Serialize Object|])
100-- @
90-- @
101--
91--
102−-- See 'Streamly.Data.Serialize.deriveSerialize' and
92+-- See 'Streamly.Data.MutByteArray.deriveSerialize' and
103−-- 'Streamly.Data.Serialize.deriveSerializeWith' for more information on
93+-- 'Streamly.Data.MutByteArray.deriveSerializeWith' for more information on
104-- deriving using Template Haskell.
94-- deriving using Template Haskell.
105--
95--
106-- Here is an example of a manual instance.
96-- Here is an example of a manual instance.
107--
97--
108−-- >>> import Streamly.Data.Serialize (Serialize(..))
98+-- >>> import Streamly.Data.MutByteArray (Serialize(..))
109--
99--
110-- >>> :{
100-- >>> :{
111-- instance Serialize Object where
101-- instance Serialize Object where
112−-- size acc obj = size (size acc (_obj1 obj)) (_obj2 obj)
102+-- addSizeTo acc obj = addSizeTo (addSizeTo acc (_obj1 obj)) (_obj2 obj)
113−-- deserialize i arr len = do
103+-- deserializeAt i arr len = do
114-- -- Check the array bounds before reading
104-- -- Check the array bounds before reading
115−-- (i1, x0) <- deserialize i arr len
105+-- (i1, x0) <- deserializeAt i arr len
116−-- (i2, x1) <- deserialize i1 arr len
106+-- (i2, x1) <- deserializeAt i1 arr len
117-- pure (i2, Object x0 x1)
107-- pure (i2, Object x0 x1)
118−-- serialize i arr (Object x0 x1) = do
108+-- serializeAt i arr (Object x0 x1) = do
119−-- i1 <- serialize i arr x0
109+-- i1 <- serializeAt i arr x0
120−-- i2 <- serialize i1 arr x1
110+-- i2 <- serializeAt i1 arr x1
121-- pure i2
111-- pure i2
122-- :}
112-- :}
123--
113--
@@ -128,49 +118,50 @@ class Serialize a where
128 -- It is of the form @Int -> a -> Int@ because you can have tail-recursive
118 -- It is of the form @Int -> a -> Int@ because you can have tail-recursive
129 -- traversal of the structures.
119 -- traversal of the structures.
130
120
131− -- | @size accum value@ returns @accum@ incremented by the size of the
121+ -- | @addSizeTo accum value@ returns @accum@ incremented by the size of the
132 -- serialized representation of @value@ in bytes. Size cannot be zero. It
122 -- serialized representation of @value@ in bytes. Size cannot be zero. It
133 -- should be at least 1 byte.
123 -- should be at least 1 byte.
134− size :: Int -> a -> Int
124+ addSizeTo :: Int -> a -> Int
135
125
136 -- We can implement the following functions without returning the `Int`
126 -- We can implement the following functions without returning the `Int`
137 -- offset but that may require traversing the Haskell structure again to get
127 -- offset but that may require traversing the Haskell structure again to get
138 -- the size. Therefore, this is a performance optimization.
128 -- the size. Therefore, this is a performance optimization.
139
129
140− -- | @deserialize byte-offset array arrayLen@ deserializes a value from the
130+ -- | @deserializeAt byte-offset array arrayLen@ deserializes a value from
141− -- given byte-offset in the array. Returns a tuple consisting of the next
131+ -- the given byte-offset in the array. Returns a tuple consisting of the
142− -- byte-offset and the deserialized value.
132+ -- next byte-offset and the deserialized value.
133+ --
134+ -- The arrayLen passed is the entire length of the input buffer. It is to
135+ -- be used to check if we would overflow the input buffer when
136+ -- deserializing.
143 --
137 --
144 -- Throws an exception if the operation would exceed the supplied arrayLen.
138 -- Throws an exception if the operation would exceed the supplied arrayLen.
145− deserialize :: Int -> MutByteArray -> Int -> IO (Int, a)
139+ deserializeAt :: Int -> MutByteArray -> Int -> IO (Int, a)
146
140
147− -- | @serialize byte-offset array value@ writes the serialized
141+ -- | @serializeAt byte-offset array value@ writes the serialized
148 -- representation of the @value@ in the array at the given byte-offset.
142 -- representation of the @value@ in the array at the given byte-offset.
149 -- Returns the next byte-offset.
143 -- Returns the next byte-offset.
150 --
144 --
151 -- This is an unsafe operation, the programmer must ensure that the array
145 -- This is an unsafe operation, the programmer must ensure that the array
152 -- has enough space available to serialize the value as determined by the
146 -- has enough space available to serialize the value as determined by the
153− -- @size@ operation.
147+ -- @addSizeTo@ operation.
154− serialize :: Int -> MutByteArray -> a -> IO Int
148+ serializeAt :: Int -> MutByteArray -> a -> IO Int
155
149
156--------------------------------------------------------------------------------
150--------------------------------------------------------------------------------
157-- Instances
151-- Instances
158--------------------------------------------------------------------------------
152--------------------------------------------------------------------------------
159
153
160−-- XXX We do not need to pass the end of data offset, we can just check the
154+-- _size is the length from array start to the last accessed byte.
161−-- mutable array end here to avoid a crash, and when we return from deserialize
162−-- we can check if the offset returned is beyond the bound or not.
163−--
164#ifdef DEBUG
155#ifdef DEBUG
165{-# INLINE checkBounds #-}
156{-# INLINE checkBounds #-}
166checkBounds :: String -> Int -> MutByteArray -> IO ()
157checkBounds :: String -> Int -> MutByteArray -> IO ()
167−checkBounds _label _off _arr = do
158+checkBounds _label _size _arr = do
168− sz <- sizeOfMutableByteArray _arr
159+ sz <- MBA.sizeOfMutableByteArray _arr
169− if (_off > sz)
160+ if (_size > sz)
170 then error
161 then error
171 $ _label
162 $ _label
172 ++ ": accessing array at offset = "
163 ++ ": accessing array at offset = "
173− ++ show (_off - 1)
164+ ++ show (_size - 1)
174 ++ " max valid offset = " ++ show (sz - 1)
165 ++ " max valid offset = " ++ show (sz - 1)
175 else return ()
166 else return ()
176#endif
167#endif
@@ -193,16 +184,16 @@ checkBounds _label _off _arr = do
193-- single array for deserializing. But that can also be done by framing the
184-- single array for deserializing. But that can also be done by framing the
194-- serialized value with a size header.
185-- serialized value with a size header.
195--
186--
196−{-# INLINE deserializeUnsafe #-}
187+{-# INLINE deserializeChecked #-}
197−deserializeUnsafe :: forall a. Unbox a => Int -> MutByteArray -> Int -> IO (Int, a)
188+deserializeChecked :: forall a. Unbox a => Int -> MutByteArray -> Int -> IO (Int, a)
198−deserializeUnsafe off arr sz =
189+deserializeChecked off arr sz =
199 let next = off + Unbox.sizeOf (Proxy :: Proxy a)
190 let next = off + Unbox.sizeOf (Proxy :: Proxy a)
200 in do
191 in do
201 -- Keep likely path in the straight branch.
192 -- Keep likely path in the straight branch.
202 if (next <= sz)
193 if (next <= sz)
203− then Unbox.peekByteIndex off arr >>= \val -> pure (next, val)
194+ then Unbox.peekAt off arr >>= \val -> pure (next, val)
204 else error
195 else error
205− $ "deserialize: accessing array at offset = "
196+ $ "deserializeAt: accessing array at offset = "
206 ++ show (next - 1)
197 ++ show (next - 1)
207 ++ " max valid offset = " ++ show (sz - 1)
198 ++ " max valid offset = " ++ show (sz - 1)
208
199
@@ -212,19 +203,19 @@ serializeUnsafe off arr val =
212 let next = off + Unbox.sizeOf (Proxy :: Proxy a)
203 let next = off + Unbox.sizeOf (Proxy :: Proxy a)
213 in do
204 in do
214#ifdef DEBUG
205#ifdef DEBUG
215− checkBounds "serialize" next arr
206+ checkBounds "serializeAt" next arr
216#endif
207#endif
217− Unbox.pokeByteIndex off arr val
208+ Unbox.pokeAt off arr val
218 pure next
209 pure next
219
210
220#define DERIVE_SERIALIZE_FROM_UNBOX(_type) \
211#define DERIVE_SERIALIZE_FROM_UNBOX(_type) \
221instance Serialize _type where \
212instance Serialize _type where \
222−; {-# INLINE size #-} \
213+; {-# INLINE addSizeTo #-} \
223−; size acc _ = acc + Unbox.sizeOf (Proxy :: Proxy _type) \
214+; addSizeTo acc _ = acc + Unbox.sizeOf (Proxy :: Proxy _type) \
224−; {-# INLINE deserialize #-} \
215+; {-# INLINE deserializeAt #-} \
225−; deserialize off arr end = deserializeUnsafe off arr end :: IO (Int, _type) \
216+; deserializeAt off arr end = deserializeChecked off arr end :: IO (Int, _type) \
226−; {-# INLINE serialize #-} \
217+; {-# INLINE serializeAt #-} \
227−; serialize = \
218+; serializeAt = \
228 serializeUnsafe :: Int -> MutByteArray -> _type -> IO Int
219 serializeUnsafe :: Int -> MutByteArray -> _type -> IO Int
229
220
230DERIVE_SERIALIZE_FROM_UNBOX(())
221DERIVE_SERIALIZE_FROM_UNBOX(())
@@ -248,103 +239,79 @@ DERIVE_SERIALIZE_FROM_UNBOX((FunPtr a))
248
239
249instance forall a. Serialize a => Serialize [a] where
240instance forall a. Serialize a => Serialize [a] where
250
241
251− -- {-# INLINE size #-}
242+ -- {-# INLINE addSizeTo #-}
243+ addSizeTo acc xs =
252− size acc xs = foldl' size (acc + (Unbox.sizeOf (Proxy :: Proxy Int))) xs
244+ foldl' addSizeTo (acc + (Unbox.sizeOf (Proxy :: Proxy Int))) xs
253
245
254 -- Inlining this causes large compilation times for tests
246 -- Inlining this causes large compilation times for tests
255− {-# INLINABLE deserialize #-}
247+ {-# INLINABLE deserializeAt #-}
256− deserialize off arr sz = do
248+ deserializeAt off arr sz = do
257− (off1, len64) <- deserialize off arr sz :: IO (Int, Int64)
249+ (off1, len64) <- deserializeAt off arr sz :: IO (Int, Int64)
258 let len = (fromIntegral :: Int64 -> Int) len64
250 let len = (fromIntegral :: Int64 -> Int) len64
259 peekList f o i | i >= 3 = do
251 peekList f o i | i >= 3 = do
260 -- Unfold the loop three times
252 -- Unfold the loop three times
261− (o1, x1) <- deserialize o arr sz
253+ (o1, x1) <- deserializeAt o arr sz
262− (o2, x2) <- deserialize o1 arr sz
254+ (o2, x2) <- deserializeAt o1 arr sz
263− (o3, x3) <- deserialize o2 arr sz
255+ (o3, x3) <- deserializeAt o2 arr sz
264 peekList (f . (\xs -> x1:x2:x3:xs)) o3 (i - 3)
256 peekList (f . (\xs -> x1:x2:x3:xs)) o3 (i - 3)
265 peekList f o 0 = pure (o, f [])
257 peekList f o 0 = pure (o, f [])
266 peekList f o i = do
258 peekList f o i = do
267− (o1, x) <- deserialize o arr sz
259+ (o1, x) <- deserializeAt o arr sz
268 peekList (f . (x:)) o1 (i - 1)
260 peekList (f . (x:)) o1 (i - 1)
269 peekList id off1 len
261 peekList id off1 len
270
262
271 -- Inlining this causes large compilation times for tests
263 -- Inlining this causes large compilation times for tests
272− {-# INLINABLE serialize #-}
264+ {-# INLINABLE serializeAt #-}
273− serialize off arr val = do
265+ serializeAt off arr val = do
274 let off1 = off + Unbox.sizeOf (Proxy :: Proxy Int64)
266 let off1 = off + Unbox.sizeOf (Proxy :: Proxy Int64)
275 let pokeList acc o [] =
267 let pokeList acc o [] =
276− Unbox.pokeByteIndex off arr (acc :: Int64) >> pure o
268+ Unbox.pokeAt off arr (acc :: Int64) >> pure o
277 pokeList acc o (x:xs) = do
269 pokeList acc o (x:xs) = do
278− o1 <- serialize o arr x
270+ o1 <- serializeAt o arr x
279 pokeList (acc + 1) o1 xs
271 pokeList (acc + 1) o1 xs
280 pokeList 0 off1 val
272 pokeList 0 off1 val
281
273
274+instance
275+#ifdef DEVBUILD
276+ Unbox a =>
277+#endif
282−instance Serialize (Array a) where
278+ Serialize (Array a) where
283− {-# INLINE size #-}
279+ {-# INLINE addSizeTo #-}
284− size i (Array {..}) = i + (arrEnd - arrStart) + 8
280+ addSizeTo i (Array {..}) = i + (arrEnd - arrStart) + 8
285
281
286− {-# INLINE deserialize #-}
282+ {-# INLINE deserializeAt #-}
287− deserialize off arr end = do
283+ deserializeAt off arr len = do
288− (off1, byteLen) <- deserialize off arr end :: IO (Int, Int)
284+ (off1, byteLen) <- deserializeAt off arr len :: IO (Int, Int)
289 let off2 = off1 + byteLen
285 let off2 = off1 + byteLen
286+ when (off2 > len) $
287+ error
288+ $ "deserializeAt: accessing array at offset = "
289+ ++ show (off2 - 1)
290+ ++ " max valid offset = " ++ show (len - 1)
291+ -- XXX Use MutByteArray.cloneSliceUnsafe
290 let slice = MutArray.MutArray arr off1 off2 off2
292 let slice = MutArray.MutArray arr off1 off2 off2
291 newArr <- MutArray.clone slice
293 newArr <- MutArray.clone slice
292 pure (off2, Array.unsafeFreeze newArr)
294 pure (off2, Array.unsafeFreeze newArr)
293
295
294− {-# INLINE serialize #-}
296+ {-# INLINE serializeAt #-}
295− serialize off arr (Array {..}) = do
297+ serializeAt off arr (Array {..}) = do
296 let arrLen = arrEnd - arrStart
298 let arrLen = arrEnd - arrStart
297− off1 <- serialize off arr arrLen
299+ off1 <- serializeAt off arr arrLen
298 MBA.putSliceUnsafe arrContents arrStart arr off1 arrLen
300 MBA.putSliceUnsafe arrContents arrStart arr off1 arrLen
299 pure (off1 + arrLen)
301 pure (off1 + arrLen)
300
302
301instance (Serialize a, Serialize b) => Serialize (a, b) where
303instance (Serialize a, Serialize b) => Serialize (a, b) where
302
304
303− {-# INLINE size #-}
305+ {-# INLINE addSizeTo #-}
304− size acc (a, b) = size (size acc a) b
306+ addSizeTo acc (a, b) = addSizeTo (addSizeTo acc a) b
305
307
306− {-# INLINE serialize #-}
308+ {-# INLINE serializeAt #-}
307− serialize off arr (a, b) = do
309+ serializeAt off arr (a, b) = do
308− off1 <- serialize off arr a
310+ off1 <- serializeAt off arr a
309− serialize off1 arr b
311+ serializeAt off1 arr b
310
312
311− {-# INLINE deserialize #-}
313+ {-# INLINE deserializeAt #-}
312− deserialize off arr end = do
314+ deserializeAt off arr end = do
313− (off1, a) <- deserialize off arr end
315+ (off1, a) <- deserializeAt off arr end
314− (off2, b) <- deserialize off1 arr end
316+ (off2, b) <- deserializeAt off1 arr end
315 pure (off2, (a, b))
317 pure (off2, (a, b))
316−
317−--------------------------------------------------------------------------------
318−-- High level functions
319−--------------------------------------------------------------------------------
320−
321−{-# INLINE encodeAs #-}
322−encodeAs :: forall a. Serialize a => PinnedState -> a -> Array Word8
323−encodeAs ps a =
324− unsafeInlineIO $ do
325− let len = size 0 a
326− mbarr <- MBA.newBytesAs ps len
327− off <- serialize 0 mbarr a
328− assertM(len == off)
329− pure $ Array mbarr 0 off
330−
331−{-# INLINE encode #-}
332−encode :: Serialize a => a -> Array Word8
333−encode = encodeAs Unpinned
334−
335−-- | Encode a Haskell type to a byte array. The array is allocated using pinned
336−-- memory so that it can be used directly in OS APIs for writing to file or
337−-- sending over the network.
338−{-# INLINE pinnedEncode #-}
339−pinnedEncode :: Serialize a => a -> Array Word8
340−pinnedEncode = encodeAs Pinned
341−
342−-- | Decode a Haskell type from its serialized representation in a byte
343−-- array.
344−{-# INLINE decode #-}
345−decode :: Serialize a => Array Word8 -> a
346−decode arr@(Array {..}) = unsafeInlineIO $ do
347− let lenArr = Array.length arr
348− (off, val) <- deserialize arrStart arrContents (arrStart + lenArr)
349− assertM(off == arrStart + lenArr)
350− pure val
core/src/Streamly/Internal/Data/Stream/Container.hs+56−48
@@ -11,16 +11,17 @@
11
11
12module Streamly.Internal.Data.Stream.Container
12module Streamly.Internal.Data.Stream.Container
13 (
13 (
14− nub
14+ -- * Deduplication
15+ ordNub
15
16
16− -- * Joins for unconstrained types
17+ -- * Joins
17− , joinLeftGeneric
18+ , leftJoin
18− , joinOuterGeneric
19+ , outerJoin
19
20
20− -- * Joins with Ord constraint
21+ -- * Ord Joins
21− , joinInner
22+ , innerOrdJoin
22− , joinLeft
23+ , leftOrdJoin
23− , joinOuter
24+ , outerOrdJoin
24 )
25 )
25where
26where
26
27
@@ -47,12 +48,15 @@ import qualified Streamly.Internal.Data.Stream.Transformer as Stream
47
48
48#include "DocTestDataStream.hs"
49#include "DocTestDataStream.hs"
49
50
51+-- | @nub@ specialized to 'Ord' types for better performance. Returns a
52+-- subsequence of the stream removing any duplicate elements.
53+--
50−-- | The memory used is proportional to the number of unique elements in the
54+-- The memory used is proportional to the number of unique elements in the
51−-- stream. If we want to limit the memory we can just use "take" to limit the
55+-- stream. One way to limit the memory is to use @take@ on the resulting
52−-- uniq elements in the stream.
56+-- stream to limit the unique elements in the stream.
53−{-# INLINE_NORMAL nub #-}
57+{-# INLINE_NORMAL ordNub #-}
54−nub :: (Monad m, Ord a) => Stream m a -> Stream m a
58+ordNub :: (Monad m, Ord a) => Stream m a -> Stream m a
55−nub (Stream step1 state1) = Stream step (Set.empty, state1)
59+ordNub (Stream step1 state1) = Stream step (Set.empty, state1)
56
60
57 where
61 where
58
62
@@ -78,8 +82,8 @@ toMap =
78--
82--
79-- XXX An IntMap may be faster when the keys are Int.
83-- XXX An IntMap may be faster when the keys are Int.
80-- XXX Use hashmap instead of map?
84-- XXX Use hashmap instead of map?
81−--
85+
82−-- | Like 'joinInner' but uses a 'Map' for efficiency.
86+-- | 'innerJoin' specialized to 'Ord' types for better performance.
83--
87--
84-- If the input streams have duplicate keys, the behavior is undefined.
88-- If the input streams have duplicate keys, the behavior is undefined.
85--
89--
@@ -90,10 +94,10 @@ toMap =
90-- Time: O(m + n)
94-- Time: O(m + n)
91--
95--
92-- /Pre-release/
96-- /Pre-release/
93−{-# INLINE joinInner #-}
97+{-# INLINE innerOrdJoin #-}
94−joinInner :: (Monad m, Ord k) =>
98+innerOrdJoin :: (Monad m, Ord k) =>
95 Stream m (k, a) -> Stream m (k, b) -> Stream m (k, a, b)
99 Stream m (k, a) -> Stream m (k, b) -> Stream m (k, a, b)
96−joinInner s1 s2 =
100+innerOrdJoin s1 s2 =
97 Stream.concatEffect $ do
101 Stream.concatEffect $ do
98 km <- toMap s2
102 km <- toMap s2
99 pure $ Stream.mapMaybe (joinAB km) s1
103 pure $ Stream.mapMaybe (joinAB km) s1
@@ -106,32 +110,33 @@ joinInner s1 s2 =
106 Nothing -> Nothing
110 Nothing -> Nothing
107
111
108-- XXX We can do this concurrently.
112-- XXX We can do this concurrently.
113+-- XXX Check performance of StreamD vs StreamK
109-- XXX If the second stream is sorted and passed as an Array or a seek capable
114-- XXX If the second stream is sorted and passed as an Array or a seek capable
110-- stream then we could use binary search if we have an Ord instance or
115-- stream then we could use binary search if we have an Ord instance or
111-- Ordering returning function. The time complexity would then become (m x log
116-- Ordering returning function. The time complexity would then become (m x log
112-- n).
117-- n).
113
118
114−-- XXX Check performance of StreamD vs StreamK
119+-- | Like 'innerJoin' but emits @(a, Just b)@ whenever a and b are equal, for
115−
116−-- | Like 'joinInner' but emit @(a, Just b)@, and additionally, for those @a@'s
117−-- that are not equal to any @b@ emit @(a, Nothing)@.
120+-- those @a@'s that are not equal to any @b@ emits @(a, Nothing)@.
118--
121--
119−-- The second stream is evaluated multiple times. If the stream is a
122+-- This is a generalization of 'innerJoin' to include all elements from the
120−-- consume-once stream then the caller should cache it in an 'Data.Array.Array'
123+-- left stream and not just those which have an equal in the right stream. This
121−-- before calling this function. Caching may also improve performance if the
124+-- is not a commutative operation, the order of the stream arguments matters.
122−-- stream is expensive to evaluate.
123--
125--
124−-- >>> joinRightGeneric eq = flip (Stream.joinLeftGeneric eq)
126+-- All the caveats mentioned in 'innerJoin' apply here as well. Right join is
127+-- not provided because it is just a flipped left join:
128+--
129+-- >>> rightJoin eq = flip (Stream.leftJoin eq)
125--
130--
126-- Space: O(n) assuming the second stream is cached in memory.
131-- Space: O(n) assuming the second stream is cached in memory.
127--
132--
128-- Time: O(m x n)
133-- Time: O(m x n)
129--
134--
130-- /Unimplemented/
135-- /Unimplemented/
131−{-# INLINE joinLeftGeneric #-}
136+{-# INLINE leftJoin #-}
132−joinLeftGeneric :: Monad m =>
137+leftJoin :: Monad m =>
133 (a -> b -> Bool) -> Stream m a -> Stream m b -> Stream m (a, Maybe b)
138 (a -> b -> Bool) -> Stream m a -> Stream m b -> Stream m (a, Maybe b)
134−joinLeftGeneric eq s1 s2 = Stream.evalStateT (return False) $ unCross $ do
139+leftJoin eq s1 s2 = Stream.evalStateT (return False) $ unCross $ do
135 a <- mkCross (Stream.liftInner s1)
140 a <- mkCross (Stream.liftInner s1)
136 -- XXX should we use StreamD monad here?
141 -- XXX should we use StreamD monad here?
137 -- XXX Is there a better way to perform some action at the end of a loop
142 -- XXX Is there a better way to perform some action at the end of a loop
@@ -152,19 +157,17 @@ joinLeftGeneric eq s1 s2 = Stream.evalStateT (return False) $ unCross $ do
152 else mkCross Stream.nil
157 else mkCross Stream.nil
153 Nothing -> return (a, Nothing)
158 Nothing -> return (a, Nothing)
154
159
155−-- XXX rename to joinLeftOrd?
160+-- | 'leftJoin' specialized to 'Ord' types for better performance.
156−
157−-- | A more efficient 'joinLeft' using a hashmap for efficiency.
158--
161--
159-- Space: O(n)
162-- Space: O(n)
160--
163--
161-- Time: O(m + n)
164-- Time: O(m + n)
162--
165--
163-- /Pre-release/
166-- /Pre-release/
164−{-# INLINE joinLeft #-}
167+{-# INLINE leftOrdJoin #-}
165−joinLeft :: (Ord k, Monad m) =>
168+leftOrdJoin :: (Ord k, Monad m) =>
166 Stream m (k, a) -> Stream m (k, b) -> Stream m (k, a, Maybe b)
169 Stream m (k, a) -> Stream m (k, b) -> Stream m (k, a, Maybe b)
167−joinLeft s1 s2 =
170+leftOrdJoin s1 s2 =
168 Stream.concatEffect $ do
171 Stream.concatEffect $ do
169 km <- toMap s2
172 km <- toMap s2
170 return $ fmap (joinAB km) s1
173 return $ fmap (joinAB km) s1
@@ -177,14 +180,19 @@ joinLeft s1 s2 =
177 Nothing -> (k, a, Nothing)
180 Nothing -> (k, a, Nothing)
178
181
179-- XXX We can do this concurrently.
182-- XXX We can do this concurrently.
183+-- XXX Check performance of StreamD vs StreamK cross operation.
180
184
181−-- XXX Check performance of StreamD vs StreamK
182−
183−-- | Like 'joinLeft' but emits a @(Just a, Just b)@. Like 'joinLeft', for those
185+-- | Like 'leftJoin' but emits a @(Just a, Just b)@. Like 'leftJoin', for those
184-- @a@'s that are not equal to any @b@ emit @(Just a, Nothing)@, but
186-- @a@'s that are not equal to any @b@ emit @(Just a, Nothing)@, but
185-- additionally, for those @b@'s that are not equal to any @a@ emit @(Nothing,
187-- additionally, for those @b@'s that are not equal to any @a@ emit @(Nothing,
186-- Just b)@.
188-- Just b)@.
187--
189--
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+--
188-- For space efficiency use the smaller stream as the second stream.
196-- For space efficiency use the smaller stream as the second stream.
189--
197--
190-- Space: O(n)
198-- Space: O(n)
@@ -192,15 +200,15 @@ joinLeft s1 s2 =
192-- Time: O(m x n)
200-- Time: O(m x n)
193--
201--
194-- /Pre-release/
202-- /Pre-release/
195−{-# INLINE joinOuterGeneric #-}
203+{-# INLINE outerJoin #-}
196−joinOuterGeneric :: MonadIO m =>
204+outerJoin :: MonadIO m =>
197 (a -> b -> Bool)
205 (a -> b -> Bool)
198 -> Stream m a
206 -> Stream m a
199 -> Stream m b
207 -> Stream m b
200 -> Stream m (Maybe a, Maybe b)
208 -> Stream m (Maybe a, Maybe b)
201−joinOuterGeneric eq s1 s =
209+outerJoin eq s1 s2 =
202 Stream.concatEffect $ do
210 Stream.concatEffect $ do
203− inputArr <- Array.fromStream s
211+ inputArr <- Array.fromStream s2
204 let len = Array.length inputArr
212 let len = Array.length inputArr
205 foundArr <-
213 foundArr <-
206 Stream.fold
214 Stream.fold
@@ -254,18 +262,18 @@ joinOuterGeneric eq s1 s =
254-- a flag. At the end go through @Stream m b@ and find those that are not in that
262-- a flag. At the end go through @Stream m b@ and find those that are not in that
255-- hash to return (Nothing, b).
263-- hash to return (Nothing, b).
256
264
257−-- | Like 'joinOuter' but uses a 'Map' for efficiency.
265+-- | 'outerJoin' specialized to 'Ord' types for better performance.
258--
266--
259-- Space: O(m + n)
267-- Space: O(m + n)
260--
268--
261-- Time: O(m + n)
269-- Time: O(m + n)
262--
270--
263-- /Pre-release/
271-- /Pre-release/
264−{-# INLINE joinOuter #-}
272+{-# INLINE outerOrdJoin #-}
265−joinOuter ::
273+outerOrdJoin ::
266 (Ord k, MonadIO m) =>
274 (Ord k, MonadIO m) =>
267 Stream m (k, a) -> Stream m (k, b) -> Stream m (k, Maybe a, Maybe b)
275 Stream m (k, a) -> Stream m (k, b) -> Stream m (k, Maybe a, Maybe b)
268−joinOuter s1 s2 =
276+outerOrdJoin s1 s2 =
269 Stream.concatEffect $ do
277 Stream.concatEffect $ do
270 km1 <- kvFold s1
278 km1 <- kvFold s1
271 km2 <- kvFold s2
279 km2 <- kvFold s2
core/src/Streamly/Internal/Data/Stream/Eliminate.hs+33−25
@@ -13,36 +13,24 @@
13--
13--
14module Streamly.Internal.Data.Stream.Eliminate
14module Streamly.Internal.Data.Stream.Eliminate
15 (
15 (
16− -- * Running a 'Fold'
16+ -- * Running a Parser
17− fold
18−
19− -- -- * Running a 'Parser'
20− , parse
17+ parse
21 , parseD
18 , parseD
22 , parseBreak
19 , parseBreak
23 , parseBreakD
20 , parseBreakD
24
21
25− -- * Stream Deconstruction
22+ -- * Deconstruction
26 , uncons
23 , uncons
27
24
28 -- * Right Folds
25 -- * Right Folds
29− , foldrM
30− , foldr
31− , foldrMx
32 , foldr1
26 , foldr1
33
27
34− -- * Left Folds
35− , foldlM'
36− , foldl'
37− , foldlMx'
38− , foldlx'
39−
40 -- * Specific Fold Functions
28 -- * Specific Fold Functions
41− , drain
42 , mapM_ -- Map and Fold
29 , mapM_ -- Map and Fold
43 , null
30 , null
44 , head
31 , head
45 , headElse
32 , headElse
33+ , init
46 , tail
34 , tail
47 , last
35 , last
48 , elem
36 , elem
@@ -60,16 +48,9 @@ module Streamly.Internal.Data.Stream.Eliminate
60 , the
48 , the
61
49
62 -- * To containers
50 -- * To containers
63− , toList
64 , toListRev
51 , toListRev
65
52
66 -- * Multi-Stream Folds
53 -- * Multi-Stream Folds
67− -- ** Comparisons
68− -- | These should probably be expressed using zipping operations.
69− , eqBy
70− , cmpBy
71−
72− -- ** Substreams
73 -- | These should probably be expressed using parsers.
54 -- | These should probably be expressed using parsers.
74 , isPrefixOf
55 , isPrefixOf
75 , isInfixOf
56 , isInfixOf
@@ -90,7 +71,7 @@ import Foreign.Storable (Storable)
90import GHC.Exts (SpecConstrAnnotation(..))
71import GHC.Exts (SpecConstrAnnotation(..))
91import GHC.Types (SPEC(..))
72import GHC.Types (SPEC(..))
92import Streamly.Internal.Data.Parser (ParseError(..))
73import Streamly.Internal.Data.Parser (ParseError(..))
93−import Streamly.Internal.Data.SVar.Type (defState)
74+import Streamly.Internal.Data.SVar.Type (adaptState, defState)
94import Streamly.Internal.Data.Unbox (Unbox)
75import Streamly.Internal.Data.Unbox (Unbox)
95
76
96import Streamly.Internal.Data.Maybe.Strict (Maybe'(..))
77import Streamly.Internal.Data.Maybe.Strict (Maybe'(..))
@@ -105,7 +86,7 @@ import qualified Streamly.Internal.Data.Stream.Transform as StreamD
105
86
106import Prelude hiding
87import Prelude hiding
107 ( Foldable(..), all, any, head, last, lookup, mapM, mapM_
88 ( Foldable(..), all, any, head, last, lookup, mapM, mapM_
108− , notElem, splitAt, tail, (!!))
89+ , notElem, splitAt, init, tail, (!!))
109import Data.Foldable (length)
90import Data.Foldable (length)
110import Streamly.Internal.Data.Stream.Type
91import Streamly.Internal.Data.Stream.Type
111
92
@@ -391,9 +372,35 @@ head = foldrM (\x _ -> return (Just x)) (return Nothing)
391headElse :: Monad m => a -> Stream m a -> m a
372headElse :: Monad m => a -> Stream m a -> m a
392headElse a = foldrM (\x _ -> return x) (return a)
373headElse a = foldrM (\x _ -> return x) (return a)
393
374
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+--
394-- Does not fuse, has the same performance as the StreamK version.
398-- Does not fuse, has the same performance as the StreamK version.
399+--
395{-# INLINE_NORMAL tail #-}
400{-# INLINE_NORMAL tail #-}
396tail :: Monad m => Stream m a -> m (Maybe (Stream m a))
401tail :: Monad m => Stream m a -> m (Maybe (Stream m a))
402+tail = fmap (fmap snd) . uncons
403+{-
397tail (UnStream step state) = go SPEC state
404tail (UnStream step state) = go SPEC state
398 where
405 where
399 go !_ st = do
406 go !_ st = do
@@ -402,6 +409,7 @@ tail (UnStream step state) = go SPEC state
402 Yield _ s -> return (Just $ Stream step s)
409 Yield _ s -> return (Just $ Stream step s)
403 Skip s -> go SPEC s
410 Skip s -> go SPEC s
404 Stop -> return Nothing
411 Stop -> return Nothing
412+-}
405
413
406-- XXX will it fuse? need custom impl?
414-- XXX will it fuse? need custom impl?
407{-# INLINE_NORMAL last #-}
415{-# INLINE_NORMAL last #-}
core/src/Streamly/Internal/Data/Stream/Exception.hs+9−6
@@ -9,17 +9,20 @@
9
9
10module Streamly.Internal.Data.Stream.Exception
10module Streamly.Internal.Data.Stream.Exception
11 (
11 (
12− gbracket_
12+ -- * Resources
13− , gbracket
14− , before
13+ before
15− , afterUnsafe
16 , afterIO
14 , afterIO
15+ , afterUnsafe
16+ , finallyIO
17+ , finallyUnsafe
18+ , gbracket_
19+ , gbracket
17 , bracketUnsafe
20 , bracketUnsafe
18 , bracketIO3
21 , bracketIO3
19 , bracketIO
22 , bracketIO
23+
24+ -- * Exceptions
20 , onException
25 , onException
21− , finallyUnsafe
22− , finallyIO
23 , ghandle
26 , ghandle
24 , handle
27 , handle
25 )
28 )
core/src/Streamly/Internal/Data/Stream/Generate.hs+2−12
@@ -16,20 +16,13 @@ module Streamly.Internal.Data.Stream.Generate
16 (
16 (
17 -- * Primitives
17 -- * Primitives
18 nil
18 nil
19− , nilM
20 , cons
19 , cons
21− , consM
22−
23− -- * From 'Unfold'
24− , unfold
25
20
26 -- * Unfolding
21 -- * Unfolding
27 , unfoldr
22 , unfoldr
28 , unfoldrM
23 , unfoldrM
29
24
30 -- * From Values
25 -- * From Values
31− , fromPure
32− , fromEffect
33 , repeat
26 , repeat
34 , repeatM
27 , repeatM
35 , replicate
28 , replicate
@@ -95,7 +88,6 @@ module Streamly.Internal.Data.Stream.Generate
95 -- * From Containers
88 -- * From Containers
96 -- | Transform an input structure into a stream.
89 -- | Transform an input structure into a stream.
97
90
98− , fromList
99 , fromListM
91 , fromListM
100 , fromFoldable
92 , fromFoldable
101 , fromFoldableM
93 , fromFoldableM
@@ -104,10 +96,6 @@ module Streamly.Internal.Data.Stream.Generate
104 , fromPtr
96 , fromPtr
105 , fromPtrN
97 , fromPtrN
106 , fromByteStr#
98 , fromByteStr#
107−
108− -- * Conversions
109− , fromStreamK
110− , toStreamK
111 )
99 )
112where
100where
113
101
@@ -154,6 +142,8 @@ import Streamly.Internal.Data.Stream.Type
154nil :: Applicative m => Stream m a
142nil :: Applicative m => Stream m a
155nil = Stream (\_ _ -> pure Stop) ()
143nil = Stream (\_ _ -> pure Stop) ()
156
144
145+infixr 5 `cons`
146+
157-- XXX implement in terms of consM?
147-- XXX implement in terms of consM?
158-- cons x = consM (return x)
148-- cons x = consM (return x)
159-- From an implementation perspective, StreamK.cons translates into a
149-- From an implementation perspective, StreamK.cons translates into a
core/src/Streamly/Internal/Data/Stream/Nesting.hs+19−146
@@ -55,11 +55,6 @@ module Streamly.Internal.Data.Stream.Nesting
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−
63 -- *** Merging
58 -- *** Merging
64 -- | Interleave elements from two streams based on a condition.
59 -- | Interleave elements from two streams based on a condition.
65 , mergeBy
60 , mergeBy
@@ -76,17 +71,10 @@ module Streamly.Internal.Data.Stream.Nesting
76 -- unfoldMany: Unfold m a b -> t m a -> t m b
71 -- unfoldMany: Unfold m a b -> t m a -> t m b
77 -- @
72 -- @
78
73
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−
85 -- *** ConcatUnfold
74 -- *** ConcatUnfold
86 -- | Generate streams by using an unfold on each element of an input
75 -- | Generate streams by using an unfold on each element of an input
87 -- stream, append the resulting streams and flatten. A special case of
76 -- stream, append the resulting streams and flatten. A special case of
88 -- gintercalate.
77 -- gintercalate.
89− , unfoldMany
90 , ConcatUnfoldInterleaveState (..)
78 , ConcatUnfoldInterleaveState (..)
91 , unfoldInterleave
79 , unfoldInterleave
92 , unfoldRoundRobin
80 , unfoldRoundRobin
@@ -118,11 +106,8 @@ module Streamly.Internal.Data.Stream.Nesting
118
106
119 -- ** Folding
107 -- ** Folding
120 -- | Apply folds on a stream.
108 -- | Apply folds on a stream.
121− , foldMany
122− , refoldMany
123 , foldSequence
109 , foldSequence
124 , foldIterateM
110 , foldIterateM
125− , refoldIterateM
126
111
127 -- ** Parsing
112 -- ** Parsing
128 -- | Parsing is opposite to flattening. 'parseMany' is dual to concatMap or
113 -- | Parsing is opposite to flattening. 'parseMany' is dual to concatMap or
@@ -139,7 +124,6 @@ module Streamly.Internal.Data.Stream.Nesting
139
124
140 -- ** Grouping
125 -- ** Grouping
141 -- | Group segments of a stream and fold. Special case of parsing.
126 -- | Group segments of a stream and fold. Special case of parsing.
142− , groupsOf
143 , groupsBy
127 , groupsBy
144 , groupsWhile
128 , groupsWhile
145 , groupsRollingBy
129 , groupsRollingBy
@@ -147,9 +131,8 @@ module Streamly.Internal.Data.Stream.Nesting
147 -- ** Splitting
131 -- ** Splitting
148 -- | A special case of parsing.
132 -- | A special case of parsing.
149 , wordsBy
133 , wordsBy
150− , splitOnSeq
134+ , splitOnSeq -- XXX splitOnSeg
151− , splitOnSuffixSeq
135+ , splitOnSuffixSeq -- XXX splitOnSegSuffix, splitOnTrailer
152− , sliceOnSuffix
153
136
154 -- XXX Implement these as folds or parsers instead.
137 -- XXX Implement these as folds or parsers instead.
155 , splitOnSuffixSeqAny
138 , splitOnSuffixSeqAny
@@ -158,9 +141,8 @@ module Streamly.Internal.Data.Stream.Nesting
158
141
159 -- * Transform (Nested Containers)
142 -- * Transform (Nested Containers)
160 -- | Opposite to compact in ArrayStream
143 -- | Opposite to compact in ArrayStream
161− , splitInnerBy
144+ , splitInnerBy -- XXX innerSplitOn
162− , splitInnerBySuffix
145+ , splitInnerBySuffix -- XXX innerSplitOnSuffix
163− , intersectBySorted
164
146
165 -- * Reduce By Streams
147 -- * Reduce By Streams
166 , dropPrefix
148 , dropPrefix
@@ -182,12 +164,9 @@ import Fusion.Plugin.Types (Fuse(..))
182import GHC.Types (SPEC(..))
164import GHC.Types (SPEC(..))
183
165
184import Streamly.Internal.Data.Array.Type (Array(..))
166import Streamly.Internal.Data.Array.Type (Array(..))
185−import Streamly.Internal.Data.Fold.Step (Step(..))
186import Streamly.Internal.Data.Fold.Type (Fold(..))
167import Streamly.Internal.Data.Fold.Type (Fold(..))
187import Streamly.Internal.Data.Parser (ParseError(..))
168import Streamly.Internal.Data.Parser (ParseError(..))
188−import Streamly.Internal.Data.Refold.Type (Refold(..))
189import Streamly.Internal.Data.SVar.Type (adaptState)
169import Streamly.Internal.Data.SVar.Type (adaptState)
190−import Streamly.Internal.Data.Tuple.Strict (Tuple'(..))
191import Streamly.Internal.Data.Unbox (Unbox, sizeOf)
170import Streamly.Internal.Data.Unbox (Unbox, sizeOf)
192import Streamly.Internal.Data.Unfold.Type (Unfold(..))
171import Streamly.Internal.Data.Unfold.Type (Unfold(..))
193
172
@@ -613,49 +592,6 @@ mergeFstBy :: -- Monad m =>
613mergeFstBy _f _m1 _m2 = undefined
592mergeFstBy _f _m1 _m2 = undefined
614 -- fromStreamK $ D.mergeFstBy f (toStreamD m1) (toStreamD m2)
593 -- fromStreamK $ D.mergeFstBy f (toStreamD m1) (toStreamD m2)
615
594
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−
659------------------------------------------------------------------------------
595------------------------------------------------------------------------------
660-- Combine N Streams - unfoldMany
596-- Combine N Streams - unfoldMany
661------------------------------------------------------------------------------
597------------------------------------------------------------------------------
@@ -1294,77 +1230,6 @@ foldIterateM func seed0 (Stream step state) =
1294 stepOuter _ (FIterYield a next) = return $ Yield a next
1230 stepOuter _ (FIterYield a next) = return $ Yield a next
1295 stepOuter _ FIterStop = return Stop
1231 stepOuter _ FIterStop = return Stop
1296
1232
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−
1368------------------------------------------------------------------------------
1233------------------------------------------------------------------------------
1369-- Parsing
1234-- Parsing
1370------------------------------------------------------------------------------
1235------------------------------------------------------------------------------
@@ -3065,12 +2930,13 @@ splitInnerBy splitter joiner (Stream step1 state1) =
3065-- | Performs infix separator style splitting.
2930-- | Performs infix separator style splitting.
3066{-# INLINE_NORMAL splitInnerBySuffix #-}
2931{-# INLINE_NORMAL splitInnerBySuffix #-}
3067splitInnerBySuffix
2932splitInnerBySuffix
3068− :: (Monad m, Eq (f a), Monoid (f a))
2933+ :: Monad m
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
3070 -> (f a -> f a -> m (f a)) -- joiner
2936 -> (f a -> f a -> m (f a)) -- joiner
3071 -> Stream m (f a)
2937 -> Stream m (f a)
3072 -> Stream m (f a)
2938 -> Stream m (f a)
3073−splitInnerBySuffix splitter joiner (Stream step1 state1) =
2939+splitInnerBySuffix isEmpty splitter joiner (Stream step1 state1) =
3074 Stream step (SplitInitial state1)
2940 Stream step (SplitInitial state1)
3075
2941
3076 where
2942 where
@@ -3097,10 +2963,11 @@ splitInnerBySuffix splitter joiner (Stream step1 state1) =
3097 Nothing -> Skip (SplitBuffering s buf')
2963 Nothing -> Skip (SplitBuffering s buf')
3098 Just x2 -> Skip (SplitYielding buf' (SplitSplitting s x2))
2964 Just x2 -> Skip (SplitYielding buf' (SplitSplitting s x2))
3099 Skip s -> return $ Skip (SplitBuffering s buf)
2965 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)
3104
2971
3105 step _ (SplitSplitting st buf) = do
2972 step _ (SplitSplitting st buf) = do
3106 (x1, mx2) <- splitter buf
2973 (x1, mx2) <- splitter buf
@@ -3119,6 +2986,8 @@ splitInnerBySuffix splitter joiner (Stream step1 state1) =
3119--
2986--
3120-- Space: @O(1)@
2987-- Space: @O(1)@
3121--
2988--
2989+-- See also stripPrefix.
2990+--
3122-- /Unimplemented/
2991-- /Unimplemented/
3123{-# INLINE dropPrefix #-}
2992{-# INLINE dropPrefix #-}
3124dropPrefix ::
2993dropPrefix ::
@@ -3131,6 +3000,8 @@ dropPrefix = error "Not implemented yet!"
3131--
3000--
3132-- Space: @O(n)@ where n is the length of the infix.
3001-- Space: @O(n)@ where n is the length of the infix.
3133--
3002--
3003+-- See also stripInfix.
3004+--
3134-- /Unimplemented/
3005-- /Unimplemented/
3135{-# INLINE dropInfix #-}
3006{-# INLINE dropInfix #-}
3136dropInfix ::
3007dropInfix ::
@@ -3143,6 +3014,8 @@ dropInfix = error "Not implemented yet!"
3143--
3014--
3144-- Space: @O(n)@ where n is the length of the suffix.
3015-- Space: @O(n)@ where n is the length of the suffix.
3145--
3016--
3017+-- See also stripSuffix.
3018+--
3146-- /Unimplemented/
3019-- /Unimplemented/
3147{-# INLINE dropSuffix #-}
3020{-# INLINE dropSuffix #-}
3148dropSuffix ::
3021dropSuffix ::
core/src/Streamly/Internal/Data/Stream/Top.hs+243−115
@@ -8,38 +8,73 @@
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.
11
48
12module Streamly.Internal.Data.Stream.Top
49module Streamly.Internal.Data.Stream.Top
13 (
50 (
14− -- * Transformation
15− -- ** Sampling
51+ -- * Sampling
16 -- | Value agnostic filtering.
52 -- | Value agnostic filtering.
17 strideFromThen
53 strideFromThen
18
54
19− -- * Nesting
55+ -- * Straight Joins
20− -- ** Set like operations
21− -- | These are not exactly set operations because streams are not
56+ -- | These are set-like operations but not exactly set operations because
22− -- necessarily sets, they may have duplicated elements. These operations
57+ -- streams are not necessarily sets, they may have duplicated elements.
23− -- are generic i.e. they work on streams of unconstrained types, therefore,
58+ -- These operations are generic i.e. they work on streams of unconstrained
24− -- they have quadratic performance characterstics. For better performance
59+ -- types, therefore, they have quadratic performance characterstics. For
60+ -- better performance using Set or Map structures see the
25− -- using Set structures see the Streamly.Internal.Data.Stream.Container
61+ -- Streamly.Internal.Data.Stream.Container module.
26− -- module.
62+ , intersectBy
27− , filterInStreamGenericBy
63+ , deleteFirstsBy
28− , deleteInStreamGenericBy
64+ , unionBy
29− , unionWithStreamGenericBy
65+
30−
31− -- ** Set like operations on sorted streams
66+ -- Set like operations on sorted streams
32− , filterInStreamAscBy
67+ , sortedIntersectBy
33− , deleteInStreamAscBy
68+ , sortedDeleteFirstsBy
34− , unionWithStreamAscBy
69+ , sortedUnionBy
35−
70+
36− -- ** Join operations
71+ -- * Cross Joins
37− , joinInnerGeneric
72+ , innerJoin
38−
73+
39− -- * Joins on sorted stream
74+ -- Joins on sorted stream
40− , joinInnerAscBy
75+ , innerSortedJoin
41− , joinLeftAscBy
76+ , leftSortedJoin
42− , joinOuterAscBy
77+ , outerSortedJoin
43 )
78 )
44where
79where
45
80
@@ -48,7 +83,7 @@ where
48import Control.Monad.IO.Class (MonadIO(..))
83import Control.Monad.IO.Class (MonadIO(..))
49import Data.IORef (newIORef, readIORef, modifyIORef')
84import Data.IORef (newIORef, readIORef, modifyIORef')
50import Streamly.Internal.Data.Fold.Type (Fold)
85import Streamly.Internal.Data.Fold.Type (Fold)
51−import Streamly.Internal.Data.Stream.Type (Stream, cross)
86+import Streamly.Internal.Data.Stream.Type (Stream(..), Step(..), cross)
52
87
53import qualified Data.List as List
88import qualified Data.List as List
54import qualified Streamly.Internal.Data.Fold as Fold
89import qualified Streamly.Internal.Data.Fold as Fold
@@ -110,29 +145,40 @@ strideFromThen offset stride =
110-- binary search if we have an Ord instance or Ordering returning function. The
145-- binary search if we have an Ord instance or Ordering returning function. The
111-- time complexity would then become (m x log n).
146-- time complexity would then become (m x log n).
112
147
113−-- | Like 'cross' but emits only those tuples where @a == b@ using the
148+-- | Like 'cross' but emits only those tuples where @a == b@ using the supplied
114−-- supplied equality predicate.
149+-- equality predicate. This is essentially a @cross intersection@ of two
150+-- streams.
115--
151--
116-- Definition:
152-- Definition:
117--
153--
118−-- >>> joinInnerGeneric eq s1 s2 = Stream.filter (\(a, b) -> a `eq` b) $ Stream.cross s1 s2
154+-- >>> innerJoin eq s1 s2 = Stream.filter (\(a, b) -> a `eq` b) $ Stream.cross s1 s2
155+--
156+-- The second (inner) stream must be finite. Moreover, it must be either pure
157+-- or capable of multiple evaluations. If not then the caller should cache it
158+-- in an 'Data.Array.Array', if the type does not have an 'Unbox' instance then
159+-- use the Generic 'Data.Array.Generic.Array'. Convert the array to stream
160+-- before calling this function. Caching may also improve performance if the
161+-- stream is expensive to evaluate.
119--
162--
120−-- You should almost always prefer @joinInnerOrd@ over 'joinInnerGeneric' if
163+-- If you care about performance this function should be your last choice among
164+-- all inner joins. 'Streamly.Internal.Data.Unfold.innerJoin' is a much faster
165+-- fused alternative. 'innerSortedJoin' is a faster alternative when streams
121−-- possible. @joinInnerOrd@ is an order of magnitude faster but may take more
166+-- are sorted. 'innerOrdJoin' is an order of magnitude faster alternative when
122−-- space for caching the second stream.
167+-- the type has an 'Ord' instance.
123--
168--
124−-- See 'Streamly.Internal.Data.Unfold.joinInnerGeneric' for a much faster fused
169+-- Note: Conceptually, this is a commutative operation. Result includes all the
125−-- alternative.
170+-- elements from the left and the right stream. The order of streams can be
171+-- changed without affecting results, except for the ordering within the tuple.
126--
172--
127-- Time: O(m x n)
173-- Time: O(m x n)
128--
174--
129-- /Pre-release/
175-- /Pre-release/
130−{-# INLINE joinInnerGeneric #-}
176+{-# INLINE innerJoin #-}
131−joinInnerGeneric :: Monad m =>
177+innerJoin :: Monad m =>
132 (a -> b -> Bool) -> Stream m a -> Stream m b -> Stream m (a, b)
178 (a -> b -> Bool) -> Stream m a -> Stream m b -> Stream m (a, b)
133−joinInnerGeneric eq s1 s2 = Stream.filter (\(a, b) -> a `eq` b) $ cross s1 s2
179+innerJoin eq s1 s2 = Stream.filter (\(a, b) -> a `eq` b) $ cross s1 s2
134{-
180{-
135−joinInnerGeneric eq s1 s2 = do
181+innerJoin eq s1 s2 = do
136 -- ConcatMap works faster than bind
182 -- ConcatMap works faster than bind
137 Stream.concatMap (\a ->
183 Stream.concatMap (\a ->
138 Stream.concatMap (\b ->
184 Stream.concatMap (\b ->
@@ -143,44 +189,44 @@ joinInnerGeneric eq s1 s2 = do
143 ) s1
189 ) s1
144-}
190-}
145
191
146−-- | A more efficient 'joinInner' for sorted streams.
192+-- | A more efficient 'innerJoin' for sorted streams.
147--
193--
148-- Space: O(1)
194-- Space: O(1)
149--
195--
150-- Time: O(m + n)
196-- Time: O(m + n)
151--
197--
152-- /Unimplemented/
198-- /Unimplemented/
153−{-# INLINE joinInnerAscBy #-}
199+{-# INLINE innerSortedJoin #-}
154−joinInnerAscBy ::
200+innerSortedJoin ::
155 (a -> b -> Ordering) -> Stream m a -> Stream m b -> Stream m (a, b)
201 (a -> b -> Ordering) -> Stream m a -> Stream m b -> Stream m (a, b)
156−joinInnerAscBy = undefined
202+innerSortedJoin = undefined
157
203
158−-- | A more efficient 'joinLeft' for sorted streams.
204+-- | A more efficient 'leftJoin' for sorted streams.
159--
205--
160-- Space: O(1)
206-- Space: O(1)
161--
207--
162-- Time: O(m + n)
208-- Time: O(m + n)
163--
209--
164-- /Unimplemented/
210-- /Unimplemented/
165−{-# INLINE joinLeftAscBy #-}
211+{-# INLINE leftSortedJoin #-}
166−joinLeftAscBy :: -- Monad m =>
212+leftSortedJoin :: -- Monad m =>
167 (a -> b -> Ordering) -> Stream m a -> Stream m b -> Stream m (a, Maybe b)
213 (a -> b -> Ordering) -> Stream m a -> Stream m b -> Stream m (a, Maybe b)
168−joinLeftAscBy _eq _s1 _s2 = undefined
214+leftSortedJoin _eq _s1 _s2 = undefined
169
215
170−-- | A more efficient 'joinOuter' for sorted streams.
216+-- | A more efficient 'outerJoin' for sorted streams.
171--
217--
172-- Space: O(1)
218-- Space: O(1)
173--
219--
174-- Time: O(m + n)
220-- Time: O(m + n)
175--
221--
176-- /Unimplemented/
222-- /Unimplemented/
177−{-# INLINE joinOuterAscBy #-}
223+{-# INLINE outerSortedJoin #-}
178−joinOuterAscBy :: -- Monad m =>
224+outerSortedJoin :: -- Monad m =>
179 (a -> b -> Ordering)
225 (a -> b -> Ordering)
180 -> Stream m a
226 -> Stream m a
181 -> Stream m b
227 -> Stream m b
182 -> Stream m (Maybe a, Maybe b)
228 -> Stream m (Maybe a, Maybe b)
183−joinOuterAscBy _eq _s1 _s2 = undefined
229+outerSortedJoin _eq _s1 _s2 = undefined
184
230
185------------------------------------------------------------------------------
231------------------------------------------------------------------------------
186-- Set operations (special joins)
232-- Set operations (special joins)
@@ -190,8 +236,8 @@ joinOuterAscBy _eq _s1 _s2 = undefined
190-- the best would be to use an Array with linear search. If the second stream
236-- the best would be to use an Array with linear search. If the second stream
191-- is sorted we can also use a binary search, using Ord constraint.
237-- is sorted we can also use a binary search, using Ord constraint.
192
238
193−-- | Keep only those elements in the second stream that are present in the
239+-- | Keep only those elements in the first stream that are present in the
194−-- first stream too. The first stream is folded to a container using the
240+-- second stream too. The second stream is folded to a container using the
195-- supplied fold and then the elements in the container are looked up using the
241-- supplied fold and then the elements in the container are looked up using the
196-- supplied lookup function.
242-- supplied lookup function.
197--
243--
@@ -206,23 +252,30 @@ filterStreamWith :: Monad m =>
206filterStreamWith fld member s1 s2 =
252filterStreamWith fld member s1 s2 =
207 Stream.concatEffect
253 Stream.concatEffect
208 $ do
254 $ do
209− xs <- Stream.fold fld s1
255+ xs <- Stream.fold fld s2
210− return $ Stream.filter (`member` xs) s2
256+ return $ Stream.filter (`member` xs) s1
211
257
212−-- | 'filterInStreamGenericBy' retains only those elements in the second stream that
258+-- XXX instead of folding the second stream to a list we could use it directly.
213−-- are present in the first stream.
259+-- If the user wants they can generate the stream from an array and also call
214−--
260+-- uniq or nub on it. We can provide a convenience Stream -> Stream to cache
215−-- >>> Stream.fold Fold.toList $ Stream.filterInStreamGenericBy (==) (Stream.fromList [1,2,2,4]) (Stream.fromList [2,1,1,3])
261+-- a finite stream in an array and serve it from the cache. The user can decide
216−-- [2,1,1]
262+-- what is best based on the context. They can also choose to use a boxed or
217−--
263+-- unboxed array for caching. To force caching we can make the second stream
218−-- >>> Stream.fold Fold.toList $ Stream.filterInStreamGenericBy (==) (Stream.fromList [2,1,1,3]) (Stream.fromList [1,2,2,4])
264+-- monad type Identity. But that may be less flexible. One option is to use
219−-- [1,2,2]
265+-- cachedIntersectBy etc for automatic caching.
266+
267+-- | 'intersectBy' returns a subsequence of the first stream which intersects
268+-- with the second stream. Note that this is not a commutative operation unlike
269+-- a set intersection, because of duplicate elements in the stream the order of
270+-- the streams matters. This is similar to 'Data.List.intersectBy'. Note that
271+-- intersectBy is a special case of 'innerJoin'.
220--
272--
221−-- Similar to the list intersectBy operation but with the stream argument order
273+-- >>> f s1 s2 = Stream.fold Fold.toList $ Stream.intersectBy (==) (Stream.fromList s1) (Stream.fromList s2)
222−-- flipped.
274+-- >>> f [1,3,4,4,5]) [2,3,4,5,5]
275+-- [3,4,4,5]
223--
276--
224−-- The first stream must be finite and must not block. Second stream is
277+-- First stream can be infinite, the second stream must be finite and must be
225−-- processed only after the first stream is fully realized.
278+-- capable of multiple evaluations.
226--
279--
227-- Space: O(n) where @n@ is the number of elements in the second stream.
280-- Space: O(n) where @n@ is the number of elements in the second stream.
228--
281--
@@ -230,46 +283,87 @@ filterStreamWith fld member s1 s2 =
230-- @n@ is the number of elements in the second stream.
283-- @n@ is the number of elements in the second stream.
231--
284--
232-- /Pre-release/
285-- /Pre-release/
233−{-# INLINE filterInStreamGenericBy #-}
286+{-# INLINE intersectBy #-}
234−filterInStreamGenericBy :: Monad m =>
287+intersectBy :: Monad m =>
235 (a -> a -> Bool) -> Stream m a -> Stream m a -> Stream m a
288 (a -> a -> Bool) -> Stream m a -> Stream m a -> Stream m a
236−filterInStreamGenericBy eq =
289+intersectBy eq =
237 -- XXX Use an (unboxed) array instead.
290 -- XXX Use an (unboxed) array instead.
238 filterStreamWith
291 filterStreamWith
239 (Fold.scanMaybe (Fold.uniqBy eq) Fold.toListRev)
292 (Fold.scanMaybe (Fold.uniqBy eq) Fold.toListRev)
240 (List.any . eq)
293 (List.any . eq)
241
294
295+-------------------------------------------------------------------------------
296+-- Intersection of sorted streams
297+-------------------------------------------------------------------------------
298+
299+-- XXX The sort order is not important as long both the streams have the same
300+-- sort order. We need to move only in one direction in each stream.
301+-- XXX Fix the argument order to use the same behavior as intersectBy.
302+
242−-- | Like 'filterInStreamGenericBy' but assumes that the input streams are sorted in
303+-- | Like 'intersectBy' but assumes that the input streams are sorted in
243-- ascending order. To use it on streams sorted in descending order pass an
304-- ascending order. To use it on streams sorted in descending order pass an
244-- inverted comparison function returning GT for less than and LT for greater
305-- inverted comparison function returning GT for less than and LT for greater
245-- than.
306-- than.
246--
307--
308+-- Both streams can be infinite.
309+--
247-- Space: O(1)
310-- Space: O(1)
248--
311--
249-- Time: O(m+n)
312-- Time: O(m+n)
250--
313--
251-- /Pre-release/
314-- /Pre-release/
252−{-# INLINE filterInStreamAscBy #-}
315+{-# INLINE_NORMAL sortedIntersectBy #-}
253−filterInStreamAscBy :: Monad m =>
316+sortedIntersectBy :: Monad m =>
254 (a -> a -> Ordering) -> Stream m a -> Stream m a -> Stream m a
317 (a -> a -> Ordering) -> Stream m a -> Stream m a -> Stream m a
255−filterInStreamAscBy eq s1 s2 = Stream.intersectBySorted eq s2 s1
318+sortedIntersectBy cmp (Stream stepa ta) (Stream stepb tb) =
319+ Stream step
320+ ( ta -- left stream state
321+ , tb -- right stream state
322+ , Nothing -- left value
323+ , Nothing -- right value
324+ )
256
325
257−-- | Delete all elements of the first stream from the seconds stream. If an
326+ where
258−-- element occurs multiple times in the first stream as many occurrences of it
327+
259−-- are deleted from the second stream.
328+ {-# INLINE_LATE step #-}
260−--
329+ -- step 1, fetch the first value
261−-- >>> Stream.fold Fold.toList $ Stream.deleteInStreamGenericBy (==) (Stream.fromList [1,2,3]) (Stream.fromList [1,2,2])
330+ step gst (sa, sb, Nothing, b) = do
262−-- [2]
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'.
263--
356--
264−-- The following laws hold:
357+-- >>> f xs ys = Stream.fold Fold.toList $ Stream.deleteFirstsBy (==) (Stream.fromList xs) (Stream.fromList ys)
358+-- >>> f [1,2,2,3,3,5] [1,2,2,3,4]
359+-- [2,3,5]
265--
360--
266−-- > deleteInStreamGenericBy (==) s1 (s1 `append` s2) === s2
361+-- The following holds:
267−-- > deleteInStreamGenericBy (==) s1 (s1 `interleave` s2) === s2
268--
362--
269−-- Same as the list 'Data.List.//' operation but with argument order flipped.
363+-- > deleteFirstsBy (==) (Stream.nub s2 `append` s1) s2 === s1
364+-- > deleteFirstsBy (==) (Stream.nub s2 `interleave` s1) s2 === s1
270--
365--
271−-- The first stream must be finite and must not block. Second stream is
366+-- First stream can be infinite, second stream must be finite.
272−-- processed only after the first stream is fully realized.
273--
367--
274-- Space: O(m) where @m@ is the number of elements in the first stream.
368-- Space: O(m) where @m@ is the number of elements in the first stream.
275--
369--
@@ -277,58 +371,83 @@ filterInStreamAscBy eq s1 s2 = Stream.intersectBySorted eq s2 s1
277-- @n@ is the number of elements in the second stream.
371-- @n@ is the number of elements in the second stream.
278--
372--
279-- /Pre-release/
373-- /Pre-release/
280−{-# INLINE deleteInStreamGenericBy #-}
374+{-# INLINE deleteFirstsBy #-}
281−deleteInStreamGenericBy :: Monad m =>
375+deleteFirstsBy :: Monad m =>
282 (a -> a -> Bool) -> Stream m a -> Stream m a -> Stream m a
376 (a -> a -> Bool) -> Stream m a -> Stream m a -> Stream m a
283−deleteInStreamGenericBy eq s1 s2 =
377+deleteFirstsBy eq s2 s1 =
284− Stream.concatEffect
378+ -- XXX s2 can be a sorted mutable array and we can use binary
285− $ do
379+ -- search to find. Mark the element deleted, count the deletions
286− -- This may work well if s1 is small
380+ -- and reconsolidate the array when a min number of elements is
287− -- If s1 is big we can go through s1, deleting elements from s2 and
381+ -- deleted.
288− -- not emitting an element if it was successfully deleted from s2.
289− -- we will need a deleteBy that can return whether the element was
290− -- deleted or not.
291− xs <- Stream.fold Fold.toList s2
292− let f = Fold.foldl' (flip (List.deleteBy eq)) xs
293− fmap Stream.fromList $ Stream.fold f s1
294
382
383+ -- XXX Use StreamK or list as second argument instead of Stream to avoid
384+ -- concatEffect?
385+ Stream.concatEffect $ do
386+ xs <- Stream.toList s1
387+ -- It reverses the list but that is fine.
388+ let del x =
389+ List.foldl' (\(ys,res) y ->
390+ if x `eq` y
391+ then (ys, True)
392+ else (x:ys, res)) ([], False)
393+ g (ys,_) x =
394+ let (ys1, deleted) = del x ys
395+ in if deleted
396+ then (ys1, Nothing)
397+ else (ys1, Just x)
398+ in return
399+ $ Stream.catMaybes
400+ $ fmap snd
401+ $ Stream.postscanl' g (xs, Nothing) s2
402+
295−-- | A more efficient 'deleteInStreamGenericBy' for streams sorted in ascending order.
403+-- | A more efficient 'deleteFirstsBy' for streams sorted in ascending order.
404+--
405+-- Both streams can be infinite.
296--
406--
297-- Space: O(1)
407-- Space: O(1)
298--
408--
299-- /Unimplemented/
409-- /Unimplemented/
300−{-# INLINE deleteInStreamAscBy #-}
410+{-# INLINE sortedDeleteFirstsBy #-}
301−deleteInStreamAscBy :: -- (Monad m) =>
411+sortedDeleteFirstsBy :: -- (Monad m) =>
302 (a -> a -> Ordering) -> Stream m a -> Stream m a -> Stream m a
412 (a -> a -> Ordering) -> Stream m a -> Stream m a -> Stream m a
303−deleteInStreamAscBy _eq _s1 _s2 = undefined
413+sortedDeleteFirstsBy _eq _s1 _s2 = undefined
304
414
305-- XXX Remove the MonadIO constraint. We can just cache one stream and then
415-- XXX Remove the MonadIO constraint. We can just cache one stream and then
306-- implement using differenceEqBy.
416-- implement using differenceEqBy.
307
417
308−-- | This essentially appends to the second stream all the occurrences of
418+-- | Returns the first stream appended with those unique elements from the
309−-- elements in the first stream that are not already present in the second
419+-- second stream that are not already present in the first stream. Note that
310−-- stream.
420+-- this is not a commutative operation unlike a set union, argument order
421+-- matters. The behavior is similar to the 'Data.List.unionBy'.
311--
422--
312-- Equivalent to the following except that @s2@ is evaluated only once:
423-- Equivalent to the following except that @s2@ is evaluated only once:
313--
424--
314−-- >>> unionWithStreamGenericBy eq s1 s2 = s2 `Stream.append` (Stream.deleteInStreamGenericBy eq s2 s1)
425+-- >>> unionBy eq s1 s2 = s1 `Stream.append` Stream.deleteFirstsBy eq s1 (Stream.nub s2)
315--
426--
316-- Example:
427-- Example:
317--
428--
318−-- >>> Stream.fold Fold.toList $ Stream.unionWithStreamGenericBy (==) (Stream.fromList [1,1,2,3]) (Stream.fromList [1,2,2,4])
429+-- >>> f s1 s2 = Stream.fold Fold.toList $ Stream.unionBy (==) (Stream.fromList s1) (Stream.fromList s2)
430+-- >>> f [1,2,2,4] [1,1,2,3,3]
319-- [1,2,2,4,3]
431-- [1,2,2,4,3]
320--
432--
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+--
321-- Space: O(n)
438-- Space: O(n)
322--
439--
323-- Time: O(m x n)
440-- Time: O(m x n)
324--
441--
325-- /Pre-release/
442-- /Pre-release/
326−{-# INLINE unionWithStreamGenericBy #-}
443+{-# INLINE unionBy #-}
327−unionWithStreamGenericBy :: MonadIO m =>
444+unionBy :: MonadIO m =>
328 (a -> a -> Bool) -> Stream m a -> Stream m a -> Stream m a
445 (a -> a -> Bool) -> Stream m a -> Stream m a -> Stream m a
329−unionWithStreamGenericBy eq s1 s2 =
446+unionBy eq s2 s1 =
330 Stream.concatEffect
447 Stream.concatEffect
331 $ do
448 $ 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.
332 xs <- Stream.fold Fold.toList s1
451 xs <- Stream.fold Fold.toList s1
333 -- XXX we can use postscanlMAfter' instead of IORef
452 -- XXX we can use postscanlMAfter' instead of IORef
334 ref <- liftIO $ newIORef $! List.nubBy eq xs
453 ref <- liftIO $ newIORef $! List.nubBy eq xs
@@ -341,12 +460,21 @@ unionWithStreamGenericBy eq s1 s2 =
341 return $ Stream.fromList xs1
460 return $ Stream.fromList xs1
342 return $ Stream.mapM f s2 `Stream.append` s3
461 return $ Stream.mapM f s2 `Stream.append` s3
343
462
344−-- | A more efficient 'unionWithStreamGenericBy' for sorted streams.
463+-- | A more efficient 'unionBy' for sorted streams.
464+--
465+-- Note that the behavior is different from 'unionBy'. In 'unionBy' we append
466+-- the unique elements from second stream only after exhausting the first one
467+-- whereas in sorted streams we can determine unique elements early even when
468+-- we are going through the first stream. Thus the result is an interleaving of
469+-- the two streams, merging those elements from the second stream that are not
470+-- present in the first.
345--
471--
346-- Space: O(1)
472-- Space: O(1)
347--
473--
474+-- Both streams can be infinite.
475+--
348-- /Unimplemented/
476-- /Unimplemented/
349−{-# INLINE unionWithStreamAscBy #-}
477+{-# INLINE sortedUnionBy #-}
350−unionWithStreamAscBy :: -- (Monad m) =>
478+sortedUnionBy :: -- (Monad m) =>
351 (a -> a -> Ordering) -> Stream m a -> Stream m a -> Stream m a
479 (a -> a -> Ordering) -> Stream m a -> Stream m a -> Stream m a
352−unionWithStreamAscBy _eq _s1 _s2 = undefined
480+sortedUnionBy _eq _s1 _s2 = undefined
core/src/Streamly/Internal/Data/Stream/Transform.hs+53−27
@@ -21,8 +21,6 @@ module Streamly.Internal.Data.Stream.Transform
21
21
22 -- * Mapping
22 -- * Mapping
23 -- | Stateless one-to-one maps.
23 -- | Stateless one-to-one maps.
24− , map
25− , mapM
26 , sequence
24 , sequence
27
25
28 -- * Mapping Effects
26 -- * Mapping Effects
@@ -75,7 +73,7 @@ module Streamly.Internal.Data.Stream.Transform
75 , scanMaybe
73 , scanMaybe
76 , filter
74 , filter
77 , filterM
75 , filterM
78− , deleteBy
76+ , deleteBy -- deleteOnceBy?
79 , uniqBy
77 , uniqBy
80 , uniq
78 , uniq
81 , prune
79 , prune
@@ -83,9 +81,8 @@ module Streamly.Internal.Data.Stream.Transform
83
81
84 -- * Trimming
82 -- * Trimming
85 -- | Produce a subset of the stream trimmed at ends.
83 -- | Produce a subset of the stream trimmed at ends.
86− , take
84+ , initNonEmpty
87− , takeWhile
85+ , tailNonEmpty
88− , takeWhileM
89 , takeWhileLast
86 , takeWhileLast
90 , takeWhileAround
87 , takeWhileAround
91 , drop
88 , drop
@@ -132,7 +129,6 @@ module Streamly.Internal.Data.Stream.Transform
132 -- * Searching
129 -- * Searching
133 , findIndices
130 , findIndices
134 , elemIndices
131 , elemIndices
135− , slicesBy
136
132
137 -- * Rolling map
133 -- * Rolling map
138 -- | Map using the previous element.
134 -- | Map using the previous element.
@@ -1074,6 +1070,55 @@ repeated = undefined
1074-- Trimming
1070-- Trimming
1075------------------------------------------------------------------------------
1071------------------------------------------------------------------------------
1076
1072
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+
1077-- | Take all consecutive elements at the end of the stream for which the
1122-- | Take all consecutive elements at the end of the stream for which the
1078-- predicate is true.
1123-- predicate is true.
1079--
1124--
@@ -1537,7 +1582,7 @@ reverse m = Stream step Nothing
1537{-# INLINE reverseUnbox #-}
1582{-# INLINE reverseUnbox #-}
1538reverseUnbox :: (MonadIO m, Unbox a) => Stream m a -> Stream m a
1583reverseUnbox :: (MonadIO m, Unbox a) => Stream m a -> Stream m a
1539reverseUnbox =
1584reverseUnbox =
1540− A.flattenArraysRev -- unfoldMany A.readRev
1585+ A.concatRev -- unfoldMany A.readerRev
1541 . fromStreamK
1586 . fromStreamK
1542 . K.reverse
1587 . K.reverse
1543 . toStreamK
1588 . toStreamK
@@ -1709,25 +1754,6 @@ findIndices p (Stream step state) = Stream step' (state, 0)
1709elemIndices :: (Monad m, Eq a) => a -> Stream m a -> Stream m Int
1754elemIndices :: (Monad m, Eq a) => a -> Stream m a -> Stream m Int
1710elemIndices a = findIndices (== a)
1755elemIndices a = findIndices (== a)
1711
1756
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−
1731------------------------------------------------------------------------------
1757------------------------------------------------------------------------------
1732-- Rolling map
1758-- Rolling map
1733------------------------------------------------------------------------------
1759------------------------------------------------------------------------------
core/src/Streamly/Internal/Data/Stream/Transformer.hs+2−1
@@ -11,10 +11,11 @@
11
11
12module Streamly.Internal.Data.Stream.Transformer
12module Streamly.Internal.Data.Stream.Transformer
13 (
13 (
14+ -- * Fold to Transformer Monad
14 foldlT
15 foldlT
15 , foldrT
16 , foldrT
16
17
17− -- * Transform Inner Monad
18+ -- * Inner Monad Operations
18 , liftInner
19 , liftInner
19 , runReaderT
20 , runReaderT
20 , usingReaderT
21 , usingReaderT
core/src/Streamly/Internal/Data/Stream/Type.hs+132−3
@@ -1,5 +1,11 @@
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 #-}
2{-# LANGUAGE UndecidableInstances #-}
7{-# LANGUAGE UndecidableInstances #-}
8+{-# LANGUAGE ViewPatterns #-}
3
9
4-- |
10-- |
5-- Module : Streamly.Internal.Data.Stream.Type
11-- Module : Streamly.Internal.Data.Stream.Type
@@ -16,18 +22,18 @@
16
22
17module Streamly.Internal.Data.Stream.Type
23module Streamly.Internal.Data.Stream.Type
18 (
24 (
19− -- * The stream type
25+ -- * Type
20 Step (..)
26 Step (..)
21 -- XXX UnStream is exported to avoid a performance issue in some
27 -- XXX UnStream is exported to avoid a performance issue in some
22 -- combinators if we use the pattern synonym "Stream".
28 -- combinators if we use the pattern synonym "Stream".
23 , Stream (Stream, UnStream)
29 , Stream (Stream, UnStream)
24
30
25− -- * CrossStream type wrapper
31+ -- * CrossStream
26 , CrossStream
32 , CrossStream
27 , unCross
33 , unCross
28 , mkCross
34 , mkCross
29
35
30− -- * Conversion to StreamK
36+ -- * To StreamK
31 , fromStreamK
37 , fromStreamK
32 , toStreamK
38 , toStreamK
33
39
@@ -85,6 +91,7 @@ module Streamly.Internal.Data.Stream.Type
85
91
86 -- * Combining Two Streams
92 -- * Combining Two Streams
87 -- ** Zipping
93 -- ** Zipping
94+ -- | Zip corresponding elements of two streams.
88 , zipWithM
95 , zipWithM
89 , zipWith
96 , zipWith
90
97
@@ -100,6 +107,8 @@ module Streamly.Internal.Data.Stream.Type
100 , unfoldMany
107 , unfoldMany
101
108
102 -- * Concat
109 -- * Concat
110+ -- | Generate streams by mapping a stream generator on each element of an
111+ -- input stream, append the resulting streams and flatten.
103 , concatEffect
112 , concatEffect
104 , concatMap
113 , concatMap
105 , concatMapM
114 , concatMapM
@@ -123,14 +132,22 @@ module Streamly.Internal.Data.Stream.Type
123 , foldManyPost
132 , foldManyPost
124 , groupsOf
133 , groupsOf
125 , refoldMany
134 , refoldMany
135+ , refoldIterateM
126
136
127 -- * Fold Iterate
137 -- * Fold Iterate
128 , reduceIterateBfs
138 , reduceIterateBfs
129 , foldIterateBfs
139 , foldIterateBfs
130
140
141+ -- * Splitting
142+ , indexOnSuffix
143+
131 -- * Multi-stream folds
144 -- * Multi-stream folds
145+ -- | These should probably be expressed using zipping operations.
132 , eqBy
146 , eqBy
133 , cmpBy
147 , cmpBy
148+
149+ -- * Deprecated
150+ , sliceOnSuffix
134 )
151 )
135where
152where
136
153
@@ -162,6 +179,7 @@ import Streamly.Internal.Data.Maybe.Strict (Maybe'(..), toMaybe)
162import Streamly.Internal.Data.Refold.Type (Refold(..))
179import Streamly.Internal.Data.Refold.Type (Refold(..))
163import Streamly.Internal.Data.Stream.Step (Step (..))
180import Streamly.Internal.Data.Stream.Step (Step (..))
164import Streamly.Internal.Data.SVar.Type (State, adaptState, defState)
181import Streamly.Internal.Data.SVar.Type (State, adaptState, defState)
182+import Streamly.Internal.Data.Tuple.Strict (Tuple'(..))
165import Streamly.Internal.Data.Unfold.Type (Unfold(..))
183import Streamly.Internal.Data.Unfold.Type (Unfold(..))
166
184
167import qualified Streamly.Internal.Data.Fold.Type as FL hiding (foldr)
185import qualified Streamly.Internal.Data.Fold.Type as FL hiding (foldr)
@@ -212,6 +230,9 @@ pattern Stream step state <- (unShare -> UnStream step state)
212nilM :: Applicative m => m b -> Stream m a
230nilM :: Applicative m => m b -> Stream m a
213nilM m = Stream (\_ _ -> m $> Stop) ()
231nilM m = Stream (\_ _ -> m $> Stop) ()
214
232
233+
234+infixr 5 `consM`
235+
215-- | Like 'cons' but fuses an effect instead of a pure value.
236-- | Like 'cons' but fuses an effect instead of a pure value.
216{-# INLINE_NORMAL consM #-}
237{-# INLINE_NORMAL consM #-}
217consM :: Applicative m => m a -> Stream m a -> Stream m a
238consM :: Applicative m => m a -> Stream m a -> Stream m a
@@ -551,8 +572,12 @@ foldAdd f =
551-- >>> Stream.foldrM step (return False) s
572-- >>> Stream.foldrM step (return False) s
552-- True
573-- True
553--
574--
575+-- >>> import Control.Monad (join)
576+-- >>> foldrM f z = join $ Stream.foldr f z
577+--
554{-# INLINE_NORMAL foldrM #-}
578{-# INLINE_NORMAL foldrM #-}
555foldrM :: Monad m => (a -> m b -> m b) -> m b -> Stream m a -> m b
579foldrM :: 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
556foldrM f z (Stream step state) = go SPEC state
581foldrM f z (Stream step state) = go SPEC state
557 where
582 where
558 {-# INLINE_LATE go #-}
583 {-# INLINE_LATE go #-}
@@ -1971,6 +1996,110 @@ refoldMany (Refold fstep inject extract) action (Stream step state) =
1971 step' _ (FoldManyYield b next) = return $ Yield b next
1996 step' _ (FoldManyYield b next) = return $ Yield b next
1972 step' _ FoldManyDone = return Stop
1997 step' _ FoldManyDone = return Stop
1973
1998
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+
1974------------------------------------------------------------------------------
2103------------------------------------------------------------------------------
1975-- Stream with a cross product style monad instance
2104-- Stream with a cross product style monad instance
1976------------------------------------------------------------------------------
2105------------------------------------------------------------------------------
core/src/Streamly/Internal/Data/StreamK.hs+19−28
@@ -16,16 +16,15 @@ module Streamly.Internal.Data.StreamK
16 --
16 --
17 -- $setup
17 -- $setup
18
18
19− -- * The stream type
20 module Streamly.Internal.Data.StreamK.Type
19 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− , StreamK(..)
23+ -- * From containers
24 , fromList
24 , fromList
25 , fromStream
25 , fromStream
26− , toStream
27
26
28− -- ** Specialized Generation
27+ -- * Specialized Generation
29 , repeatM
28 , repeatM
30 , replicate
29 , replicate
31 , replicateM
30 , replicateM
@@ -37,8 +36,6 @@ module Streamly.Internal.Data.StreamK
37 -- * Elimination
36 -- * Elimination
38 -- ** General Folds
37 -- ** General Folds
39 , foldr1
38 , foldr1
40− , foldlM'
41− , foldlMx'
42 , fold
39 , fold
43 , foldBreak
40 , foldBreak
44 , foldEither
41 , foldEither
@@ -69,13 +66,13 @@ module Streamly.Internal.Data.StreamK
69 , find
66 , find
70 , (!!)
67 , (!!)
71
68
69+ -- ** To Containers
70+ , toList
71+ , toStream
72+
72 -- ** Map and Fold
73 -- ** Map and Fold
73 , mapM_
74 , mapM_
74
75
75− -- ** Conversions
76− , toList
77− , hoist
78−
79 -- * Transformation
76 -- * Transformation
80 -- ** By folding (scans)
77 -- ** By folding (scans)
81 , scanl'
78 , scanl'
@@ -102,6 +99,7 @@ module Streamly.Internal.Data.StreamK
102
99
103 -- ** Reordering
100 -- ** Reordering
104 , sortBy
101 , sortBy
102+ , sortOn
105
103
106 -- ** Map and Filter
104 -- ** Map and Filter
107 , mapMaybe
105 , mapMaybe
@@ -117,6 +115,9 @@ module Streamly.Internal.Data.StreamK
117 -- ** Transformation comprehensions
115 -- ** Transformation comprehensions
118 , the
116 , the
119
117
118+ -- ** Transforming Inner Monad
119+ , hoist
120+
120 -- * Exceptions
121 -- * Exceptions
121 , handle
122 , handle
122
123
@@ -133,6 +134,7 @@ import Control.Exception (mask_, Exception)
133import Control.Monad (void, join)
134import Control.Monad (void, join)
134import Control.Monad.Catch (MonadCatch)
135import Control.Monad.Catch (MonadCatch)
135import Control.Monad.IO.Class (MonadIO(..))
136import Control.Monad.IO.Class (MonadIO(..))
137+import Data.Ord (comparing)
136import Data.Proxy (Proxy(..))
138import Data.Proxy (Proxy(..))
137import GHC.Types (SPEC(..))
139import GHC.Types (SPEC(..))
138import Streamly.Internal.Data.Array.Type (Array(..))
140import Streamly.Internal.Data.Array.Type (Array(..))
@@ -306,19 +308,6 @@ foldr1 step m = do
306 yieldk a r = fmap (step p) (go a r)
308 yieldk a r = fmap (step p) (go a r)
307 in foldStream defState yieldk single stp m1
309 in foldStream defState yieldk single stp m1
308
310
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−
322-- | Fold a stream using the supplied left 'Fold' and reducing the resulting
311-- | Fold a stream using the supplied left 'Fold' and reducing the resulting
323-- expression strictly at each step. The behavior is similar to 'foldl''. A
312-- expression strictly at each step. The behavior is similar to 'foldl''. A
324-- 'Fold' can terminate early without consuming the full stream. See the
313-- 'Fold' can terminate early without consuming the full stream. See the
@@ -470,11 +459,6 @@ foldConcat
470 Stream.Skip s -> go1 SPEC fs s
459 Stream.Skip s -> go1 SPEC fs s
471 Stream.Stop -> return $ Left fs
460 Stream.Stop -> return $ Left fs
472
461
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−
478------------------------------------------------------------------------------
462------------------------------------------------------------------------------
479-- Specialized folds
463-- Specialized folds
480------------------------------------------------------------------------------
464------------------------------------------------------------------------------
@@ -1737,3 +1721,10 @@ sortBy cmp =
1737 . Stream.catRights -- its a non-failing backtracking parser
1721 . Stream.catRights -- its a non-failing backtracking parser
1738 . Stream.parseMany (fmap (either id id) p)
1722 . Stream.parseMany (fmap (either id id) p)
1739 . Stream.fromStreamK
1723 . Stream.fromStreamK
1724+
1725+{-# INLINE sortOn #-}
1726+sortOn :: (Monad m, Ord b) => (a -> b) -> StreamK m a -> StreamK m a
1727+sortOn f =
1728+ fmap snd
1729+ . sortBy (comparing fst)
1730+ . fmap (\x -> let y = f x in y `seq` (y, x))
core/src/Streamly/Internal/Data/StreamK/Type.hs+80−11
@@ -1,4 +1,8 @@
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 #-}
2{-# LANGUAGE UndecidableInstances #-}
6{-# LANGUAGE UndecidableInstances #-}
3-- |
7-- |
4-- Module : Streamly.Internal.Data.StreamK.Type
8-- Module : Streamly.Internal.Data.StreamK.Type
@@ -84,6 +88,8 @@ module Streamly.Internal.Data.StreamK.Type
84 -- ** Strict Left Folds
88 -- ** Strict Left Folds
85 , Streamly.Internal.Data.StreamK.Type.foldl'
89 , Streamly.Internal.Data.StreamK.Type.foldl'
86 , foldlx'
90 , foldlx'
91+ , foldlMx'
92+ , foldlM'
87
93
88 -- ** Lazy Right Folds
94 -- ** Lazy Right Folds
89 , Streamly.Internal.Data.StreamK.Type.foldr
95 , Streamly.Internal.Data.StreamK.Type.foldr
@@ -91,8 +97,11 @@ module Streamly.Internal.Data.StreamK.Type
91 -- ** Specific Folds
97 -- ** Specific Folds
92 , drain
98 , drain
93 , null
99 , null
100+ , headNonEmpty
94 , tail
101 , tail
102+ , tailNonEmpty
95 , init
103 , init
104+ , initNonEmpty
96
105
97 -- * Mapping
106 -- * Mapping
98 , map
107 , map
@@ -817,6 +826,24 @@ foldlx' step begin done m = get $ go m begin
817foldl' :: Monad m => (b -> a -> b) -> b -> StreamK m a -> m b
826foldl' :: Monad m => (b -> a -> b) -> b -> StreamK m a -> m b
818foldl' step begin = foldlx' step begin id
827foldl' step begin = foldlx' step begin id
819
828
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+
820------------------------------------------------------------------------------
847------------------------------------------------------------------------------
821-- Specialized folds
848-- Specialized folds
822------------------------------------------------------------------------------
849------------------------------------------------------------------------------
@@ -1787,14 +1814,42 @@ iterateMWith cns step = go
1787 !next <- s
1814 !next <- s
1788 foldStreamShared st stp sng yld (return next `cns` go (step next))
1815 foldStreamShared st stp sng yld (return next `cns` go (step next))
1789
1816
1790−{-# INLINE headPartial #-}
1817+-- | head for non-empty streams, fails for empty stream case.
1818+--
1819+{-# INLINE headNonEmpty #-}
1791−headPartial :: Monad m => StreamK m a -> m a
1820+headNonEmpty :: Monad m => StreamK m a -> m a
1792−headPartial = foldrM (\x _ -> return x) (error "head of nil")
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
1793
1843
1794−{-# INLINE tailPartial #-}
1844+-- | tail for non-empty streams, fails for empty stream case.
1845+--
1846+-- See also 'tail' for a non-partial version of this function..
1847+--
1848+-- Note: this is same as "drop 1" with error on empty stream.
1849+{-# INLINE tailNonEmpty #-}
1795−tailPartial :: StreamK m a -> StreamK m a
1850+tailNonEmpty :: StreamK m a -> StreamK m a
1796−tailPartial m = mkStream $ \st yld sng stp ->
1851+tailNonEmpty m = mkStream $ \st yld sng stp ->
1797− let stop = error "tail of nil"
1852+ let stop = error "tailNonEmpty: empty stream"
1798 single _ = stp
1853 single _ = stp
1799 yieldk _ r = foldStream st yld sng stp r
1854 yieldk _ r = foldStream st yld sng stp r
1800 in foldStream st yieldk single stop m
1855 in foldStream st yieldk single stop m
@@ -1859,10 +1914,10 @@ mfix f = mkStream $ \st yld sng stp ->
1859 where
1914 where
1860
1915
1861 -- fix the head element of the stream
1916 -- fix the head element of the stream
1862− xs = fix (f . headPartial)
1917+ xs = fix (f . headNonEmpty)
1863
1918
1864 -- now fix the tail recursively
1919 -- now fix the tail recursively
1865− ys = mfix (tailPartial . f)
1920+ ys = mfix (tailNonEmpty . f)
1866
1921
1867-------------------------------------------------------------------------------
1922-------------------------------------------------------------------------------
1868-- Conversions
1923-- Conversions
@@ -1893,6 +1948,15 @@ uncons m =
1893 yieldk a r = pure (Just (a, r))
1948 yieldk a r = pure (Just (a, r))
1894 in foldStream defState yieldk single stop m
1949 in foldStream defState yieldk single stop m
1895
1950
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+--
1896{-# INLINE tail #-}
1960{-# INLINE tail #-}
1897tail :: Applicative m => StreamK m a -> m (Maybe (StreamK m a))
1961tail :: Applicative m => StreamK m a -> m (Maybe (StreamK m a))
1898tail =
1962tail =
@@ -1901,9 +1965,14 @@ tail =
1901 yieldk _ r = pure $ Just r
1965 yieldk _ r = pure $ Just r
1902 in foldStream defState yieldk single stop
1966 in foldStream defState yieldk single stop
1903
1967
1904−-- | Extract all but the last element of the stream, if any.
1968+-- Note that this is not a StreamK -> StreamK because then we cannot handle the
1969+-- empty stream case without making this a partial function.
1905--
1970--
1906−-- Note: This will end up buffering the entire stream.
1971+-- XXX How do we implement unsnoc? Make StreamK a monad and return the
1972+-- remaining stream as a result value in the monad?
1973+
1974+-- | Extract all but the last element of the stream, if any. This will end up
1975+-- evaluating the last element as well to find out that it is last.
1907--
1976--
1908-- /Pre-release/
1977-- /Pre-release/
1909{-# INLINE init #-}
1978{-# INLINE init #-}
core/src/Streamly/Internal/Data/Time/Units.hs−4
@@ -1,7 +1,3 @@
1−{-# LANGUAGE DataKinds #-}
2−{-# LANGUAGE PolyKinds #-}
3−{-# LANGUAGE UnboxedTuples #-}
4−
5-- |
1-- |
6-- Module : Streamly.Internal.Data.Time.Units
2-- Module : Streamly.Internal.Data.Time.Units
7-- Copyright : (c) 2019 Composewell Technologies
3-- Copyright : (c) 2019 Composewell Technologies
core/src/Streamly/Internal/Data/Unbox.hs+120−75
@@ -1,5 +1,8 @@
1{-# LANGUAGE DataKinds #-}
1{-# LANGUAGE DataKinds #-}
2−{-# LANGUAGE DefaultSignatures #-}
2+{-# LANGUAGE TypeFamilies #-}
3+-- Must come after TypeFamilies, otherwise it is re-enabled.
4+-- MonoLocalBinds enabled by TypeFamilies causes perf regressions in general.
5+{-# LANGUAGE NoMonoLocalBinds #-}
3{-# LANGUAGE UnboxedTuples #-}
6{-# LANGUAGE UnboxedTuples #-}
4{-# LANGUAGE UndecidableInstances #-}
7{-# LANGUAGE UndecidableInstances #-}
5
8
@@ -47,9 +50,6 @@ import Data.Functor.Const (Const(..))
47import Data.Functor.Identity (Identity(..))
50import Data.Functor.Identity (Identity(..))
48import Data.Kind (Type)
51import Data.Kind (Type)
49import Data.Proxy (Proxy (..))
52import Data.Proxy (Proxy (..))
50−#ifdef DEBUG
51−import Debug.Trace (trace)
52−#endif
53import Foreign.Ptr (IntPtr(..), WordPtr(..))
53import Foreign.Ptr (IntPtr(..), WordPtr(..))
54import GHC.Base (IO(..))
54import GHC.Base (IO(..))
55import GHC.Fingerprint.Type (Fingerprint(..))
55import GHC.Fingerprint.Type (Fingerprint(..))
@@ -67,7 +67,10 @@ import GHC.Exts
67import GHC.TypeLits
67import GHC.TypeLits
68import Prelude hiding (read)
68import Prelude hiding (read)
69
69
70−import Streamly.Internal.Data.MutByteArray (MutByteArray(..))
70+import Streamly.Internal.Data.MutByteArray.Type (MutByteArray(..))
71+#ifdef DEBUG
72+import Streamly.Internal.Data.MutByteArray.Type (sizeOfMutableByteArray)
73+#endif
71
74
72--------------------------------------------------------------------------------
75--------------------------------------------------------------------------------
73-- The Unbox type class
76-- The Unbox type class
@@ -162,9 +165,9 @@ import Streamly.Internal.Data.MutByteArray (MutByteArray(..))
162-- have multiple constructors of different sizes, the size of a sum type is
165-- have multiple constructors of different sizes, the size of a sum type is
163-- computed as the maximum required by any constructor.
166-- computed as the maximum required by any constructor.
164--
167--
165−-- The 'peekByteIndex' operation reads as many bytes from the mutable byte
168+-- The 'peekAt' operation reads as many bytes from the mutable byte
166-- array as the @size@ of the data type and builds a Haskell data type from
169-- array as the @size@ of the data type and builds a Haskell data type from
167−-- these bytes. 'pokeByteIndex' operation converts a Haskell data type to its
170+-- these bytes. 'pokeAt' operation converts a Haskell data type to its
168-- binary representation which consists of @size@ bytes and then stores
171-- binary representation which consists of @size@ bytes and then stores
169-- these bytes into the mutable byte array. These operations do not check the
172-- these bytes into the mutable byte array. These operations do not check the
170-- bounds of the array, the user of the type class is expected to check the
173-- bounds of the array, the user of the type class is expected to check the
@@ -196,17 +199,17 @@ import Streamly.Internal.Data.MutByteArray (MutByteArray(..))
196-- } deriving Generic
199-- } deriving Generic
197-- :}
200-- :}
198--
201--
199−-- >>> import Streamly.Data.Serialize (Unbox(..))
202+-- >>> import Streamly.Data.MutByteArray (Unbox(..))
200-- >>> instance Unbox Object
203-- >>> instance Unbox Object
201--
204--
202-- To derive the instance via Template Haskell:
205-- To derive the instance via Template Haskell:
203--
206--
204-- @
207-- @
205−-- import Streamly.Data.Serialize (deriveUnbox)
208+-- import Streamly.Data.MutByteArray (deriveUnbox)
206-- \$(deriveUnbox [d|instance Unbox Object|])
209-- \$(deriveUnbox [d|instance Unbox Object|])
207-- @
210-- @
208--
211--
209−-- See 'Streamly.Data.Serialize.deriveUnbox' for more information on deriving
212+-- See 'Streamly.Data.MutByteArray.deriveUnbox' for more information on deriving
210-- using Template Haskell.
213-- using Template Haskell.
211--
214--
212-- If you want to write the instance manually:
215-- If you want to write the instance manually:
@@ -214,15 +217,15 @@ import Streamly.Internal.Data.MutByteArray (MutByteArray(..))
214-- >>> :{
217-- >>> :{
215-- instance Unbox Object where
218-- instance Unbox Object where
216-- sizeOf _ = 16
219-- sizeOf _ = 16
217−-- peekByteIndex i arr = do
220+-- peekAt i arr = do
218-- -- Check the array bounds
221-- -- Check the array bounds
219−-- x0 <- peekByteIndex i arr
222+-- x0 <- peekAt i arr
220−-- x1 <- peekByteIndex (i + 8) arr
223+-- x1 <- peekAt (i + 8) arr
221-- return $ Object x0 x1
224-- return $ Object x0 x1
222−-- pokeByteIndex i arr (Object x0 x1) = do
225+-- pokeAt i arr (Object x0 x1) = do
223-- -- Check the array bounds
226-- -- Check the array bounds
224−-- pokeByteIndex i arr x0
227+-- pokeAt i arr x0
225−-- pokeByteIndex (i + 8) arr x1
228+-- pokeAt (i + 8) arr x1
226-- :}
229-- :}
227--
230--
228class Unbox a where
231class Unbox a where
@@ -233,67 +236,99 @@ class Unbox a where
233 default sizeOf :: (SizeOfRep (Rep a)) => Proxy a -> Int
236 default sizeOf :: (SizeOfRep (Rep a)) => Proxy a -> Int
234 sizeOf = genericSizeOf
237 sizeOf = genericSizeOf
235
238
236− -- | @peekByteIndex byte-index array@ reads an element of type @a@ from the
239+ -- | @peekAt byte-offset array@ reads an element of type @a@ from the
237− -- the given the byte index in the array.
240+ -- the given the byte offset in the array.
238 --
241 --
239 -- IMPORTANT: The implementation of this interface may not check the bounds
242 -- IMPORTANT: The implementation of this interface may not check the bounds
240 -- of the array, the caller must not assume that.
243 -- of the array, the caller must not assume that.
241− peekByteIndex :: Int -> MutByteArray -> IO a
244+ peekAt :: Int -> MutByteArray -> IO a
242
245
243− {-# INLINE peekByteIndex #-}
246+ {-# INLINE peekAt #-}
244− default peekByteIndex :: (Generic a, PeekRep (Rep a)) =>
247+ default peekAt :: (Generic a, PeekRep (Rep a)) =>
245 Int -> MutByteArray -> IO a
248 Int -> MutByteArray -> IO a
246− peekByteIndex i arr = genericPeekByteIndex arr i
249+ peekAt i arr = genericPeekByteIndex arr i
250+
251+ peekByteIndex :: Int -> MutByteArray -> IO a
252+ peekByteIndex = peekAt
247
253
248− -- | @pokeByteIndex byte-index array@ writes an element of type @a@ to the
254+ -- | @pokeAt byte-offset array@ writes an element of type @a@ to the
249− -- the given the byte index in the array.
255+ -- the given the byte offset in the array.
250 --
256 --
251 -- IMPORTANT: The implementation of this interface may not check the bounds
257 -- IMPORTANT: The implementation of this interface may not check the bounds
252 -- of the array, the caller must not assume that.
258 -- of the array, the caller must not assume that.
259+ pokeAt :: Int -> MutByteArray -> a -> IO ()
260+
253 pokeByteIndex :: Int -> MutByteArray -> a -> IO ()
261 pokeByteIndex :: Int -> MutByteArray -> a -> IO ()
262+ pokeByteIndex = pokeAt
254
263
255− {-# INLINE pokeByteIndex #-}
264+ {-# INLINE pokeAt #-}
256− default pokeByteIndex :: (Generic a, PokeRep (Rep a)) =>
265+ default pokeAt :: (Generic a, PokeRep (Rep a)) =>
257 Int -> MutByteArray -> a -> IO ()
266 Int -> MutByteArray -> a -> IO ()
258− pokeByteIndex i arr = genericPokeByteIndex arr i
267+ pokeAt i arr = genericPokeByteIndex arr i
259
268
260−-- XXX Add asserts to check bounds
269+{-# DEPRECATED peekByteIndex "Use peekAt." #-}
270+{-# DEPRECATED pokeByteIndex "Use pokeAt." #-}
271+
272+-- _size is the length from array start to the last accessed byte.
273+{-# INLINE checkBounds #-}
274+checkBounds :: String -> Int -> MutByteArray -> IO ()
275+checkBounds _label _size _arr = do
276+#ifdef DEBUG
277+ sz <- sizeOfMutableByteArray _arr
278+ if (_size > sz)
279+ then error
280+ $ _label
281+ ++ ": accessing array at offset = "
282+ ++ show (_size - 1)
283+ ++ " max valid offset = " ++ show (sz - 1)
284+ else return ()
285+#else
286+ return ()
287+#endif
261
288
262#define DERIVE_UNBOXED(_type, _constructor, _readArray, _writeArray, _sizeOf) \
289#define DERIVE_UNBOXED(_type, _constructor, _readArray, _writeArray, _sizeOf) \
263−instance Unbox _type where { \
290+instance Unbox _type where { \
264−; {-# INLINE peekByteIndex #-} \
291+; {-# INLINE peekAt #-} \
265−; peekByteIndex (I# n) (MutByteArray mbarr) = IO $ \s -> \
292+; peekAt off@(I# n) arr@(MutByteArray mbarr) = \
293+ checkBounds "peek" (off + sizeOf (Proxy :: Proxy _type)) arr \
294+ >> (IO $ \s -> \
266− case _readArray mbarr n s of \
295+ case _readArray mbarr n s of \
267− { (# s1, i #) -> (# s1, _constructor i #) } \
296+ { (# s1, i #) -> (# s1, _constructor i #) }) \
268−; {-# INLINE pokeByteIndex #-} \
297+; {-# INLINE pokeAt #-} \
269−; pokeByteIndex (I# n) (MutByteArray mbarr) (_constructor val) = \
298+; pokeAt off@(I# n) arr@(MutByteArray mbarr) (_constructor val) = \
299+ checkBounds "poke" (off + sizeOf (Proxy :: Proxy _type)) arr \
270− IO $ \s -> (# _writeArray mbarr n val s, () #) \
300+ >> (IO $ \s -> (# _writeArray mbarr n val s, () #)) \
271−; {-# INLINE sizeOf #-} \
301+; {-# INLINE sizeOf #-} \
272−; sizeOf _ = _sizeOf \
302+; sizeOf _ = _sizeOf \
273}
303}
274
304
275#define DERIVE_WRAPPED_UNBOX(_constraint, _type, _constructor, _innerType) \
305#define DERIVE_WRAPPED_UNBOX(_constraint, _type, _constructor, _innerType) \
276instance _constraint Unbox _type where \
306instance _constraint Unbox _type where \
277−; {-# INLINE peekByteIndex #-} \
307+; {-# INLINE peekAt #-} \
308+; peekAt i arr = \
309+ checkBounds "peek" (i + sizeOf (Proxy :: Proxy _type)) arr \
278−; peekByteIndex i arr = _constructor <$> peekByteIndex i arr \
310+ >> _constructor <$> peekAt i arr \
279−; {-# INLINE pokeByteIndex #-} \
311+; {-# INLINE pokeAt #-} \
280−; pokeByteIndex i arr (_constructor a) = pokeByteIndex i arr a \
312+; pokeAt i arr (_constructor a) = \
313+ checkBounds "poke" (i + sizeOf (Proxy :: Proxy _type)) arr \
314+ >> pokeAt i arr a \
281; {-# INLINE sizeOf #-} \
315; {-# INLINE sizeOf #-} \
282; sizeOf _ = SIZE_OF(_innerType)
316; sizeOf _ = SIZE_OF(_innerType)
283
317
284−#define DERIVE_BINARY_UNBOX(_constraint, _type, _constructor, _innerType) \
318+#define DERIVE_BINARY_UNBOX(_constraint, _type, _constructor, _innerType) \
285−instance _constraint Unbox _type where { \
319+instance _constraint Unbox _type where { \
286−; {-# INLINE peekByteIndex #-} \
320+; {-# INLINE peekAt #-} \
287−; peekByteIndex i arr = \
321+; peekAt i arr = \
288− peekByteIndex i arr >>= \
322+ checkBounds "peek" (i + sizeOf (Proxy :: Proxy _type)) arr >> \
323+ peekAt i arr >>= \
289− (\p1 -> peekByteIndex (i + SIZE_OF(_innerType)) arr \
324+ (\p1 -> peekAt (i + SIZE_OF(_innerType)) arr <&> _constructor p1) \
290− <&> _constructor p1) \
325+; {-# INLINE pokeAt #-} \
291−; {-# INLINE pokeByteIndex #-} \
292−; pokeByteIndex i arr (_constructor p1 p2) = \
326+; pokeAt i arr (_constructor p1 p2) = \
293− pokeByteIndex i arr p1 >> \
327+ checkBounds "poke" (i + sizeOf (Proxy :: Proxy _type)) arr >> \
328+ pokeAt i arr p1 >> \
294− pokeByteIndex (i + SIZE_OF(_innerType)) arr p2 \
329+ pokeAt (i + SIZE_OF(_innerType)) arr p2 \
295−; {-# INLINE sizeOf #-} \
330+; {-# INLINE sizeOf #-} \
296−; sizeOf _ = 2 * SIZE_OF(_innerType) \
331+; sizeOf _ = 2 * SIZE_OF(_innerType) \
297}
332}
298
333
299-------------------------------------------------------------------------------
334-------------------------------------------------------------------------------
@@ -413,11 +448,13 @@ DERIVE_BINARY_UNBOX(,Fingerprint,Fingerprint,Word64)
413
448
414instance Unbox () where
449instance Unbox () where
415
450
416− {-# INLINE peekByteIndex #-}
451+ {-# INLINE peekAt #-}
417− peekByteIndex _ _ = return ()
452+ peekAt i arr =
453+ checkBounds "peek ()" (i + sizeOf (Proxy :: Proxy ())) arr >> return ()
418
454
419− {-# INLINE pokeByteIndex #-}
455+ {-# INLINE pokeAt #-}
420− pokeByteIndex _ _ _ = return ()
456+ pokeAt i arr _ =
457+ checkBounds "poke ()" (i + sizeOf (Proxy :: Proxy ())) arr >> return ()
421
458
422 {-# INLINE sizeOf #-}
459 {-# INLINE sizeOf #-}
423 sizeOf _ = 1
460 sizeOf _ = 1
@@ -425,11 +462,17 @@ instance Unbox () where
425#if MIN_VERSION_base(4,15,0)
462#if MIN_VERSION_base(4,15,0)
426instance Unbox IoSubSystem where
463instance Unbox IoSubSystem where
427
464
428− {-# INLINE peekByteIndex #-}
465+ {-# INLINE peekAt #-}
429− peekByteIndex i arr = toEnum <$> peekByteIndex i arr
466+ peekAt i arr =
467+ checkBounds
468+ "peek IoSubSystem" (i + sizeOf (Proxy :: Proxy IoSubSystem)) arr
469+ >> toEnum <$> peekAt i arr
430
470
431− {-# INLINE pokeByteIndex #-}
471+ {-# INLINE pokeAt #-}
472+ pokeAt i arr a =
473+ checkBounds
474+ "poke IoSubSystem" (i + sizeOf (Proxy :: Proxy IoSubSystem)) arr
432− pokeByteIndex i arr a = pokeByteIndex i arr (fromEnum a)
475+ >> pokeAt i arr (fromEnum a)
433
476
434 {-# INLINE sizeOf #-}
477 {-# INLINE sizeOf #-}
435 sizeOf _ = sizeOf (Proxy :: Proxy Int)
478 sizeOf _ = sizeOf (Proxy :: Proxy Int)
@@ -437,16 +480,18 @@ instance Unbox IoSubSystem where
437
480
438instance Unbox Bool where
481instance Unbox Bool where
439
482
440− {-# INLINE peekByteIndex #-}
483+ {-# INLINE peekAt #-}
441− peekByteIndex i arr = do
484+ peekAt i arr = do
485+ checkBounds "peek Bool" (i + sizeOf (Proxy :: Proxy Bool)) arr
442− res <- peekByteIndex i arr
486+ res <- peekAt i arr
443 return $ res /= (0 :: Int8)
487 return $ res /= (0 :: Int8)
444
488
445− {-# INLINE pokeByteIndex #-}
489+ {-# INLINE pokeAt #-}
446− pokeByteIndex i arr a =
490+ pokeAt i arr a =
491+ checkBounds "poke Bool" (i + sizeOf (Proxy :: Proxy Bool)) arr
447− if a
492+ >> if a
448− then pokeByteIndex i arr (1 :: Int8)
493+ then pokeAt i arr (1 :: Int8)
449− else pokeByteIndex i arr (0 :: Int8)
494+ else pokeAt i arr (0 :: Int8)
450
495
451 {-# INLINE sizeOf #-}
496 {-# INLINE sizeOf #-}
452 sizeOf _ = 1
497 sizeOf _ = 1
@@ -489,7 +534,7 @@ readUnsafe = Peeker (Builder step)
489 ++ show next
534 ++ show next
490 ++ " end = " ++ show end
535 ++ " end = " ++ show end
491#endif
536#endif
492− r <- peekByteIndex pos arr
537+ r <- peekAt pos arr
493 return (r, BoundedPtr arr next end)
538 return (r, BoundedPtr arr next end)
494
539
495{-# INLINE read #-}
540{-# INLINE read #-}
@@ -506,7 +551,7 @@ read = Peeker (Builder step)
506 $ error $ "read: reading beyond limit. next = "
551 $ error $ "read: reading beyond limit. next = "
507 ++ show next
552 ++ show next
508 ++ " end = " ++ show end
553 ++ " end = " ++ show end
509− r <- peekByteIndex pos arr
554+ r <- peekAt pos arr
510 return (r, BoundedPtr arr next end)
555 return (r, BoundedPtr arr next end)
511
556
512{-# INLINE skipByte #-}
557{-# INLINE skipByte #-}
@@ -551,7 +596,7 @@ pokeBoundedPtrUnsafe a (BoundedPtr arr pos end) = do
551 ++ show next
596 ++ show next
552 ++ " end = " ++ show end
597 ++ " end = " ++ show end
553#endif
598#endif
554− pokeByteIndex pos arr a
599+ pokeAt pos arr a
555 return (BoundedPtr arr next end)
600 return (BoundedPtr arr next end)
556
601
557{-# INLINE pokeBoundedPtr #-}
602{-# INLINE pokeBoundedPtr #-}
@@ -559,7 +604,7 @@ pokeBoundedPtr :: forall a. Unbox a => a -> BoundedPtr -> IO BoundedPtr
559pokeBoundedPtr a (BoundedPtr arr pos end) = do
604pokeBoundedPtr a (BoundedPtr arr pos end) = do
560 let next = pos + sizeOf (Proxy :: Proxy a)
605 let next = pos + sizeOf (Proxy :: Proxy a)
561 when (next > end) $ error "pokeBoundedPtr writing beyond limit"
606 when (next > end) $ error "pokeBoundedPtr writing beyond limit"
562− pokeByteIndex pos arr a
607+ pokeAt pos arr a
563 return (BoundedPtr arr next end)
608 return (BoundedPtr arr next end)
564
609
565--------------------------------------------------------------------------------
610--------------------------------------------------------------------------------
core/src/Streamly/Internal/Data/Unbox/TH.hs+12−9
@@ -241,6 +241,7 @@ 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?
244 [] -> [|pure $(conE cname)|]
245 [] -> [|pure $(conE cname)|]
245 _ ->
246 _ ->
246 letE
247 letE
@@ -252,7 +253,7 @@ mkPeekExprOne tagSize (DataCon cname _ _ fields) =
252
253
253 where
254 where
254
255
255− peekField i = [|peekByteIndex $(varE (mkOffsetName i)) $(varE _arr)|]
256+ peekField i = [|peekAt $(varE (mkOffsetName i)) $(varE _arr)|]
256
257
257mkPeekExpr :: Type -> [DataCon] -> Q Exp
258mkPeekExpr :: Type -> [DataCon] -> Q Exp
258mkPeekExpr headTy cons =
259mkPeekExpr headTy cons =
@@ -266,7 +267,7 @@ mkPeekExpr headTy cons =
266 doE
267 doE
267 [ bindS
268 [ bindS
268 (varP _tag)
269 (varP _tag)
269− [|peekByteIndex $(varE _initialOffset) $(varE _arr)|]
270+ [|peekAt $(varE _initialOffset) $(varE _arr)|]
270 , noBindS
271 , noBindS
271 (caseE
272 (caseE
272 (sigE (varE _tag) (conT tagType))
273 (sigE (varE _tag) (conT tagType))
@@ -299,7 +300,7 @@ mkPokeExprTag tagType tagVal = pokeTag
299 where
300 where
300
301
301 pokeTag =
302 pokeTag =
302− [|pokeByteIndex
303+ [|pokeAt
303 $(varE _initialOffset)
304 $(varE _initialOffset)
304 $(varE _arr)
305 $(varE _arr)
305 $((sigE (litE (IntegerL (fromIntegral tagVal))) (conT tagType)))|]
306 $((sigE (litE (IntegerL (fromIntegral tagVal))) (conT tagType)))|]
@@ -317,7 +318,7 @@ mkPokeExprFields tagSize fields = do
317
318
318 numFields = length fields
319 numFields = length fields
319 pokeField i =
320 pokeField i =
320− [|pokeByteIndex
321+ [|pokeAt
321 $(varE (mkOffsetName i))
322 $(varE (mkOffsetName i))
322 $(varE _arr)
323 $(varE _arr)
323 $(varE (mkFieldName i))|]
324 $(varE (mkFieldName i))|]
@@ -336,7 +337,9 @@ mkPokeExpr headTy cons =
336 [|error
337 [|error
337 ("Attempting to poke type with no constructors (" ++
338 ("Attempting to poke type with no constructors (" ++
338 $(lift (pprint headTy)) ++ ")")|]
339 $(lift (pprint headTy)) ++ ")")|]
340+ -- XXX We don't gaurentee encoded equivalilty for Unbox. Does it still
341+ -- make sense to encode a default value for unit constructor?
339− [(DataCon _ _ _ [])] -> [|pure ()|]
342+ [(DataCon _ _ _ [])] -> [|pure ()|] -- mkPokeExprTag ''Word8 0
340 [(DataCon cname _ _ fields)] ->
343 [(DataCon cname _ _ fields)] ->
341 caseE
344 caseE
342 (varE _val)
345 (varE _val)
@@ -402,9 +405,9 @@ deriveUnboxInternal headTy cons mkDec = do
402 -- none
405 -- none
403 [ -- PragmaD (InlineP 'sizeOf Inline FunLike AllPhases)
406 [ -- PragmaD (InlineP 'sizeOf Inline FunLike AllPhases)
404 FunD 'sizeOf [Clause [WildP] (NormalB sizeOfMethod) []]
407 FunD 'sizeOf [Clause [WildP] (NormalB sizeOfMethod) []]
405− , PragmaD (InlineP 'peekByteIndex Inline FunLike AllPhases)
408+ , PragmaD (InlineP 'peekAt Inline FunLike AllPhases)
406 , FunD
409 , FunD
407− 'peekByteIndex
410+ 'peekAt
408 [ Clause
411 [ Clause
409 (if isUnitType cons
412 (if isUnitType cons
410 then [WildP, WildP]
413 then [WildP, WildP]
@@ -412,9 +415,9 @@ deriveUnboxInternal headTy cons mkDec = do
412 (NormalB peekMethod)
415 (NormalB peekMethod)
413 []
416 []
414 ]
417 ]
415− , PragmaD (InlineP 'pokeByteIndex Inline FunLike AllPhases)
418+ , PragmaD (InlineP 'pokeAt Inline FunLike AllPhases)
416 , FunD
419 , FunD
417− 'pokeByteIndex
420+ 'pokeAt
418 [ Clause
421 [ Clause
419 (if isUnitType cons
422 (if isUnitType cons
420 then [WildP, WildP, WildP]
423 then [WildP, WildP, WildP]
core/src/Streamly/Internal/Data/Unfold.hs+6−4
@@ -82,7 +82,7 @@ module Streamly.Internal.Data.Unfold
82 , dropWhileM
82 , dropWhileM
83
83
84 -- ** Cross product
84 -- ** Cross product
85− , joinInnerGeneric
85+ , innerJoin
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,10 +677,12 @@ 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
681+-- 'Streamly.Internal.Data.Stream.innerJoin' for more details.
680−{-# INLINE_NORMAL joinInnerGeneric #-}
682+{-# INLINE_NORMAL innerJoin #-}
681−joinInnerGeneric :: Monad m =>
683+innerJoin :: Monad m =>
682 (b -> c -> Bool) -> Unfold m a b -> Unfold m a c -> Unfold m a (b, c)
684 (b -> c -> Bool) -> Unfold m a b -> Unfold m a c -> Unfold m a (b, c)
683−joinInnerGeneric eq s1 s2 = filter (\(a, b) -> a `eq` b) $ cross s1 s2
685+innerJoin eq s1 s2 = filter (\(a, b) -> a `eq` b) $ cross s1 s2
684
686
685------------------------------------------------------------------------------
687------------------------------------------------------------------------------
686-- Exceptions
688-- Exceptions
core/src/Streamly/Internal/FileSystem/File.hs+4−9
@@ -103,15 +103,14 @@ 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(..), pinnedWriteNUnsafe)
106+import Streamly.Internal.Data.Array.Type (Array(..))
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)
110import Streamly.Internal.Data.Unfold.Type (Unfold(..))
109import Streamly.Internal.Data.Unfold.Type (Unfold(..))
111-- import Streamly.String (encodeUtf8, decodeUtf8, foldLines)
110-- import Streamly.String (encodeUtf8, decodeUtf8, foldLines)
112import Streamly.Internal.System.IO (defaultChunkSize)
111import Streamly.Internal.System.IO (defaultChunkSize)
113
112
114−import qualified Streamly.Data.Array as A
113+import qualified Streamly.Internal.Data.Array as A
115import qualified Streamly.Data.Stream as S
114import qualified Streamly.Data.Stream as S
116import qualified Streamly.Data.Unfold as UF
115import qualified Streamly.Data.Unfold as UF
117import qualified Streamly.Internal.Data.Array.Type as IA (pinnedChunksOf)
116import qualified Streamly.Internal.Data.Array.Type as IA (pinnedChunksOf)
@@ -339,10 +338,6 @@ readWithBufferOf = readerWith
339reader :: (MonadIO m, MonadCatch m) => Unfold m FilePath Word8
338reader :: (MonadIO m, MonadCatch m) => Unfold m FilePath Word8
340reader = UF.many A.reader (usingFile FH.chunkReader)
339reader = UF.many A.reader (usingFile FH.chunkReader)
341
340
342−{-# INLINE concatChunks #-}
343−concatChunks :: (Monad m, Unbox a) => Stream m (Array a) -> Stream m a
344−concatChunks = S.unfoldMany A.reader
345−
346-- | Generate a stream of bytes from a file specified by path. The stream ends
341-- | Generate a stream of bytes from a file specified by path. The stream ends
347-- when EOF is encountered. File is locked using multiple reader and single
342-- when EOF is encountered. File is locked using multiple reader and single
348-- writer locking mode.
343-- writer locking mode.
@@ -351,7 +346,7 @@ concatChunks = S.unfoldMany A.reader
351--
346--
352{-# INLINE read #-}
347{-# INLINE read #-}
353read :: (MonadIO m, MonadCatch m) => FilePath -> Stream m Word8
348read :: (MonadIO m, MonadCatch m) => FilePath -> Stream m Word8
354−read file = concatChunks $ withFile file ReadMode FH.readChunks
349+read file = A.concat $ withFile file ReadMode FH.readChunks
355
350
356{-# DEPRECATED toBytes "Please use 'read' instead" #-}
351{-# DEPRECATED toBytes "Please use 'read' instead" #-}
357{-# INLINE toBytes #-}
352{-# INLINE toBytes #-}
@@ -467,7 +462,7 @@ writeChunks path = Fold step initial extract final
467writeWith :: (MonadIO m, MonadCatch m)
462writeWith :: (MonadIO m, MonadCatch m)
468 => Int -> FilePath -> Fold m Word8 ()
463 => Int -> FilePath -> Fold m Word8 ()
469writeWith n path =
464writeWith n path =
470− groupsOf n (pinnedWriteNUnsafe n) (writeChunks path)
465+ groupsOf n (A.unsafePinnedCreateOf n) (writeChunks path)
471
466
472{-# DEPRECATED writeWithBufferOf "Please use 'writeWith' instead" #-}
467{-# DEPRECATED writeWithBufferOf "Please use 'writeWith' instead" #-}
473{-# INLINE writeWithBufferOf #-}
468{-# INLINE writeWithBufferOf #-}
core/src/Streamly/Internal/FileSystem/Handle.hs+21−28
@@ -40,8 +40,8 @@ module Streamly.Internal.FileSystem.Handle
40
40
41 -- * Streams
41 -- * Streams
42 , read
42 , read
43− , readWith
43+ , readWith -- readConcatChunksOf
44− , readChunksWith
44+ , readChunksWith -- readChunksOf
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
62+ , writeWith -- writeChunksOf
63 , writeChunks
63 , writeChunks
64− , writeChunksWith
64+ , writeChunksWith -- writeCompactChunksOf
65− , writeMaybesWith
65+ , writeMaybesWith -- writeCompactJustsOf
66
66
67 -- * Refolds
67 -- * Refolds
68 , writer
68 , writer
@@ -131,16 +131,13 @@ 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(..), pinnedWriteNUnsafe, unsafeFreezeWithShrink, byteLength)
134+ (Array(..), unsafeFreezeWithShrink, byteLength)
135import Streamly.Internal.Data.Stream.Type (Stream)
135import Streamly.Internal.Data.Stream.Type (Stream)
136−import Streamly.Internal.Data.Array.Stream (lpackArraysChunksOf)
137-- import Streamly.String (encodeUtf8, decodeUtf8, foldLines)
136-- import Streamly.String (encodeUtf8, decodeUtf8, foldLines)
138import Streamly.Internal.System.IO (defaultChunkSize)
137import Streamly.Internal.System.IO (defaultChunkSize)
139
138
140import qualified Streamly.Data.Fold as FL
139import qualified Streamly.Data.Fold as FL
141−import qualified Streamly.Data.Array as A
140+import qualified Streamly.Internal.Data.Array as A
142−import qualified Streamly.Internal.Data.Array.Type as A
143−import qualified Streamly.Internal.Data.Array.Stream as AS
144import qualified Streamly.Internal.Data.MutArray.Type as MArray
141import qualified Streamly.Internal.Data.MutArray.Type as MArray
145import qualified Streamly.Internal.Data.Refold.Type as Refold
142import qualified Streamly.Internal.Data.Refold.Type as Refold
146import qualified Streamly.Internal.Data.Fold.Type as FL(refoldMany)
143import qualified Streamly.Internal.Data.Fold.Type as FL(refoldMany)
@@ -169,6 +166,8 @@ import qualified Streamly.Internal.Data.StreamK.Type as K (mkStream)
169-- Array IO (Input)
166-- Array IO (Input)
170-------------------------------------------------------------------------------
167-------------------------------------------------------------------------------
171
168
169+-- XXX add an API that compacts the arrays to an exact size.
170+
172-- | Read a 'ByteArray' consisting of one or more bytes from a file handle. If
171-- | Read a 'ByteArray' consisting of one or more bytes from a file handle. If
173-- no data is available on the handle it blocks until at least one byte becomes
172-- no data is available on the handle it blocks until at least one byte becomes
174-- available. If any data is available then it immediately returns that data
173-- available. If any data is available then it immediately returns that data
@@ -178,9 +177,9 @@ import qualified Streamly.Internal.Data.StreamK.Type as K (mkStream)
178{-# INLINABLE getChunk #-}
177{-# INLINABLE getChunk #-}
179getChunk :: MonadIO m => Int -> Handle -> m (Array Word8)
178getChunk :: MonadIO m => Int -> Handle -> m (Array Word8)
180getChunk size h = liftIO $ do
179getChunk size h = liftIO $ do
181− arr <- MArray.pinnedNewBytes size
180+ arr :: MArray.MutArray Word8 <- MArray.pinnedEmptyOf size
182 -- ptr <- mallocPlainForeignPtrAlignedBytes size (alignment (undefined :: Word8))
181 -- ptr <- mallocPlainForeignPtrAlignedBytes size (alignment (undefined :: Word8))
183− MArray.asPtrUnsafe arr $ \p -> do
182+ MArray.unsafePinnedAsPtr arr $ \p -> do
184 n <- hGetBufSome h p size
183 n <- hGetBufSome h p size
185 -- XXX shrink only if the diff is significant
184 -- XXX shrink only if the diff is significant
186 return $
185 return $
@@ -338,10 +337,6 @@ readerWith = UF.many A.reader chunkReaderWith
338readWithBufferOf :: MonadIO m => Unfold m (Int, Handle) Word8
337readWithBufferOf :: MonadIO m => Unfold m (Int, Handle) Word8
339readWithBufferOf = readerWith
338readWithBufferOf = readerWith
340
339
341−{-# INLINE concatChunks #-}
342−concatChunks :: (Monad m, Unbox a) => Stream m (Array a) -> Stream m a
343−concatChunks = S.unfoldMany A.reader
344−
345-- | @readWith bufsize handle@ reads a byte stream from a file
340-- | @readWith bufsize handle@ reads a byte stream from a file
346-- handle, reads are performed in chunks of up to @bufsize@.
341-- handle, reads are performed in chunks of up to @bufsize@.
347--
342--
@@ -350,7 +345,7 @@ concatChunks = S.unfoldMany A.reader
350-- /Pre-release/
345-- /Pre-release/
351{-# INLINE readWith #-}
346{-# INLINE readWith #-}
352readWith :: MonadIO m => Int -> Handle -> Stream m Word8
347readWith :: MonadIO m => Int -> Handle -> Stream m Word8
353−readWith size h = concatChunks $ readChunksWith size h
348+readWith size h = A.concat $ readChunksWith size h
354
349
355-- | Unfolds a file handle into a byte stream. IO requests to the device are
350-- | Unfolds a file handle into a byte stream. IO requests to the device are
356-- performed in sizes of
351-- performed in sizes of
@@ -369,7 +364,7 @@ reader = UF.many A.reader chunkReader
369-- /Pre-release/
364-- /Pre-release/
370{-# INLINE read #-}
365{-# INLINE read #-}
371read :: MonadIO m => Handle -> Stream m Word8
366read :: MonadIO m => Handle -> Stream m Word8
372−read = concatChunks . readChunks
367+read = A.concat . readChunks
373
368
374-------------------------------------------------------------------------------
369-------------------------------------------------------------------------------
375-- Writing
370-- Writing
@@ -384,12 +379,12 @@ read = concatChunks . readChunks
384{-# INLINABLE putChunk #-}
379{-# INLINABLE putChunk #-}
385putChunk :: MonadIO m => Handle -> Array a -> m ()
380putChunk :: MonadIO m => Handle -> Array a -> m ()
386putChunk _ arr | byteLength arr == 0 = return ()
381putChunk _ arr | byteLength arr == 0 = return ()
387−putChunk h arr = A.asPtrUnsafe arr $ \ptr ->
382+putChunk h arr = A.unsafePinnedAsPtr arr $ \ptr ->
388 liftIO $ hPutBuf h ptr aLen
383 liftIO $ hPutBuf h ptr aLen
389
384
390 where
385 where
391
386
392− -- XXX We should have the length passed by asPtrUnsafe itself.
387+ -- XXX We should have the length passed by unsafePinnedAsPtr itself.
393 aLen = A.byteLength arr
388 aLen = A.byteLength arr
394
389
395-------------------------------------------------------------------------------
390-------------------------------------------------------------------------------
@@ -421,7 +416,7 @@ putChunks h = S.fold (FL.drainMapM (putChunk h))
421{-# INLINE putChunksWith #-}
416{-# INLINE putChunksWith #-}
422putChunksWith :: (MonadIO m, Unbox a)
417putChunksWith :: (MonadIO m, Unbox a)
423 => Int -> Handle -> Stream m (Array a) -> m ()
418 => Int -> Handle -> Stream m (Array a) -> m ()
424−putChunksWith n h xs = putChunks h $ AS.compact n xs
419+putChunksWith n h xs = putChunks h $ A.compactLE n xs
425
420
426-- > putBytesWith n h m = Handle.putChunks h $ A.pinnedChunksOf n m
421-- > putBytesWith n h m = Handle.putChunks h $ A.pinnedChunksOf n m
427
422
@@ -461,8 +456,6 @@ writeChunks h = FL.drainMapM (putChunk h)
461chunkWriter :: MonadIO m => Refold m Handle (Array a) ()
456chunkWriter :: MonadIO m => Refold m Handle (Array a) ()
462chunkWriter = Refold.drainBy putChunk
457chunkWriter = Refold.drainBy putChunk
463
458
464−-- XXX lpackArraysChunksOf should be written idiomatically
465−
466-- | @writeChunksWith bufsize handle@ writes a stream of arrays
459-- | @writeChunksWith bufsize handle@ writes a stream of arrays
467-- to @handle@ after coalescing the adjacent arrays in chunks of @bufsize@.
460-- to @handle@ after coalescing the adjacent arrays in chunks of @bufsize@.
468-- We never split an array, if a single array is bigger than the specified size
461-- We never split an array, if a single array is bigger than the specified size
@@ -472,7 +465,7 @@ chunkWriter = Refold.drainBy putChunk
472{-# INLINE writeChunksWith #-}
465{-# INLINE writeChunksWith #-}
473writeChunksWith :: (MonadIO m, Unbox a)
466writeChunksWith :: (MonadIO m, Unbox a)
474 => Int -> Handle -> Fold m (Array a) ()
467 => Int -> Handle -> Fold m (Array a) ()
475−writeChunksWith n h = lpackArraysChunksOf n (writeChunks h)
468+writeChunksWith n h = A.lCompactGE n (writeChunks h)
476
469
477-- | Same as 'writeChunksWith'
470-- | Same as 'writeChunksWith'
478--
471--
@@ -496,11 +489,11 @@ writeChunksWithBufferOf = writeChunksWith
496-- Bytes in the input stream are collected into a buffer until we have a chunk
489-- Bytes in the input stream are collected into a buffer until we have a chunk
497-- of @reqSize@ and then written to the IO device.
490-- of @reqSize@ and then written to the IO device.
498--
491--
499−-- >>> writeWith n h = Fold.groupsOf n (Array.writeNUnsafe n) (Handle.writeChunks h)
492+-- >>> writeWith n h = Fold.groupsOf n (Array.unsafeCreateOf n) (Handle.writeChunks h)
500--
493--
501{-# INLINE writeWith #-}
494{-# INLINE writeWith #-}
502writeWith :: MonadIO m => Int -> Handle -> Fold m Word8 ()
495writeWith :: MonadIO m => Int -> Handle -> Fold m Word8 ()
503−writeWith n h = FL.groupsOf n (pinnedWriteNUnsafe n) (writeChunks h)
496+writeWith n h = FL.groupsOf n (A.unsafePinnedCreateOf n) (writeChunks h)
504
497
505-- | Same as 'writeWith'
498-- | Same as 'writeWith'
506--
499--
@@ -518,7 +511,7 @@ writeWithBufferOf = writeWith
518writeMaybesWith :: (MonadIO m )
511writeMaybesWith :: (MonadIO m )
519 => Int -> Handle -> Fold m (Maybe Word8) ()
512 => Int -> Handle -> Fold m (Maybe Word8) ()
520writeMaybesWith n h =
513writeMaybesWith n h =
521− let writeNJusts = FL.lmap fromJust $ A.pinnedWriteN n
514+ let writeNJusts = FL.lmap fromJust $ A.pinnedCreateOf n
522 writeOnNothing = FL.takeEndBy_ isNothing writeNJusts
515 writeOnNothing = FL.takeEndBy_ isNothing writeNJusts
523 in FL.many writeOnNothing (writeChunks h)
516 in FL.many writeOnNothing (writeChunks h)
524
517
@@ -528,7 +521,7 @@ writeMaybesWith n h =
528{-# INLINE writerWith #-}
521{-# INLINE writerWith #-}
529writerWith :: MonadIO m => Int -> Refold m Handle Word8 ()
522writerWith :: MonadIO m => Int -> Refold m Handle Word8 ()
530writerWith n =
523writerWith n =
531− FL.refoldMany (FL.take n $ pinnedWriteNUnsafe n) chunkWriter
524+ FL.refoldMany (FL.take n $ A.unsafePinnedCreateOf n) chunkWriter
532
525
533-- | Write a byte stream to a file handle. Accumulates the input in chunks of
526-- | Write a byte stream to a file handle. Accumulates the input in chunks of
534-- up to 'Streamly.Internal.Data.Array.Type.defaultChunkSize' before writing
527-- up to 'Streamly.Internal.Data.Array.Type.defaultChunkSize' before writing
core/src/Streamly/Internal/FileSystem/Path.hsnew file+515
@@ -0,0 +1,515 @@
1+{-# LANGUAGE TemplateHaskell #-}
2+
3+-- |
4+-- Module : Streamly.Internal.FileSystem.Path
5+-- Copyright : (c) 2023 Composewell Technologies
6+-- License : BSD3
7+-- Maintainer : streamly@composewell.com
8+-- Portability : GHC
9+--
10+-- Well typed and flexible file systems paths, preserving the OS and filesystem
11+-- encoding.
12+--
13+-- You can choose the level of type safety you want. 'Path' is the basic path
14+-- type which can represent a file, directory, absolute or relative path with
15+-- no restrictions. Depending on how much type safety you want you can choose
16+-- appropriate type wrappers to wrap 'Path'. @File Path@ mandates the path to
17+-- be a file whereas @Abs (File Path)@ mandates it to be an absolute path
18+-- representing a file.
19+--
20+-- You can upgrade or downgrade the safety. Whenever a less restrictive path
21+-- type is converted to a more restrctive path type the conversion involves
22+-- checks and it may fail. However, a more restrictive path type can be freely
23+-- converted to a less restrictive one.
24+--
25+-- See the @streamly-filepath@ package for interworking with the 'OsPath' type.
26+-- The 'Path' type can be converted to and from 'OsPath' type at zero cost
27+-- since the underlying representation of both is the same.
28+
29+-- Conventions: A trailing separator on a path indicates that it is a directory.
30+-- However, the absence of a trailing separator does not convey any
31+-- information, it could either be a directory or a file.
32+
33+-- You may also find the 'str' quasiquoter from "Streamly.Unicode.String" to be
34+-- useful in creating paths.
35+--
36+module Streamly.Internal.FileSystem.Path
37+ (
38+ -- * Path Types
39+ Path (..)
40+ , File
41+ , Dir
42+ , Abs
43+ , Rel
44+
45+ -- * Conversions
46+ , IsPath (..)
47+ , adaptPath
48+
49+ -- * Construction
50+ , fromChunk
51+ , fromChunkUnsafe
52+ , fromString
53+ , fromChars
54+
55+ -- * Statically Verified Literals
56+ -- quasiquoters
57+ , path
58+ , abs
59+ , rel
60+ , dir
61+ , file
62+ , absdir
63+ , reldir
64+ , absfile
65+ , relfile
66+
67+ -- * Statically Verified Strings
68+ -- TH macros
69+ , mkPath
70+ , mkAbs
71+ , mkRel
72+ , mkDir
73+ , mkFile
74+ , mkAbsDir
75+ , mkRelDir
76+ , mkAbsFile
77+ , mkRelFile
78+
79+ -- * Elimination
80+ , toChunk
81+ , toString
82+ , toChars
83+
84+ -- * Operations
85+ -- Do we need to export the separator functions? They are not essential if
86+ -- operations to split and combine paths are provided. If someone wants to
87+ -- work on paths at low level then they know what they are.
88+ , primarySeparator
89+ , isSeparator
90+ , extendPath
91+ , extendDir
92+ )
93+where
94+
95+import Control.Exception (Exception)
96+import Control.Monad.Catch (MonadThrow(..))
97+import Data.Char (chr)
98+import Data.Functor.Identity (Identity(..))
99+import Data.Word (Word8)
100+#if defined(mingw32_HOST_OS) || defined(__MINGW32__)
101+import Data.Word (Word16)
102+#endif
103+import Language.Haskell.TH (Q, Exp)
104+import Language.Haskell.TH.Quote (QuasiQuoter)
105+import Streamly.Internal.Data.Array (Array)
106+import Streamly.Internal.Data.Stream (Stream)
107+import System.IO.Unsafe (unsafePerformIO)
108+
109+import qualified Streamly.Internal.Data.Array as Array
110+import qualified Streamly.Internal.Data.Fold as Fold
111+import qualified Streamly.Internal.Data.MutArray as MutArray
112+import qualified Streamly.Internal.Data.Stream as Stream
113+import qualified Streamly.Internal.Unicode.Stream as Unicode
114+
115+import Prelude hiding (abs)
116+
117+#if defined(mingw32_HOST_OS) || defined(__MINGW32__)
118+#define WORD_TYPE Word16
119+#define SEPARATOR 92
120+#else
121+#define WORD_TYPE Word8
122+#define SEPARATOR 47
123+#endif
124+
125+------------------------------------------------------------------------------
126+-- Exceptions
127+------------------------------------------------------------------------------
128+
129+-- | Exceptions thrown by path operations.
130+data PathException =
131+ InvalidPath String
132+ | InvalidAbsPath String
133+ | InvalidRelPath String
134+ | InvalidFilePath String
135+ | InvalidDirPath String
136+ deriving (Show,Eq)
137+
138+instance Exception PathException
139+
140+-- XXX Path must not contain null char on Posix. System calls consider the path
141+-- as null terminated.
142+-- XXX Maintain the Array with null termination because Unix system calls
143+-- require a null terminated string, also they return null terminated strings
144+-- as paths. Implementation of path append will have to handle the null
145+-- termination. Or we can choose to always copy the array when using it in
146+-- system calls.
147+
148+-- XXX The eq instance needs to make sure that the paths are equivalent. If we
149+-- normalize the paths we can do a byte comparison. However, on windows paths
150+-- are case insensitive but the case is preserved, therefore, we cannot
151+-- normalize and need to do case insensitive comparison.
152+
153+------------------------------------------------------------------------------
154+-- Types
155+------------------------------------------------------------------------------
156+
157+-- | A type representing file system paths for directories or files.
158+newtype Path = Path (Array WORD_TYPE) -- deriving Eq
159+
160+-- Show instance prints raw bytes without any decoding for rountdtripping.
161+-- Should we print this as a string instead, may be useful for ascii chars but
162+-- utf8 encoded chars may be unprintable. Better use toString if you want to
163+-- pretty print the path.
164+{-
165+instance Show Path where
166+ show (Path x) = show x
167+-}
168+
169+-- XXX Do we need a type for file or dir Name as names cannot have the
170+-- separator char and there may be other restrictions on names? For example,
171+-- length restriction. A file name cannot be "." or "..". We can use the types
172+-- "File Name" and "Dir Name" to represent names. Also, file systems may put
173+-- limits on names. Can have an IsName type class with members Name, (File
174+-- Name), (Dir Name).
175+
176+-- | A type representing a file path.
177+newtype File a = File a
178+
179+-- | A type representing a directory path.
180+newtype Dir a = Dir a
181+
182+-- | A type representing absolute paths.
183+newtype Abs a = Abs a
184+
185+-- | A type representing relative paths.
186+newtype Rel a = Rel a
187+
188+------------------------------------------------------------------------------
189+-- Conversions
190+------------------------------------------------------------------------------
191+
192+-- | A member of 'IsPath' knows how to convert to and from the 'Path' type.
193+class IsPath a where
194+ -- | Like 'fromPath' but does not check the properties of 'Path'. Provides
195+ -- performance and simplicity when we know that the properties of the path
196+ -- are already verified, for example, when we get the path from the file
197+ -- system or the OS APIs.
198+ fromPathUnsafe :: Path -> a
199+
200+ -- | Convert a raw 'Path' to other forms of well-typed paths. It may fail
201+ -- if the path does not satisfy the properties of the target type.
202+ --
203+ -- Path components may have limits.
204+ -- Total path length may have a limit.
205+ fromPath :: MonadThrow m => Path -> m a
206+
207+ -- | Convert a well-typed path to a raw 'Path'. Never fails.
208+ toPath :: a -> Path
209+
210+instance IsPath Path where
211+ fromPathUnsafe = id
212+ fromPath = pure
213+ toPath = id
214+
215+instance IsPath (File Path) where
216+ fromPathUnsafe p = File p
217+ fromPath p = pure (File p)
218+ toPath (File p) = p
219+
220+instance IsPath (Dir Path) where
221+ fromPathUnsafe p = Dir p
222+ fromPath p = pure (Dir p)
223+ toPath (Dir p) = p
224+
225+instance IsPath (Abs Path) where
226+ fromPathUnsafe p = Abs p
227+ fromPath p = pure (Abs p)
228+ toPath (Abs p) = p
229+
230+instance IsPath (Rel Path) where
231+ fromPathUnsafe p = Rel p
232+ fromPath p = pure (Rel p)
233+ toPath (Rel p) = p
234+
235+instance IsPath (Abs (File Path)) where
236+ fromPathUnsafe p = Abs (File p)
237+ fromPath p = pure (Abs (File p))
238+ toPath (Abs (File p)) = p
239+
240+instance IsPath (Abs (Dir Path)) where
241+ fromPathUnsafe p = Abs (Dir p)
242+ fromPath p = pure (Abs (Dir p))
243+ toPath (Abs (Dir p)) = p
244+
245+instance IsPath (Rel (File Path)) where
246+ fromPathUnsafe p = Rel (File p)
247+ fromPath p = pure (Rel (File p))
248+ toPath (Rel (File p)) = p
249+
250+instance IsPath (Rel (Dir Path)) where
251+ fromPathUnsafe p = Rel (Dir p)
252+ fromPath p = pure (Rel (Dir p))
253+ toPath (Rel (Dir p)) = p
254+
255+-- XXX Use rewrite rules to eliminate intermediate conversions for better
256+-- efficiency.
257+
258+-- | Convert a path type to another path type. This operation may fail with a
259+-- 'PathException' when converting a less restrictive path type to a more
260+-- restrictive one.
261+adaptPath :: (MonadThrow m, IsPath a, IsPath b) => a -> m b
262+adaptPath p = fromPath $ toPath p
263+
264+------------------------------------------------------------------------------
265+-- Construction
266+------------------------------------------------------------------------------
267+
268+-- A chunk is essentially an untyped Array i.e. Array Word8. We can either use
269+-- the term ByteArray for that or just Chunk. The latter is shorter and we have
270+-- been using it consistently in streamly.
271+
272+-- | /Unsafe/: On Posix, a path cannot contain null characters. On Windows, the
273+-- array passed must be a multiple of 2 bytes as the underlying representation
274+-- uses 'Word16'.
275+{-# INLINE fromChunkUnsafe #-}
276+fromChunkUnsafe :: Array Word8 -> Path
277+fromChunkUnsafe arr = Path (Array.castUnsafe arr)
278+
279+-- | On Posix it may fail if the byte array contains null characters. On
280+-- Windows the array passed must be a multiple of 2 bytes as the underlying
281+-- representation uses 'Word16'.
282+--
283+-- Throws 'InvalidPath'.
284+fromChunk :: MonadThrow m => Array Word8 -> m Path
285+fromChunk arr =
286+ case Array.cast arr of
287+ Nothing ->
288+ -- XXX Windows only message.
289+ throwM
290+ $ InvalidPath
291+ $ "Encoded path length " ++ show (Array.byteLength arr)
292+ ++ " is not a multiple of 16-bit."
293+ Just x -> pure (Path x)
294+
295+-- | Convert 'Path' to an array of bytes.
296+toChunk :: Path -> Array Word8
297+toChunk (Path arr) = Array.asBytes arr
298+
299+-- | Encode a Unicode char stream to 'Path' using strict UTF-8 encoding on
300+-- Posix. On Posix it may fail if the stream contains null characters.
301+-- TBD: Use UTF16LE on Windows.
302+fromChars :: MonadThrow m => Stream Identity Char -> m Path
303+fromChars s =
304+ let n = runIdentity $ Stream.fold Fold.length s
305+#if defined(mingw32_HOST_OS) || defined(__MINGW32__)
306+ in pure $ Path (Array.fromPureStreamN n (Unicode.encodeUtf16le' s))
307+#else
308+ in pure $ Path (Array.fromPureStreamN n (Unicode.encodeUtf8' s))
309+#endif
310+
311+-- | Decode the path to a stream of Unicode chars using strict UTF-8 decoding
312+-- on Posix.
313+-- TBD: Use UTF16LE on Windows.
314+toChars :: Monad m => Path -> Stream m Char
315+toChars (Path arr) =
316+#if defined(mingw32_HOST_OS) || defined(__MINGW32__)
317+ Unicode.decodeUtf16le' $ Array.read arr
318+#else
319+ Unicode.decodeUtf8' $ Array.read arr
320+#endif
321+
322+-- | Encode a Unicode string to 'Path' using strict UTF-8 encoding on Posix.
323+-- On Posix it may fail if the stream contains null characters.
324+-- TBD: Use UTF16LE on Windows.
325+fromString :: MonadThrow m => [Char] -> m Path
326+fromString = fromChars . Stream.fromList
327+
328+-- | Decode the path to a Unicode string using strict UTF-8 decoding on Posix.
329+-- TBD: Use UTF16LE on Windows.
330+toString :: Path -> [Char]
331+toString = runIdentity . Stream.toList . toChars
332+
333+------------------------------------------------------------------------------
334+-- Statically Verified Literals
335+------------------------------------------------------------------------------
336+
337+-- XXX Build these on top of the str quasiquoter so that we get the
338+-- interpolation for free.
339+
340+-- | Generates a 'Path' type from an interpolated string literal.
341+--
342+-- /Unimplemented/
343+path :: QuasiQuoter
344+path = undefined
345+
346+-- | Generates an @Abs Path@ type from an interpolated string literal.
347+--
348+-- /Unimplemented/
349+abs :: QuasiQuoter
350+abs = undefined
351+
352+-- | Generates an @Rel Path@ type from an interpolated string literal.
353+--
354+-- /Unimplemented/
355+rel :: QuasiQuoter
356+rel = undefined
357+
358+-- | Generates an @Dir Path@ type from an interpolated string literal.
359+--
360+-- /Unimplemented/
361+dir :: QuasiQuoter
362+dir = undefined
363+
364+-- | Generates an @File Path@ type from an interpolated string literal.
365+--
366+-- /Unimplemented/
367+file :: QuasiQuoter
368+file = undefined
369+
370+-- | Generates an @Abs (Dir Path)@ type from an interpolated string literal.
371+--
372+-- /Unimplemented/
373+absdir :: QuasiQuoter
374+absdir = undefined
375+
376+-- | Generates an @Rel (Dir Path)@ type from an interpolated string literal.
377+--
378+-- /Unimplemented/
379+reldir :: QuasiQuoter
380+reldir = undefined
381+
382+-- | Generates an @Abs (File Path)@ type from an interpolated string literal.
383+--
384+-- /Unimplemented/
385+absfile :: QuasiQuoter
386+absfile = undefined
387+
388+-- | Generates an @Rel (File Path)@ type from an interpolated string literal.
389+--
390+-- /Unimplemented/
391+relfile :: QuasiQuoter
392+relfile = undefined
393+
394+------------------------------------------------------------------------------
395+-- Statically Verified Strings
396+------------------------------------------------------------------------------
397+
398+-- | Generates a 'Path' type.
399+--
400+-- /Unimplemented/
401+mkPath :: String -> Q Exp
402+mkPath = undefined
403+
404+-- | Generates an @Abs Path@ type.
405+--
406+-- /Unimplemented/
407+mkAbs :: String -> Q Exp
408+mkAbs = undefined
409+
410+-- | Generates an @Rel Path@ type.
411+--
412+-- /Unimplemented/
413+mkRel :: String -> Q Exp
414+mkRel = undefined
415+
416+-- | Generates an @Dir Path@ type.
417+--
418+-- /Unimplemented/
419+mkDir :: String -> Q Exp
420+mkDir = undefined
421+
422+-- | Generates an @File Path@ type.
423+--
424+-- /Unimplemented/
425+mkFile :: String -> Q Exp
426+mkFile = undefined
427+
428+-- | Generates an @Abs (Dir Path)@ type.
429+--
430+-- /Unimplemented/
431+mkAbsDir :: String -> Q Exp
432+mkAbsDir = undefined
433+
434+-- | Generates an @Rel (Dir Path)@ type.
435+--
436+-- /Unimplemented/
437+mkRelDir :: String -> Q Exp
438+mkRelDir = undefined
439+
440+-- | Generates an @Abs (File Path)@ type.
441+--
442+-- /Unimplemented/
443+mkAbsFile :: String -> Q Exp
444+mkAbsFile = undefined
445+
446+-- | Generates an @Rel (File Path)@ type.
447+--
448+-- /Unimplemented/
449+mkRelFile :: String -> Q Exp
450+mkRelFile = undefined
451+
452+------------------------------------------------------------------------------
453+-- Operations
454+------------------------------------------------------------------------------
455+
456+separatorWord :: WORD_TYPE
457+separatorWord = SEPARATOR
458+
459+-- Portable definition for exporting.
460+
461+-- | Primary path separator character, @/@ on Posix and @\\@ on Windows.
462+-- Windows supports @/@ too as a separator. Please use 'isSeparator' for
463+-- testing if a char is a separator char.
464+primarySeparator :: Char
465+primarySeparator = chr (SEPARATOR)
466+
467+-- | On Posix only @/@ is a path separator but in windows it could be either
468+-- @/@ or @\\@.
469+isSeparator :: Char -> Bool
470+#if defined(mingw32_HOST_OS) || defined(__MINGW32__)
471+isSeparator c = (c == '/') || (c == '\\')
472+#else
473+isSeparator = (== '/')
474+#endif
475+
476+-- If we append an absolute path it may fail with an error if the 'Path'
477+-- implementation stores absolute path information (a leading separator char).
478+-- However, the implementation may choose to store the path as a list of
479+-- components in which case we cannot distinguish an absolute path from
480+-- relative.
481+
482+-- | Like 'extendDir' but for the less restrictive 'Path' type which will always
483+-- create a syntactically valid 'Path' type but it may not be semantically valid
484+-- because we may append an absolute path or we may append to a file path.
485+-- The onus lies on the user to ensure that the first path is not a file and
486+-- the second path is not absolute.
487+extendPath :: Path -> Path -> Path
488+extendPath (Path a) (Path b) =
489+ let len = Array.byteLength a + 1 + Array.byteLength b
490+ -- XXX Check the leading separator or drive identifier. However,
491+ -- checking the drive letter may add an additional overhead (can it be
492+ -- arbitrarily long?), if it is significant we may want to have a
493+ -- separate combinePathChecked API for that.
494+ --
495+ -- Also, do not add a separator char if the first path has a trailing
496+ -- separator.
497+ newArr = unsafePerformIO $ do
498+ arr <- MutArray.new len
499+ arr1 <- MutArray.spliceUnsafe arr (Array.unsafeThaw a)
500+ arr2 <- MutArray.snocUnsafe arr1 separatorWord
501+ arr3 <- MutArray.spliceUnsafe arr2 (Array.unsafeThaw b)
502+ return (Array.unsafeFreeze arr3)
503+ in Path newArr
504+
505+-- The only safety we need for paths is: (1) The first path can only be a Dir
506+-- type path, and (2) second path can only be a Rel path.
507+-- Can this be coerced to create unsafe versions?
508+
509+-- | Extend a directory path by appending a relative path to it. This is the
510+-- equivalent to the @</>@ operator from the @filepath@ package.
511+{-# INLINE extendDir #-}
512+extendDir :: (IsPath (a (Dir Path)), IsPath b, IsPath (a b)) =>
513+ (a (Dir Path)) -> Rel b -> a b
514+extendDir a (Rel b) =
515+ fromPathUnsafe $ extendPath (toPath a) (toPath b)
core/src/Streamly/Internal/Unicode/Parser.hs+3
@@ -300,6 +300,9 @@ 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+
303-- | A generic parser for scientific notation of numbers. Returns (mantissa,
306-- | A generic parser for scientific notation of numbers. Returns (mantissa,
304-- exponent) tuple. The result can be mapped to 'Double' or any other number
307-- exponent) tuple. The result can be mapped to 'Double' or any other number
305-- representation e.g. @Scientific@.
308-- representation e.g. @Scientific@.
core/src/Streamly/Internal/Unicode/Stream.hs+33−6
@@ -28,6 +28,7 @@ module Streamly.Internal.Unicode.Stream
28 , decodeUtf8
28 , decodeUtf8
29 , decodeUtf8'
29 , decodeUtf8'
30 , decodeUtf8_
30 , decodeUtf8_
31+ , decodeUtf16le'
31
32
32 -- ** Resumable UTF-8 Decoding
33 -- ** Resumable UTF-8 Decoding
33 , DecodeError(..)
34 , DecodeError(..)
@@ -55,6 +56,7 @@ module Streamly.Internal.Unicode.Stream
55 , encodeUtf8'
56 , encodeUtf8'
56 , encodeUtf8_
57 , encodeUtf8_
57 , encodeStrings
58 , encodeStrings
59+ , encodeUtf16le'
58 {-
60 {-
59 -- * Operations on character strings
61 -- * Operations on character strings
60 , strip -- (dropAround isSpace)
62 , strip -- (dropAround isSpace)
@@ -97,7 +99,7 @@ import Data.Char (chr, ord)
97#if MIN_VERSION_base(4,17,0)
99#if MIN_VERSION_base(4,17,0)
98import Data.Char (generalCategory, GeneralCategory(Space))
100import Data.Char (generalCategory, GeneralCategory(Space))
99#endif
101#endif
100−import Data.Word (Word8)
102+import Data.Word (Word8, Word16)
101import Foreign.Marshal.Alloc (mallocBytes)
103import Foreign.Marshal.Alloc (mallocBytes)
102import Foreign.Storable (Storable(..))
104import Foreign.Storable (Storable(..))
103#ifndef __GHCJS__
105#ifndef __GHCJS__
@@ -109,14 +111,14 @@ import GHC.IO.Encoding.Failure (isSurrogate)
109import GHC.Ptr (Ptr (..), plusPtr)
111import GHC.Ptr (Ptr (..), plusPtr)
110import System.IO.Unsafe (unsafePerformIO)
112import System.IO.Unsafe (unsafePerformIO)
111import Streamly.Internal.Data.Array.Type (Array(..))
113import Streamly.Internal.Data.Array.Type (Array(..))
112−import Streamly.Internal.Data.MutArray.Type (MutByteArray)
114+import Streamly.Internal.Data.MutByteArray.Type (MutByteArray)
113import Streamly.Internal.Data.Fold (Fold)
115import Streamly.Internal.Data.Fold (Fold)
114import Streamly.Internal.Data.Parser (Parser)
116import Streamly.Internal.Data.Parser (Parser)
115import Streamly.Internal.Data.Stream (Stream)
117import Streamly.Internal.Data.Stream (Stream)
116import Streamly.Internal.Data.Stream (Step (..))
118import Streamly.Internal.Data.Stream (Step (..))
117import Streamly.Internal.Data.SVar.Type (adaptState)
119import Streamly.Internal.Data.SVar.Type (adaptState)
118import Streamly.Internal.Data.Tuple.Strict (Tuple'(..))
120import Streamly.Internal.Data.Tuple.Strict (Tuple'(..))
119−import Streamly.Internal.Data.Unbox (Unbox(peekByteIndex))
121+import Streamly.Internal.Data.Unbox (Unbox(peekAt))
120import Streamly.Internal.Data.Unfold.Type (Unfold(..))
122import Streamly.Internal.Data.Unfold.Type (Unfold(..))
121import Streamly.Internal.System.IO (unsafeInlineIO)
123import Streamly.Internal.System.IO (unsafeInlineIO)
122
124
@@ -676,6 +678,19 @@ decodeUtf8_ = decodeUtf8D_
676decodeUtf8Lax :: Monad m => Stream m Word8 -> Stream m Char
678decodeUtf8Lax :: Monad m => Stream m Word8 -> Stream m Char
677decodeUtf8Lax = decodeUtf8
679decodeUtf8Lax = decodeUtf8
678
680
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+
679-------------------------------------------------------------------------------
694-------------------------------------------------------------------------------
680-- Decoding Array Streams
695-- Decoding Array Streams
681-------------------------------------------------------------------------------
696-------------------------------------------------------------------------------
@@ -746,7 +761,7 @@ decodeUtf8ArraysWithD cfm (D.Stream step state) =
746 | p == end = do
761 | p == end = do
747 return $ Skip $ OuterLoop st Nothing
762 return $ Skip $ OuterLoop st Nothing
748 step' _ _ (InnerLoopDecodeInit st contents p end) = do
763 step' _ _ (InnerLoopDecodeInit st contents p end) = do
749− x <- liftIO $ peekByteIndex p contents
764+ x <- liftIO $ peekAt p contents
750 -- Note: It is important to use a ">" instead of a "<=" test here for
765 -- Note: It is important to use a ">" instead of a "<=" test here for
751 -- GHC to generate code layout for default branch prediction for the
766 -- GHC to generate code layout for default branch prediction for the
752 -- common case. This is fragile and might change with the compiler
767 -- common case. This is fragile and might change with the compiler
@@ -780,7 +795,7 @@ decodeUtf8ArraysWithD cfm (D.Stream step state) =
780 step' _ _ (InnerLoopDecoding st _ p end sv cp)
795 step' _ _ (InnerLoopDecoding st _ p end sv cp)
781 | p == end = return $ Skip $ OuterLoop st (Just (sv, cp))
796 | p == end = return $ Skip $ OuterLoop st (Just (sv, cp))
782 step' table _ (InnerLoopDecoding st contents p end statePtr codepointPtr) = do
797 step' table _ (InnerLoopDecoding st contents p end statePtr codepointPtr) = do
783− x <- liftIO $ peekByteIndex p contents
798+ x <- liftIO $ peekAt p contents
784 let (Tuple' sv cp) = decode1 table statePtr codepointPtr x
799 let (Tuple' sv cp) = decode1 table statePtr codepointPtr x
785 return $
800 return $
786 case sv of
801 case sv of
@@ -946,6 +961,18 @@ encodeUtf8_ = encodeUtf8D_
946encodeUtf8Lax :: Monad m => Stream m Char -> Stream m Word8
961encodeUtf8Lax :: Monad m => Stream m Char -> Stream m Word8
947encodeUtf8Lax = encodeUtf8
962encodeUtf8Lax = encodeUtf8
948
963
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+
949-------------------------------------------------------------------------------
976-------------------------------------------------------------------------------
950-- Decoding string literals
977-- Decoding string literals
951-------------------------------------------------------------------------------
978-------------------------------------------------------------------------------
@@ -1030,7 +1057,7 @@ stripHead = Stream.dropWhile isSpace
1030-- >>> Stream.fold Fold.toList $ Unicode.lines Fold.toList (Stream.fromList "lines\nthis\nstring\n\n\n")
1057-- >>> Stream.fold Fold.toList $ Unicode.lines Fold.toList (Stream.fromList "lines\nthis\nstring\n\n\n")
1031-- ["lines","this","string","",""]
1058-- ["lines","this","string","",""]
1032--
1059--
1033−-- > lines = Stream.splitOnSuffix (== '\n')
1060+-- >>> lines f = Stream.foldMany (Fold.takeEndBy_ (== '\n') f)
1034--
1061--
1035-- /Pre-release/
1062-- /Pre-release/
1036{-# INLINE lines #-}
1063{-# 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)
49+import Streamly.Internal.Data.Parser (Parser, ParseError)
50
50
51import Language.Haskell.TH
51import Language.Haskell.TH
52import Language.Haskell.TH.Quote
52import Language.Haskell.TH.Quote
@@ -106,9 +106,12 @@ 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+
109expandVars :: String -> Q Exp
112expandVars :: String -> Q Exp
110−expandVars ln =
113+expandVars input =
111− case runIdentity $ Stream.parse strParser (Stream.fromList ln) of
114+ case parseStr input of
112 Left e ->
115 Left e ->
113 fail $ "str QuasiQuoter parse error: " ++ displayException e
116 fail $ "str QuasiQuoter parse error: " ++ displayException e
114 Right x ->
117 Right x ->
@@ -141,9 +144,6 @@ expandVars ln =
141-- :}
144-- :}
142-- "hello world!"
145-- "hello world!"
143--
146--
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+71−22
@@ -1,8 +1,13 @@
1cabal-version: 2.2
1cabal-version: 2.2
2name: streamly-core
2name: streamly-core
3−version: 0.2.0
3+version: 0.2.2
4−synopsis: Streaming, parsers, arrays and more
4+synopsis: Streaming, parsers, arrays, serialization and more
5description:
5description:
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>.
8+ .
9+ Streamly is a standard library for Haskell that focuses on C-like
10+ performance, modular combinators, and streaming data flow model.
6 Streamly consists of two packages: "streamly-core" and "streamly".
11 Streamly consists of two packages: "streamly-core" and "streamly".
7 <https://hackage.haskell.org/package/streamly-core streamly-core>
12 <https://hackage.haskell.org/package/streamly-core streamly-core>
8 provides basic features, and depends only on GHC boot libraries (see
13 provides basic features, and depends only on GHC boot libraries (see
@@ -12,8 +17,41 @@ description:
12 and networking. For documentation, visit the
17 and networking. For documentation, visit the
13 <https://streamly.composewell.com Streamly website>.
18 <https://streamly.composewell.com Streamly website>.
14 .
19 .
15− This package provides streams, arrays, parsers, unicode text, file
20+ The streamly-core package provides the following functionality:
21+ .
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
16− IO, and console IO functionality.
54+ functionality.
17 .
55 .
18 Note: The dependencies "heaps" and "monad-control" are included in
56 Note: The dependencies "heaps" and "monad-control" are included in
19 the package solely for backward compatibility, and will be removed in
57 the package solely for backward compatibility, and will be removed in
@@ -77,6 +115,8 @@ extra-doc-files:
77 Changelog.md
115 Changelog.md
78 docs/*.md
116 docs/*.md
79 docs/ApiChangelogs/0.1.0.txt
117 docs/ApiChangelogs/0.1.0.txt
118+ docs/ApiChangelogs/0.1.0-0.2.0.txt
119+ docs/ApiChangelogs/0.2.0-0.2.2.txt
80
120
81source-repository head
121source-repository head
82 type: git
122 type: git
@@ -178,8 +218,6 @@ common compile-options
178common default-extensions
218common default-extensions
179 default-extensions:
219 default-extensions:
180 BangPatterns
220 BangPatterns
181− CApiFFI
182− CPP
183 ConstraintKinds
221 ConstraintKinds
184 DeriveDataTypeable
222 DeriveDataTypeable
185 DeriveGeneric
223 DeriveGeneric
@@ -190,24 +228,29 @@ common default-extensions
190 GeneralizedNewtypeDeriving
228 GeneralizedNewtypeDeriving
191 InstanceSigs
229 InstanceSigs
192 KindSignatures
230 KindSignatures
193− LambdaCase
194− MagicHash
195 MultiParamTypeClasses
231 MultiParamTypeClasses
196− PatternSynonyms
197 RankNTypes
232 RankNTypes
198− RecordWildCards
199 ScopedTypeVariables
233 ScopedTypeVariables
200 StandaloneDeriving
234 StandaloneDeriving
201 TupleSections
235 TupleSections
202 TypeApplications
236 TypeApplications
203− TypeFamilies
204 TypeOperators
237 TypeOperators
205− ViewPatterns
206
238
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
207 -- MonoLocalBinds, enabled by TypeFamilies, causes performance
250 -- MonoLocalBinds, enabled by TypeFamilies, causes performance
208 -- regressions. Disable it. This must come after TypeFamilies,
251 -- regressions. Disable it. This must come after TypeFamilies,
209 -- otherwise TypeFamilies will enable it again.
252 -- otherwise TypeFamilies will enable it again.
210− NoMonoLocalBinds
253+ -- NoMonoLocalBinds
211
254
212 -- UndecidableInstances -- Does not show any perf impact
255 -- UndecidableInstances -- Does not show any perf impact
213 -- UnboxedTuples -- interferes with (#.)
256 -- UnboxedTuples -- interferes with (#.)
@@ -221,6 +264,7 @@ common optimization-options
221
264
222 -- For this to be effective it must come after the -O2 option
265 -- For this to be effective it must come after the -O2 option
223 if flag(dev) || flag(debug) || !flag(opt)
266 if flag(dev) || flag(debug) || !flag(opt)
267+ cpp-options: -DDEBUG
224 ghc-options: -fno-ignore-asserts
268 ghc-options: -fno-ignore-asserts
225
269
226common threading-options
270common threading-options
@@ -292,9 +336,11 @@ library
292 , Streamly.Internal.Data.Producer
336 , Streamly.Internal.Data.Producer
293
337
294 -- streamly-core-array-types
338 -- streamly-core-array-types
295− , Streamly.Internal.Data.Serialize
339+ , Streamly.Internal.Data.MutByteArray
340+
341+ -- streaming and parsing Haskell types to/from bytes
296− , Streamly.Internal.Data.Serialize.Parser
342+ , Streamly.Internal.Data.Binary.Parser
297− , Streamly.Internal.Data.Serialize.Stream
343+ , Streamly.Internal.Data.Binary.Stream
298
344
299 -- May depend on streamly-core-stream
345 -- May depend on streamly-core-stream
300 , Streamly.Internal.Data.MutArray
346 , Streamly.Internal.Data.MutArray
@@ -330,6 +376,7 @@ library
330 , Streamly.Internal.Unicode.Array
376 , Streamly.Internal.Unicode.Array
331
377
332 -- Filesystem/IO
378 -- Filesystem/IO
379+ , Streamly.Internal.FileSystem.Path
333 , Streamly.Internal.FileSystem.Handle
380 , Streamly.Internal.FileSystem.Handle
334 , Streamly.Internal.FileSystem.File
381 , Streamly.Internal.FileSystem.File
335 , Streamly.Internal.FileSystem.Dir
382 , Streamly.Internal.FileSystem.Dir
@@ -367,7 +414,7 @@ library
367 -- streamly-core released modules in alphabetic order
414 -- streamly-core released modules in alphabetic order
368 -- NOTE: these must be added to streamly.cabal as well
415 -- NOTE: these must be added to streamly.cabal as well
369 , Streamly.Console.Stdio
416 , Streamly.Console.Stdio
370− , Streamly.Data.Serialize
417+ , Streamly.Data.MutByteArray
371 , Streamly.Data.Array
418 , Streamly.Data.Array
372 , Streamly.Data.Array.Generic
419 , Streamly.Data.Array.Generic
373 , Streamly.Data.MutArray
420 , Streamly.Data.MutArray
@@ -421,9 +468,10 @@ library
421 , Streamly.Internal.Data.Array.Type
468 , Streamly.Internal.Data.Array.Type
422
469
423 -- Unboxed IORef
470 -- Unboxed IORef
424− , Streamly.Internal.Data.MutByteArray
425− , Streamly.Internal.Data.Unbox
426 , Streamly.Internal.Data.IORef.Unboxed
471 , Streamly.Internal.Data.IORef.Unboxed
472+
473+ , Streamly.Internal.Data.MutByteArray.Type
474+ , Streamly.Internal.Data.Unbox
427 , Streamly.Internal.Data.Unbox.TH
475 , Streamly.Internal.Data.Unbox.TH
428 , Streamly.Internal.Data.Serialize.Type
476 , Streamly.Internal.Data.Serialize.Type
429 , Streamly.Internal.Data.Serialize.TH
477 , Streamly.Internal.Data.Serialize.TH
@@ -437,9 +485,10 @@ library
437 , Streamly.Internal.Data.Time.Clock.Type
485 , Streamly.Internal.Data.Time.Clock.Type
438
486
439 if flag(dev)
487 if flag(dev)
440− other-modules:
488+ exposed-modules:
441 Streamly.Internal.Data.StreamK.Alt
489 Streamly.Internal.Data.StreamK.Alt
490+ -- XXX Compilation needs to be fixed
442− , Streamly.Internal.Data.List
491+ -- , Streamly.Internal.Data.List
443
492
444 build-depends:
493 build-depends:
445 -- streamly-base
494 -- streamly-base
@@ -458,7 +507,7 @@ library
458 , base >= 4.12 && < 4.20
507 , base >= 4.12 && < 4.20
459 , exceptions >= 0.8.0 && < 0.11
508 , exceptions >= 0.8.0 && < 0.11
460 , transformers >= 0.5.5 && < 0.7
509 , transformers >= 0.5.5 && < 0.7
461− , filepath >= 1.4.2 && < 1.5
510+ , filepath >= 1.4.2 && < 1.6
462
511
463 -- streamly-unicode-core
512 -- streamly-unicode-core
464 , template-haskell >= 2.14 && < 2.22
513 , template-haskell >= 2.14 && < 2.22
docs/Developer/optimization-guidelines.md+12
@@ -191,6 +191,18 @@ the stages of a loop, it is often not useful to inline the whole loop
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+
194### NoSpecConstr
206### NoSpecConstr
195
207
196It is not always useful to specialize function calls for all constructors, some
208It is not always useful to specialize function calls for all constructors, some
docs/Developer/paths.rst+25−2
@@ -140,12 +140,35 @@ know exactly how the file system stores the path. Otherwise if we display
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+
143Type Safety Requirements
166Type Safety Requirements
144------------------------
167------------------------
145
168
146* Safety against using an absolute path where a relative path is to be
169* Safety against using an absolute path where a relative path is to be
147− used and vice-versa.
170+ used and vice-versa.
148−
171+
149 * Validations for absolute or relative path when constructing a path.
172 * Validations for absolute or relative path when constructing a path.
150 * We cannot append an absolute path to another path
173 * We cannot append an absolute path to another path
151* Safety against using a file name where a directory name is to be used and
174* Safety against using a file name where a directory name is to be used and
docs/User/Project/ApiChangelogs/0.9.0-0.10.0.txtnew file+424
@@ -0,0 +1,424 @@
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.Network.Socket
17+ [A] readWith :: MonadIO m => Int -> Socket -> Stream m Word8
18+ [A] readChunksWith :: MonadIO m => Int -> Socket -> Stream m (Array Word8)
19+[C] Streamly.Network.Inet.TCP
20+ [D] acceptorOnPortLocal :: MonadIO m => Unfold m PortNumber Socket
21+ [D] acceptorOnPort :: MonadIO m => Unfold m PortNumber Socket
22+ [A] acceptorLocal :: MonadIO m => Unfold m PortNumber Socket
23+ [A] acceptor :: MonadIO m => Unfold m PortNumber Socket
24+ [A] acceptOnAddrWith :: MonadIO m => [(SocketOption, Int)] -> (Word8, Word8, Word8, Word8) -> PortNumber -> Stream m Socket
25+ [A] acceptLocal :: MonadIO m => PortNumber -> Stream m Socket
26+ [A] accept :: MonadIO m => PortNumber -> Stream m Socket
27+[C] Streamly.Data.Stream.Prelude
28+ [D] tapCount :: MonadAsync m => (a -> Bool) -> (Stream m Int -> m b) -> Stream m a -> Stream m a
29+ [A] parTapCount :: MonadAsync m => (a -> Bool) -> (Stream m Int -> m b) -> Stream m a -> Stream m a
30+[A] Streamly.Data.Fold.Prelude
31+ [A] Config
32+ [A] toHashMapIO :: (MonadIO m, Hashable k, Ord k) => (a -> k) -> Fold m a b -> Fold m a (HashMap k b)
33+ [A] parEval :: MonadAsync m => (Config -> Config) -> Fold m a b -> Fold m a b
34+ [A] maxBuffer :: Int -> Config -> Config
35+ [A] inspect :: Bool -> Config -> Config
36+ [A] boundThreads :: Bool -> Config -> Config
37+
38+---------------------------------
39+Internal API diff
40+---------------------------------
41+
42+[C] Streamly.Internal.Network.Inet.TCP
43+ [A] acceptorWith :: MonadIO m => [(SocketOption, Int)] -> Unfold m PortNumber Socket
44+ [R] acceptorOnPortWith :: MonadIO m => [(SocketOption, Int)] -> Unfold m PortNumber Socket
45+ [D] acceptorOnPortLocal :: MonadIO m => Unfold m PortNumber Socket
46+ [D] acceptorOnPort :: MonadIO m => Unfold m PortNumber Socket
47+ [A] acceptorLocal :: MonadIO m => Unfold m PortNumber Socket
48+ [A] acceptor :: MonadIO m => Unfold m PortNumber Socket
49+ [R] acceptOnPortLocal :: MonadIO m => PortNumber -> Stream m Socket
50+ [R] acceptOnPort :: MonadIO m => PortNumber -> Stream m Socket
51+ [A] acceptLocal :: MonadIO m => PortNumber -> Stream m Socket
52+ [A] accept :: MonadIO m => PortNumber -> Stream m Socket
53+[R] Streamly.Internal.Data.Unfold.SVar
54+[A] Streamly.Internal.Data.Unfold.Prelude
55+ [A] gbracket :: MonadRunInIO m => (a -> m c) -> (c -> m d) -> Unfold m (c, e) b -> (forall s. m s -> m (Either e s)) -> Unfold m c b -> Unfold m a b
56+ [A] fromSVar :: MonadAsync m => Unfold m (SVar t m a) a
57+ [A] fromProducer :: MonadAsync m => Unfold m (SVar t m a) a
58+ [A] finally :: (MonadAsync m, MonadCatch m) => (a -> m c) -> Unfold m a b -> Unfold m a b
59+ [A] bracket :: (MonadAsync m, MonadCatch m) => (a -> m c) -> (c -> m d) -> Unfold m c b -> Unfold m a b
60+ [A] after :: MonadRunInIO m => (a -> m c) -> Unfold m a b -> Unfold m a b
61+[R] Streamly.Internal.Data.Unfold.Exception
62+[R] Streamly.Internal.Data.Stream.Zip.Concurrent
63+[D] Streamly.Internal.Data.Stream.Zip
64+ [C] GHC.Base.Functor
65+ [A] instance GHC.Base.Monad m => GHC.Base.Functor (Streamly.Internal.Data.Stream.Zip.ZipConcurrent m)
66+ [C] GHC.Base.Applicative
67+ [A] instance Streamly.Internal.Control.Concurrent.MonadAsync m => GHC.Base.Applicative (Streamly.Internal.Data.Stream.Zip.ZipConcurrent m)
68+ [A] ZipConcurrent
69+ [A] [getZipConcurrent] :: ZipConcurrent m a -> Stream m a
70+ [A] ZipConcurrent :: Stream m a -> ZipConcurrent m a
71+[R] Streamly.Internal.Data.Stream.Time
72+[R] Streamly.Internal.Data.Stream.SVar.Generate
73+[R] Streamly.Internal.Data.Stream.SVar.Eliminate
74+[A] Streamly.Internal.Data.Stream.Prelude
75+ [A] StopWhen
76+ [A] FirstStops :: StopWhen
77+ [A] AnyStops :: StopWhen
78+ [A] AllStop :: StopWhen
79+ [A] Rate
80+ [A] [rateLow] :: Rate -> Double
81+ [A] [rateHigh] :: Rate -> Double
82+ [A] [rateGoal] :: Rate -> Double
83+ [A] [rateBuffer] :: Rate -> Int
84+ [A] Rate :: Double -> Double -> Double -> Int -> Rate
85+ [A] Config
86+ [A] Channel
87+ [A] [yieldRateInfo] :: Channel m a -> Maybe YieldRateInfo
88+ [A] [workerThreads] :: Channel m a -> IORef (Set ThreadId)
89+ [A] [workerStopMVar] :: Channel m a -> MVar ()
90+ [A] [workerCount] :: Channel m a -> IORef Int
91+ [A] [workLoop] :: Channel m a -> Maybe WorkerInfo -> m ()
92+ [A] [svarStats] :: Channel m a -> SVarStats
93+ [A] [svarRef] :: Channel m a -> Maybe (IORef ())
94+ [A] [svarMrun] :: Channel m a -> RunInIO m
95+ [A] [svarInspectMode] :: Channel m a -> Bool
96+ [A] [svarCreator] :: Channel m a -> ThreadId
97+ [A] [remainingWork] :: Channel m a -> Maybe (IORef Count)
98+ [A] [readOutputQ] :: Channel m a -> m [ChildEvent a]
99+ [A] [postProcess] :: Channel m a -> m Bool
100+ [A] [outputQueue] :: Channel m a -> IORef ([ChildEvent a], Int)
101+ [A] [outputDoorBell] :: Channel m a -> MVar ()
102+ [A] [maxWorkerLimit] :: Channel m a -> Limit
103+ [A] [maxBufferLimit] :: Channel m a -> Limit
104+ [A] [isWorkDone] :: Channel m a -> IO Bool
105+ [A] [isQueueDone] :: Channel m a -> IO Bool
106+ [A] [enqueue] :: Channel m a -> Bool -> (RunInIO m, StreamK m a) -> IO ()
107+ [A] [eagerDispatch] :: Channel m a -> m ()
108+ [A] [doorBellOnWorkQ] :: Channel m a -> IORef Bool
109+ [A] [accountThread] :: Channel m a -> ThreadId -> m ()
110+ [A] Channel :: RunInIO m -> IORef ([ChildEvent a], Int) -> MVar () -> m [ChildEvent a] -> m Bool -> Limit -> Limit -> Maybe (IORef Count) -> Maybe YieldRateInfo -> (Bool -> (RunInIO m, StreamK m a) -> IO ()) -> m () -> IO Bool -> IO Bool -> IORef Bool -> (Maybe WorkerInfo -> m ()) -> IORef (Set ThreadId) -> IORef Int -> (ThreadId -> m ()) -> MVar () -> Maybe (IORef ()) -> SVarStats -> Bool -> ThreadId -> Channel m a
111+ [A] type MonadAsync m = (MonadIO m, MonadBaseControl IO m, MonadThrow m)
112+ [A] yield :: Channel m a -> Maybe WorkerInfo -> a -> IO Bool
113+ [A] withChannelK :: MonadAsync m => (Config -> Config) -> StreamK m a -> (Channel m b -> StreamK m a -> StreamK m b) -> StreamK m b
114+ [A] withChannel :: MonadAsync m => (Config -> Config) -> Stream m a -> (Channel m b -> Stream m a -> Stream m b) -> Stream m b
115+ [A] toChannelK :: MonadRunInIO m => Channel m a -> StreamK m a -> m ()
116+ [A] toChannel :: MonadRunInIO m => Channel m a -> Stream m a -> m ()
117+ [A] ticksRate :: MonadAsync m => Rate -> Stream m ()
118+ [A] ticks :: MonadIO m => Double -> Stream m ()
119+ [A] takeLastInterval :: Double -> Stream m a -> Stream m a
120+ [A] takeInterval :: MonadAsync m => Double -> Stream m a -> Stream m a
121+ [A] stopWhen :: StopWhen -> Config -> Config
122+ [A] stopChannel :: MonadIO m => Channel m a -> m ()
123+ [A] stop :: Channel m a -> Maybe WorkerInfo -> IO ()
124+ [A] startChannel :: MonadRunInIO m => Channel m a -> m ()
125+ [A] sendWorkerWait :: MonadIO m => Bool -> (Channel m a -> IO ()) -> (Channel m a -> m Bool) -> Channel m a -> m ()
126+ [A] sendWorkerDelayPaced :: Channel m a -> IO ()
127+ [A] sendWorkerDelay :: Channel m a -> IO ()
128+ [A] sampleIntervalStart :: MonadAsync m => Double -> Stream m a -> Stream m a
129+ [A] sampleIntervalEnd :: MonadAsync m => Double -> Stream m a -> Stream m a
130+ [A] sampleBurstStart :: MonadAsync m => Double -> Stream m a -> Stream m a
131+ [A] sampleBurstEnd :: MonadAsync m => Double -> Stream m a -> Stream m a
132+ [A] sampleBurst :: MonadAsync m => Bool -> Double -> Stream m a -> Stream m a
133+ [A] retryD :: forall e m a. (Exception e, Ord e, MonadCatch m) => Map e Int -> (e -> Stream m a) -> Stream m a -> Stream m a
134+ [A] retry :: (MonadCatch m, Exception e, Ord e) => Map e Int -> (e -> Stream m a) -> Stream m a -> Stream m a
135+ [A] readOutputQPaced :: MonadRunInIO m => Channel m a -> m [ChildEvent a]
136+ [A] readOutputQBounded :: MonadRunInIO m => Bool -> Channel m a -> m [ChildEvent a]
137+ [A] rate :: Maybe Rate -> Config -> Config
138+ [A] pushWorker :: MonadRunInIO m => Count -> Channel m a -> m ()
139+ [A] postProcessPaced :: MonadRunInIO m => Channel m a -> m Bool
140+ [A] postProcessBounded :: MonadRunInIO m => Channel m a -> m Bool
141+ [A] periodic :: MonadIO m => m a -> Double -> Stream m a
142+ [A] parZipWithM :: MonadAsync m => (Config -> Config) -> (a -> b -> m c) -> Stream m a -> Stream m b -> Stream m c
143+ [A] parZipWith :: MonadAsync m => (Config -> Config) -> (a -> b -> c) -> Stream m a -> Stream m b -> Stream m c
144+ [A] parTwo :: MonadAsync m => (Config -> Config) -> Stream m a -> Stream m a -> Stream m a
145+ [A] parTapCount :: MonadAsync m => (a -> Bool) -> (Stream m Int -> m b) -> Stream m a -> Stream m a
146+ [A] parSequence :: MonadAsync m => (Config -> Config) -> Stream m (m a) -> Stream m a
147+ [A] parReplicateM :: MonadAsync m => (Config -> Config) -> Int -> m a -> Stream m a
148+ [A] parRepeatM :: MonadAsync m => (Config -> Config) -> m a -> Stream m a
149+ [A] parMergeByM :: MonadAsync m => (Config -> Config) -> (a -> a -> m Ordering) -> Stream m a -> Stream m a -> Stream m a
150+ [A] parMergeBy :: MonadAsync m => (Config -> Config) -> (a -> a -> Ordering) -> Stream m a -> Stream m a -> Stream m a
151+ [A] parMapM :: MonadAsync m => (Config -> Config) -> (a -> m b) -> Stream m a -> Stream m b
152+ [A] parListOrdered :: MonadAsync m => [Stream m a] -> Stream m a
153+ [A] parListLazy :: MonadAsync m => [Stream m a] -> Stream m a
154+ [A] parListInterleaved :: MonadAsync m => [Stream m a] -> Stream m a
155+ [A] parListEagerMin :: MonadAsync m => [Stream m a] -> Stream m a
156+ [A] parListEagerFst :: MonadAsync m => [Stream m a] -> Stream m a
157+ [A] parListEager :: MonadAsync m => [Stream m a] -> Stream m a
158+ [A] parList :: MonadAsync m => (Config -> Config) -> [Stream m a] -> Stream m a
159+ [A] parEval :: MonadAsync m => (Config -> Config) -> Stream m a -> Stream m a
160+ [A] parConcatMap :: MonadAsync m => (Config -> Config) -> (a -> Stream m b) -> Stream m a -> Stream m b
161+ [A] parConcatIterate :: MonadAsync m => (Config -> Config) -> (a -> Stream m a) -> Stream m a -> Stream m a
162+ [A] parConcat :: MonadAsync m => (Config -> Config) -> Stream m (Stream m a) -> Stream m a
163+ [A] parApply :: MonadAsync m => (Config -> Config) -> Stream m (a -> b) -> Stream m a -> Stream m b
164+ [A] ordered :: Bool -> Config -> Config
165+ [A] newInterleaveChannel :: MonadAsync m => (Config -> Config) -> m (Channel m a)
166+ [A] newChannel :: MonadAsync m => (Config -> Config) -> m (Channel m a)
167+ [A] newAppendChannel :: MonadRunInIO m => (Config -> Config) -> m (Channel m a)
168+ [A] minRate :: Double -> Config -> Config
169+ [A] maxThreads :: Int -> Config -> Config
170+ [A] maxRate :: Double -> Config -> Config
171+ [A] maxBuffer :: Int -> Config -> Config
172+ [A] intervalsOf :: MonadAsync m => Double -> Fold m a b -> Stream m a -> Stream m b
173+ [A] interleaved :: Bool -> Config -> Config
174+ [A] interject :: MonadAsync m => m a -> Double -> Stream m a -> Stream m a
175+ [A] inspect :: Bool -> Config -> Config
176+ [A] groupsOfTimeout :: MonadAsync m => Int -> Double -> Fold m a b -> Stream m a -> Stream m b
177+ [A] getStopWhen :: Config -> StopWhen
178+ [A] fromChannelK :: MonadAsync m => Channel m a -> StreamK m a
179+ [A] fromChannel :: MonadAsync m => Channel m a -> Stream m a
180+ [A] fromCallback :: MonadAsync m => ((a -> m ()) -> m ()) -> Stream m a
181+ [A] finally :: (MonadAsync m, MonadCatch m) => m b -> Stream m a -> Stream m a
182+ [A] eager :: Bool -> Config -> Config
183+ [A] dumpSVar :: Channel m a -> IO String
184+ [A] dropLastInterval :: Int -> Stream m a -> Stream m a
185+ [A] dropInterval :: MonadAsync m => Double -> Stream m a -> Stream m a
186+ [A] dispatchWorkerPaced :: MonadRunInIO m => Channel m a -> m Bool
187+ [A] dispatchWorker :: MonadRunInIO m => Count -> Channel m a -> m Bool
188+ [A] defaultConfig :: Config
189+ [A] constRate :: Double -> Config -> Config
190+ [A] classifySessionsOf :: (MonadAsync m, Ord k) => (Int -> m Bool) -> Double -> Fold m a b -> Stream m (AbsTime, (k, a)) -> Stream m (k, b)
191+ [A] classifySessionsByGeneric :: forall m f a b. (MonadAsync m, IsMap f) => Proxy (f :: Type -> Type) -> Double -> Bool -> (Int -> m Bool) -> Double -> Fold m a b -> Stream m (AbsTime, (Key f, a)) -> Stream m (Key f, b)
192+ [A] classifySessionsBy :: (MonadAsync m, Ord k) => Double -> Bool -> (Int -> m Bool) -> Double -> Fold m a b -> Stream m (AbsTime, (k, a)) -> Stream m (k, b)
193+ [A] classifyKeepAliveSessions :: (MonadAsync m, Ord k) => (Int -> m Bool) -> Double -> Fold m a b -> Stream m (AbsTime, (k, a)) -> Stream m (k, b)
194+ [A] bufferOldestN :: Int -> Stream m a -> Stream m a
195+ [A] bufferLatestN :: Int -> Stream m a -> Stream m a
196+ [A] bufferLatest :: Stream m a -> Stream m (Maybe a)
197+ [A] bracket3D :: (MonadAsync m, MonadCatch m) => m b -> (b -> m c) -> (b -> m d) -> (b -> m e) -> (b -> Stream m a) -> Stream m a
198+ [A] bracket3 :: (MonadAsync m, MonadCatch m) => m b -> (b -> m c) -> (b -> m d) -> (b -> m e) -> (b -> Stream m a) -> Stream m a
199+ [A] bracket :: (MonadAsync m, MonadCatch m) => m b -> (b -> m c) -> (b -> Stream m a) -> Stream m a
200+ [A] avgRate :: Double -> Config -> Config
201+ [A] afterD :: MonadRunInIO m => m b -> Stream m a -> Stream m a
202+ [A] after :: (MonadIO m, MonadBaseControl IO m) => m b -> Stream m a -> Stream m a
203+[R] Streamly.Internal.Data.Stream.Exception.Lifted
204+[R] Streamly.Internal.Data.Stream.Concurrent.Channel.Type
205+[R] Streamly.Internal.Data.Stream.Concurrent.Channel.Operations
206+[R] Streamly.Internal.Data.Stream.Concurrent.Channel.Interleave
207+[R] Streamly.Internal.Data.Stream.Concurrent.Channel.Dispatcher
208+[R] Streamly.Internal.Data.Stream.Concurrent.Channel.Consumer
209+[R] Streamly.Internal.Data.Stream.Concurrent.Channel.Append
210+[R] Streamly.Internal.Data.Stream.Concurrent.Channel
211+[R] Streamly.Internal.Data.Stream.Concurrent
212+[R] Streamly.Internal.Data.Stream.Channel.Worker
213+[R] Streamly.Internal.Data.Stream.Channel.Types
214+[R] Streamly.Internal.Data.Stream.Channel.Dispatcher
215+[R] Streamly.Internal.Data.SVar.Worker
216+[R] Streamly.Internal.Data.SVar.Pull
217+[R] Streamly.Internal.Data.SVar.Dispatch
218+[D] Streamly.Internal.Data.SVar
219+ [A] Work
220+ [A] PartialWorker :: Count -> Work
221+ [A] ManyWorkers :: Int -> Count -> Work
222+ [A] BlockWait :: NanoSecond64 -> Work
223+ [A] workerUpdateLatency :: YieldRateInfo -> WorkerInfo -> IO ()
224+ [A] workerRateControl :: SVar t m a -> YieldRateInfo -> WorkerInfo -> IO Bool
225+ [A] withDiagMVar :: SVar t m a -> String -> IO () -> IO ()
226+ [A] updateYieldCount :: WorkerInfo -> IO Count
227+ [A] sendYield :: SVar t m a -> Maybe WorkerInfo -> ChildEvent a -> IO Bool
228+ [A] sendWorkerWait :: MonadAsync m => (SVar t m a -> IO ()) -> (SVar t m a -> m Bool) -> SVar t m a -> m ()
229+ [A] sendWorkerDelayPaced :: SVar t m a -> IO ()
230+ [A] sendWorkerDelay :: SVar t m a -> IO ()
231+ [A] sendToProducer :: SVar t m a -> ChildEvent a -> IO Int
232+ [A] sendStopToProducer :: MonadIO m => SVar t m a -> m ()
233+ [A] sendStop :: SVar t m a -> Maybe WorkerInfo -> IO ()
234+ [A] sendFirstWorker :: MonadAsync m => SVar t m a -> t m a -> m (SVar t m a)
235+ [A] send :: SVar t m a -> ChildEvent a -> IO Int
236+ [A] ringDoorBell :: SVar t m a -> IO ()
237+ [A] resetBufferLimit :: SVar t m a -> IO ()
238+ [A] recordMaxWorkers :: MonadIO m => SVar t m a -> m ()
239+ [A] readOutputQRaw :: SVar t m a -> IO ([ChildEvent a], Int)
240+ [A] readOutputQPaced :: MonadAsync m => SVar t m a -> m [ChildEvent a]
241+ [A] readOutputQBounded :: MonadAsync m => SVar t m a -> m [ChildEvent a]
242+ [A] readOutputQBasic :: IORef ([ChildEvent a], Int) -> IO ([ChildEvent a], Int)
243+ [A] pushWorkerPar :: MonadAsync m => SVar t m a -> (Maybe WorkerInfo -> m ()) -> m ()
244+ [A] pushWorker :: MonadAsync m => Count -> SVar t m a -> m ()
245+ [A] printSVar :: SVar t m a -> String -> IO ()
246+ [A] postProcessPaced :: MonadAsync m => SVar t m a -> m Bool
247+ [A] postProcessBounded :: MonadAsync m => SVar t m a -> m Bool
248+ [A] modifyThread :: MonadIO m => SVar t m a -> ThreadId -> m ()
249+ [A] minThreadDelay :: NanoSecond64
250+ [A] isBeyondMaxRate :: SVar t m a -> YieldRateInfo -> IO Bool
251+ [A] incrementYieldLimit :: SVar t m a -> IO ()
252+ [A] incrementBufferLimit :: SVar t m a -> IO ()
253+ [A] handleFoldException :: SVar t m a -> SomeException -> IO ()
254+ [A] handleChildException :: SVar t m a -> SomeException -> IO ()
255+ [A] estimateWorkers :: Limit -> Count -> Count -> NanoSecond64 -> NanoSecond64 -> NanoSecond64 -> LatencyRange -> Work
256+ [A] dumpSVar :: SVar t m a -> IO String
257+ [A] dispatchWorkerPaced :: MonadAsync m => SVar t m a -> m Bool
258+ [A] dispatchWorker :: MonadAsync m => Count -> SVar t m a -> m Bool
259+ [A] delThread :: MonadIO m => SVar t m a -> ThreadId -> m ()
260+ [A] decrementYieldLimit :: SVar t m a -> IO Bool
261+ [A] decrementBufferLimit :: SVar t m a -> IO ()
262+ [A] collectLatency :: SVar t m a -> YieldRateInfo -> Bool -> IO (Count, AbsTime, NanoSecond64)
263+ [A] cleanupSVarFromWorker :: SVar t m a -> IO ()
264+ [A] cleanupSVar :: SVar t m a -> IO ()
265+ [A] allThreadsDone :: MonadIO m => SVar t m a -> m Bool
266+[C] Streamly.Internal.Data.IsMap.HashMap
267+ [C] Streamly.Internal.Data.IsMap.IsMap
268+ [C] (Data.HashMap.Internal.HashMap k)
269+ [O] instance (Data.Hashable.Class.Hashable k, GHC.Classes.Eq k) => Streamly.Internal.Data.IsMap.IsMap (Data.HashMap.Internal.HashMap k)
270+ [N] instance Data.Hashable.Class.Hashable k => Streamly.Internal.Data.IsMap.IsMap (Data.HashMap.Internal.HashMap k)
271+[C] Streamly.Internal.Data.IOFinalizer.Lifted
272+ [C] IOFinalizer
273+[R] Streamly.Internal.Data.Fold.SVar
274+[A] Streamly.Internal.Data.Fold.Prelude
275+ [A] Config
276+ [A] Channel
277+ [A] [svarStats] :: Channel m a b -> SVarStats
278+ [A] [svarRef] :: Channel m a b -> Maybe (IORef ())
279+ [A] [svarInspectMode] :: Channel m a b -> Bool
280+ [A] [svarCreator] :: Channel m a b -> ThreadId
281+ [A] [readOutputQ] :: Channel m a b -> m [ChildEvent a]
282+ [A] [outputQueue] :: Channel m a b -> IORef ([ChildEvent a], Int)
283+ [A] [outputQueueFromConsumer] :: Channel m a b -> IORef ([ChildEvent b], Int)
284+ [A] [outputDoorBell] :: Channel m a b -> MVar ()
285+ [A] [outputDoorBellFromConsumer] :: Channel m a b -> MVar ()
286+ [A] [maxBufferLimit] :: Channel m a b -> Limit
287+ [A] [bufferSpaceDoorBell] :: Channel m a b -> MVar ()
288+ [A] Channel :: IORef ([ChildEvent a], Int) -> Limit -> MVar () -> m [ChildEvent a] -> IORef ([ChildEvent b], Int) -> MVar () -> MVar () -> Maybe (IORef ()) -> SVarStats -> Bool -> ThreadId -> Channel m a b
289+ [A] writeLimited :: MonadIO m => SVar t m a -> Maybe WorkerInfo -> Fold m a ()
290+ [A] write :: MonadIO m => SVar t m a -> Maybe WorkerInfo -> Fold m a ()
291+ [A] takeInterval :: MonadAsync m => Double -> Fold m a b -> Fold m a b
292+ [A] sendToWorker :: MonadAsync m => Channel m a b -> a -> m (Maybe b)
293+ [A] parEval :: MonadAsync m => (Config -> Config) -> Fold m a b -> Fold m a b
294+ [A] newChannel :: MonadRunInIO m => (Config -> Config) -> Fold m a b -> m (Channel m a b)
295+ [A] maxBuffer :: Int -> Config -> Config
296+ [A] intervalsOf :: MonadAsync m => Double -> Fold m a b -> Fold m b c -> Fold m a c
297+ [A] inspect :: Bool -> Config -> Config
298+ [A] dumpSVar :: Channel m a b -> IO String
299+ [A] checkFoldStatus :: MonadAsync m => Channel m a b -> m (Maybe b)
300+ [A] boundThreads :: Bool -> Config -> Config
301+[R] Streamly.Internal.Data.Fold.Concurrent.Channel.Type
302+[R] Streamly.Internal.Data.Fold.Concurrent.Channel
303+[R] Streamly.Internal.Data.Fold.Concurrent
304+[R] Streamly.Internal.Data.Fold.Async
305+[A] Streamly.Internal.Data.Channel
306+ [A] YieldRateInfo
307+ [A] [workerPollingInterval] :: YieldRateInfo -> IORef Count
308+ [A] [workerPendingLatency] :: YieldRateInfo -> IORef (Count, Count, NanoSecond64)
309+ [A] [workerMeasuredLatency] :: YieldRateInfo -> IORef NanoSecond64
310+ [A] [workerCollectedLatency] :: YieldRateInfo -> IORef (Count, Count, NanoSecond64)
311+ [A] [workerBootstrapLatency] :: YieldRateInfo -> Maybe NanoSecond64
312+ [A] [svarRateBuffer] :: YieldRateInfo -> Int
313+ [A] [svarLatencyTarget] :: YieldRateInfo -> NanoSecond64
314+ [A] [svarLatencyRange] :: YieldRateInfo -> LatencyRange
315+ [A] [svarGainedLostYields] :: YieldRateInfo -> IORef Count
316+ [A] [svarAllTimeLatency] :: YieldRateInfo -> IORef (Count, AbsTime)
317+ [A] YieldRateInfo :: NanoSecond64 -> LatencyRange -> Int -> IORef Count -> IORef (Count, AbsTime) -> Maybe NanoSecond64 -> IORef Count -> IORef (Count, Count, NanoSecond64) -> IORef (Count, Count, NanoSecond64) -> IORef NanoSecond64 -> YieldRateInfo
318+ [A] WorkerInfo
319+ [A] [workerYieldMax] :: WorkerInfo -> Count
320+ [A] [workerYieldCount] :: WorkerInfo -> IORef Count
321+ [A] [workerLatencyStart] :: WorkerInfo -> IORef (Count, AbsTime)
322+ [A] WorkerInfo :: Count -> IORef Count -> IORef (Count, AbsTime) -> WorkerInfo
323+ [A] Work
324+ [A] PartialWorker :: Count -> Work
325+ [A] ManyWorkers :: Int -> Count -> Work
326+ [A] BlockWait :: NanoSecond64 -> Work
327+ [A] ThreadAbort
328+ [A] ThreadAbort :: ThreadAbort
329+ [A] StopWhen
330+ [A] FirstStops :: StopWhen
331+ [A] AnyStops :: StopWhen
332+ [A] AllStop :: StopWhen
333+ [A] SVarStats
334+ [A] [totalDispatches] :: SVarStats -> IORef Int
335+ [A] [svarStopTime] :: SVarStats -> IORef (Maybe AbsTime)
336+ [A] [minWorkerLatency] :: SVarStats -> IORef NanoSecond64
337+ [A] [maxWorkers] :: SVarStats -> IORef Int
338+ [A] [maxWorkerLatency] :: SVarStats -> IORef NanoSecond64
339+ [A] [maxWorkQSize] :: SVarStats -> IORef Int
340+ [A] [maxOutQSize] :: SVarStats -> IORef Int
341+ [A] [maxHeapSize] :: SVarStats -> IORef Int
342+ [A] [avgWorkerLatency] :: SVarStats -> IORef (Count, NanoSecond64)
343+ [A] SVarStats :: IORef Int -> IORef Int -> IORef Int -> IORef Int -> IORef Int -> IORef (Count, NanoSecond64) -> IORef NanoSecond64 -> IORef NanoSecond64 -> IORef (Maybe AbsTime) -> SVarStats
344+ [A] Rate
345+ [A] [rateLow] :: Rate -> Double
346+ [A] [rateHigh] :: Rate -> Double
347+ [A] [rateGoal] :: Rate -> Double
348+ [A] [rateBuffer] :: Rate -> Int
349+ [A] Rate :: Double -> Double -> Double -> Int -> Rate
350+ [A] Limit
351+ [A] Unlimited :: Limit
352+ [A] Limited :: Word -> Limit
353+ [A] LatencyRange
354+ [A] [minLatency] :: LatencyRange -> NanoSecond64
355+ [A] [maxLatency] :: LatencyRange -> NanoSecond64
356+ [A] LatencyRange :: NanoSecond64 -> NanoSecond64 -> LatencyRange
357+ [A] Config
358+ [A] ChildEvent
359+ [A] ChildYield :: a -> ChildEvent a
360+ [A] ChildStopChannel :: ChildEvent a
361+ [A] ChildStop :: ThreadId -> Maybe SomeException -> ChildEvent a
362+ [A] Count
363+ [A] Count :: Int64 -> Count
364+ [A] workerRateControl :: Limit -> IORef Int -> YieldRateInfo -> WorkerInfo -> IO Bool
365+ [A] withDiagMVar :: Bool -> IO String -> String -> IO () -> IO ()
366+ [A] stopWhen :: StopWhen -> Config -> Config
367+ [A] setStreamLatency :: Int -> Config -> Config
368+ [A] sendYield :: Limit -> Limit -> IORef Int -> Maybe WorkerInfo -> Maybe YieldRateInfo -> IORef ([ChildEvent a], Int) -> MVar () -> ChildEvent a -> IO Bool
369+ [A] sendWithDoorBell :: IORef ([ChildEvent a], Int) -> MVar () -> ChildEvent a -> IO Int
370+ [A] sendStop :: IORef Int -> Maybe WorkerInfo -> Maybe YieldRateInfo -> IORef ([ChildEvent a], Int) -> MVar () -> IO ()
371+ [A] ringDoorBell :: IORef Bool -> MVar () -> IO ()
372+ [A] recordMaxWorkers :: MonadIO m => IORef Int -> SVarStats -> m ()
373+ [A] readOutputQRaw :: IORef ([ChildEvent a], Int) -> Maybe SVarStats -> IO ([ChildEvent a], Int)
374+ [A] readOutputQBasic :: IORef ([ChildEvent a], Int) -> IO ([ChildEvent a], Int)
375+ [A] rate :: Maybe Rate -> Config -> Config
376+ [A] printSVar :: IO String -> String -> IO ()
377+ [A] ordered :: Bool -> Config -> Config
378+ [A] newSVarStats :: IO SVarStats
379+ [A] newRateInfo :: Config -> IO (Maybe YieldRateInfo)
380+ [A] modifyThread :: MonadIO m => IORef (Set ThreadId) -> MVar () -> ThreadId -> m ()
381+ [A] minThreadDelay :: NanoSecond64
382+ [A] minRate :: Double -> Config -> Config
383+ [A] maxYields :: Maybe Int64 -> Config -> Config
384+ [A] maxThreads :: Int -> Config -> Config
385+ [A] maxRate :: Double -> Config -> Config
386+ [A] maxBuffer :: Int -> Config -> Config
387+ [A] magicMaxBuffer :: Word
388+ [A] isBeyondMaxRate :: Limit -> IORef Int -> YieldRateInfo -> IO Bool
389+ [A] interleaved :: Bool -> Config -> Config
390+ [A] inspect :: Bool -> Config -> Config
391+ [A] incrementYieldLimit :: Maybe (IORef Count) -> IO ()
392+ [A] handleChildException :: IORef ([ChildEvent a], Int) -> MVar () -> SomeException -> IO ()
393+ [A] getYieldLimit :: Config -> Maybe Count
394+ [A] getStreamRate :: Config -> Maybe Rate
395+ [A] getStreamLatency :: Config -> Maybe NanoSecond64
396+ [A] getStopWhen :: Config -> StopWhen
397+ [A] getOrdered :: Config -> Bool
398+ [A] getMaxThreads :: Config -> Limit
399+ [A] getMaxBuffer :: Config -> Limit
400+ [A] getInterleaved :: Config -> Bool
401+ [A] getInspectMode :: Config -> Bool
402+ [A] getEagerDispatch :: Config -> Bool
403+ [A] getBound :: Config -> Bool
404+ [A] estimateWorkers :: Limit -> Count -> Count -> NanoSecond64 -> NanoSecond64 -> NanoSecond64 -> LatencyRange -> Work
405+ [A] eager :: Bool -> Config -> Config
406+ [A] dumpWorkerCount :: Show a => IORef a -> IO String
407+ [A] dumpSVarStats :: Bool -> Maybe YieldRateInfo -> SVarStats -> IO String
408+ [A] dumpRunningThreads :: Show a => IORef a -> IO String
409+ [A] dumpOutputQ :: (Foldable t, Show a1) => IORef (t a2, a1) -> IO String
410+ [A] dumpNeedDoorBell :: Show a => IORef a -> IO String
411+ [A] dumpDoorBell :: Show a => MVar a -> IO String
412+ [A] dumpCreator :: Show a => a -> String
413+ [A] delThread :: MonadIO m => IORef (Set ThreadId) -> ThreadId -> m ()
414+ [A] defaultConfig :: Config
415+ [A] decrementYieldLimit :: Maybe (IORef Count) -> IO Bool
416+ [A] constRate :: Double -> Config -> Config
417+ [A] collectLatency :: Bool -> SVarStats -> YieldRateInfo -> Bool -> IO (Count, AbsTime, NanoSecond64)
418+ [A] cleanupSVar :: IORef (Set ThreadId) -> IO ()
419+ [A] boundThreads :: Bool -> Config -> Config
420+ [A] avgRate :: Double -> Config -> Config
421+ [A] allThreadsDone :: MonadIO m => IORef (Set ThreadId) -> m Bool
422+ [A] addThread :: MonadIO m => IORef (Set ThreadId) -> ThreadId -> m ()
423+[C] Streamly.Internal.Control.ForkLifted
424+ [A] doForkWith :: MonadRunInIO m => Bool -> m () -> RunInIO m -> (SomeException -> IO ()) -> m ThreadId
docs/User/Project/Changelog.md+23−10
@@ -2,11 +2,17 @@
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+
5## 0.10.0 (Nov 2023)
9## 0.10.0 (Nov 2023)
6
10
7−This changelog lists only the changes of the higher level `streamly`
11+See
12+[0.9.0-0.10.0 API Changelog](https://github.com/composewell/streamly/blob/streamly-0.10.0/docs/User/Project/ApiChangelogs/0.9.0-0.10.0.txt)
13+for a full list of API changes in this release. Only a few significant
8−package. For changes to the core functionality please see the changelog
14+changes are mentioned here. For changes to the core functionality
9−in the `streamly-core` package.
15+please see the changelog of the `streamly-core` package.
10
16
11### Breaking Changes
17### Breaking Changes
12
18
@@ -15,15 +21,21 @@ in the `streamly-core` package.
15 breaking change in `transformers` 0.6. You can replace `lift` with
21 breaking change in `transformers` 0.6. You can replace `lift` with
16 `fromEffect` when using these as top level monads in a monad stack.
22 `fromEffect` when using these as top level monads in a monad stack.
17
23
24+### Enhancements
25+
26+* __Concurrent Folds__: `Streamly.Data.Fold.Prelude` module added with
27+ concurrent fold APIs and a container fold API to fold a key value
28+ stream to a HashMap.
29+
18## 0.9.0 (Mar 2023)
30## 0.9.0 (Mar 2023)
19
31
20Also see the following:
32Also see the following:
21
33
22−* [streamly-0.9.0 Upgrade Guide](/docs/User/ProjectRelated/Upgrading.md) or
34+* [streamly-0.9.0 Upgrade Guide](https://github.com/composewell/streamly/blob/streamly-0.10.0/docs/User/Project/Upgrading-0.8-to-0.9.md) or
23 https://hackage.haskell.org/package/streamly-0.9.0/docs/docs/User/ProjectRelated/Upgrading.md
35 https://hackage.haskell.org/package/streamly-0.9.0/docs/docs/User/ProjectRelated/Upgrading.md
24−* [streamly-0.9.0 API Changelog](/docs/User/ProjectRelated/ApiChangelogs/0.8.3-0.9.0.txt) or
36+* [streamly-0.9.0 API Changelog](https://github.com/composewell/streamly/blob/streamly-0.10.0/docs/User/Project/ApiChangelogs/0.8.3-0.9.0.txt) or
25 https://hackage.haskell.org/package/streamly-0.9.0/docs/docs/User/ProjectRelated/ApiChangelogs/0.8.3-0.9.0.txt
37 https://hackage.haskell.org/package/streamly-0.9.0/docs/docs/User/ProjectRelated/ApiChangelogs/0.8.3-0.9.0.txt
26−* [streamly-core-0.1.0 API Changelog](/core/docs/ApiChangelogs/0.1.0.txt) or
38+* [streamly-core-0.1.0 API Changelog](https://github.com/composewell/streamly/blob/streamly-0.10.0/core/docs/ApiChangelogs/0.1.0.txt) or
27 https://hackage.haskell.org/package/streamly-core-0.1.0/docs/docs/ApiChangelogs/0.1.0.txt
39 https://hackage.haskell.org/package/streamly-core-0.1.0/docs/docs/ApiChangelogs/0.1.0.txt
28
40
29### Package split
41### Package split
@@ -70,8 +82,9 @@ added which provide a CPS based stream implementation. `Stream` and
70`StreamK` types can be easily interconverted.
82`StreamK` types can be easily interconverted.
71
83
72The old code can be adapted to use the new modules with some changes.
84The old code can be adapted to use the new modules with some changes.
73−See the [upgrade guide](/docs/User/ProjectRelated/Upgrading.md) for more
85+See the
86+[streamly-0.9.0 Upgrade Guide](https://github.com/composewell/streamly/blob/streamly-0.10.0/docs/User/Project/Upgrading-0.8-to-0.9.md)
74−details on how to adapt your existing code to the new release.
87+for more details on how to adapt your existing code to the new release.
75
88
76### Enhancements
89### Enhancements
77
90
@@ -152,7 +165,7 @@ See ApiChangelogs/0.8.3.txt for new APIs introduced.
152
165
153## 0.8.0 (Jun 2021)
166## 0.8.0 (Jun 2021)
154
167
155−See [API Changelog](/docs/User/ProjectRelated/ApiChangelogs/0.8.3.txt) for a complete list of signature
168+See [API Changelog](/docs/User/Project/ApiChangelogs/0.8.3.txt) for a complete list of signature
156changes and new APIs introduced.
169changes and new APIs introduced.
157
170
158### Breaking changes
171### Breaking changes
@@ -191,7 +204,7 @@ changes and new APIs introduced.
191
204
192### Enhancements
205### Enhancements
193
206
194−* See [API Changelog](/docs/User/ProjectRelated/ApiChangelogs/0.8.3.txt) for a complete list of new
207+* See [API Changelog](/docs/User/Project/ApiChangelogs/0.8.3.txt) for a complete list of new
195 modules and APIs introduced.
208 modules and APIs introduced.
196* The Fold type is now more powerful, the new termination behavior allows
209* The Fold type is now more powerful, the new termination behavior allows
197 to express basic parsing of streams using folds.
210 to express basic parsing of streams using folds.
docs/User/Project/README.md+9−2
@@ -1,8 +1,15 @@
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)
4[![Hackage](https://img.shields.io/hackage/v/streamly.svg?style=flat)](https://hackage.haskell.org/package/streamly)
5[![Hackage](https://img.shields.io/hackage/v/streamly.svg?style=flat)](https://hackage.haskell.org/package/streamly)
5
6
7+## Upgrading to 0.9.0+
8+
9+Please read the [Streamly 0.9.0 Upgrade Guide](/docs/User/Project/Upgrading-0.8-to-0.9.md).
10+
11+## Overview
12+
6Streamly is a powerful Haskell library that provides developers with
13Streamly is a powerful Haskell library that provides developers with
7the essential building blocks to create safe, scalable, modular, and
14the essential building blocks to create safe, scalable, modular, and
8high-performance software. With Streamly, developers can enjoy the
15high-performance software. With Streamly, developers can enjoy the
@@ -17,8 +24,8 @@ comprising:
17* Fearless, declarative concurrency for seamless parallel execution.
24* Fearless, declarative concurrency for seamless parallel execution.
18* A collection of ecosystem libraries for fast and efficient development.
25* A collection of ecosystem libraries for fast and efficient development.
19
26
20−Check out the [Streamly Setup and Usage
27+Check out the [Streamly Getting Started
21−Guide](/docs/User/Tutorials/setup-and-usage.md) and [Quick
28+Guide](/docs/User/Tutorials/getting-started.md) and [Quick
22Overview](/docs/User/Tutorials/quick-overview.md) for an introduction
29Overview](/docs/User/Tutorials/quick-overview.md) for an introduction
23to the library. For more detailed documentation, visit the [Haskell
30to the library. For more detailed documentation, visit the [Haskell
24Streamly website][Streamly].
31Streamly website][Streamly].
docs/User/Project/Upgrading.md → docs/User/Project/Upgrading-0.8-to-0.9.mdrenamed+87−9
@@ -1,9 +1,9 @@
1# Upgrading to streamly 0.9.0
1# Upgrading to streamly 0.9.0
2
2
3−Also see:
3+Also see the detailed changelog describing all the changes in the release:
4
4
5−* [0.9.0 API Changelog](/docs/User/ProjectRelated/ApiChangelogs/0.8.3-0.9.0.txt) or
5+* [0.9.0 API Changelog](/docs/User/Project/ApiChangelogs/0.8.3-0.9.0.txt) or
6− https://hackage.haskell.org/package/streamly-0.9.0/docs/docs/User/ProjectRelated/ApiChangelogs/0.8.3-0.9.0.txt
6+* [0.9.0 API Changelog](https://hackage.haskell.org/package/streamly-0.9.0/docs/docs/User/ProjectRelated/ApiChangelogs/0.8.3-0.9.0.txt)
7
7
8`Streamly.Prelude` module has been deprecated, equivalent
8`Streamly.Prelude` module has been deprecated, equivalent
9functionality is covered by the `Streamly.Data.Stream`,
9functionality is covered by the `Streamly.Data.Stream`,
@@ -46,13 +46,14 @@ Assume the following imports in the code snippets below:
46
46
47## Dependencies in cabal file
47## Dependencies in cabal file
48
48
49−If you allow streamly < 0.9 in version bounds then you may not be able
49+If you allow streamly < 0.9 in version bounds in your package then you
50−to build if you:
50+may not be able to build if any of the following is true:
51
51
52−* also depend on the new `streamly-core` package (module name conflicts)
52+* there is a dependency on `streamly-core` (module name conflicts)
53−* depend on `streamly-bytestring` >= 0.2 (type conflict)
53+* there is a dependency on `streamly-bytestring` >= 0.2 (type conflict)
54
54
55−You can use a build flag to allow older streamly versions:
55+You can define and use a build flag in your package to allow older
56+streamly versions:
56
57
57```
58```
58flag old-streamly
59flag old-streamly
@@ -103,6 +104,82 @@ ZipSerialM or WSerialT) with custom Applicative or Monadic properties.
103But in general, try to avoid specific types and use explicit functions
104But in general, try to avoid specific types and use explicit functions
104from the stream module.
105from the stream module.
105
106
107+Here are the TH macro based recipes to define types equivalent to all the older
108+types:
109+
110+```
111+{-# LANGUAGE StandaloneDeriving #-}
112+{-# LANGUAGE TemplateHaskell #-}
113+{-# LANGUAGE TypeFamilies #-}
114+{-# LANGUAGE UndecidableInstances #-}
115+
116+import Streamly.Data.Stream.MkType
117+import qualified Streamly.Data.Stream.Prelude as Stream
118+import qualified Streamly.Internal.Data.StreamK as StreamK
119+import Language.Haskell.TH
120+```
121+
122+`SerialT`: Use `CrossStreamK` from `Streamly.Internal.Data.StreamK` equivalent
123+Monad and other instances.
124+
125+For `WSerialT`, create a newtype wrapper using the following monad bind
126+operation:
127+
128+```
129+bind = StreamK.bindWith StreamK.interleave
130+```
131+
132+`WSerialT` requires `StreamK`, therefore you can not generate the code directly
133+using TH macros. However, you can print the code generated by the TH macro.
134+Then you can tweak that code, replacing `Stream` type with `StreamK`.
135+
136+```
137+expr <- runQ (mkCrossType "WSerialT" "bind" True)
138+putStrLn $ pprint expr
139+```
140+
141+For `AsyncT`:
142+
143+```
144+bind = flip (Stream.parConcatMap id)
145+$(mkCrossType "AsyncT" "bind" True)
146+```
147+
148+For `WAsyncT`:
149+
150+```
151+bind = flip (Stream.parConcatMap (Stream.interleaved True))
152+$(mkCrossType "WAsyncT" "bind" True)
153+```
154+
155+For `AheadT`:
156+
157+```
158+bind = flip (Stream.parConcatMap (Stream.ordered True))
159+$(mkCrossType "AheadT" "bind" True)
160+```
161+
162+For `ParallelT`:
163+
164+```
165+bind = flip (Stream.parConcatMap (Stream.eager True))
166+$(mkCrossType "ParallelT" "bind" True)
167+```
168+
169+For `ZipSerialM`:
170+
171+```
172+apply = Stream.zipWith ($)
173+$(mkZipType "ZipSerialM" "apply" False)
174+```
175+
176+For `ZipAsync`:
177+
178+```
179+apply = Stream.parApply id
180+$(mkZipType "ZipAsync" "apply" False)
181+```
182+
106`adapt` is not needed anymore.
183`adapt` is not needed anymore.
107
184
108```haskell docspec
185```haskell docspec
@@ -228,7 +305,8 @@ combinators are prefixed with `par`.
228Parallel combinators take a concurrency config argument to specify the
305Parallel combinators take a concurrency config argument to specify the
229concurrency control parameters. The following combinators have the same
306concurrency control parameters. The following combinators have the same
230meaning as before except that they are used to set the config parameters
307meaning as before except that they are used to set the config parameters
231−instead of being applied on the stream.
308+instead of being applied on the stream:
309+
232* `rate`
310* `rate`
233* `maxRate`
311* `maxRate`
234* `constRate`
312* `constRate`
docs/User/Tutorials/quick-overview.md+10−14
@@ -44,8 +44,6 @@ The meaning of these types are:
44 `a` in the IO Monad.
44 `a` in the IO Monad.
45* A `Fold IO a b` is a representation of a function that converts a stream of
45* A `Fold IO a b` is a representation of a function that converts a stream of
46 type `a` to a final accumulator of type `b` in the IO Monad.
46 type `a` to a final accumulator of type `b` in the IO Monad.
47−* An `Unfold IO a b` is a representation of a function that converts a seed
48− value of type `a` into a stream of values of type `b` in the IO Monad.
49
47
50## The Examples
48## The Examples
51
49
@@ -219,7 +217,7 @@ import qualified Streamly.Unicode.Stream as Unicode
219
217
220countArray :: Array Word8 -> IO Counts
218countArray :: Array Word8 -> IO Counts
221countArray arr =
219countArray arr =
222− Stream.unfold Array.reader arr -- Stream IO Word8
220+ Array.read arr -- Stream IO Word8
223 & Unicode.decodeLatin1 -- Stream IO Char
221 & Unicode.decodeLatin1 -- Stream IO Char
224 & Stream.fold (Fold.foldl' count (Counts 0 0 0 True)) -- IO Counts
222 & Stream.fold (Fold.foldl' count (Counts 0 0 0 True)) -- IO Counts
225```
223```
@@ -335,9 +333,9 @@ fetch w = threadDelay 1000000 >> return (w,w)
335-- connection is closed.
333-- connection is closed.
336lookupWords :: Socket -> IO ()
334lookupWords :: Socket -> IO ()
337lookupWords sk =
335lookupWords sk =
338− Stream.unfold Socket.reader sk -- Stream IO Word8
336+ Socket.read sk -- Stream IO Word8
339 & Unicode.decodeLatin1 -- Stream IO Char
337 & Unicode.decodeLatin1 -- Stream IO Char
340− & Stream.parseMany word -- Stream IO String
338+ & Stream.wordsBy isSpace Fold.toList -- Stream IO String
341 & Stream.parMapM cfg fetch -- Stream IO (String, String)
339 & Stream.parMapM cfg fetch -- Stream IO (String, String)
342 & fmap show -- Stream IO String
340 & fmap show -- Stream IO String
343 & Stream.intersperse "\n" -- Stream IO String
341 & Stream.intersperse "\n" -- Stream IO String
@@ -346,7 +344,6 @@ lookupWords sk =
346
344
347 where
345 where
348
346
349− word = Parser.wordBy isSpace Fold.toList
350 cfg = Stream.ordered True
347 cfg = Stream.ordered True
351
348
352serve :: Socket -> IO ()
349serve :: Socket -> IO ()
@@ -357,9 +354,9 @@ serve sk = finally (lookupWords sk) (close sk)
357-- "nc" as a client to try it out.
354-- "nc" as a client to try it out.
358main :: IO ()
355main :: IO ()
359main =
356main =
360− Stream.unfold TCP.acceptorOnPort 8091 -- Stream IO Socket
357+ TCP.accept 8091 -- Stream IO Socket
361− & Stream.parMapM id serve -- Stream IO ()
358+ & Stream.parMapM id serve -- Stream IO ()
362− & Stream.fold Fold.drain -- IO ()
359+ & Stream.fold Fold.drain -- IO ()
363```
360```
364
361
365### Merging Incoming Streams
362### Merging Incoming Streams
@@ -379,7 +376,6 @@ including the imports that we have omitted below.
379import Streamly.Data.Stream (Stream)
376import Streamly.Data.Stream (Stream)
380import System.IO (IOMode(AppendMode), Handle, withFile)
377import System.IO (IOMode(AppendMode), Handle, withFile)
381
378
382−import qualified Streamly.Data.Unfold as Unfold
383import qualified Streamly.Network.Socket as Socket
379import qualified Streamly.Network.Socket as Socket
384import qualified Streamly.FileSystem.Handle as Handle
380import qualified Streamly.FileSystem.Handle as Handle
385
381
@@ -388,9 +384,9 @@ import qualified Streamly.FileSystem.Handle as Handle
388-- buffering for safety.
384-- buffering for safety.
389readLines :: Socket -> Stream IO (Array Char)
385readLines :: Socket -> Stream IO (Array Char)
390readLines sk =
386readLines sk =
391− Stream.unfold Socket.reader sk -- Stream IO Word8
387+ Socket.read sk -- Stream IO Word8
392− & Unicode.decodeLatin1 -- Stream IO Char
388+ & Unicode.decodeLatin1 -- Stream IO Char
393− & Stream.foldMany line -- Stream IO (Array Char)
389+ & Stream.foldMany line -- Stream IO (Array Char)
394
390
395 where
391 where
396
392
@@ -405,7 +401,7 @@ recv sk = Stream.finallyIO (close sk) (readLines sk)
405-- streams at line boundaries and writes the merged stream to a file.
401-- streams at line boundaries and writes the merged stream to a file.
406server :: Handle -> IO ()
402server :: Handle -> IO ()
407server file =
403server file =
408− Stream.unfold TCP.acceptorOnPort 8090 -- Stream IO Socket
404+ TCP.accept 8090 -- Stream IO Socket
409 & Stream.parConcatMap (Stream.eager True) recv -- Stream IO (Array Char)
405 & Stream.parConcatMap (Stream.eager True) recv -- Stream IO (Array Char)
410 & Stream.unfoldMany Array.reader -- Stream IO Char
406 & Stream.unfoldMany Array.reader -- Stream IO Char
411 & Unicode.encodeLatin1 -- Stream IO Word8
407 & Unicode.encodeLatin1 -- Stream IO Word8
src/Streamly/Data/Fold/Prelude.hs+31−13
@@ -6,8 +6,27 @@
6-- Stability : released
6-- Stability : released
7-- Portability : GHC
7-- Portability : GHC
8--
8--
9+-- All Fold related combinators including the streamly-core
10+-- "Streamly.Data.Fold" module, concurrency, unordered container operations.
11+--
9module Streamly.Data.Fold.Prelude
12module Streamly.Data.Fold.Prelude
13+ (
14+ -- * "Streamly.Data.Fold"
15+ -- | All "Streamly.Data.Fold" combinators are re-exported via this
16+ -- module. For more pre-release combinators also see
17+ -- "Streamly.Internal.Data.Fold" module.
10− ( module Streamly.Data.Fold
18+ module Streamly.Data.Fold
19+ -- * Concurrent Operations
20+ -- ** Configuration
21+ , Config
22+ , maxBuffer
23+ , boundThreads
24+ , inspect
25+
26+ -- ** Combinators
27+ , parEval
28+
29+ -- * Container Related
11 , toHashMapIO
30 , toHashMapIO
12 )
31 )
13where
32where
@@ -17,27 +36,26 @@ import Data.HashMap.Strict (HashMap)
17import Data.Hashable (Hashable)
36import Data.Hashable (Hashable)
18import Streamly.Data.Fold
37import Streamly.Data.Fold
19import Streamly.Internal.Data.Fold (toContainerIO)
38import Streamly.Internal.Data.Fold (toContainerIO)
39+import Streamly.Internal.Data.Fold.Concurrent
20import Streamly.Internal.Data.IsMap.HashMap ()
40import Streamly.Internal.Data.IsMap.HashMap ()
21
41
22-- | Split the input stream based on a hashable component of the key field and
42-- | Split the input stream based on a hashable component of the key field and
23-- fold each split using the given fold. Useful for map/reduce, bucketizing
43-- fold each split using the given fold. Useful for map/reduce, bucketizing
24-- the input in different bins or for generating histograms.
44-- the input in different bins or for generating histograms.
25--
45--
26−-- Example:
27−--
28-- >>> import Data.HashMap.Strict (HashMap, fromList)
46-- >>> import Data.HashMap.Strict (HashMap, fromList)
29-- >>> import qualified Streamly.Data.Fold.Prelude as Fold
47-- >>> import qualified Streamly.Data.Fold.Prelude as Fold
30-- >>> import qualified Streamly.Data.Stream as Stream
48-- >>> import qualified Streamly.Data.Stream as Stream
31−-- >>> import Streamly.Data.Fold.Prelude (toHashMapIO)
49+--
32−-- >>> :{
50+-- Consider a stream of key value pairs:
33−-- do
51+--
34−-- let input = Stream.fromList [("ONE",1),("ONE",1.1),("TWO",2), ("TWO",2.2)]
52+-- >>> input = Stream.fromList [("k1",1),("k1",1.1),("k2",2), ("k2",2.2)]
53+--
54+-- Classify each key to a different hash bin and fold the bins:
55+--
35−-- classify = toHashMapIO fst (Fold.lmap snd Fold.toList)
56+-- >>> classify = Fold.toHashMapIO fst (Fold.lmap snd Fold.toList)
36−-- x <- Stream.fold classify input :: IO (HashMap String [Double])
57+-- >>> Stream.fold classify input :: IO (HashMap String [Double])
37−-- let y = fromList [("ONE",[1.0,1.1]),("TWO",[2.0,2.2])]
58+-- fromList [("k2",[2.0,2.2]),("k1",[1.0,1.1])]
38−-- return (x == y)
39−-- :}
40−-- True
41--
59--
42-- /Pre-release/
60-- /Pre-release/
43--
61--
src/Streamly/Data/Stream/Concurrent.hsdeleted−194
@@ -1,194 +0,0 @@
1−-- |
2−-- Module : Streamly.Data.Stream.Concurrent
3−-- Copyright : (c) 2022 Composewell Technologies
4−-- License : BSD-3-Clause
5−-- Maintainer : streamly@composewell.com
6−-- Stability : released
7−-- Portability : GHC
8−--
9−-- This module provides concurrent streaming abstractions.
10−--
11−-- == Programming Tips
12−--
13−-- The names in this module do not conflict with other stream modules,
14−-- therefore, you can import it in the same namespace:
15−--
16−-- >>> import qualified Streamly.Data.Stream.Concurrent as Stream
17−
18−module Streamly.Data.Stream.Concurrent
19− (
20− -- * Concurrency
21− -- $concurrency
22− MonadAsync
23−
24− -- ** Configuration
25− , Config
26−
27− -- *** Limits
28− , maxThreads
29− , maxBuffer
30−
31− -- *** Rate Control
32− , Rate(..)
33− , rate
34− , avgRate
35− , minRate
36− , maxRate
37− , constRate
38−
39− -- *** Stop behavior
40− , StopWhen (..)
41− , stopWhen
42−
43− -- *** Scheduling behavior
44− , eager
45− , ordered
46− , interleaved
47−
48− -- *** Diagnostics
49− , inspect
50−
51− -- ** Combinators
52− -- | Stream combinators using a concurrent channel.
53−
54− -- *** Evaluate
55− -- | Evaluates a serial stream asynchronously using a concurency channel.
56−
57− , parEval
58−
59− -- *** Generate
60− -- | Uses a concurrency channel to evaluate multiple actions concurrently.
61− , parRepeatM
62− , parReplicateM
63− , fromCallback
64−
65− -- *** Map
66− -- | Uses a concurrency channel to evaluate multiple mapped actions
67− -- concurrently.
68−
69− , parMapM
70− , parSequence
71−
72− -- *** Combine two
73− -- | Use a channel for each pair.
74− , parZipWithM
75− , parZipWith
76− , parMergeByM
77− , parMergeBy
78−
79− -- *** List of streams
80− -- | Shares a single channel across many streams.
81−
82− , parList
83− -- , zipWithM
84− -- , zipWith
85−
86− -- *** Stream of streams
87− -- **** Apply
88−
89− , parApply
90−
91− -- **** Concat
92− -- | Shares a single channel across many streams.
93−
94− , parConcat
95− , parConcatMap
96−
97− -- **** ConcatIterate
98− , parConcatIterate
99−
100− -- *** Observation
101− , parTapCount
102−
103− -- ** Deprecated
104− , tapCount
105− )
106−where
107−
108−import Streamly.Internal.Data.Stream.Concurrent
109−import Prelude hiding (mapM, sequence, concat, concatMap, zipWith)
110−
111−-- $concurrency
112−--
113−-- == Concurrency Channels
114−--
115−-- At a lower level, concurrency is implemented using channels that support
116−-- concurrent evaluation of streams. We create a channel, and add one or more
117−-- streams to it. The channel evaluates multiple streams concurrently and then
118−-- generates a single output stream from the results. How the streams are
119−-- combined depends on the configuration of the channel.
120−--
121−-- == Concurrency Primitives
122−--
123−-- There are only a few fundamental abstractions for concurrency, 'parEval',
124−-- 'parConcatMap', and 'parConcatIterate', all concurrency combinators can be
125−-- expressed in terms of these.
126−--
127−-- 'parEval', evaluates a single stream asynchronously, a worker thread runs
128−-- the stream and buffers the results, and the consumer of the stream runs in
129−-- another thread consuming it from the buffer, thus decoupling the production
130−-- and consumption of the stream. This can be used to run different stages of a
131−-- pipeline concurrently.
132−--
133−-- 'parConcatMap' is used to evaluate multiple streams concurrently and combine
134−-- the results. A stream generator function is mapped to the input stream and
135−-- all the generated streams are then evaluated concurrently, and the results
136−-- are combined.
137−--
138−-- 'parConcatIterate' is like 'parConcatMap' but iterates a stream generator
139−-- function recursively over the stream. This can be used to traverse trees or
140−-- graphs.
141−--
142−-- == Concurrency Configuration
143−--
144−-- Concurrent combinators take a 'Config' argument which controls the
145−-- concurrent behavior. For example, maximum number of threads to be used
146−-- ('maxThreads') or the maxmimum size of the buffer ('maxBuffer'), or how the
147−-- streams are scheduled with respect to each other ('interleaved'), or how the
148−-- results are consumed ('ordered').
149−--
150−-- Configuration is specified as @Config -> Config@ modifier functions that can
151−-- be composed together using function composition. For example, to specify the
152−-- maximum threads we can use @parConcatMap (maxThreads 10)@ if we also want to
153−-- specify the maximum buffer we can compose the two options @parConcatMap
154−-- (maxThreads 10 . maxBuffer 100)@. To use default configuration use 'id' as
155−-- the config modifier e.g. @parConcatMap id@.
156−--
157−-- See the @Configuration@ section and individual configuration options'
158−-- documentation for the default behavior and default values of configuration
159−-- parameters.
160−--
161−-- == Scheduling behavior
162−--
163−-- The most important configuration option is to control whether the output of
164−-- the concurrent execution is consumed in the same order as the corresponding
165−-- actions in the input stream or as soon as they arrive. The default is the
166−-- latter, however, we can enforce the original order by using the 'ordered'
167−-- option.
168−--
169−-- Another important option controls whether the number of worker threads are
170−-- automatically increased and decreased based on the consumption rate or
171−-- threads are started as aggresively as possible until the 'maxThreads' or
172−-- 'maxBuffer' limits are hit. The default is the former. However, the 'eager'
173−-- option can be enabled to use the latter behavior. When 'eager' is on, even
174−-- if the stream consumer thread blocks it does not make any impact on the
175−-- scheduling of the available tasks.
176−--
177−-- == Concurrent Combinators
178−--
179−-- Using the few fundamental concurrency primitives we can implement all the
180−-- usual streaming combinators with concurrent behavior. Combinators like
181−-- 'unfoldrM', 'iterateM' that are inherently serial can be evaluated
182−-- concurrently with respect to the consumer pipeline using 'parEval'.
183−-- Combinators like 'zipWithM', 'mergeByM' can also use 'parEval' on the input
184−-- streams to evaluate them concurrently before combining.
185−--
186−-- Combinators like 'repeatM', 'replicateM', 'fromListM', 'sequence', 'mapM' in
187−-- which all actions are independent of each other can be made concurrent using
188−-- the 'parConcatMap' operation.
189−--
190−-- A concurrent 'repeatM' repeats an action using multiple concurrent
191−-- executions of the action. Similarly, a concurrent 'mapM' performs the mapped
192−-- action in independent threads.
193−--
194−-- Some common concurrent combinators are provided in this module.
src/Streamly/Data/Stream/Exception.hsdeleted−25
@@ -1,25 +0,0 @@
1−-- |
2−-- Module : Streamly.Data.Stream.Exception
3−-- Copyright : (c) 2022 Composewell Technologies
4−-- License : BSD-3-Clause
5−-- Maintainer : streamly@composewell.com
6−-- Stability : released
7−-- Portability : GHC
8−--
9−-- This module is designed such that it does not conflict with
10−-- "Streamly.Data.Stream" module.
11−--
12−-- >>> import qualified Streamly.Data.Stream.Exception as Stream
13−--
14−module Streamly.Data.Stream.Exception
15− (
16− -- * Lifted Exceptions
17− after
18− , bracket
19− -- , bracket3
20− , finally
21− )
22−where
23−
24−import Streamly.Internal.Data.Stream.Exception.Lifted
25− ( bracket, finally, after )
src/Streamly/Data/Stream/MkType.hs+41−10
@@ -7,7 +7,8 @@
7-- Portability : GHC
7-- Portability : GHC
8--
8--
9-- Template Haskell macros to create custom newtype wrappers for the 'Stream'
9-- Template Haskell macros to create custom newtype wrappers for the 'Stream'
10−-- type, deriving all the usual instances.
10+-- type. See the examples below to create the standard stream types that were
11+-- available in streamly versions before 0.9.0.
11--
12--
12-- To use this module, the following extensions must be enabled:
13-- To use this module, the following extensions must be enabled:
13--
14--
@@ -23,31 +24,61 @@
23-- >>> import Streamly.Data.Stream.MkType
24-- >>> import Streamly.Data.Stream.MkType
24-- >>> import qualified Streamly.Data.Stream.Prelude as Stream
25-- >>> import qualified Streamly.Data.Stream.Prelude as Stream
25--
26--
26−-- Example, create an applicative type with zipping apply:
27+-- For 'Streamly.Prelude.AsyncT' monad type with a concurrent cross product
28+-- bind:
27--
29--
28-- >>> :{
30-- >>> :{
29−-- zipApply = Stream.zipWith ($)
31+-- bind = flip (Stream.parConcatMap id)
30−-- $(mkZipType "ZipStream" "zipApply" False)
32+-- $(mkCrossType "AsyncT" "bind" True)
31-- :}
33-- :}
32--
34--
33−-- Example, create an applicative type with concurrent zipping apply:
35+-- For 'Streamly.Prelude.WAsyncT' monad type with a concurrent interleaved
36+-- bind:
34--
37--
35-- >>> :{
38-- >>> :{
36−-- parApply = Stream.parApply id
39+-- bind = flip (Stream.parConcatMap (Stream.interleaved True))
37−-- $(mkZipType "ParZipStream" "parApply" True)
40+-- $(mkCrossType "WAsyncT" "bind" True)
38-- :}
41-- :}
39--
42--
43+-- For 'Streamly.Prelude.AheadT' monad type with a concurrent ordered
44+-- cross product bind:
45+--
46+-- >>> :{
47+-- bind = flip (Stream.parConcatMap (Stream.ordered True))
48+-- $(mkCrossType "AheadT" "bind" True)
49+-- :}
50+--
40−-- Example, create a monad type with an eager concurrent cross product bind:
51+-- For 'Streamly.Prelude.ParallelT' monad type with an eager concurrent cross
52+-- product bind:
41--
53--
42-- >>> :{
54-- >>> :{
43-- parBind = flip (Stream.parConcatMap (Stream.eager True))
55-- parBind = flip (Stream.parConcatMap (Stream.eager True))
44−-- $(mkCrossType "ParEagerStream" "parBind" True)
56+-- $(mkCrossType "ParallelT" "parBind" True)
57+-- :}
58+--
59+-- For 'Streamly.Prelude.ZipSerialM' serial zipping applicative type:
60+--
61+-- >>> :{
62+-- zipApply = Stream.zipWith ($)
63+-- $(mkZipType "ZipSerialM" "zipApply" False)
64+-- :}
65+--
66+-- For 'Streamly.Prelude.ZipAsync' concurrent zipping applicative type:
67+--
68+-- >>> :{
69+-- parApply = Stream.parApply id
70+-- $(mkZipType "ZipAsync" "parApply" True)
45-- :}
71-- :}
46--
72--
47-- Instead of using these macros directly you could use the generated code as
73-- Instead of using these macros directly you could use the generated code as
48-- well. Use these macros in ghci to generate the required code and paste it in
74-- well. Use these macros in ghci to generate the required code and paste it in
49-- your package, you can customize the code as desired. See the docs of the
75-- your package, you can customize the code as desired. See the docs of the
50−-- macros below for examples about how to view the generated code.
76+-- macros below for examples about how to view the generated code. For example:
77+--
78+-- >>> bind = flip (Stream.parConcatMap id)
79+-- >>> expr <- runQ (mkCrossType "AsyncT" "bind" True)
80+--
81+-- >> putStrLn $ pprint expr
51--
82--
52module Streamly.Data.Stream.MkType
83module Streamly.Data.Stream.MkType
53 (
84 (
src/Streamly/Data/Stream/Prelude.hs+217−15
@@ -6,25 +6,227 @@
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>.
9−-- This module re-exports the "Streamly.Data.Stream" module from the
11+-- Also, see the "Streamly.Data.Stream.MkType" module for direct replacement of
10−-- "streamly-core" package and additionally provides concurrency, time and
12+-- stream types that have been removed in 0.9.0.
11−-- lifted exception operations as well in a single module.
12--
13--
14+-- All Stream related combinators including the streamly-core
15+-- "Streamly.Data.Stream" module, concurrency, time and lifted
13−-- Also see the following modules for more pre-release operations:
16+-- exception operations. For more pre-release operations also see
14−--
15−-- * "Streamly.Internal.Data.Stream.Concurrent"
16−-- * "Streamly.Internal.Data.Stream.Time"
17+-- "Streamly.Internal.Data.Stream.Prelude" module.
17−-- * "Streamly.Internal.Data.Stream.Exception.Lifted"
18--
18--
19module Streamly.Data.Stream.Prelude
19module Streamly.Data.Stream.Prelude
20+ (
21+ -- * "Streamly.Data.Stream"
20− ( module Streamly.Data.Stream
22+ -- | All "Streamly.Data.Stream" combinators are re-exported via this
23+ -- module. For more pre-release combinators also see
21− , module Streamly.Data.Stream.Concurrent
24+ -- "Streamly.Internal.Data.Stream" module.
22− , module Streamly.Data.Stream.Time
25+ module Streamly.Data.Stream
23− , module Streamly.Data.Stream.Exception
26+
27+ -- * Concurrent Operations
28+ -- $concurrency
29+
30+ -- ** Types
31+ , MonadAsync
32+
33+ -- ** Configuration
34+ , Config
35+
36+ -- *** Limits
37+ , maxThreads
38+ , maxBuffer
39+
40+ -- *** Rate Control
41+ , Rate(..)
42+ , rate
43+ , avgRate
44+ , minRate
45+ , maxRate
46+ , constRate
47+
48+ -- *** Stop behavior
49+ , StopWhen (..)
50+ , stopWhen
51+
52+ -- *** Scheduling behavior
53+ , eager
54+ , ordered
55+ , interleaved
56+
57+ -- *** Diagnostics
58+ , inspect
59+
60+ -- ** Combinators
61+ -- | Stream combinators using a concurrent channel.
62+
63+ -- *** Evaluate
64+ -- | Evaluate a stream as a whole concurrently with respect to the consumer
65+ -- of the stream.
66+
67+ , parEval
68+
69+ -- *** Generate
70+ -- | Generate a stream by evaluating multiple actions concurrently.
71+ , parRepeatM
72+ , parReplicateM
73+ , fromCallback
74+
75+ -- *** Map
76+ -- | Map actions on a stream such that the mapped actions are evaluated
77+ -- concurrently with each other.
78+
79+ , parMapM
80+ , parSequence
81+
82+ -- *** Combine two
83+ -- | Combine two streams such that each stream as a whole is evaluated
84+ -- concurrently with respect to the other stream as well as the consumer of
85+ -- the resulting stream.
86+ , parZipWithM
87+ , parZipWith
88+ , parMergeByM
89+ , parMergeBy
90+
91+ -- *** List of streams
92+ -- | Shares a single channel across many streams.
93+
94+ , parList
95+ -- , zipWithM
96+ -- , zipWith
97+
98+ -- *** Stream of streams
99+ -- **** Apply
100+
101+ , parApply
102+
103+ -- **** Concat
104+ -- | Shares a single channel across many streams.
105+
106+ , parConcat
107+ , parConcatMap
108+
109+ -- **** ConcatIterate
110+ , parConcatIterate
111+
112+ -- *** Observation
113+ , parTapCount
114+
115+ -- * Time Related
116+
117+ -- ** Timers
118+ , interject
119+
120+ -- ** Trimming
121+ , takeInterval
122+ , dropInterval
123+
124+ -- ** Chunking
125+ , intervalsOf
126+
127+ -- ** Sampling
128+ , sampleIntervalEnd
129+ , sampleIntervalStart
130+ , sampleBurstEnd
131+ , sampleBurstStart
132+
133+ -- * Lifted Exceptions
134+ , after
135+ , bracket
136+ -- , bracket3
137+ , finally
138+
139+ -- ** Deprecated
140+ , tapCount
24 )
141 )
25where
142where
26
143
27import Streamly.Data.Stream
144import Streamly.Data.Stream
28−import Streamly.Data.Stream.Concurrent
145+import Streamly.Internal.Data.Stream.Prelude
29−import Streamly.Data.Stream.Exception
146+
30−import Streamly.Data.Stream.Time
147+-- $concurrency
148+--
149+-- == Channels
150+--
151+-- At a lower level, concurrency is implemented using channels that support
152+-- concurrent evaluation of streams. We create a channel, and add one or more
153+-- streams to it. The channel evaluates multiple streams concurrently and then
154+-- generates a single output stream from the results. How the streams are
155+-- combined depends on the configuration of the channel.
156+--
157+-- == Primitives
158+--
159+-- There are only a few fundamental abstractions for concurrency, 'parEval',
160+-- 'parConcatMap', and 'parConcatIterate', all concurrency combinators can be
161+-- expressed in terms of these.
162+--
163+-- 'parEval' evaluates a stream as a whole asynchronously with respect to
164+-- the consumer of the stream. A worker thread evaluates multiple elements of
165+-- the stream ahead of time and buffers the results; the consumer of the stream
166+-- runs in another thread consuming the elements from the buffer, thus
167+-- decoupling the production and consumption of the stream. 'parEval' can be
168+-- used to run different stages of a pipeline concurrently.
169+--
170+-- 'parConcatMap' is used to evaluate multiple actions in a stream concurrently
171+-- with respect to each other or to evaluate multiple streams concurrently and
172+-- combine the results. A stream generator function is mapped to the input
173+-- stream and all the generated streams are then evaluated concurrently, and
174+-- the results are combined.
175+--
176+-- 'parConcatIterate' is like 'parConcatMap' but iterates a stream generator
177+-- function recursively over the stream. This can be used to traverse trees or
178+-- graphs.
179+--
180+-- == Configuration
181+--
182+-- Concurrent combinators take a 'Config' argument which controls the
183+-- concurrent behavior. For example, maximum number of threads to be used
184+-- ('maxThreads') or the maxmimum size of the buffer ('maxBuffer'), or how the
185+-- streams are scheduled with respect to each other ('interleaved'), or how the
186+-- results are consumed ('ordered').
187+--
188+-- Configuration is specified as @Config -> Config@ modifier functions that can
189+-- be composed together using function composition. For example, to specify the
190+-- maximum threads we can use @parConcatMap (maxThreads 10)@ if we also want to
191+-- specify the maximum buffer we can compose the two options @parConcatMap
192+-- (maxThreads 10 . maxBuffer 100)@. To use default configuration use 'id' as
193+-- the config modifier e.g. @parConcatMap id@.
194+--
195+-- See the @Configuration@ section and individual configuration options'
196+-- documentation for the default behavior and default values of configuration
197+-- parameters.
198+--
199+-- == Scheduling
200+--
201+-- The most important configuration option is to control whether the output of
202+-- the concurrent execution is consumed in the same order as the corresponding
203+-- actions in the input stream or as soon as they arrive. The default is the
204+-- latter, however, we can enforce the original order by using the 'ordered'
205+-- option.
206+--
207+-- Another important option controls whether the number of worker threads are
208+-- automatically increased and decreased based on the consumption rate or
209+-- threads are started as aggresively as possible until the 'maxThreads' or
210+-- 'maxBuffer' limits are hit. The default is the former. However, the 'eager'
211+-- option can be enabled to use the latter behavior. When 'eager' is on, even
212+-- if the stream consumer thread blocks it does not make any impact on the
213+-- scheduling of the available tasks.
214+--
215+-- == Combinators
216+--
217+-- Using the few fundamental concurrency primitives we can implement all the
218+-- usual streaming combinators with concurrent behavior. Combinators like
219+-- 'unfoldrM', 'iterateM' that are inherently serial can be evaluated
220+-- concurrently with respect to the consumer pipeline using 'parEval'.
221+-- Combinators like 'zipWithM', 'mergeByM' can also use 'parEval' on the input
222+-- streams to evaluate them concurrently before combining.
223+--
224+-- Combinators like 'repeatM', 'replicateM', 'fromListM', 'sequence', 'mapM' in
225+-- which all actions are independent of each other can be made concurrent using
226+-- the 'parConcatMap' operation.
227+--
228+-- A concurrent 'repeatM' repeats an action using multiple concurrent
229+-- executions of the action. Similarly, a concurrent 'mapM' performs the mapped
230+-- action in independent threads.
231+--
232+-- Some common concurrent combinators are provided in this module.
src/Streamly/Data/Stream/Time.hsdeleted−37
@@ -1,37 +0,0 @@
1−-- |
2−-- Module : Streamly.Data.Stream.Time
3−-- Copyright : (c) 2022 Composewell Technologies
4−-- License : BSD-3-Clause
5−-- Maintainer : streamly@composewell.com
6−-- Stability : released
7−-- Portability : GHC
8−--
9−-- This module provides time related streaming abstractions.
10−--
11−-- The names in this module do not conflict with other stream modules,
12−-- therefore, you can import it in the same namespace:
13−--
14−-- >>> import qualified Streamly.Data.Stream.Time as Stream
15−--
16−module Streamly.Data.Stream.Time
17− (
18− -- * Time
19− -- ** Timers
20− interject
21−
22− -- ** Trimming
23− , takeInterval
24− , dropInterval
25−
26− -- ** Chunking
27− , intervalsOf
28−
29− -- ** Sampling
30− , sampleIntervalEnd
31− , sampleIntervalStart
32− , sampleBurstEnd
33− , sampleBurstStart
34− )
35−where
36−
37−import Streamly.Internal.Data.Stream.Time
src/Streamly/Internal/Data/Channel.hsnew file+19
@@ -0,0 +1,19 @@
1+-- |
2+-- Module : Streamly.Internal.Data.Channel
3+-- Copyright : (c) 2017 Composewell Technologies
4+-- License : BSD-3-Clause
5+-- Maintainer : streamly@composewell.com
6+-- Stability : experimental
7+-- Portability : GHC
8+
9+module Streamly.Internal.Data.Channel
10+ (
11+ module Streamly.Internal.Data.Channel.Dispatcher
12+ , module Streamly.Internal.Data.Channel.Types
13+ , module Streamly.Internal.Data.Channel.Worker
14+ )
15+where
16+
17+import Streamly.Internal.Data.Channel.Dispatcher
18+import Streamly.Internal.Data.Channel.Types
19+import Streamly.Internal.Data.Channel.Worker
src/Streamly/Internal/Data/Stream/Channel/Dispatcher.hs → src/Streamly/Internal/Data/Channel/Dispatcher.hsrenamed+3−3
@@ -1,5 +1,5 @@
1-- |
1-- |
2−-- Module : Streamly.Internal.Data.Stream.Channel.Dispatcher
2+-- Module : Streamly.Internal.Data.Channel.Dispatcher
3-- Copyright : (c) 2017 Composewell Technologies
3-- Copyright : (c) 2017 Composewell Technologies
4-- License : BSD-3-Clause
4-- License : BSD-3-Clause
5-- Maintainer : streamly@composewell.com
5-- Maintainer : streamly@composewell.com
@@ -7,7 +7,7 @@
7-- Portability : GHC
7-- Portability : GHC
8--
8--
9--
9--
10−module Streamly.Internal.Data.Stream.Channel.Dispatcher
10+module Streamly.Internal.Data.Channel.Dispatcher
11 (
11 (
12 -- * Latency collection
12 -- * Latency collection
13 minThreadDelay
13 minThreadDelay
@@ -40,7 +40,7 @@ import Streamly.Internal.Data.Time.Units
40
40
41import qualified Data.Set as S
41import qualified Data.Set as S
42
42
43−import Streamly.Internal.Data.Stream.Channel.Types
43+import Streamly.Internal.Data.Channel.Types
44
44
45-------------------------------------------------------------------------------
45-------------------------------------------------------------------------------
46-- Worker latency data processing
46-- Worker latency data processing
src/Streamly/Internal/Data/Stream/Channel/Types.hs → src/Streamly/Internal/Data/Channel/Types.hsrenamed+12−37
@@ -1,5 +1,5 @@
1-- |
1-- |
2−-- Module : Streamly.Internal.Data.Stream.Channel.Types
2+-- Module : Streamly.Internal.Data.Channel.Types
3-- Copyright : (c) 2017 Composewell Technologies
3-- Copyright : (c) 2017 Composewell Technologies
4-- License : BSD-3-Clause
4-- License : BSD-3-Clause
5-- Maintainer : streamly@composewell.com
5-- Maintainer : streamly@composewell.com
@@ -31,7 +31,7 @@
31-- consumer is not pulling fast enough. The number of workers may even get
31-- consumer is not pulling fast enough. The number of workers may even get
32-- reduced depending on the consuming pace.
32-- reduced depending on the consuming pace.
33--
33--
34−module Streamly.Internal.Data.Stream.Channel.Types
34+module Streamly.Internal.Data.Channel.Types
35 (
35 (
36 -- * Types
36 -- * Types
37 Count (..)
37 Count (..)
@@ -76,7 +76,7 @@ module Streamly.Internal.Data.Stream.Channel.Types
76 , stopWhen
76 , stopWhen
77 , ordered
77 , ordered
78 , interleaved
78 , interleaved
79− , bound
79+ , boundThreads
80
80
81 , rate
81 , rate
82 , avgRate
82 , avgRate
@@ -111,8 +111,6 @@ module Streamly.Internal.Data.Stream.Channel.Types
111
111
112 , withDiagMVar
112 , withDiagMVar
113 , printSVar
113 , printSVar
114−
115− , concatMapDivK
116 )
114 )
117where
115where
118
116
@@ -127,13 +125,11 @@ import Data.IORef (IORef, newIORef, readIORef, writeIORef)
127import Data.Set (Set)
125import Data.Set (Set)
128import Streamly.Internal.Data.Atomics
126import Streamly.Internal.Data.Atomics
129 (atomicModifyIORefCAS, atomicModifyIORefCAS_, storeLoadBarrier)
127 (atomicModifyIORefCAS, atomicModifyIORefCAS_, storeLoadBarrier)
130−import Streamly.Internal.Data.SVar.Type (adaptState)
131import Streamly.Internal.Data.Time.Clock (Clock(Monotonic), getTime)
128import Streamly.Internal.Data.Time.Clock (Clock(Monotonic), getTime)
132import Streamly.Internal.Data.Time.Units (AbsTime, NanoSecond64(..))
129import Streamly.Internal.Data.Time.Units (AbsTime, NanoSecond64(..))
133import System.IO (hPutStrLn, stderr)
130import System.IO (hPutStrLn, stderr)
134
131
135import qualified Data.Set as Set
132import qualified Data.Set as Set
136−import qualified Streamly.Internal.Data.StreamK as K
137
133
138------------------------------------------------------------------------------
134------------------------------------------------------------------------------
139-- Common types
135-- Common types
@@ -499,10 +495,10 @@ getInspectMode = _inspect
499
495
500-- | By default, processing of output from the worker threads is given priority
496-- | By default, processing of output from the worker threads is given priority
501-- over dispatching new workers. More workers are dispatched only when there is
497-- over dispatching new workers. More workers are dispatched only when there is
502−-- no output to process. With 'eager' on workers are dispatched aggresively as
498+-- no output to process. When 'eager' is set to 'True', workers are dispatched
503−-- long as there is more work to do irrespective of whether there is output
499+-- aggresively as long as there is more work to do irrespective of whether
500+-- there is output pending to be processed by the stream consumer. However,
504−-- pending to be processed. However, dispatching may stop if 'maxThreads' or
501+-- dispatching may stop if 'maxThreads' or 'maxBuffer' is reached.
505−-- 'maxBuffer' is reached.
506--
502--
507-- /Note:/ This option has no effect when rate has been specified.
503-- /Note:/ This option has no effect when rate has been specified.
508--
504--
@@ -546,13 +542,14 @@ interleaved flag st = st { _interleaved = flag }
546getInterleaved :: Config -> Bool
542getInterleaved :: Config -> Bool
547getInterleaved = _interleaved
543getInterleaved = _interleaved
548
544
545+-- TODO: Make it consistently take effect everywhere.
546+
549-- | Spawn bound threads (i.e., spawn threads using 'forkOS' instead of
547-- | Spawn bound threads (i.e., spawn threads using 'forkOS' instead of
550-- 'forkIO'). The default value is 'False'.
548-- 'forkIO'). The default value is 'False'.
551--
549--
552−-- Currently, this only takes effect for concurrent folds. TODO: Make it
550+-- Currently, this only takes effect only for concurrent folds.
553−-- consistently take effect everywhere.
554−bound :: Bool -> Config -> Config
551+boundThreads :: Bool -> Config -> Config
555−bound flag st = st { _bound = flag }
552+boundThreads flag st = st { _bound = flag }
556
553
557getBound :: Config -> Bool
554getBound :: Config -> Bool
558getBound = _bound
555getBound = _bound
@@ -818,25 +815,3 @@ cleanupSVar workerSet = do
818 Prelude.mapM_ (`throwTo` ThreadAbort)
815 Prelude.mapM_ (`throwTo` ThreadAbort)
819 -- (Prelude.filter (/= self) $ Set.toList workers)
816 -- (Prelude.filter (/= self) $ Set.toList workers)
820 (Set.toList workers)
817 (Set.toList workers)
821−
822−-------------------------------------------------------------------------------
823−-- Evaluator
824−-------------------------------------------------------------------------------
825−
826−-- | @concatMapDivK useTail useHead stream@, divides the stream in head and
827−-- tail, maps a stream generator on the head and maps an action on the tail of
828−-- a stream. Returns the stream generated by the head.
829−--
830−-- Used for concurrent evaluation of streams using a Channel.
831−{-# INLINE concatMapDivK #-}
832−concatMapDivK :: Monad m =>
833− (K.StreamK m a -> m ())
834− -> (a -> K.StreamK m b)
835− -> K.StreamK m a
836− -> K.StreamK m b
837−concatMapDivK useTail useHead stream =
838− K.mkStream $ \st yld sng stp -> do
839− let foldShared = K.foldStreamShared st yld sng stp
840− single a = foldShared $ useHead a
841− yieldk a r = useTail r >> single a
842− in K.foldStreamShared (adaptState st) yieldk single stp stream
src/Streamly/Internal/Data/Stream/Channel/Worker.hs → src/Streamly/Internal/Data/Channel/Worker.hsrenamed+3−3
@@ -1,5 +1,5 @@
1-- |
1-- |
2−-- Module : Streamly.Internal.Data.Stream.Concurrent.Channel.Worker
2+-- Module : Streamly.Internal.Data.Channel.Worker
3-- Copyright : (c) 2017 Composewell Technologies
3-- Copyright : (c) 2017 Composewell Technologies
4-- License : BSD-3-Clause
4-- License : BSD-3-Clause
5-- Maintainer : streamly@composewell.com
5-- Maintainer : streamly@composewell.com
@@ -8,7 +8,7 @@
8--
8--
9-- Collecting results from child workers in a streamed fashion
9-- Collecting results from child workers in a streamed fashion
10
10
11−module Streamly.Internal.Data.Stream.Channel.Worker
11+module Streamly.Internal.Data.Channel.Worker
12 (
12 (
13 Work (..)
13 Work (..)
14 , estimateWorkers
14 , estimateWorkers
@@ -34,7 +34,7 @@ import Streamly.Internal.Data.Time.Clock (Clock(Monotonic), getTime)
34import Streamly.Internal.Data.Time.Units
34import Streamly.Internal.Data.Time.Units
35 (AbsTime, NanoSecond64(..), diffAbsTime64, fromRelTime64)
35 (AbsTime, NanoSecond64(..), diffAbsTime64, fromRelTime64)
36
36
37−import Streamly.Internal.Data.Stream.Channel.Types
37+import Streamly.Internal.Data.Channel.Types
38
38
39-------------------------------------------------------------------------------
39-------------------------------------------------------------------------------
40-- Yield control
40-- Yield control
src/Streamly/Internal/Data/Fold/Concurrent.hs+1−44
@@ -50,51 +50,8 @@
50
50
51module Streamly.Internal.Data.Fold.Concurrent
51module Streamly.Internal.Data.Fold.Concurrent
52 (
52 (
53− -- * Configuration
53+ module Streamly.Internal.Data.Fold.Concurrent.Channel
54− Config
55− , maxBuffer
56− , bound
57− , inspect
58−
59− -- * Combinators
60− -- | Stream combinators using Async channel
61−
62− , parEval
63 )
54 )
64where
55where
65
56
66−-- import Streamly.Internal.Control.Concurrent (MonadAsync)
67−-- import Streamly.Internal.Data.Fold (Fold)
68−
69import Streamly.Internal.Data.Fold.Concurrent.Channel
57import Streamly.Internal.Data.Fold.Concurrent.Channel
70−
71−-- $setup
72−--
73−-- Imports for example snippets in this module.
74−--
75−-- >>> :m
76−-- >>> :set -XFlexibleContexts
77−-- >>> import Control.Concurrent (threadDelay)
78−-- >>> import qualified Streamly.Data.Array as Array
79−-- >>> import qualified Streamly.Data.Fold as Fold
80−-- >>> import qualified Streamly.Data.Parser as Parser
81−-- >>> import qualified Streamly.Data.Stream as Stream
82−-- >>> import qualified Streamly.Internal.Data.Fold.Concurrent as Fold
83−-- >>> import Prelude hiding (concatMap, concat)
84−-- >>> :{
85−-- delay n = do
86−-- threadDelay (n * 1000000) -- sleep for n seconds
87−-- putStrLn (show n ++ " sec") -- print "n sec"
88−-- return n -- IO Int
89−-- :}
90−
91−{-
92−-- | Evaluate a stream asynchronously using a channel and serve the consumer
93−-- from the evaluation buffer.
94−--
95−-- >>> eval = Fold.parEval id
96−--
97−{-# INLINE eval #-}
98−eval :: MonadAsync m => Fold m a b -> Fold m a b
99−eval = parEval id
100−-}
src/Streamly/Internal/Data/Fold/Concurrent/Channel.hs+4−6
@@ -8,13 +8,11 @@
8
8
9module Streamly.Internal.Data.Fold.Concurrent.Channel
9module Streamly.Internal.Data.Fold.Concurrent.Channel
10 (
10 (
11− -- * Channel
11+ module Streamly.Internal.Data.Fold.Concurrent.Channel.Type
12− Channel
13
12
14 -- * Configuration
13 -- * Configuration
15− , Config
16 , maxBuffer
14 , maxBuffer
17− , bound
15+ , boundThreads
18 , inspect
16 , inspect
19
17
20 -- * Fold operations
18 -- * Fold operations
@@ -28,11 +26,11 @@ import Control.Monad.IO.Class (MonadIO(liftIO))
28import Data.IORef (writeIORef)
26import Data.IORef (writeIORef)
29import Streamly.Internal.Control.Concurrent (MonadAsync)
27import Streamly.Internal.Control.Concurrent (MonadAsync)
30import Streamly.Internal.Data.Fold (Fold(..), Step (..))
28import Streamly.Internal.Data.Fold (Fold(..), Step (..))
31−import Streamly.Internal.Data.Stream.Channel.Worker (sendWithDoorBell)
29+import Streamly.Internal.Data.Channel.Worker (sendWithDoorBell)
32import Streamly.Internal.Data.Time.Clock (Clock(Monotonic), getTime)
30import Streamly.Internal.Data.Time.Clock (Clock(Monotonic), getTime)
33
31
34import Streamly.Internal.Data.Fold.Concurrent.Channel.Type
32import Streamly.Internal.Data.Fold.Concurrent.Channel.Type
35−import Streamly.Internal.Data.Stream.Channel.Types
33+import Streamly.Internal.Data.Channel.Types
36
34
37-------------------------------------------------------------------------------
35-------------------------------------------------------------------------------
38-- Evaluating a Fold
36-- Evaluating a Fold
src/Streamly/Internal/Data/Fold/Concurrent/Channel/Type.hs+3−3
@@ -30,13 +30,13 @@ import Streamly.Internal.Control.Concurrent
30 (MonadAsync, MonadRunInIO, askRunInIO)
30 (MonadAsync, MonadRunInIO, askRunInIO)
31import Streamly.Internal.Control.ForkLifted (doForkWith)
31import Streamly.Internal.Control.ForkLifted (doForkWith)
32import Streamly.Internal.Data.Fold (Fold(..))
32import Streamly.Internal.Data.Fold (Fold(..))
33−import Streamly.Internal.Data.Stream.Channel.Dispatcher (dumpSVarStats)
33+import Streamly.Internal.Data.Channel.Dispatcher (dumpSVarStats)
34−import Streamly.Internal.Data.Stream.Channel.Worker (sendWithDoorBell)
34+import Streamly.Internal.Data.Channel.Worker (sendWithDoorBell)
35
35
36import qualified Streamly.Internal.Data.Fold as Fold
36import qualified Streamly.Internal.Data.Fold as Fold
37import qualified Streamly.Internal.Data.Stream as D
37import qualified Streamly.Internal.Data.Stream as D
38
38
39−import Streamly.Internal.Data.Stream.Channel.Types
39+import Streamly.Internal.Data.Channel.Types
40
40
41data Channel m a b = Channel
41data Channel m a b = Channel
42 {
42 {
src/Streamly/Internal/Data/Fold/Prelude.hsnew file+19
@@ -0,0 +1,19 @@
1+-- |
2+-- Module : Streamly.Internal.Data.Fold.Prelude
3+-- Copyright : (c) 2022 Composewell Technologies
4+-- License : BSD-3-Clause
5+-- Maintainer : streamly@composewell.com
6+-- Stability : experimental
7+-- Portability : GHC
8+--
9+module Streamly.Internal.Data.Fold.Prelude
10+ (
11+ module Streamly.Internal.Data.Fold.Async
12+ , module Streamly.Internal.Data.Fold.Concurrent
13+ , module Streamly.Internal.Data.Fold.SVar
14+ )
15+where
16+
17+import Streamly.Internal.Data.Fold.Async
18+import Streamly.Internal.Data.Fold.Concurrent
19+import Streamly.Internal.Data.Fold.SVar
src/Streamly/Internal/Data/Fold/SVar.hs+1
@@ -1,3 +1,4 @@
1+{-# OPTIONS_GHC -Wno-deprecations #-}
1-- |
2-- |
2-- Module : Streamly.Internal.Data.Fold.SVar
3-- Module : Streamly.Internal.Data.Fold.SVar
3-- Copyright : (c) 2017 Composewell Technologies
4-- Copyright : (c) 2017 Composewell Technologies
src/Streamly/Internal/Data/IsMap/HashMap.hs+5
@@ -1,3 +1,8 @@
1+{-# LANGUAGE TypeFamilies #-}
2+-- Must come after TypeFamilies, otherwise it is re-enabled.
3+-- MonoLocalBinds enabled by TypeFamilies causes perf regressions in general.
4+{-# LANGUAGE NoMonoLocalBinds #-}
5+
1{-# OPTIONS_GHC -Wno-orphans #-}
6{-# OPTIONS_GHC -Wno-orphans #-}
2
7
3-- |
8-- |
src/Streamly/Internal/Data/SVar.hs+1
@@ -8,6 +8,7 @@
8--
8--
9--
9--
10module Streamly.Internal.Data.SVar
10module Streamly.Internal.Data.SVar
11+ {-# DEPRECATED "SVar is replaced by Channel." #-}
11 (
12 (
12 module Streamly.Internal.Data.SVar.Type
13 module Streamly.Internal.Data.SVar.Type
13 , module Streamly.Internal.Data.SVar.Worker
14 , module Streamly.Internal.Data.SVar.Worker
src/Streamly/Internal/Data/SmallArray.hs+1−1
@@ -106,7 +106,7 @@ foldr f z arr = runIdentity $ D.foldr f z $ toStreamD arr
106-- of elements use an 'Array' from either "Streamly.Data.Array.Generic" or "Streamly.Data.Array.Foreign".
106-- of elements use an 'Array' from either "Streamly.Data.Array.Generic" or "Streamly.Data.Array.Foreign".
107{-# INLINE_NORMAL writeN #-}
107{-# INLINE_NORMAL writeN #-}
108writeN :: MonadIO m => Int -> Fold m a (SmallArray a)
108writeN :: MonadIO m => Int -> Fold m a (SmallArray a)
109−writeN len = FL.Fold step initial extract
109+writeN len = FL.Fold step initial extract extract
110
110
111 where
111 where
112
112
src/Streamly/Internal/Data/Stream/Concurrent.hs+47−35
@@ -21,26 +21,10 @@ module Streamly.Internal.Data.Stream.Concurrent
21 -- * Imports
21 -- * Imports
22 -- $setup
22 -- $setup
23
23
24+ module Streamly.Internal.Data.Stream.Concurrent.Channel
25+
24 -- * Types
26 -- * Types
25− MonadAsync
27+ , MonadAsync
26−
27− -- * Configuration
28− , Config
29− , maxThreads
30− , maxBuffer
31− , eager
32− , StopWhen (..)
33− , stopWhen
34− , ordered
35− , interleaved
36− -- maxYields
37− , Rate(..)
38− , rate
39− , avgRate
40− , minRate
41− , maxRate
42− , constRate
43− , inspect
44
28
45 -- * Combinators
29 -- * Combinators
46 -- | Stream combinators using a concurrent channel
30 -- | Stream combinators using a concurrent channel
@@ -104,13 +88,11 @@ import Control.Monad (void, when)
104import Control.Monad.IO.Class (MonadIO(liftIO))
88import Control.Monad.IO.Class (MonadIO(liftIO))
105import Streamly.Internal.Control.Concurrent (MonadAsync, askRunInIO)
89import Streamly.Internal.Control.Concurrent (MonadAsync, askRunInIO)
106import Streamly.Internal.Control.ForkLifted (forkManaged)
90import Streamly.Internal.Control.ForkLifted (forkManaged)
107−import Streamly.Internal.Data.Stream.Channel.Dispatcher (modifyThread)
91+import Streamly.Internal.Data.Channel.Dispatcher (modifyThread)
108−import Streamly.Internal.Data.Stream.Channel.Types
92+import Streamly.Internal.Data.Channel.Types (ChildEvent(..))
109− ( ChildEvent(..)
110− , concatMapDivK
111− )
112−import Streamly.Internal.Data.Stream.Channel.Worker (sendWithDoorBell)
93+import Streamly.Internal.Data.Channel.Worker (sendWithDoorBell)
113import Streamly.Internal.Data.Stream (Stream, Step(..))
94import Streamly.Internal.Data.Stream (Stream, Step(..))
95+import Streamly.Internal.Data.SVar.Type (adaptState)
114
96
115import qualified Streamly.Internal.Data.MutArray as Unboxed
97import qualified Streamly.Internal.Data.MutArray as Unboxed
116import qualified Streamly.Internal.Data.Stream as Stream
98import qualified Streamly.Internal.Data.Stream as Stream
@@ -165,15 +147,23 @@ parEvalD modifier m = D.Stream step Nothing
165 D.Stop -> D.Stop
147 D.Stop -> D.Stop
166-}
148-}
167
149
168−-- | Evaluate a stream asynchronously. In a serial stream, each element of the
150+-- | 'parEval' evaluates a stream as a whole asynchronously with respect to
169−-- stream is generated as it is demanded by the consumer. `parEval` evaluates
151+-- the consumer of the stream. A worker thread evaluates multiple elements of
170−-- multiple elements of the stream ahead of time and serves the results from a
152+-- the stream ahead of time and buffers the results; the consumer of the stream
171−-- buffer.
153+-- runs in another thread consuming the elements from the buffer, thus
154+-- decoupling the production and consumption of the stream. 'parEval' can be
155+-- used to run different stages of a pipeline concurrently.
156+--
157+-- It is important to note that 'parEval' does not evaluate individual actions
158+-- in the stream concurrently with respect to each other, it merely evaluates
159+-- the stream serially but in a different thread than the consumer thread,
160+-- thus the consumer and producer can run concurrently. See 'parMapM' and
161+-- 'parSequence' to evaluate actions in the stream concurrently.
172--
162--
173−-- Note that the evaluation requires only one thread as only one stream needs
163+-- The evaluation requires only one thread as only one stream needs to be
174−-- to be evaluated. Therefore, the concurrency options that are relevant to
164+-- evaluated. Therefore, the concurrency options that are relevant to multiple
175−-- multiple streams won't apply here e.g. maxThreads, eager, interleaved,
165+-- streams do not apply here e.g. maxThreads, eager, interleaved, ordered,
176−-- ordered, stopWhen options won't have any effect.
166+-- stopWhen options do not have any effect on 'parEval'.
177--
167--
178-- Useful idioms:
168-- Useful idioms:
179--
169--
@@ -275,6 +265,28 @@ parTwo modifier stream1 stream2 =
275 $ appendWithK
265 $ appendWithK
276 modifier (Stream.toStreamK stream1) (Stream.toStreamK stream2)
266 modifier (Stream.toStreamK stream1) (Stream.toStreamK stream2)
277
267
268+-------------------------------------------------------------------------------
269+-- Evaluator
270+-------------------------------------------------------------------------------
271+
272+-- | @concatMapDivK useTail useHead stream@, divides the stream in head and
273+-- tail, maps a stream generator on the head and maps an action on the tail of
274+-- a stream. Returns the stream generated by the head.
275+--
276+-- Used for concurrent evaluation of streams using a Channel.
277+{-# INLINE concatMapDivK #-}
278+concatMapDivK :: Monad m =>
279+ (K.StreamK m a -> m ())
280+ -> (a -> K.StreamK m b)
281+ -> K.StreamK m a
282+ -> K.StreamK m b
283+concatMapDivK useTail useHead stream =
284+ K.mkStream $ \st yld sng stp -> do
285+ let foldShared = K.foldStreamShared st yld sng stp
286+ single a = foldShared $ useHead a
287+ yieldk a r = useTail r >> single a
288+ in K.foldStreamShared (adaptState st) yieldk single stp stream
289+
278-------------------------------------------------------------------------------
290-------------------------------------------------------------------------------
279-- concat streams
291-- concat streams
280-------------------------------------------------------------------------------
292-------------------------------------------------------------------------------
@@ -642,8 +654,8 @@ parConcatIterate modifier f input =
642
654
643 where
655 where
644
656
645− iterateStream channel stream =
657+ iterateStream channel =
646− parConcatMapChanKGeneric modifier channel (generate channel) stream
658+ parConcatMapChanKGeneric modifier channel (generate channel)
647
659
648 generate channel x =
660 generate channel x =
649 -- XXX The channel q should be FIFO for DFS, otherwise it is BFS
661 -- XXX The channel q should be FIFO for DFS, otherwise it is BFS
src/Streamly/Internal/Data/Stream/Concurrent/Channel.hs+17−14
@@ -8,15 +8,18 @@
8
8
9module Streamly.Internal.Data.Stream.Concurrent.Channel
9module Streamly.Internal.Data.Stream.Concurrent.Channel
10 (
10 (
11+ module Streamly.Internal.Data.Stream.Concurrent.Channel.Type
12+ , module Streamly.Internal.Data.Stream.Concurrent.Channel.Operations
13+ , module Streamly.Internal.Data.Stream.Concurrent.Channel.Append
14+ , module Streamly.Internal.Data.Stream.Concurrent.Channel.Interleave
15+ , module Streamly.Internal.Data.Stream.Concurrent.Channel.Dispatcher
16+ , module Streamly.Internal.Data.Stream.Concurrent.Channel.Consumer
17+
11 -- * Channel
18 -- * Channel
12− Channel (..)
19+ , Channel (..)
13 , newChannel
20 , newChannel
14 , withChannel
21 , withChannel
15 , withChannelK
22 , withChannelK
16− , fromChannel
17− , toChannel
18− , toChannelK
19− , stopChannel
20 -- quiesceChannel -- wait for running tasks but do not schedule any more.
23 -- quiesceChannel -- wait for running tasks but do not schedule any more.
21
24
22 -- * Configuration
25 -- * Configuration
@@ -52,17 +55,17 @@ where
52
55
53import Streamly.Internal.Control.Concurrent (MonadAsync)
56import Streamly.Internal.Control.Concurrent (MonadAsync)
54import Streamly.Internal.Data.Stream (Stream)
57import Streamly.Internal.Data.Stream (Stream)
55−import Streamly.Internal.Data.Stream.Concurrent.Channel.Operations
56− (fromChannel, fromChannelK, toChannel, toChannelK)
57
58
58−import qualified Streamly.Internal.Data.Stream.Concurrent.Channel.Append
59− as Append
60−import qualified Streamly.Internal.Data.Stream.Concurrent.Channel.Interleave
61− as Interleave
62import qualified Streamly.Internal.Data.StreamK as K
59import qualified Streamly.Internal.Data.StreamK as K
63
60
61+import Streamly.Internal.Data.Channel.Types
62+
64import Streamly.Internal.Data.Stream.Concurrent.Channel.Type
63import Streamly.Internal.Data.Stream.Concurrent.Channel.Type
65−import Streamly.Internal.Data.Stream.Channel.Types
64+import Streamly.Internal.Data.Stream.Concurrent.Channel.Operations
65+import Streamly.Internal.Data.Stream.Concurrent.Channel.Append
66+import Streamly.Internal.Data.Stream.Concurrent.Channel.Interleave
67+import Streamly.Internal.Data.Stream.Concurrent.Channel.Dispatcher
68+import Streamly.Internal.Data.Stream.Concurrent.Channel.Consumer
66
69
67-- | Create a new concurrent stream evaluation channel. The monad
70-- | Create a new concurrent stream evaluation channel. The monad
68-- state used to run the stream actions is captured from the call site of
71-- state used to run the stream actions is captured from the call site of
@@ -73,8 +76,8 @@ newChannel :: MonadAsync m =>
73newChannel modifier =
76newChannel modifier =
74 let cfg = modifier defaultConfig
77 let cfg = modifier defaultConfig
75 in if getInterleaved cfg
78 in if getInterleaved cfg
76− then Interleave.newChannel modifier
79+ then newInterleaveChannel modifier
77− else Append.newChannel modifier
80+ else newAppendChannel modifier
78
81
79-- | Allocate a channel and evaluate the stream using the channel and the
82-- | Allocate a channel and evaluate the stream using the channel and the
80-- supplied evaluator function. The evaluator is run in a worker thread.
83-- supplied evaluator function. The evaluator is run in a worker thread.
src/Streamly/Internal/Data/Stream/Concurrent/Channel/Append.hs+10−10
@@ -7,7 +7,7 @@
7-- Portability : GHC
7-- Portability : GHC
8--
8--
9-- The functions in this module are separated from the combinators using
9-- The functions in this module are separated from the combinators using
10−-- these because of a GHC issue. We need to have newChannel specialized but
10+-- these because of a GHC issue. We need to have newAppendChannel specialized but
11-- not inlined. If we keep it in the same module as its users we cannot achieve
11-- not inlined. If we keep it in the same module as its users we cannot achieve
12-- that and the code becomes bloated. But if it is in a separate module we can
12-- that and the code becomes bloated. But if it is in a separate module we can
13-- use INLINABLE and SPECIALIZE on it which makes it specialized but it is not
13-- use INLINABLE and SPECIALIZE on it which makes it specialized but it is not
@@ -15,7 +15,7 @@
15
15
16module Streamly.Internal.Data.Stream.Concurrent.Channel.Append
16module Streamly.Internal.Data.Stream.Concurrent.Channel.Append
17 (
17 (
18− newChannel
18+ newAppendChannel
19 )
19 )
20where
20where
21
21
@@ -32,7 +32,7 @@ import Streamly.Internal.Control.Concurrent
32 (MonadRunInIO, RunInIO(..), askRunInIO, restoreM)
32 (MonadRunInIO, RunInIO(..), askRunInIO, restoreM)
33import Streamly.Internal.Data.Atomics
33import Streamly.Internal.Data.Atomics
34 (atomicModifyIORefCAS, atomicModifyIORefCAS_)
34 (atomicModifyIORefCAS, atomicModifyIORefCAS_)
35−import Streamly.Internal.Data.Stream.Channel.Dispatcher (modifyThread)
35+import Streamly.Internal.Data.Channel.Dispatcher (modifyThread)
36
36
37import qualified Data.Heap as H
37import qualified Data.Heap as H
38import qualified Data.Set as Set
38import qualified Data.Set as Set
@@ -41,8 +41,8 @@ import qualified Streamly.Internal.Data.StreamK as K
41import Streamly.Internal.Data.Stream.Concurrent.Channel.Consumer
41import Streamly.Internal.Data.Stream.Concurrent.Channel.Consumer
42import Streamly.Internal.Data.Stream.Concurrent.Channel.Dispatcher
42import Streamly.Internal.Data.Stream.Concurrent.Channel.Dispatcher
43import Streamly.Internal.Data.Stream.Concurrent.Channel.Type
43import Streamly.Internal.Data.Stream.Concurrent.Channel.Type
44−import Streamly.Internal.Data.Stream.Channel.Types
44+import Streamly.Internal.Data.Channel.Types
45−import Streamly.Internal.Data.Stream.Channel.Worker
45+import Streamly.Internal.Data.Channel.Worker
46
46
47------------------------------------------------------------------------------
47------------------------------------------------------------------------------
48-- Concurrent streams with first-come-first serve results
48-- Concurrent streams with first-come-first serve results
@@ -1037,10 +1037,10 @@ getLifoSVar mrun cfg = do
1037
1037
1038-- | Create a new async style concurrent stream evaluation channel. The monad
1038-- | Create a new async style concurrent stream evaluation channel. The monad
1039-- state used to run the stream actions is taken from the call site of
1039-- state used to run the stream actions is taken from the call site of
1040−-- newChannel.
1040+-- newAppendChannel.
1041−{-# INLINABLE newChannel #-}
1041+{-# INLINABLE newAppendChannel #-}
1042−{-# SPECIALIZE newChannel :: (Config -> Config) -> IO (Channel IO a) #-}
1042+{-# SPECIALIZE newAppendChannel :: (Config -> Config) -> IO (Channel IO a) #-}
1043−newChannel :: MonadRunInIO m => (Config -> Config) -> m (Channel m a)
1043+newAppendChannel :: MonadRunInIO m => (Config -> Config) -> m (Channel m a)
1044−newChannel modifier = do
1044+newAppendChannel modifier = do
1045 mrun <- askRunInIO
1045 mrun <- askRunInIO
1046 liftIO $ getLifoSVar mrun (modifier defaultConfig)
1046 liftIO $ getLifoSVar mrun (modifier defaultConfig)
src/Streamly/Internal/Data/Stream/Concurrent/Channel/Consumer.hs+2−2
@@ -25,8 +25,8 @@ import Streamly.Internal.Control.Concurrent (MonadRunInIO)
25
25
26import Streamly.Internal.Data.Stream.Concurrent.Channel.Dispatcher
26import Streamly.Internal.Data.Stream.Concurrent.Channel.Dispatcher
27import Streamly.Internal.Data.Stream.Concurrent.Channel.Type
27import Streamly.Internal.Data.Stream.Concurrent.Channel.Type
28−import Streamly.Internal.Data.Stream.Channel.Dispatcher
28+import Streamly.Internal.Data.Channel.Dispatcher
29−import Streamly.Internal.Data.Stream.Channel.Types
29+import Streamly.Internal.Data.Channel.Types
30
30
31-------------------------------------------------------------------------------
31-------------------------------------------------------------------------------
32-- Reading from the workers' output queue/buffer
32-- Reading from the workers' output queue/buffer
src/Streamly/Internal/Data/Stream/Concurrent/Channel/Dispatcher.hs+3−3
@@ -34,9 +34,9 @@ import Streamly.Internal.Data.Time.Units
34 (MicroSecond64(..), diffAbsTime64, fromRelTime64, toRelTime64)
34 (MicroSecond64(..), diffAbsTime64, fromRelTime64, toRelTime64)
35
35
36import Streamly.Internal.Data.Stream.Concurrent.Channel.Type
36import Streamly.Internal.Data.Stream.Concurrent.Channel.Type
37−import Streamly.Internal.Data.Stream.Channel.Dispatcher
37+import Streamly.Internal.Data.Channel.Dispatcher
38−import Streamly.Internal.Data.Stream.Channel.Types
38+import Streamly.Internal.Data.Channel.Types
39−import Streamly.Internal.Data.Stream.Channel.Worker
39+import Streamly.Internal.Data.Channel.Worker
40
40
41-------------------------------------------------------------------------------
41-------------------------------------------------------------------------------
42-- Dispatching workers
42-- Dispatching workers
src/Streamly/Internal/Data/Stream/Concurrent/Channel/Interleave.hs+8−8
@@ -8,7 +8,7 @@
8
8
9module Streamly.Internal.Data.Stream.Concurrent.Channel.Interleave
9module Streamly.Internal.Data.Stream.Concurrent.Channel.Interleave
10 (
10 (
11− newChannel
11+ newInterleaveChannel
12 )
12 )
13where
13where
14
14
@@ -21,14 +21,14 @@ import Data.Concurrent.Queue.MichaelScott (LinkedQueue, newQ, nullQ, tryPopR, pu
21import Data.IORef (newIORef, readIORef)
21import Data.IORef (newIORef, readIORef)
22import Streamly.Internal.Control.Concurrent
22import Streamly.Internal.Control.Concurrent
23 (MonadRunInIO, MonadAsync, RunInIO(..), askRunInIO, restoreM)
23 (MonadRunInIO, MonadAsync, RunInIO(..), askRunInIO, restoreM)
24−import Streamly.Internal.Data.Stream.Channel.Dispatcher (delThread)
24+import Streamly.Internal.Data.Channel.Dispatcher (delThread)
25
25
26import qualified Data.Set as Set
26import qualified Data.Set as Set
27import qualified Streamly.Internal.Data.StreamK as K
27import qualified Streamly.Internal.Data.StreamK as K
28
28
29import Streamly.Internal.Data.Stream.Concurrent.Channel.Consumer
29import Streamly.Internal.Data.Stream.Concurrent.Channel.Consumer
30import Streamly.Internal.Data.Stream.Concurrent.Channel.Type
30import Streamly.Internal.Data.Stream.Concurrent.Channel.Type
31−import Streamly.Internal.Data.Stream.Channel.Types
31+import Streamly.Internal.Data.Channel.Types
32
32
33------------------------------------------------------------------------------
33------------------------------------------------------------------------------
34-- Creating a channel
34-- Creating a channel
@@ -242,11 +242,11 @@ getFifoSVar mrun cfg = do
242--
242--
243-- | Create a new async style concurrent stream evaluation channel. The monad
243-- | Create a new async style concurrent stream evaluation channel. The monad
244-- state used to run the stream actions is taken from the call site of
244-- state used to run the stream actions is taken from the call site of
245−-- newChannel.
245+-- newInterleaveChannel.
246−{-# INLINABLE newChannel #-}
246+{-# INLINABLE newInterleaveChannel #-}
247−{-# SPECIALIZE newChannel :: (Config -> Config) -> IO (Channel IO a) #-}
247+{-# SPECIALIZE newInterleaveChannel :: (Config -> Config) -> IO (Channel IO a) #-}
248−newChannel :: MonadAsync m =>
248+newInterleaveChannel :: MonadAsync m =>
249 (Config -> Config) -> m (Channel m a)
249 (Config -> Config) -> m (Channel m a)
250−newChannel modifier = do
250+newInterleaveChannel modifier = do
251 mrun <- askRunInIO
251 mrun <- askRunInIO
252 liftIO $ getFifoSVar mrun (modifier defaultConfig)
252 liftIO $ getFifoSVar mrun (modifier defaultConfig)
src/Streamly/Internal/Data/Stream/Concurrent/Channel/Operations.hs+1−1
@@ -44,7 +44,7 @@ import qualified Streamly.Internal.Data.StreamK as K
44
44
45import Streamly.Internal.Data.Stream.Concurrent.Channel.Dispatcher
45import Streamly.Internal.Data.Stream.Concurrent.Channel.Dispatcher
46import Streamly.Internal.Data.Stream.Concurrent.Channel.Type
46import Streamly.Internal.Data.Stream.Concurrent.Channel.Type
47−import Streamly.Internal.Data.Stream.Channel.Types hiding (inspect)
47+import Streamly.Internal.Data.Channel.Types hiding (inspect)
48
48
49import Prelude hiding (map, concat, concatMap)
49import Prelude hiding (map, concat, concatMap)
50
50
src/Streamly/Internal/Data/Stream/Concurrent/Channel/Type.hs+3−3
@@ -25,12 +25,12 @@ import Data.List (intersperse)
25import Data.Set (Set)
25import Data.Set (Set)
26import Streamly.Internal.Control.Concurrent (RunInIO)
26import Streamly.Internal.Control.Concurrent (RunInIO)
27import Streamly.Internal.Data.Atomics (atomicModifyIORefCAS_)
27import Streamly.Internal.Data.Atomics (atomicModifyIORefCAS_)
28−import Streamly.Internal.Data.Stream.Channel.Dispatcher (dumpSVarStats)
28+import Streamly.Internal.Data.Channel.Dispatcher (dumpSVarStats)
29−import Streamly.Internal.Data.Stream.Channel.Worker
29+import Streamly.Internal.Data.Channel.Worker
30 (sendYield, sendStop, sendWithDoorBell)
30 (sendYield, sendStop, sendWithDoorBell)
31import Streamly.Internal.Data.StreamK (StreamK)
31import Streamly.Internal.Data.StreamK (StreamK)
32
32
33−import Streamly.Internal.Data.Stream.Channel.Types
33+import Streamly.Internal.Data.Channel.Types
34
34
35-- IMPORTANT NOTE: we cannot update the SVar after generating it as we have
35-- IMPORTANT NOTE: we cannot update the SVar after generating it as we have
36-- references to the original SVar stored in several functions which will keep
36-- references to the original SVar stored in several functions which will keep
src/Streamly/Internal/Data/Stream/IsStream.hs+6
@@ -22,6 +22,9 @@ module Streamly.Internal.Data.Stream.IsStream {-# DEPRECATED "Please use \"Stre
22 , module Streamly.Internal.Data.Stream.IsStream.Exception
22 , module Streamly.Internal.Data.Stream.IsStream.Exception
23 , module Streamly.Internal.Data.Stream.IsStream.Lift
23 , module Streamly.Internal.Data.Stream.IsStream.Lift
24 , module Streamly.Internal.Data.Stream.IsStream.Top
24 , module Streamly.Internal.Data.Stream.IsStream.Top
25+ , module Streamly.Internal.Data.Stream.IsStream.Combinators
26+ , module Streamly.Internal.Data.Stream.IsStream.Common
27+ , module Streamly.Internal.Data.Stream.IsStream.Enumeration
25 , fromStream
28 , fromStream
26 , toStream
29 , toStream
27 )
30 )
@@ -38,6 +41,9 @@ import Streamly.Internal.Data.Stream.IsStream.Transform
38import Streamly.Internal.Data.Stream.IsStream.Type
41import Streamly.Internal.Data.Stream.IsStream.Type
39 hiding (cmpBy, drain, eqBy, foldl', fold, toList, toStream
42 hiding (cmpBy, drain, eqBy, foldl', fold, toList, toStream
40 , fromEffect, fromPure, repeat, fromStream)
43 , fromEffect, fromPure, repeat, fromStream)
44+import Streamly.Internal.Data.Stream.IsStream.Combinators
45+import Streamly.Internal.Data.Stream.IsStream.Common
46+import Streamly.Internal.Data.Stream.IsStream.Enumeration
41
47
42import qualified Streamly.Internal.Data.Stream as D
48import qualified Streamly.Internal.Data.Stream as D
43
49
src/Streamly/Internal/Data/Stream/IsStream/Common.hs+2−2
@@ -79,7 +79,7 @@ import Streamly.Internal.Data.Stream.IsStream.Type
79import Streamly.Internal.Data.Stream.Serial (SerialT)
79import Streamly.Internal.Data.Stream.Serial (SerialT)
80import Streamly.Internal.Data.Time.Units (AbsTime, RelTime64, addToAbsTime64)
80import Streamly.Internal.Data.Time.Units (AbsTime, RelTime64, addToAbsTime64)
81import Streamly.Internal.System.IO (defaultChunkSize)
81import Streamly.Internal.System.IO (defaultChunkSize)
82−import Streamly.Data.Serialize (Unbox)
82+import Streamly.Data.MutByteArray (Unbox)
83
83
84import qualified Streamly.Internal.Data.Array as A
84import qualified Streamly.Internal.Data.Array as A
85import qualified Streamly.Internal.Data.Stream.Async as Async
85import qualified Streamly.Internal.Data.Stream.Async as Async
@@ -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.flattenArraysRev -- unfoldMany A.readRev
532+ . A.concatRev -- unfoldMany A.readerRev
533 . D.fromStreamK
533 . D.fromStreamK
534 . K.reverse
534 . K.reverse
535 . D.toStreamK
535 . D.toStreamK
src/Streamly/Internal/Data/Stream/IsStream/Eliminate.hs+1−1
@@ -164,7 +164,7 @@ import Streamly.Internal.Data.Stream.IsStream.Common
164import Streamly.Internal.Data.Stream.IsStream.Type
164import Streamly.Internal.Data.Stream.IsStream.Type
165 (IsStream, fromStreamD, toStreamD)
165 (IsStream, fromStreamD, toStreamD)
166import Streamly.Internal.Data.Stream.Serial (SerialT)
166import Streamly.Internal.Data.Stream.Serial (SerialT)
167−import Streamly.Data.Serialize (Unbox)
167+import Streamly.Data.MutByteArray (Unbox)
168
168
169import qualified Streamly.Internal.Data.Array as A
169import qualified Streamly.Internal.Data.Array as A
170import qualified Streamly.Internal.Data.Stream.Serial as Stream
170import qualified Streamly.Internal.Data.Stream.Serial as Stream
src/Streamly/Internal/Data/Stream/IsStream/Exception.hs+1−1
@@ -42,7 +42,7 @@ import qualified Streamly.Internal.Data.Stream as D
42 , handle
42 , handle
43 )
43 )
44
44
45−import qualified Streamly.Internal.Data.Stream.Exception.Lifted as D
45+import qualified Streamly.Internal.Data.Stream.Lifted as D
46 ( afterD
46 ( afterD
47 , bracket3D
47 , bracket3D
48 , retryD
48 , retryD
src/Streamly/Internal/Data/Stream/IsStream/Expand.hs+5
@@ -1,3 +1,8 @@
1+{-# LANGUAGE TypeFamilies #-}
2+-- Must come after TypeFamilies, otherwise it is re-enabled.
3+-- MonoLocalBinds enabled by TypeFamilies causes perf regressions in general.
4+{-# LANGUAGE NoMonoLocalBinds #-}
5+
1{-# OPTIONS_GHC -Wno-deprecations #-}
6{-# OPTIONS_GHC -Wno-deprecations #-}
2
7
3-- |
8-- |
src/Streamly/Internal/Data/Stream/IsStream/Generate.hs+1−1
@@ -103,7 +103,7 @@ import Streamly.Internal.Data.Stream.IsStream.Type
103 (IsStream (..), fromSerial, consM, fromStreamD)
103 (IsStream (..), fromSerial, consM, fromStreamD)
104import Streamly.Internal.Data.Stream.Serial (SerialT, WSerialT)
104import Streamly.Internal.Data.Stream.Serial (SerialT, WSerialT)
105import Streamly.Internal.Data.Time.Units (AbsTime , RelTime64, addToAbsTime64)
105import Streamly.Internal.Data.Time.Units (AbsTime , RelTime64, addToAbsTime64)
106−import Streamly.Data.Serialize (Unbox)
106+import Streamly.Data.MutByteArray (Unbox)
107
107
108import qualified Streamly.Internal.Data.MutArray as Unboxed
108import qualified Streamly.Internal.Data.MutArray as Unboxed
109 (pollIntIORef, IORef)
109 (pollIntIORef, IORef)
src/Streamly/Internal/Data/Stream/IsStream/Reduce.hs+2−2
@@ -180,7 +180,7 @@ import Streamly.Internal.Data.Stream.Serial(toStreamK)
180import Streamly.Internal.Data.Time.Units
180import Streamly.Internal.Data.Time.Units
181 ( AbsTime, MilliSecond64(..), addToAbsTime, toRelTime
181 ( AbsTime, MilliSecond64(..), addToAbsTime, toRelTime
182 , toAbsTime)
182 , toAbsTime)
183−import Streamly.Data.Serialize (Unbox)
183+import Streamly.Data.MutByteArray (Unbox)
184
184
185import qualified Data.Heap as H
185import qualified Data.Heap as H
186import qualified Streamly.Internal.Data.Unfold as Unfold
186import qualified Streamly.Internal.Data.Unfold as Unfold
@@ -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 splitter joiner $ toStreamD xs
1627+ fromStreamD $ D.splitInnerBySuffix (== mempty) splitter joiner $ toStreamD xs
src/Streamly/Internal/Data/Stream/IsStream/Top.hs+1−1
@@ -649,7 +649,7 @@ intersectBySorted :: (IsStream t, Monad m) =>
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.intersectBySorted eq (IsStream.toStreamD s1)
652+ . StreamD.sortedIntersectBy 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/Exception/Lifted.hs → src/Streamly/Internal/Data/Stream/Lifted.hsrenamed+2−2
@@ -1,12 +1,12 @@
1-- |
1-- |
2−-- Module : Streamly.Internal.Data.Stream.Exception.Lifted
2+-- Module : Streamly.Internal.Data.Stream.Lifted
3-- Copyright : (c) 2019 Composewell Technologies
3-- Copyright : (c) 2019 Composewell Technologies
4-- License : BSD-3-Clause
4-- License : BSD-3-Clause
5-- Maintainer : streamly@composewell.com
5-- Maintainer : streamly@composewell.com
6-- Stability : experimental
6-- Stability : experimental
7-- Portability : GHC
7-- Portability : GHC
8
8
9−module Streamly.Internal.Data.Stream.Exception.Lifted
9+module Streamly.Internal.Data.Stream.Lifted
10 (
10 (
11 after
11 after
12 , bracket
12 , bracket
src/Streamly/Internal/Data/Stream/MkType.hs+33−34
@@ -334,10 +334,14 @@ monadthrowInstance _Type =
334 [ clause
334 [ clause
335 []
335 []
336 (normalB
336 (normalB
337+ (infixE
338+ (Just (conE _Type))
339+ (varE _dotOp)
340+ (Just
337− (infixE
341+ (infixE
338− (Just (varE _lift))
342+ (Just (varE (mkName "Stream.fromEffect")))
339− (varE _dotOp)
343+ (varE _dotOp)
340− (Just (varE _throwM))))
344+ (Just (varE _throwM))))))
341 []
345 []
342 ]
346 ]
343 ]
347 ]
@@ -504,7 +508,7 @@ flattenDec (ma:mas) = do
504-- >>> expr <- runQ (mkZipType "ZipStream" "zipApply" False)
508-- >>> expr <- runQ (mkZipType "ZipStream" "zipApply" False)
505-- >>> putStrLn $ pprint expr
509-- >>> putStrLn $ pprint expr
506-- newtype ZipStream m a
510-- newtype ZipStream m a
507−-- = ZipStream (Stream.Stream m a)
511+-- = ZipStream (Stream.Stream m a)
508-- deriving Foldable
512-- deriving Foldable
509-- mkZipStream :: Stream.Stream m a -> ZipStream m a
513-- mkZipStream :: Stream.Stream m a -> ZipStream m a
510-- mkZipStream = ZipStream
514-- mkZipStream = ZipStream
@@ -516,19 +520,17 @@ flattenDec (ma:mas) = do
516-- deriving instance GHC.Classes.Eq a => Eq (ZipStream Identity a)
520-- deriving instance GHC.Classes.Eq a => Eq (ZipStream Identity a)
517-- deriving instance GHC.Classes.Ord a => Ord (ZipStream Identity a)
521-- deriving instance GHC.Classes.Ord a => Ord (ZipStream Identity a)
518-- instance Show a => Show (ZipStream Identity a)
522-- instance Show a => Show (ZipStream Identity a)
519−-- where {-# INLINE show #-}
520−-- show (ZipStream strm) = show strm
523+-- where {{-# INLINE show #-}; show (ZipStream strm) = show strm}
521-- instance Read a => Read (ZipStream Identity a)
524-- instance Read a => Read (ZipStream Identity a)
522−-- where {-# INLINE readPrec #-}
523−-- readPrec = fmap ZipStream readPrec
525+-- where {{-# INLINE readPrec #-}; readPrec = fmap ZipStream readPrec}
524-- instance Monad m => Functor (ZipStream m)
526-- instance Monad m => Functor (ZipStream m)
525−-- where {-# INLINE fmap #-}
527+-- where {{-# INLINE fmap #-};
526−-- fmap f (ZipStream strm) = ZipStream (fmap f strm)
528+-- fmap f (ZipStream strm) = ZipStream (fmap f strm)}
527-- instance Monad m => Applicative (ZipStream m)
529-- instance Monad m => Applicative (ZipStream m)
528−-- where {-# INLINE pure #-}
530+-- where {{-# INLINE pure #-};
529−-- pure = ZipStream . Stream.repeat
531+-- pure = ZipStream . Stream.repeat;
530−-- {-# INLINE (<*>) #-}
532+-- {-# INLINE (<*>) #-};
531−-- (<*>) (ZipStream strm1) (ZipStream strm2) = ZipStream (zipApply strm1 strm2)
533+-- (<*>) (ZipStream strm1) (ZipStream strm2) = ZipStream (zipApply strm1 strm2)}
532mkZipType
534mkZipType
533 :: String -- ^ Name of the type
535 :: String -- ^ Name of the type
534 -> String -- ^ Function to use for (\<*\>)
536 -> String -- ^ Function to use for (\<*\>)
@@ -577,27 +579,25 @@ mkZipType dtNameStr apOpStr isConcurrent =
577-- unParallel :: Parallel m a -> Stream.Stream m a
579-- unParallel :: Parallel m a -> Stream.Stream m a
578-- unParallel (Parallel strm) = strm
580-- unParallel (Parallel strm) = strm
579-- instance Monad m => Functor (Parallel m)
581-- instance Monad m => Functor (Parallel m)
580−-- where {-# INLINE fmap #-}
582+-- where {{-# INLINE fmap #-};
581−-- fmap f (Parallel strm) = Parallel (fmap f strm)
583+-- fmap f (Parallel strm) = Parallel (fmap f strm)}
582-- instance Stream.MonadAsync m => Monad (Parallel m)
584-- instance Stream.MonadAsync m => Monad (Parallel m)
583−-- where {-# INLINE (>>=) #-}
585+-- where {{-# INLINE (>>=) #-};
584−-- (>>=) (Parallel strm1) f = let f1 a = unParallel (f a)
586+-- (>>=) (Parallel strm1) f = let f1 a = unParallel (f a)
585−-- in Parallel (parBind strm1 f1)
587+-- in Parallel (parBind strm1 f1)}
586-- instance Stream.MonadAsync m => Applicative (Parallel m)
588-- instance Stream.MonadAsync m => Applicative (Parallel m)
587−-- where {-# INLINE pure #-}
589+-- where {{-# INLINE pure #-};
588−-- pure = Parallel . Stream.fromPure
590+-- pure = Parallel . Stream.fromPure;
589−-- {-# INLINE (<*>) #-}
591+-- {-# INLINE (<*>) #-};
590−-- (<*>) = ap
592+-- (<*>) = ap}
591-- instance (Monad (Parallel m), MonadIO m) => MonadIO (Parallel m)
593-- instance (Monad (Parallel m), MonadIO m) => MonadIO (Parallel m)
592−-- where {-# INLINE liftIO #-}
594+-- where {{-# INLINE liftIO #-};
593−-- liftIO = Parallel . (Stream.fromEffect . liftIO)
595+-- liftIO = Parallel . (Stream.fromEffect . liftIO)}
594−-- instance MonadTrans Parallel
595−-- where {-# INLINE lift #-}
596−-- lift = Parallel . Stream.fromEffect
597-- instance (Monad (Parallel m),
596-- instance (Monad (Parallel m),
598-- MonadThrow m) => MonadThrow (Parallel m)
597-- MonadThrow m) => MonadThrow (Parallel m)
599−-- where {-# INLINE throwM #-}
598+-- where {{-# INLINE throwM #-};
600−-- throwM = lift . throwM
599+-- throwM = Parallel . (Stream.fromEffect . 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,10 +627,9 @@ mkCrossType dtNameStr bindOpStr isConcurrent =
627 "Stream.fromPure"
627 "Stream.fromPure"
628 "ap"
628 "ap"
629 , monadioInstance _Type
629 , monadioInstance _Type
630− , monadtransInstance _Type
631 , monadthrowInstance _Type
630 , monadthrowInstance _Type
632 -- , monadreaderInstance _Type
631 -- , monadreaderInstance _Type
633− ]
632+ ] ++ [monadtransInstance _Type | not isConcurrent]
634 ]
633 ]
635
634
636 where
635 where
src/Streamly/Internal/Data/Stream/Prelude.hsnew file+19
@@ -0,0 +1,19 @@
1+-- |
2+-- Module : Streamly.Internal.Data.Stream.Prelude
3+-- Copyright : (c) 2022 Composewell Technologies
4+-- License : BSD-3-Clause
5+-- Maintainer : streamly@composewell.com
6+-- Stability : experimental
7+-- Portability : GHC
8+--
9+module Streamly.Internal.Data.Stream.Prelude
10+ (
11+ module Streamly.Internal.Data.Stream.Concurrent
12+ , module Streamly.Internal.Data.Stream.Time
13+ , module Streamly.Internal.Data.Stream.Lifted
14+ )
15+where
16+
17+import Streamly.Internal.Data.Stream.Concurrent
18+import Streamly.Internal.Data.Stream.Time
19+import Streamly.Internal.Data.Stream.Lifted
src/Streamly/Internal/Data/Stream/SVar.hsnew file+18
@@ -0,0 +1,18 @@
1+-- |
2+-- Module : Streamly.Internal.Data.Stream.SVar
3+-- Copyright : (c) 2022 Composewell Technologies
4+-- License : BSD-3-Clause
5+-- Maintainer : streamly@composewell.com
6+-- Stability : experimental
7+-- Portability : GHC
8+--
9+module Streamly.Internal.Data.Stream.SVar
10+ {-# DEPRECATED "This module will be removed in future." #-}
11+ (
12+ module Streamly.Internal.Data.Stream.SVar.Eliminate
13+ , module Streamly.Internal.Data.Stream.SVar.Generate
14+ )
15+where
16+
17+import Streamly.Internal.Data.Stream.SVar.Eliminate
18+import Streamly.Internal.Data.Stream.SVar.Generate
src/Streamly/Internal/Data/Stream/Serial.hs+5
@@ -1,4 +1,9 @@
1+{-# LANGUAGE TypeFamilies #-}
2+-- Must come after TypeFamilies, otherwise it is re-enabled.
3+-- MonoLocalBinds enabled by TypeFamilies causes perf regressions in general.
4+{-# LANGUAGE NoMonoLocalBinds #-}
1{-# LANGUAGE UndecidableInstances #-}
5{-# LANGUAGE UndecidableInstances #-}
6+
2{-# OPTIONS_GHC -Wno-deprecations #-}
7{-# OPTIONS_GHC -Wno-deprecations #-}
3{-# OPTIONS_GHC -Wno-redundant-constraints #-}
8{-# OPTIONS_GHC -Wno-redundant-constraints #-}
4
9
src/Streamly/Internal/Data/Stream/Zip.hs+37
@@ -1,4 +1,9 @@
1+{-# LANGUAGE TypeFamilies #-}
2+-- Must come after TypeFamilies, otherwise it is re-enabled.
3+-- MonoLocalBinds enabled by TypeFamilies causes perf regressions in general.
4+{-# LANGUAGE NoMonoLocalBinds #-}
1{-# LANGUAGE UndecidableInstances #-}
5{-# LANGUAGE UndecidableInstances #-}
6+
2{-# OPTIONS_GHC -Wno-deprecations #-}
7{-# OPTIONS_GHC -Wno-deprecations #-}
3
8
4-- |
9-- |
@@ -13,8 +18,11 @@
13-- To run examples in this module:
18-- To run examples in this module:
14--
19--
15-- >>> import qualified Streamly.Prelude as Stream
20-- >>> import qualified Streamly.Prelude as Stream
21+-- >>> import qualified Streamly.Internal.Data.Stream as D
22+-- >>> import qualified Streamly.Data.Fold as Fold
16--
23--
17module Streamly.Internal.Data.Stream.Zip
24module Streamly.Internal.Data.Stream.Zip
25+ {-# DEPRECATED "Use \"Streamly.Data.Stream.MkType\" instead." #-}
18 (
26 (
19 ZipSerialM (..)
27 ZipSerialM (..)
20 , ZipSerial
28 , ZipSerial
@@ -22,6 +30,8 @@ module Streamly.Internal.Data.Stream.Zip
22 , zipWithK
30 , zipWithK
23 , zipWithMK
31 , zipWithMK
24
32
33+ , ZipConcurrent (..)
34+
25 -- * Deprecated
35 -- * Deprecated
26 , ZipStream
36 , ZipStream
27 )
37 )
@@ -49,6 +59,7 @@ import Streamly.Internal.BaseCompat ((#.))
49import Streamly.Internal.Data.Maybe.Strict (Maybe'(..), toMaybe)
59import Streamly.Internal.Data.Maybe.Strict (Maybe'(..), toMaybe)
50import Streamly.Internal.Data.Stream.Serial (SerialT(..))
60import Streamly.Internal.Data.Stream.Serial (SerialT(..))
51import Streamly.Internal.Data.StreamK (Stream)
61import Streamly.Internal.Data.StreamK (Stream)
62+import Streamly.Internal.Data.Stream.Concurrent (MonadAsync, parZipWith)
52
63
53import qualified Streamly.Internal.Data.Stream.Common as P
64import qualified Streamly.Internal.Data.Stream.Common as P
54import qualified Streamly.Internal.Data.StreamK as K
65import qualified Streamly.Internal.Data.StreamK as K
@@ -61,6 +72,8 @@ import Prelude hiding (map, repeat, zipWith)
61
72
62-- $setup
73-- $setup
63-- >>> import qualified Streamly.Prelude as Stream
74-- >>> import qualified Streamly.Prelude as Stream
75+-- >>> import qualified Streamly.Internal.Data.Stream as D
76+-- >>> import qualified Streamly.Data.Fold as Fold
64-- >>> import Control.Concurrent (threadDelay)
77-- >>> import Control.Concurrent (threadDelay)
65-- >>> :{
78-- >>> :{
66-- delay n = do
79-- delay n = do
@@ -135,3 +148,27 @@ instance Monad m => Applicative (ZipSerialM m) where
135
148
136FOLDABLE_INSTANCE(ZipSerialM)
149FOLDABLE_INSTANCE(ZipSerialM)
137TRAVERSABLE_INSTANCE(ZipSerialM)
150TRAVERSABLE_INSTANCE(ZipSerialM)
151+
152+-------------------------------------------------------------------------------
153+-- ZipConcurrent
154+-------------------------------------------------------------------------------
155+
156+newtype ZipConcurrent m a = ZipConcurrent {getZipConcurrent :: D.Stream m a}
157+ deriving (Functor)
158+
159+-- | An IO stream whose applicative instance zips streams concurrently. Note
160+-- that it uses the default concurrency options.
161+--
162+-- >>> s = ZipConcurrent $ D.fromList [1, 2, 3]
163+-- >>> x = (,,) <$> s <*> s <*> s
164+-- >>> D.fold Fold.toList (getZipConcurrent x)
165+-- [(1,1,1),(2,2,2),(3,3,3)]
166+--
167+-- @since 0.9.0
168+
169+instance MonadAsync m => Applicative (ZipConcurrent m) where
170+ pure = ZipConcurrent . D.repeat
171+
172+ {-# INLINE (<*>) #-}
173+ ZipConcurrent m1 <*> ZipConcurrent m2 =
174+ ZipConcurrent $ parZipWith id id m1 m2
src/Streamly/Internal/Data/Stream/Zip/Concurrent.hsdeleted−50
@@ -1,50 +0,0 @@
1−{-# LANGUAGE UndecidableInstances #-}
2−-- |
3−-- Module : Streamly.Internal.Data.Stream.Concurrent
4−-- Copyright : (c) 2017 Composewell Technologies
5−-- License : BSD-3-Clause
6−-- Maintainer : streamly@composewell.com
7−-- Stability : experimental
8−-- Portability : GHC
9−--
10−-- To run examples in this module:
11−--
12−-- >>> import qualified Streamly.Internal.Data.Stream as Stream
13−-- >>> import qualified Streamly.Data.Fold as Fold
14−--
15−module Streamly.Internal.Data.Stream.Zip.Concurrent
16− (
17− ZipConcurrent (..)
18− )
19−where
20−
21−import Streamly.Internal.Data.Stream (Stream)
22−import Streamly.Internal.Data.Stream.Concurrent (MonadAsync, parZipWith)
23−
24−import qualified Streamly.Internal.Data.Stream as Stream (repeat)
25−import Prelude hiding (map, repeat, zipWith)
26−
27−-- $setup
28−-- >>> import qualified Streamly.Internal.Data.Stream as Stream
29−-- >>> import qualified Streamly.Data.Fold as Fold
30−--
31−
32−newtype ZipConcurrent m a = ZipConcurrent {getZipConcurrent :: Stream m a}
33− deriving (Functor)
34−
35−-- | An IO stream whose applicative instance zips streams concurrently. Note
36−-- that it uses the default concurrency options.
37−--
38−-- >>> s = ZipConcurrent $ Stream.fromList [1, 2, 3]
39−-- >>> x = (,,) <$> s <*> s <*> s
40−-- >>> Stream.fold Fold.toList (getZipConcurrent x)
41−-- [(1,1,1),(2,2,2),(3,3,3)]
42−--
43−-- @since 0.9.0
44−
45−instance MonadAsync m => Applicative (ZipConcurrent m) where
46− pure = ZipConcurrent . Stream.repeat
47−
48− {-# INLINE (<*>) #-}
49− ZipConcurrent m1 <*> ZipConcurrent m2 =
50− ZipConcurrent $ parZipWith id id m1 m2
src/Streamly/Internal/Data/Stream/ZipAsync.hs+2−1
@@ -13,7 +13,8 @@
13--
13--
14-- >>> import qualified Streamly.Prelude as Stream
14-- >>> import qualified Streamly.Prelude as Stream
15--
15--
16−module Streamly.Internal.Data.Stream.ZipAsync {-# DEPRECATED "Please use \"Streamly.Internal.Data.Stream.Zip.Concurrent\" instead." #-}
16+module Streamly.Internal.Data.Stream.ZipAsync
17+ {-# DEPRECATED "Use \"Streamly.Data.Stream.MkType\" instead." #-}
17 ( ZipAsyncM(..)
18 ( ZipAsyncM(..)
18 , ZipAsync
19 , ZipAsync
19 , consMZipAsync
20 , consMZipAsync
src/Streamly/Internal/Data/Unfold/Prelude.hsnew file+17
@@ -0,0 +1,17 @@
1+-- |
2+-- Module : Streamly.Internal.Data.Unfold.Prelude
3+-- Copyright : (c) 2022 Composewell Technologies
4+-- License : BSD-3-Clause
5+-- Maintainer : streamly@composewell.com
6+-- Stability : experimental
7+-- Portability : GHC
8+--
9+module Streamly.Internal.Data.Unfold.Prelude
10+ (
11+ module Streamly.Internal.Data.Unfold.Exception
12+ , module Streamly.Internal.Data.Unfold.SVar
13+ )
14+where
15+
16+import Streamly.Internal.Data.Unfold.Exception
17+import Streamly.Internal.Data.Unfold.SVar
src/Streamly/Internal/Data/Unfold/SVar.hs+1
@@ -1,3 +1,4 @@
1+{-# OPTIONS_GHC -Wno-deprecations #-}
1-- |
2-- |
2-- Module : Streamly.Internal.Data.Unfold.SVar
3-- Module : Streamly.Internal.Data.Unfold.SVar
3-- Copyright : (c) 2019 Composewell Technologies
4-- Copyright : (c) 2019 Composewell Technologies
src/Streamly/Internal/FileSystem/Event.hs+3
@@ -20,6 +20,9 @@
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+
23module Streamly.Internal.FileSystem.Event
26module Streamly.Internal.FileSystem.Event
24 (
27 (
25 -- * Creating a Watch
28 -- * 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.asPtrUnsafe arr $ \ptr ->
454+ A.unsafePinnedAsPtr 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, asPtrUnsafe
191+ ( fromStream, asCStringUnsafe, unsafePinnedAsPtr
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.asPtrUnsafe arr readHeader
812+ (ewd, eflags, cookie, pathLen) <- PR.fromEffect $ A.unsafePinnedAsPtr 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/Event/Windows.hs+2−2
@@ -119,13 +119,13 @@ import System.Win32.Types (BOOL, DWORD, HANDLE, LPVOID, LPDWORD, failIfFalse_)
119
119
120import Streamly.Data.Array (Array)
120import Streamly.Data.Array (Array)
121import Streamly.Data.Stream (Stream)
121import Streamly.Data.Stream (Stream)
122−import Streamly.Data.Stream.Concurrent (eager)
122+import Streamly.Data.Stream.Prelude (eager)
123
123
124import qualified Data.List.NonEmpty as NonEmpty
124import qualified Data.List.NonEmpty as NonEmpty
125import qualified Streamly.Data.Array as A (fromList)
125import qualified Streamly.Data.Array as A (fromList)
126import qualified Streamly.Data.Fold as Fold
126import qualified Streamly.Data.Fold as Fold
127import qualified Streamly.Data.Stream as S
127import qualified Streamly.Data.Stream as S
128−import qualified Streamly.Data.Stream.Concurrent as S
128+import qualified Streamly.Data.Stream.Prelude as S
129import qualified Streamly.Unicode.Stream as U
129import qualified Streamly.Unicode.Stream as U
130import qualified Streamly.Internal.Unicode.Utf8 as UTF8 (pack, toArray)
130import qualified Streamly.Internal.Unicode.Utf8 as UTF8 (pack, toArray)
131import qualified Streamly.Internal.Data.Array as A (read)
131import qualified Streamly.Internal.Data.Array as A (read)
src/Streamly/Internal/FileSystem/FD.hs+7−8
@@ -120,7 +120,6 @@ import Control.Monad.IO.Class (MonadIO(..))
120import Data.Word (Word8)
120import Data.Word (Word8)
121-- import Foreign.ForeignPtr.Unsafe (unsafeForeignPtrToPtr)
121-- import Foreign.ForeignPtr.Unsafe (unsafeForeignPtrToPtr)
122import Foreign.Ptr (castPtr)
122import Foreign.Ptr (castPtr)
123−import Streamly.Internal.Data.Unbox (Unbox)
124-- import System.IO (Handle, hGetBufSome, hPutBuf)
123-- import System.IO (Handle, hGetBufSome, hPutBuf)
125import System.IO (IOMode)
124import System.IO (IOMode)
126import Prelude hiding (read)
125import Prelude hiding (read)
@@ -128,10 +127,10 @@ import Prelude hiding (read)
128import qualified GHC.IO.FD as FD
127import qualified GHC.IO.FD as FD
129import qualified GHC.IO.Device as RawIO
128import qualified GHC.IO.Device as RawIO
130
129
131−import Streamly.Data.Array (Array)
130+import Streamly.Data.Array (Array, Unbox)
132import Streamly.Data.Stream (Stream)
131import Streamly.Data.Stream (Stream)
133
132
134−import Streamly.Internal.Data.Array (byteLength, unsafeFreeze, asPtrUnsafe)
133+import Streamly.Internal.Data.Array (byteLength, unsafeFreeze, unsafePinnedAsPtr)
135import Streamly.Internal.System.IO (defaultChunkSize)
134import Streamly.Internal.System.IO (defaultChunkSize)
136
135
137#if !defined(mingw32_HOST_OS)
136#if !defined(mingw32_HOST_OS)
@@ -148,8 +147,8 @@ import qualified Streamly.Internal.System.IOVec.Type as RawIO
148import qualified Streamly.Data.Array as A
147import qualified Streamly.Data.Array as A
149import qualified Streamly.Data.Fold as FL
148import qualified Streamly.Data.Fold as FL
150import qualified Streamly.Internal.Data.MutArray as MArray
149import qualified Streamly.Internal.Data.MutArray as MArray
151− (MutArray(..), asPtrUnsafe, pinnedNewBytes)
150+ (MutArray(..), unsafePinnedAsPtr, pinnedNewBytes)
152−import qualified Streamly.Internal.Data.Stream.Chunked as AS
151+import qualified Streamly.Internal.Data.Array.Stream as AS
153import qualified Streamly.Internal.Data.Stream as S
152import qualified Streamly.Internal.Data.Stream as S
154import qualified Streamly.Internal.Data.Stream as D
153import qualified Streamly.Internal.Data.Stream as D
155 (Stream(..), Step(..))
154 (Stream(..), Step(..))
@@ -221,7 +220,7 @@ readArrayUpto :: Int -> Handle -> IO (Array Word8)
221readArrayUpto size (Handle fd) = do
220readArrayUpto size (Handle fd) = do
222 arr <- MArray.pinnedNewBytes size
221 arr <- MArray.pinnedNewBytes size
223 -- ptr <- mallocPlainForeignPtrAlignedBytes size (alignment (undefined :: Word8))
222 -- ptr <- mallocPlainForeignPtrAlignedBytes size (alignment (undefined :: Word8))
224− MArray.asPtrUnsafe arr $ \p -> do
223+ MArray.unsafePinnedAsPtr arr $ \p -> do
225 -- n <- hGetBufSome h p size
224 -- n <- hGetBufSome h p size
226#if MIN_VERSION_base(4,15,0)
225#if MIN_VERSION_base(4,15,0)
227 n <- RawIO.read fd p 0 size
226 n <- RawIO.read fd p 0 size
@@ -245,7 +244,7 @@ readArrayUpto size (Handle fd) = do
245writeArray :: Unbox a => Handle -> Array a -> IO ()
244writeArray :: Unbox a => Handle -> Array a -> IO ()
246writeArray _ arr | A.length arr == 0 = return ()
245writeArray _ arr | A.length arr == 0 = return ()
247writeArray (Handle fd) arr =
246writeArray (Handle fd) arr =
248− asPtrUnsafe arr $ \p ->
247+ unsafePinnedAsPtr arr $ \p ->
249 -- RawIO.writeAll fd (castPtr p) aLen
248 -- RawIO.writeAll fd (castPtr p) aLen
250#if MIN_VERSION_base(4,15,0)
249#if MIN_VERSION_base(4,15,0)
251 RawIO.write fd (castPtr p) 0 aLen
250 RawIO.write fd (castPtr p) 0 aLen
@@ -271,7 +270,7 @@ writeArray (Handle fd) arr =
271writeIOVec :: Handle -> Array RawIO.IOVec -> IO ()
270writeIOVec :: Handle -> Array RawIO.IOVec -> IO ()
272writeIOVec _ iov | A.length iov == 0 = return ()
271writeIOVec _ iov | A.length iov == 0 = return ()
273writeIOVec (Handle fd) iov =
272writeIOVec (Handle fd) iov =
274− asPtrUnsafe iov $ \p ->
273+ unsafePinnedAsPtr iov $ \p ->
275 RawIO.writevAll fd p (A.length iov)
274 RawIO.writevAll fd p (A.length iov)
276-}
275-}
277#endif
276#endif
src/Streamly/Internal/Network/Inet/TCP.hs+69−40
@@ -11,20 +11,25 @@
11
11
12module Streamly.Internal.Network.Inet.TCP
12module Streamly.Internal.Network.Inet.TCP
13 (
13 (
14+ -- * Setup
15+ -- | To execute the code examples provided in this module in ghci, please
16+ -- run the following commands first.
17+ --
18+ -- $setup
19+
14 -- * TCP Servers
20 -- * TCP Servers
15 -- ** Streams
21 -- ** Streams
22+ accept
23+ , acceptLocal
16− acceptOnAddr
24+ , acceptOnAddr
17 , acceptOnAddrWith
25 , acceptOnAddrWith
18− , acceptOnPort
19− -- , acceptOnPortWith
20− , acceptOnPortLocal
21
26
22 -- ** Unfolds
27 -- ** Unfolds
28+ , acceptor
29+ , acceptorLocal
30+ , acceptorWith
23 , acceptorOnAddr
31 , acceptorOnAddr
24 , acceptorOnAddrWith
32 , acceptorOnAddrWith
25− , acceptorOnPort
26− , acceptorOnPortWith
27− , acceptorOnPortLocal
28
33
29 -- * TCP clients
34 -- * TCP clients
30 -- | IP Address based operations.
35 -- | IP Address based operations.
@@ -89,6 +94,10 @@ module Streamly.Internal.Network.Inet.TCP
89 , datagrams
94 , datagrams
90 , datagramsOn
95 , datagramsOn
91 -}
96 -}
97+
98+ -- * Deprecated
99+ , acceptorOnPort
100+ , acceptorOnPortLocal
92 )
101 )
93where
102where
94
103
@@ -107,13 +116,12 @@ import Prelude hiding (read)
107import Streamly.Internal.Control.Concurrent (MonadAsync)
116import Streamly.Internal.Control.Concurrent (MonadAsync)
108import Streamly.Internal.Control.ForkLifted (fork)
117import Streamly.Internal.Control.ForkLifted (fork)
109import Streamly.Data.Array (Array)
118import Streamly.Data.Array (Array)
110−import Streamly.Internal.Data.Array (pinnedWriteNUnsafe)
111import Streamly.Internal.Data.Fold ( Fold(..) )
119import Streamly.Internal.Data.Fold ( Fold(..) )
112import Streamly.Data.Stream (Stream)
120import Streamly.Data.Stream (Stream)
113import Streamly.Internal.Data.Tuple.Strict (Tuple'(..))
121import Streamly.Internal.Data.Tuple.Strict (Tuple'(..))
114−import Streamly.Data.Serialize (Unbox)
122+import Streamly.Data.MutByteArray (Unbox)
115import Streamly.Data.Unfold (Unfold)
123import Streamly.Data.Unfold (Unfold)
116−import Streamly.Internal.Network.Socket (SockSpec(..), accept, acceptor)
124+import Streamly.Internal.Network.Socket (SockSpec(..))
117import Streamly.Internal.System.IO (defaultChunkSize)
125import Streamly.Internal.System.IO (defaultChunkSize)
118
126
119import qualified Control.Monad.Catch as MC
127import qualified Control.Monad.Catch as MC
@@ -123,13 +131,19 @@ import qualified Streamly.Data.Array as A
123import qualified Streamly.Data.Fold as FL
131import qualified Streamly.Data.Fold as FL
124import qualified Streamly.Data.Stream as S
132import qualified Streamly.Data.Stream as S
125import qualified Streamly.Data.Unfold as UF
133import qualified Streamly.Data.Unfold as UF
126−import qualified Streamly.Internal.Data.Array as A (pinnedChunksOf)
134+import qualified Streamly.Internal.Data.Array as A
135+ (pinnedChunksOf, unsafePinnedCreateOf)
127−import qualified Streamly.Internal.Data.Unfold as UF (first, bracketIO)
136+import qualified Streamly.Internal.Data.Unfold as UF (bracketIO)
128import qualified Streamly.Internal.Data.Fold as FL (Step(..), reduce)
137import qualified Streamly.Internal.Data.Fold as FL (Step(..), reduce)
129
138
130−import qualified Streamly.Internal.Data.Stream.Exception.Lifted as S (bracket)
139+import qualified Streamly.Internal.Data.Stream.Lifted as S (bracket)
131import qualified Streamly.Internal.Network.Socket as ISK
140import qualified Streamly.Internal.Network.Socket as ISK
132− (chunkReader, putBytes, putChunks, read, readChunks, writeChunks)
141+
142+-- $setup
143+-- >>> :m
144+--
145+-- >>> import qualified Streamly.Data.Unfold as Unfold
146+-- >>> import qualified Streamly.Network.Inet.TCP as TCP
133
147
134-------------------------------------------------------------------------------
148-------------------------------------------------------------------------------
135-- Accept (unfolds)
149-- Accept (unfolds)
@@ -140,7 +154,7 @@ acceptorOnAddrWith
140 :: MonadIO m
154 :: MonadIO m
141 => [(SocketOption, Int)]
155 => [(SocketOption, Int)]
142 -> Unfold m ((Word8, Word8, Word8, Word8), PortNumber) Socket
156 -> Unfold m ((Word8, Word8, Word8, Word8), PortNumber) Socket
143−acceptorOnAddrWith opts = UF.lmap f acceptor
157+acceptorOnAddrWith opts = UF.lmap f ISK.acceptor
144 where
158 where
145 f (addr, port) =
159 f (addr, port) =
146 (maxListenQueue
160 (maxListenQueue
@@ -163,36 +177,50 @@ acceptorOnAddr
163 => Unfold m ((Word8, Word8, Word8, Word8), PortNumber) Socket
177 => Unfold m ((Word8, Word8, Word8, Word8), PortNumber) Socket
164acceptorOnAddr = acceptorOnAddrWith []
178acceptorOnAddr = acceptorOnAddrWith []
165
179
166−{-# INLINE acceptorOnPortWith #-}
180+{-# INLINE acceptorWith #-}
167−acceptorOnPortWith :: MonadIO m
181+acceptorWith :: MonadIO m
168 => [(SocketOption, Int)]
182 => [(SocketOption, Int)]
169 -> Unfold m PortNumber Socket
183 -> Unfold m PortNumber Socket
170−acceptorOnPortWith opts = UF.first (0,0,0,0) (acceptorOnAddrWith opts)
184+acceptorWith opts = UF.first (0,0,0,0) (acceptorOnAddrWith opts)
171
185
172-- | Like 'acceptorOnAddr' but binds on the IPv4 address @0.0.0.0@ i.e. on all
186-- | Like 'acceptorOnAddr' but binds on the IPv4 address @0.0.0.0@ i.e. on all
173-- IPv4 addresses/interfaces of the machine and listens for TCP connections on
187-- IPv4 addresses/interfaces of the machine and listens for TCP connections on
174-- the specified port.
188-- the specified port.
175--
189--
176−-- > acceptorOnPort = UF.first acceptorOnAddr (0,0,0,0)
190+-- >>> acceptor = Unfold.first (0,0,0,0) TCP.acceptorOnAddr
177--
191--
192+{-# INLINE acceptor #-}
193+acceptor :: MonadIO m => Unfold m PortNumber Socket
194+acceptor = UF.first (0,0,0,0) acceptorOnAddr
195+
196+{-# DEPRECATED acceptorOnPort "Use \"acceptor\" instead." #-}
178{-# INLINE acceptorOnPort #-}
197{-# INLINE acceptorOnPort #-}
179acceptorOnPort :: MonadIO m => Unfold m PortNumber Socket
198acceptorOnPort :: MonadIO m => Unfold m PortNumber Socket
180−acceptorOnPort = UF.first (0,0,0,0) acceptorOnAddr
199+acceptorOnPort = acceptor
181
200
182−-- | Like 'acceptorOnAddr' but binds on the localhost IPv4 address @127.0.0.1@.
201+-- | Like 'acceptor' but binds on the localhost IPv4 address @127.0.0.1@. The
183−-- The server can only be accessed from the local host, it cannot be accessed
202+-- server can only be accessed from the local host, it cannot be accessed from
184−-- from other hosts on the network.
203+-- other hosts on the network.
185--
204--
186−-- > acceptorOnPortLocal = UF.first acceptorOnAddr (127,0,0,1)
205+-- >>> acceptorLocal = Unfold.first (127,0,0,1) TCP.acceptorOnAddr
187--
206--
207+{-# INLINE acceptorLocal #-}
208+acceptorLocal :: MonadIO m => Unfold m PortNumber Socket
209+acceptorLocal = UF.first (127,0,0,1) acceptorOnAddr
210+
211+{-# DEPRECATED acceptorOnPortLocal "Use \"acceptorLocal\" instead." #-}
188{-# INLINE acceptorOnPortLocal #-}
212{-# INLINE acceptorOnPortLocal #-}
189acceptorOnPortLocal :: MonadIO m => Unfold m PortNumber Socket
213acceptorOnPortLocal :: MonadIO m => Unfold m PortNumber Socket
190−acceptorOnPortLocal = UF.first (127,0,0,1) acceptorOnAddr
214+acceptorOnPortLocal = acceptorLocal
191
215
192-------------------------------------------------------------------------------
216-------------------------------------------------------------------------------
193-- Accept (streams)
217-- Accept (streams)
194-------------------------------------------------------------------------------
218-------------------------------------------------------------------------------
195
219
220+-- | Like 'acceptOnAddr' but with the ability to specify a list of socket
221+-- options.
222+--
223+-- /Pre-release/
196{-# INLINE acceptOnAddrWith #-}
224{-# INLINE acceptOnAddrWith #-}
197acceptOnAddrWith
225acceptOnAddrWith
198 :: MonadIO m
226 :: MonadIO m
@@ -201,7 +229,7 @@ acceptOnAddrWith
201 -> PortNumber
229 -> PortNumber
202 -> Stream m Socket
230 -> Stream m Socket
203acceptOnAddrWith opts addr port =
231acceptOnAddrWith opts addr port =
204− accept maxListenQueue SockSpec
232+ ISK.accept maxListenQueue SockSpec
205 { sockFamily = AF_INET
233 { sockFamily = AF_INET
206 , sockType = Stream
234 , sockType = Stream
207 , sockProto = defaultProtocol
235 , sockProto = defaultProtocol
@@ -209,8 +237,9 @@ acceptOnAddrWith opts addr port =
209 }
237 }
210 (SockAddrInet port (tupleToHostAddress addr))
238 (SockAddrInet port (tupleToHostAddress addr))
211
239
212−-- | Like 'accept' but binds on the specified IPv4 address of the machine
240+-- | Like 'accept' but binds on the specified IPv4 address.
213−-- and listens for TCP connections on the specified port.
241+--
242+-- >>> acceptOnAddr = TCP.acceptOnAddrWith []
214--
243--
215-- /Pre-release/
244-- /Pre-release/
216{-# INLINE acceptOnAddr #-}
245{-# INLINE acceptOnAddr #-}
@@ -221,27 +250,27 @@ acceptOnAddr
221 -> Stream m Socket
250 -> Stream m Socket
222acceptOnAddr = acceptOnAddrWith []
251acceptOnAddr = acceptOnAddrWith []
223
252
224−-- | Like 'accept' but binds on the IPv4 address @0.0.0.0@ i.e. on all
253+-- | Start a TCP stream server that binds on the IPV4 address @0.0.0.0@ and
225−-- IPv4 addresses/interfaces of the machine and listens for TCP connections on
254+-- listens for TCP connections from remote hosts on the specified server port.
226−-- the specified port.
255+-- The server generates a stream of connected sockets.
227--
256--
228−-- > acceptOnPort = acceptOnAddr (0,0,0,0)
257+-- >>> accept = TCP.acceptOnAddr (0,0,0,0)
229--
258--
230-- /Pre-release/
259-- /Pre-release/
231−{-# INLINE acceptOnPort #-}
260+{-# INLINE accept #-}
232−acceptOnPort :: MonadIO m => PortNumber -> Stream m Socket
261+accept :: MonadIO m => PortNumber -> Stream m Socket
233−acceptOnPort = acceptOnAddr (0,0,0,0)
262+accept = acceptOnAddr (0,0,0,0)
234
263
235-- | Like 'accept' but binds on the localhost IPv4 address @127.0.0.1@.
264-- | Like 'accept' but binds on the localhost IPv4 address @127.0.0.1@.
236-- The server can only be accessed from the local host, it cannot be accessed
265-- The server can only be accessed from the local host, it cannot be accessed
237-- from other hosts on the network.
266-- from other hosts on the network.
238--
267--
239−-- > acceptOnPortLocal = acceptOnAddr (127,0,0,1)
268+-- >>> acceptLocal = TCP.acceptOnAddr (127,0,0,1)
240--
269--
241-- /Pre-release/
270-- /Pre-release/
242−{-# INLINE acceptOnPortLocal #-}
271+{-# INLINE acceptLocal #-}
243−acceptOnPortLocal :: MonadIO m => PortNumber -> Stream m Socket
272+acceptLocal :: MonadIO m => PortNumber -> Stream m Socket
244−acceptOnPortLocal = acceptOnAddr (127,0,0,1)
273+acceptLocal = acceptOnAddr (127,0,0,1)
245
274
246-------------------------------------------------------------------------------
275-------------------------------------------------------------------------------
247-- TCP Clients
276-- TCP Clients
@@ -406,7 +435,7 @@ writeWithBufferOf
406 -> PortNumber
435 -> PortNumber
407 -> Fold m Word8 ()
436 -> Fold m Word8 ()
408writeWithBufferOf n addr port =
437writeWithBufferOf n addr port =
409− FL.groupsOf n (pinnedWriteNUnsafe n) (writeChunks addr port)
438+ FL.groupsOf n (A.unsafePinnedCreateOf n) (writeChunks addr port)
410
439
411-- | 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.
412--
441--
src/Streamly/Internal/Network/Socket.hs+17−14
@@ -11,7 +11,7 @@
11--
11--
12module Streamly.Internal.Network.Socket
12module Streamly.Internal.Network.Socket
13 (
13 (
14− SockSpec (..)
14+ SockSpec (..)
15 -- * Use a socket
15 -- * Use a socket
16 , forSocketM
16 , forSocketM
17 , withSocket
17 , withSocket
@@ -72,7 +72,7 @@ import Control.Monad.IO.Class (MonadIO(..))
72import Data.Maybe (isNothing, fromJust)
72import Data.Maybe (isNothing, fromJust)
73import Data.Word (Word8)
73import Data.Word (Word8)
74import Foreign.Ptr (plusPtr, Ptr, castPtr)
74import Foreign.Ptr (plusPtr, Ptr, castPtr)
75−import Streamly.Data.Serialize (Unbox)
75+import Streamly.Data.MutByteArray (Unbox)
76import Network.Socket
76import Network.Socket
77 (Socket, SocketOption(..), Family(..), SockAddr(..),
77 (Socket, SocketOption(..), Family(..), SockAddr(..),
78 ProtocolNumber, withSocketsDo, SocketType(..), socket, bind,
78 ProtocolNumber, withSocketsDo, SocketType(..), socket, bind,
@@ -87,7 +87,6 @@ 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)
91import Streamly.Data.Fold (Fold)
90import Streamly.Data.Fold (Fold)
92import Streamly.Data.Stream (Stream)
91import Streamly.Data.Stream (Stream)
93import Streamly.Internal.Data.Unfold (Unfold(..))
92import Streamly.Internal.Data.Unfold (Unfold(..))
@@ -99,12 +98,11 @@ import qualified Streamly.Data.Fold as FL
99import qualified Streamly.Data.Stream as S
98import qualified Streamly.Data.Stream as S
100import qualified Streamly.Data.Unfold as UF
99import qualified Streamly.Data.Unfold as UF
101import qualified Streamly.Internal.Data.Array as A
100import qualified Streamly.Internal.Data.Array as A
102− ( unsafeFreeze, asPtrUnsafe, byteLength, pinnedChunksOf,
101+ ( unsafeFreeze, unsafePinnedAsPtr, byteLength, pinnedChunksOf,
103− pinnedWriteN, pinnedWriteNUnsafe )
102+ pinnedCreateOf, unsafePinnedCreateOf, lCompactGE )
104import qualified Streamly.Internal.Data.MutArray as MArray
103import qualified Streamly.Internal.Data.MutArray as MArray
105− (MutArray(..), asPtrUnsafe, pinnedNewBytes)
104+ (MutArray(..), unsafePinnedAsPtr, pinnedEmptyOf)
106import qualified Streamly.Internal.Data.Stream as S (fromStreamK, Stream(..), Step(..))
105import qualified Streamly.Internal.Data.Stream as S (fromStreamK, Stream(..), Step(..))
107−import qualified Streamly.Internal.Data.Unfold as UF (first, map)
108import qualified Streamly.Internal.Data.StreamK as K (mkStream)
106import qualified Streamly.Internal.Data.StreamK as K (mkStream)
109
107
110-- $setup
108-- $setup
@@ -140,6 +138,9 @@ withSocket sk f = S.finallyIO (Net.close sk) (f sk)
140-- XXX Protocol specific socket options should be separated from socket level
138-- XXX Protocol specific socket options should be separated from socket level
141-- options.
139-- options.
142--
140--
141+-- NOTE: the socket config is specified as a record and not by composing
142+-- functions because all the fields are mandatory except the sockOpts field.
143+
143-- | Specify the socket protocol details.
144-- | Specify the socket protocol details.
144data SockSpec = SockSpec
145data SockSpec = SockSpec
145 {
146 {
@@ -183,7 +184,7 @@ listenTuples = Unfold step inject
183--
184--
184{-# INLINE acceptor #-}
185{-# INLINE acceptor #-}
185acceptor :: MonadIO m => Unfold m (Int, SockSpec, SockAddr) Socket
186acceptor :: MonadIO m => Unfold m (Int, SockSpec, SockAddr) Socket
186−acceptor = UF.map fst listenTuples
187+acceptor = fmap fst listenTuples
187
188
188{-# INLINE connectCommon #-}
189{-# INLINE connectCommon #-}
189connectCommon :: SockSpec -> Maybe SockAddr -> SockAddr -> IO Socket
190connectCommon :: SockSpec -> Maybe SockAddr -> SockAddr -> IO Socket
@@ -251,6 +252,8 @@ accept tcpListenQ spec addr =
251-- Array IO (Input)
252-- Array IO (Input)
252-------------------------------------------------------------------------------
253-------------------------------------------------------------------------------
253
254
255+-- XXX add an API that compacts the arrays to an exact size.
256+
254{-# INLINABLE readArrayUptoWith #-}
257{-# INLINABLE readArrayUptoWith #-}
255readArrayUptoWith
258readArrayUptoWith
256 :: (h -> Ptr Word8 -> Int -> IO Int)
259 :: (h -> Ptr Word8 -> Int -> IO Int)
@@ -258,9 +261,9 @@ readArrayUptoWith
258 -> h
261 -> h
259 -> IO (Array Word8)
262 -> IO (Array Word8)
260readArrayUptoWith f size h = do
263readArrayUptoWith f size h = do
261− arr <- MArray.pinnedNewBytes size
264+ arr <- MArray.pinnedEmptyOf size
262 -- ptr <- mallocPlainForeignPtrAlignedBytes size (alignment (undefined :: Word8))
265 -- ptr <- mallocPlainForeignPtrAlignedBytes size (alignment (undefined :: Word8))
263− MArray.asPtrUnsafe arr $ \p -> do
266+ MArray.unsafePinnedAsPtr arr $ \p -> do
264 n <- f h p size
267 n <- f h p size
265 let v = A.unsafeFreeze
268 let v = A.unsafeFreeze
266 $ arr { MArray.arrEnd = n, MArray.arrBound = size }
269 $ arr { MArray.arrEnd = n, MArray.arrBound = size }
@@ -308,7 +311,7 @@ writeArrayWith :: Unbox a
308 -> Array a
311 -> Array a
309 -> IO ()
312 -> IO ()
310writeArrayWith _ _ arr | A.length arr == 0 = return ()
313writeArrayWith _ _ arr | A.length arr == 0 = return ()
311−writeArrayWith f h arr = A.asPtrUnsafe arr $ \ptr -> f h (castPtr ptr) aLen
314+writeArrayWith f h arr = A.unsafePinnedAsPtr arr $ \ptr -> f h (castPtr ptr) aLen
312
315
313 where
316 where
314
317
@@ -471,7 +474,7 @@ writeChunks h = FL.drainMapM (liftIO . putChunk h)
471{-# INLINE writeChunksWith #-}
474{-# INLINE writeChunksWith #-}
472writeChunksWith :: (MonadIO m, Unbox a)
475writeChunksWith :: (MonadIO m, Unbox a)
473 => Int -> Socket -> Fold m (Array a) ()
476 => Int -> Socket -> Fold m (Array a) ()
474−writeChunksWith n h = lpackArraysChunksOf n (writeChunks h)
477+writeChunksWith n h = A.lCompactGE n (writeChunks h)
475
478
476-- | Same as 'writeChunksWith'
479-- | Same as 'writeChunksWith'
477--
480--
@@ -502,7 +505,7 @@ putBytesWith n h m = putChunks h $ A.pinnedChunksOf n m
502--
505--
503{-# INLINE writeWith #-}
506{-# INLINE writeWith #-}
504writeWith :: MonadIO m => Int -> Socket -> Fold m Word8 ()
507writeWith :: MonadIO m => Int -> Socket -> Fold m Word8 ()
505−writeWith n h = FL.groupsOf n (A.pinnedWriteNUnsafe n) (writeChunks h)
508+writeWith n h = FL.groupsOf n (A.unsafePinnedCreateOf n) (writeChunks h)
506
509
507-- | Same as 'writeWith'
510-- | Same as 'writeWith'
508--
511--
@@ -520,7 +523,7 @@ writeWithBufferOf = writeWith
520writeMaybesWith :: (MonadIO m )
523writeMaybesWith :: (MonadIO m )
521 => Int -> Socket -> Fold m (Maybe Word8) ()
524 => Int -> Socket -> Fold m (Maybe Word8) ()
522writeMaybesWith n h =
525writeMaybesWith n h =
523− let writeNJusts = FL.lmap fromJust $ A.pinnedWriteN n
526+ let writeNJusts = FL.lmap fromJust $ A.pinnedCreateOf n
524 writeOnNothing = FL.takeEndBy_ isNothing writeNJusts
527 writeOnNothing = FL.takeEndBy_ isNothing writeNJusts
525 in FL.many writeOnNothing (writeChunks h)
528 in FL.many writeOnNothing (writeChunks h)
526
529
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 st = ComposeJamo (Hangul c) st
230+ initHangul c = ComposeJamo (Hangul c)
231
231
232 {-# INLINE initJamo #-}
232 {-# INLINE initJamo #-}
233− initJamo c st = ComposeJamo (Jamo c) st
233+ initJamo c = ComposeJamo (Jamo c)
234
234
235 {-# INLINE initReg #-}
235 {-# INLINE initReg #-}
236− initReg !c st = ComposeReg 0 [c] st
236+ initReg !c = ComposeReg 0 [c]
237
237
238 {-# INLINE composeNone #-}
238 {-# INLINE composeNone #-}
239 composeNone ch st
239 composeNone ch st
src/Streamly/Network/Inet/TCP.hs+41−30
@@ -7,18 +7,52 @@
7-- Stability : released
7-- Stability : released
8-- Portability : GHC
8-- Portability : GHC
9--
9--
10−-- Combinators to build Inet/TCP clients and servers.
10+-- Combinators to build Inet\/IPv4/TCP clients and servers.
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+-- :}
14
42
15module Streamly.Network.Inet.TCP
43module Streamly.Network.Inet.TCP
16 (
44 (
17 -- * Accept Connections
45 -- * Accept Connections
46+ -- ** Streams
47+ accept
48+ , acceptLocal
49+ , acceptOnAddr
50+ , acceptOnAddrWith
51+
18 -- ** Unfolds
52 -- ** Unfolds
53+ , acceptor
54+ , acceptorLocal
19− acceptorOnAddr
55+ , acceptorOnAddr
20− , acceptorOnPort
21− , acceptorOnPortLocal
22
56
23 -- * Connect to Servers
57 -- * Connect to Servers
24 , connect
58 , connect
@@ -48,34 +82,11 @@ module Streamly.Network.Inet.TCP
48 , datagrams
82 , datagrams
49 , datagramsOn
83 , datagramsOn
50 -}
84 -}
85+
51 -- * Deprecated
86 -- * Deprecated
52− , acceptOnAddr
87+ , acceptorOnPort
53− , acceptOnPort
88+ , acceptorOnPortLocal
54− , acceptOnPortLocal
55 )
89 )
56where
90where
57
91
58−import Control.Monad.IO.Class (MonadIO(..))
59−import Data.Word (Word8)
60−import Network.Socket (Socket, PortNumber)
61−import Streamly.Internal.Data.Unfold (Unfold(..))
62−
63import Streamly.Internal.Network.Inet.TCP
92import Streamly.Internal.Network.Inet.TCP
64− hiding (acceptOnAddr, acceptOnPort, acceptOnPortLocal)
65−
66−{-# DEPRECATED acceptOnAddr "Please use 'acceptorOnAddr' instead" #-}
67−{-# INLINE acceptOnAddr #-}
68−acceptOnAddr
69− :: MonadIO m
70− => Unfold m ((Word8, Word8, Word8, Word8), PortNumber) Socket
71−acceptOnAddr = acceptorOnAddr
72−
73−{-# DEPRECATED acceptOnPort "Please use 'acceptorOnPort' instead" #-}
74−{-# INLINE acceptOnPort #-}
75−acceptOnPort :: MonadIO m => Unfold m PortNumber Socket
76−acceptOnPort = acceptorOnPort
77−
78−{-# DEPRECATED acceptOnPortLocal "Please use 'acceptorOnPortLocal' instead" #-}
79−{-# INLINE acceptOnPortLocal #-}
80−acceptOnPortLocal :: MonadIO m => Unfold m PortNumber Socket
81−acceptOnPortLocal = acceptorOnPortLocal
src/Streamly/Network/Socket.hs+16−30
@@ -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 operations please consult the @Network.Socket@
13+-- For basic socket types and non-streaming 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 'acceptor' unfold to start listening for
18+-- To write a server, use the 'accept' stream to start listening for
19−-- connections from clients. 'acceptor' generates a stream of connected
19+-- connections from clients. 'accept' 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,7 +33,6 @@
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
37-- >>> import qualified Streamly.Data.Stream.Prelude as Stream
36-- >>> import qualified Streamly.Data.Stream.Prelude as Stream
38-- >>> import qualified Streamly.Network.Socket as Socket
37-- >>> import qualified Streamly.Network.Socket as Socket
39-- >>>
38-- >>>
@@ -50,11 +49,11 @@
50-- in server spec addr
49-- in server spec addr
51-- where
50-- where
52-- server spec addr =
51-- server spec addr =
53−-- Stream.unfold Socket.acceptor (maxListenQueue, spec, addr)
52+-- Socket.accept maxListenQueue spec addr
54-- & Stream.parMapM (Stream.eager True) (Socket.forSocketM echo)
53-- & Stream.parMapM (Stream.eager True) (Socket.forSocketM echo)
55-- & Stream.fold Fold.drain
54-- & Stream.fold Fold.drain
56-- echo sk =
55-- echo sk =
57−-- Stream.unfold Socket.chunkReader sk -- Stream IO (Array Word8)
56+-- Socket.readChunks sk -- Stream IO (Array Word8)
58-- & Stream.fold (Socket.writeChunks sk) -- IO ()
57-- & Stream.fold (Socket.writeChunks sk) -- IO ()
59-- :}
58-- :}
60--
59--
@@ -95,12 +94,19 @@ module Streamly.Network.Socket
95 SockSpec(..)
94 SockSpec(..)
96
95
97 -- * Accept Connections
96 -- * Accept Connections
97+ , accept
98 , acceptor
98 , acceptor
99
99
100 -- * Reads
100 -- * Reads
101 -- ** Singleton
101 -- ** Singleton
102 , getChunk
102 , getChunk
103
103
104+ -- ** Streams
105+ , read
106+ , readWith
107+ , readChunks
108+ , readChunksWith
109+
104 -- ** Unfolds
110 -- ** Unfolds
105 , reader
111 , reader
106 , readerWith
112 , readerWith
@@ -121,33 +127,23 @@ module Streamly.Network.Socket
121 , forSocketM
127 , forSocketM
122
128
123 -- * Deprecated
129 -- * Deprecated
124− , accept
125 , readChunk
130 , readChunk
126 , writeChunk
131 , writeChunk
127− , read
128 , readWithBufferOf
132 , readWithBufferOf
129− , readChunks
130 , readChunksWithBufferOf
133 , readChunksWithBufferOf
131 , writeWithBufferOf
134 , writeWithBufferOf
132 , writeChunksWithBufferOf
135 , writeChunksWithBufferOf
133 )
136 )
134where
137where
135
138
136−import Control.Monad.IO.Class (MonadIO(..))
137import Data.Word (Word8)
139import Data.Word (Word8)
138−import Network.Socket (Socket, SockAddr)
140+import Network.Socket (Socket)
139−import Streamly.Internal.Data.Unfold (Unfold(..))
141+import Streamly.Data.Array (Array)
140−import Streamly.Internal.Data.Array (Array(..))
141−import Streamly.Data.Serialize (Unbox)
142+import Streamly.Data.MutByteArray (Unbox)
142
143
143−import Streamly.Internal.Network.Socket hiding (accept, read, readChunks)
144+import Streamly.Internal.Network.Socket
144import Prelude hiding (read)
145import Prelude hiding (read)
145
146
146−{-# DEPRECATED accept "Please use 'acceptor' instead" #-}
147−{-# INLINE accept #-}
148−accept :: MonadIO m => Unfold m (Int, SockSpec, SockAddr) Socket
149−accept = acceptor
150−
151{-# DEPRECATED readChunk "Please use 'getChunk' instead" #-}
147{-# DEPRECATED readChunk "Please use 'getChunk' instead" #-}
152{-# INLINABLE readChunk #-}
148{-# INLINABLE readChunk #-}
153readChunk :: Int -> Socket -> IO (Array Word8)
149readChunk :: Int -> Socket -> IO (Array Word8)
@@ -157,13 +153,3 @@ readChunk = getChunk
157{-# INLINABLE writeChunk #-}
153{-# INLINABLE writeChunk #-}
158writeChunk :: Unbox a => Socket -> Array a -> IO ()
154writeChunk :: Unbox a => Socket -> Array a -> IO ()
159writeChunk = putChunk
155writeChunk = putChunk
160−
161−{-# DEPRECATED read "Please use 'reader' instead" #-}
162−{-# INLINE read #-}
163−read :: MonadIO m => Unfold m Socket Word8
164−read = reader
165−
166−{-# DEPRECATED readChunks "Please use 'chunkReader' instead" #-}
167−{-# INLINE readChunks #-}
168−readChunks :: MonadIO m => Unfold m Socket (Array Word8)
169−readChunks = chunkReader
streamly.cabal+68−51
@@ -1,9 +1,14 @@
1cabal-version: 2.2
1cabal-version: 2.2
2name: streamly
2name: streamly
3−version: 0.10.0
3+version: 0.10.1
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
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+ .
9+ Streamly is a standard library for Haskell that focuses on C-like
10+ performance, modular combinators, and streaming data flow model.
6− Streamly comprises two packages, the
11+ Streamly consists of two packages, the
7 <https://hackage.haskell.org/package/streamly-core streamly-core> package
12 <https://hackage.haskell.org/package/streamly-core streamly-core> package
8 provides functionality that depends only on boot libraries, and
13 provides functionality that depends only on boot libraries, and
9 the <https://hackage.haskell.org/package/streamly streamly> package
14 the <https://hackage.haskell.org/package/streamly streamly> package
@@ -15,10 +20,10 @@ description:
15 high-performance, concurrent, scalable applications in Haskell. Stream
20 high-performance, concurrent, scalable applications in Haskell. Stream
16 fusion optimizations in streamly enable exceptional modularity with
21 fusion optimizations in streamly enable exceptional modularity with
17 high performance comparable to C. Streamly complements the Haskell
22 high performance comparable to C. Streamly complements the Haskell
18− <https://hackage.haskell.org/package/base base> package, supplying the
23+ <https://hackage.haskell.org/package/base base> package, supplying
19− missing functionality to quickly build general-purpose applications
24+ additional functionality to quickly build general-purpose applications
20 in Haskell. For high-level functionality built over streamly like
25 in Haskell. For high-level functionality built over streamly like
21− streaming system processes, shell programming, GNU coreutils,
26+ streaming OS processes, shell programming, GNU coreutils,
22 statistics, and compression libraries please see the
27 statistics, and compression libraries please see the
23 <https://streamly.composewell.com/ecosystem.html streamly ecosystem packages>.
28 <https://streamly.composewell.com/ecosystem.html streamly ecosystem packages>.
24 .
29 .
@@ -28,6 +33,7 @@ description:
28 * API close to standard Haskell lists (<https://github.com/composewell/streamly-examples Examples>)
33 * API close to standard Haskell lists (<https://github.com/composewell/streamly-examples Examples>)
29 * Declarative concurrency with automatic scaling
34 * Declarative concurrency with automatic scaling
30 * Filesystem, fsnotify, network, and Unicode support included
35 * Filesystem, fsnotify, network, and Unicode support included
36+ * Fast binary serialization (with optional JSON like features)
31 * More functionality is provided via many (<https://streamly.composewell.com/ecosystem.html ecosystem packages>)
37 * More functionality is provided via many (<https://streamly.composewell.com/ecosystem.html ecosystem packages>)
32 .
38 .
33 Unified and powerful abstractions:
39 Unified and powerful abstractions:
@@ -36,6 +42,7 @@ description:
36 * Unifies @Data.List@, @list-t@, and @logict@ with streaming
42 * Unifies @Data.List@, @list-t@, and @logict@ with streaming
37 * Unifies concurrency with standard streaming abstractions
43 * Unifies concurrency with standard streaming abstractions
38 * Unifies reactive, time-domain programming with streaming
44 * Unifies reactive, time-domain programming with streaming
45+ * Unifies binary serialization and unboxed arrays
39 * Interworks with other streaming libraries
46 * Interworks with other streaming libraries
40
47
41homepage: https://streamly.composewell.com
48homepage: https://streamly.composewell.com
@@ -167,9 +174,11 @@ extra-doc-files:
167 docs/User/Explanatory/*.md
174 docs/User/Explanatory/*.md
168 docs/User/Explanatory/streaming-pradigms.rst
175 docs/User/Explanatory/streaming-pradigms.rst
169 docs/User/Project/*.md
176 docs/User/Project/*.md
177+ docs/User/Project/Upgrading-0.8-to-0.9.md
170 docs/User/Project/*.link
178 docs/User/Project/*.link
171− docs/User/Project/ApiChangelogs/0.8.3-0.9.0.txt
172 docs/User/Project/ApiChangelogs/0.8.3.txt
179 docs/User/Project/ApiChangelogs/0.8.3.txt
180+ docs/User/Project/ApiChangelogs/0.8.3-0.9.0.txt
181+ docs/User/Project/ApiChangelogs/0.9.0-0.10.0.txt
173 test/README.md
182 test/README.md
174 docs/Developer/Tests.md
183 docs/Developer/Tests.md
175
184
@@ -285,8 +294,6 @@ common compile-options
285common default-extensions
294common default-extensions
286 default-extensions:
295 default-extensions:
287 BangPatterns
296 BangPatterns
288− CApiFFI
289− CPP
290 ConstraintKinds
297 ConstraintKinds
291 DeriveDataTypeable
298 DeriveDataTypeable
292 DeriveGeneric
299 DeriveGeneric
@@ -297,23 +304,25 @@ common default-extensions
297 GeneralizedNewtypeDeriving
304 GeneralizedNewtypeDeriving
298 InstanceSigs
305 InstanceSigs
299 KindSignatures
306 KindSignatures
300− LambdaCase
301− MagicHash
302 MultiParamTypeClasses
307 MultiParamTypeClasses
303− PatternSynonyms
304 RankNTypes
308 RankNTypes
305− RecordWildCards
306 ScopedTypeVariables
309 ScopedTypeVariables
307 TupleSections
310 TupleSections
308 TypeApplications
311 TypeApplications
309− TypeFamilies
310 TypeOperators
312 TypeOperators
311− ViewPatterns
312
313
314+ -- Not GHC2021
315+ CApiFFI
316+ CPP
317+ LambdaCase
318+ MagicHash
319+ RecordWildCards
320+
321+ -- TypeFamilies -- required by IsHashMap type class
313 -- MonoLocalBinds, enabled by TypeFamilies, causes performance
322 -- MonoLocalBinds, enabled by TypeFamilies, causes performance
314 -- regressions. Disable it. This must come after TypeFamilies,
323 -- regressions. Disable it. This must come after TypeFamilies,
315 -- otherwise TypeFamilies will enable it again.
324 -- otherwise TypeFamilies will enable it again.
316− NoMonoLocalBinds
325+ -- NoMonoLocalBinds
317
326
318 -- UndecidableInstances -- Does not show any perf impact
327 -- UndecidableInstances -- Does not show any perf impact
319 -- UnboxedTuples -- interferes with (#.)
328 -- UnboxedTuples -- interferes with (#.)
@@ -328,6 +337,7 @@ common optimization-options
328 -- For this to be effective it must come after the -O2 option
337 -- For this to be effective it must come after the -O2 option
329 if flag(dev) || flag(debug) || !flag(opt)
338 if flag(dev) || flag(debug) || !flag(opt)
330 ghc-options: -fno-ignore-asserts
339 ghc-options: -fno-ignore-asserts
340+ cpp-options: -DDEBUG
331
341
332common threading-options
342common threading-options
333 ghc-options: -threaded
343 ghc-options: -threaded
@@ -363,6 +373,7 @@ library
363 -- graphmod | dot -Tps > deps.ps
373 -- graphmod | dot -Tps > deps.ps
364 Streamly.Internal.Data.IsMap.HashMap
374 Streamly.Internal.Data.IsMap.HashMap
365 , Streamly.Internal.Data.Cont
375 , Streamly.Internal.Data.Cont
376+ , Streamly.Internal.Data.Channel
366 , Streamly.Internal.Data.Stream.MkType
377 , Streamly.Internal.Data.Stream.MkType
367
378
368 -- streamly-concurrent
379 -- streamly-concurrent
@@ -370,26 +381,10 @@ library
370 , Streamly.Internal.Control.ForkLifted
381 , Streamly.Internal.Control.ForkLifted
371 , Streamly.Internal.Data.Atomics
382 , Streamly.Internal.Data.Atomics
372 , Streamly.Internal.Data.IOFinalizer.Lifted
383 , Streamly.Internal.Data.IOFinalizer.Lifted
373− , Streamly.Internal.Data.Stream.Channel.Types
384+
374− , Streamly.Internal.Data.Stream.Channel.Dispatcher
385+ , Streamly.Internal.Data.Stream.Prelude
375− , Streamly.Internal.Data.Stream.Channel.Worker
376−
377− , Streamly.Internal.Data.Unfold.Exception
386+ , Streamly.Internal.Data.Unfold.Prelude
378− , Streamly.Internal.Data.Fold.Concurrent.Channel.Type
379− , Streamly.Internal.Data.Fold.Concurrent.Channel
380− , Streamly.Internal.Data.Fold.Concurrent
387+ , Streamly.Internal.Data.Fold.Prelude
381−
382− , Streamly.Internal.Data.Stream.Concurrent.Channel.Type
383− , Streamly.Internal.Data.Stream.Concurrent.Channel.Dispatcher
384− , Streamly.Internal.Data.Stream.Concurrent.Channel.Consumer
385− , Streamly.Internal.Data.Stream.Concurrent.Channel.Append
386− , Streamly.Internal.Data.Stream.Concurrent.Channel.Interleave
387− , Streamly.Internal.Data.Stream.Concurrent.Channel.Operations
388− , Streamly.Internal.Data.Stream.Concurrent.Channel
389− , Streamly.Internal.Data.Stream.Concurrent
390− , Streamly.Internal.Data.Stream.Zip.Concurrent
391− , Streamly.Internal.Data.Stream.Time
392− , Streamly.Internal.Data.Stream.Exception.Lifted
393
388
394 -- streamly-unicode (depends on unicode-data)
389 -- streamly-unicode (depends on unicode-data)
395 , Streamly.Internal.Unicode.Utf8
390 , Streamly.Internal.Unicode.Utf8
@@ -415,12 +410,7 @@ library
415
410
416 -- Deprecated Internal modules
411 -- Deprecated Internal modules
417 , Streamly.Internal.Data.SVar
412 , Streamly.Internal.Data.SVar
418−
419− , Streamly.Internal.Data.Unfold.SVar
413+ , Streamly.Internal.Data.Stream.SVar
420− , Streamly.Internal.Data.Fold.SVar
421− , Streamly.Internal.Data.Fold.Async
422− , Streamly.Internal.Data.Stream.SVar.Generate
423− , Streamly.Internal.Data.Stream.SVar.Eliminate
424
414
425 , Streamly.Internal.Data.Stream.Serial
415 , Streamly.Internal.Data.Stream.Serial
426 , Streamly.Internal.Data.Stream.Zip
416 , Streamly.Internal.Data.Stream.Zip
@@ -430,9 +420,6 @@ library
430 , Streamly.Internal.Data.Stream.ZipAsync
420 , Streamly.Internal.Data.Stream.ZipAsync
431
421
432 , Streamly.Internal.Data.Stream.IsStream
422 , Streamly.Internal.Data.Stream.IsStream
433− , Streamly.Internal.Data.Stream.IsStream.Combinators
434− , Streamly.Internal.Data.Stream.IsStream.Common
435− , Streamly.Internal.Data.Stream.IsStream.Enumeration
436
423
437 if !impl(ghcjs) && flag(dev)
424 if !impl(ghcjs) && flag(dev)
438 other-modules:
425 other-modules:
@@ -442,9 +429,11 @@ library
442 , Streamly.Internal.FileSystem.FD
429 , Streamly.Internal.FileSystem.FD
443
430
444 if flag(dev)
431 if flag(dev)
432+ exposed-modules: Streamly.Internal.Data.SmallArray
433+ -- Exposed modules show up on hackage irrespective of the flag, so keep
434+ -- it hidden.
445 other-modules: Streamly.Data.SmallArray
435 other-modules: Streamly.Data.SmallArray
446− , Streamly.Internal.Data.SmallArray
447− , Streamly.Internal.Data.SmallArray.Type
436+ , Streamly.Internal.Data.SmallArray.Type
448
437
449 if os(windows)
438 if os(windows)
450 exposed-modules: Streamly.Internal.FileSystem.Event.Windows
439 exposed-modules: Streamly.Internal.FileSystem.Event.Windows
@@ -461,10 +450,30 @@ library
461 exposed-modules: Streamly.Internal.FileSystem.Event
450 exposed-modules: Streamly.Internal.FileSystem.Event
462
451
463 other-modules:
452 other-modules:
464− Streamly.Data.Stream.Exception
453+ Streamly.Internal.Data.Channel.Types
454+ , Streamly.Internal.Data.Channel.Dispatcher
455+ , Streamly.Internal.Data.Channel.Worker
456+
457+ , Streamly.Internal.Data.Stream.Concurrent.Channel.Type
458+ , Streamly.Internal.Data.Stream.Concurrent.Channel.Dispatcher
459+ , Streamly.Internal.Data.Stream.Concurrent.Channel.Consumer
460+ , Streamly.Internal.Data.Stream.Concurrent.Channel.Append
461+ , Streamly.Internal.Data.Stream.Concurrent.Channel.Interleave
462+ , Streamly.Internal.Data.Stream.Concurrent.Channel.Operations
463+ , Streamly.Internal.Data.Stream.Concurrent.Channel
464+ , Streamly.Internal.Data.Stream.Concurrent
465+ , Streamly.Internal.Data.Stream.Time
466+ , Streamly.Internal.Data.Stream.Lifted
467+
468+ , Streamly.Internal.Data.Fold.Async
469+ , Streamly.Internal.Data.Fold.Concurrent.Channel.Type
470+ , Streamly.Internal.Data.Fold.Concurrent.Channel
471+ , Streamly.Internal.Data.Fold.Concurrent
472+
473+ , Streamly.Internal.Data.Unfold.Exception
465− , Streamly.Data.Stream.Concurrent
474+ , Streamly.Internal.Data.Unfold.SVar
466− , Streamly.Data.Stream.Time
475+
467−
476+ -- Deprecated
468 , Streamly.Internal.Data.Stream.Common
477 , Streamly.Internal.Data.Stream.Common
469 , Streamly.Internal.Data.Stream.IsStream.Type
478 , Streamly.Internal.Data.Stream.IsStream.Type
470 , Streamly.Internal.Data.Stream.IsStream.Generate
479 , Streamly.Internal.Data.Stream.IsStream.Generate
@@ -475,6 +484,14 @@ library
475 , Streamly.Internal.Data.Stream.IsStream.Exception
484 , Streamly.Internal.Data.Stream.IsStream.Exception
476 , Streamly.Internal.Data.Stream.IsStream.Lift
485 , Streamly.Internal.Data.Stream.IsStream.Lift
477 , Streamly.Internal.Data.Stream.IsStream.Top
486 , Streamly.Internal.Data.Stream.IsStream.Top
487+ , Streamly.Internal.Data.Stream.IsStream.Combinators
488+ , Streamly.Internal.Data.Stream.IsStream.Common
489+ , Streamly.Internal.Data.Stream.IsStream.Enumeration
490+
491+ , Streamly.Internal.Data.Stream.SVar.Generate
492+ , Streamly.Internal.Data.Stream.SVar.Eliminate
493+
494+ , Streamly.Internal.Data.Fold.SVar
478
495
479 , Streamly.Internal.Data.SVar.Worker
496 , Streamly.Internal.Data.SVar.Worker
480 , Streamly.Internal.Data.SVar.Dispatch
497 , Streamly.Internal.Data.SVar.Dispatch
@@ -496,7 +513,7 @@ library
496 , template-haskell >= 2.14 && < 2.22
513 , template-haskell >= 2.14 && < 2.22
497
514
498 -- The core streamly package
515 -- The core streamly package
499− , streamly-core == 0.2.0
516+ , streamly-core == 0.2.2
500
517
501 , hashable >= 1.3 && < 1.5
518 , hashable >= 1.3 && < 1.5
502 , unordered-containers >= 0.2 && < 0.3
519 , unordered-containers >= 0.2 && < 0.3
targets/Targets.hs+4−1
@@ -187,10 +187,13 @@ targets =
187 , ("Data.Fold.Window", [ "parser_grp", "infinite_grp" ])
187 , ("Data.Fold.Window", [ "parser_grp", "infinite_grp" ])
188 , ("Data.Parser", [ "parser_grp", "infinite_grp" ])
188 , ("Data.Parser", [ "parser_grp", "infinite_grp" ])
189
189
190− , ("Data.Unbox", [])
190+ , ("Data.Unbox", ["noTest"])
191+ , ("Data.Unbox.TH", ["noBench"])
192+ , ("Data.Unbox.Derive.Generic", ["noBench"])
191 , ("Data.Unbox.Derive.TH", [])
193 , ("Data.Unbox.Derive.TH", [])
192 , ("Data.Serialize", [])
194 , ("Data.Serialize", [])
193 , ("Data.Serialize.Derive.TH", ["noBench"])
195 , ("Data.Serialize.Derive.TH", ["noBench"])
196+ , ("Data.Serialize.ENABLE_constructorTagAsString", ["noBench"])
194 , ("Data.Unfold", ["infinite_grp"])
197 , ("Data.Unfold", ["infinite_grp"])
195 , ("FileSystem.Handle", [])
198 , ("FileSystem.Handle", [])
196 , ("Unicode.Stream", [])
199 , ("Unicode.Stream", [])
test/Streamly/Test/Data/Array.hs+3−3
@@ -14,7 +14,7 @@ import Data.Proxy (Proxy(..))
14import Data.Word(Word8)
14import Data.Word(Word8)
15import Foreign.Storable (peek)
15import Foreign.Storable (peek)
16import GHC.Ptr (plusPtr)
16import GHC.Ptr (plusPtr)
17−import Streamly.Internal.Data.Serialize (Unbox, sizeOf)
17+import Streamly.Internal.Data.MutByteArray (Unbox, sizeOf)
18import Streamly.Internal.Data.MutArray (MutArray)
18import Streamly.Internal.Data.MutArray (MutArray)
19import Test.QuickCheck (chooseInt, listOf)
19import Test.QuickCheck (chooseInt, listOf)
20
20
@@ -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.asPtrUnsafe arr (getList (0 :: Int)) `shouldReturn` [0 .. 99]
188+ MA.unsafePinnedAsPtr 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.writeNUnsafe n)))
232+ (foldManyWith (\n -> Fold.take n (A.unsafeCreateOf 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+7−6
@@ -2,7 +2,8 @@
2
2
3module Main (main) where
3module Main (main) where
4
4
5−import Data.List (sort)
5+import Data.List (sort, sortBy)
6+import Data.Ord (comparing, Down(..))
6import Data.Semigroup (Sum(..), getSum)
7import Data.Semigroup (Sum(..), getSum)
7import Streamly.Test.Common (checkListEqual, listEquals)
8import Streamly.Test.Common (checkListEqual, listEquals)
8import Test.QuickCheck
9import Test.QuickCheck
@@ -412,7 +413,7 @@ partitionByM =
412 where
413 where
413
414
414 action ls = do
415 action ls = do
415− let f = \x -> if odd x then return (Left x) else return (Right x)
416+ let f x = if odd x then return (Left x) else return (Right x)
416 v1 <-
417 v1 <-
417 run
418 run
418 $ Stream.fold (Fold.partitionByM f Fold.length Fold.length)
419 $ Stream.fold (Fold.partitionByM f Fold.length Fold.length)
@@ -429,7 +430,7 @@ partitionByFstM =
429 where
430 where
430
431
431 action _ = do
432 action _ = do
432− let f = \x -> if odd x then return (Left x) else return (Right x)
433+ let f x = if odd x then return (Left x) else return (Right x)
433 v1 <-
434 v1 <-
434 run
435 run
435 $ Stream.fold
436 $ Stream.fold
@@ -447,7 +448,7 @@ partitionByMinM1 =
447 where
448 where
448
449
449 action _ = do
450 action _ = do
450− let f = \x -> if odd x then return (Left x) else return (Right x)
451+ let f x = if odd x then return (Left x) else return (Right x)
451 v1 <-
452 v1 <-
452 run
453 run
453 $ Stream.fold
454 $ Stream.fold
@@ -465,7 +466,7 @@ partitionByMinM2 =
465 where
466 where
466
467
467 action _ = do
468 action _ = do
468− let f = \x -> if odd x then return (Left x) else return (Right x)
469+ let f x = if odd x then return (Left x) else return (Right x)
469 v1 <-
470 v1 <-
470 run
471 run
471 $ Stream.fold
472 $ Stream.fold
@@ -690,7 +691,7 @@ topBy isTop = forAll (listOf (chooseInt (-50, 100))) $ \ls0 ->
690 if isTop
691 if isTop
691 then do
692 then do
692 lst <- Stream.fold (Fold.top n) (Stream.fromList ls) >>= MArray.toList
693 lst <- Stream.fold (Fold.top n) (Stream.fromList ls) >>= MArray.toList
693− assert ((Prelude.take n . Prelude.reverse . sort) ls == lst)
694+ assert ((Prelude.take n . sortBy (comparing Down)) ls == lst)
694 else do
695 else do
695 lst <- Stream.fold (Fold.bottom n) (Stream.fromList ls) >>= MArray.toList
696 lst <- Stream.fold (Fold.bottom n) (Stream.fromList ls) >>= MArray.toList
696 assert ((Prelude.take n . sort) ls == lst)
697 assert ((Prelude.take n . sort) ls == lst)
test/Streamly/Test/Data/MutArray.hs+1−1
@@ -13,7 +13,7 @@ import GHC.Int (Int16(..), Int32(..), Int64(..), Int8(..))
13import GHC.Real (Ratio(..))
13import GHC.Real (Ratio(..))
14import GHC.Stable (StablePtr(..))
14import GHC.Stable (StablePtr(..))
15import GHC.Word (Word16(..), Word32(..), Word64(..), Word8(..))
15import GHC.Word (Word16(..), Word32(..), Word64(..), Word8(..))
16−import Streamly.Internal.Data.Serialize (Unbox)
16+import Streamly.Internal.Data.MutByteArray (Unbox)
17import Streamly.Test.Common (chooseInt)
17import Streamly.Test.Common (chooseInt)
18import Test.Hspec (hspec, describe, it)
18import Test.Hspec (hspec, describe, it)
19import Test.Hspec.QuickCheck
19import Test.Hspec.QuickCheck
test/Streamly/Test/Data/Parser.hs+6−4
@@ -198,8 +198,8 @@ takeBetweenPass =
198 $ Prelude.take lpl ls
198 $ Prelude.take lpl ls
199 Left _ -> property False
199 Left _ -> property False
200
200
201−takeBetween :: Property
201+_takeBetween :: Property
202−takeBetween =
202+_takeBetween =
203 forAll (chooseInt (min_value, max_value)) $ \m ->
203 forAll (chooseInt (min_value, max_value)) $ \m ->
204 forAll (chooseInt (min_value, max_value)) $ \n ->
204 forAll (chooseInt (min_value, max_value)) $ \n ->
205 forAll (listOf (chooseInt (min_value, max_value))) $ \ls ->
205 forAll (listOf (chooseInt (min_value, max_value))) $ \ls ->
@@ -1318,8 +1318,10 @@ main =
1318 describe "test for sequence parser" $ do
1318 describe "test for sequence parser" $ do
1319 prop "P.takeBetween = Prelude.take when len >= m and len <= n"
1319 prop "P.takeBetween = Prelude.take when len >= m and len <= n"
1320 takeBetweenPass
1320 takeBetweenPass
1321+ -- XXX This test fails
1322+ -- XXX cabal run test:Data.Parser -- --match "/Data.Parser/test for sequence parser/P.takeBetween = Prelude.take when len >= m and len <= n and failotherwise fail/" --seed 1563586298
1321− prop ("P.takeBetween = Prelude.take when len >= m and len <= n and fail"
1323+ -- prop ("P.takeBetween = Prelude.take when len >= m and len <= n and fail"
1322− ++ "otherwise fail") Main.takeBetween
1324+ -- ++ "otherwise fail") Main._takeBetween
1323 prop "P.take = Prelude.take" Main.take
1325 prop "P.take = Prelude.take" Main.take
1324 prop "P.takeEQ = Prelude.take when len >= n" takeEQPass
1326 prop "P.takeEQ = Prelude.take when len >= n" takeEQPass
1325 prop "P.takeEQ = Prelude.take when len >= n and fail otherwise"
1327 prop "P.takeEQ = Prelude.take when len >= n and fail otherwise"
test/Streamly/Test/Data/Serialize.hs+45−14
@@ -21,16 +21,18 @@ 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)
24import System.Random (randomRIO)
26import System.Random (randomRIO)
25−import Streamly.Internal.Data.Serialize (MutByteArray, newByteArray)
27+import Streamly.Internal.Data.MutByteArray (MutByteArray)
26import GHC.Generics (Generic)
28import GHC.Generics (Generic)
27−import Streamly.Data.Serialize (Serialize)
29+import Streamly.Data.MutByteArray (Serialize)
28import Streamly.Test.Data.Serialize.TH (genDatatype)
30import Streamly.Test.Data.Serialize.TH (genDatatype)
29
31
30import Data.Functor.Identity (Identity (..))
32import Data.Functor.Identity (Identity (..))
31
33
32import qualified Streamly.Internal.Data.Array as Array
34import qualified Streamly.Internal.Data.Array as Array
33−import qualified Streamly.Internal.Data.Serialize as Serialize
35+import qualified Streamly.Internal.Data.MutByteArray as Serialize
34
36
35import qualified Streamly.Test.Data.Serialize.CompatV0 as CompatV0
37import qualified Streamly.Test.Data.Serialize.CompatV0 as CompatV0
36import qualified Streamly.Test.Data.Serialize.CompatV1 as CompatV1
38import qualified Streamly.Test.Data.Serialize.CompatV1 as CompatV1
@@ -45,10 +47,10 @@ import Test.Hspec as H
45
47
46#ifdef ENABLE_constructorTagAsString
48#ifdef ENABLE_constructorTagAsString
47#define CONF_NAME "ENABLE_constructorTagAsString"
49#define CONF_NAME "ENABLE_constructorTagAsString"
48−#define CONF (Serialize.encodeConstrNames True Serialize.serializeConfig)
50+#define CONF (Serialize.encodeConstrNames True)
49#else
51#else
50#define CONF_NAME "DEFAULT"
52#define CONF_NAME "DEFAULT"
51−#define CONF Serialize.serializeConfig
53+#define CONF id
52#endif
54#endif
53
55
54--------------------------------------------------------------------------------
56--------------------------------------------------------------------------------
@@ -107,9 +109,19 @@ $(Serialize.deriveSerializeWith
107 CONF
109 CONF
108 [d|instance Serialize a => Serialize (BinTree a)|])
110 [d|instance Serialize a => Serialize (BinTree a)|])
109
111
112+-- XXX This may not terminate, or could become really large.
110instance Arbitrary a => Arbitrary (BinTree a) where
113instance Arbitrary a => Arbitrary (BinTree a) where
111 arbitrary = oneof [Leaf <$> arbitrary, Tree <$> arbitrary <*> arbitrary]
114 arbitrary = oneof [Leaf <$> arbitrary, Tree <$> arbitrary <*> arbitrary]
112
115
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+
113--------------------------------------------------------------------------------
125--------------------------------------------------------------------------------
114-- Record syntax type
126-- Record syntax type
115--------------------------------------------------------------------------------
127--------------------------------------------------------------------------------
@@ -160,7 +172,7 @@ poke ::
160 => a
172 => a
161 -> IO (MutByteArray, Int, Int)
173 -> IO (MutByteArray, Int, Int)
162poke val = do
174poke val = do
163− let sz = Serialize.size 0 val
175+ let sz = Serialize.addSizeTo 0 val
164
176
165 let excessSize = 100
177 let excessSize = 100
166 randomOff <- randomRIO (10, excessSize)
178 randomOff <- randomRIO (10, excessSize)
@@ -169,9 +181,19 @@ poke val = do
169 let arrSize = sz + excessSize
181 let arrSize = sz + excessSize
170 serStartOff = randomOff
182 serStartOff = randomOff
171 serEndOff = randomOff + sz
183 serEndOff = randomOff + sz
172− arr <- newByteArray arrSize
184+ 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+
189+ 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
173
196
174− off1 <- Serialize.serialize serStartOff arr val
175 off1 `shouldBe` serEndOff
197 off1 `shouldBe` serEndOff
176 pure (arr, serStartOff, serEndOff)
198 pure (arr, serStartOff, serEndOff)
177
199
@@ -181,13 +203,13 @@ peekAndVerify ::
181 -> a
203 -> a
182 -> IO ()
204 -> IO ()
183peekAndVerify (arr, serStartOff, serEndOff) val = do
205peekAndVerify (arr, serStartOff, serEndOff) val = do
184− (off2, val2) <- Serialize.deserialize serStartOff arr serEndOff
206+ (off2, val2) <- Serialize.deserializeAt serStartOff arr serEndOff
185 val2 `shouldBe` val
207 val2 `shouldBe` val
186 off2 `shouldBe` serEndOff
208 off2 `shouldBe` serEndOff
187 let slice = Array.Array arr serStartOff serEndOff
209 let slice = Array.Array arr serStartOff serEndOff
188− val `shouldBe` Serialize.decode slice
210+ val `shouldBe` Array.deserialize slice
189 clonedSlice <- Array.clone slice
211 clonedSlice <- Array.clone slice
190− val `shouldBe` Serialize.decode clonedSlice
212+ val `shouldBe` Array.deserialize clonedSlice
191
213
192roundtrip
214roundtrip
193 :: forall a. (Eq a, Show a, Serialize.Serialize a)
215 :: forall a. (Eq a, Show a, Serialize.Serialize a)
@@ -195,7 +217,11 @@ roundtrip
195 -> IO ()
217 -> IO ()
196roundtrip val = do
218roundtrip val = do
197
219
198− val `shouldBe` Serialize.decode (Serialize.encode val)
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)
199
225
200 res <- poke val
226 res <- poke val
201 peekAndVerify res val
227 peekAndVerify res val
@@ -207,7 +233,7 @@ testSerializeList
207 -> IO ()
233 -> IO ()
208testSerializeList sizeOfA val = do
234testSerializeList sizeOfA val = do
209
235
210− let sz = Serialize.size 0 val
236+ let sz = Serialize.addSizeTo 0 val
211
237
212 sz `shouldBe` sizeOfA
238 sz `shouldBe` sizeOfA
213
239
@@ -258,7 +284,12 @@ testCases = do
258 $ \(x :: [CustomDatatype]) -> roundtrip x
284 $ \(x :: [CustomDatatype]) -> roundtrip x
259
285
260 limitQC
286 limitQC
261− $ prop "BinTree" $ \(x :: BinTree Int) -> roundtrip x
287+ $ prop "BinTree"
288+ $ forAll (elements [1..15])
289+ (\(x :: Int) -> do
290+ (r :: BinTree Int) <- mkBinTree x
291+ roundtrip r
292+ )
262
293
263 where
294 where
264
295
test/Streamly/Test/Data/Serialize/CompatV0.hs+4−4
@@ -10,8 +10,8 @@ module Streamly.Test.Data.Serialize.CompatV0
10--------------------------------------------------------------------------------
10--------------------------------------------------------------------------------
11
11
12import Test.QuickCheck (Arbitrary, arbitrary, elements)
12import Test.QuickCheck (Arbitrary, arbitrary, elements)
13−import Streamly.Internal.Data.Serialize (Serialize)
13+import Streamly.Internal.Data.MutByteArray (Serialize)
14−import qualified Streamly.Internal.Data.Serialize as Serialize
14+import qualified Streamly.Internal.Data.MutByteArray as Serialize
15
15
16--------------------------------------------------------------------------------
16--------------------------------------------------------------------------------
17-- Types
17-- Types
@@ -29,7 +29,7 @@ instance Arbitrary a => Arbitrary (Rec a) where
29 arbitrary = Rec <$> arbitrary <*> arbitrary <*> arbitrary
29 arbitrary = Rec <$> arbitrary <*> arbitrary <*> arbitrary
30
30
31$(Serialize.deriveSerializeWith
31$(Serialize.deriveSerializeWith
32− (Serialize.encodeRecordFields True Serialize.serializeConfig)
32+ (Serialize.encodeRecordFields True)
33 [d|instance Serialize a => Serialize (Rec a)|])
33 [d|instance Serialize a => Serialize (Rec a)|])
34
34
35data River
35data River
@@ -42,5 +42,5 @@ instance Arbitrary River where
42 arbitrary = elements [Ganga, Yamuna, Godavari]
42 arbitrary = elements [Ganga, Yamuna, Godavari]
43
43
44$(Serialize.deriveSerializeWith
44$(Serialize.deriveSerializeWith
45− (Serialize.encodeConstrNames True Serialize.serializeConfig)
45+ (Serialize.encodeConstrNames True)
46 [d|instance Serialize River|])
46 [d|instance Serialize River|])
test/Streamly/Test/Data/Serialize/CompatV1.hs+4−4
@@ -10,8 +10,8 @@ module Streamly.Test.Data.Serialize.CompatV1
10--------------------------------------------------------------------------------
10--------------------------------------------------------------------------------
11
11
12import Test.QuickCheck (Arbitrary, arbitrary, elements)
12import Test.QuickCheck (Arbitrary, arbitrary, elements)
13−import Streamly.Internal.Data.Serialize (Serialize)
13+import Streamly.Internal.Data.MutByteArray (Serialize)
14−import qualified Streamly.Internal.Data.Serialize as Serialize
14+import qualified Streamly.Internal.Data.MutByteArray as Serialize
15
15
16--------------------------------------------------------------------------------
16--------------------------------------------------------------------------------
17-- Types
17-- Types
@@ -30,7 +30,7 @@ instance Arbitrary a => Arbitrary (Rec a) where
30 arbitrary = Rec <$> arbitrary <*> arbitrary <*> arbitrary <*> arbitrary
30 arbitrary = Rec <$> arbitrary <*> arbitrary <*> arbitrary <*> arbitrary
31
31
32$(Serialize.deriveSerializeWith
32$(Serialize.deriveSerializeWith
33− (Serialize.encodeRecordFields True Serialize.serializeConfig)
33+ (Serialize.encodeRecordFields True)
34 [d|instance Serialize a => Serialize (Rec a)|])
34 [d|instance Serialize a => Serialize (Rec a)|])
35
35
36data River
36data River
@@ -44,5 +44,5 @@ instance Arbitrary River where
44 arbitrary = elements [Ganga, Yamuna, Godavari]
44 arbitrary = elements [Ganga, Yamuna, Godavari]
45
45
46$(Serialize.deriveSerializeWith
46$(Serialize.deriveSerializeWith
47− (Serialize.encodeConstrNames True Serialize.serializeConfig)
47+ (Serialize.encodeConstrNames True)
48 [d|instance Serialize River|])
48 [d|instance Serialize River|])
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/Stream/Concurrent.hs+1−1
@@ -26,7 +26,7 @@ import Test.Hspec as H
26import qualified Streamly.Data.Fold as Fold ( toList )
26import qualified Streamly.Data.Fold as Fold ( toList )
27import qualified Streamly.Data.Stream as Stream
27import qualified Streamly.Data.Stream as Stream
28 ( replicate, fromEffect, fromPure, fromList, fold, take, nil )
28 ( replicate, fromEffect, fromPure, fromList, fold, take, nil )
29−import qualified Streamly.Internal.Data.Stream.Concurrent as Async
29+import qualified Streamly.Internal.Data.Stream.Prelude as Async
30
30
31import Streamly.Test.Common (listEquals)
31import Streamly.Test.Common (listEquals)
32
32
test/Streamly/Test/Data/Unbox.hs+34−14
@@ -20,6 +20,12 @@ 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+
23import Data.Complex (Complex ((:+)))
29import Data.Complex (Complex ((:+)))
24import Data.Functor.Const (Const (..))
30import Data.Functor.Const (Const (..))
25import Data.Functor.Identity (Identity (..))
31import Data.Functor.Identity (Identity (..))
@@ -27,7 +33,8 @@ import Data.Proxy (Proxy(..))
27import GHC.Generics (Generic, Rep(..))
33import GHC.Generics (Generic, Rep(..))
28import GHC.Real (Ratio(..))
34import GHC.Real (Ratio(..))
29
35
30−import Streamly.Internal.Data.Serialize
36+import Streamly.Internal.Data.MutByteArray
37+import qualified Streamly.Internal.Data.MutByteArray as MBA
31
38
32import Test.Hspec as H
39import Test.Hspec as H
33
40
@@ -39,14 +46,14 @@ import Test.Hspec as H
39
46
40#define MODULE_NAME "Data.Serialize.Deriving.TH"
47#define MODULE_NAME "Data.Serialize.Deriving.TH"
41#define DERIVE_UNBOX(typ) $(deriveSerialize [d|instance Serialize typ|])
48#define DERIVE_UNBOX(typ) $(deriveSerialize [d|instance Serialize typ|])
42−#define PEEK(i, arr, sz) (deserialize i arr sz)
49+#define PEEK(i, arr, sz) (deserializeAt i arr sz)
43−#define POKE(i, arr, val) (serialize i arr val)
50+#define POKE(i, arr, val) (serializeAt i arr val)
44#define TYPE_CLASS Serialize
51#define TYPE_CLASS Serialize
45
52
46#else
53#else
47
54
48−#define PEEK(i, arr, sz) peekByteIndexWithNextOff i arr
55+#define PEEK(i, arr, sz) peekAtWithNextOff i arr
49−#define POKE(i, arr, val) pokeByteIndexWithNextOff i arr val
56+#define POKE(i, arr, val) pokeAtWithNextOff i arr val
50#define TYPE_CLASS Unbox
57#define TYPE_CLASS Unbox
51
58
52#ifdef USE_TH
59#ifdef USE_TH
@@ -69,23 +76,23 @@ import Test.Hspec as H
69
76
70#ifndef USE_SERIALIZE
77#ifndef USE_SERIALIZE
71
78
72−peekByteIndexWithNextOff ::
79+peekAtWithNextOff ::
73 forall a. Unbox a
80 forall a. Unbox a
74 => Int
81 => Int
75 -> MutByteArray
82 -> MutByteArray
76 -> IO (Int, a)
83 -> IO (Int, a)
77−peekByteIndexWithNextOff i arr = do
84+peekAtWithNextOff i arr = do
78− val <- peekByteIndex i arr
85+ val <- peekAt i arr
79 pure (i + sizeOf (Proxy :: Proxy a), val)
86 pure (i + sizeOf (Proxy :: Proxy a), val)
80
87
81−pokeByteIndexWithNextOff ::
88+pokeAtWithNextOff ::
82 forall a. Unbox a
89 forall a. Unbox a
83 => Int
90 => Int
84 -> MutByteArray
91 -> MutByteArray
85 -> a
92 -> a
86 -> IO Int
93 -> IO Int
87−pokeByteIndexWithNextOff i arr val = do
94+pokeAtWithNextOff i arr val = do
88− pokeByteIndex i arr val
95+ pokeAt i arr val
89 pure $ i + sizeOf (Proxy :: Proxy a)
96 pure $ i + sizeOf (Proxy :: Proxy a)
90
97
91#endif
98#endif
@@ -184,7 +191,7 @@ variableSizeOf ::
184 forall a. Serialize a
191 forall a. Serialize a
185 => a
192 => a
186 -> Int
193 -> Int
187−variableSizeOf = size 0
194+variableSizeOf = addSizeTo 0
188#endif
195#endif
189
196
190testSerialization ::
197testSerialization ::
@@ -198,8 +205,21 @@ testSerialization val = do
198#else
205#else
199 (sizeOf (Proxy :: Proxy a))
206 (sizeOf (Proxy :: Proxy a))
200#endif
207#endif
201− arr <- newByteArray len
208+ arr <- MBA.new len
202 nextOff <- POKE(0, arr, val)
209 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
203 (nextOff1, val1) <- PEEK(0, arr, len)
223 (nextOff1, val1) <- PEEK(0, arr, len)
204 val1 `shouldBe` val
224 val1 `shouldBe` val
205 nextOff1 `shouldBe` len
225 nextOff1 `shouldBe` len
@@ -232,7 +252,7 @@ testGenericConsistency val = do
232 len `shouldBe` genericSizeOf (Proxy :: Proxy a)
252 len `shouldBe` genericSizeOf (Proxy :: Proxy a)
233
253
234 -- Test the serialization and deserialization
254 -- Test the serialization and deserialization
235− arr <- newByteArray (sizeOf (Proxy :: Proxy a))
255+ arr <- MBA.new (sizeOf (Proxy :: Proxy a))
236
256
237 nextOff <- POKE(0, arr, val)
257 nextOff <- POKE(0, arr, val)
238 genericPeekByteIndex arr 0 `shouldReturn` val
258 genericPeekByteIndex arr 0 `shouldReturn` val
test/Streamly/Test/Data/Unbox/TH.hs+5−4
@@ -15,7 +15,8 @@ module Streamly.Test.Data.Unbox.TH (main) where
15--------------------------------------------------------------------------------
15--------------------------------------------------------------------------------
16
16
17import Data.Proxy (Proxy(..))
17import Data.Proxy (Proxy(..))
18−import Streamly.Internal.Data.Serialize
18+import Streamly.Internal.Data.MutByteArray
19+import qualified Streamly.Internal.Data.MutByteArray as MBA
19
20
20import Test.Hspec as H
21import Test.Hspec as H
21
22
@@ -28,9 +29,9 @@ testSerialization ::
28 => a
29 => a
29 -> IO ()
30 -> IO ()
30testSerialization val = do
31testSerialization val = do
31− arr <- newByteArray (sizeOf (Proxy :: Proxy a))
32+ arr <- MBA.new (sizeOf (Proxy :: Proxy a))
32− pokeByteIndex 0 arr val
33+ pokeAt 0 arr val
33− peekByteIndex 0 arr `shouldReturn` val
34+ peekAt 0 arr `shouldReturn` val
34
35
35-- Size is also implicitly tested while serializing and deserializing.
36-- Size is also implicitly tested while serializing and deserializing.
36checkSizeOf :: forall a. Unbox a => Proxy a -> Int -> IO ()
37checkSizeOf :: forall a. Unbox a => Proxy a -> Int -> IO ()
test/Streamly/Test/FileSystem/Event/Common.hs+1−1
@@ -67,7 +67,7 @@ import System.IO.Temp (withSystemTempDirectory)
67import qualified Data.List.NonEmpty as NonEmpty
67import qualified Data.List.NonEmpty as NonEmpty
68import qualified Streamly.Internal.Data.Array as Array
68import qualified Streamly.Internal.Data.Array as Array
69import qualified Streamly.Internal.Data.Stream as Stream
69import qualified Streamly.Internal.Data.Stream as Stream
70−import qualified Streamly.Internal.Data.Stream.Concurrent as Stream
70+import qualified Streamly.Internal.Data.Stream.Prelude as Stream
71import qualified Streamly.Unicode.Stream as Unicode
71import qualified Streamly.Unicode.Stream as Unicode
72
72
73#if defined(FILESYSTEM_EVENT_LINUX)
73#if defined(FILESYSTEM_EVENT_LINUX)
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/Prelude/Serial.hs+3−3
@@ -26,7 +26,7 @@ import Data.Maybe ( isJust, fromJust )
26import Data.Word (Word8)
26import Data.Word (Word8)
27import Data.Semigroup (Sum(..), getSum)
27import Data.Semigroup (Sum(..), getSum)
28import Foreign.Storable (Storable)
28import Foreign.Storable (Storable)
29−import Streamly.Internal.Data.Serialize (Unbox)
29+import Streamly.Internal.Data.MutByteArray (Unbox)
30import Test.Hspec.QuickCheck
30import Test.Hspec.QuickCheck
31import Test.QuickCheck
31import Test.QuickCheck
32 ( Gen
32 ( Gen
@@ -478,7 +478,7 @@ testTakeInterval :: IO Bool
478testTakeInterval = do
478testTakeInterval = do
479 r <-
479 r <-
480 S.fold (FL.tee FL.head FL.last)
480 S.fold (FL.tee FL.head FL.last)
481− $ S.takeInterval (toRelTime64 takeDropTime)
481+ $ S.takeInterval (fromIntegral takeDropTime * 10**(-9))
482 $ S.repeatM (threadDelay 1000 >> getTime Monotonic)
482 $ S.repeatM (threadDelay 1000 >> getTime Monotonic)
483 checkTakeDropTime r
483 checkTakeDropTime r
484
484
@@ -487,7 +487,7 @@ testDropInterval = do
487 t0 <- getTime Monotonic
487 t0 <- getTime Monotonic
488 mt1 <-
488 mt1 <-
489 S.fold FL.head
489 S.fold FL.head
490− $ S.dropInterval (toRelTime64 takeDropTime)
490+ $ S.dropInterval (fromIntegral takeDropTime * 10**(-9))
491 $ S.repeatM (threadDelay 1000 >> getTime Monotonic)
491 $ S.repeatM (threadDelay 1000 >> getTime Monotonic)
492 checkTakeDropTime (Just t0, mt1)
492 checkTakeDropTime (Just t0, mt1)
493#endif
493#endif
test/Streamly/Test/Serialize/Serializable.hs+2−2
@@ -10,8 +10,8 @@ import Test.QuickCheck
10 )
10 )
11import Test.QuickCheck.Monadic (monadicIO)
11import Test.QuickCheck.Monadic (monadicIO)
12
12
13−import qualified Streamly.Internal.Data.Serialize.Parser as Decoder
13+import qualified Streamly.Internal.Data.Binary.Parser as Decoder
14−import qualified Streamly.Internal.Data.Serialize.Stream as Encoder
14+import qualified Streamly.Internal.Data.Binary.Stream as Encoder
15import qualified Streamly.Data.Stream as Stream
15import qualified Streamly.Data.Stream as Stream
16import Streamly.Internal.Data.Parser (Parser)
16import Streamly.Internal.Data.Parser (Parser)
17
17
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 $ all (== True) res
57+ return $ and 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) =
test/lib/Streamly/Test/Prelude/Common.hs+1−1
@@ -138,7 +138,7 @@ import Streamly.Prelude (avgRate, rate, maxBuffer, maxThreads)
138import qualified Streamly.Prelude as S
138import qualified Streamly.Prelude as S
139import qualified Streamly.Data.Fold as FL
139import qualified Streamly.Data.Fold as FL
140import qualified Streamly.Internal.Data.Stream.IsStream as S
140import qualified Streamly.Internal.Data.Stream.IsStream as S
141−import qualified Streamly.Internal.Data.Stream.IsStream.Common as IS
141+import qualified Streamly.Internal.Data.Stream.IsStream as IS
142import qualified Streamly.Internal.Data.Unfold as UF
142import qualified Streamly.Internal.Data.Unfold as UF
143
143
144import qualified Data.Map.Strict as Map
144import qualified Data.Map.Strict as Map
test/streamly-tests.cabal+8−7
@@ -216,13 +216,14 @@ common always-optimized
216-- Test suites in sorted order
216-- Test suites in sorted order
217-------------------------------------------------------------------------------
217-------------------------------------------------------------------------------
218
218
219+-- The Streamly.Data.List needs to be fixed to enable this
219−test-suite Data.List
220+-- test-suite Data.List
220− import: test-options
221+-- import: test-options
221− type: exitcode-stdio-1.0
222+-- type: exitcode-stdio-1.0
222− main-is: Streamly/Test/Data/List.hs
223+-- main-is: Streamly/Test/Data/List.hs
223− cpp-options: -DUSE_STREAMLY_LIST
224+-- cpp-options: -DUSE_STREAMLY_LIST
224− if !flag(dev)
225+-- if !flag(dev)
225− buildable: False
226+-- buildable: False
226
227
227test-suite Data.List.Base
228test-suite Data.List.Base
228 import: test-options
229 import: test-options