{-# LINE 1 "Codec/Compression/Zlib/Stream.hsc" #-} {-# LANGUAGE CPP, ForeignFunctionInterface, DeriveDataTypeable #-} {-# LINE 3 "Codec/Compression/Zlib/Stream.hsc" #-} {-# LANGUAGE DeriveGeneric #-} {-# LINE 5 "Codec/Compression/Zlib/Stream.hsc" #-} {-# LINE 6 "Codec/Compression/Zlib/Stream.hsc" #-} {-# LANGUAGE CApiFFI #-} {-# LINE 8 "Codec/Compression/Zlib/Stream.hsc" #-} ----------------------------------------------------------------------------- -- | -- 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, {-# LINE 97 "Codec/Compression/Zlib/Stream.hsc" #-} ) 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 ) {-# LINE 109 "Codec/Compression/Zlib/Stream.hsc" #-} import Foreign.ForeignPtr.Unsafe ( unsafeForeignPtrToPtr ) import System.IO.Unsafe ( unsafePerformIO ) {-# LINE 114 "Codec/Compression/Zlib/Stream.hsc" #-} {-# LINE 115 "Codec/Compression/Zlib/Stream.hsc" #-} import Foreign ( finalizeForeignPtr ) {-# LINE 118 "Codec/Compression/Zlib/Stream.hsc" #-} import Foreign.C import Data.ByteString.Internal (nullForeignPtr) import qualified Data.ByteString.Unsafe as B import Data.ByteString (ByteString) {-# LINE 125 "Codec/Compression/Zlib/Stream.hsc" #-} import Control.Monad (ap,liftM) {-# LINE 127 "Codec/Compression/Zlib/Stream.hsc" #-} {-# LINE 128 "Codec/Compression/Zlib/Stream.hsc" #-} import Control.Monad.ST.Strict {-# LINE 132 "Codec/Compression/Zlib/Stream.hsc" #-} import Control.Monad.ST.Unsafe {-# LINE 136 "Codec/Compression/Zlib/Stream.hsc" #-} import Control.Exception (assert) import Data.Typeable (Typeable) {-# LINE 139 "Codec/Compression/Zlib/Stream.hsc" #-} import GHC.Generics (Generic) {-# LINE 141 "Codec/Compression/Zlib/Stream.hsc" #-} {-# LINE 144 "Codec/Compression/Zlib/Stream.hsc" #-} import Prelude hiding (length) 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 (>>) = (*>) return = pure fail = (finalise >>) . failZ 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 (112) {-# LINE 412 "Codec/Compression/Zlib/Stream.hsc" #-} withForeignPtr stream $ \ptr -> do (\hsc_ptr -> pokeByteOff hsc_ptr 48) ptr nullPtr {-# LINE 414 "Codec/Compression/Zlib/Stream.hsc" #-} (\hsc_ptr -> pokeByteOff hsc_ptr 64) ptr nullPtr {-# LINE 415 "Codec/Compression/Zlib/Stream.hsc" #-} (\hsc_ptr -> pokeByteOff hsc_ptr 72) ptr nullPtr {-# LINE 416 "Codec/Compression/Zlib/Stream.hsc" #-} (\hsc_ptr -> pokeByteOff hsc_ptr 80) ptr nullPtr {-# LINE 417 "Codec/Compression/Zlib/Stream.hsc" #-} (\hsc_ptr -> pokeByteOff hsc_ptr 0) ptr nullPtr {-# LINE 418 "Codec/Compression/Zlib/Stream.hsc" #-} (\hsc_ptr -> pokeByteOff hsc_ptr 24) ptr nullPtr {-# LINE 419 "Codec/Compression/Zlib/Stream.hsc" #-} (\hsc_ptr -> pokeByteOff hsc_ptr 8) ptr (0 :: CUInt) {-# LINE 420 "Codec/Compression/Zlib/Stream.hsc" #-} (\hsc_ptr -> pokeByteOff hsc_ptr 32) ptr (0 :: CUInt) {-# LINE 421 "Codec/Compression/Zlib/Stream.hsc" #-} 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 -- {-# LINE 516 "Codec/Compression/Zlib/Stream.hsc" #-} ---------------------------- -- 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 (0) -> return Ok {-# LINE 543 "Codec/Compression/Zlib/Stream.hsc" #-} (1) -> return StreamEnd {-# LINE 544 "Codec/Compression/Zlib/Stream.hsc" #-} (2) -> do {-# LINE 545 "Codec/Compression/Zlib/Stream.hsc" #-} adler <- withStreamPtr ((\hsc_ptr -> peekByteOff hsc_ptr 96)) {-# LINE 546 "Codec/Compression/Zlib/Stream.hsc" #-} err (NeedDict (DictHash adler)) "custom dictionary needed" (-5) -> err BufferError "buffer error" {-# LINE 548 "Codec/Compression/Zlib/Stream.hsc" #-} (-1) -> err FileError "file error" {-# LINE 549 "Codec/Compression/Zlib/Stream.hsc" #-} (-2) -> err StreamError "stream error" {-# LINE 550 "Codec/Compression/Zlib/Stream.hsc" #-} (-3) -> err DataError "data error" {-# LINE 551 "Codec/Compression/Zlib/Stream.hsc" #-} (-4) -> err MemoryError "insufficient memory" {-# LINE 552 "Codec/Compression/Zlib/Stream.hsc" #-} (-6) -> err VersionError "incompatible zlib version" {-# LINE 553 "Codec/Compression/Zlib/Stream.hsc" #-} other -> return $ Error Unexpected ("unexpected zlib status: " ++ show other) where err errCode altMsg = liftM (Error errCode) $ do msgPtr <- withStreamPtr ((\hsc_ptr -> peekByteOff hsc_ptr 48)) {-# LINE 558 "Codec/Compression/Zlib/Stream.hsc" #-} 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 = 0 {-# LINE 577 "Codec/Compression/Zlib/Stream.hsc" #-} fromFlush SyncFlush = 2 {-# LINE 578 "Codec/Compression/Zlib/Stream.hsc" #-} fromFlush FullFlush = 3 {-# LINE 579 "Codec/Compression/Zlib/Stream.hsc" #-} fromFlush Finish = 4 {-# LINE 580 "Codec/Compression/Zlib/Stream.hsc" #-} -- 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 {-# LINE 589 "Codec/Compression/Zlib/Stream.hsc" #-} , Generic {-# LINE 591 "Codec/Compression/Zlib/Stream.hsc" #-} ) {-# 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 {-# LINE 638 "Codec/Compression/Zlib/Stream.hsc" #-} , Generic {-# LINE 640 "Codec/Compression/Zlib/Stream.hsc" #-} ) {-# 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 = 8 {-# LINE 652 "Codec/Compression/Zlib/Stream.hsc" #-} -- | 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 {-# LINE 666 "Codec/Compression/Zlib/Stream.hsc" #-} , Generic {-# LINE 668 "Codec/Compression/Zlib/Stream.hsc" #-} ) {-# 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 {-# LINE 730 "Codec/Compression/Zlib/Stream.hsc" #-} , Generic {-# LINE 732 "Codec/Compression/Zlib/Stream.hsc" #-} ) {-# 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 {-# LINE 790 "Codec/Compression/Zlib/Stream.hsc" #-} , Generic {-# LINE 792 "Codec/Compression/Zlib/Stream.hsc" #-} ) {-# 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 {-# LINE 844 "Codec/Compression/Zlib/Stream.hsc" #-} , Generic {-# LINE 846 "Codec/Compression/Zlib/Stream.hsc" #-} ) {- -- -- 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 = 0 {-# LINE 886 "Codec/Compression/Zlib/Stream.hsc" #-} fromCompressionStrategy Filtered = 1 {-# LINE 887 "Codec/Compression/Zlib/Stream.hsc" #-} fromCompressionStrategy HuffmanOnly = 2 {-# LINE 888 "Codec/Compression/Zlib/Stream.hsc" #-} --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 -> (\hsc_ptr -> pokeByteOff hsc_ptr 8) ptr (fromIntegral val :: CUInt) {-# LINE 905 "Codec/Compression/Zlib/Stream.hsc" #-} getInAvail :: Stream Int getInAvail = liftM (fromIntegral :: CUInt -> Int) $ withStreamPtr ((\hsc_ptr -> peekByteOff hsc_ptr 8)) {-# LINE 909 "Codec/Compression/Zlib/Stream.hsc" #-} setInNext :: Ptr Word8 -> Stream () setInNext val = withStreamPtr (\ptr -> (\hsc_ptr -> pokeByteOff hsc_ptr 0) ptr val) {-# LINE 912 "Codec/Compression/Zlib/Stream.hsc" #-} getInNext :: Stream (Ptr Word8) getInNext = withStreamPtr ((\hsc_ptr -> peekByteOff hsc_ptr 0)) {-# LINE 915 "Codec/Compression/Zlib/Stream.hsc" #-} setOutFree :: Int -> Stream () setOutFree val = withStreamPtr $ \ptr -> (\hsc_ptr -> pokeByteOff hsc_ptr 32) ptr (fromIntegral val :: CUInt) {-# LINE 919 "Codec/Compression/Zlib/Stream.hsc" #-} getOutFree :: Stream Int getOutFree = liftM (fromIntegral :: CUInt -> Int) $ withStreamPtr ((\hsc_ptr -> peekByteOff hsc_ptr 32)) {-# LINE 923 "Codec/Compression/Zlib/Stream.hsc" #-} setOutNext :: Ptr Word8 -> Stream () setOutNext val = withStreamPtr (\ptr -> (\hsc_ptr -> pokeByteOff hsc_ptr 24) ptr val) {-# LINE 926 "Codec/Compression/Zlib/Stream.hsc" #-} {-# LINE 931 "Codec/Compression/Zlib/Stream.hsc" #-} 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 () {-# LINE 978 "Codec/Compression/Zlib/Stream.hsc" #-} --TODO: finalizeForeignPtr is ghc-only finalise = getStreamState >>= unsafeLiftIO . finalizeForeignPtr {-# LINE 983 "Codec/Compression/Zlib/Stream.hsc" #-} 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 {-# LINE 1026 "Codec/Compression/Zlib/Stream.hsc" #-} 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 {-# LINE 1053 "Codec/Compression/Zlib/Stream.hsc" #-} 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