git.iko.soy/rowtype-yoga/purescript-arraybuffer-builder
on master help sign in
.github/workflows/ci.ymlnew file+46
since splitsnapshots
unifiedsplit
@@ -0,0 +1,46 @@
1+name: CI
2+
3+on:
4+ push:
5+ pull_request:
6+
7+jobs:
8+ build:
9+ runs-on: ubuntu-latest
10+
11+ steps:
12+ - uses: actions/checkout@v2
13+
14+ - name: Set up a PureScript toolchain
15+ uses: purescript-contrib/setup-purescript@main
16+ with:
17+ purescript: "unstable"
18+ purs-tidy: "latest"
19+
20+ - name: Cache PureScript dependencies
21+ uses: actions/cache@v2
22+ with:
23+ key: ${{ runner.os }}-spago-${{ hashFiles('**/*.dhall') }}-2
24+ path: |
25+ .spago
26+ output
27+ - name: Install dependencies
28+ run: spago install
29+
30+ - name: Build source
31+ run: spago build --no-install --purs-args '--censor-lib --strict --censor-codes='UserDefinedWarning''
32+
33+ - name: Install dev dependencies
34+ run: spago -x spago-dev.dhall install
35+
36+ - name: Run tests
37+ run: spago -x spago-dev.dhall test --no-install
38+
39+ - name: Check formatting
40+ run: purs-tidy check src test bench
41+
42+ - name: Verify Bower & Pulp
43+ run: |
44+ npm install bower pulp@16.0.0-0
45+ npx bower install
46+ npx pulp build -- --censor-lib --strict
.github/workflows/push-build.ymldeleted−42
@@ -1,42 +0,0 @@
1−name: CI
2−
3−on: [push]
4−
5−jobs:
6− build:
7−
8− runs-on: ubuntu-latest
9−
10− # https://github.com/purescript-contrib/setup-purescript
11−
12− steps:
13− # https://github.com/cachix/install-nix-action
14− - uses: actions/checkout@v2.3.4
15− - uses: cachix/install-nix-action@v13
16− with:
17− nix_path: nixpkgs=channel:nixos-unstable
18− - run: nix-shell --command 'spago --global-cache skip --config test.dhall test'
19−
20− # https://github.com/purescript-contrib/setup-purescript
21−
22− # - uses: actions/checkout@v2
23− # - name: Set up a PureScript toolchain
24− # uses: purescript-contrib/setup-purescript@main
25− # with:
26− # purescript: "0.14.1"
27− # spago: "0.20.1"
28−
29−# - name: Cache PureScript dependencies
30−# uses: actions/cache@v2
31−# # This cache uses the .dhall files to know when it should reinstall
32−# # and rebuild packages. It caches both the installed packages from
33−# # the `.spago` directory and compilation artifacts from the `output`
34−# # directory. When restored the compiler will rebuild any files that
35−# # have changed. If you do not want to cache compiled output, remove
36−# # the `output` path.
37−# with:
38−# key: ${{ runner.os }}-spago-${{ hashFiles('**/*.dhall') }}
39−# path: |
40−# .spago
41−
42− # - run: spago -x test.dhall test
CHANGELOG.md+34−3
@@ -1,8 +1,39 @@
1−## v2.0.0 2021-04-29
1+# Changelog
2
2
3−Added the `Bytes` type.
3+## v3.1.0 2022-12-10
4
4
5+- Transfer to __rowtype-yoga__.
6+- Docs.
7+
8+## v3.0.1 2022-05-04
9+
10+CI repair. Change `test.dhall` to `spago-dev.dhall`.
11+
12+## v3.0.0 2022-05-04
13+
14+Build for PureScript v0.15.
15+
16+Added `MonadRec` constraint to `execPut`, `execPutM`, `foldl`, `foldM`,
17+they are now stack-safe.
18+
19+## v2.1.0 2021-07-22
20+
21+Renamed `Data.ArrayBuffer.Internal.Bytes` and exported
22+it as `Data.ArrayBuffer.DataBuff`.
23+
24+## v2.0.0 2021-07-19
25+
5−Spago build for PureScript v0.14.1.
26+Build for PureScript v0.14.
27+
28+### Breaking Changes
29+
30+Previously we could only add `ArrayBuffer`s to the `Builder`,
31+and now we can add either `ArrayBuffer` or `DataView` to the `Builder`,
32+because we have added a `Bytes` type which distinguishes between `ArrayBuffer`
33+and `DataView`. Because of this, we can avoid making an
34+intermediate `ArrayBuffer` copy when adding a slice of an existing `ArrayBuffer`.
35+
36+Factored out the `Internal` module and added documentation.
6
37
7## v1.1.0 2020-09-10
38## v1.1.0 2020-09-10
8
39
DEVELOPMENT.mddeleted−43
@@ -1,43 +0,0 @@
1−# Development Notes
2−
3−All of the commands needed for development are in the `shell.nix` environment.
4−
5−```
6−$ nix-shell
7−```
8−
9−## Edit
10−
11−The `psc-ide` command is available for the [VS Code Purescript IDE extension](https://github.com/nwolverson/vscode-ide-purescript.git).
12−
13−```
14−[nix-shell:]$ code .
15−```
16−
17−## Build
18−
19−```
20−[nix-shell:]$ spago build
21−```
22−
23−## Test
24−
25−The test dependencies are specified seperately in `test.dhall`.
26−
27−```
28−[nix-shell:]$ spago -x test.dhall test
29−```
30−
31−## Publishing to Pursuit with `spago`
32−
33−[Publishing with `spago`](https://github.com/purescript/spago#publish-my-library).
34−
35−```
36−[nix-shell:]$ bower install --save
37−[nix-shell:]$ spago bump-version --no-dry-run major
38−[nix-shell:]$ pulp publish --no-push
39−```
40−
41−## Bower
42−
43−The `bower.json` is generated by running `spago bump-version`, do not edit it.
README.md+50−43
@@ -1,27 +1,21 @@
1# purescript-arraybuffer-builder
1# purescript-arraybuffer-builder
2
2
3−[![CI](https://github.com/jamesdbrock/purescript-arraybuffer-builder/workflows/CI/badge.svg?branch=master)](https://github.com/jamesdbrock/purescript-arraybuffer-builder/actions)
3+[![CI](https://github.com/rowtype-yoga/purescript-arraybuffer-builder/workflows/CI/badge.svg?branch=master)](https://github.com/rowtype-yoga/purescript-arraybuffer-builder/actions)
4[![Pursuit](http://pursuit.purescript.org/packages/purescript-arraybuffer-builder/badge)](http://pursuit.purescript.org/packages/purescript-arraybuffer-builder/)
4[![Pursuit](http://pursuit.purescript.org/packages/purescript-arraybuffer-builder/badge)](http://pursuit.purescript.org/packages/purescript-arraybuffer-builder/)
5+[![Maintainer: jamesdbrock](https://img.shields.io/badge/maintainer-jamesdbrock-teal.svg)](https://github.com/jamesdbrock)
5
6
7+
8+[`ArrayBuffer`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/ArrayBuffer)
6−`ArrayBuffer` serialization in the “builder monoid” and “builder monad” style.
9+serialization in the “builder monoid” and “builder monad” style.
7−In this style, we build up serialized data structures by `append`ing to
10+In this style, we build up serialized data structures by `tell`ing to
8−a monoid in a Writer monad with do-notation. This style of serialization
11+a `Writer` monad with `do`-notation. This style of serialization
9has been used for a long time and we [insist that it works really well](https://wiki.haskell.org/Do_notation_considered_harmful#Library_design).
12has been used for a long time and we [insist that it works really well](https://wiki.haskell.org/Do_notation_considered_harmful#Library_design).
10
13
11This package provides a `Builder` monoid and a `PutM` monad which are roughly
14This package provides a `Builder` monoid and a `PutM` monad which are roughly
12equivalent to types of the same name in the Haskell
15equivalent to types of the same name in the Haskell
13−[`Data.Binary.Put`](https://hackage.haskell.org/package/binary/docs/Data-Binary-Put.html)
16+[__Data.Binary.Put__](https://hackage.haskell.org/package/binary/docs/Data-Binary-Put.html)
14module.
17module.
15
18
16−This package defines
17−[no typeclasses](http://code.slipthrough.net/2018/03/13/thoughts-on-typeclass-codecs/).
18−Typeclass-based
19−serialization/deserialization generally assumes that we are round-tripping
20−some data structures out to storage and then back into our program.
21−This package is designed for the case in which we are serializing some data
22−to be sent to another program which expects a serialization format
23−that we don't control.
24−
25## Usage Examples
19## Usage Examples
26
20
27All `ArrayBuffer` building must occur in `Effect`.
21All `ArrayBuffer` building must occur in `Effect`.
@@ -45,7 +39,7 @@ IEEE-754 double-precision floats.
45import Data.ArrayBuffer.Builder (execPut, putFloat64be)
39import Data.ArrayBuffer.Builder (execPut, putFloat64be)
46
40
47do
41do
48− arraybuffer :: ArrayBuffer <- execPut $ do
42+ arraybuffer :: ArrayBuffer <- execPut do
49 putFloat64be 1.0
43 putFloat64be 1.0
50 putFloat64be 2.0
44 putFloat64be 2.0
51 putFloat64be 3.0
45 putFloat64be 3.0
@@ -57,23 +51,26 @@ Encode a `String` as UTF8 with a length prefix into our `Builder`.
57
51
58We give this as an example, rather than supporting it in the library, because
52We give this as an example, rather than supporting it in the library, because
59it depends on
53it depends on
60−[`Data.TextEncoding.encodeUtf8`](https://pursuit.purescript.org/packages/purescript-text-encoding/docs/Data.TextEncoding#v:encodeUtf8).
54+[`Web.Encoding.TextEncoder`](https://pursuit.purescript.org/packages/purescript-web-encoding/docs/Web.Encoding.TextEncoder).
61
55
62```purescript
56```purescript
57+import Effect.Class (class MonadEffect, liftEffect)
58+import Data.UInt (fromInt)
59+import Data.ArrayBuffer.Types (ArrayBuffer(..))
63−import Data.ArrayBuffer.Builder (PutM, putArrayBuffer, execPut)
60+import Data.ArrayBuffer.Builder (PutM, putArrayBuffer, execPut, putUint32be)
64import Data.ArrayBuffer.Typed (buffer)
61import Data.ArrayBuffer.Typed (buffer)
65−import Data.TextEncoding (encodeUtf8)
66import Data.ArrayBuffer.ArrayBuffer (byteLength)
62import Data.ArrayBuffer.ArrayBuffer (byteLength)
63+import Web.Encoding.TextEncoder (new, TextEncoder, encode)
67
64
68−putStringUtf8 :: forall m. (MonadEffect m) => String -> PutM m Unit
65+putStringUtf8 :: forall m. MonadEffect m => String -> PutM m Unit
69putStringUtf8 s = do
66putStringUtf8 s = do
67+ textEncoder <- liftEffect new
70− let stringbuf = buffer $ encodeUtf8 s
68+ let stringbuf = buffer $ encode s textEncoder
71− -- Put a 32-bit big-endian length prefix for the length of the utf8 string, in bytes.
69+ -- Put a 32-bit big-endian length for the utf8 string, in bytes.
72− putInt32be $ byteLength stringbuf
70+ putUint32be $ fromInt $ byteLength stringbuf
73 putArrayBuffer stringbuf
71 putArrayBuffer stringbuf
74
72
75−do
76− arraybuffer :: ArrayBuffer <- execPut $ putStringUtf8 "BLM"
73+arraybuffer :: ArrayBuffer <- execPut $ putStringUtf8 "🦝"
77```
74```
78
75
79### Serialize an `Array Int`
76### Serialize an `Array Int`
@@ -90,9 +87,9 @@ import Data.ArrayBuffer.Builder (execPut, putInt32be)
90import Data.Foldable (traverse_)
87import Data.Foldable (traverse_)
91import Data.Array (length)
88import Data.Array (length)
92
89
93−putArrayInt32 :: forall m. (MonadEffect m) => Array Int -> PutM m Unit
90+putArrayInt32 :: forall m. MonadEffect m => Array Int -> PutM m Unit
94putArrayInt32 xs = do
91putArrayInt32 xs = do
95− -- Put a 64-bit big-endian length prefix for the length of the array.
92+ -- Put a 64-bit big-endian length prefix for the array.
96 putInt32be 0
93 putInt32be 0
97 putInt32be $ length xs
94 putInt32be $ length xs
98 traverse_ putInt32be xs
95 traverse_ putInt32be xs
@@ -101,37 +98,47 @@ do
101 arraybuffer <- execPut $ putArrayInt32 [1,2,3]
98 arraybuffer <- execPut $ putArrayInt32 [1,2,3]
102```
99```
103
100
101+### Serialize an `Array Int` stack-safe
104
102
105−## `unsafePerformEffect execPut`
103+Stack-safe version of the `putArrayInt32` function above. For stack-safety
104+we use `foldRecM` instead of `traverse_` because `foldRecM` has a `MonadRec`
105+constraint which makes it stack-safe.
106
106
107−Dear computer programmer, you can call `execPut` inside `unsafePerformEffect`.
107+```purescript
108+import Data.Array (foldRecM)
108
109
109−The reason why this whole library is `Effect`ful is that `ArrayBuffer`s are mutable, so the `ArrayBuffer` library functions do everything in `Effect` to to confirm that any mutations are properly sequenced.
110+putArrayInt32 :: forall m. MonadEffect m => MonadRec m => Array Int -> PutM m Unit
111+putArrayInt32 xs = do
112+ -- Put a 64-bit big-endian length prefix for the array.
113+ putInt32be 0
114+ putInt32be $ length xs
115+ foldRecM (\_ x -> putInt32be x) unit xs
116+```
110
117
111−However, all of the `put` functions in this library are actually side-effect free, so we can use `unsafePerformEffect` to tell the compiler to trust us that we don't perform any side-effects while we're building an `ArrayBuffer`.
118+## Stack-safety
112
119
113−```purescript
120+This package will always be stack-safe if all of the functions called inside
114−let serialized :: ArrayBuffer
121+of the `PutM` builder expression are stack-safe.
115− serialized = unsafePerformEffect $ execPut do
116− putInt32be 3 -- ✅ Good, this has no side-effects
117− putInt32be =<< getCurrentPosixTime -- ❌ No don't do this
118− putArrayBuffer =<< readFileFromMyFilesystem -- ❌ No don’t do this either
119−```
120
122
121## Deserialization
123## Deserialization
122
124
123This package is only for writing `ArrayBuffer`s, not reading them.
125This package is only for writing `ArrayBuffer`s, not reading them.
124See
126See
125−[__purescript-parsing-dataview__](https://pursuit.purescript.org/packages/purescript-parsing-dataview/)
127+[__parsing-dataview__](https://pursuit.purescript.org/packages/purescript-parsing-dataview/)
126for a way to deserialize from `ArrayBuffer` back to Purescript data.
128for a way to deserialize from `ArrayBuffer` back to Purescript data.
127
129
128−## References
129−
130−* [MDN `ArrayBuffer`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/ArrayBuffer)
131−
132## Alternative packages for serializing to an `ArrayBuffer`
130## Alternative packages for serializing to an `ArrayBuffer`
133
131
134* [__dynamic-buffers__](https://pursuit.purescript.org/packages/purescript-dynamic-buffers)
132* [__dynamic-buffers__](https://pursuit.purescript.org/packages/purescript-dynamic-buffers)
135* [__node-buffer__](https://pursuit.purescript.org/packages/purescript-node-buffer)
133* [__node-buffer__](https://pursuit.purescript.org/packages/purescript-node-buffer)
136* [__arraybuffer-class__](https://pursuit.purescript.org/packages/purescript-arraybuffer-class)
134* [__arraybuffer-class__](https://pursuit.purescript.org/packages/purescript-arraybuffer-class)
137−* [__bytestrings__](https://pursuit.purescript.org/packages/purescript-bytestrings/)
135+* [__bytestrings__](https://pursuit.purescript.org/packages/purescript-bytestrings/)
136+* [__array-builder__](https://pursuit.purescript.org/packages/purescript-array-builder)
137+
138+## Development
139+
140+To run the tests,
141+
142+```shell
143+spago -x spago-dev.dhall test
144+```
bower.json+12−8
@@ -14,13 +14,17 @@
14 "output"
14 "output"
15 ],
15 ],
16 "dependencies": {
16 "dependencies": {
17− "purescript-arraybuffer": "^v8.0.3",
17+ "purescript-arraybuffer": "^v13.1.1",
18− "purescript-arraybuffer-types": "^v3.0.0",
18+ "purescript-arraybuffer-types": "^v3.0.2",
19− "purescript-effect": "^v3.0.0",
19+ "purescript-effect": "^v4.0.0",
20− "purescript-float32": "^v0.2.0",
20+ "purescript-float32": "^v2.0.0",
21+ "purescript-identity": "^v6.0.0",
22+ "purescript-lists": "^v7.0.0",
21− "purescript-maybe": "^v5.0.0",
23+ "purescript-maybe": "^v6.0.0",
24+ "purescript-newtype": "^v5.0.0",
22− "purescript-prelude": "^v5.0.0",
25+ "purescript-prelude": "^v6.0.1",
26+ "purescript-tailrec": "^v6.1.0",
23− "purescript-transformers": "^v5.0.0",
27+ "purescript-transformers": "^v6.0.0",
24− "purescript-uint": "^v5.1.4"
28+ "purescript-uint": "^v7.0.0"
25 }
29 }
26}
30}
packages.dhall+3−47
@@ -116,56 +116,12 @@ let additions =
116 }
116 }
117-------------------------------
117-------------------------------
118-}
118-}
119−
120let upstream =
119let upstream =
121− https://github.com/purescript/package-sets/releases/download/psc-0.14.1-20210427/packages.dhall sha256:edbb8f70232fb83895c7ce02f5d2b29f6ee1722f1a70fc58d3bc0ab0de18afe4
120+ https://raw.githubusercontent.com/purescript/package-sets/psc-0.15.4-20221209/src/packages.dhall
121+ sha256:b55c24bf585df4041ae6e87124cab7b35d474a9a77c9c3862ee6bf1dae618d72
122
122
123let overrides = {=}
123let overrides = {=}
124
124
125−-- TODO Remove all of these additions when they are in package-sets for
126−-- Purescript v0.14.
127−let additions =
125+let additions = {=}
128− { arraybuffer =
129− { dependencies =
130− [ "effect"
131− , "arraybuffer-types"
132− , "maybe"
133− , "typelevel"
134− , "uint"
135− , "float32"
136− , "partial"
137− , "nullable"
138− , "functions"
139− ]
140− , repo =
141− "https://github.com/jacereda/purescript-arraybuffer.git"
142− , version =
143− "v8.0.3"
144− }
145− , float32 =
146− { dependencies =
147− [ "prelude"
148− ]
149− , repo =
150− "https://github.com/athanclark/purescript-float32.git"
151− , version =
152− "v0.2.0"
153− }
154− , uint =
155− { dependencies =
156− [ "effect"
157− , "quickcheck-laws"
158− , "console"
159− , "maybe"
160− , "math"
161− , "psci-support"
162− , "quickcheck"
163− ]
164− , repo =
165− "https://github.com/zaquest/purescript-uint.git"
166− , version =
167− "v5.1.4"
168− }
169− }
170
126
171in upstream // overrides // additions
127in upstream // overrides // additions
psc-package.jsondeleted−16
@@ -1,16 +0,0 @@
1−{
2− "name": "purescript-arraybuffer-builder",
3− "set": "psc-0.14.1-20210429",
4− "source": "https://github.com/purescript/package-sets.git",
5− "depends": [
6− "prelude",
7− "effect",
8− "float32",
9− "maybe",
10− "prelude",
11− "transformers",
12− "uint",
13− "arraybuffer-types",
14− "arraybuffer"
15− ]
16−}
shell.nixdeleted−21
@@ -1,21 +0,0 @@
1−{ pkgs ? import <nixpkgs> { } }:
2−let
3− easy-ps = import (builtins.fetchGit {
4− # url = "git@github.com:justinwoo/easy-purescript-nix.git";
5− url = "https://github.com/justinwoo/easy-purescript-nix.git";
6− ref = "master";
7− rev = "dae91f43317fd5ff207e11ea6bf4b6130e4ba9fc";
8− }) { inherit pkgs; };
9−in
10−pkgs.mkShell {
11− buildInputs = [
12− easy-ps.purs-0_14_1
13− easy-ps.spago
14− easy-ps.pulp
15− easy-ps.psc-package
16− # easy-ps.psc-publish
17− pkgs.nodejs-14_x
18− pkgs.nodePackages.bower
19− ];
20− LC_ALL = "C.UTF-8"; # https://github.com/purescript/spago/issues/507
21−}
test.dhall → spago-dev.dhallrenamed+6−2
@@ -12,5 +12,9 @@ let conf = ./spago.dhall
12
12
13in conf //
13in conf //
14 { sources = conf.sources # [ "test/**/*.purs" ]
14 { sources = conf.sources # [ "test/**/*.purs" ]
15− , dependencies = conf.dependencies # [ "assert" ]
15+ , dependencies = conf.dependencies #
16+ [ "assert"
17+ , "arrays"
18+ , "web-encoding"
19+ ]
16− }
20+ }
spago.dhall+4
@@ -4,12 +4,16 @@
4, dependencies =
4, dependencies =
5 [ "effect"
5 [ "effect"
6 , "float32"
6 , "float32"
7+ , "identity"
8+ , "lists"
7 , "maybe"
9 , "maybe"
10+ , "newtype"
8 , "prelude"
11 , "prelude"
9 , "transformers"
12 , "transformers"
10 , "uint"
13 , "uint"
11 , "arraybuffer-types"
14 , "arraybuffer-types"
12 , "arraybuffer"
15 , "arraybuffer"
16+ , "tailrec"
13 ]
17 ]
14, packages = ./packages.dhall
18, packages = ./packages.dhall
15, sources = [ "src/**/*.purs" ]
19, sources = [ "src/**/*.purs" ]
src/Data/ArrayBuffer/Builder.purs+55−56
@@ -5,47 +5,42 @@
5-- | Writing to an `ArrayBuffer` is an `Effect`ful activity, so most
5-- | Writing to an `ArrayBuffer` is an `Effect`ful activity, so most
6-- | functions in this module must be run in a `MonadEffect` context.
6-- | functions in this module must be run in a `MonadEffect` context.
7-- |
7-- |
8−-- | For operations for working with `ArrayBuffer`, see
8+-- | For other operations for working with `ArrayBuffer`, see
9-- | module
9-- | module
10-- | [`Data.ArrayBuffer.ArrayBuffer`](https://pursuit.purescript.org/packages/purescript-arraybuffer/docs/Data.ArrayBuffer.ArrayBuffer)
10-- | [`Data.ArrayBuffer.ArrayBuffer`](https://pursuit.purescript.org/packages/purescript-arraybuffer/docs/Data.ArrayBuffer.ArrayBuffer)
11−-- | in package __purescript-arraybuffer__.
11+-- | in package __arraybuffer__.
12module Data.ArrayBuffer.Builder
12module Data.ArrayBuffer.Builder
13−( PutM
13+ ( PutM
14−, Put
14+ , Put
15−, execPutM
15+ , execPutM
16−, execPut
16+ , execPut
17−, subBuilder
17+ , subBuilder
18−, putArrayBuffer
18+ , putArrayBuffer
19−, putDataView
19+ , putDataView
20+ , putDataBuff
20−, putUint8
21+ , putUint8
21−, putInt8
22+ , putInt8
22−, putUint16be
23+ , putUint16be
23−, putUint16le
24+ , putUint16le
24−, putInt16be
25+ , putInt16be
25−, putInt16le
26+ , putInt16le
26−, putUint32be
27+ , putUint32be
27−, putUint32le
28+ , putUint32le
28−, putInt32be
29+ , putInt32be
29−, putInt32le
30+ , putInt32le
30−, putFloat32be
31+ , putFloat32be
31−, putFloat32le
32+ , putFloat32le
32−, putFloat64be
33+ , putFloat64be
33−, putFloat64le
34+ , putFloat64le
34−, module Data.ArrayBuffer.Builder.Internal
35+ , module Data.ArrayBuffer.Builder.Internal
35−)
36−where
36+ ) where
37−
38−import Data.ArrayBuffer.Builder.Internal
39− ( Builder
40− , (<>>)
41− , length
42− )
43−
44−import Data.ArrayBuffer.Builder.Internal as Internal
45
37
46import Prelude
38import Prelude
47
39
40+import Control.Monad.Rec.Class (class MonadRec)
48import Control.Monad.Writer.Trans (WriterT, execWriterT, tell, lift)
41import Control.Monad.Writer.Trans (WriterT, execWriterT, tell, lift)
42+import Data.ArrayBuffer.Builder.Internal (Builder, DataBuff(..), length, toView, (<>>))
43+import Data.ArrayBuffer.Builder.Internal as Internal
49import Data.ArrayBuffer.Types (ArrayBuffer, DataView)
44import Data.ArrayBuffer.Types (ArrayBuffer, DataView)
50import Data.Float32 (Float32)
45import Data.Float32 (Float32)
51import Data.UInt (UInt)
46import Data.UInt (UInt)
@@ -63,11 +58,11 @@ type PutM = WriterT Builder
63-- | The `PutM` type reified to `Effect`, in other words `WriterT Builder Effect`.
58-- | The `PutM` type reified to `Effect`, in other words `WriterT Builder Effect`.
64type Put = PutM Effect
59type Put = PutM Effect
65
60
66−-- | Build an `ArrayBuffer` with do-notation in any `MonadEffect`. *O(n)*
61+-- | Build an `ArrayBuffer` with do-notation in any `MonadEffect`. *O(n)* Stack-safe.
67−execPutM :: forall m. (MonadEffect m) => PutM m Unit -> m ArrayBuffer
62+execPutM :: forall m. MonadEffect m => MonadRec m => PutM m Unit -> m ArrayBuffer
68execPutM = Internal.execBuilder <=< execWriterT
63execPutM = Internal.execBuilder <=< execWriterT
69
64
70−-- | Build an `ArrayBuffer` with do-notation in `Effect`. *O(n)*
65+-- | Build an `ArrayBuffer` with do-notation in `Effect`. *O(n)* Stack-safe.
71execPut :: Put Unit -> Effect ArrayBuffer
66execPut :: Put Unit -> Effect ArrayBuffer
72execPut = execPutM
67execPut = execPutM
73
68
@@ -89,69 +84,73 @@ execPut = execPutM
89-- | putInt32be $ length messageBuilder
84-- | putInt32be $ length messageBuilder
90-- | tell messageBuilder
85-- | tell messageBuilder
91-- | ```
86-- | ```
92−subBuilder :: forall m. (MonadEffect m) => PutM m Unit -> PutM m Builder
87+subBuilder :: forall m. Monad m => PutM m Unit -> PutM m Builder
93subBuilder = lift <<< execWriterT
88subBuilder = lift <<< execWriterT
94
89
95-- | Append an `ArrayBuffer` to the builder.
90-- | Append an `ArrayBuffer` to the builder.
96−putArrayBuffer :: forall m. (MonadEffect m) => ArrayBuffer -> PutM m Unit
91+putArrayBuffer :: forall m. Monad m => ArrayBuffer -> PutM m Unit
97−putArrayBuffer = tell <<< Internal.singleton
92+putArrayBuffer = tell <<< Internal.singleton <<< Internal.Buff
98
93
99-- | Append a `DataView` to the builder.
94-- | Append a `DataView` to the builder.
100−putDataView :: forall m. (MonadEffect m) => DataView -> PutM m Unit
95+putDataView :: forall m. Monad m => DataView -> PutM m Unit
101−putDataView = tell <<< Internal.singleton_
96+putDataView = tell <<< Internal.singleton <<< Internal.View
97+
98+-- | Append either an `ArrayBuffer` or a `DataView` to the builder.
99+putDataBuff :: forall m. Monad m => DataBuff -> PutM m Unit
100+putDataBuff = tell <<< Internal.singleton
102
101
103-- | Append an 8-bit unsigned integer (byte) to the builder.
102-- | Append an 8-bit unsigned integer (byte) to the builder.
104−putUint8 :: forall m. (MonadEffect m) => UInt -> PutM m Unit
103+putUint8 :: forall m. MonadEffect m => UInt -> PutM m Unit
105putUint8 = putArrayBuffer <=< Internal.encodeUint8
104putUint8 = putArrayBuffer <=< Internal.encodeUint8
106
105
107-- | Append an 8-bit two’s-complement signed integer (char) to the builder.
106-- | Append an 8-bit two’s-complement signed integer (char) to the builder.
108−putInt8 :: forall m. (MonadEffect m) => Int -> PutM m Unit
107+putInt8 :: forall m. MonadEffect m => Int -> PutM m Unit
109putInt8 = putArrayBuffer <=< Internal.encodeInt8
108putInt8 = putArrayBuffer <=< Internal.encodeInt8
110
109
111-- | Append a 16-bit big-endian unsigned integer to the builder.
110-- | Append a 16-bit big-endian unsigned integer to the builder.
112−putUint16be :: forall m. (MonadEffect m) => UInt -> PutM m Unit
111+putUint16be :: forall m. MonadEffect m => UInt -> PutM m Unit
113putUint16be = putArrayBuffer <=< Internal.encodeUint16be
112putUint16be = putArrayBuffer <=< Internal.encodeUint16be
114
113
115-- | Append a 16-bit little-endian unsigned integer to the builder.
114-- | Append a 16-bit little-endian unsigned integer to the builder.
116−putUint16le :: forall m. (MonadEffect m) => UInt -> PutM m Unit
115+putUint16le :: forall m. MonadEffect m => UInt -> PutM m Unit
117putUint16le = putArrayBuffer <=< Internal.encodeUint16le
116putUint16le = putArrayBuffer <=< Internal.encodeUint16le
118
117
119-- | Append a 16-bit big-endian two’s-complement signed integer to the builder.
118-- | Append a 16-bit big-endian two’s-complement signed integer to the builder.
120−putInt16be :: forall m. (MonadEffect m) => Int -> PutM m Unit
119+putInt16be :: forall m. MonadEffect m => Int -> PutM m Unit
121putInt16be = putArrayBuffer <=< Internal.encodeInt16be
120putInt16be = putArrayBuffer <=< Internal.encodeInt16be
122
121
123-- | Append a 16-bit little-endian two’s-complement signed integer to the builder.
122-- | Append a 16-bit little-endian two’s-complement signed integer to the builder.
124−putInt16le :: forall m. (MonadEffect m) => Int -> PutM m Unit
123+putInt16le :: forall m. MonadEffect m => Int -> PutM m Unit
125putInt16le = putArrayBuffer <=< Internal.encodeInt16le
124putInt16le = putArrayBuffer <=< Internal.encodeInt16le
126
125
127-- | Append a 32-bit big-endian unsigned integer to the builder.
126-- | Append a 32-bit big-endian unsigned integer to the builder.
128−putUint32be :: forall m. (MonadEffect m) => UInt -> PutM m Unit
127+putUint32be :: forall m. MonadEffect m => UInt -> PutM m Unit
129putUint32be = putArrayBuffer <=< Internal.encodeUint32be
128putUint32be = putArrayBuffer <=< Internal.encodeUint32be
130
129
131-- | Append a 32-bit little-endian unsigned integer to the builder.
130-- | Append a 32-bit little-endian unsigned integer to the builder.
132−putUint32le :: forall m. (MonadEffect m) => UInt -> PutM m Unit
131+putUint32le :: forall m. MonadEffect m => UInt -> PutM m Unit
133putUint32le = putArrayBuffer <=< Internal.encodeUint32le
132putUint32le = putArrayBuffer <=< Internal.encodeUint32le
134
133
135-- | Append a 32-bit big-endian two’s-complement signed integer to the builder.
134-- | Append a 32-bit big-endian two’s-complement signed integer to the builder.
136−putInt32be :: forall m. (MonadEffect m) => Int -> PutM m Unit
135+putInt32be :: forall m. MonadEffect m => Int -> PutM m Unit
137putInt32be = putArrayBuffer <=< Internal.encodeInt32be
136putInt32be = putArrayBuffer <=< Internal.encodeInt32be
138
137
139-- | Append a 32-bit little-endian two’s-complement signed integer to the builder.
138-- | Append a 32-bit little-endian two’s-complement signed integer to the builder.
140−putInt32le :: forall m. (MonadEffect m) => Int -> PutM m Unit
139+putInt32le :: forall m. MonadEffect m => Int -> PutM m Unit
141putInt32le = putArrayBuffer <=< Internal.encodeInt32le
140putInt32le = putArrayBuffer <=< Internal.encodeInt32le
142
141
143-- | Append a 32-bit big-endian IEEE single-precision float to the builder.
142-- | Append a 32-bit big-endian IEEE single-precision float to the builder.
144−putFloat32be :: forall m. (MonadEffect m) => Float32 -> PutM m Unit
143+putFloat32be :: forall m. MonadEffect m => Float32 -> PutM m Unit
145putFloat32be = putArrayBuffer <=< Internal.encodeFloat32be
144putFloat32be = putArrayBuffer <=< Internal.encodeFloat32be
146
145
147-- | Append a 32-bit little-endian IEEE single-precision float to the builder.
146-- | Append a 32-bit little-endian IEEE single-precision float to the builder.
148−putFloat32le :: forall m. (MonadEffect m) => Float32 -> PutM m Unit
147+putFloat32le :: forall m. MonadEffect m => Float32 -> PutM m Unit
149putFloat32le = putArrayBuffer <=< Internal.encodeFloat32le
148putFloat32le = putArrayBuffer <=< Internal.encodeFloat32le
150
149
151-- | Append a 64-bit big-endian IEEE double-precision float to the builder.
150-- | Append a 64-bit big-endian IEEE double-precision float to the builder.
152−putFloat64be :: forall m. (MonadEffect m) => Number -> PutM m Unit
151+putFloat64be :: forall m. MonadEffect m => Number -> PutM m Unit
153putFloat64be = putArrayBuffer <=< Internal.encodeFloat64be
152putFloat64be = putArrayBuffer <=< Internal.encodeFloat64be
154
153
155-- | Append a 64-bit little-endian IEEE double-precision float to the builder.
154-- | Append a 64-bit little-endian IEEE double-precision float to the builder.
156−putFloat64le :: forall m. (MonadEffect m) => Number -> PutM m Unit
155+putFloat64le :: forall m. MonadEffect m => Number -> PutM m Unit
157putFloat64le = putArrayBuffer <=< Internal.encodeFloat64le
156putFloat64le = putArrayBuffer <=< Internal.encodeFloat64le
src/Data/ArrayBuffer/Internal.purs+97−96
@@ -2,7 +2,7 @@
2-- |
2-- |
3-- | You probably don’t want to import anything from this module.
3-- | You probably don’t want to import anything from this module.
4-- |
4-- |
5−-- | # Implementation Details
5+-- | ## Implementation Details
6-- |
6-- |
7-- | We want our `Builder` to be a data structure with
7-- | We want our `Builder` to be a data structure with
8-- | * *O(1)* monoid append
8-- | * *O(1)* monoid append
@@ -57,19 +57,20 @@
57-- | __bytestring-tree-builder__
57-- | __bytestring-tree-builder__
58-- | which “according to the benchmarks … beats all the alternatives.”
58-- | which “according to the benchmarks … beats all the alternatives.”
59-- | However, we
59-- | However, we
60−-- | haven't chosen this implementation because it's fast, we've chosen
60+-- | haven’t chosen this implementation because it’s fast, we've chosen
61−-- | this implementation because it's simple.
61+-- | this implementation because it’s simple.
62−-- | If someone wants to create a fast PureScript ArrayBuffer serialization
62+-- | If someone wants to create a fast PureScript `ArrayBuffer` serialization
63-- | library, then they can benchmark against this one to prove that the new
63-- | library, then they can benchmark against this one to prove that the new
64-- | one is fast.
64-- | one is fast.
65-- |
65-- |
66-- | One relatively cheap and simple performance improvement for this library would be to
66-- | One relatively cheap and simple performance improvement for this library would be to
67−-- | remove the Null constructor of `Builder` and instead use Javascript nulls.
67+-- | remove the `Null` constructor of `Builder` and instead use Javascript nulls?
68−-- | __UPDATE__ Actually no, we need the Null constructor for the Monoid instance.
69-- |
68-- |
70-- | In the longer term, it might make sense to try to change the `Builder` so
69-- | In the longer term, it might make sense to try to change the `Builder` so
71-- | that it works like the
70-- | that it works like the
72−-- | https://hackage.haskell.org/package/bytestring/docs/Data-ByteString-Builder.html
71+-- | https://hackage.haskell.org/package/bytestring/docs/Data-ByteString-Builder.html .
72+-- | That’s the approach taken by
73+-- | https://pursuit.purescript.org/packages/purescript-dynamic-buffers .
73-- |
74-- |
74-- | Here are some benchmarks of different Haskell ByteString builders
75-- | Here are some benchmarks of different Haskell ByteString builders
75-- | https://github.com/haskell-perf/strict-bytestring-builders
76-- | https://github.com/haskell-perf/strict-bytestring-builders
@@ -77,56 +78,63 @@
77-- | We've tried to design the API for this library with minimal assumptions,
78-- | We've tried to design the API for this library with minimal assumptions,
78-- | so that if we want to change the `Builder` implementation later then we can.
79-- | so that if we want to change the `Builder` implementation later then we can.
79-- |
80-- |
80−-- | Another way this could go:
81−-- | https://kodimensional.dev/posts/2019-03-25-comonadic-builders
82−-- |
83−-- | Our convention is that functions which add `DataView`s to
84−-- | the `Builder` are suffixed with underscore (`_`).
85−-- |
86module Data.ArrayBuffer.Builder.Internal
81module Data.ArrayBuffer.Builder.Internal
87−( Builder(..)
82+ ( Builder(..)
88−, (<>>)
83+ , (<>>)
89−, Bytes(..)
84+ , DataBuff(..)
90−, toView
85+ , toView
91−, execBuilder
86+ , execBuilder
92−, length
87+ , length
93−, foldl
88+ , foldl
94−, foldM
89+ , foldM
95−, singleton
90+ , singleton
96−, singleton_
97−, cons
91+ , cons
98−, cons_
99−, snoc
92+ , snoc
100−, snoc_
101−, encodeUint8
93+ , encodeUint8
102−, encodeInt8
94+ , encodeInt8
103−, encodeUint16be
95+ , encodeUint16be
104−, encodeUint16le
96+ , encodeUint16le
105−, encodeInt16be
97+ , encodeInt16be
106−, encodeInt16le
98+ , encodeInt16le
107−, encodeUint32be
99+ , encodeUint32be
108−, encodeUint32le
100+ , encodeUint32le
109−, encodeInt32be
101+ , encodeInt32be
110−, encodeInt32le
102+ , encodeInt32le
111−, encodeFloat32be
103+ , encodeFloat32be
112−, encodeFloat32le
104+ , encodeFloat32le
113−, encodeFloat64be
105+ , encodeFloat64be
114−, encodeFloat64le
106+ , encodeFloat64le
115−)
116−where
107+ ) where
117
108
118import Prelude
109import Prelude
119
110
111+import Control.Monad.Rec.Class (class MonadRec, Step(..), tailRecM)
120import Data.ArrayBuffer.ArrayBuffer as AB
112import Data.ArrayBuffer.ArrayBuffer as AB
121import Data.ArrayBuffer.DataView as DV
113import Data.ArrayBuffer.DataView as DV
122import Data.ArrayBuffer.Typed as AT
114import Data.ArrayBuffer.Typed as AT
123import Data.ArrayBuffer.Types (ArrayBuffer, ByteLength, DataView, Uint8Array)
115import Data.ArrayBuffer.Types (ArrayBuffer, ByteLength, DataView, Uint8Array)
124import Data.Float32 (Float32)
116import Data.Float32 (Float32)
117+import Data.Identity (Identity)
118+import Data.List (List(..), uncons, (:))
125import Data.Maybe (Maybe(..))
119import Data.Maybe (Maybe(..))
120+import Data.Newtype (unwrap)
126import Data.UInt (UInt)
121import Data.UInt (UInt)
127import Effect (Effect)
122import Effect (Effect)
128import Effect.Class (class MonadEffect, liftEffect)
123import Effect.Class (class MonadEffect, liftEffect)
129
124
125+-- | For distinguishing between `ArrayBuffer` and `DataView`.
126+data DataBuff
127+ = Buff ArrayBuffer
128+ | View DataView
129+
130+-- Concerning DataViews.
131+-- https://v8.dev/blog/dataview
132+
133+-- | View the contents of `DataBuff` as a `DataView`.
134+toView :: DataBuff -> DataView
135+toView (Buff ab) = DV.whole ab
136+toView (View dv) = dv
137+
130-- | Monoidal builder for `ArrayBuffer`s.
138-- | Monoidal builder for `ArrayBuffer`s.
131-- |
139-- |
132-- | We can add two types of things to the `Builder`:
140-- | We can add two types of things to the `Builder`:
@@ -138,7 +146,7 @@ import Effect.Class (class MonadEffect, liftEffect)
138-- | from some other `ArrayBuffer`, so that we don’t
146-- | from some other `ArrayBuffer`, so that we don’t
139-- | need an extra intermediate copy of the slice.
147-- | need an extra intermediate copy of the slice.
140data Builder
148data Builder
141− = Node Builder Bytes Builder
149+ = Node Builder DataBuff Builder
142 | Null
150 | Null
143
151
144-- | ### Left-associative `<>>` append operator
152-- | ### Left-associative `<>>` append operator
@@ -182,71 +190,62 @@ instance semigroupBuilder :: Semigroup Builder where
182 append Null r = r
190 append Null r = r
183 append l (Node Null rx rr) = Node l rx rr -- this is the snoc case
191 append l (Node Null rx rr) = Node l rx rr -- this is the snoc case
184 append (Node ll lx Null) r = Node ll lx r -- found the rightmost Null
192 append (Node ll lx Null) r = Node ll lx r -- found the rightmost Null
185− -- of the left tree
193+ -- of the left tree
186 append (Node ll lx lr) r = Node ll lx $ append lr r -- search down the right
194 append (Node ll lx lr) r = Node ll lx $ append lr r -- search down the right
195+
187− -- side of the left tree
196+-- side of the left tree
188− -- to find the rightmost
197+-- to find the rightmost
189− -- Null
198+-- Null
190
199
191infixl 5 append as <>>
200infixl 5 append as <>>
192
201
193instance monoidBuilder :: Monoid Builder where
202instance monoidBuilder :: Monoid Builder where
194 mempty = Null
203 mempty = Null
195
204
196−-- | For distinguishing between `ArrayBuffer` and `DataView`.
205+-- | Stack-safe `foldl` over a `Builder`. *O(n)*
197−data Bytes
198− = ByteBuff ArrayBuffer
199− | ByteView DataView
200−
201−-- | `Builder` is not an instance of `Foldable` because `Builder` is monomorphic.
202−foldl :: forall b. (b -> Bytes -> b) -> b -> Builder -> b
206+foldl :: forall a. (a -> DataBuff -> a) -> a -> Builder -> a
203−foldl _ a Null = a
207+foldl f a0 b = (unwrap :: Identity a -> a) $ foldM (\a' b' -> pure (f a' b')) a0 b
204−foldl f a (Node l x r) = foldl f (f (foldl f a l) x) r
205−
206−-- | Monomorphic foldM copied from
207−-- | [`Data.Foldable.foldM`](https://pursuit.purescript.org/packages/purescript-foldable-traversable/4.1.1/docs/Data.Foldable#v:foldM)
208−foldM :: forall b m. (Monad m) => (b -> Bytes -> m b) -> b -> Builder -> m b
209−foldM f a0 = foldl (\ma b -> ma >>= flip f b) (pure a0)
210
208
211−-- | Construct a `Builder` with a single `ArrayBuffer`. *O(1)*
209+-- | Stack-safe `foldM` over a `Builder`. *O(n)*.
212−singleton :: ArrayBuffer -> Builder
210+foldM :: forall m a. MonadRec m => (a -> DataBuff -> m a) -> a -> Builder -> m a
213−singleton buf = Node Null (ByteBuff buf) Null
211+-- The Art of Computer Programming 2.3.1
214−
212+-- Inorder traversal using a stack
213+-- https://yuyuan.org/MorrisAlgorithm/
214+foldM f a0 b = tailRecM outer { p: b, stack: Nil, accum: a0 }
215+ where
216+ outer { p, stack, accum } = do
217+ stack' <- tailRecM inner { p_: p, stack_: stack }
218+ case uncons stack' of
219+ Nothing -> pure $ Done accum
220+ Just { head: Node _ x r, tail } -> do
221+ accum' <- f accum x
222+ pure $ Loop { p: r, stack: tail, accum: accum' }
223+ Just { head: Null } -> pure $ Done accum
224+ inner { p_: Null, stack_ } = pure $ Done stack_
225+ inner { p_: n@(Node l _ _), stack_ } = pure $ Loop { p_: l, stack_: n : stack_ }
226+
215−-- | Construct a `Builder` with a single `DataView`. *O(1)*
227+-- | Construct a `Builder` with a single `DataBuff`. *O(1)*
216−singleton_ :: DataView -> Builder
228+singleton :: DataBuff -> Builder
217−singleton_ buf = Node Null (ByteView buf) Null
229+singleton buf = Node Null buf Null
218
230
219-- | Calculate the total byte length of the `Builder`, without actually
231-- | Calculate the total byte length of the `Builder`, without actually
220-- | building it yet. *O(n)*
232-- | building it yet. *O(n)*
221length :: Builder -> ByteLength
233length :: Builder -> ByteLength
222length bldr = foldl (\b a -> b + len a) 0 bldr
234length bldr = foldl (\b a -> b + len a) 0 bldr
223 where
235 where
224− len (ByteBuff ab) = AB.byteLength ab
236+ len (Buff ab) = AB.byteLength ab
225− len (ByteView dv) = DV.byteLength dv
237+ len (View dv) = DV.byteLength dv
226
238
227− -- | View the contents of `Bytes` as a `DataView`.
239+-- | Prepend a `DataBuff` to the beginning of the `Builder`. *O(1)*
228−toView :: Bytes -> DataView
240+cons :: DataBuff -> Builder -> Builder
229−toView (ByteBuff ab) = DV.whole ab
241+cons x bs = Node Null x bs
230−toView (ByteView dv) = dv
231
242
232−-- | Prepend an `ArrayBuffer` to the beginning of the `Builder`. *O(1)*
233−cons :: ArrayBuffer -> Builder -> Builder
234−cons x bs = Node Null (ByteBuff x) bs
235−
236−-- | Prepend a `DataView` to the beginning of the `Builder`. *O(1)*
237−cons_ :: DataView -> Builder -> Builder
238−cons_ x bs = Node Null (ByteView x) bs
239−
240−-- | Append an `ArrayBuffer` to the end of the `Builder`. *O(1)*
243+-- | Append a `DataBuff` to the end of the `Builder`. *O(1)*
241−snoc :: Builder -> ArrayBuffer -> Builder
244+snoc :: Builder -> DataBuff -> Builder
242−snoc bs x = Node bs (ByteBuff x) Null
245+snoc bs x = Node bs x Null
243−
244−-- | Append a `DataView` to the end of the `Builder`. *O(1)*
245−snoc_ :: Builder -> DataView -> Builder
246−snoc_ bs x = Node bs (ByteView x) Null
247
246
248-- | Build a single `ArrayBuffer` from a `Builder`. *O(n)*
247-- | Build a single `ArrayBuffer` from a `Builder`. *O(n)*
249−execBuilder :: forall m. (MonadEffect m) => Builder -> m ArrayBuffer
248+execBuilder :: forall m. MonadEffect m => MonadRec m => Builder -> m ArrayBuffer
250execBuilder bldr = do
249execBuilder bldr = do
251 let buflen = length bldr
250 let buflen = length bldr
252 -- Allocate the final ArrayBuffer
251 -- Allocate the final ArrayBuffer
@@ -260,12 +259,14 @@ execBuilder bldr = do
260 aview <- liftEffect $ toUint8Array a
259 aview <- liftEffect $ toUint8Array a
261 _ <- liftEffect $ AT.setTyped newview (Just offset) aview
260 _ <- liftEffect $ AT.setTyped newview (Just offset) aview
262 pure $ offset + AT.byteLength aview
261 pure $ offset + AT.byteLength aview
262+ )
263+ 0
263− ) 0 bldr
264+ bldr
264 pure buf
265 pure buf
265− where
266+ where
266− toUint8Array :: Bytes -> Effect Uint8Array
267+ toUint8Array :: DataBuff -> Effect Uint8Array
267− toUint8Array (ByteBuff ab) = AT.whole ab
268+ toUint8Array (Buff ab) = AT.whole ab
268− toUint8Array (ByteView dv) =
269+ toUint8Array (View dv) =
269 AT.part (DV.buffer dv) (DV.byteOffset dv) (DV.byteLength dv)
270 AT.part (DV.buffer dv) (DV.byteOffset dv) (DV.byteLength dv)
270
271
271-- | Serialize an 8-bit unsigned integer (byte) into a new `ArrayBuffer`.
272-- | Serialize an 8-bit unsigned integer (byte) into a new `ArrayBuffer`.
test/Main.purs+95−64
@@ -1,16 +1,24 @@
1module Test.Main where
1module Test.Main where
2
2
3−import Data.ArrayBuffer.Builder
3+import Prelude
4
4
5import Control.Monad.Writer.Trans (tell)
5import Control.Monad.Writer.Trans (tell)
6+import Data.Array (foldRecM)
7+import Data.Array as Array
8+import Data.ArrayBuffer.ArrayBuffer (byteLength)
9+import Data.ArrayBuffer.Builder (Builder, DataBuff(..), PutM, execPut, putArrayBuffer, putInt16be, putInt32be, putInt32le, putInt8, subBuilder)
6−import Data.ArrayBuffer.Builder.Internal (cons, encodeInt8, execBuilder, length, singleton, singleton_, (<>>))
10+import Data.ArrayBuffer.Builder.Internal (cons, encodeInt8, execBuilder, length, singleton, (<>>))
7import Data.ArrayBuffer.DataView as DV
11import Data.ArrayBuffer.DataView as DV
12+import Data.ArrayBuffer.Typed (buffer)
8import Data.ArrayBuffer.Typed as AT
13import Data.ArrayBuffer.Typed as AT
9−import Data.ArrayBuffer.Types (ArrayBuffer, Uint8Array)
14+import Data.ArrayBuffer.Typed as AV
15+import Data.ArrayBuffer.Types (ArrayBuffer, Uint8Array, Int8Array)
10import Data.UInt as UInt
16import Data.UInt as UInt
11import Effect (Effect)
17import Effect (Effect)
12−import Prelude (Unit, bind, pure, discard, map, negate, ($), (<$>), (=<<), (<>))
18+import Effect.Class (class MonadEffect, liftEffect)
13import Test.Assert (assertEqual')
19import Test.Assert (assertEqual')
20+import Web.Encoding.TextEncoder (encode)
21+import Web.Encoding.TextEncoder as TextEncoder
14
22
15asBytes :: ArrayBuffer -> Effect (Array Int)
23asBytes :: ArrayBuffer -> Effect (Array Int)
16asBytes x = do
24asBytes x = do
@@ -20,80 +28,82 @@ asBytes x = do
20putTest :: String -> Array Int -> PutM Effect Unit -> Effect Unit
28putTest :: String -> Array Int -> PutM Effect Unit -> Effect Unit
21putTest label expected put = do
29putTest label expected put = do
22 actual <- asBytes =<< execPut put
30 actual <- asBytes =<< execPut put
23− assertEqual' label {actual,expected}
31+ assertEqual' label { actual, expected }
24
32
25buildTest :: String -> Array Int -> Effect Builder -> Effect Unit
33buildTest :: String -> Array Int -> Effect Builder -> Effect Unit
26buildTest label expected bldr = do
34buildTest label expected bldr = do
27 actual <- asBytes =<< execBuilder =<< bldr
35 actual <- asBytes =<< execBuilder =<< bldr
28− assertEqual' label {actual,expected}
36+ assertEqual' label { actual, expected }
29
37
30main :: Effect Unit
38main :: Effect Unit
31main = do
39main = do
32− putTest "Test 0" [6,7,8] $ do
40+ putTest "Test 0" [ 6, 7, 8 ] do
33 putInt8 6
41 putInt8 6
34 putInt8 7
42 putInt8 7
35 putInt8 8
43 putInt8 8
36
44
37− putTest "Test 1" [255,254] $ do
45+ putTest "Test 1" [ 255, 254 ] do
38 putInt16be (-2)
46 putInt16be (-2)
39
47
40− putTest "Test 3" [3,0,0,0] $ do
48+ putTest "Test 3" [ 3, 0, 0, 0 ] do
41 putInt32le 3
49 putInt32le 3
42
50
43− buildTest "Test 4" [1,2,3,4] $ do
51+ buildTest "Test 4" [ 1, 2, 3, 4 ] do
44− b1 <- encodeInt8 1
52+ b1 <- Buff <$> encodeInt8 1
45− b2 <- encodeInt8 2
53+ b2 <- Buff <$> encodeInt8 2
46− b3 <- encodeInt8 3
54+ b3 <- Buff <$> encodeInt8 3
47− b4 <- encodeInt8 4
55+ b4 <- Buff <$> encodeInt8 4
48 pure $ singleton b1 <> singleton b2 <> singleton b3 <> singleton b4
56 pure $ singleton b1 <> singleton b2 <> singleton b3 <> singleton b4
49
57
50− buildTest "Test 5" [1,2,3,4] $ do
58+ buildTest "Test 5" [ 1, 2, 3, 4 ] do
51− b1 <- encodeInt8 1
59+ b1 <- Buff <$> encodeInt8 1
52− b2 <- encodeInt8 2
60+ b2 <- Buff <$> encodeInt8 2
53− b3 <- encodeInt8 3
61+ b3 <- Buff <$> encodeInt8 3
54− b4 <- encodeInt8 4
62+ b4 <- Buff <$> encodeInt8 4
55 pure $ singleton b1 <>> singleton b2 <>> singleton b3 <>> singleton b4
63 pure $ singleton b1 <>> singleton b2 <>> singleton b3 <>> singleton b4
56
64
57− buildTest "Test 6" [1,2,3,4] $ do
65+ buildTest "Test 6" [ 1, 2, 3, 4 ] do
58− b1 <- encodeInt8 1
66+ b1 <- Buff <$> encodeInt8 1
59− b2 <- encodeInt8 2
67+ b2 <- Buff <$> encodeInt8 2
60− b3 <- encodeInt8 3
68+ b3 <- Buff <$> encodeInt8 3
61− b4 <- encodeInt8 4
69+ b4 <- Buff <$> encodeInt8 4
62 pure $ singleton b1 <> (singleton b2 <> singleton b3) <> singleton b4
70 pure $ singleton b1 <> (singleton b2 <> singleton b3) <> singleton b4
63
71
64− buildTest "Test 6" [1,2,3,4,5,6,7,8] $ do
72+ buildTest "Test 6" [ 1, 2, 3, 4, 5, 6, 7, 8 ] do
65− b1 <- encodeInt8 1
73+ b1 <- Buff <$> encodeInt8 1
66− b2 <- encodeInt8 2
74+ b2 <- Buff <$> encodeInt8 2
67− b3 <- encodeInt8 3
75+ b3 <- Buff <$> encodeInt8 3
68− b4 <- encodeInt8 4
76+ b4 <- Buff <$> encodeInt8 4
69− b5 <- encodeInt8 5
77+ b5 <- Buff <$> encodeInt8 5
70− b6 <- encodeInt8 6
78+ b6 <- Buff <$> encodeInt8 6
71− b7 <- encodeInt8 7
79+ b7 <- Buff <$> encodeInt8 7
72− b8 <- encodeInt8 8
80+ b8 <- Buff <$> encodeInt8 8
73 pure $ singleton b1 <> (singleton b2 <> singleton b3) <> singleton b4
81 pure $ singleton b1 <> (singleton b2 <> singleton b3) <> singleton b4
82+ <> singleton b5
74− <> singleton b5 <> (singleton b6 <> singleton b7) <> singleton b8
83+ <> (singleton b6 <> singleton b7)
75−
84+ <> singleton b8
85+
76− buildTest "Test 7" [1,2,3,4,5,6,7,8] $ do
86+ buildTest "Test 7" [ 1, 2, 3, 4, 5, 6, 7, 8 ] do
77− b1 <- encodeInt8 1
87+ b1 <- Buff <$> encodeInt8 1
78− b2 <- encodeInt8 2
88+ b2 <- Buff <$> encodeInt8 2
79− b3 <- encodeInt8 3
89+ b3 <- Buff <$> encodeInt8 3
80− b4 <- encodeInt8 4
90+ b4 <- Buff <$> encodeInt8 4
81− b5 <- encodeInt8 5
91+ b5 <- Buff <$> encodeInt8 5
82− b6 <- encodeInt8 6
92+ b6 <- Buff <$> encodeInt8 6
83− b7 <- encodeInt8 7
93+ b7 <- Buff <$> encodeInt8 7
84− b8 <- encodeInt8 8
94+ b8 <- Buff <$> encodeInt8 8
85 pure $ ((singleton b1 <> singleton b2) <> (singleton b3 <> singleton b4))
95 pure $ ((singleton b1 <> singleton b2) <> (singleton b3 <> singleton b4))
86− <> ((singleton b5 <> singleton b6) <> (singleton b7 <> singleton b8))
96+ <> ((singleton b5 <> singleton b6) <> (singleton b7 <> singleton b8))
87
97
88− buildTest "Test 8" [1,2,3,4] $ do
98+ buildTest "Test 8" [ 1, 2, 3, 4 ] do
89− b1 <- encodeInt8 1
99+ b1 <- Buff <$> encodeInt8 1
90− b2 <- encodeInt8 2
100+ b2 <- Buff <$> encodeInt8 2
91− b3 <- encodeInt8 3
101+ b3 <- Buff <$> encodeInt8 3
92− b4 <- encodeInt8 4
102+ b4 <- Buff <$> encodeInt8 4
93 pure $ (cons b1 (singleton b2)) <> (cons b3 (singleton b4))
103 pure $ (cons b1 (singleton b2)) <> (cons b3 (singleton b4))
94
104
95− putTest "Test 9" [1,2,2,3,4] $ do
105+ putTest "Test 9" [ 1, 2, 2, 3, 4 ] do
96− x <- subBuilder $ do
106+ x <- subBuilder do
97 putInt8 3
107 putInt8 3
98 putInt8 4
108 putInt8 4
99 putInt8 1
109 putInt8 1
@@ -101,15 +111,36 @@ main = do
101 putInt8 $ length x
111 putInt8 $ length x
102 tell x
112 tell x
103
113
104− buildTest "Test 10" [1,2,3,4,5,6,7,8] $ do
114+ buildTest "Test 10" [ 1, 2, 3, 4, 5, 6, 7, 8 ] do
105− b1 <- DV.whole <$> encodeInt8 1
115+ b1 <- View <$> DV.whole <$> encodeInt8 1
106− b2 <- DV.whole <$> encodeInt8 2
116+ b2 <- View <$> DV.whole <$> encodeInt8 2
107− b3 <- DV.whole <$> encodeInt8 3
117+ b3 <- View <$> DV.whole <$> encodeInt8 3
108− b4 <- DV.whole <$> encodeInt8 4
118+ b4 <- View <$> DV.whole <$> encodeInt8 4
109− b5 <- DV.whole <$> encodeInt8 5
119+ b5 <- View <$> DV.whole <$> encodeInt8 5
110− b6 <- DV.whole <$> encodeInt8 6
120+ b6 <- View <$> DV.whole <$> encodeInt8 6
111− b7 <- DV.whole <$> encodeInt8 7
121+ b7 <- View <$> DV.whole <$> encodeInt8 7
112− b8 <- DV.whole <$> encodeInt8 8
122+ b8 <- View <$> DV.whole <$> encodeInt8 8
113− pure $ ((singleton_ b1 <> singleton_ b2) <> (singleton_ b3 <> singleton_ b4))
123+ pure $ ((singleton b1 <> singleton b2) <> (singleton b3 <> singleton b4))
114− <> ((singleton_ b5 <> singleton_ b6) <> (singleton_ b7 <> singleton_ b8))
124+ <> ((singleton b5 <> singleton b6) <> (singleton b7 <> singleton b8))
115−
125+
126+ let stackblower = Array.replicate 20000 2
127+ putTest "Stack test" stackblower do
128+ foldRecM (\_ x -> putInt8 x) unit stackblower
129+
130+ do
131+ let
132+ putStringUtf8 :: forall m. MonadEffect m => String -> PutM m Unit
133+ putStringUtf8 s = do
134+ textEncoder <- liftEffect TextEncoder.new
135+ let stringbuf = buffer $ encode s textEncoder
136+ -- Put a 32-bit big-endian length prefix for the length of the utf8 string, in bytes.
137+ putInt32be $ byteLength stringbuf
138+ putArrayBuffer stringbuf
139+
140+ arraybuffer :: ArrayBuffer <- execPut $ putStringUtf8 "🦝"
141+ view :: Int8Array <- AV.whole arraybuffer
142+ viewarray <- AV.toArray view
143+ assertEqual' "utf-8 test"
144+ { actual: viewarray
145+ , expected: [ 0, 0, 0, 4, -16, -97, -90, -99 ]
146+ }