|
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 #-}
|