Blame Data/Conduit/ByteString/Builder.hs

Packit 4b2029
{-# LANGUAGE FlexibleContexts #-}
Packit 4b2029
{-# LANGUAGE BangPatterns #-}
Packit 4b2029
{-# LANGUAGE RankNTypes #-}
Packit 4b2029
-- | Convert a stream of blaze-builder @Builder@s into a stream of @ByteString@s.
Packit 4b2029
--
Packit 4b2029
-- Works with both blaze-builder < 0.4's @Builder@s and
Packit 4b2029
-- 'Data.ByteString.Builder.Builder'.
Packit 4b2029
--
Packit 4b2029
-- Adapted from blaze-builder-enumerator, written by myself and Simon Meier.
Packit 4b2029
--
Packit 4b2029
-- Note that the functions here can work in any monad built on top of @IO@ or
Packit 4b2029
-- @ST@.
Packit 4b2029
--
Packit 4b2029
-- Since 1.1.7.0
Packit 4b2029
--
Packit 4b2029
module Data.Conduit.ByteString.Builder
Packit 4b2029
    (
Packit 4b2029
Packit 4b2029
  -- * Conduits from builders to bytestrings
Packit 4b2029
    builderToByteString
Packit 4b2029
  , unsafeBuilderToByteString
Packit 4b2029
  , builderToByteStringWith
Packit 4b2029
Packit 4b2029
  -- ** Flush
Packit 4b2029
  , builderToByteStringFlush
Packit 4b2029
  , builderToByteStringWithFlush
Packit 4b2029
Packit 4b2029
  -- * Buffers
Packit 4b2029
  , Buffer
Packit 4b2029
Packit 4b2029
  -- ** Status information
Packit 4b2029
  , freeSize
Packit 4b2029
  , sliceSize
Packit 4b2029
  , bufferSize
Packit 4b2029
Packit 4b2029
  -- ** Creation and modification
Packit 4b2029
  , allocBuffer
Packit 4b2029
  , reuseBuffer
Packit 4b2029
  , nextSlice
Packit 4b2029
Packit 4b2029
  -- ** Conversion to bytestings
Packit 4b2029
  , unsafeFreezeBuffer
Packit 4b2029
  , unsafeFreezeNonEmptyBuffer
Packit 4b2029
Packit 4b2029
  -- * Buffer allocation strategies
Packit 4b2029
  , BufferAllocStrategy
Packit 4b2029
  , allNewBuffersStrategy
Packit 4b2029
  , reuseBufferStrategy
Packit 4b2029
    ) where
Packit 4b2029
Packit 4b2029
import Data.Conduit
Packit 4b2029
import Control.Monad (unless, liftM)
Packit 4b2029
import Control.Monad.Trans.Class (lift, MonadTrans)
Packit 4b2029
Packit 4b2029
import qualified Data.ByteString                   as S
Packit 4b2029
Packit 4b2029
import Control.Monad.Primitive (PrimMonad, unsafePrimToPrim)
Packit 4b2029
import Control.Monad.Base (MonadBase, liftBase)
Packit 4b2029
import Data.Streaming.ByteString.Builder.Class
Packit 4b2029
Packit 4b2029
unsafeLiftIO :: (MonadBase base m, PrimMonad base) => IO a -> m a
Packit 4b2029
unsafeLiftIO = liftBase . unsafePrimToPrim
Packit 4b2029
Packit 4b2029
-- | Incrementally execute builders and pass on the filled chunks as
Packit 4b2029
-- bytestrings.
Packit 4b2029
builderToByteString :: (MonadBase base m, PrimMonad base, StreamingBuilder b)
Packit 4b2029
                    => Conduit b m S.ByteString
Packit 4b2029
builderToByteString =
Packit 4b2029
  builderToByteStringWith defaultStrategy
Packit 4b2029
{-# INLINE builderToByteString #-}
Packit 4b2029
Packit 4b2029
-- |
Packit 4b2029
--
Packit 4b2029
-- Since 0.0.2
Packit 4b2029
builderToByteStringFlush :: (MonadBase base m, PrimMonad base, StreamingBuilder b)
Packit 4b2029
                         => Conduit (Flush b) m (Flush S.ByteString)
Packit 4b2029
builderToByteStringFlush =
Packit 4b2029
  builderToByteStringWithFlush defaultStrategy
Packit 4b2029
{-# INLINE builderToByteStringFlush #-}
Packit 4b2029
Packit 4b2029
-- | Incrementally execute builders on the given buffer and pass on the filled
Packit 4b2029
-- chunks as bytestrings. Note that, if the given buffer is too small for the
Packit 4b2029
-- execution of a build step, a larger one will be allocated.
Packit 4b2029
--
Packit 4b2029
-- WARNING: This conduit yields bytestrings that are NOT
Packit 4b2029
-- referentially transparent. Their content will be overwritten as soon
Packit 4b2029
-- as control is returned from the inner sink!
Packit 4b2029
unsafeBuilderToByteString :: (MonadBase base m, PrimMonad base, StreamingBuilder b)
Packit 4b2029
                          => IO Buffer  -- action yielding the inital buffer.
Packit 4b2029
                          -> Conduit b m S.ByteString
Packit 4b2029
unsafeBuilderToByteString = builderToByteStringWith . reuseBufferStrategy
Packit 4b2029
{-# INLINE unsafeBuilderToByteString #-}
Packit 4b2029
Packit 4b2029
Packit 4b2029
-- | A conduit that incrementally executes builders and passes on the
Packit 4b2029
-- filled chunks as bytestrings to an inner sink.
Packit 4b2029
--
Packit 4b2029
-- INV: All bytestrings passed to the inner sink are non-empty.
Packit 4b2029
builderToByteStringWith :: (MonadBase base m, PrimMonad base, StreamingBuilder b)
Packit 4b2029
                        => BufferAllocStrategy
Packit 4b2029
                        -> Conduit b m S.ByteString
Packit 4b2029
builderToByteStringWith =
Packit 4b2029
    helper (liftM (fmap Chunk) await) yield'
Packit 4b2029
  where
Packit 4b2029
    yield' Flush = return ()
Packit 4b2029
    yield' (Chunk bs) = yield bs
Packit 4b2029
{-# INLINE builderToByteStringWith #-}
Packit 4b2029
Packit 4b2029
-- |
Packit 4b2029
--
Packit 4b2029
-- Since 0.0.2
Packit 4b2029
builderToByteStringWithFlush
Packit 4b2029
    :: (MonadBase base m, PrimMonad base, StreamingBuilder b)
Packit 4b2029
    => BufferAllocStrategy
Packit 4b2029
    -> Conduit (Flush b) m (Flush S.ByteString)
Packit 4b2029
builderToByteStringWithFlush = helper await yield
Packit 4b2029
{-# INLINE builderToByteStringWithFlush #-}
Packit 4b2029
Packit 4b2029
helper :: (MonadBase base m, PrimMonad base, Monad (t m), MonadTrans t, StreamingBuilder b)
Packit 4b2029
       => t m (Maybe (Flush b))
Packit 4b2029
       -> (Flush S.ByteString -> t m ())
Packit 4b2029
       -> BufferAllocStrategy
Packit 4b2029
       -> t m ()
Packit 4b2029
helper await' yield' strat = do
Packit 4b2029
    (recv, finish) <- lift $ unsafeLiftIO $ newBuilderRecv strat
Packit 4b2029
    let loop = await' >>= maybe finish' cont
Packit 4b2029
        finish' = do
Packit 4b2029
            mbs <- lift $ unsafeLiftIO finish
Packit 4b2029
            maybe (return ()) (yield' . Chunk) mbs
Packit 4b2029
        cont fbuilder = do
Packit 4b2029
            let builder =
Packit 4b2029
                    case fbuilder of
Packit 4b2029
                        Flush -> builderFlush
Packit 4b2029
                        Chunk b -> b
Packit 4b2029
            popper <- lift $ unsafeLiftIO $ recv builder
Packit 4b2029
            let cont' = do
Packit 4b2029
                    bs <- lift $ unsafeLiftIO popper
Packit 4b2029
                    unless (S.null bs) $ do
Packit 4b2029
                        yield' (Chunk bs)
Packit 4b2029
                        cont'
Packit 4b2029
            cont'
Packit 4b2029
            case fbuilder of
Packit 4b2029
                Flush -> yield' Flush
Packit 4b2029
                Chunk _ -> return ()
Packit 4b2029
            loop
Packit 4b2029
    loop
Packit 4b2029
{-# INLINE helper #-}