{-# LANGUAGE CPP, BangPatterns #-}
#ifdef USE_MONO_PAT_BINDS
{-# LANGUAGE MonoPatBinds #-}
#endif
module Blaze.ByteString.Builder.Internal.Write (
Poke
, runPoke
, pokeN
, Write
, runWrite
, getBound
, getBound'
, getPoke
, exactWrite
, boundedWrite
, writeLiftIO
, writeIf
, writeEq
, writeOrdering
, writeOrd
, fromWrite
, fromWriteSingleton
, fromWriteList
, writeStorable
, fromStorable
, fromStorables
) where
import Foreign
import Data.Monoid
import Control.Monad
import Blaze.ByteString.Builder.Internal.Types
newtype Poke =
Poke { runPoke :: Ptr Word8 -> IO (Ptr Word8) }
data Write = Write {-# UNPACK #-} !Int Poke
{-# INLINE getPoke #-}
getPoke :: Write -> Poke
getPoke (Write _ wio) = wio
{-# INLINE runWrite #-}
runWrite :: Write -> Ptr Word8 -> IO (Ptr Word8)
runWrite = runPoke . getPoke
{-# INLINE getBound #-}
getBound :: Write -> Int
getBound (Write bound _) = bound
{-# INLINE getBound' #-}
getBound' :: String
-> (a -> Write)
-> Int
getBound' msg write =
getBound $ write $ error $
"getBound' called from " ++ msg ++ ": write bound is not data-independent."
instance Monoid Poke where
{-# INLINE mempty #-}
mempty = Poke $ return
{-# INLINE mappend #-}
(Poke po1) `mappend` (Poke po2) = Poke $ po1 >=> po2
{-# INLINE mconcat #-}
mconcat = foldr mappend mempty
instance Monoid Write where
{-# INLINE mempty #-}
mempty = Write 0 mempty
{-# INLINE mappend #-}
(Write bound1 w1) `mappend` (Write bound2 w2) =
Write (bound1 + bound2) (w1 `mappend` w2)
{-# INLINE mconcat #-}
mconcat = foldr mappend mempty
{-# INLINE pokeN #-}
pokeN :: Int
-> (Ptr Word8 -> IO ()) -> Poke
pokeN size io = Poke $ \op -> io op >> return (op `plusPtr` size)
{-# INLINE exactWrite #-}
exactWrite :: Int
-> (Ptr Word8 -> IO ())
-> Write
exactWrite size io = Write size (pokeN size io)
{-# INLINE boundedWrite #-}
boundedWrite :: Int -> Poke -> Write
boundedWrite = Write
{-# INLINE writeLiftIO #-}
writeLiftIO :: (a -> Write) -> IO a -> Write
writeLiftIO write io =
Write (getBound' "writeLiftIO" write)
(Poke $ \pf -> do x <- io; runWrite (write x) pf)
{-# INLINE writeIf #-}
writeIf :: (a -> Bool) -> (a -> Write) -> (a -> Write) -> (a -> Write)
writeIf p wTrue wFalse x =
boundedWrite (max (getBound $ wTrue x) (getBound $ wFalse x))
(if p x then getPoke $ wTrue x else getPoke $ wFalse x)
{-# INLINE writeEq #-}
writeEq :: Eq a => a -> (a -> Write) -> (a -> Write) -> (a -> Write)
writeEq test = writeIf (test ==)
{-# INLINE writeOrdering #-}
writeOrdering :: (a -> Ordering)
-> (a -> Write) -> (a -> Write) -> (a -> Write)
-> (a -> Write)
writeOrdering ord wLT wEQ wGT x =
boundedWrite bound (case ord x of LT -> getPoke $ wLT x;
EQ -> getPoke $ wEQ x;
GT -> getPoke $ wGT x)
where
bound = max (getBound $ wLT x) (max (getBound $ wEQ x) (getBound $ wGT x))
{-# INLINE writeOrd #-}
writeOrd :: Ord a
=> a
-> (a -> Write) -> (a -> Write) -> (a -> Write)
-> (a -> Write)
writeOrd test = writeOrdering (`compare` test)
{-# INLINE fromWrite #-}
fromWrite :: Write -> Builder
fromWrite (Write maxSize wio) =
fromBuildStepCont step
where
step k (BufRange op ope)
| op `plusPtr` maxSize <= ope = do
op' <- runPoke wio op
let !br' = BufRange op' ope
k br'
| otherwise = return $ bufferFull maxSize op (step k)
{-# INLINE fromWriteSingleton #-}
fromWriteSingleton :: (a -> Write) -> (a -> Builder)
fromWriteSingleton write =
mkBuilder
where
mkBuilder x = fromBuildStepCont step
where
step k (BufRange op ope)
| op `plusPtr` maxSize <= ope = do
op' <- runPoke wio op
let !br' = BufRange op' ope
k br'
| otherwise = return $ bufferFull maxSize op (step k)
where
Write maxSize wio = write x
fromWriteList :: (a -> Write) -> [a] -> Builder
fromWriteList write =
makeBuilder
where
makeBuilder xs0 = fromBuildStepCont $ step xs0
where
step xs1 k !(BufRange op0 ope0) = go xs1 op0
where
go [] !op = do
let !br' = BufRange op ope0
k br'
go xs@(x':xs') !op
| op `plusPtr` maxSize <= ope0 = do
!op' <- runPoke wio op
go xs' op'
| otherwise = return $ bufferFull maxSize op (step xs k)
where
Write maxSize wio = write x'
{-# INLINE fromWriteList #-}
{-# INLINE writeStorable #-}
writeStorable :: Storable a => a -> Write
writeStorable x = exactWrite (sizeOf x) (\op -> poke (castPtr op) x)
{-# INLINE fromStorable #-}
fromStorable :: Storable a => a -> Builder
fromStorable = fromWriteSingleton writeStorable
fromStorables :: Storable a => [a] -> Builder
fromStorables = fromWriteList writeStorable