packages feed

pipes-zlib 0.4.3 → 0.4.4

raw patch · 5 files changed

+213/−54 lines, 5 filesdep +HUnitdep +QuickCheckdep +pipes-zlibdep −zlibdep −zlib-bindingsdep ~basedep ~bytestringdep ~pipesPVP ok

version bump matches the API change (PVP)

Dependencies added: HUnit, QuickCheck, pipes-zlib, quickcheck-instances, streaming-commons, tasty, tasty-hunit, tasty-quickcheck

Dependencies removed: zlib, zlib-bindings

Dependency ranges changed: base, bytestring, pipes, transformers

API changes (from Hackage documentation)

+ Pipes.GZip: data CompressionLevel
+ Pipes.GZip: decompress' :: MonadIO m => Producer ByteString m r -> Producer ByteString m (Either (Producer ByteString m r) r)
+ Pipes.Zlib: data CompressionLevel
+ Pipes.Zlib: decompress' :: MonadIO m => WindowBits -> Producer ByteString m r -> Producer ByteString m (Either (Producer ByteString m r) r)
+ Pipes.Zlib: instance GHC.Classes.Eq Pipes.Zlib.CompressionLevel
+ Pipes.Zlib: instance GHC.Classes.Ord Pipes.Zlib.CompressionLevel
+ Pipes.Zlib: instance GHC.Read.Read Pipes.Zlib.CompressionLevel
+ Pipes.Zlib: instance GHC.Show.Show Pipes.Zlib.CompressionLevel

Files

changelog.md view
@@ -1,3 +1,14 @@+# Version 0.4.4++* Depend on `streaming-commons` instead of `zlib` and `zlib-bindings`,+  as the latter are deprecated.++* Add `Pipes.Zlib.decompress'` and `Pipes.GZip.decompress'`.++* Bump upper bound dependency on `transformers`.++* Add tests.+ # Version 0.4.3  * Fix usage of the `Producer'` type synonym (#14).
pipes-zlib.cabal view
@@ -1,5 +1,5 @@ name:               pipes-zlib-version:            0.4.3+version:            0.4.4 license:            BSD3 license-file:       LICENSE Copyright:          Copyright (c) Paolo Capriotti 2012,@@ -24,10 +24,30 @@     hs-source-dirs:  src     exposed-modules: Pipes.Zlib                      Pipes.GZip-    build-depends:   base             (>= 4.5 && < 5.0)-                   , transformers     (>= 0.2 && < 0.5)-                   , pipes            (>= 4.0 && < 4.2)-                   , bytestring       (>= 0.9.2.1)-                   , zlib             (>= 0.5 && < 0.7)-                   , zlib-bindings    (>= 0.1 && < 0.2)+    build-depends:   base >= 4.5 && < 5.0+                   , transformers >= 0.2 && < 0.6+                   , pipes >= 4.0 && < 4.2+                   , bytestring >= 0.9.2.1+                   , streaming-commons >= 0.1.15 && < 0.2     ghc-options: -Wall -O2++test-suite test+  type:                exitcode-stdio-1.0+  hs-source-dirs:      test+  main-is:             Main.hs+  build-depends:       base+                     , pipes-zlib+                     , pipes+                     , bytestring+                     , HUnit+                     , QuickCheck+                     , quickcheck-instances+                     , tasty+                     , tasty-quickcheck+                     , tasty-hunit+  ghc-options:         -threaded -rtsopts -with-rtsopts=-N+  default-language:    Haskell2010++source-repository head+  type:     git+  location: https://github.com/githubuser/twio
src/Pipes/GZip.hs view
@@ -6,18 +6,19 @@ module Pipes.GZip (   -- * Streams     decompress+  , decompress'   , compress    -- * Compression level-  -- $ccz-re-export-  , ZC.defaultCompression-  , ZC.noCompression-  , ZC.bestSpeed-  , ZC.bestCompression-  , ZC.compressionLevel+  , Pipes.Zlib.CompressionLevel+  , Pipes.Zlib.defaultCompression+  , Pipes.Zlib.noCompression+  , Pipes.Zlib.bestSpeed+  , Pipes.Zlib.bestCompression+  , Pipes.Zlib.compressionLevel   ) where -import qualified Codec.Compression.Zlib    as ZC+import qualified Data.Streaming.Zlib       as Zlib import qualified Data.ByteString           as B import           Pipes import qualified Pipes.Zlib@@ -35,11 +36,21 @@   :: MonadIO m   => Proxy x' x () B.ByteString m r -- ^ Compressed stream   -> Proxy x' x () B.ByteString m r -- ^ Decompressed stream-  -- -> Producer' B.ByteString m r -- ^ Decompressed stream-decompress = Pipes.Zlib.decompress (ZC.WindowBits 31)+decompress = Pipes.Zlib.decompress gzWindowBits {-# INLINABLE decompress #-} +-- | Decompress bytes flowing from a 'Producer', returning any leftover input+-- that follows the compressed stream.+decompress'+  :: MonadIO m+  => Producer B.ByteString m r -- ^ Compressed stream+  -> Producer B.ByteString m (Either (Producer B.ByteString m r) r)+     -- ^ Decompressed stream, returning either a 'Producer' of the leftover input+     -- or the return value from the input 'Producer'.+decompress' = Pipes.Zlib.decompress' gzWindowBits+{-# INLINABLE decompress' #-} + -- | Compress bytes flowing from a 'Producer'. -- -- @@@ -50,8 +61,11 @@ -- @ compress   :: MonadIO m-  => ZC.CompressionLevel+  => Pipes.Zlib.CompressionLevel   -> Proxy x' x () B.ByteString m r -- ^ Decompressed stream   -> Proxy x' x () B.ByteString m r -- ^ Compressed stream-compress clevel = Pipes.Zlib.compress clevel (ZC.WindowBits 31)+compress clevel = Pipes.Zlib.compress clevel gzWindowBits {-# INLINABLE compress #-}++gzWindowBits :: Zlib.WindowBits+gzWindowBits = Zlib.WindowBits 31
src/Pipes/Zlib.hs view
@@ -1,4 +1,5 @@ {-# LANGUAGE RankNTypes #-}+{-# LANGUAGE ScopedTypeVariables #-}  -- | This module exports utilities to compress and decompress @pipes@ streams -- using the zlib compression codec.@@ -9,24 +10,25 @@ module Pipes.Zlib (   -- * Streams     decompress+  , decompress'   , compress -  -- * Compression level-  -- $ccz-re-export-  , ZC.defaultCompression-  , ZC.noCompression-  , ZC.bestSpeed-  , ZC.bestCompression-  , ZC.compressionLevel+  -- * Compression levels+  , CompressionLevel+  , defaultCompression+  , noCompression+  , bestSpeed+  , bestCompression+  , compressionLevel    -- * Window size   -- $ccz-re-export-  , ZC.defaultWindowBits-  , ZC.windowBits+  , Z.defaultWindowBits+  , windowBits   ) where -import qualified Codec.Compression.Zlib    as ZC-import qualified Codec.Zlib                as Z+import           Data.Streaming.Zlib       as Z+import           Control.Exception         (throwIO) import           Control.Monad             (unless) import qualified Data.ByteString           as B import           Pipes@@ -39,13 +41,13 @@ -- -- @ -- 'decompress' :: 'MonadIO' m---            => 'ZC.WindowBits'+--            => 'Z.WindowBits' --            => 'Producer' 'B.ByteString' m r --            -> 'Producer' 'B.ByteString' m r -- @ decompress   :: MonadIO m-  => ZC.WindowBits+  => Z.WindowBits   -> Proxy x' x () B.ByteString m r -- ^ Compressed stream   -> Proxy x' x () B.ByteString m r -- ^ Decompressed stream decompress wbits p0 = do@@ -58,27 +60,52 @@     return r {-# INLINABLE decompress #-} +-- | Decompress bytes flowing from a 'Producer', returning any leftover input+-- that follows the compressed stream.+decompress'+  :: MonadIO m+  => Z.WindowBits+  -> Producer B.ByteString m r -- ^ Compressed stream+  -> Producer B.ByteString m (Either (Producer B.ByteString m r) r)+     -- ^ Decompressed stream, ending with either leftovers or a result+decompress' wbits p0 = go p0 =<< liftIO (Z.initInflate wbits)+  where+    flush inf = do+      bs <- liftIO $ Z.flushInflate inf+      unless (B.null bs) (yield bs)+    go p inf = do+      res <- lift (next p)+      case res of+         Left r -> return $ Right r+         Right (bs, p') -> do+            fromPopper =<< liftIO (Z.feedInflate inf bs)+            flush inf+            leftover <- liftIO $ Z.getUnusedInflate inf+            if B.null leftover+               then go p' inf+               else return $ Left (yield leftover >> p')+{-# INLINABLE decompress' #-}  -- | Compress bytes flowing from a 'Producer'. -- -- See the "Codec.Compression.Zlib" module for details about--- 'ZC.CompressionLevel' and 'ZC.WindowBits'.+-- 'Z.CompressionLevel' and 'Z.WindowBits'. -- -- @ -- 'compress' :: 'MonadIO' m---          => 'ZC.CompressionLevel'---          -> 'ZC.WindowBits'+--          => 'Z.CompressionLevel'+--          -> 'Z.WindowBits' --          -> 'Producer' 'B.ByteString' m r --          -> 'Producer' 'B.ByteString' m r -- @ compress   :: MonadIO m-  => ZC.CompressionLevel-  -> ZC.WindowBits+  => CompressionLevel+  -> Z.WindowBits   -> Proxy x' x () B.ByteString m r -- ^ Decompressed stream   -> Proxy x' x () B.ByteString m r -- ^ Compressed stream-compress clevel wbits p0 = do-    def <- liftIO $ Z.initDeflate (fromCompressionLevel clevel) wbits+compress (CompressionLevel clevel) wbits p0 = do+    def <- liftIO $ Z.initDeflate clevel wbits     r <- for p0 $ \bs -> do        popper <- liftIO (Z.feedDeflate def bs)        fromPopper popper@@ -94,26 +121,40 @@ -- convenience.  --------------------------------------------------------------------------------+-- Compression Levels++-- | How hard should we try to compress?+newtype CompressionLevel = CompressionLevel Int+                         deriving (Show, Read, Eq, Ord)++defaultCompression, noCompression, bestSpeed, bestCompression :: CompressionLevel+defaultCompression = CompressionLevel (-1)+noCompression      = CompressionLevel 0+bestSpeed          = CompressionLevel 1+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"++windowBits :: Int -> WindowBits+windowBits = WindowBits++-------------------------------------------------------------------------------- -- Internal stuff  -- | Produce values from the given 'Z.Popper' until exhausted.-fromPopper :: MonadIO m => Z.Popper -> Producer' B.ByteString m ()-fromPopper pop = loop where+fromPopper :: MonadIO m+           => Z.Popper+           -> Producer' B.ByteString m ()+fromPopper pop = loop+  where     loop = do       mbs <- liftIO pop       case mbs of-         Nothing -> return ()-         Just bs -> yield bs >> loop+          PRDone     -> return ()+          PRError e  -> liftIO $ throwIO e+          PRNext bs  -> yield bs >> loop {-# INLINABLE fromPopper #-}---- We need this function until the @zlib@ library hides the--- 'ZC.CompressionLevel' constructors in future version 0.7.-fromCompressionLevel :: ZC.CompressionLevel -> Int-fromCompressionLevel level = case level of-    ZC.DefaultCompression   -> -1-    ZC.NoCompression        -> 0-    ZC.BestSpeed            -> 1-    ZC.BestCompression      -> 9-    ZC.CompressionLevel n-         | n >= 0 && n <= 9 -> fromIntegral n-    _  -> error "CompressLevel must be in the range 1..9"
+ test/Main.hs view
@@ -0,0 +1,73 @@+{-# LANGUAGE OverloadedStrings #-}+import           Control.Arrow+import qualified Data.ByteString.Char8 as B8+import           Data.List+import           Data.Ord+import           Test.Tasty+import           Test.Tasty.QuickCheck as QC+import           Test.QuickCheck.Instances ()+import           Test.Tasty.HUnit++import qualified Pipes as P+import qualified Pipes.Prelude as P+import qualified Pipes.Zlib as PZ+import qualified Pipes.GZip as PGZ++main = defaultMain tests++--------------------------------------------------------------------------------+tests :: TestTree+tests = testGroup "Tests" [properties, unitTests]++properties :: TestTree+properties = testGroup "Properties" [qcProps]++qcProps = testGroup "(checked by QuickCheck)"+ [ QC.testProperty "id == decompress . compress" $ \bs -> QC.ioProperty $ do+     let pc = PZ.compress PZ.defaultCompression PZ.defaultWindowBits (P.yield bs)+         pd = PZ.decompress PZ.defaultWindowBits pc+     bs' <- B8.concat <$> P.toListM pd+     return (bs QC.=== bs')++ , QC.testProperty "id == decompress' . compress" $ \bs bsl -> QC.ioProperty $ do+     let pc = PZ.compress PZ.defaultCompression PZ.defaultWindowBits (P.yield bs)+         pd = PZ.decompress' PZ.defaultWindowBits (pc >> P.yield bsl)+     (bs', elr) <- first B8.concat <$> P.toListM' pd+     case elr of+        Left pl -> do+           bsl' <- B8.concat <$> P.toListM pl+           return $ (bs QC.=== bs') QC..&&. (bsl QC.=== bsl')+        Right () -> do+           return $ (bs QC.=== bs') QC..&&. (bsl QC.=== B8.empty)+ ]++unitTests = testGroup "Unit tests"+  [ testCase "Zlib compression default" $ do+      let pc = PZ.compress PZ.defaultCompression PZ.defaultWindowBits+                   (P.yield bsUncompressed)+      bs <- B8.concat <$> P.toListM pc+      bs @?= bsCompressedZlibDefault+  , testCase "Zlib decompression default" $ do+      let pd = PZ.decompress PZ.defaultWindowBits+                   (P.yield bsCompressedZlibDefault)+      bs <- B8.concat <$> P.toListM pd+      bs @?= bsUncompressed+  , testCase "GZip compression default" $ do+      let pd = PGZ.compress PGZ.defaultCompression (P.yield bsUncompressed)+      bs <- B8.concat <$> P.toListM pd+      bs @?= bsCompressedGZipDefault+  , testCase "GZip decompression default" $ do+      let pd = PGZ.decompress (P.yield bsCompressedGZipDefault)+      bs <- B8.concat <$> P.toListM pd+      bs @?= bsUncompressed+  ]++bsUncompressed :: B8.ByteString+bsUncompressed = "foo"++bsCompressedZlibDefault :: B8.ByteString+bsCompressedZlibDefault = "x\156K\203\207\a\NUL\STX\130\SOHE"++bsCompressedGZipDefault :: B8.ByteString+bsCompressedGZipDefault =+  "\US\139\b\NUL\NUL\NUL\NUL\NUL\NUL\ETXK\203\207\a\NUL!es\140\ETX\NUL\NUL\NUL"