git.iko.soy/rowtype-yoga/purescript-arraybuffer-builder
on master help sign in
Add cons, length, subBuilder
commit9a795f5740d96fde435215239e42b7d75337e99d authorJames Brock <jamesbrock@gmail.com> dateThu 10 Sep 2020, 12:41 (6 years ago) parent92173c5
5 files changed+72−16
CHANGELOG.md+7 DEVELOPMENT.md+2−3 shell.nix+3−7 src/Data/ArrayBuffer/Builder.purs+41−5 test/Main.purs+19−1
CHANGELOG.mdnew file+7
@@ -0,0 +1,7 @@
1+## v1.1.0 2020-09-10
2+
3+Add and export
4+
5+* `cons :: ArrayBuffer -> Builder -> Builder`
6+* `subBuilder :: forall m. (MonadEffect m) => PutM m Unit -> PutM m Builder`
7+* `length :: Builder -> Int`
DEVELOPMENT.md+2−3
@@ -34,10 +34,9 @@ The test dependencies are specified seperately in `test.dhall`.
3434
3535```
3636[nix-shell:]$ spago bump-version --no-dry-run major
37−[nix-shell:]$ npm install pulp
38−[nix-shell:]$ node node_modules/pulp/index.js publish
37+[nix-shell:]$ pulp publish
3938```
4039
4140## Bower
4241
43−The `bower.json` is generated by running `spago bump-version`, do not edit it.
42+The `bower.json` is generated by running `spago bump-version`, do not edit it.
shell.nix+3−7
@@ -4,8 +4,8 @@ let
44 (pkgs.fetchFromGitHub {
55 owner = "justinwoo";
66 repo = "easy-purescript-nix";
7− rev = "0ba91d9aa9f7421f6bfe4895677159a8a999bf20";
8− sha256 = "1baq7mmd3vjas87f0gzlq83n2l1h3dlqajjqr7fgaazpa9xgzs7q";
7+ rev = "1ec689df0adf8e8ada7fcfcb513876307ea34226";
8+ sha256 = "12hk2zbjkrq2i5fs6xb3x254lnhm9fzkcxph0a7ngxyzfykvf4hi";
99 }) {
1010 inherit pkgs;
1111 };
@@ -14,13 +14,9 @@ pkgs.mkShell {
1414 buildInputs = [
1515 easy-ps.purs-0_13_8
1616 easy-ps.spago
17+ easy-ps.pulp
1718 pkgs.nodejs-13_x
1819 pkgs.nodePackages.bower
19− # pkgs.nodePackages.pulp
20− #
21− # The pulp nix derivation doesn't work, so to run pulp:
22− # $ npm install pulp
23− # $ node node_modules/pulp/index.js
2420 ];
2521 LC_ALL = "C.UTF-8"; # https://github.com/purescript/spago/issues/507
2622}
src/Data/ArrayBuffer/Builder.purs+41−5
@@ -14,6 +14,7 @@ module Data.ArrayBuffer.Builder
1414, Put
1515, execPutM
1616, execPut
17+, subBuilder
1718, putArrayBuffer
1819, putUint8
1920, putInt8
@@ -32,7 +33,9 @@ module Data.ArrayBuffer.Builder
3233, Builder
3334, (<>>)
3435, execBuilder
36+, length
3537, singleton
38+, cons
3639, snoc
3740, encodeUint8
3841, encodeInt8
@@ -53,7 +56,7 @@ where
5356
5457import Prelude
5558
56−import Control.Monad.Writer.Trans (WriterT, execWriterT, tell)
59+import Control.Monad.Writer.Trans (WriterT, execWriterT, tell, lift)
5760import Data.ArrayBuffer.ArrayBuffer as AB
5861import Data.ArrayBuffer.DataView as DV
5962import Data.ArrayBuffer.Typed as AT
@@ -67,10 +70,12 @@ import Effect.Class (class MonadEffect, liftEffect)
6770-- | The `PutM` monad is a `WriterT Builder` transformer monad which
6871-- | gives us do-notation for the `Builder` monoid. The base monad must be
6972-- | a `MonadEffect`.
73+-- |
74+-- | To append `Builder`s in this monad call `tell`, or any of the `put*`
75+-- | functions in this module.
7076type PutM = WriterT Builder
7177
72−-- | The `Put` monad is a `WriterT Builder Effect` monad which
73−-- | gives us do-notation for the `Builder` monoid.
78+-- | The `PutM` type reified to `Effect`, in other words `WriterT Builder Effect`.
7479type Put = PutM Effect
7580
7681-- | Monoidal builder for `ArrayBuffer`s.
@@ -143,10 +148,18 @@ foldM f a0 = foldl (\ma b -> ma >>= flip f b) (pure a0)
143148singleton :: ArrayBuffer -> Builder
144149singleton buf = Node Null buf Null
145150
146−-- | Add an `ArrayBuffer` to the end of the `Builder`. *O(1)*
151+-- | Prepend an `ArrayBuffer` to the beginning of the `Builder`. *O(1)*
152+cons :: ArrayBuffer -> Builder -> Builder
153+cons x bs = Node Null x bs
154+
155+-- | Append an `ArrayBuffer` to the end of the `Builder`. *O(1)*
147156snoc :: Builder -> ArrayBuffer -> Builder
148157snoc bs x = Node bs x Null
149158
159+-- | Calculate the total byte length of the `Builder`. *O(n)*
160+length :: Builder -> Int
161+length bldr = foldl (\b a -> b + AB.byteLength a) 0 bldr
162+
150163-- | Build a single `ArrayBuffer` from a `Builder`. *O(n)*
151164execBuilder :: forall m. (MonadEffect m) => Builder -> m ArrayBuffer
152165execBuilder bldr = do
@@ -173,6 +186,28 @@ execPutM = execBuilder <=< execWriterT
173186execPut :: Put Unit -> Effect ArrayBuffer
174187execPut = execPutM
175188
189+-- | Build up a sub-`Builder` without `tell`ing it to the `Writer` yet.
190+-- |
191+-- | One case where we might want to call a `subBuilder` is when
192+-- | serializing length-prefixed messages in some protocol. In that case,
193+-- | we must serialize the message first, calculate the message length,
194+-- | append the message length, and then append the message.
195+-- |
196+-- | In a `PutM` monad do-block, we can
197+-- |
198+-- |
199+-- | ```
200+-- | do
201+-- | messageBuilder <- subBuilder $ do
202+-- | putField1
203+-- | putField2
204+-- |
205+-- | putInt32 $ length messageBuilder
206+-- | tell messageBuilder
207+-- | ```
208+subBuilder :: forall m. (MonadEffect m) => PutM m Unit -> PutM m Builder
209+subBuilder = lift <<< execWriterT
210+
176211-- | Append an `ArrayBuffer` to the builder.
177212putArrayBuffer :: forall m. (MonadEffect m) => ArrayBuffer -> PutM m Unit
178213putArrayBuffer = tell <<< singleton
@@ -404,4 +439,5 @@ putFloat64le = putArrayBuffer <=< encodeFloat64le
404439-- so that if we want to change the Builder implementation later then we can.
405440--
406441-- Another way this could go:
407−-- https://kodimensional.dev/posts/2019-03-25-comonadic-builders
442+-- https://kodimensional.dev/posts/2019-03-25-comonadic-builders
443+
test/Main.purs+19−1
@@ -7,6 +7,7 @@ import Data.UInt as UInt
77import Effect (Effect)
88import Test.Assert (assertEqual')
99import Data.ArrayBuffer.Builder
10+import Control.Monad.Writer.Trans (tell)
1011
1112asBytes :: ArrayBuffer -> Effect (Array Int)
1213asBytes x = do
@@ -79,4 +80,21 @@ main = do
7980 b7 <- encodeInt8 7
8081 b8 <- encodeInt8 8
8182 pure $ ((singleton b1 <> singleton b2) <> (singleton b3 <> singleton b4))
82− <> ((singleton b5 <> singleton b6) <> (singleton b7 <> singleton b8))
83+ <> ((singleton b5 <> singleton b6) <> (singleton b7 <> singleton b8))
84+
85+ buildTest "Test 8" [1,2,3,4] $ do
86+ b1 <- encodeInt8 1
87+ b2 <- encodeInt8 2
88+ b3 <- encodeInt8 3
89+ b4 <- encodeInt8 4
90+ pure $ (cons b1 (singleton b2)) <> (cons b3 (singleton b4))
91+
92+ putTest "Test 9" [1,2,2,3,4] $ do
93+ x <- subBuilder $ do
94+ putInt8 3
95+ putInt8 4
96+ putInt8 1
97+ putInt8 2
98+ putInt8 $ length x
99+ tell x
100+