1105 lines
		
	
	
		
			36 KiB
		
	
	
	
		
			Haskell
		
	
	
	
	
	
			
		
		
	
	
			1105 lines
		
	
	
		
			36 KiB
		
	
	
	
		
			Haskell
		
	
	
	
	
	
| {-# LANGUAGE CPP, ForeignFunctionInterface, DeriveDataTypeable #-}
 | |
| #if __GLASGOW_HASKELL__ >= 702
 | |
| {-# LANGUAGE DeriveGeneric #-}
 | |
| #endif
 | |
| #if __GLASGOW_HASKELL__ >= 706
 | |
| {-# LANGUAGE CApiFFI #-}
 | |
| #endif
 | |
| -----------------------------------------------------------------------------
 | |
| -- |
 | |
| -- Copyright   :  (c) 2006-2015 Duncan Coutts
 | |
| -- License     :  BSD-style
 | |
| --
 | |
| -- Maintainer  :  duncan@community.haskell.org
 | |
| --
 | |
| -- Zlib wrapper layer
 | |
| --
 | |
| -----------------------------------------------------------------------------
 | |
| module Codec.Compression.Zlib.Stream (
 | |
| 
 | |
|   -- * The Zlib state monad
 | |
|   Stream,
 | |
|   State,
 | |
|   mkState,
 | |
|   runStream,
 | |
|   unsafeLiftIO,
 | |
|   finalise,
 | |
| 
 | |
|   -- * Initialisation
 | |
|   deflateInit, 
 | |
|   inflateInit,
 | |
| 
 | |
|   -- ** Initialisation parameters
 | |
|   Format(..),
 | |
|     gzipFormat,
 | |
|     zlibFormat,
 | |
|     rawFormat,
 | |
|     gzipOrZlibFormat,
 | |
|     formatSupportsDictionary,
 | |
|   CompressionLevel(..),
 | |
|     defaultCompression,
 | |
|     noCompression,
 | |
|     bestSpeed,
 | |
|     bestCompression,
 | |
|     compressionLevel,
 | |
|   Method(..),
 | |
|     deflateMethod,
 | |
|   WindowBits(..),
 | |
|     defaultWindowBits,
 | |
|     windowBits,
 | |
|   MemoryLevel(..),
 | |
|     defaultMemoryLevel,
 | |
|     minMemoryLevel,
 | |
|     maxMemoryLevel,
 | |
|     memoryLevel,
 | |
|   CompressionStrategy(..),
 | |
|     defaultStrategy,
 | |
|     filteredStrategy,
 | |
|     huffmanOnlyStrategy,
 | |
| 
 | |
|   -- * The buisness
 | |
|   deflate,
 | |
|   inflate,
 | |
|   Status(..),
 | |
|   Flush(..),
 | |
|   ErrorCode(..),
 | |
|   -- ** Special operations
 | |
|   inflateReset,
 | |
| 
 | |
|   -- * Buffer management
 | |
|   -- ** Input buffer
 | |
|   pushInputBuffer,
 | |
|   inputBufferEmpty,
 | |
|   popRemainingInputBuffer,
 | |
| 
 | |
|   -- ** Output buffer
 | |
|   pushOutputBuffer,
 | |
|   popOutputBuffer,
 | |
|   outputBufferBytesAvailable,
 | |
|   outputBufferSpaceRemaining,
 | |
|   outputBufferFull,
 | |
| 
 | |
|   -- ** Dictionary
 | |
|   deflateSetDictionary,
 | |
|   inflateSetDictionary,
 | |
| 
 | |
|   -- ** Dictionary hashes
 | |
|   DictionaryHash,
 | |
|   dictionaryHash,
 | |
|   zeroDictionaryHash,
 | |
| 
 | |
| #ifdef DEBUG
 | |
|   -- * Debugging
 | |
|   consistencyCheck,
 | |
|   dump,
 | |
|   trace,
 | |
| #endif
 | |
| 
 | |
|   ) where
 | |
| 
 | |
| -- Note we don't use the MIN_VERSION_* macros here for compatability with
 | |
| -- old Cabal versions that come with old GHC, that didn't provide these
 | |
| -- macros for .hsc files. So we use __GLASGOW_HASKELL__ as a proxy.
 | |
| 
 | |
| import Foreign
 | |
|          ( Word8, Ptr, nullPtr, plusPtr, peekByteOff, pokeByteOff
 | |
|          , ForeignPtr, FinalizerPtr, mallocForeignPtrBytes, addForeignPtrFinalizer
 | |
|          , withForeignPtr, touchForeignPtr, minusPtr )
 | |
| #if __GLASGOW_HASKELL__ >= 702
 | |
| import Foreign.ForeignPtr.Unsafe ( unsafeForeignPtrToPtr )
 | |
| import System.IO.Unsafe          ( unsafePerformIO )
 | |
| #else
 | |
| import Foreign ( unsafeForeignPtrToPtr, unsafePerformIO )
 | |
| #endif
 | |
| #ifdef __GLASGOW_HASKELL__
 | |
| import Foreign
 | |
|          ( finalizeForeignPtr )
 | |
| #endif
 | |
| import Foreign.C
 | |
| import Data.ByteString.Internal (nullForeignPtr)
 | |
| import qualified Data.ByteString.Unsafe as B
 | |
| import Data.ByteString (ByteString)
 | |
| #if !(__GLASGOW_HASKELL__ >= 710)
 | |
| import Control.Applicative (Applicative(..))
 | |
| #endif
 | |
| import Control.Monad (ap,liftM)
 | |
| #if MIN_VERSION_base(4,9,0)
 | |
| import qualified Control.Monad.Fail as Fail
 | |
| #endif
 | |
| #if __GLASGOW_HASKELL__ >= 702
 | |
| #if __GLASGOW_HASKELL__ >= 708
 | |
| import Control.Monad.ST.Strict
 | |
| #else
 | |
| import Control.Monad.ST.Strict hiding (unsafeIOToST)
 | |
| #endif
 | |
| import Control.Monad.ST.Unsafe
 | |
| #else
 | |
| import Control.Monad.ST.Strict
 | |
| #endif
 | |
| import Control.Exception (assert)
 | |
| import Data.Typeable (Typeable)
 | |
| #if __GLASGOW_HASKELL__ >= 702
 | |
| import GHC.Generics (Generic)
 | |
| #endif
 | |
| #ifdef DEBUG
 | |
| import System.IO (hPutStrLn, stderr)
 | |
| #endif
 | |
| 
 | |
| import Prelude hiding (length)
 | |
| 
 | |
| #include "zlib.h"
 | |
| 
 | |
| 
 | |
| pushInputBuffer :: ForeignPtr Word8 -> Int -> Int -> Stream ()
 | |
| pushInputBuffer inBuf' offset length = do
 | |
| 
 | |
|   -- must not push a new input buffer if the last one is not used up
 | |
|   inAvail <- getInAvail
 | |
|   assert (inAvail == 0) $ return ()
 | |
| 
 | |
|   -- Now that we're setting a new input buffer, we can be sure that zlib no
 | |
|   -- longer has a reference to the old one. Therefore this is the last point
 | |
|   -- at which the old buffer had to be retained. It's safe to release now.
 | |
|   inBuf <- getInBuf 
 | |
|   unsafeLiftIO $ touchForeignPtr inBuf    
 | |
| 
 | |
|   -- now set the available input buffer ptr and length
 | |
|   setInBuf   inBuf'
 | |
|   setInAvail length
 | |
|   setInNext  (unsafeForeignPtrToPtr inBuf' `plusPtr` offset)
 | |
|   -- Note the 'unsafe'. We are passing the raw ptr inside inBuf' to zlib.
 | |
|   -- To make this safe we need to hold on to the ForeignPtr for at least as
 | |
|   -- long as zlib is using the underlying raw ptr.
 | |
| 
 | |
| 
 | |
| inputBufferEmpty :: Stream Bool
 | |
| inputBufferEmpty = getInAvail >>= return . (==0)
 | |
| 
 | |
| 
 | |
| popRemainingInputBuffer :: Stream (ForeignPtr Word8, Int, Int)
 | |
| popRemainingInputBuffer = do
 | |
| 
 | |
|   inBuf    <- getInBuf
 | |
|   inNext   <- getInNext
 | |
|   inAvail  <- getInAvail
 | |
| 
 | |
|   -- there really should be something to pop, otherwise it's silly
 | |
|   assert (inAvail > 0) $ return ()
 | |
|   setInAvail 0
 | |
| 
 | |
|   return (inBuf, inNext `minusPtr` unsafeForeignPtrToPtr inBuf, inAvail)
 | |
| 
 | |
| 
 | |
| pushOutputBuffer :: ForeignPtr Word8 -> Int -> Int -> Stream ()
 | |
| pushOutputBuffer outBuf' offset length = do
 | |
| 
 | |
|   --must not push a new buffer if there is still data in the old one
 | |
|   outAvail <- getOutAvail
 | |
|   assert (outAvail == 0) $ return ()
 | |
|   -- Note that there may still be free space in the output buffer, that's ok,
 | |
|   -- you might not want to bother completely filling the output buffer say if
 | |
|   -- there's only a few free bytes left.
 | |
| 
 | |
|   outBuf <- getOutBuf
 | |
|   unsafeLiftIO $ touchForeignPtr outBuf
 | |
| 
 | |
|   -- now set the available input buffer ptr and length
 | |
|   setOutBuf  outBuf'
 | |
|   setOutFree length
 | |
|   setOutNext (unsafeForeignPtrToPtr outBuf' `plusPtr` offset)
 | |
| 
 | |
|   setOutOffset offset
 | |
|   setOutAvail  0
 | |
| 
 | |
| 
 | |
| -- get that part of the output buffer that is currently full
 | |
| -- (might be 0, use outputBufferBytesAvailable to check)
 | |
| -- this may leave some space remaining in the buffer, use
 | |
| -- outputBufferSpaceRemaining to check.
 | |
| popOutputBuffer :: Stream (ForeignPtr Word8, Int, Int)
 | |
| popOutputBuffer = do
 | |
| 
 | |
|   outBuf    <- getOutBuf
 | |
|   outOffset <- getOutOffset
 | |
|   outAvail  <- getOutAvail
 | |
| 
 | |
|   -- there really should be something to pop, otherwise it's silly
 | |
|   assert (outAvail > 0) $ return ()
 | |
| 
 | |
|   setOutOffset (outOffset + outAvail)
 | |
|   setOutAvail  0
 | |
| 
 | |
|   return (outBuf, outOffset, outAvail)
 | |
| 
 | |
| 
 | |
| -- this is the number of bytes available in the output buffer
 | |
| outputBufferBytesAvailable :: Stream Int
 | |
| outputBufferBytesAvailable = getOutAvail
 | |
| 
 | |
| 
 | |
| -- you needen't get all the output immediately, you can continue until
 | |
| -- there is no more output space available, this tells you that amount
 | |
| outputBufferSpaceRemaining :: Stream Int
 | |
| outputBufferSpaceRemaining = getOutFree
 | |
| 
 | |
| 
 | |
| -- you only need to supply a new buffer when there is no more output buffer
 | |
| -- space remaining
 | |
| outputBufferFull :: Stream Bool
 | |
| outputBufferFull = liftM (==0) outputBufferSpaceRemaining
 | |
| 
 | |
| 
 | |
| -- you can only run this when the output buffer is not empty
 | |
| -- you can run it when the input buffer is empty but it doesn't do anything
 | |
| -- after running deflate either the output buffer will be full
 | |
| -- or the input buffer will be empty (or both)
 | |
| deflate :: Flush -> Stream Status
 | |
| deflate flush = do
 | |
| 
 | |
|   outFree <- getOutFree
 | |
| 
 | |
|   -- deflate needs free space in the output buffer
 | |
|   assert (outFree > 0) $ return ()
 | |
| 
 | |
|   result <- deflate_ flush
 | |
|   outFree' <- getOutFree
 | |
|     
 | |
|   -- number of bytes of extra output there is available as a result of
 | |
|   -- the call to deflate:
 | |
|   let outExtra = outFree - outFree'
 | |
|   
 | |
|   outAvail <- getOutAvail
 | |
|   setOutAvail (outAvail + outExtra)
 | |
|   return result
 | |
| 
 | |
| 
 | |
| inflate :: Flush -> Stream Status
 | |
| inflate flush = do
 | |
| 
 | |
|   outFree <- getOutFree
 | |
| 
 | |
|   -- inflate needs free space in the output buffer
 | |
|   assert (outFree > 0) $ return ()
 | |
| 
 | |
|   result <- inflate_ flush
 | |
|   outFree' <- getOutFree
 | |
| 
 | |
|   -- number of bytes of extra output there is available as a result of
 | |
|   -- the call to inflate:
 | |
|   let outExtra = outFree - outFree'
 | |
| 
 | |
|   outAvail <- getOutAvail
 | |
|   setOutAvail (outAvail + outExtra)
 | |
|   return result
 | |
| 
 | |
| 
 | |
| inflateReset :: Stream ()
 | |
| inflateReset = do
 | |
| 
 | |
|   outAvail <- getOutAvail
 | |
|   inAvail  <- getInAvail
 | |
|   -- At the point where this is used, all the output should have been consumed
 | |
|   -- and any trailing input should be extracted and resupplied explicitly, not
 | |
|   -- just left.
 | |
|   assert (outAvail == 0 && inAvail == 0) $ return ()
 | |
| 
 | |
|   err <- withStreamState $ \zstream ->
 | |
|     c_inflateReset zstream
 | |
|   failIfError err
 | |
| 
 | |
| 
 | |
| 
 | |
| deflateSetDictionary :: ByteString -> Stream Status
 | |
| deflateSetDictionary dict = do
 | |
|   err <- withStreamState $ \zstream ->
 | |
|            B.unsafeUseAsCStringLen dict $ \(ptr, len) ->
 | |
|              c_deflateSetDictionary zstream ptr (fromIntegral len)
 | |
|   toStatus err
 | |
| 
 | |
| inflateSetDictionary :: ByteString -> Stream Status
 | |
| inflateSetDictionary dict = do
 | |
|   err <- withStreamState $ \zstream -> do
 | |
|            B.unsafeUseAsCStringLen dict $ \(ptr, len) ->
 | |
|              c_inflateSetDictionary zstream ptr (fromIntegral len)
 | |
|   toStatus err
 | |
| 
 | |
| -- | A hash of a custom compression dictionary. These hashes are used by
 | |
| -- zlib as dictionary identifiers.
 | |
| -- (The particular hash function used is Adler32.)
 | |
| --
 | |
| newtype DictionaryHash = DictHash CULong
 | |
|   deriving (Eq, Ord, Read, Show)
 | |
| 
 | |
| -- | Update a running 'DictionaryHash'. You can generate a 'DictionaryHash'
 | |
| -- from one or more 'ByteString's by starting from 'zeroDictionaryHash', e.g.
 | |
| --
 | |
| -- > dictionaryHash zeroDictionaryHash :: ByteString -> DictionaryHash
 | |
| --
 | |
| -- or
 | |
| --
 | |
| -- > foldl' dictionaryHash zeroDictionaryHash :: [ByteString] -> DictionaryHash
 | |
| --
 | |
| dictionaryHash :: DictionaryHash -> ByteString -> DictionaryHash
 | |
| dictionaryHash (DictHash adler) dict =
 | |
|   unsafePerformIO $
 | |
|     B.unsafeUseAsCStringLen dict $ \(ptr, len) ->
 | |
|       liftM DictHash $ c_adler32 adler ptr (fromIntegral len)
 | |
| 
 | |
| -- | A zero 'DictionaryHash' to use as the initial value with 'dictionaryHash'.
 | |
| --
 | |
| zeroDictionaryHash :: DictionaryHash
 | |
| zeroDictionaryHash = DictHash 0
 | |
| 
 | |
| ----------------------------
 | |
| -- Stream monad
 | |
| --
 | |
| 
 | |
| newtype Stream a = Z {
 | |
|     unZ :: ForeignPtr StreamState
 | |
|         -> ForeignPtr Word8
 | |
|         -> ForeignPtr Word8
 | |
|         -> Int -> Int
 | |
|         -> IO (ForeignPtr Word8
 | |
|               ,ForeignPtr Word8
 | |
|               ,Int, Int, a)
 | |
|   }
 | |
| 
 | |
| instance Functor Stream where
 | |
|   fmap   = liftM
 | |
| 
 | |
| instance Applicative Stream where
 | |
|   pure   = returnZ
 | |
|   (<*>)  = ap
 | |
|   (*>)   = thenZ_
 | |
| 
 | |
| instance Monad Stream where
 | |
|   (>>=)  = thenZ
 | |
| --  m >>= f = (m `thenZ` \a -> consistencyCheck `thenZ_` returnZ a) `thenZ` f
 | |
|   (>>)   = (*>)
 | |
| 
 | |
| #if !MIN_VERSION_base(4,8,0)
 | |
|   return = pure
 | |
| #endif
 | |
| 
 | |
| #if !MIN_VERSION_base(4,9,0)
 | |
|   fail   = (finalise >>) . failZ
 | |
| #elif !MIN_VERSION_base(4,13,0)
 | |
|   fail   = Fail.fail
 | |
| #endif
 | |
| 
 | |
| #if MIN_VERSION_base(4,9,0)
 | |
| instance Fail.MonadFail Stream where
 | |
|   fail   = (finalise >>) . failZ
 | |
| #endif
 | |
| 
 | |
| returnZ :: a -> Stream a
 | |
| returnZ a = Z $ \_ inBuf outBuf outOffset outLength ->
 | |
|                   return (inBuf, outBuf, outOffset, outLength, a)
 | |
| {-# INLINE returnZ #-}
 | |
| 
 | |
| thenZ :: Stream a -> (a -> Stream b) -> Stream b
 | |
| thenZ (Z m) f =
 | |
|   Z $ \stream inBuf outBuf outOffset outLength ->
 | |
|     m stream inBuf outBuf outOffset outLength >>=
 | |
|       \(inBuf', outBuf', outOffset', outLength', a) ->
 | |
|         unZ (f a) stream inBuf' outBuf' outOffset' outLength'
 | |
| {-# INLINE thenZ #-}
 | |
| 
 | |
| thenZ_ :: Stream a -> Stream b -> Stream b
 | |
| thenZ_ (Z m) f =
 | |
|   Z $ \stream inBuf outBuf outOffset outLength ->
 | |
|     m stream inBuf outBuf outOffset outLength >>=
 | |
|       \(inBuf', outBuf', outOffset', outLength', _) ->
 | |
|         unZ f stream inBuf' outBuf' outOffset' outLength'
 | |
| {-# INLINE thenZ_ #-}
 | |
| 
 | |
| failZ :: String -> Stream a
 | |
| failZ msg = Z (\_ _ _ _ _ -> fail ("Codec.Compression.Zlib: " ++ msg))
 | |
| 
 | |
| data State s = State !(ForeignPtr StreamState)
 | |
|                      !(ForeignPtr Word8)
 | |
|                      !(ForeignPtr Word8)
 | |
|       {-# UNPACK #-} !Int
 | |
|       {-# UNPACK #-} !Int
 | |
| 
 | |
| mkState :: ST s (State s)
 | |
| mkState = unsafeIOToST $ do
 | |
|   stream <- mallocForeignPtrBytes (#{const sizeof(z_stream)})
 | |
|   withForeignPtr stream $ \ptr -> do
 | |
|     #{poke z_stream, msg}       ptr nullPtr
 | |
|     #{poke z_stream, zalloc}    ptr nullPtr
 | |
|     #{poke z_stream, zfree}     ptr nullPtr
 | |
|     #{poke z_stream, opaque}    ptr nullPtr
 | |
|     #{poke z_stream, next_in}   ptr nullPtr
 | |
|     #{poke z_stream, next_out}  ptr nullPtr
 | |
|     #{poke z_stream, avail_in}  ptr (0 :: CUInt)
 | |
|     #{poke z_stream, avail_out} ptr (0 :: CUInt)
 | |
|   return (State stream nullForeignPtr nullForeignPtr 0 0)
 | |
| 
 | |
| runStream :: Stream a -> State s -> ST s (a, State s)
 | |
| runStream (Z m) (State stream inBuf outBuf outOffset outLength) =
 | |
|   unsafeIOToST $
 | |
|     m stream inBuf outBuf outOffset outLength >>=
 | |
|       \(inBuf', outBuf', outOffset', outLength', a) ->
 | |
|         return (a, State stream inBuf' outBuf' outOffset' outLength')
 | |
| 
 | |
| -- This is marked as unsafe because runStream uses unsafeIOToST so anything
 | |
| -- lifted here can end up being unsafePerformIO'd.
 | |
| unsafeLiftIO :: IO a -> Stream a
 | |
| unsafeLiftIO m = Z $ \_stream inBuf outBuf outOffset outLength -> do
 | |
|   a <- m
 | |
|   return (inBuf, outBuf, outOffset, outLength, a)
 | |
| 
 | |
| getStreamState :: Stream (ForeignPtr StreamState)
 | |
| getStreamState = Z $ \stream inBuf outBuf outOffset outLength -> do
 | |
|   return (inBuf, outBuf, outOffset, outLength, stream)
 | |
| 
 | |
| getInBuf :: Stream (ForeignPtr Word8)
 | |
| getInBuf = Z $ \_stream inBuf outBuf outOffset outLength -> do
 | |
|   return (inBuf, outBuf, outOffset, outLength, inBuf)
 | |
| 
 | |
| getOutBuf :: Stream (ForeignPtr Word8)
 | |
| getOutBuf = Z $ \_stream inBuf outBuf outOffset outLength -> do
 | |
|   return (inBuf, outBuf, outOffset, outLength, outBuf)
 | |
| 
 | |
| getOutOffset :: Stream Int
 | |
| getOutOffset = Z $ \_stream inBuf outBuf outOffset outLength -> do
 | |
|   return (inBuf, outBuf, outOffset, outLength, outOffset)
 | |
| 
 | |
| getOutAvail :: Stream Int
 | |
| getOutAvail = Z $ \_stream inBuf outBuf outOffset outLength -> do
 | |
|   return (inBuf, outBuf, outOffset, outLength, outLength)
 | |
| 
 | |
| setInBuf :: ForeignPtr Word8 -> Stream ()
 | |
| setInBuf inBuf = Z $ \_stream _ outBuf outOffset outLength -> do
 | |
|   return (inBuf, outBuf, outOffset, outLength, ())
 | |
| 
 | |
| setOutBuf :: ForeignPtr Word8 -> Stream ()
 | |
| setOutBuf outBuf = Z $ \_stream inBuf _ outOffset outLength -> do
 | |
|   return (inBuf, outBuf, outOffset, outLength, ())
 | |
| 
 | |
| setOutOffset :: Int -> Stream ()
 | |
| setOutOffset outOffset = Z $ \_stream inBuf outBuf _ outLength -> do
 | |
|   return (inBuf, outBuf, outOffset, outLength, ())
 | |
| 
 | |
| setOutAvail :: Int -> Stream ()
 | |
| setOutAvail outLength = Z $ \_stream inBuf outBuf outOffset _ -> do
 | |
|   return (inBuf, outBuf, outOffset, outLength, ())
 | |
| 
 | |
| ----------------------------
 | |
| -- Debug stuff
 | |
| --
 | |
| 
 | |
| #ifdef DEBUG
 | |
| trace :: String -> Stream ()
 | |
| trace = unsafeLiftIO . hPutStrLn stderr
 | |
| 
 | |
| dump :: Stream ()
 | |
| dump = do
 | |
|   inNext  <- getInNext
 | |
|   inAvail <- getInAvail
 | |
| 
 | |
|   outNext <- getOutNext
 | |
|   outFree <- getOutFree
 | |
|   outAvail <- getOutAvail
 | |
|   outOffset <- getOutOffset
 | |
| 
 | |
|   unsafeLiftIO $ hPutStrLn stderr $
 | |
|     "Stream {\n" ++
 | |
|     "  inNext    = " ++ show inNext    ++ ",\n" ++
 | |
|     "  inAvail   = " ++ show inAvail   ++ ",\n" ++
 | |
|     "\n" ++
 | |
|     "  outNext   = " ++ show outNext   ++ ",\n" ++
 | |
|     "  outFree   = " ++ show outFree   ++ ",\n" ++
 | |
|     "  outAvail  = " ++ show outAvail  ++ ",\n" ++
 | |
|     "  outOffset = " ++ show outOffset ++ "\n"  ++
 | |
|     "}"
 | |
| 
 | |
|   consistencyCheck
 | |
| 
 | |
| consistencyCheck :: Stream ()
 | |
| consistencyCheck = do
 | |
| 
 | |
|   outBuf    <- getOutBuf
 | |
|   outOffset <- getOutOffset
 | |
|   outAvail  <- getOutAvail
 | |
|   outNext   <- getOutNext
 | |
| 
 | |
|   let outBufPtr = unsafeForeignPtrToPtr outBuf
 | |
| 
 | |
|   assert (outBufPtr `plusPtr` (outOffset + outAvail) == outNext) $ return ()
 | |
| #endif
 | |
| 
 | |
| 
 | |
| ----------------------------
 | |
| -- zlib wrapper layer
 | |
| --
 | |
| 
 | |
| data Status =
 | |
|     Ok
 | |
|   | StreamEnd
 | |
|   | Error ErrorCode String
 | |
| 
 | |
| data ErrorCode =
 | |
|     NeedDict DictionaryHash
 | |
|   | FileError
 | |
|   | StreamError
 | |
|   | DataError
 | |
|   | MemoryError
 | |
|   | BufferError -- ^ No progress was possible or there was not enough room in
 | |
|                 --   the output buffer when 'Finish' is used. Note that
 | |
|                 --   'BuferError' is not fatal, and 'inflate' can be called
 | |
|                 --   again with more input and more output space to continue.
 | |
|   | VersionError
 | |
|   | Unexpected
 | |
| 
 | |
| toStatus :: CInt -> Stream Status
 | |
| toStatus errno = case errno of
 | |
|   (#{const Z_OK})            -> return Ok
 | |
|   (#{const Z_STREAM_END})    -> return StreamEnd
 | |
|   (#{const Z_NEED_DICT})     -> do
 | |
|     adler <- withStreamPtr (#{peek z_stream, adler})
 | |
|     err (NeedDict (DictHash adler))  "custom dictionary needed"
 | |
|   (#{const Z_BUF_ERROR})     -> err BufferError  "buffer error"
 | |
|   (#{const Z_ERRNO})         -> err FileError    "file error"
 | |
|   (#{const Z_STREAM_ERROR})  -> err StreamError  "stream error"
 | |
|   (#{const Z_DATA_ERROR})    -> err DataError    "data error"
 | |
|   (#{const Z_MEM_ERROR})     -> err MemoryError  "insufficient memory"
 | |
|   (#{const Z_VERSION_ERROR}) -> err VersionError "incompatible zlib version"
 | |
|   other                      -> return $ Error Unexpected
 | |
|                                   ("unexpected zlib status: " ++ show other)
 | |
|  where
 | |
|    err errCode altMsg = liftM (Error errCode) $ do
 | |
|     msgPtr <- withStreamPtr (#{peek z_stream, msg})
 | |
|     if msgPtr /= nullPtr
 | |
|      then unsafeLiftIO (peekCAString msgPtr)
 | |
|      else return altMsg
 | |
| 
 | |
| failIfError :: CInt -> Stream ()
 | |
| failIfError errno = toStatus errno >>= \status -> case status of
 | |
|   (Error _ msg) -> fail msg
 | |
|   _             -> return ()
 | |
| 
 | |
| 
 | |
| data Flush =
 | |
|     NoFlush
 | |
|   | SyncFlush
 | |
|   | FullFlush
 | |
|   | Finish
 | |
| --  | Block -- only available in zlib 1.2 and later, uncomment if you need it.
 | |
| 
 | |
| fromFlush :: Flush -> CInt
 | |
| fromFlush NoFlush   = #{const Z_NO_FLUSH}
 | |
| fromFlush SyncFlush = #{const Z_SYNC_FLUSH}
 | |
| fromFlush FullFlush = #{const Z_FULL_FLUSH}
 | |
| fromFlush Finish    = #{const Z_FINISH}
 | |
| --  fromFlush Block     = #{const Z_BLOCK}
 | |
| 
 | |
| 
 | |
| -- | The format used for compression or decompression. There are three
 | |
| -- variations.
 | |
| --
 | |
| data Format = GZip | Zlib | Raw | GZipOrZlib
 | |
|   deriving (Eq, Ord, Enum, Bounded, Show, Typeable
 | |
| #if __GLASGOW_HASKELL__ >= 702
 | |
|               , Generic
 | |
| #endif
 | |
|            )
 | |
| 
 | |
| {-# DEPRECATED GZip       "Use gzipFormat. Format constructors will be hidden in version 0.7"       #-}
 | |
| {-# DEPRECATED Zlib       "Use zlibFormat. Format constructors will be hidden in version 0.7"       #-}
 | |
| {-# DEPRECATED Raw        "Use rawFormat. Format constructors will be hidden in version 0.7"        #-}
 | |
| {-# DEPRECATED GZipOrZlib "Use gzipOrZlibFormat. Format constructors will be hidden in version 0.7" #-}
 | |
| 
 | |
| -- | The gzip format uses a header with a checksum and some optional meta-data
 | |
| -- about the compressed file. It is intended primarily for compressing
 | |
| -- individual files but is also sometimes used for network protocols such as
 | |
| -- HTTP. The format is described in detail in RFC #1952
 | |
| -- <http://www.ietf.org/rfc/rfc1952.txt>
 | |
| --
 | |
| gzipFormat :: Format
 | |
| gzipFormat = GZip
 | |
| 
 | |
| -- | The zlib format uses a minimal header with a checksum but no other
 | |
| -- meta-data. It is especially designed for use in network protocols. The
 | |
| -- format is described in detail in RFC #1950
 | |
| -- <http://www.ietf.org/rfc/rfc1950.txt>
 | |
| --
 | |
| zlibFormat :: Format
 | |
| zlibFormat = Zlib
 | |
| 
 | |
| -- | The \'raw\' format is just the compressed data stream without any
 | |
| -- additional header, meta-data or data-integrity checksum. The format is
 | |
| -- described in detail in RFC #1951 <http://www.ietf.org/rfc/rfc1951.txt>
 | |
| --
 | |
| rawFormat :: Format
 | |
| rawFormat = Raw
 | |
| 
 | |
| -- | This is not a format as such. It enabled zlib or gzip decoding with
 | |
| -- automatic header detection. This only makes sense for decompression.
 | |
| --
 | |
| gzipOrZlibFormat :: Format
 | |
| gzipOrZlibFormat = GZipOrZlib
 | |
| 
 | |
| formatSupportsDictionary :: Format -> Bool
 | |
| formatSupportsDictionary Zlib = True
 | |
| formatSupportsDictionary Raw  = True
 | |
| formatSupportsDictionary _    = False
 | |
| 
 | |
| -- | The compression method
 | |
| --
 | |
| data Method = Deflated
 | |
|   deriving (Eq, Ord, Enum, Bounded, Show, Typeable
 | |
| #if __GLASGOW_HASKELL__ >= 702
 | |
|               , Generic
 | |
| #endif
 | |
|            )
 | |
| 
 | |
| {-# DEPRECATED Deflated "Use deflateMethod. Method constructors will be hidden in version 0.7" #-}
 | |
| 
 | |
| -- | \'Deflate\' is the only method supported in this version of zlib.
 | |
| -- Indeed it is likely to be the only method that ever will be supported.
 | |
| --
 | |
| deflateMethod :: Method
 | |
| deflateMethod = Deflated
 | |
| 
 | |
| fromMethod :: Method -> CInt
 | |
| fromMethod Deflated = #{const Z_DEFLATED}
 | |
| 
 | |
| 
 | |
| -- | The compression level parameter controls the amount of compression. This
 | |
| -- is a trade-off between the amount of compression and the time required to do
 | |
| -- the compression.
 | |
| --
 | |
| data CompressionLevel = 
 | |
|     DefaultCompression
 | |
|   | NoCompression
 | |
|   | BestSpeed
 | |
|   | BestCompression
 | |
|   | CompressionLevel Int
 | |
|   deriving (Eq, Show, Typeable
 | |
| #if __GLASGOW_HASKELL__ >= 702
 | |
|               , Generic
 | |
| #endif
 | |
|            )
 | |
| 
 | |
| {-# DEPRECATED DefaultCompression "Use defaultCompression. CompressionLevel constructors will be hidden in version 0.7" #-}
 | |
| {-# DEPRECATED NoCompression      "Use noCompression. CompressionLevel constructors will be hidden in version 0.7"      #-}
 | |
| {-# DEPRECATED BestSpeed          "Use bestSpeed. CompressionLevel constructors will be hidden in version 0.7"          #-}
 | |
| {-# DEPRECATED BestCompression    "Use bestCompression. CompressionLevel constructors will be hidden in version 0.7"    #-}
 | |
| --FIXME: cannot deprecate constructor named the same as the type
 | |
| {- DEPRECATED CompressionLevel   "Use compressionLevel. CompressionLevel constructors will be hidden in version 0.7"   -}
 | |
| 
 | |
| -- | The default compression level is 6 (that is, biased towards higher
 | |
| -- compression at expense of speed).
 | |
| defaultCompression :: CompressionLevel
 | |
| defaultCompression = DefaultCompression
 | |
| 
 | |
| -- | No compression, just a block copy.
 | |
| noCompression :: CompressionLevel
 | |
| noCompression = CompressionLevel 0
 | |
| 
 | |
| -- | The fastest compression method (less compression)
 | |
| bestSpeed :: CompressionLevel
 | |
| bestSpeed = CompressionLevel 1
 | |
| 
 | |
| -- | The slowest compression method (best compression).
 | |
| bestCompression :: CompressionLevel
 | |
| bestCompression = CompressionLevel 9
 | |
| 
 | |
| -- | A specific compression level between 0 and 9.
 | |
| compressionLevel :: Int -> CompressionLevel
 | |
| compressionLevel n
 | |
|   | n >= 0 && n <= 9 = CompressionLevel n
 | |
|   | otherwise        = error "CompressionLevel must be in the range 0..9"
 | |
| 
 | |
| fromCompressionLevel :: CompressionLevel -> CInt
 | |
| fromCompressionLevel DefaultCompression   = -1
 | |
| fromCompressionLevel NoCompression        = 0
 | |
| fromCompressionLevel BestSpeed            = 1
 | |
| fromCompressionLevel BestCompression      = 9
 | |
| fromCompressionLevel (CompressionLevel n)
 | |
|            | n >= 0 && n <= 9 = fromIntegral n
 | |
|            | otherwise        = error "CompressLevel must be in the range 1..9"
 | |
| 
 | |
| 
 | |
| -- | This specifies the size of the compression window. Larger values of this
 | |
| -- parameter result in better compression at the expense of higher memory
 | |
| -- usage.
 | |
| --
 | |
| -- The compression window size is the value of the the window bits raised to
 | |
| -- the power 2. The window bits must be in the range @9..15@ which corresponds
 | |
| -- to compression window sizes of 512b to 32Kb. The default is 15 which is also
 | |
| -- the maximum size.
 | |
| --
 | |
| -- The total amount of memory used depends on the window bits and the
 | |
| -- 'MemoryLevel'. See the 'MemoryLevel' for the details.
 | |
| --
 | |
| data WindowBits = WindowBits Int
 | |
|                 | DefaultWindowBits -- This constructor must be last to make
 | |
|                                     -- the Ord instance work. The Ord instance
 | |
|                                     -- is used by the tests.
 | |
|                                     -- It makse sense because the default value
 | |
|                                     -- is is also the max value at 15.
 | |
|   deriving (Eq, Ord, Show, Typeable
 | |
| #if __GLASGOW_HASKELL__ >= 702
 | |
|               , Generic
 | |
| #endif
 | |
|            )
 | |
| 
 | |
| {-# DEPRECATED DefaultWindowBits  "Use defaultWindowBits. WindowBits constructors will be hidden in version 0.7" #-}
 | |
| --FIXME: cannot deprecate constructor named the same as the type
 | |
| {- DEPRECATED WindowBits         "Use windowBits. WindowBits constructors will be hidden in version 0.7"        -}
 | |
| 
 | |
| -- | The default 'WindowBits' is 15 which is also the maximum size.
 | |
| --
 | |
| defaultWindowBits :: WindowBits
 | |
| defaultWindowBits = WindowBits 15
 | |
| 
 | |
| -- | A specific compression window size, specified in bits in the range @9..15@
 | |
| --
 | |
| windowBits :: Int -> WindowBits
 | |
| windowBits n
 | |
|   | n >= 9 && n <= 15 = WindowBits n
 | |
|   | otherwise         = error "WindowBits must be in the range 9..15"
 | |
| 
 | |
| fromWindowBits :: Format -> WindowBits-> CInt
 | |
| fromWindowBits format bits = (formatModifier format) (checkWindowBits bits)
 | |
|   where checkWindowBits DefaultWindowBits = 15
 | |
|         checkWindowBits (WindowBits n)
 | |
|           | n >= 9 && n <= 15 = fromIntegral n
 | |
|           | otherwise         = error "WindowBits must be in the range 9..15"
 | |
|         formatModifier Zlib       = id
 | |
|         formatModifier GZip       = (+16)
 | |
|         formatModifier GZipOrZlib = (+32)
 | |
|         formatModifier Raw        = negate
 | |
| 
 | |
| 
 | |
| -- | The 'MemoryLevel' parameter specifies how much memory should be allocated
 | |
| -- for the internal compression state. It is a tradoff between memory usage,
 | |
| -- compression ratio and compression speed. Using more memory allows faster
 | |
| -- compression and a better compression ratio.
 | |
| --
 | |
| -- The total amount of memory used for compression depends on the 'WindowBits'
 | |
| -- and the 'MemoryLevel'. For decompression it depends only on the
 | |
| -- 'WindowBits'. The totals are given by the functions:
 | |
| --
 | |
| -- > compressTotal windowBits memLevel = 4 * 2^windowBits + 512 * 2^memLevel
 | |
| -- > decompressTotal windowBits = 2^windowBits
 | |
| --
 | |
| -- For example, for compression with the default @windowBits = 15@ and
 | |
| -- @memLevel = 8@ uses @256Kb@. So for example a network server with 100
 | |
| -- concurrent compressed streams would use @25Mb@. The memory per stream can be
 | |
| -- halved (at the cost of somewhat degraded and slower compressionby) by
 | |
| -- reducing the @windowBits@ and @memLevel@ by one.
 | |
| --
 | |
| -- Decompression takes less memory, the default @windowBits = 15@ corresponds
 | |
| -- to just @32Kb@.
 | |
| --
 | |
| data MemoryLevel =
 | |
|     DefaultMemoryLevel
 | |
|   | MinMemoryLevel
 | |
|   | MaxMemoryLevel
 | |
|   | MemoryLevel Int
 | |
|   deriving (Eq, Show, Typeable
 | |
| #if __GLASGOW_HASKELL__ >= 702
 | |
|               , Generic
 | |
| #endif
 | |
|            )
 | |
| 
 | |
| {-# DEPRECATED DefaultMemoryLevel "Use defaultMemoryLevel. MemoryLevel constructors will be hidden in version 0.7" #-}
 | |
| {-# DEPRECATED MinMemoryLevel     "Use minMemoryLevel. MemoryLevel constructors will be hidden in version 0.7"     #-}
 | |
| {-# DEPRECATED MaxMemoryLevel     "Use maxMemoryLevel. MemoryLevel constructors will be hidden in version 0.7"     #-}
 | |
| --FIXME: cannot deprecate constructor named the same as the type
 | |
| {- DEPRECATED MemoryLevel        "Use memoryLevel. MemoryLevel constructors will be hidden in version 0.7"        -}
 | |
| 
 | |
| -- | The default memory level. (Equivalent to @'memoryLevel' 8@)
 | |
| --
 | |
| defaultMemoryLevel :: MemoryLevel
 | |
| defaultMemoryLevel = MemoryLevel 8
 | |
| 
 | |
| -- | Use minimum memory. This is slow and reduces the compression ratio.
 | |
| -- (Equivalent to @'memoryLevel' 1@)
 | |
| --
 | |
| minMemoryLevel :: MemoryLevel
 | |
| minMemoryLevel = MemoryLevel 1
 | |
| 
 | |
| -- | Use maximum memory for optimal compression speed.
 | |
| -- (Equivalent to @'memoryLevel' 9@)
 | |
| --
 | |
| maxMemoryLevel :: MemoryLevel
 | |
| maxMemoryLevel = MemoryLevel 9
 | |
| 
 | |
| -- | A specific level in the range @1..9@
 | |
| --
 | |
| memoryLevel :: Int -> MemoryLevel
 | |
| memoryLevel n
 | |
|   | n >= 1 && n <= 9 = MemoryLevel n
 | |
|   | otherwise        = error "MemoryLevel must be in the range 1..9"
 | |
| 
 | |
| fromMemoryLevel :: MemoryLevel -> CInt
 | |
| fromMemoryLevel DefaultMemoryLevel = 8
 | |
| fromMemoryLevel MinMemoryLevel     = 1
 | |
| fromMemoryLevel MaxMemoryLevel     = 9
 | |
| fromMemoryLevel (MemoryLevel n)
 | |
|          | n >= 1 && n <= 9 = fromIntegral n
 | |
|          | otherwise        = error "MemoryLevel must be in the range 1..9"
 | |
| 
 | |
| 
 | |
| -- | The strategy parameter is used to tune the compression algorithm.
 | |
| --
 | |
| -- The strategy parameter only affects the compression ratio but not the
 | |
| -- correctness of the compressed output even if it is not set appropriately.
 | |
| --
 | |
| data CompressionStrategy =
 | |
|     DefaultStrategy
 | |
|   | Filtered
 | |
|   | HuffmanOnly
 | |
|   deriving (Eq, Ord, Enum, Bounded, Show, Typeable
 | |
| #if __GLASGOW_HASKELL__ >= 702
 | |
|               , Generic
 | |
| #endif
 | |
|            )
 | |
| 
 | |
| {-
 | |
| -- -- only available in zlib 1.2 and later, uncomment if you need it.
 | |
|   | RLE             -- ^ Use 'RLE' to limit match distances to one (run-length
 | |
|                     --   encoding). 'RLE' is designed to be almost as fast as
 | |
|                     --   'HuffmanOnly', but give better compression for PNG
 | |
|                     --   image data.
 | |
|   | Fixed           -- ^ 'Fixed' prevents the use of dynamic Huffman codes,
 | |
|                     --   allowing for a simpler decoder for special applications.
 | |
| -}
 | |
| 
 | |
| {-# DEPRECATED DefaultStrategy "Use defaultStrategy. CompressionStrategy constructors will be hidden in version 0.7"     #-}
 | |
| {-# DEPRECATED Filtered        "Use filteredStrategy. CompressionStrategy constructors will be hidden in version 0.7"    #-}
 | |
| {-# DEPRECATED HuffmanOnly     "Use huffmanOnlyStrategy. CompressionStrategy constructors will be hidden in version 0.7" #-}
 | |
| 
 | |
| -- | Use this default compression strategy for normal data.
 | |
| --
 | |
| defaultStrategy :: CompressionStrategy
 | |
| defaultStrategy = DefaultStrategy
 | |
| 
 | |
| -- | Use the filtered compression strategy for data produced by a filter (or
 | |
| -- predictor). Filtered data consists mostly of small values with a somewhat
 | |
| -- random distribution. In this case, the compression algorithm is tuned to
 | |
| -- compress them better. The effect of this strategy is to force more Huffman
 | |
| -- coding and less string matching; it is somewhat intermediate between
 | |
| -- 'defaultCompressionStrategy' and 'huffmanOnlyCompressionStrategy'.
 | |
| --
 | |
| filteredStrategy :: CompressionStrategy
 | |
| filteredStrategy = Filtered
 | |
| 
 | |
| -- | Use the Huffman-only compression strategy to force Huffman encoding only
 | |
| -- (no string match).
 | |
| --
 | |
| huffmanOnlyStrategy :: CompressionStrategy
 | |
| huffmanOnlyStrategy = HuffmanOnly
 | |
| 
 | |
| 
 | |
| fromCompressionStrategy :: CompressionStrategy -> CInt
 | |
| fromCompressionStrategy DefaultStrategy = #{const Z_DEFAULT_STRATEGY}
 | |
| fromCompressionStrategy Filtered        = #{const Z_FILTERED}
 | |
| fromCompressionStrategy HuffmanOnly     = #{const Z_HUFFMAN_ONLY}
 | |
| --fromCompressionStrategy RLE             = #{const Z_RLE}
 | |
| --fromCompressionStrategy Fixed           = #{const Z_FIXED}
 | |
| 
 | |
| 
 | |
| withStreamPtr :: (Ptr StreamState -> IO a) -> Stream a
 | |
| withStreamPtr f = do
 | |
|   stream <- getStreamState
 | |
|   unsafeLiftIO (withForeignPtr stream f)
 | |
| 
 | |
| withStreamState :: (StreamState -> IO a) -> Stream a
 | |
| withStreamState f = do
 | |
|   stream <- getStreamState
 | |
|   unsafeLiftIO (withForeignPtr stream (f . StreamState))
 | |
| 
 | |
| setInAvail :: Int -> Stream ()
 | |
| setInAvail val = withStreamPtr $ \ptr ->
 | |
|   #{poke z_stream, avail_in} ptr (fromIntegral val :: CUInt)
 | |
| 
 | |
| getInAvail :: Stream Int
 | |
| getInAvail = liftM (fromIntegral :: CUInt -> Int) $
 | |
|   withStreamPtr (#{peek z_stream, avail_in})
 | |
| 
 | |
| setInNext :: Ptr Word8 -> Stream ()
 | |
| setInNext val = withStreamPtr (\ptr -> #{poke z_stream, next_in} ptr val)
 | |
| 
 | |
| getInNext :: Stream (Ptr Word8)
 | |
| getInNext = withStreamPtr (#{peek z_stream, next_in})
 | |
| 
 | |
| setOutFree :: Int -> Stream ()
 | |
| setOutFree val = withStreamPtr $ \ptr ->
 | |
|   #{poke z_stream, avail_out} ptr (fromIntegral val :: CUInt)
 | |
| 
 | |
| getOutFree :: Stream Int
 | |
| getOutFree = liftM (fromIntegral :: CUInt -> Int) $
 | |
|   withStreamPtr (#{peek z_stream, avail_out})
 | |
| 
 | |
| setOutNext  :: Ptr Word8 -> Stream ()
 | |
| setOutNext val = withStreamPtr (\ptr -> #{poke z_stream, next_out} ptr val)
 | |
| 
 | |
| #ifdef DEBUG
 | |
| getOutNext :: Stream (Ptr Word8)
 | |
| getOutNext = withStreamPtr (#{peek z_stream, next_out})
 | |
| #endif
 | |
| 
 | |
| inflateInit :: Format -> WindowBits -> Stream ()
 | |
| inflateInit format bits = do
 | |
|   checkFormatSupported format
 | |
|   err <- withStreamState $ \zstream ->
 | |
|     c_inflateInit2 zstream (fromIntegral (fromWindowBits format bits))
 | |
|   failIfError err
 | |
|   getStreamState >>= unsafeLiftIO . addForeignPtrFinalizer c_inflateEnd
 | |
| 
 | |
| deflateInit :: Format
 | |
|             -> CompressionLevel
 | |
|             -> Method
 | |
|             -> WindowBits
 | |
|             -> MemoryLevel
 | |
|             -> CompressionStrategy
 | |
|             -> Stream ()
 | |
| deflateInit format compLevel method bits memLevel strategy = do
 | |
|   checkFormatSupported format
 | |
|   err <- withStreamState $ \zstream ->
 | |
|     c_deflateInit2 zstream
 | |
|                   (fromCompressionLevel compLevel)
 | |
|                   (fromMethod method)
 | |
|                   (fromWindowBits format bits)
 | |
|                   (fromMemoryLevel memLevel)
 | |
|                   (fromCompressionStrategy strategy)
 | |
|   failIfError err
 | |
|   getStreamState >>= unsafeLiftIO . addForeignPtrFinalizer c_deflateEnd
 | |
| 
 | |
| inflate_ :: Flush -> Stream Status
 | |
| inflate_ flush = do
 | |
|   err <- withStreamState $ \zstream ->
 | |
|     c_inflate zstream (fromFlush flush)
 | |
|   toStatus err
 | |
| 
 | |
| deflate_ :: Flush -> Stream Status
 | |
| deflate_ flush = do
 | |
|   err <- withStreamState $ \zstream ->
 | |
|     c_deflate zstream (fromFlush flush)
 | |
|   toStatus err
 | |
| 
 | |
| -- | This never needs to be used as the stream's resources will be released
 | |
| -- automatically when no longer needed, however this can be used to release
 | |
| -- them early. Only use this when you can guarantee that the stream will no
 | |
| -- longer be needed, for example if an error occurs or if the stream ends.
 | |
| --
 | |
| finalise :: Stream ()
 | |
| #ifdef __GLASGOW_HASKELL__
 | |
| --TODO: finalizeForeignPtr is ghc-only
 | |
| finalise = getStreamState >>= unsafeLiftIO . finalizeForeignPtr
 | |
| #else
 | |
| finalise = return ()
 | |
| #endif
 | |
| 
 | |
| checkFormatSupported :: Format -> Stream ()
 | |
| checkFormatSupported format = do
 | |
|   version <- unsafeLiftIO (peekCAString =<< c_zlibVersion)
 | |
|   case version of
 | |
|     ('1':'.':'1':'.':_)
 | |
|        | format == GZip
 | |
|       || format == GZipOrZlib
 | |
|       -> fail $ "version 1.1.x of the zlib C library does not support the"
 | |
|              ++ " 'gzip' format via the in-memory api, only the 'raw' and "
 | |
|              ++ " 'zlib' formats."
 | |
|     _ -> return ()
 | |
| 
 | |
| ----------------------
 | |
| -- The foreign imports
 | |
| 
 | |
| newtype StreamState = StreamState (Ptr StreamState)
 | |
| 
 | |
| -- inflateInit2 and deflateInit2 are actually defined as macros in zlib.h
 | |
| -- They are defined in terms of inflateInit2_ and deflateInit2_ passing two
 | |
| -- additional arguments used to detect compatability problems. They pass the
 | |
| -- version of zlib as a char * and the size of the z_stream struct.
 | |
| -- If we compile via C then we can avoid this hassle however thats not really
 | |
| -- kosher since the Haskell FFI is defined at the C ABI level, not the C
 | |
| -- language level. There is no requirement to compile via C and pick up C
 | |
| -- headers. So it's much better if we can make it work properly and that'd
 | |
| -- also allow compiling via ghc's ncg which is a good thing since the C
 | |
| -- backend is not going to be around forever.
 | |
| --
 | |
| -- So we define c_inflateInit2 and c_deflateInit2 here as wrappers around
 | |
| -- their _ counterparts and pass the extra args.
 | |
| --
 | |
| -- As of GHC 7.6, we can import macros directly using the CApiFFI extension.
 | |
| -- This avoids the need for the hsc2hs #{const_str} construct, which means
 | |
| -- hsc2hs can run in cross-compilation mode.
 | |
| 
 | |
| ##ifdef NON_BLOCKING_FFI
 | |
| ##define SAFTY safe
 | |
| ##else
 | |
| ##define SAFTY unsafe
 | |
| ##endif
 | |
| 
 | |
| #if __GLASGOW_HASKELL__ >= 706
 | |
| foreign import capi unsafe "zlib.h inflateInit2"
 | |
|   c_inflateInit2 :: StreamState -> CInt -> IO CInt
 | |
|  
 | |
| foreign import capi unsafe "zlib.h deflateInit2"
 | |
|   c_deflateInit2 :: StreamState
 | |
|                  -> CInt -> CInt -> CInt -> CInt -> CInt -> IO CInt
 | |
| #else
 | |
| foreign import ccall unsafe "zlib.h inflateInit2_"
 | |
|   c_inflateInit2_ :: StreamState -> CInt -> Ptr CChar -> CInt -> IO CInt
 | |
| 
 | |
| c_inflateInit2 :: StreamState -> CInt -> IO CInt
 | |
| c_inflateInit2 z n =
 | |
|   withCAString #{const_str ZLIB_VERSION} $ \versionStr ->
 | |
|     c_inflateInit2_ z n versionStr (#{const sizeof(z_stream)} :: CInt)
 | |
| 
 | |
| foreign import ccall unsafe "zlib.h deflateInit2_"
 | |
|   c_deflateInit2_ :: StreamState
 | |
|                   -> CInt -> CInt -> CInt -> CInt -> CInt
 | |
|                   -> Ptr CChar -> CInt
 | |
|                   -> IO CInt
 | |
| 
 | |
| c_deflateInit2 :: StreamState
 | |
|                -> CInt -> CInt -> CInt -> CInt -> CInt -> IO CInt
 | |
| c_deflateInit2 z a b c d e =
 | |
|   withCAString #{const_str ZLIB_VERSION} $ \versionStr ->
 | |
|     c_deflateInit2_ z a b c d e versionStr (#{const sizeof(z_stream)} :: CInt)
 | |
| #endif
 | |
| 
 | |
| foreign import ccall SAFTY "zlib.h inflate"
 | |
|   c_inflate :: StreamState -> CInt -> IO CInt
 | |
| 
 | |
| foreign import ccall unsafe "zlib.h &inflateEnd"
 | |
|   c_inflateEnd :: FinalizerPtr StreamState
 | |
| 
 | |
| foreign import ccall unsafe "zlib.h inflateReset"
 | |
|   c_inflateReset :: StreamState -> IO CInt
 | |
| 
 | |
| foreign import ccall unsafe "zlib.h deflateSetDictionary"
 | |
|   c_deflateSetDictionary :: StreamState
 | |
|                          -> Ptr CChar
 | |
|                          -> CUInt
 | |
|                          -> IO CInt
 | |
| 
 | |
| foreign import ccall unsafe "zlib.h inflateSetDictionary"
 | |
|   c_inflateSetDictionary :: StreamState
 | |
|                          -> Ptr CChar
 | |
|                          -> CUInt
 | |
|                          -> IO CInt
 | |
| 
 | |
| foreign import ccall SAFTY "zlib.h deflate"
 | |
|   c_deflate :: StreamState -> CInt -> IO CInt
 | |
| 
 | |
| foreign import ccall unsafe "zlib.h &deflateEnd"
 | |
|   c_deflateEnd :: FinalizerPtr StreamState
 | |
| 
 | |
| foreign import ccall unsafe "zlib.h zlibVersion"
 | |
|   c_zlibVersion :: IO CString
 | |
| 
 | |
| foreign import ccall unsafe "zlib.h adler32"
 | |
|   c_adler32 :: CULong
 | |
|             -> Ptr CChar
 | |
|             -> CUInt
 | |
|             -> IO CULong
 |