pinch-0.1.0.0: Pinch/Internal/Builder.hs
{-# LANGUAGE CPP #-}
-- |
-- Module : Pinch.Internal.Builder
-- Copyright : (c) Abhinav Gupta 2015
-- License : BSD3
--
-- Maintainer : Abhinav Gupta <mail@abhinavg.net>
-- Stability : experimental
--
-- This module provides a wrapper around @Data.ByteString.Builder@ that keeps
-- track of the final size of the generated ByteString.
module Pinch.Internal.Builder
( Builder
, Build
, run
, int8
, int16
, int32
, int64
, double
, byteString
) where
#if __GLASGOW_HASKELL__ < 709
import Control.Applicative
#endif
import Data.ByteString (ByteString)
import Data.Int
import Data.Monoid
import Prelude hiding (length)
import qualified Data.ByteString as B
import qualified Data.ByteString.Builder as BB
-- | Alias for a build that produces no results.
type Build = Builder ()
-- We're using big endian byte order for now. We can add little endian later
-- if needed.
-- | Used for building a long chain of ByteStrings.
data Builder a = B !a {-# UNPACK #-} !Int64 !BB.Builder
-- TODO CPS?
instance Functor Builder where
fmap f (B a l b) = B (f a) l b
instance Applicative Builder where
pure a = B a 0 mempty
B f l0 b0 <*> B a l1 b1 = B (f a) (l0 + l1) (b0 <> b1)
instance Monad Builder where
(>>) = (*>)
return = pure
B a l0 b0 >>= k =
let B b l1 b1 = k a
in B b (l0 + l1) (b0 <> b1)
-- | Returns the ByteString Builder for this build and its length.
run :: Build -> (Int64, BB.Builder)
run (B () l b) = (l, b)
-- | Writes a byte.
int8 :: Int8 -> Build
int8 = B () 1 . BB.int8
-- | Writes a 16-bit integer.
int16 :: Int16 -> Build
int16 = B () 2 . BB.int16BE
-- | Writes a 32-bit integer.
int32 :: Int32 -> Build
int32 = B () 4 . BB.int32BE
-- | Writes a 64-bit integer.
int64 :: Int64 -> Build
int64 = B () 8 . BB.int64BE
-- | Writes a 64-bit double.
double :: Double -> Build
double = B () 8 . BB.doubleBE
-- | Writes an arbitrary ByteString.
byteString :: ByteString -> Build
byteString bs = B () (fromIntegral $ B.length bs) (BB.byteString bs)