packages feed

zlib-enum 0.1 → 0.2

raw patch · 3 files changed

+419/−59 lines, 3 filesdep +QuickCheckdep +processdep +test-frameworkdep −mtldep ~basedep ~bytestringdep ~enumeratornew-component:exe:zlib-enum-test

Dependencies added: QuickCheck, process, test-framework, test-framework-quickcheck2, transformers

Dependencies removed: mtl

Dependency ranges changed: base, bytestring, enumerator, zlib-bindings

Files

Codec/Zlib/Enum.hs view
@@ -1,80 +1,81 @@ module Codec.Zlib.Enum (     -- * Enumeratees-    compress, decompress,-    -- * Re-exported from zlib-bindings -    WindowBits, defaultWindowBits+    compress, decompress, gzip, ungzip,+    -- * Re-exported from zlib-bindings+    WindowBits (..), defaultWindowBits ) where  import Codec.Zlib import Data.Enumerator as E-import Control.Monad.Trans (MonadIO, liftIO)+import qualified Data.Enumerator.List as EL+import Control.Monad.Trans.Class (lift)+import Control.Monad.IO.Class (MonadIO, liftIO) import Data.ByteString (ByteString)-import Control.Monad (join) -joinIO :: MonadIO m => IO (m (Step a m b)) -> Iteratee a m b-joinIO = Iteratee . join . liftIO+-- | Gzip compression with default parameters.+gzip :: MonadIO m => Enumeratee ByteString ByteString m a+gzip = compress 1 defaultWindowBits -enumLoop :: Monad m =>-     ((Stream b -> Iteratee b m c) -> Iteratee b m c)-     -> (a -> (Stream b -> Iteratee b m c) -> Iteratee b m c)-     -> Enumeratee a b m c-enumLoop done more = checkDone loop where-    loop k = do maybe_x <- E.head-                case maybe_x of-                     Nothing -> return $$ done k-                     Just x  -> checkDone loop $$ more x k+-- | Gzip decompression with default parameters.+ungzip :: MonadIO m => Enumeratee ByteString ByteString m a+ungzip = decompress defaultWindowBits  -- | -- Decompress (inflate) a stream of 'ByteString's. For example:---      +-- -- >    run $ enumFile "test.z" $$ decompress defaultWindowBits $$ printChunks True -decompress :: MonadIO m+decompress+    :: MonadIO m     => WindowBits -- ^ Zlib parameter (see the zlib-bindings package as well as the zlib C library)     -> Enumeratee ByteString ByteString m a-decompress config step0 = do-    inflate <- liftIO $ initInflate config-    let done k      = do lastChunk <- liftIO $ finishInflate inflate-                         k (Chunks [lastChunk])-        more x k    = joinIO $ withInflateInput inflate x (return . callback k)-    enumLoop done more step0+decompress config inner = do+    inf <- liftIO $ initInflate config+    decompress' inf inner +decompress' :: MonadIO m => Inflate -> Enumeratee ByteString ByteString m b+decompress' inf (Continue k) = do+    x <- EL.head+    case x of+        Nothing -> do+            chunk <- liftIO $ finishInflate inf+            lift $ runIteratee $ k $ Chunks [chunk]+        Just bs -> do+            chunks <- liftIO $ withInflateInput inf bs $ callback+            step <- lift $ runIteratee $ k $ Chunks chunks+            decompress' inf step+decompress' _ step = return step+ -- | -- Compress (deflate) a stream of 'ByteString's. The 'WindowBits' also control -- the format (zlib vs. gzip). -compress :: MonadIO m-    => Int        -- ^ Compression level-    -> WindowBits -- ^ Zlib parameter (see the zlib-bindings package as well as the zlib C library)+compress+    :: MonadIO m+    => Int         -- ^ Compression level+    -> WindowBits  -- ^ Zlib parameter (see the zlib-bindings package as well as the zlib C library)     -> Enumeratee ByteString ByteString m a-compress level config step0 = do-    deflate <- liftIO $ initDeflate level config-    let done k    = joinIO $ finishDeflate deflate (return . callback k)-        more x k  = joinIO $ withDeflateInput deflate x (return . callback k)-    enumLoop done more step0---- A callback function for withInflateInput / withDeflateInput-callback :: MonadIO m =>-    (Stream a -> Iteratee a m b) -> IO (Maybe a) -> m (Step a m b)+compress level config inner = do+    def <- liftIO $ initDeflate level config+    compress' def inner -callback k pop = maybe done more =<< liftIO pop-  where-    done   = return (Continue k)-    more y = do step <- runIteratee (k (Chunks [y]))-                case step of-                     Continue k'    -> callback k' pop-                     other          -> return other+compress' :: MonadIO m => Deflate -> Enumeratee ByteString ByteString m b+compress' def (Continue k) = do+    x <- EL.head+    case x of+        Nothing -> do+            chunks <- liftIO $ finishDeflate def $ callback+            lift $ runIteratee $ k $ Chunks chunks+        Just bs -> do+            chunks <- liftIO $ withDeflateInput def bs $ callback+            step <- lift $ runIteratee $ k $ Chunks chunks+            compress' def step+compress' _ step = return step --- testInflate = do---     h <- openBinaryFile "test-out" WriteMode---     run $ enumFile "test.z"---            $$ decompress defaultWindowBits---            $$ iterHandle h---     hClose h--- --- testDeflate = do---     h <- openBinaryFile "test.z" WriteMode---     run $ enumFile "test"---            $$ compress 7 defaultWindowBits---            $$ iterHandle h---     hClose h+callback :: Monad m => m (Maybe a) -> m [a]+callback pop = go id where+    go front = do+       x <- pop+       case x of+           Nothing -> return $ front []+           Just y -> go (front . (:) y)
+ test.hs view
@@ -0,0 +1,331 @@+{-# LANGUAGE NoImplicitPrelude #-}+{-# LANGUAGE OverloadedStrings #-}+{-# OPTIONS_GHC -fno-warn-orphans #-}++-------------------------------------------------------------------------------+-- |+-- Module     : zlib-enum-test+-- Copyright  : (c) 2011 Malte Sommerkorn, Michael Snoyman, Paulo Tanimoto+-- License    : BSD3+--+-- Maintainer : Malte Sommerkorn <malte.sommerkorn@googlemail.com>+--+-------------------------------------------------------------------------------++module Main where++-------------------------------------------------------------------------------+-- Imports+-------------------------------------------------------------------------------++import Prelude++import Data.String ()++import Data.ByteString (ByteString)+import qualified Data.ByteString as B+import qualified Data.ByteString.Lazy as L++import Data.Enumerator+  ( Iteratee (..), Enumeratee+  , ($$), joinI, run_ )+import qualified Data.Enumerator as E+import qualified Data.Enumerator.List as EL+import qualified Data.Enumerator.Binary as EB++import Control.Monad (foldM)+import Control.Monad.IO.Class (MonadIO (..))+import Control.Exception (bracket)++import Codec.Zlib+import qualified Codec.Zlib.Enum as Z++import Test.QuickCheck+import Test.QuickCheck.Monadic+import qualified Test.QuickCheck.Monadic as Q++import System.IO (IOMode (..), openBinaryFile, hClose)++import Test.Framework (Test, defaultMain, testGroup)+import Test.Framework.Providers.QuickCheck2 (testProperty)++-------------------------------------------------------------------------------+-- Instances+-------------------------------------------------------------------------------++-- | QuickCheck wants a Show instance for WindowBits+instance Show WindowBits where+  show (WindowBits n) = "WindowBits " ++ show n+  show _              = "DefaultWindowBits" -- not exported from zlib++-- | Random values for WindowBits: 15 or 31+instance Arbitrary WindowBits where+  arbitrary = elements [WindowBits 15, WindowBits 31]++-- | Generate relatively large ByteStrings+instance Arbitrary B.ByteString where+    arbitrary = (B.pack . concat . replicate 1000) `fmap` arbitrary++-- | Generate relatively large Lazy ByteStrings+instance Arbitrary L.ByteString where+    arbitrary = (L.fromChunks . concat . replicate 100) `fmap` arbitrary++-------------------------------------------------------------------------------+-- Utility Functions+-------------------------------------------------------------------------------++-- | Iteratee that consumes the entire Stream as a strict ByteString.+consume :: Monad m => Iteratee ByteString m ByteString+consume = do+  xs <- EL.consume+  return $ B.concat xs++-- | Enumeratee that consumes only a few bytes.+unconsumed :: Monad m => Enumeratee ByteString ByteString m a+unconsumed = E.sequence $ do+  xs <- EB.take 100+  return $ B.concat $ L.toChunks xs++-- | Concatenate an Iteratee and a list of Enumeratees into an Iteratee.+concatWith :: Monad m => Iteratee a m b -> [Enumeratee a a m b] -> Iteratee a m b+concatWith = foldr f+  where+  f iter enums = joinI $ iter $$ enums++-- | Compress a list of ByteStrings+compress+  :: MonadIO m+  => WindowBits+  -> [ByteString]+  -> m ByteString+compress win xs =+  E.run_ $  E.enumList 1 xs+         $$ joinI $ Z.compress 7 win+         $$ consume++-- | Decompress a list of ByteStrings+decompress+  :: MonadIO m+  => WindowBits+  -> [ByteString]+  -> m ByteString+decompress win xs =+  E.run_ $  E.enumList 1 xs+         $$ joinI $ Z.decompress win+         $$ consume++-- | Compress and decompress without doing anything else.+compressDecompress+  :: MonadIO m+  => WindowBits+  -> [ByteString]+  -> m ByteString+compressDecompress win xs =+  E.run_ $  E.enumList 1 xs+         $$ joinI $ Z.compress 7 win+         $$ joinI $ Z.decompress win+         $$ consume++-- | The same, but for 'gzip', 'ungzip'+gzipUngzip :: MonadIO m => [ByteString] -> m ByteString+gzipUngzip xs =+  E.run_ $  E.enumList 1 xs+         $$ joinI $ Z.gzip+         $$ joinI $ Z.ungzip+         $$ consume++-- | Compress and decompress a ByteString with given WindowBits,+-- piping the stream with an Enumeratee.+compressDecompressWith+  :: MonadIO m+  => Enumeratee ByteString ByteString m ByteString+  -> WindowBits+  -> [ByteString]+  -> m ByteString+compressDecompressWith enum win xs =+  E.run_ $  E.enumList 1 xs+         $$ joinI $ Z.compress 7 win+         $$ joinI $ enum+         $$ joinI $ Z.decompress win+         $$ consume++-- | Compress a ByteString 'n' times and then decompress it 'n' times+compressDecompressMany+  :: MonadIO m+  => WindowBits+  -> Int+  -> [ByteString]+  -> m ByteString+compressDecompressMany win n xs =+  E.run_ $  E.enumList 1 xs+         $$ concatWith consume es+  where+  es = replicate m (Z.compress 7 win) ++ replicate m (Z.decompress win)+  m = 1 + (abs n `rem` 10) -- restrict n to [1, 10]++-- | Compress a [ByteString] to a file with an Enumeratee+compressFileWith+  :: Enumeratee ByteString ByteString IO ()+  -> WindowBits -> FilePath -> [ByteString] -> IO ()+compressFileWith enum win file xs = bracket+  (openBinaryFile file WriteMode)+  (hClose)+  $ \ h -> do+    run_ $  E.enumList 1 xs+         $$ joinI $ Z.compress 7 win+         $$ joinI $ enum+         $$ EB.iterHandle h++-- | Decompress from a file with an Enumeratee+decompressFileWith+  :: Enumeratee ByteString ByteString IO ByteString+  -> WindowBits -> FilePath -> IO ByteString+decompressFileWith enum win file =+  run_ $  EB.enumFile file+       $$ joinI $ Z.decompress win+       $$ joinI $ enum+       $$ consume++-- | Alternative implementation of compress for comparison+compressChunks :: WindowBits -> [ByteString] -> IO [ByteString]+compressChunks win xs = do+    def <- initDeflate 7 win+    gziped <- foldM (go' def) id xs+    gziped' <- finishDeflate def $ go gziped+    return $ gziped' []+    where+    go' def front bs = withDeflateInput def bs $ go front+    go front x = do+        y <- x+        case y of+            Nothing -> return front+            Just z -> go (front . (:) z) x++-- | Alternative implementation of decompress for comparison+decompressChunks :: WindowBits -> [ByteString] -> IO [ByteString]+decompressChunks win xs = do+    inf <- initInflate win+    ungziped <- foldM (go' inf) id xs+    final <- finishInflate inf+    return $ ungziped [final]+    where+    go' inf front bs = withInflateInput inf bs $ go front+    go front x = do+        y <- x+        case y of+            Nothing -> return front+            Just z -> go (front . (:) z) x++-------------------------------------------------------------------------------+-- Properties+-------------------------------------------------------------------------------++-- | Compare compresssion via lists and enumerator+prop_compress_compare :: WindowBits -> [ByteString] -> Property+prop_compress_compare win xs = monadicIO $ do+  chks <- Q.run $ B.concat `fmap` compressChunks win xs+  enum <- Q.run $ compress win xs+  assert $ enum == chks++-- | Compare decompression via lists and enumerator+prop_decompress_compare :: WindowBits -> [ByteString] -> Property+prop_decompress_compare win xs = monadicIO $ do+  comp <- Q.run $ compressChunks win xs+  chks <- Q.run $ B.concat `fmap` decompressChunks win comp+  enum <- Q.run $ decompress win comp+  assert $ enum == chks++-- | Check: bs == decompress (compress bs)+-- (That is, with separate Enumeratees)+prop_compress_decompress :: WindowBits -> [ByteString] -> Property+prop_compress_decompress win xs = monadicIO $ do+  cs <- Q.run $ compress win xs+  ys <- Q.run $ decompress win [cs]+  assert (B.concat xs == ys)++-- | Check: bs == compressDecompress bs+-- (That is, with a single Enumeratee)+prop_compress_decompress' :: WindowBits -> [ByteString] -> Property+prop_compress_decompress' win xs = monadicIO $ do+  ys <- Q.run $ compressDecompress win xs+  assert (B.concat xs == ys)++-- | Check: bs == gzipUngzip bs+-- (just to see if the default parameters aren't broken)+prop_gzip_ungzip :: [ByteString] -> Property+prop_gzip_ungzip xs = monadicIO $ do+  ys <- Q.run $ gzipUngzip xs+  assert (B.concat xs == ys)++-- | Check if using an Iteratee that consumes only a few bytes works+prop_unconsumed :: WindowBits -> [ByteString] -> Property+prop_unconsumed win xs = monadicIO $ do+  ys <- Q.run $ compressDecompressWith unconsumed win xs+  assert (B.concat xs == ys)++-- | Check if mapping the identity function doesn't affect anything+prop_map_id :: WindowBits -> [ByteString] -> Property+prop_map_id win xs = monadicIO $ do+  ys <- Q.run $ compressDecompressWith (E.map id) win xs+  assert (B.concat xs == ys)++-- | Check if mapping 'reverse . reverse' doesn't affect anything+prop_map_revrev :: WindowBits -> [ByteString] -> Property+prop_map_revrev win xs = monadicIO $ do+  ys <- Q.run $ compressDecompressWith (E.map $ B.reverse . B.reverse) win xs+  assert (B.concat xs == ys)++-- | Check if compressing and decompressing multiple times works+prop_many :: WindowBits -> Int -> [ByteString] -> Property+prop_many win n xs = monadicIO $ do+  ys <- Q.run $ compressDecompressMany win n xs+  assert (B.concat xs == ys)++-- | Check compressing and decompressing a file+prop_files_map_id :: FilePath -> WindowBits -> [ByteString] -> Property+prop_files_map_id file win xs = monadicIO $ do+  Q.run $ compressFileWith enum win file xs+  ys <- Q.run $ decompressFileWith enum win file+  assert (B.concat xs == ys)+  where+  enum = E.map id++-- | Check compressing and decompressing a file with an Iteratee that+-- consumes only a few bytes+prop_files_unconsumed :: FilePath -> WindowBits -> [ByteString] -> Property+prop_files_unconsumed file win xs = monadicIO $ do+  Q.run $ compressFileWith unconsumed win file xs+  ys <- Q.run $ decompressFileWith unconsumed win file+  assert (B.concat xs == ys)++-------------------------------------------------------------------------------+-- Tests+-------------------------------------------------------------------------------++tests :: [Test]+tests = let testFile = "zlib-enum-test-file" in+  [ testGroup "compare"+    [ testProperty "compress_compressChunks" prop_compress_compare+    , testProperty "decompress_decompressChunks" prop_decompress_compare+    ]+  , testGroup "enumList"+    [ testProperty "compress_decompress" prop_compress_decompress+    , testProperty "compress_decompress'" prop_compress_decompress'+    , testProperty "gzip_ungzip" prop_gzip_ungzip+    , testProperty "unconsumed" prop_unconsumed+    , testProperty "map_id" prop_map_id+    , testProperty "map_revrev" prop_map_revrev+    , testProperty "many" prop_many+    ]+  , testGroup "enumFile"+    [ testProperty "files_map_id" (prop_files_map_id testFile)+    , testProperty "files_unconsumed" (prop_files_unconsumed testFile)+    ]+  ]++-------------------------------------------------------------------------------+-- IO+-------------------------------------------------------------------------------++main :: IO ()+main = defaultMain tests
zlib-enum.cabal view
@@ -1,8 +1,8 @@ name:           zlib-enum-version:        0.1+version:        0.2 license:        MIT license-file:   LICENSE-author:         Malte Sommerkorn <malte.sommerkorn@googlemail.com>+author:         Paulo Tanimoto <ptanimoto@gmail.com> maintainer:     Malte Sommerkorn <malte.sommerkorn@googlemail.com> synopsis:       Enumerator interface for zlib compression description:    zlib-enum is a stop-gap package to provide enumeratees for@@ -13,15 +13,43 @@ build-type:     Simple homepage:       http://github.com/maltem/zlib-enum +-------------------------------------------------------------------------------+flag test+  description: Build the test executable.+  default: False++------------------------------------------------------------------------------- library+    if flag(test)+         Buildable: False     build-depends: base                  >= 4 && < 5                  , bytestring-                 , mtl                   >= 2+                 , transformers          == 0.2.*                  , enumerator            >= 0.4                  , zlib-bindings     exposed-modules: Codec.Zlib.Enum     ghc-options:     -Wall +-------------------------------------------------------------------------------+executable zlib-enum-test+    main-is: test.hs+    if flag(test)+        Buildable: True+        build-depends:+              base                       >= 4.0   &&   < 5.0+            , bytestring                 >= 0.9   &&   < 0.10+            , transformers               == 0.2.*+            , enumerator                 >= 0.4   &&   < 0.5+            , zlib-bindings              >= 0.0   &&   < 0.1+            , process                    >= 1.0   &&   < 1.1+            , test-framework             >= 0.3   &&   < 0.4+            , test-framework-quickcheck2 >= 0.2.9 &&   < 0.3.0+            , QuickCheck                 >= 2.4   &&   < 2.5+    else+        Buildable: False+    ghc-options:     -Wall++------------------------------------------------------------------------------- source-repository head   type:     git   location: git://github.com/maltem/zlib-enum.git