conduit-algorithms 0.0.13.0 → 0.0.14.0
raw patch · 7 files changed
+239/−14 lines, 7 filesdep +lzmadep −lzma-conduitPVP ok
version bump matches the API change (PVP)
Dependencies added: lzma
Dependencies removed: lzma-conduit
API changes (from Hackage documentation)
+ Data.Conduit.Lzma2: compress :: MonadIO m => Maybe Int -> ConduitM ByteString ByteString m ()
+ Data.Conduit.Lzma2: compressWith :: MonadIO m => CompressParams -> ConduitM ByteString ByteString m ()
+ Data.Conduit.Lzma2: decompress :: (MonadThrow m, MonadIO m) => Maybe Word64 -> ConduitM ByteString ByteString m ()
+ Data.Conduit.Lzma2: decompressWith :: (MonadThrow m, MonadIO m) => DecompressParams -> ConduitM ByteString ByteString m ()
Files
- ChangeLog +5/−0
- Data/Conduit/Algorithms/Async.hs +1/−1
- Data/Conduit/Lzma2.hs +105/−0
- README.md +2/−0
- conduit-algorithms.cabal +11/−11
- tests/TestLZMA.hs +108/−0
- tests/Tests.hs +7/−2
ChangeLog view
@@ -1,3 +1,8 @@+Version 0.0.14.0 2024-02-16 by luispedro+ * Data.Conduit.Lzma2: Incorporate Data.Conduit.Lzma2 (adapted from+ lzma-conduit) instead of using the external lzma-conduit package as that+ is no longer maintained.+ Version 0.0.13.0 2022-08-01 by luispedro * Support non-default values for compression level in more compression formats
Data/Conduit/Algorithms/Async.hs view
@@ -47,7 +47,7 @@ import qualified Data.Conduit.TQueue as CA import qualified Data.Conduit.List as CL import qualified Data.Conduit.Zlib as CZ-import qualified Data.Conduit.Lzma as CX+import qualified Data.Conduit.Lzma2 as CX import qualified Data.Streaming.Zlib as SZ import qualified Data.Conduit.BZlib as CBZ import qualified Data.Conduit.Zstd as CZstd
+ Data/Conduit/Lzma2.hs view
@@ -0,0 +1,105 @@+{-- Originally from the lzma-conduit package+ -- https://hackage.haskell.org/package/lzma-conduit+ --+ -- Adapted & updated+ -}+module Data.Conduit.Lzma2(compress, compressWith, decompress, decompressWith) where++import qualified Codec.Compression.Lzma as Lzma+import Control.Monad.IO.Class (MonadIO (liftIO))+import Control.Monad.Trans.Resource+import Data.ByteString (ByteString)+import qualified Data.ByteString as B+import Data.Conduit+import Data.Conduit.List (peek)+import Data.Maybe (fromMaybe)+import Data.Word++prettyRet+ :: Lzma.LzmaRet+ -> String+prettyRet r = case r of+ Lzma.LzmaRetOK -> "Operation completed successfully"+ Lzma.LzmaRetStreamEnd -> "End of stream was reached"+ Lzma.LzmaRetUnsupportedCheck -> "Cannot calculate the integrity check"+ Lzma.LzmaRetGetCheck -> "Integrity check type is now available"+ Lzma.LzmaRetMemError -> "Cannot allocate memory"+ Lzma.LzmaRetMemlimitError -> "Memory usage limit was reached"+ Lzma.LzmaRetFormatError -> "File format not recognized"+ Lzma.LzmaRetOptionsError -> "Invalid or unsupported options"+ Lzma.LzmaRetDataError -> "Data is corrupt"+ Lzma.LzmaRetBufError -> "No progress is possible"+ Lzma.LzmaRetProgError -> "Programming error"+++-- | Decompress a 'ByteString' from a lzma or xz container stream.+decompress+ :: (MonadThrow m, MonadIO m)+ => Maybe Word64 -- ^ Memory limit, in bytes.+ -> ConduitM ByteString ByteString m ()+decompress memlimit =+ decompressWith Lzma.defaultDecompressParams+ { Lzma.decompressMemLimit = fromMaybe maxBound memlimit+ , Lzma.decompressAutoDecoder = True+ , Lzma.decompressConcatenated = True+ }++decompressWith+ :: (MonadThrow m, MonadIO m)+ => Lzma.DecompressParams+ -> ConduitM ByteString ByteString m ()+decompressWith parms = do+ c <- peek+ case c of+ Nothing -> throwM $ userError "Data.Conduit.Lzma.decompress: invalid empty input"+ Just _ -> liftIO (Lzma.decompressIO parms) >>= go+ where+ go s@(Lzma.DecompressInputRequired more) = do+ mx <- await+ case mx of+ Just x+ | B.null x -> go s -- ignore/skip empty bytestring chunks+ | otherwise -> liftIO (more x) >>= go+ Nothing -> liftIO (more B.empty) >>= go+ go (Lzma.DecompressOutputAvailable output cont) = do+ yield output+ liftIO cont >>= go+ go (Lzma.DecompressStreamEnd rest)+ | B.null rest = return ()+ | otherwise = leftover rest+ go (Lzma.DecompressStreamError err) =+ throwM $ userError $ "Data.Conduit.Lzma.decompress: error: "++prettyRet err+++-- | Compress a 'ByteString' into a xz container stream.+compress+ :: (MonadIO m)+ => Maybe Int -- ^ Compression level from [0..9], defaults to 6.+ -> ConduitM ByteString ByteString m ()+compress level =+ compressWith Lzma.defaultCompressParams { Lzma.compressLevel = level' }+ where+ level' = case level of+ Nothing -> Lzma.CompressionLevel6+ Just n -> let+ n' = max minBound (min maxBound n) -- clamp to [minBound..maxBound] range+ in toEnum n'++compressWith+ :: MonadIO m+ => Lzma.CompressParams+ -> ConduitM ByteString ByteString m ()+compressWith parms =+ liftIO (Lzma.compressIO parms) >>= go+ where+ go s@(Lzma.CompressInputRequired _flush more) = do+ mx <- await+ case mx of+ Just x+ | B.null x -> go s -- ignore/skip empty bytestring chunks+ | otherwise -> liftIO (more x) >>= go+ Nothing -> liftIO (more B.empty) >>= go+ go (Lzma.CompressOutputAvailable output cont) = do+ yield output+ liftIO cont >>= go+ go Lzma.CompressStreamEnd = return ()
README.md view
@@ -14,3 +14,5 @@ ## Author Luis Pedro Coelho | [Email](mailto:luis@luispedro.org) | [Twitter](https://twitter.com/luispedrocoelho)++Since version `0.0.14.0`, this includes code originally in [lzma-conduit](https://github.com/alphaHeavy/lzma-conduit) from Alpha Heavy Industries
conduit-algorithms.cabal view
@@ -1,13 +1,11 @@ cabal-version: 1.12 --- This file has been generated from package.yaml by hpack version 0.34.4.+-- This file has been generated from package.yaml by hpack version 0.36.0. -- -- see: https://github.com/sol/hpack------ hash: 92a9214b73c5e10d0b9b949f0ee1ff3be9366c137750238aa91f8b3ce28ba130 name: conduit-algorithms-version: 0.0.13.0+version: 0.0.14.0 synopsis: Conduit-based algorithms description: Algorithms on Conduits, including higher level asynchronous processing and some other utilities. category: Conduit@@ -28,6 +26,7 @@ library exposed-modules:+ Data.Conduit.Lzma2 Data.Conduit.Algorithms Data.Conduit.Algorithms.Utils Data.Conduit.Algorithms.Async@@ -52,7 +51,7 @@ , deepseq , exceptions , fingertree- , lzma-conduit+ , lzma , monad-control , mtl , resourcet@@ -62,17 +61,18 @@ , transformers , unliftio-core , vector+ default-language: Haskell2010 if os(windows) cpp-options: -DWINDOWS- default-language: Haskell2010 test-suite conduit-algorithms-test type: exitcode-stdio-1.0 main-is: Tests.hs other-modules:+ TestLZMA Paths_conduit_algorithms hs-source-dirs:- ./tests+ tests default-extensions: BangPatterns OverloadedStrings@@ -96,7 +96,7 @@ , directory , exceptions , fingertree- , lzma-conduit+ , lzma , monad-control , mtl , resourcet@@ -110,9 +110,9 @@ , transformers , unliftio-core , vector+ default-language: Haskell2010 if os(windows) cpp-options: -DWINDOWS- default-language: Haskell2010 benchmark conduit-algorithms-bench type: exitcode-stdio-1.0@@ -140,7 +140,7 @@ , deepseq , exceptions , fingertree- , lzma-conduit+ , lzma , monad-control , mtl , resourcet@@ -150,6 +150,6 @@ , transformers , unliftio-core , vector+ default-language: Haskell2010 if os(windows) cpp-options: -DWINDOWS- default-language: Haskell2010
+ tests/TestLZMA.hs view
@@ -0,0 +1,108 @@+{-# LANGUAGE TemplateHaskell, QuasiQuotes #-}+module TestLZMA+ ( tgroup_lzma+ ) where+import Test.Tasty.TH+import Test.Tasty.HUnit (assertFailure, (@?=))+import Test.Tasty.QuickCheck+import Test.QuickCheck.Monadic (monadicIO, run, pick, pre, assert)+import Control.Monad.Trans.Resource++import qualified Data.ByteString as B+import qualified Data.Conduit as C+import qualified Data.Conduit.List as CL+import Conduit ((.|))+import Data.Word+import Data.Conduit.Lzma2+import System.IO.Error (tryIOError)++tgroup_lzma = $(testGroupGenerator)++assertLeft (Left _) = return ()+assertLeft _ = run $ assertFailure "Expected Left, got Right"++someString :: Gen B.ByteString+someString = do+ val <- listOf $ elements [0..255::Word8]+ return $ B.pack val++runC :: MonadUnliftIO m => C.ConduitT () C.Void (ResourceT m) a -> m a+runC = runResourceT . C.runConduit++newtype BigString = BigString B.ByteString+ deriving (Eq, Show)+++instance Arbitrary BigString where+ arbitrary = BigString <$> resize (1024*16) someString++prop_compressAndDiscard :: BigString -> Property+prop_compressAndDiscard (BigString str) = monadicIO $ do+ run . runC $+ CL.sourceList [str]+ .| compress Nothing+ .| CL.sinkNull++prop_compressAndCheckLength :: BigString -> Property+prop_compressAndCheckLength (BigString str) = monadicIO $ do+ len <- run . runC $+ CL.sourceList [str]+ .| compress Nothing+ .| CL.fold (\ acc el -> acc + B.length el) 0+ -- random strings don't compress very well+ assert $ len > B.length str `div` 2+ assert $ len - 64 < B.length str * 2++prop_chain :: BigString -> Property+prop_chain (BigString str) = monadicIO . run $ do+ str' <- runC $+ CL.sourceList [str]+ .| compress Nothing+ .| decompress Nothing+ .| CL.consume+ str @?= B.concat str'++prop_compressThenDecompress :: BigString -> Property+prop_compressThenDecompress (BigString str) = monadicIO $ do+ blob <- run . runC $ CL.sourceList [str] .| compress Nothing .| CL.consume+ let blob' = B.concat blob+ randIdx <- pick $ elements [0..B.length blob'-1]+ let resplit = let (x,y) = B.splitAt randIdx blob' in [x,y]+ str' <- run . runC $ CL.sourceList resplit .| decompress Nothing .| CL.consume+ run $ str @?= B.concat str'+++prop_decompressRandom :: BigString -> Property+prop_decompressRandom (BigString str) = monadicIO $ do+ -- The BigString is not necessarily big. It can even be the empty string+ -- https://github.com/alphaHeavy/lzma-conduit/issues/19+ pre $ B.length str > 64+ header <- run . runC $+ CL.sourceList []+ .| compress Nothing+ .| CL.consume+ let blob = header ++ [str]+ ioErrorE <- run $+ tryIOError (runC $ CL.sourceList blob .| decompress Nothing .| CL.sinkNull)+ assertLeft ioErrorE++prop_decompressCorrupt :: BigString -> Property+prop_decompressCorrupt (BigString str) = monadicIO $ do+ header <- run . runC $ (CL.sourceList [] .| compress Nothing .| CL.consume)+ let header' = B.concat header+ randVal <- pick $ elements [0..255::Word8]+ randIdx <- pick $ elements [0..B.length header'-1]+ let (left, right) = B.splitAt randIdx header'+ updated = left `B.append` (randVal `B.cons` B.tail right)+ blob = [updated, str]+ ioErrorE <- run $+ tryIOError (runC $ CL.sourceList blob .| decompress Nothing .| CL.sinkNull)+ assertLeft ioErrorE+++prop_decompressEmpty :: Property+prop_decompressEmpty = monadicIO $ do+ count <- pick $ elements [0..10]+ let blob = replicate count B.empty+ ioErrorE <- run . tryIOError . runC $ CL.sourceList blob .| decompress Nothing .| CL.sinkNull+ assertLeft ioErrorE
tests/Tests.hs view
@@ -1,9 +1,10 @@-{- Copyright 2017-2021 Luis Pedro Coelho+{- Copyright 2017-2024 Luis Pedro Coelho - License: MIT -} {-# LANGUAGE TemplateHaskell, CPP, QuasiQuotes, FlexibleContexts, OverloadedStrings #-} module Main where +import Test.Tasty import Test.Tasty.TH import Test.Tasty.HUnit @@ -31,8 +32,12 @@ import qualified Data.Conduit.Algorithms.Async as CAlg import qualified Data.Conduit.Algorithms.Async.ByteString as CAlg +import TestLZMA (tgroup_lzma)+ main :: IO ()-main = $(defaultMainGenerator)+main = defaultMain $ testGroup "All tests" [tgroup_lzma, maintests]++maintests = $(testGroupGenerator) testingFileNameGZ :: FilePath testingFileNameGZ = "file_just_for_testing_delete_me_please.gz"