Data.ArrayBuffer.Builder.Internal
- Package
- purescript-arraybuffer-builder
- Repository
- jamesdbrock/purescript-arraybuffer-builder
Internal module.
You probably don’t want to import anything from this module.
Implementation Details
We want our Builder to be a data structure with
- O(1) monoid append
- O(n) fold
Our Builder implementation is an unbalanced binary tree.
For monoid append, what we actually get is O(1) when either the
left or right tree is a singleton. If that's not true, then in the
unlikely worst case append might be O(n).
Builder is optimized for what we consider to be normal usage, that is,
snocing singleton elements to the end of the Builder.
If a Builder is built entirely by snocing, it will look like a
left-only binary tree, a.k.a. a linked list.
④
╱
③
╱
②
╱
①
If two of these snoc-built trees are appended, then the new tree
will look like
④
╱ ╲
③ ⑧
╱ ╱
② ⑦
╱ ╱
① ⑥
╱
⑤
This is all similar to
bytestring-tree-builder
, except that the
Tree
structure in bytestring-tree-builder only carries values in its
leaves, which is how it achieves consistent O(1) appending, at the cost of
a higher coefficient time factor on the fold.
We hope that this implementation is fairly fast, since it is similar to
bytestring-tree-builder
which “according to the benchmarks … beats all the alternatives.”
However, we
haven’t chosen this implementation because it’s fast, we've chosen
this implementation because it’s simple.
If someone wants to create a fast PureScript ArrayBuffer serialization
library, then they can benchmark against this one to prove that the new
one is fast.
One relatively cheap and simple performance improvement for this library would be to
remove the Null constructor of Builder and instead use Javascript nulls?
In the longer term, it might make sense to try to change the Builder so
that it works like the
https://hackage.haskell.org/package/bytestring/docs/Data-ByteString-Builder.html .
That’s the approach taken by
https://pursuit.purescript.org/packages/purescript-dynamic-buffers .
Here are some benchmarks of different Haskell ByteString builders https://github.com/haskell-perf/strict-bytestring-builders
We've tried to design the API for this library with minimal assumptions,
so that if we want to change the Builder implementation later then we can.
#Builder Source
data BuilderMonoidal builder for ArrayBuffers.
We can add two types of things to the Builder:
ArrayBufferDataView
We might prefer
to add a DataView to a Builder when we’re adding a large slice of data
from some other ArrayBuffer, so that we don’t
need an extra intermediate copy of the slice.
Constructors
Instances
#execBuilder Source
execBuilder :: forall m. MonadEffect m => Builder -> m ArrayBufferBuild a single ArrayBuffer from a Builder. O(n)
#length Source
length :: Builder -> ByteLengthCalculate the total byte length of the Builder, without actually
building it yet. O(n)
#foldM Source
foldM :: forall b m. Monad m => (b -> DataBuff -> m b) -> b -> Builder -> m bMonomorphic foldM copied from
Data.Foldable.foldM
#encodeUint8 Source
encodeUint8 :: forall m. MonadEffect m => UInt -> m ArrayBufferSerialize an 8-bit unsigned integer (byte) into a new ArrayBuffer.
#encodeInt8 Source
encodeInt8 :: forall m. MonadEffect m => Int -> m ArrayBufferSerialize an 8-bit two’s-complement signed integer (char) into a new ArrayBuffer.
#encodeUint16be Source
encodeUint16be :: forall m. MonadEffect m => UInt -> m ArrayBufferSerialize a 16-bit big-endian unsigned integer into a new ArrayBuffer.
#encodeUint16le Source
encodeUint16le :: forall m. MonadEffect m => UInt -> m ArrayBufferSerialize a 16-bit little-endian unsigned integer into a new ArrayBuffer.
#encodeInt16be Source
encodeInt16be :: forall m. MonadEffect m => Int -> m ArrayBufferSerialize a 16-bit big-endian two’s-complement signed integer into a new ArrayBuffer.
#encodeInt16le Source
encodeInt16le :: forall m. MonadEffect m => Int -> m ArrayBufferSerialize a 16-bit little-endian two’s-complement signed integer into a new ArrayBuffer.
#encodeUint32be Source
encodeUint32be :: forall m. MonadEffect m => UInt -> m ArrayBufferSerialize a 32-bit big-endian unsigned integer into a new ArrayBuffer.
#encodeUint32le Source
encodeUint32le :: forall m. MonadEffect m => UInt -> m ArrayBufferSerialize a 32-bit little-endian unsigned integer into a new ArrayBuffer.
#encodeInt32be Source
encodeInt32be :: forall m. MonadEffect m => Int -> m ArrayBufferSerialize a 32-bit big-endian two’s-complement signed integer into a new ArrayBuffer.
#encodeInt32le Source
encodeInt32le :: forall m. MonadEffect m => Int -> m ArrayBufferSerialize a 32-bit little-endian two’s-complement signed integer into a new ArrayBuffer.
#encodeFloat32be Source
encodeFloat32be :: forall m. MonadEffect m => Float32 -> m ArrayBufferSerialize a 32-bit big-endian IEEE single-precision float into a new ArrayBuffer.
#encodeFloat32le Source
encodeFloat32le :: forall m. MonadEffect m => Float32 -> m ArrayBufferSerialize a 32-bit little-endian IEEE single-precision float into a new ArrayBuffer.
#encodeFloat64be Source
encodeFloat64be :: forall m. MonadEffect m => Number -> m ArrayBufferSerialize a 64-bit big-endian IEEE double-precision float into a new ArrayBuffer.
#encodeFloat64le Source
encodeFloat64le :: forall m. MonadEffect m => Number -> m ArrayBufferSerialize a 64-bit little-endian IEEE double-precision float into a new ArrayBuffer.
Left-associative
<>>append operatorTL;DR You probably don't want to use the
Buildermonoid directly in your code, it’s better to use thePutMmonad with do-notation instead.The
Buildermonoid in this library is efficient when wesnocsingle items onto the end of it, or when we onlyconssingle items to the beginning, but it can be less efficient when we are mixingconsandsnoc. Most of the time we want tosnoc, but theSemigroupappend operator<>is right-associative, which means it chains likecons.To solve this, we provide an operator
<>>for appendingBuilders.<>>is exactly the same as<>, but left-associative, which means it chains likesnoc.This only matters when we're chaining together three or more
Builders in a single associative expression. Instead ofwe should always prefer to write
so that we get the efficient
snocing ofBuilders.If we build our
ArrayBuffers with thePutMmonad instead of appending by using theSemigroupinstance ofBuilder, then we always get the efficientsnoccase.