{-# LANGUAGE CPP, BangPatterns, Rank2Types, MonoPatBinds #-} -- | -- Module : Blaze.ByteString.Builder.Internal.Types -- Copyright : (c) 2010 Simon Meier -- License : BSD3-style (see LICENSE) -- -- Maintainer : Simon Meier -- Stability : experimental -- Portability : tested on GHC only -- -- Core types and functions for the 'Builder' monoid and the 'Put' monad based -- based on the 'blaze-builder' library by Jasper van der Jeugt and Simon -- Meier. -- module Blaze.ByteString.Builder.Internal.Types where #ifdef APPLICATIVE_IN_BASE import Control.Applicative #endif import Data.Monoid import qualified Data.ByteString as S import Foreign ------------------------------------------------------------------------------ -- The core: BuildSteps ------------------------------------------------------------------------------ data BufRange = BufRange {-# UNPACK #-} !(Ptr Word8) {-# UNPACK #-} !(Ptr Word8) data BuildSignal a = Done {-# UNPACK #-} !(Ptr Word8) a | BufferFull {-# UNPACK #-} !Int {-# UNPACK #-} !(Ptr Word8) !(BuildStep a) | InsertByteString {-# UNPACK #-} !(Ptr Word8) !S.ByteString !(BuildStep a) newtype BuildStep a = BuildStep { runBuildStep :: BufRange -> IO (BuildSignal a) } -- Hiding the implementation of 'BuildStep's done :: Ptr Word8 -> a -> BuildSignal a done = Done bufferFull :: Int -> Ptr Word8 -> (BufRange -> IO (BuildSignal a)) -> BuildSignal a bufferFull size op step = BufferFull size op (buildStep step) insertByteString :: Ptr Word8 -> S.ByteString -> (BufRange -> IO (BuildSignal a)) -> BuildSignal a insertByteString op bs step = InsertByteString op bs (buildStep step) buildStep :: (BufRange -> IO (BuildSignal a)) -> BuildStep a buildStep = BuildStep ------------------------------------------------------------------------------ -- The 'Builder' Monoid and the 'Put' Monad ------------------------------------------------------------------------------ newtype Builder = Builder { unBuilder :: forall r. BuildStep r -> BuildStep r } instance Monoid Builder where mempty = Builder id {-# INLINE mempty #-} (Builder b1) `mappend` (Builder b2) = Builder $ b1 . b2 {-# INLINE mappend #-} mconcat = foldr mappend mempty {-# INLINE mconcat #-} newtype Put a = Put { unPut :: forall r. (a -> BuildStep r) -> BuildStep r } instance Functor Put where fmap f (Put put) = Put $ \k -> put (\x -> k (f x)) {-# INLINE fmap #-} #ifdef APPLICATIVE_IN_BASE instance Applicative Put where pure x = Put $ \k -> k x {-# INLINE pure #-} f <*> a = Put $ \k -> unPut f (\f' -> unPut a (\a' -> k (f' a'))) {-# INLINE (<*>) #-} a <* b = Put $ \k -> unPut a (\a' -> unPut b (\_ -> k a')) {-# INLINE (<*) #-} a *> b = Put $ \k -> unPut a (\_ -> unPut b k) {-# INLINE (*>) #-} #endif instance Monad Put where return x = Put $ \k -> k x {-# INLINE return #-} m >>= f = Put $ \k -> unPut m (\m' -> unPut (f m') k) {-# INLINE (>>=) #-} m >> n = Put $ \k -> unPut m (\_ -> unPut n k) {-# INLINE (>>) #-} -- Creation from concrete 'BuildStep's ------------------------------------------------------------------------------ putBuildStepCont :: (forall r. (a -> BufRange -> IO (BuildSignal r)) -> ( BufRange -> IO (BuildSignal r)) ) -> Put a putBuildStepCont step = Put step' where step' k = BuildStep $ step (\x -> runBuildStep (k x)) fromBuildStepCont :: (forall r. (BufRange -> IO (BuildSignal r)) -> (BufRange -> IO (BuildSignal r)) ) -> Builder fromBuildStepCont step = Builder step' where step' k = BuildStep $ step (runBuildStep k) -- Conversion between Put and Builder ------------------------------------------------------------------------------ -- | Put the given builder. putBuilder :: Builder -> Put () putBuilder (Builder build) = Put $ \k -> build (k ()) -- | Ignore the value of a put and only exploit its output side effect. fromPut :: Put a -> Builder fromPut (Put put) = Builder $ \k -> put (\_ -> k) -- Lifting IO actions --------------------- -- | Lift the given IO action. {-# INLINE putLiftIO #-} putLiftIO :: IO a -> Put a putLiftIO io = putBuildStepCont $ \k br -> io >>= (`k` br)