zlib-enum-0.2.3.1/0000755000000000000000000000000012336711661011751 5ustar0000000000000000zlib-enum-0.2.3.1/Setup.hs0000644000000000000000000000005612336711661013406 0ustar0000000000000000import Distribution.Simple main = defaultMain zlib-enum-0.2.3.1/LICENSE0000644000000000000000000000204212336711661012754 0ustar0000000000000000Copyright 2011 Malte Sommerkorn. Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. zlib-enum-0.2.3.1/zlib-enum.cabal0000644000000000000000000000407412336711661014644 0ustar0000000000000000name: zlib-enum version: 0.2.3.1 license: MIT license-file: LICENSE author: Paulo Tanimoto maintainer: Malte Sommerkorn synopsis: Enumerator interface for zlib compression description: zlib-enum is a stop-gap package to provide enumeratees for zlib compression/decompression. category: Codec, Compression, Enumerator stability: Experimental cabal-version: >= 1.6 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 >= 0.9 && < 0.11 , transformers >= 0.2 && < 0.5 , enumerator == 0.4.* , zlib-bindings == 0.1.* 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.11 , transformers >= 0.2 && < 0.5 , enumerator >= 0.4 && < 0.5 , zlib-bindings >= 0.1 , test-framework >= 0.6 && < 0.7 , test-framework-quickcheck2 >= 0.2.12.1 && < 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 zlib-enum-0.2.3.1/test.hs0000644000000000000000000002527612336711661013300 0ustar0000000000000000{-# 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 -- ------------------------------------------------------------------------------- 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' <- go gziped (finishDeflate def) return $ gziped' [] where go' def front bs = feedDeflate 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 = feedInflate 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 (EL.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 (EL.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 = EL.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-0.2.3.1/Codec/0000755000000000000000000000000012336711661012766 5ustar0000000000000000zlib-enum-0.2.3.1/Codec/Zlib/0000755000000000000000000000000012336711661013666 5ustar0000000000000000zlib-enum-0.2.3.1/Codec/Zlib/Enum.hs0000644000000000000000000000521712336711661015133 0ustar0000000000000000module Codec.Zlib.Enum ( -- * Enumeratees compress, decompress, gzip, ungzip, -- * Re-exported from zlib-bindings WindowBits (..), defaultWindowBits, ZlibException ) where import Codec.Zlib import Data.Enumerator as E import qualified Data.Enumerator.List as EL import Control.Monad.Trans.Class (lift) import Control.Monad.IO.Class (MonadIO, liftIO) import Data.ByteString (ByteString) -- | Gzip compression with default parameters. gzip :: MonadIO m => Enumeratee ByteString ByteString m a gzip = compress 1 (WindowBits 31) -- | Gzip decompression with default parameters. ungzip :: MonadIO m => Enumeratee ByteString ByteString m a ungzip = decompress (WindowBits 31) -- | -- Decompress (inflate) a stream of 'ByteString's. For example: -- -- > run $ enumFile "test.z" $$ decompress defaultWindowBits $$ printChunks True 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 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 $ (feedInflate 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) -> Enumeratee ByteString ByteString m a compress level config inner = do def <- liftIO $ initDeflate level config compress' def inner compress' :: MonadIO m => Deflate -> Enumeratee ByteString ByteString m b compress' def (Continue k) = do x <- EL.head case x of Nothing -> do chunks <- liftIO $ callback (finishDeflate def) lift $ runIteratee $ k $ Chunks chunks Just bs -> do chunks <- liftIO $ (feedDeflate def bs >>= callback) step <- lift $ runIteratee $ k $ Chunks chunks compress' def step compress' _ step = return step 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)