    {-# LANGUAGE DeriveDataTypeable #-}
    -- | This is a middle-level wrapper around the zlib C API. It allows you to
    -- work fully with bytestrings and not touch the FFI at all, but is still
    -- low-level enough to allow you to implement high-level abstractions such as
    -- enumerators. Significantly, it does not use lazy IO.
    --
    -- You'll probably need to reference the docs a bit to understand the
    -- WindowBits parameters below, but a basic rule of thumb is 15 is for zlib
    -- compression, and 31 for gzip compression.
    --
    -- A simple streaming compressor in pseudo-code would look like:
    --
    -- > def <- initDeflate ...
    -- > popper <- feedDeflate def rawContent
    -- > pullPopper popper
    -- > ...
    -- > finishDeflate def sendCompressedData
    --
    -- You can see a more complete example is available in the included
    -- file-test.hs.
    module Codec.Zlib
        ( -- * Inflate
          Inflate
        , initInflate
        , initInflateWithDictionary
        , feedInflate
        , finishInflate
        , flushInflate
          -- * Deflate
        , Deflate
        , initDeflate
        , initDeflateWithDictionary
        , feedDeflate
        , finishDeflate
        , flushDeflate
          -- * Data types
        , WindowBits (..)
        , defaultWindowBits
        , ZlibException (..)
        , Popper
        ) where

    import Codec.Zlib.Lowlevel
    import Foreign.ForeignPtr
    import Foreign.C.Types
    import Data.ByteString.Unsafe
    import Codec.Compression.Zlib (WindowBits(WindowBits), defaultWindowBits)
    import qualified Data.ByteString as S
    import Data.ByteString.Lazy.Internal (defaultChunkSize)
    import Control.Monad (when)
    import Data.Typeable (Typeable)
    import Control.Exception (Exception, throwIO)

    type ZStreamPair = (ForeignPtr ZStreamStruct, ForeignPtr CChar)

    -- | The state of an inflation (eg, decompression) process. All allocated
    -- memory is automatically reclaimed by the garbage collector.
    -- Also can contain the inflation dictionary that is used for decompression.
    newtype Inflate = Inflate (ZStreamPair, Maybe S.ByteString)

    -- | The state of a deflation (eg, compression) process. All allocated memory
    -- is automatically reclaimed by the garbage collector.
    newtype Deflate = Deflate ZStreamPair

    -- | Exception that can be thrown from the FFI code. The parameter is the
    -- numerical error code from the zlib library. Quoting the zlib.h file
    -- directly:
    --
    -- * #define Z_OK            0
    --
    -- * #define Z_STREAM_END    1
    --
    -- * #define Z_NEED_DICT     2
    --
    -- * #define Z_ERRNO        (-1)
    --
    -- * #define Z_STREAM_ERROR (-2)
    --
    -- * #define Z_DATA_ERROR   (-3)
    --
    -- * #define Z_MEM_ERROR    (-4)
    --
    -- * #define Z_BUF_ERROR    (-5)
    --
    -- * #define Z_VERSION_ERROR (-6)

    data ZlibException = ZlibException Int
        deriving (Int -> ZlibException -> ShowS
    [ZlibException] -> ShowS
    ZlibException -> String
    (Int -> ZlibException -> ShowS)
    -> (ZlibException -> String)
    -> ([ZlibException] -> ShowS)
    -> Show ZlibException
    forall a.
    (Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
    $cshowsPrec :: Int -> ZlibException -> ShowS
    showsPrec :: Int -> ZlibException -> ShowS
    $cshow :: ZlibException -> String
    show :: ZlibException -> String
    $cshowList :: [ZlibException] -> ShowS
    showList :: [ZlibException] -> ShowS
    Show, Typeable)
    instance Exception ZlibException

    -- | Some constants for the error codes, used internally
    zNeedDict :: CInt
    zNeedDict :: CInt
    zNeedDict = CInt
    2

    zBufError :: CInt
    zBufError :: CInt
    zBufError = -CInt
    5

    -- | Initialize an inflation process with the given 'WindowBits'. You will need
    -- to call 'feedInflate' to feed compressed data to this and
    -- 'finishInflate' to extract the final chunk of decompressed data.
    initInflate :: WindowBits -> IO Inflate
    initInflate :: WindowBits -> IO Inflate
    initInflate WindowBits
    w = do
        zstr <- IO ZStream'
    zstreamNew
        inflateInit2 zstr w
        fzstr <- newForeignPtr c_free_z_stream_inflate zstr
        fbuff <- mallocForeignPtrBytes defaultChunkSize
        withForeignPtr fbuff $ \Ptr CChar
    buff ->
            ZStream' -> Ptr CChar -> CUInt -> IO ()
    c_set_avail_out ZStream'
    zstr Ptr CChar
    buff (CUInt -> IO ()) -> CUInt -> IO ()
    forall a b. (a -> b) -> a -> b
    $ Int -> CUInt
    forall a b. (Integral a, Num b) => a -> b
    fromIntegral Int
    defaultChunkSize
        return $ Inflate ((fzstr, fbuff), Nothing)

    -- | Initialize an inflation process with the given 'WindowBits'.
    -- Unlike initInflate a dictionary for inflation is set which must
    -- match the one set during compression.
    initInflateWithDictionary :: WindowBits -> S.ByteString -> IO Inflate
    initInflateWithDictionary :: WindowBits -> ByteString -> IO Inflate
    initInflateWithDictionary WindowBits
    w ByteString
    bs = do
        zstr <- IO ZStream'
    zstreamNew
        inflateInit2 zstr w
        fzstr <- newForeignPtr c_free_z_stream_inflate zstr
        fbuff <- mallocForeignPtrBytes defaultChunkSize

        withForeignPtr fbuff $ \Ptr CChar
    buff ->
            ZStream' -> Ptr CChar -> CUInt -> IO ()
    c_set_avail_out ZStream'
    zstr Ptr CChar
    buff (CUInt -> IO ()) -> CUInt -> IO ()
    forall a b. (a -> b) -> a -> b
    $ Int -> CUInt
    forall a b. (Integral a, Num b) => a -> b
    fromIntegral Int
    defaultChunkSize
        return $ Inflate ((fzstr, fbuff), Just bs)

    -- | Initialize a deflation process with the given compression level and
    -- 'WindowBits'. You will need to call 'feedDeflate' to feed uncompressed
    -- data to this and 'finishDeflate' to extract the final chunks of compressed
    -- data.
    initDeflate :: Int -- ^ Compression level
                -> WindowBits -> IO Deflate
    initDeflate :: Int -> WindowBits -> IO Deflate
    initDeflate Int
    level WindowBits
    w = do
        zstr <- IO ZStream'
    zstreamNew
        deflateInit2 zstr level w 8 StrategyDefault
        fzstr <- newForeignPtr c_free_z_stream_deflate zstr
        fbuff <- mallocForeignPtrBytes defaultChunkSize
        withForeignPtr fbuff $ \Ptr CChar
    buff ->
            ZStream' -> Ptr CChar -> CUInt -> IO ()
    c_set_avail_out ZStream'
    zstr Ptr CChar
    buff (CUInt -> IO ()) -> CUInt -> IO ()
    forall a b. (a -> b) -> a -> b
    $ Int -> CUInt
    forall a b. (Integral a, Num b) => a -> b
    fromIntegral Int
    defaultChunkSize
        return $ Deflate (fzstr, fbuff)

    -- | Initialize an deflation process with the given compression level and
    -- 'WindowBits'.
    -- Unlike initDeflate a dictionary for deflation is set.
    initDeflateWithDictionary :: Int -- ^ Compression level
                              -> S.ByteString -- ^ Deflate dictionary
                              -> WindowBits -> IO Deflate
    initDeflateWithDictionary :: Int -> ByteString -> WindowBits -> IO Deflate
    initDeflateWithDictionary Int
    level ByteString
    bs WindowBits
    w = do
        zstr <- IO ZStream'
    zstreamNew
        deflateInit2 zstr level w 8 StrategyDefault
        fzstr <- newForeignPtr c_free_z_stream_deflate zstr
        fbuff <- mallocForeignPtrBytes defaultChunkSize

        unsafeUseAsCStringLen bs $ \(Ptr CChar
    cstr, Int
    len) -> do
            ZStream' -> Ptr CChar -> CUInt -> IO ()
    c_call_deflate_set_dictionary ZStream'
    zstr Ptr CChar
    cstr (CUInt -> IO ()) -> CUInt -> IO ()
    forall a b. (a -> b) -> a -> b
    $ Int -> CUInt
    forall a b. (Integral a, Num b) => a -> b
    fromIntegral Int
    len

        withForeignPtr fbuff $ \Ptr CChar
    buff ->
            ZStream' -> Ptr CChar -> CUInt -> IO ()
    c_set_avail_out ZStream'
    zstr Ptr CChar
    buff (CUInt -> IO ()) -> CUInt -> IO ()
    forall a b. (a -> b) -> a -> b
    $ Int -> CUInt
    forall a b. (Integral a, Num b) => a -> b
    fromIntegral Int
    defaultChunkSize
        return $ Deflate (fzstr, fbuff)

    -- | Feed the given 'S.ByteString' to the inflater. Return a 'Popper',
    -- an IO action that returns the decompressed data a chunk at a time.
    -- The 'Popper' must be called to exhaustion before using the 'Inflate'
    -- object again.
    --
    -- Note that this function automatically buffers the output to
    -- 'defaultChunkSize', and therefore you won't get any data from the popper
    -- until that much decompressed data is available. After you have fed all of
    -- the compressed data to this function, you can extract your final chunk of
    -- decompressed data using 'finishInflate'.
    feedInflate
        :: Inflate
        -> S.ByteString
        -> IO Popper
    feedInflate :: Inflate -> ByteString -> IO Popper
    feedInflate (Inflate ((ForeignPtr ZStreamStruct
    fzstr, ForeignPtr CChar
    fbuff), Maybe ByteString
    inflateDictionary)) ByteString
    bs = do
        ForeignPtr ZStreamStruct -> (ZStream' -> IO ()) -> IO ()
    forall a b. ForeignPtr a -> (Ptr a -> IO b) -> IO b
    withForeignPtr ForeignPtr ZStreamStruct
    fzstr ((ZStream' -> IO ()) -> IO ()) -> (ZStream' -> IO ()) -> IO ()
    forall a b. (a -> b) -> a -> b
    $ \ZStream'
    zstr ->
            ByteString -> (CStringLen -> IO ()) -> IO ()
    forall a. ByteString -> (CStringLen -> IO a) -> IO a
    unsafeUseAsCStringLen ByteString
    bs ((CStringLen -> IO ()) -> IO ()) -> (CStringLen -> IO ()) -> IO ()
    forall a b. (a -> b) -> a -> b
    $ \(Ptr CChar
    cstr, Int
    len) ->
                ZStream' -> Ptr CChar -> CUInt -> IO ()
    c_set_avail_in ZStream'
    zstr Ptr CChar
    cstr (CUInt -> IO ()) -> CUInt -> IO ()
    forall a b. (a -> b) -> a -> b
    $ Int -> CUInt
    forall a b. (Integral a, Num b) => a -> b
    fromIntegral Int
    len
        Popper -> IO Popper
    forall a. a -> IO a
    forall (m :: * -> *) a. Monad m => a -> m a
    return (Popper -> IO Popper) -> Popper -> IO Popper
    forall a b. (a -> b) -> a -> b
    $ ForeignPtr CChar
    -> ForeignPtr ZStreamStruct
    -> Maybe ByteString
    -> (ZStream' -> IO CInt)
    -> Bool
    -> Popper
    drain ForeignPtr CChar
    fbuff ForeignPtr ZStreamStruct
    fzstr (ByteString -> Maybe ByteString
    forall a. a -> Maybe a
    Just ByteString
    bs) ZStream' -> IO CInt
    inflate Bool
    False
      where
        inflate :: ZStream' -> IO CInt
    inflate ZStream'
    zstr = do
            res <- ZStream' -> IO CInt
    c_call_inflate_noflush ZStream'
    zstr
            if (res == zNeedDict)
                then maybe (throwIO $ ZlibException $ fromIntegral zNeedDict) -- no dictionary supplied so throw error
                           (\ByteString
    dict -> (ByteString -> (CStringLen -> IO CInt) -> IO CInt
    forall a. ByteString -> (CStringLen -> IO a) -> IO a
    unsafeUseAsCStringLen ByteString
    dict ((CStringLen -> IO CInt) -> IO CInt)
    -> (CStringLen -> IO CInt) -> IO CInt
    forall a b. (a -> b) -> a -> b
    $ \(Ptr CChar
    cstr, Int
    len) -> do
                                        ZStream' -> Ptr CChar -> CUInt -> IO ()
    c_call_inflate_set_dictionary ZStream'
    zstr Ptr CChar
    cstr (CUInt -> IO ()) -> CUInt -> IO ()
    forall a b. (a -> b) -> a -> b
    $ Int -> CUInt
    forall a b. (Integral a, Num b) => a -> b
    fromIntegral Int
    len
                                        ZStream' -> IO CInt
    c_call_inflate_noflush ZStream'
    zstr))
                           inflateDictionary
                else return res

    -- | An IO action that returns the next chunk of data, returning 'Nothing' when
    -- there is no more data to be popped.
    type Popper = IO (Maybe S.ByteString)

    -- | Ensure that the given @ByteString@ is not deallocated.
    keepAlive :: Maybe S.ByteString -> IO a -> IO a
    keepAlive :: forall a. Maybe ByteString -> IO a -> IO a
    keepAlive Maybe ByteString
    Nothing = IO a -> IO a
    forall a. a -> a
    id
    keepAlive (Just ByteString
    bs) = ByteString -> (CStringLen -> IO a) -> IO a
    forall a. ByteString -> (CStringLen -> IO a) -> IO a
    unsafeUseAsCStringLen ByteString
    bs ((CStringLen -> IO a) -> IO a)
    -> (IO a -> CStringLen -> IO a) -> IO a -> IO a
    forall b c a. (b -> c) -> (a -> b) -> a -> c
    . IO a -> CStringLen -> IO a
    forall a b. a -> b -> a
    const

    drain :: ForeignPtr CChar
          -> ForeignPtr ZStreamStruct
          -> Maybe S.ByteString
          -> (ZStream' -> IO CInt)
          -> Bool
          -> Popper
    drain :: ForeignPtr CChar
    -> ForeignPtr ZStreamStruct
    -> Maybe ByteString
    -> (ZStream' -> IO CInt)
    -> Bool
    -> Popper
    drain ForeignPtr CChar
    fbuff ForeignPtr ZStreamStruct
    fzstr Maybe ByteString
    mbs ZStream' -> IO CInt
    func Bool
    isFinish = ForeignPtr ZStreamStruct -> (ZStream' -> Popper) -> Popper
    forall a b. ForeignPtr a -> (Ptr a -> IO b) -> IO b
    withForeignPtr ForeignPtr ZStreamStruct
    fzstr ((ZStream' -> Popper) -> Popper) -> (ZStream' -> Popper) -> Popper
    forall a b. (a -> b) -> a -> b
    $ \ZStream'
    zstr -> Maybe ByteString -> Popper -> Popper
    forall a. Maybe ByteString -> IO a -> IO a
    keepAlive Maybe ByteString
    mbs (Popper -> Popper) -> Popper -> Popper
    forall a b. (a -> b) -> a -> b
    $ do
        res <- ZStream' -> IO CInt
    func ZStream'
    zstr
        when (res < 0 && res /= zBufError)
            $ throwIO $ ZlibException $ fromIntegral res
        avail <- c_get_avail_out zstr
        let size = Int
    defaultChunkSize Int -> Int -> Int
    forall a. Num a => a -> a -> a
    - CUInt -> Int
    forall a b. (Integral a, Num b) => a -> b
    fromIntegral CUInt
    avail
            toOutput = CUInt
    avail CUInt -> CUInt -> Bool
    forall a. Eq a => a -> a -> Bool
    == CUInt
    0 Bool -> Bool -> Bool
    || (Bool
    isFinish Bool -> Bool -> Bool
    && Int
    size Int -> Int -> Bool
    forall a. Eq a => a -> a -> Bool
    /= Int
    0)
        if toOutput
            then withForeignPtr fbuff $ \Ptr CChar
    buff -> do
                bs <- CStringLen -> IO ByteString
    S.packCStringLen (Ptr CChar
    buff, Int
    size)
                c_set_avail_out zstr buff
                    $ fromIntegral defaultChunkSize
                return $ Just bs
            else return Nothing

    -- | As explained in 'feedInflate', inflation buffers your decompressed
    -- data. After you call 'feedInflate' with your last chunk of compressed
    -- data, you will likely have some data still sitting in the buffer. This
    -- function will return it to you.
    finishInflate :: Inflate -> IO S.ByteString
    finishInflate :: Inflate -> IO ByteString
    finishInflate (Inflate ((ForeignPtr ZStreamStruct
    fzstr, ForeignPtr CChar
    fbuff), Maybe ByteString
    _)) =
        ForeignPtr ZStreamStruct
    -> (ZStream' -> IO ByteString) -> IO ByteString
    forall a b. ForeignPtr a -> (Ptr a -> IO b) -> IO b
    withForeignPtr ForeignPtr ZStreamStruct
    fzstr ((ZStream' -> IO ByteString) -> IO ByteString)
    -> (ZStream' -> IO ByteString) -> IO ByteString
    forall a b. (a -> b) -> a -> b
    $ \ZStream'
    zstr ->
            ForeignPtr CChar -> (Ptr CChar -> IO ByteString) -> IO ByteString
    forall a b. ForeignPtr a -> (Ptr a -> IO b) -> IO b
    withForeignPtr ForeignPtr CChar
    fbuff ((Ptr CChar -> IO ByteString) -> IO ByteString)
    -> (Ptr CChar -> IO ByteString) -> IO ByteString
    forall a b. (a -> b) -> a -> b
    $ \Ptr CChar
    buff -> do
                avail <- ZStream' -> IO CUInt
    c_get_avail_out ZStream'
    zstr
                let size = Int
    defaultChunkSize Int -> Int -> Int
    forall a. Num a => a -> a -> a
    - CUInt -> Int
    forall a b. (Integral a, Num b) => a -> b
    fromIntegral CUInt
    avail
                bs <- S.packCStringLen (buff, size)
                c_set_avail_out zstr buff $ fromIntegral defaultChunkSize
                return bs

    -- | Flush the inflation buffer. Useful for interactive application.
    --
    -- This is actually a synonym for 'finishInflate'. It is provided for its more
    -- semantic name.
    --
    -- Since 0.0.3
    flushInflate :: Inflate -> IO S.ByteString
    flushInflate :: Inflate -> IO ByteString
    flushInflate = Inflate -> IO ByteString
    finishInflate

    -- | Feed the given 'S.ByteString' to the deflater. Return a 'Popper',
    -- an IO action that returns the compressed data a chunk at a time.
    -- The 'Popper' must be called to exhaustion before using the 'Deflate'
    -- object again.
    --
    -- Note that this function automatically buffers the output to
    -- 'defaultChunkSize', and therefore you won't get any data from the popper
    -- until that much compressed data is available. After you have fed all of the
    -- decompressed data to this function, you can extract your final chunks of
    -- compressed data using 'finishDeflate'.
    feedDeflate :: Deflate -> S.ByteString -> IO Popper
    feedDeflate :: Deflate -> ByteString -> IO Popper
    feedDeflate (Deflate (ForeignPtr ZStreamStruct
    fzstr, ForeignPtr CChar
    fbuff)) ByteString
    bs = do
        ForeignPtr ZStreamStruct -> (ZStream' -> IO ()) -> IO ()
    forall a b. ForeignPtr a -> (Ptr a -> IO b) -> IO b
    withForeignPtr ForeignPtr ZStreamStruct
    fzstr ((ZStream' -> IO ()) -> IO ()) -> (ZStream' -> IO ()) -> IO ()
    forall a b. (a -> b) -> a -> b
    $ \ZStream'
    zstr ->
            ByteString -> (CStringLen -> IO ()) -> IO ()
    forall a. ByteString -> (CStringLen -> IO a) -> IO a
    unsafeUseAsCStringLen ByteString
    bs ((CStringLen -> IO ()) -> IO ()) -> (CStringLen -> IO ()) -> IO ()
    forall a b. (a -> b) -> a -> b
    $ \(Ptr CChar
    cstr, Int
    len) -> do
                ZStream' -> Ptr CChar -> CUInt -> IO ()
    c_set_avail_in ZStream'
    zstr Ptr CChar
    cstr (CUInt -> IO ()) -> CUInt -> IO ()
    forall a b. (a -> b) -> a -> b
    $ Int -> CUInt
    forall a b. (Integral a, Num b) => a -> b
    fromIntegral Int
    len
        Popper -> IO Popper
    forall a. a -> IO a
    forall (m :: * -> *) a. Monad m => a -> m a
    return (Popper -> IO Popper) -> Popper -> IO Popper
    forall a b. (a -> b) -> a -> b
    $ ForeignPtr CChar
    -> ForeignPtr ZStreamStruct
    -> Maybe ByteString
    -> (ZStream' -> IO CInt)
    -> Bool
    -> Popper
    drain ForeignPtr CChar
    fbuff ForeignPtr ZStreamStruct
    fzstr (ByteString -> Maybe ByteString
    forall a. a -> Maybe a
    Just ByteString
    bs) ZStream' -> IO CInt
    c_call_deflate_noflush Bool
    False

    -- | As explained in 'feedDeflate', deflation buffers your compressed
    -- data. After you call 'feedDeflate' with your last chunk of uncompressed
    -- data, use this to flush the rest of the data and signal end of input.
    finishDeflate :: Deflate -> Popper
    finishDeflate :: Deflate -> Popper
    finishDeflate (Deflate (ForeignPtr ZStreamStruct
    fzstr, ForeignPtr CChar
    fbuff)) =
        ForeignPtr CChar
    -> ForeignPtr ZStreamStruct
    -> Maybe ByteString
    -> (ZStream' -> IO CInt)
    -> Bool
    -> Popper
    drain ForeignPtr CChar
    fbuff ForeignPtr ZStreamStruct
    fzstr Maybe ByteString
    forall a. Maybe a
    Nothing ZStream' -> IO CInt
    c_call_deflate_finish Bool
    True

    -- | Flush the deflation buffer. Useful for interactive application.
    -- Internally this passes Z_SYNC_FLUSH to the zlib library.
    --
    -- Unlike 'finishDeflate', 'flushDeflate' does not signal end of input,
    -- meaning you can feed more uncompressed data afterward.
    --
    -- Since 0.0.3
    flushDeflate :: Deflate -> Popper
    flushDeflate :: Deflate -> Popper
    flushDeflate (Deflate (ForeignPtr ZStreamStruct
    fzstr, ForeignPtr CChar
    fbuff)) =
        ForeignPtr CChar
    -> ForeignPtr ZStreamStruct
    -> Maybe ByteString
    -> (ZStream' -> IO CInt)
    -> Bool
    -> Popper
    drain ForeignPtr CChar
    fbuff ForeignPtr ZStreamStruct
    fzstr Maybe ByteString
    forall a. Maybe a
    Nothing ZStream' -> IO CInt
    c_call_deflate_flush Bool
    True