Skip to content

rowtype-yoga/purescript-arraybuffer-builder

Repository files navigation

purescript-arraybuffer-builder

CI Pursuit Maintainer: jamesdbrock

ArrayBuffer serialization in the “builder monoid” and “builder monad” style. In this style, we build up serialized data structures by telling to a Writer monad with do-notation. This style of serialization has been used for a long time and we insist that it works really well.

This package provides a Builder monoid and a PutM monad which are roughly equivalent to types of the same name in the Haskell Data.Binary.Put module.

Usage Examples

All ArrayBuffer building must occur in Effect.

Serialize an integer

Create a two-byte arraybuffer :: ArrayBuffer which contains the number -10 encoded as big-endian 16-bit two’s-complement.

import Data.ArrayBuffer.Builder (execPut, putInt16be)

do
  arraybuffer :: ArrayBuffer <- execPut $ putInt16be (-10)

Serialize three floats

Create a 24-byte arraybuffer :: ArrayBuffer which contains three big-endian IEEE-754 double-precision floats.

import Data.ArrayBuffer.Builder (execPut, putFloat64be)

do
  arraybuffer :: ArrayBuffer <- execPut do
    putFloat64be 1.0
    putFloat64be 2.0
    putFloat64be 3.0

Serialize a String as UTF8

Encode a String as UTF8 with a length prefix into our Builder.

We give this as an example, rather than supporting it in the library, because it depends on Web.Encoding.TextEncoder.

import Effect.Class (class MonadEffect, liftEffect)
import Data.UInt (fromInt)
import Data.ArrayBuffer.Types (ArrayBuffer(..))
import Data.ArrayBuffer.Builder (PutM, putArrayBuffer, execPut, putUint32be)
import Data.ArrayBuffer.Typed (buffer)
import Data.ArrayBuffer.ArrayBuffer (byteLength)
import Web.Encoding.TextEncoder (new, TextEncoder, encode)

putStringUtf8 :: forall m. MonadEffect m => String -> PutM m Unit
putStringUtf8 s = do
  textEncoder <- liftEffect new
  let stringbuf = buffer $ encode s textEncoder
  -- Put a 32-bit big-endian length for the utf8 string, in bytes.
  putUint32be $ fromInt $ byteLength stringbuf
  putArrayBuffer stringbuf

arraybuffer :: ArrayBuffer <- execPut $ putStringUtf8 "🦝"

Serialize an Array Int

Encode an Array Int with a length prefix in a way that's compatible with the Binary instance for [Int32] from the Haskell binary library.

import Data.ArrayBuffer.Builder (execPut, putInt32be)
import Data.Foldable (traverse_)
import Data.Array (length)

putArrayInt32 :: forall m. MonadEffect m => Array Int -> PutM m Unit
putArrayInt32 xs = do
    -- Put a 64-bit big-endian length prefix for the array.
    putInt32be 0
    putInt32be $ length xs
    traverse_ putInt32be xs

do
  arraybuffer <- execPut $ putArrayInt32 [1,2,3]

Serialize an Array Int stack-safe

Stack-safe version of the putArrayInt32 function above. For stack-safety we use foldRecM instead of traverse_ because foldRecM has a MonadRec constraint which makes it stack-safe.

import Data.Array (foldRecM)

putArrayInt32 :: forall m. MonadEffect m => MonadRec m => Array Int -> PutM m Unit
putArrayInt32 xs = do
    -- Put a 64-bit big-endian length prefix for the array.
    putInt32be 0
    putInt32be $ length xs
    foldRecM (\_ x -> putInt32be x) unit xs

Stack-safety

This package will always be stack-safe if all of the functions called inside of the PutM builder expression are stack-safe.

Deserialization

This package is only for writing ArrayBuffers, not reading them. See parsing-dataview for a way to deserialize from ArrayBuffer back to Purescript data.

Alternative packages for serializing to an ArrayBuffer

Development

To run the tests,

spago -x spago-dev.dhall test