{-# LANGUAGE BangPatterns #-}
{-# LANGUAGE OverloadedStrings #-}
module System.IO.Streams.Builder
(
builderStream
, unsafeBuilderStream
, builderStreamWith
) where
import Control.Monad (when)
import Data.ByteString.Char8 (ByteString)
import qualified Data.ByteString.Char8 as S
import Data.IORef (newIORef, readIORef, writeIORef)
import Blaze.ByteString.Builder.Internal (defaultBufferSize)
import Blaze.ByteString.Builder.Internal.Types (BufRange (..), BuildSignal (..), Builder (..), buildStep)
import Blaze.ByteString.Builder.Internal.Buffer (Buffer, BufferAllocStrategy, allNewBuffersStrategy, execBuildStep, reuseBufferStrategy, unsafeFreezeBuffer, unsafeFreezeNonEmptyBuffer, updateEndOfSlice)
import System.IO.Streams.Internal (OutputStream, makeOutputStream, write)
builderStream :: OutputStream ByteString -> IO (OutputStream Builder)
builderStream = builderStreamWith (allNewBuffersStrategy defaultBufferSize)
unsafeBuilderStream :: IO Buffer
-> OutputStream ByteString
-> IO (OutputStream Builder)
unsafeBuilderStream = builderStreamWith . reuseBufferStrategy
builderStreamWith :: BufferAllocStrategy
-> OutputStream ByteString
-> IO (OutputStream Builder)
builderStreamWith (ioBuf0, nextBuf) os = do
bufRef <- newIORef ioBuf0
makeOutputStream $ sink bufRef
where
sink bufRef m = do
buf <- readIORef bufRef
maybe (eof buf) (chunk buf) m
where
eof ioBuf = do
buf <- ioBuf
case unsafeFreezeNonEmptyBuffer buf of
Nothing -> write Nothing os
x@(Just s) -> do
when (not $ S.null s) $ write x os
write Nothing os
chunk ioBuf c = feed bufRef (unBuilder c (buildStep finalStep)) ioBuf
finalStep !(BufRange pf _) = return $! Done pf $! ()
feed bufRef bStep ioBuf = do
!buf <- ioBuf
signal <- execBuildStep bStep buf
case signal of
Done op' _ ->
writeIORef bufRef $ (return (updateEndOfSlice buf op'))
BufferFull minSize op' bStep' -> do
let buf' = updateEndOfSlice buf op'
{-# INLINE cont #-}
cont = do
ioBuf' <- nextBuf minSize buf'
feed bufRef bStep' ioBuf'
write (Just $! unsafeFreezeBuffer buf') os
cont
InsertByteString op' bs bStep' -> do
let buf' = updateEndOfSlice buf op'
case unsafeFreezeNonEmptyBuffer buf' of
Nothing -> return $! ()
x -> write x os
write (Just bs) os
ioBuf' <- nextBuf 1 buf'
feed bufRef bStep' ioBuf'