pipes-zlib-0.3.0: src/Pipes/Zlib.hs
{-# LANGUAGE RankNTypes #-}
-- | This module exports utilities to compress and decompress @pipes@ streams
-- using the zlib compression codec.
module Pipes.Zlib (
-- * Streams
decompress
, compress
-- * Compression level
-- $ccz-re-export
, ZC.defaultCompression
, ZC.noCompression
, ZC.bestSpeed
, ZC.bestCompression
, ZC.compressionLevel
-- * Window size
-- $ccz-re-export
, ZC.defaultWindowBits
, ZC.windowBits
) where
import qualified Codec.Zlib as Z
import qualified Codec.Compression.Zlib as ZC
import Control.Monad (forever)
import Pipes
import qualified Data.ByteString as B
--------------------------------------------------------------------------------
-- | Decompress bytes flowing downstream.
--
-- See the "Codec.Compression.Zlib" module for details about 'Z.WindowBits'.
decompress :: MonadIO m => ZC.WindowBits -> Pipe B.ByteString B.ByteString m r
decompress config = forever $ do
inf <- liftIO (Z.initInflate config)
a <- awaitNonEmpty
popper <- liftIO (Z.feedInflate inf a)
fromPopper popper
bs <- liftIO (Z.finishInflate inf)
if B.null bs
then return ()
else yield bs
{-# INLINABLE decompress #-}
-- | Compress bytes flowing downstream.
--
-- See the "Codec.Compression.Zlib" module for details about
-- 'ZC.CompressionLevel' and 'ZC.WindowBits'.
compress
:: MonadIO m
=> ZC.CompressionLevel -> ZC.WindowBits -> Pipe B.ByteString B.ByteString m r
compress level config = forever $ do
def <- liftIO (Z.initDeflate level' config)
a <- awaitNonEmpty
popper <- liftIO (Z.feedDeflate def a)
fromPopper popper
mbs <- liftIO (Z.finishDeflate def)
case mbs of
Just bs -> yield bs
Nothing -> return ()
where
level' = fromCompressionLevel level
{-# INLINABLE compress #-}
--------------------------------------------------------------------------------
-- $ccz-re-export
--
-- The following are re-exported from "Codec.Compression.Zlib" for your
-- convenience.
--------------------------------------------------------------------------------
-- Internal stuff
awaitNonEmpty :: Monad m => Consumer' B.ByteString m B.ByteString
awaitNonEmpty = loop
where
loop = do
bs <- await
if B.null bs
then loop
else return bs
{-# INLINABLE awaitNonEmpty #-}
-- | Produce values from the given 'Z.Poppler' until exhausted.
fromPopper :: MonadIO m => Z.Popper -> Producer' B.ByteString m ()
fromPopper pop = loop
where
loop = do
mbs <- liftIO pop
case mbs of
Nothing -> return ()
Just bs -> yield bs >> loop
{-# INLINABLE fromPopper #-}
-- We need this function until the @zlib@ library hides the
-- 'ZC.CompressionLevel' constructors in future version 0.7.
fromCompressionLevel :: ZC.CompressionLevel -> Int
fromCompressionLevel level = case level of
ZC.DefaultCompression -> -1
ZC.NoCompression -> 0
ZC.BestSpeed -> 1
ZC.BestCompression -> 9
ZC.CompressionLevel n
| n >= 0 && n <= 9 -> fromIntegral n
_ -> error "CompressLevel must be in the range 1..9"