@@ -16,6 +16,7 @@ module Data.ArrayBuffer.Builder
17, subBuilder
17, subBuilder
18, putArrayBuffer
18, putArrayBuffer
21, putUint16be
22, putUint16be
@@ -32,11 +33,16 @@ module Data.ArrayBuffer.Builder
32, putFloat64le
33, putFloat64le
35, execBuilder
38, execBuilder
37, singleton
40, singleton
40, encodeUint8
46, encodeUint8
41, encodeInt8
47, encodeInt8
42, encodeUint16be
48, encodeUint16be
@@ -60,7 +66,7 @@ import Control.Monad.Writer.Trans (WriterT, execWriterT, tell, lift)
60import Data.ArrayBuffer.ArrayBuffer as AB
66import Data.ArrayBuffer.ArrayBuffer as AB
61import Data.ArrayBuffer.DataView as DV
67import Data.ArrayBuffer.DataView as DV
62import Data.ArrayBuffer.Typed as AT
68import Data.ArrayBuffer.Typed as AT
63−import Data.ArrayBuffer.Types (ArrayBuffer, Uint8Array)
69+import Data.ArrayBuffer.Types (ArrayBuffer, Uint8Array, DataView, ByteLength)
64import Data.Float32 (Float32)
70import Data.Float32 (Float32)
65import Data.Maybe (Maybe(..))
71import Data.Maybe (Maybe(..))
66import Data.UInt (UInt)
72import Data.UInt (UInt)
@@ -80,6 +86,15 @@ type Put = PutM Effect
81-- | Monoidal builder for `ArrayBuffer`s.
87-- | Monoidal builder for `ArrayBuffer`s.
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 (`_`).
83-- | ### Left-associative `<>>` append operator
98-- | ### Left-associative `<>>` append operator
85-- | __TL;DR__ You probably don't want to use the `Builder` monoid directly
100-- | __TL;DR__ You probably don't want to use the `Builder` monoid directly
@@ -114,7 +129,7 @@ type Put = PutM Effect
114-- | `snoc` case.
129-- | `snoc` case.
116data Builder
131data Builder
117− = Node Builder ArrayBuffer Builder
132+ = Node Builder Bytes Builder
120instance semigroupBuilder :: Semigroup Builder where
135instance semigroupBuilder :: Semigroup Builder where
@@ -134,36 +149,60 @@ infixl 5 append as <>>
134instance monoidBuilder :: Monoid Builder where
149instance monoidBuilder :: Monoid Builder where
135 mempty = Null
150 mempty = Null
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
137-- | `Builder` is not an instance of `Foldable` because `Builder` is monomorphic.
161-- | `Builder` is not an instance of `Foldable` because `Builder` is monomorphic.
138−foldl :: forall b. (b -> ArrayBuffer -> b) -> b -> Builder -> b
162+foldl :: forall b. (b -> Bytes -> b) -> b -> Builder -> b
139−foldl f a Null = a
163+foldl _ a Null = a
140foldl f a (Node l x r) = foldl f (f (foldl f a l) x) r
164foldl f a (Node l x r) = foldl f (f (foldl f a l) x) r
142-- | Monomorphic foldM copied from
166-- | Monomorphic foldM copied from
143-- | [`Data.Foldable.foldM`](https://pursuit.purescript.org/packages/purescript-foldable-traversable/4.1.1/docs/Data.Foldable#v:foldM)
167-- | [`Data.Foldable.foldM`](https://pursuit.purescript.org/packages/purescript-foldable-traversable/4.1.1/docs/Data.Foldable#v:foldM)
144−foldM :: forall b m. (Monad m) => (b -> ArrayBuffer -> m b) -> b -> Builder -> m b
168+foldM :: forall b m. (Monad m) => (b -> Bytes -> m b) -> b -> Builder -> m b
145foldM f a0 = foldl (\ma b -> ma >>= flip f b) (pure a0)
169foldM f a0 = foldl (\ma b -> ma >>= flip f b) (pure a0)
147-- | Construct a `Builder` with a single `ArrayBuffer`. *O(1)*
171-- | Construct a `Builder` with a single `ArrayBuffer`. *O(1)*
148singleton :: ArrayBuffer -> Builder
172singleton :: ArrayBuffer -> Builder
149−singleton buf = Node Null buf Null
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
151-- | Prepend an `ArrayBuffer` to the beginning of the `Builder`. *O(1)*
179-- | Prepend an `ArrayBuffer` to the beginning of the `Builder`. *O(1)*
152cons :: ArrayBuffer -> Builder -> Builder
180cons :: ArrayBuffer -> Builder -> Builder
153−cons x bs = Node Null x bs
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
155-- | Append an `ArrayBuffer` to the end of the `Builder`. *O(1)*
187-- | Append an `ArrayBuffer` to the end of the `Builder`. *O(1)*
156snoc :: Builder -> ArrayBuffer -> Builder
188snoc :: Builder -> ArrayBuffer -> Builder
157−snoc bs x = Node bs x Null
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
159-- | Calculate the total byte length of the `Builder`. *O(n)*
195-- | Calculate the total byte length of the `Builder`. *O(n)*
160−length :: Builder -> Int
196+length :: Builder -> ByteLength
161−length bldr = foldl (\b a -> b + AB.byteLength a) 0 bldr
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
163-- | Build a single `ArrayBuffer` from a `Builder`. *O(n)*
202-- | Build a single `ArrayBuffer` from a `Builder`. *O(n)*
164execBuilder :: forall m. (MonadEffect m) => Builder -> m ArrayBuffer
203execBuilder :: forall m. (MonadEffect m) => Builder -> m ArrayBuffer
165execBuilder bldr = do
204execBuilder bldr = do
166− let buflen = foldl (\b a -> b + AB.byteLength a) 0 bldr
205+ let buflen = length bldr
167 -- Allocate the final ArrayBuffer
206 -- Allocate the final ArrayBuffer
168 buf <- liftEffect $ AB.empty buflen
207 buf <- liftEffect $ AB.empty buflen
169 -- Then copy each ArrayBuffer into the final ArrayBuffer.
208 -- Then copy each ArrayBuffer into the final ArrayBuffer.
@@ -172,11 +211,16 @@ execBuilder bldr = do
172 newview <- liftEffect (AT.whole buf :: Effect Uint8Array)
211 newview <- liftEffect (AT.whole buf :: Effect Uint8Array)
173 _ <- foldM
212 _ <- foldM
174 ( \offset a -> do
213 ( \offset a -> do
175− aview <- liftEffect (AT.whole a :: Effect Uint8Array)
214+ aview <- liftEffect $ toUint8Array a
176 _ <- liftEffect $ AT.setTyped newview (Just offset) aview
215 _ <- liftEffect $ AT.setTyped newview (Just offset) aview
177− pure $ offset + AB.byteLength a
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)
181-- | Build an `ArrayBuffer` with do-notation in any `MonadEffect`. *O(n)*
225-- | Build an `ArrayBuffer` with do-notation in any `MonadEffect`. *O(n)*
182execPutM :: forall m. (MonadEffect m) => PutM m Unit -> m ArrayBuffer
226execPutM :: forall m. (MonadEffect m) => PutM m Unit -> m ArrayBuffer
@@ -212,6 +256,10 @@ subBuilder = lift <<< execWriterT
212putArrayBuffer :: forall m. (MonadEffect m) => ArrayBuffer -> PutM m Unit
256putArrayBuffer :: forall m. (MonadEffect m) => ArrayBuffer -> PutM m Unit
213putArrayBuffer = tell <<< singleton
257putArrayBuffer = tell <<< singleton
259+-- | Append a `DataView` to the builder.
260+putDataView :: forall m. (MonadEffect m) => DataView -> PutM m Unit
261+putDataView = tell <<< singleton_
215-- | Serialize an 8-bit unsigned integer (byte) into a new `ArrayBuffer`.
263-- | Serialize an 8-bit unsigned integer (byte) into a new `ArrayBuffer`.
216encodeUint8 :: forall m. (MonadEffect m) => UInt -> m ArrayBuffer
264encodeUint8 :: forall m. (MonadEffect m) => UInt -> m ArrayBuffer
217encodeUint8 x = do
265encodeUint8 x = do
@@ -379,7 +427,7 @@ putFloat64le = putArrayBuffer <=< encodeFloat64le
379-- left or right tree is a singleton. If that's not true, then in the
427-- left or right tree is a singleton. If that's not true, then in the
380-- unlikely worst case `append` might be *O(n)*.
428-- unlikely worst case `append` might be *O(n)*.
382−-- `Builder` is optimized what we consider to be normal usage, that is,
430+-- `Builder` is optimized for what we consider to be normal usage, that is,
383-- `snoc`ing singleton elements to the end of the `Builder`.
431-- `snoc`ing singleton elements to the end of the `Builder`.
385-- If a Builder is built entirely by `snoc`ing, it will look like a
433-- If a Builder is built entirely by `snoc`ing, it will look like a
@@ -427,6 +475,7 @@ putFloat64le = putArrayBuffer <=< encodeFloat64le
428-- One relatively cheap and simple performance improvement for this library would be to
476-- One relatively cheap and simple performance improvement for this library would be to
429-- remove the Null constructor of `Builder` and instead use Javascript nulls.
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.
431-- In the longer term, it might make sense to try to change the Builder so
480-- In the longer term, it might make sense to try to change the Builder so
432-- that it works like the
481-- that it works like the