@@ -5,47 +5,73 @@
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.
8−-- | For other operations for working with `ArrayBuffer`, see
8+-- | For operations for working with `ArrayBuffer`, see
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 __arraybuffer__.
11+-- | in package __purescript-arraybuffer__.
12module Data.ArrayBuffer.Builder
12module Data.ArrayBuffer.Builder
15− , execPutM
15+, execPutM
16− , execPut
16+, execPut
17− , subBuilder
17+, subBuilder
18− , putArrayBuffer
18+, putArrayBuffer
19− , putDataView
19+, putDataView
21− , putUint8
20+, putUint8
22− , putInt8
21+, putInt8
23− , putUint16be
22+, putUint16be
24− , putUint16le
23+, putUint16le
25− , putInt16be
24+, putInt16be
26− , putInt16le
25+, putInt16le
27− , putUint32be
26+, putUint32be
28− , putUint32le
27+, putUint32le
29− , putInt32be
28+, putInt32be
30− , putInt32le
29+, putInt32le
31− , putFloat32be
30+, putFloat32be
32− , putFloat32le
31+, putFloat32le
33− , putFloat64be
32+, putFloat64be
34− , putFloat64le
33+, putFloat64le
35− , module Data.ArrayBuffer.Builder.Internal
34+, Builder
38import Prelude
63import Prelude
40−import Control.Monad.Rec.Class (class MonadRec)
41import Control.Monad.Writer.Trans (WriterT, execWriterT, tell, lift)
65import Control.Monad.Writer.Trans (WriterT, execWriterT, tell, lift)
42−import Data.ArrayBuffer.Builder.Internal (Builder, DataBuff(..), length, toView, (<>>))
66+import Data.ArrayBuffer.ArrayBuffer as AB
67+import Data.ArrayBuffer.DataView as DV
43−import Data.ArrayBuffer.Builder.Internal as Internal
68+import Data.ArrayBuffer.Typed as AT
44−import Data.ArrayBuffer.Types (ArrayBuffer, DataView)
69+import Data.ArrayBuffer.Types (ArrayBuffer, Uint8Array, DataView, ByteLength)
45import Data.Float32 (Float32)
70import Data.Float32 (Float32)
71+import Data.Maybe (Maybe(..))
46import Data.UInt (UInt)
72import Data.UInt (UInt)
47import Effect (Effect)
73import Effect (Effect)
48−import Effect.Class (class MonadEffect)
74+import Effect.Class (class MonadEffect, liftEffect)
50-- | The `PutM` monad is a `WriterT Builder` transformer monad which
76-- | The `PutM` monad is a `WriterT Builder` transformer monad which
51-- | gives us do-notation for the `Builder` monoid. The base monad must be
77-- | gives us do-notation for the `Builder` monoid. The base monad must be
@@ -58,11 +84,149 @@ type PutM = WriterT Builder
58-- | The `PutM` type reified to `Effect`, in other words `WriterT Builder Effect`.
84-- | The `PutM` type reified to `Effect`, in other words `WriterT Builder Effect`.
59type Put = PutM Effect
85type Put = PutM Effect
61−-- | Build an `ArrayBuffer` with do-notation in any `MonadEffect`. *O(n)* Stack-safe.
87+-- | Monoidal builder for `ArrayBuffer`s.
62−execPutM :: forall m. MonadEffect m => MonadRec m => PutM m Unit -> m ArrayBuffer
88+-- |
63−execPutM = Internal.execBuilder <=< execWriterT
89+-- | We can add two types of things to the `Builder`: `ArrayBuffer`s and
92+-- | to add a `DataView` to a `Builder` when we’re adding a large slice of data
93+-- | from some other `ArrayBuffer`, so that we don’t
94+-- | need an extra intermediate copy of the slice.
95+-- | Our convention is that functions which add `DataView`s to
96+-- | the `Builder` are suffixed with underscore (`_`).
98+-- | ### Left-associative `<>>` append operator
100+-- | __TL;DR__ You probably don't want to use the `Builder` monoid directly
101+-- | in your code, it’s better to use the `PutM` monad with do-notation instead.
103+-- | The `Builder` monoid in this library is efficient when we `snoc` single
104+-- | items onto the end of it, or when we only `cons` single items to the
105+-- | beginning, but it can be less efficient when we are mixing `cons` and
107+-- | This matters when we're appending three or more `Builder`s
108+-- | in one associative expression, because the
109+-- | `Semigroup` append operator `<>` is right-associative.
111+-- | To solve this, we provide an operator `<>>` for appending `Builders`.
112+-- | `<>>` is exactly the same as `<>`, but left-associative.
113+-- | This only matters when we're chaining together three
114+-- | or more `Builder`s in a single associative expression.
117+-- | builder₁ <> builder₂ <> builder₃
119+-- | we should always prefer to
122+-- | builder₁ <>> builder₂ <>> builder₃
124+-- | so that we get the efficient
125+-- | `snoc`ing of `Builder`s.
127+-- | If we build our `ArrayBuffer`s with the `PutM` monad instead of appending by
128+-- | using the `Semigroup` instance of `Builder`, then we always get the efficient
132+ = Node Builder Bytes Builder
135+instance semigroupBuilder :: Semigroup Builder where
136+ append Null Null = Null
139+ append l (Node Null rx rr) = Node l rx rr -- this is the snoc case
140+ append (Node ll lx Null) r = Node ll lx r -- found the rightmost Null
142+ append (Node ll lx lr) r = Node ll lx $ append lr r -- search down the right
143+ -- side of the left tree
144+ -- to find the rightmost
147+infixl 5 append as <>>
149+instance monoidBuilder :: Monoid Builder where
152+-- | For distinguishing between `ArrayBuffer` and `DataView`.
154+ = ByteBuff ArrayBuffer
157+toView :: Bytes -> DataView
158+toView (ByteBuff ab) = DV.whole ab
159+toView (ByteView dv) = dv
161+-- | `Builder` is not an instance of `Foldable` because `Builder` is monomorphic.
162+foldl :: forall b. (b -> Bytes -> b) -> b -> Builder -> b
164+foldl f a (Node l x r) = foldl f (f (foldl f a l) x) r
166+-- | Monomorphic foldM copied from
167+-- | [`Data.Foldable.foldM`](https://pursuit.purescript.org/packages/purescript-foldable-traversable/4.1.1/docs/Data.Foldable#v:foldM)
168+foldM :: forall b m. (Monad m) => (b -> Bytes -> m b) -> b -> Builder -> m b
169+foldM f a0 = foldl (\ma b -> ma >>= flip f b) (pure a0)
171+-- | Construct a `Builder` with a single `ArrayBuffer`. *O(1)*
172+singleton :: ArrayBuffer -> Builder
173+singleton buf = Node Null (ByteBuff buf) Null
175+-- | Construct a `Builder` with a single `DataView`. *O(1)*
176+singleton_ :: DataView -> Builder
177+singleton_ buf = Node Null (ByteView buf) Null
179+-- | Prepend an `ArrayBuffer` to the beginning of the `Builder`. *O(1)*
180+cons :: ArrayBuffer -> Builder -> Builder
181+cons x bs = Node Null (ByteBuff x) bs
183+-- | Prepend a `DataView` to the beginning of the `Builder`. *O(1)*
184+cons_ :: DataView -> Builder -> Builder
185+cons_ x bs = Node Null (ByteView x) bs
187+-- | Append an `ArrayBuffer` to the end of the `Builder`. *O(1)*
188+snoc :: Builder -> ArrayBuffer -> Builder
189+snoc bs x = Node bs (ByteBuff x) Null
191+-- | Append a `DataView` to the end of the `Builder`. *O(1)*
192+snoc_ :: Builder -> DataView -> Builder
193+snoc_ bs x = Node bs (ByteView x) Null
195+-- | Calculate the total byte length of the `Builder`. *O(n)*
196+length :: Builder -> ByteLength
197+length bldr = foldl (\b a -> b + len a) 0 bldr
199+ len (ByteBuff ab) = AB.byteLength ab
200+ len (ByteView dv) = DV.byteLength dv
202+-- | Build a single `ArrayBuffer` from a `Builder`. *O(n)*
203+execBuilder :: forall m. (MonadEffect m) => Builder -> m ArrayBuffer
204+execBuilder bldr = do
205+ let buflen = length bldr
206+ -- Allocate the final ArrayBuffer
207+ buf <- liftEffect $ AB.empty buflen
208+ -- Then copy each ArrayBuffer into the final ArrayBuffer.
209+ -- To memcpy from one ArrayBuffer to another, apparently we have to first view
210+ -- both ArrayBuffers as ArrayView (Typed Array).
211+ newview <- liftEffect (AT.whole buf :: Effect Uint8Array)
214+ aview <- liftEffect $ toUint8Array a
215+ _ <- liftEffect $ AT.setTyped newview (Just offset) aview
216+ pure $ offset + AT.byteLength aview
220+ toUint8Array :: Bytes -> Effect Uint8Array
221+ toUint8Array (ByteBuff ab) = AT.whole ab
222+ toUint8Array (ByteView dv) =
223+ AT.part (DV.buffer dv) (DV.byteOffset dv) (DV.byteLength dv)
65−-- | Build an `ArrayBuffer` with do-notation in `Effect`. *O(n)* Stack-safe.
225+-- | Build an `ArrayBuffer` with do-notation in any `MonadEffect`. *O(n)*
226+execPutM :: forall m. (MonadEffect m) => PutM m Unit -> m ArrayBuffer
227+execPutM = execBuilder <=< execWriterT
229+-- | Build an `ArrayBuffer` with do-notation. *O(n)*
66execPut :: Put Unit -> Effect ArrayBuffer
230execPut :: Put Unit -> Effect ArrayBuffer
67execPut = execPutM
231execPut = execPutM
@@ -75,6 +239,7 @@ execPut = execPutM
76-- | In a `PutM` monad do-block, we can
240-- | In a `PutM` monad do-block, we can
80-- | messageBuilder <- subBuilder $ do
245-- | messageBuilder <- subBuilder $ do
@@ -84,73 +249,244 @@ execPut = execPutM
84-- | putInt32be $ length messageBuilder
249-- | putInt32be $ length messageBuilder
85-- | tell messageBuilder
250-- | tell messageBuilder
87−subBuilder :: forall m. Monad m => PutM m Unit -> PutM m Builder
252+subBuilder :: forall m. (MonadEffect m) => PutM m Unit -> PutM m Builder
88subBuilder = lift <<< execWriterT
253subBuilder = lift <<< execWriterT
90-- | Append an `ArrayBuffer` to the builder.
255-- | Append an `ArrayBuffer` to the builder.
91−putArrayBuffer :: forall m. Monad m => ArrayBuffer -> PutM m Unit
256+putArrayBuffer :: forall m. (MonadEffect m) => ArrayBuffer -> PutM m Unit
92−putArrayBuffer = tell <<< Internal.singleton <<< Internal.Buff
257+putArrayBuffer = tell <<< singleton
94-- | Append a `DataView` to the builder.
259-- | Append a `DataView` to the builder.
95−putDataView :: forall m. Monad m => DataView -> PutM m Unit
260+putDataView :: forall m. (MonadEffect m) => DataView -> PutM m Unit
96−putDataView = tell <<< Internal.singleton <<< Internal.View
261+putDataView = tell <<< singleton_
98−-- | Append either an `ArrayBuffer` or a `DataView` to the builder.
263+-- | Serialize an 8-bit unsigned integer (byte) into a new `ArrayBuffer`.
99−putDataBuff :: forall m. Monad m => DataBuff -> PutM m Unit
264+encodeUint8 :: forall m. (MonadEffect m) => UInt -> m ArrayBuffer
100−putDataBuff = tell <<< Internal.singleton
265+encodeUint8 x = do
266+ buf <- liftEffect $ AB.empty 1
267+ _ <- liftEffect $ DV.setUint8 (DV.whole buf) 0 x
102-- | Append an 8-bit unsigned integer (byte) to the builder.
270-- | Append an 8-bit unsigned integer (byte) to the builder.
103−putUint8 :: forall m. MonadEffect m => UInt -> PutM m Unit
271+putUint8 :: forall m. (MonadEffect m) => UInt -> PutM m Unit
104−putUint8 = putArrayBuffer <=< Internal.encodeUint8
272+putUint8 = putArrayBuffer <=< encodeUint8
274+-- | Serialize an 8-bit two’s-complement signed integer (char) into a new `ArrayBuffer`.
275+encodeInt8 :: forall m. (MonadEffect m) => Int -> m ArrayBuffer
277+ buf <- liftEffect $ AB.empty 1
278+ _ <- liftEffect $ DV.setInt8 (DV.whole buf) 0 x
106-- | Append an 8-bit two’s-complement signed integer (char) to the builder.
281-- | Append an 8-bit two’s-complement signed integer (char) to the builder.
107−putInt8 :: forall m. MonadEffect m => Int -> PutM m Unit
282+putInt8 :: forall m. (MonadEffect m) => Int -> PutM m Unit
108−putInt8 = putArrayBuffer <=< Internal.encodeInt8
283+putInt8 = putArrayBuffer <=< encodeInt8
285+-- | Serialize a 16-bit big-endian unsigned integer into a new `ArrayBuffer`.
286+encodeUint16be :: forall m. (MonadEffect m) => UInt -> m ArrayBuffer
287+encodeUint16be x = do
288+ buf <- liftEffect $ AB.empty 2
289+ _ <- liftEffect $ DV.setUint16be (DV.whole buf) 0 x
110-- | Append a 16-bit big-endian unsigned integer to the builder.
292-- | Append a 16-bit big-endian unsigned integer to the builder.
111−putUint16be :: forall m. MonadEffect m => UInt -> PutM m Unit
293+putUint16be :: forall m. (MonadEffect m) => UInt -> PutM m Unit
112−putUint16be = putArrayBuffer <=< Internal.encodeUint16be
294+putUint16be = putArrayBuffer <=< encodeUint16be
296+-- | Serialize a 16-bit little-endian unsigned integer into a new `ArrayBuffer`.
297+encodeUint16le :: forall m. (MonadEffect m) => UInt -> m ArrayBuffer
298+encodeUint16le x = do
299+ buf <- liftEffect $ AB.empty 2
300+ _ <- liftEffect $ DV.setUint16le (DV.whole buf) 0 x
114-- | Append a 16-bit little-endian unsigned integer to the builder.
303-- | Append a 16-bit little-endian unsigned integer to the builder.
115−putUint16le :: forall m. MonadEffect m => UInt -> PutM m Unit
304+putUint16le :: forall m. (MonadEffect m) => UInt -> PutM m Unit
116−putUint16le = putArrayBuffer <=< Internal.encodeUint16le
305+putUint16le = putArrayBuffer <=< encodeUint16le
307+-- | Serialize a 16-bit big-endian two’s-complement signed integer into a new `ArrayBuffer`.
308+encodeInt16be :: forall m. (MonadEffect m) => Int -> m ArrayBuffer
310+ buf <- liftEffect $ AB.empty 2
311+ _ <- liftEffect $ DV.setInt16be (DV.whole buf) 0 x
118-- | Append a 16-bit big-endian two’s-complement signed integer to the builder.
314-- | Append a 16-bit big-endian two’s-complement signed integer to the builder.
119−putInt16be :: forall m. MonadEffect m => Int -> PutM m Unit
315+putInt16be :: forall m. (MonadEffect m) => Int -> PutM m Unit
120−putInt16be = putArrayBuffer <=< Internal.encodeInt16be
316+putInt16be = putArrayBuffer <=< encodeInt16be
318+-- | Serialize a 16-bit little-endian two’s-complement signed integer into a new `ArrayBuffer`.
319+encodeInt16le :: forall m. (MonadEffect m) => Int -> m ArrayBuffer
321+ buf <- liftEffect $ AB.empty 2
322+ _ <- liftEffect $ DV.setInt16le (DV.whole buf) 0 x
122-- | Append a 16-bit little-endian two’s-complement signed integer to the builder.
325-- | Append a 16-bit little-endian two’s-complement signed integer to the builder.
123−putInt16le :: forall m. MonadEffect m => Int -> PutM m Unit
326+putInt16le :: forall m. (MonadEffect m) => Int -> PutM m Unit
124−putInt16le = putArrayBuffer <=< Internal.encodeInt16le
327+putInt16le = putArrayBuffer <=< encodeInt16le
329+-- | Serialize a 32-bit big-endian unsigned integer into a new `ArrayBuffer`.
330+encodeUint32be :: forall m. (MonadEffect m) => UInt -> m ArrayBuffer
331+encodeUint32be x = do
332+ buf <- liftEffect $ AB.empty 4
333+ _ <- liftEffect $ DV.setUint32be (DV.whole buf) 0 x
126-- | Append a 32-bit big-endian unsigned integer to the builder.
336-- | Append a 32-bit big-endian unsigned integer to the builder.
127−putUint32be :: forall m. MonadEffect m => UInt -> PutM m Unit
337+putUint32be :: forall m. (MonadEffect m) => UInt -> PutM m Unit
128−putUint32be = putArrayBuffer <=< Internal.encodeUint32be
338+putUint32be = putArrayBuffer <=< encodeUint32be
340+-- | Serialize a 32-bit little-endian unsigned integer into a new `ArrayBuffer`.
341+encodeUint32le :: forall m. (MonadEffect m) => UInt -> m ArrayBuffer
342+encodeUint32le x = do
343+ buf <- liftEffect $ AB.empty 4
344+ _ <- liftEffect $ DV.setUint32le (DV.whole buf) 0 x
130-- | Append a 32-bit little-endian unsigned integer to the builder.
347-- | Append a 32-bit little-endian unsigned integer to the builder.
131−putUint32le :: forall m. MonadEffect m => UInt -> PutM m Unit
348+putUint32le :: forall m. (MonadEffect m) => UInt -> PutM m Unit
132−putUint32le = putArrayBuffer <=< Internal.encodeUint32le
349+putUint32le = putArrayBuffer <=< encodeUint32le
351+-- | Serialize a 32-bit big-endian two’s-complement signed integer into a new `ArrayBuffer`.
352+encodeInt32be :: forall m. (MonadEffect m) => Int -> m ArrayBuffer
354+ buf <- liftEffect $ AB.empty 4
355+ _ <- liftEffect $ DV.setInt32be (DV.whole buf) 0 x
134-- | Append a 32-bit big-endian two’s-complement signed integer to the builder.
358-- | Append a 32-bit big-endian two’s-complement signed integer to the builder.
135−putInt32be :: forall m. MonadEffect m => Int -> PutM m Unit
359+putInt32be :: forall m. (MonadEffect m) => Int -> PutM m Unit
136−putInt32be = putArrayBuffer <=< Internal.encodeInt32be
360+putInt32be = putArrayBuffer <=< encodeInt32be
362+-- | Serialize a 32-bit little-endian two’s-complement signed integer into a new `ArrayBuffer`.
363+encodeInt32le :: forall m. (MonadEffect m) => Int -> m ArrayBuffer
365+ buf <- liftEffect $ AB.empty 4
366+ _ <- liftEffect $ DV.setInt32le (DV.whole buf) 0 x
138-- | Append a 32-bit little-endian two’s-complement signed integer to the builder.
369-- | Append a 32-bit little-endian two’s-complement signed integer to the builder.
139−putInt32le :: forall m. MonadEffect m => Int -> PutM m Unit
370+putInt32le :: forall m. (MonadEffect m) => Int -> PutM m Unit
140−putInt32le = putArrayBuffer <=< Internal.encodeInt32le
371+putInt32le = putArrayBuffer <=< encodeInt32le
373+-- | Serialize a 32-bit big-endian IEEE single-precision float into a new `ArrayBuffer`.
374+encodeFloat32be :: forall m. (MonadEffect m) => Float32 -> m ArrayBuffer
375+encodeFloat32be x = do
376+ buf <- liftEffect $ AB.empty 4
377+ _ <- liftEffect $ DV.setFloat32be (DV.whole buf) 0 x
142-- | Append a 32-bit big-endian IEEE single-precision float to the builder.
380-- | Append a 32-bit big-endian IEEE single-precision float to the builder.
143−putFloat32be :: forall m. MonadEffect m => Float32 -> PutM m Unit
381+putFloat32be :: forall m. (MonadEffect m) => Float32 -> PutM m Unit
144−putFloat32be = putArrayBuffer <=< Internal.encodeFloat32be
382+putFloat32be = putArrayBuffer <=< encodeFloat32be
384+-- | Serialize a 32-bit little-endian IEEE single-precision float into a new `ArrayBuffer`.
385+encodeFloat32le :: forall m. (MonadEffect m) => Float32 -> m ArrayBuffer
386+encodeFloat32le x = do
387+ buf <- liftEffect $ AB.empty 4
388+ _ <- liftEffect $ DV.setFloat32le (DV.whole buf) 0 x
146-- | Append a 32-bit little-endian IEEE single-precision float to the builder.
391-- | Append a 32-bit little-endian IEEE single-precision float to the builder.
147−putFloat32le :: forall m. MonadEffect m => Float32 -> PutM m Unit
392+putFloat32le :: forall m. (MonadEffect m) => Float32 -> PutM m Unit
148−putFloat32le = putArrayBuffer <=< Internal.encodeFloat32le
393+putFloat32le = putArrayBuffer <=< encodeFloat32le
395+-- | Serialize a 64-bit big-endian IEEE double-precision float into a new `ArrayBuffer`.
396+encodeFloat64be :: forall m. (MonadEffect m) => Number -> m ArrayBuffer
397+encodeFloat64be x = do
398+ buf <- liftEffect $ AB.empty 8
399+ _ <- liftEffect $ DV.setFloat64be (DV.whole buf) 0 x
150-- | Append a 64-bit big-endian IEEE double-precision float to the builder.
402-- | Append a 64-bit big-endian IEEE double-precision float to the builder.
151−putFloat64be :: forall m. MonadEffect m => Number -> PutM m Unit
403+putFloat64be :: forall m. (MonadEffect m) => Number -> PutM m Unit
152−putFloat64be = putArrayBuffer <=< Internal.encodeFloat64be
404+putFloat64be = putArrayBuffer <=< encodeFloat64be
406+-- | Serialize a 64-bit little-endian IEEE double-precision float into a new `ArrayBuffer`.
407+encodeFloat64le :: forall m. (MonadEffect m) => Number -> m ArrayBuffer
408+encodeFloat64le x = do
409+ buf <- liftEffect $ AB.empty 8
410+ _ <- liftEffect $ DV.setFloat64le (DV.whole buf) 0 x
154-- | Append a 64-bit little-endian IEEE double-precision float to the builder.
413-- | Append a 64-bit little-endian IEEE double-precision float to the builder.
155−putFloat64le :: forall m. MonadEffect m => Number -> PutM m Unit
414+putFloat64le :: forall m. (MonadEffect m) => Number -> PutM m Unit
156−putFloat64le = putArrayBuffer <=< Internal.encodeFloat64le
415+putFloat64le = putArrayBuffer <=< encodeFloat64le
418+-- ## Implementation Details
420+-- We want our `Builder` to be a data structure with
421+-- * *O(1)* monoid append
424+-- Our `Builder` implementation is an unbalanced binary tree.
426+-- For monoid `append`, what we actually get is *O(1)* when either the
427+-- left or right tree is a singleton. If that's not true, then in the
428+-- unlikely worst case `append` might be *O(n)*.
430+-- `Builder` is optimized for what we consider to be normal usage, that is,
431+-- `snoc`ing singleton elements to the end of the `Builder`.
433+-- If a Builder is built entirely by `snoc`ing, it will look like a
434+-- left-only binary tree, a.k.a. a linked list.
446+-- If two of these `snoc`-built trees are `append`ed, then the new tree
461+-- This is all similar to
462+-- [__bytestring-tree-builder__](https://hackage.haskell.org/package/bytestring-tree-builder)
464+-- [`Tree`](https://hackage.haskell.org/package/bytestring-tree-builder-0.2.7.3/docs/src/ByteString.TreeBuilder.Tree.html#Tree)
465+-- structure in __bytestring-tree-builder__ only carries values in its
466+-- leaves, which is how it achieves *O(1)* appending, at the cost of
467+-- a higher coefficient time factor on the fold.
469+-- We hope that this implementation is fairly fast, but we
470+-- haven't chosen this implementation because it's fast, we've chosen
471+-- this implementation because it's simple.
472+-- If someone wants to create a fast Purescript ArrayBuffer serialization
473+-- library, then they can benchmark against this one to prove that the new
476+-- One relatively cheap and simple performance improvement for this library would be to
477+-- remove the Null constructor of `Builder` and instead use Javascript nulls.
478+-- UPDATE Actually no, we need the Null constructor for the Monoid instance.
480+-- In the longer term, it might make sense to try to change the Builder so
481+-- that it works like the
482+-- https://hackage.haskell.org/package/bytestring/docs/Data-ByteString-Builder.html
484+-- Here are some benchmarks of different Haskell ByteString builders
485+-- https://github.com/haskell-perf/strict-bytestring-builders
487+-- We've tried to design the API for this library with minimal assumptions,
488+-- so that if we want to change the Builder implementation later then we can.
490+-- Another way this could go:
491+-- https://kodimensional.dev/posts/2019-03-25-comonadic-builders