packages feed

lzlib 0.1.1.1 → 0.2.0.0

raw patch · 7 files changed

+126/−57 lines, 7 filesdep +criteriondep +temporarydep −filepatterndep ~basePVP ok

version bump matches the API change (PVP)

Dependencies added: criterion, temporary

Dependencies removed: filepattern

Dependency ranges changed: base

API changes (from Hackage documentation)

- Codec.Lzip: compressStrict :: ByteString -> ByteString
- Codec.Lzip: compressWithStrict :: CompressionLevel -> ByteString -> ByteString
- Codec.Lzip: decompressStrict :: ByteString -> ByteString
+ Codec.Lzip: compress :: ByteString -> ByteString
+ Codec.Lzip: compressWith :: CompressionLevel -> ByteString -> ByteString
+ Codec.Lzip: decompress :: ByteString -> ByteString
- Codec.Lzip.Raw: lZMaxDictionaryBits :: IO CInt
+ Codec.Lzip.Raw: lZMaxDictionaryBits :: CInt
- Codec.Lzip.Raw: lZMaxDictionarySize :: IO CInt
+ Codec.Lzip.Raw: lZMaxDictionarySize :: CInt
- Codec.Lzip.Raw: lZMaxMatchLenLimit :: IO CInt
+ Codec.Lzip.Raw: lZMaxMatchLenLimit :: CInt
- Codec.Lzip.Raw: lZMinDictionaryBits :: IO CInt
+ Codec.Lzip.Raw: lZMinDictionaryBits :: CInt
- Codec.Lzip.Raw: lZMinDictionarySize :: IO CInt
+ Codec.Lzip.Raw: lZMinDictionarySize :: CInt
- Codec.Lzip.Raw: lZMinMatchLenLimit :: IO CInt
+ Codec.Lzip.Raw: lZMinMatchLenLimit :: CInt

Files

CHANGELOG.md view
@@ -1,5 +1,11 @@ # lzlib +## 0.2.0.0++  * Performance improvements+  * Pure functions no longer occur in IO+  * Change type signatures of compression/decompression functions+ ## 0.1.1.1      * Fix distribution
+ bench/Bench.hs view
@@ -0,0 +1,40 @@+module Main (main) where++import           Codec.Lzip+import           Criterion.Main+import qualified Data.ByteString      as BS+import qualified Data.ByteString.Lazy as BSL+import           System.FilePath      ((</>))+import           System.IO.Temp       (withSystemTempDirectory)++roundtrip :: BS.ByteString -> BSL.ByteString+roundtrip = compress . BSL.toStrict . decompress++roundtrip' :: BS.ByteString -> BSL.ByteString+roundtrip' = decompress . BSL.toStrict . compress++unpack :: IO ()+unpack = withSystemTempDirectory "lzlib" $+    \fp -> BSL.writeFile (fp </> "lzlib.tar.lz") =<<+        (roundtrip <$> BS.readFile "lzlib-1.10.tar.lz")++unpack' :: IO ()+unpack' = withSystemTempDirectory "lzlib" $+    \fp -> BSL.writeFile (fp </> "lzlib.tar") =<<+        (roundtrip' <$> BS.readFile "lzlib-1.10.tar")++main :: IO ()+main =+    defaultMain [ env file $ \ f ->+                  bgroup "roundtrip"+                      [ bench "lzlib" $ nf roundtrip f ]+                , bgroup "unpack"+                      [ bench "lzlib" $ nfIO unpack ]+                , env decompressed $ \f ->+                  bgroup "roundtrip'"+                      [ bench "lzlib" $ nf roundtrip' f ]+                , bgroup "unpack"+                      [ bench "lzlib" $ nfIO unpack' ]+                ]+    where file = BS.readFile "lzlib-1.10.tar.lz"+          decompressed = BS.readFile "lzlib-1.10.tar"
lzlib.cabal view
@@ -1,6 +1,6 @@ cabal-version:      1.18 name:               lzlib-version:            0.1.1.1+version:            0.2.0.0 license:            BSD3 license-file:       LICENSE copyright:          Copyright: (c) 2019 Vanessa McHale@@ -51,7 +51,10 @@     other-extensions: BangPatterns     include-dirs:     cbits     install-includes: cbits/lzlib.h-    ghc-options:      -Wall+    ghc-options:+        -Wall -Wincomplete-uni-patterns -Wincomplete-record-updates+        -Wredundant-constraints+     build-depends:         base >=4.9 && <5,         bytestring -any@@ -59,11 +62,6 @@     if !flag(cross)         build-tool-depends: c2hs:c2hs -any -    if impl(ghc >=8.0)-        ghc-options:-            -Wincomplete-uni-patterns -Wincomplete-record-updates-            -Wredundant-constraints-     if impl(ghc >=8.4)         ghc-options: -Wmissing-export-lists @@ -72,20 +70,36 @@     main-is:          Spec.hs     hs-source-dirs:   test     default-language: Haskell2010-    ghc-options:      -threaded -rtsopts -with-rtsopts=-N -Wall+    ghc-options:+        -threaded -rtsopts -with-rtsopts=-N -Wall -Wincomplete-uni-patterns+        -Wincomplete-record-updates -Wredundant-constraints+     build-depends:         base -any,         lzlib -any,         hspec -any,         bytestring -any,-        filepattern -any,-        filepath -any,         directory -any -    if impl(ghc >=8.0)-        ghc-options:-            -Wincomplete-uni-patterns -Wincomplete-record-updates-            -Wredundant-constraints+    if impl(ghc >=8.4)+        ghc-options: -Wmissing-export-lists++benchmark lzlib-bench+    type:             exitcode-stdio-1.0+    main-is:          Bench.hs+    hs-source-dirs:   bench+    default-language: Haskell2010+    ghc-options:+        -Wall -Wincomplete-uni-patterns -Wincomplete-record-updates+        -Wredundant-constraints++    build-depends:+        base -any,+        lzlib -any,+        criterion -any,+        bytestring -any,+        temporary -any,+        filepath -any      if impl(ghc >=8.4)         ghc-options: -Wmissing-export-lists
src/Codec/Lzip.hs view
@@ -1,8 +1,8 @@ {-# LANGUAGE BangPatterns #-} -module Codec.Lzip ( compressStrict-                  , compressWithStrict-                  , decompressStrict+module Codec.Lzip ( compress+                  , compressWith+                  , decompress                   , CompressionLevel (..)                   -- * Low-level bindings                   , module Codec.Lzip.Raw@@ -12,6 +12,7 @@ import           Control.Monad         (void) import           Data.Bits             (shiftL) import qualified Data.ByteString       as BS+import qualified Data.ByteString.Lazy  as BSL import           Data.Int              (Int64) import           Data.Maybe            (fromMaybe) import           Data.Semigroup@@ -48,9 +49,9 @@  -- | This does not do any error recovery; for that you should use -- [lziprecover](https://www.nongnu.org/lzip/lziprecover.html).-{-# NOINLINE decompressStrict #-}-decompressStrict :: BS.ByteString -> BS.ByteString-decompressStrict bs = unsafePerformIO $ BS.useAsCStringLen bs $ \(bytes, sz) -> do+{-# NOINLINE decompress #-}+decompress :: BS.ByteString -> BSL.ByteString+decompress bs = unsafePerformIO $ BS.useAsCStringLen bs $ \(bytes, sz) -> do      decoder <- lZDecompressOpen     maxSz <- lZDecompressWriteSize decoder@@ -62,10 +63,10 @@     void $ lZDecompressClose decoder     free buf -    pure res+    pure (BSL.fromChunks res)      where-        loop :: LZDecoderPtr -> (Ptr UInt8, Int) -> (Ptr UInt8, Int) -> Int -> Int -> BS.ByteString -> IO BS.ByteString+        loop :: LZDecoderPtr -> (Ptr UInt8, Int) -> (Ptr UInt8, Int) -> Int -> Int -> [BS.ByteString] -> IO [BS.ByteString]         loop decoder (buf, bufSz) (bytes, sz) offset total !acc = do             let toWrite = min bufSz (total - offset)             bytesWritten <- if offset < total@@ -78,42 +79,43 @@                     let newOffset = offset + fromIntegral (fromMaybe 0 bytesWritten)                     bytesRead <- lZDecompressRead decoder buf (fromIntegral bufSz)                     bsActual <- BS.packCStringLen (castPtr buf, fromIntegral bytesRead)-                    loop decoder (buf, bufSz) (bytes, sz) newOffset total (acc <> bsActual)+                    loop decoder (buf, bufSz) (bytes, sz) newOffset total (acc ++ [bsActual]) -{-# NOINLINE compressStrict #-}-compressStrict :: BS.ByteString -> BS.ByteString-compressStrict = compressWithStrict Nine+{-# NOINLINE compress #-}+compress :: BS.ByteString -> BSL.ByteString+compress = compressWith Nine --- TODO: memory use-{-# NOINLINE compressWithStrict #-}-compressWithStrict :: CompressionLevel -> BS.ByteString -> BS.ByteString-compressWithStrict level bs = unsafePerformIO $ BS.useAsCStringLen bs $ \(bytes, sz) -> do+{-# NOINLINE compressWith #-}+compressWith :: CompressionLevel -> BS.ByteString -> BSL.ByteString+compressWith level bs = unsafePerformIO $ BS.useAsCStringLen bs $ \(bytes, sz) -> do -    encoder <- lZCompressOpen (fromIntegral dictionarySize) (fromIntegral matchLenLimit) (fromIntegral memberSize)+    encoder <- lZCompressOpen (fromIntegral $ dictionarySize sz) (fromIntegral matchLenLimit) (fromIntegral memberSize)      void $ lZCompressWrite encoder (castPtr bytes) (fromIntegral sz)     void $ lZCompressFinish encoder -    newBytes <- mallocBytes sz-    res <- readLoop encoder (newBytes, sz) 0 mempty+    let delta = sz `div` 4 + 64+    newBytes <- mallocBytes delta+    res <- readLoop encoder (newBytes, delta) 0 mempty      void $ lZCompressClose encoder -    pure res+    pure (BSL.fromChunks res)      where--        readLoop :: LZEncoderPtr -> (Ptr UInt8, Int) -> Int -> BS.ByteString -> IO BS.ByteString+        readLoop :: LZEncoderPtr -> (Ptr UInt8, Int) -> Int -> [BS.ByteString] -> IO [BS.ByteString]         readLoop encoder (buf, sz) bytesRead acc = do             bytesActual <- lZCompressRead encoder buf (fromIntegral sz)             res <- lZCompressFinished encoder             bsActual <- BS.packCStringLen (castPtr buf, fromIntegral bytesActual)             if res == 1-                then pure (acc <> bsActual)-                else readLoop encoder (buf, sz) (bytesRead + fromIntegral bytesActual) (acc <> bsActual)+                then pure (acc ++ [bsActual])+                else readLoop encoder (buf, sz) (bytesRead + fromIntegral bytesActual) (acc ++ [bsActual])          memberSize :: Int64         memberSize = maxBound -        dictionarySize = _dictionarySize $ encoderOptions level-        matchLenLimit = _matchLenLimit $ encoderOptions level+        -- saves memory+        dictionarySize = max (fromIntegral lZMinDictionarySize) . min (_dictionarySize $ encoderOptions level)+        matchLenLimit = _matchLenLimit opts+        opts = encoderOptions level
src/Codec/Lzip/Raw.chs view
@@ -65,12 +65,12 @@  {# fun pure LZ_version as ^ {} -> `String' #} {# fun pure LZ_strerror as ^ { `LZErrno' } -> `String' #}-{# fun LZ_min_dictionary_bits as ^ {} -> `CInt' #}-{# fun LZ_min_dictionary_size as ^ {} -> `CInt' #}-{# fun LZ_max_dictionary_bits as ^ {} -> `CInt' #}-{# fun LZ_max_dictionary_size as ^ {} -> `CInt' #}-{# fun LZ_min_match_len_limit as ^ {} -> `CInt' #}-{# fun LZ_max_match_len_limit as ^ {} -> `CInt' #}+{# fun pure LZ_min_dictionary_bits as ^ {} -> `CInt' #}+{# fun pure LZ_min_dictionary_size as ^ {} -> `CInt' #}+{# fun pure LZ_max_dictionary_bits as ^ {} -> `CInt' #}+{# fun pure LZ_max_dictionary_size as ^ {} -> `CInt' #}+{# fun pure LZ_min_match_len_limit as ^ {} -> `CInt' #}+{# fun pure LZ_max_match_len_limit as ^ {} -> `CInt' #}  instance Show LZErrno where     show = lZStrerror
stack.yaml view
@@ -1,4 +1,5 @@ --- resolver: lts-14.5+ghc-options: {"$locals": -ddump-to-file -ddump-hi} packages:   - '.'
test/Spec.hs view
@@ -1,25 +1,31 @@ module Main ( main ) where  import           Codec.Lzip-import qualified Data.ByteString              as BS-import           Data.Foldable                (traverse_)-import           System.Directory             (doesDirectoryExist)-import           System.FilePath              ((</>))-import           System.FilePattern.Directory (getDirectoryFiles)+import           Control.Monad        (filterM)+import qualified Data.ByteString      as BS+import qualified Data.ByteString.Lazy as BSL+import           Data.Foldable        (traverse_)+import           System.Directory     (doesFileExist) import           Test.Hspec  compressFile :: FilePath -> Spec compressFile fp = parallel $     it "roundtrip should be identity" $ do         str <- BS.readFile fp-        decompressStrict (compressStrict str) `shouldBe` str+        decompress (BSL.toStrict (compress str)) `shouldBe` (BSL.fromStrict str) +roundtripFile :: FilePath -> Spec+roundtripFile fp = parallel $+    it "roundtrip should be identity" $ do+        str <- BS.readFile fp+        compress (BSL.toStrict (decompress str)) `shouldBe` (BSL.fromStrict str)+ main :: IO () main = do-    b <- doesDirectoryExist "dist-newstyle"-    libs <- if b-        then getDirectoryFiles "dist-newstyle" ["**/*.so", "**/*.dll"]-        else pure []+    ex' <- filterM doesFileExist ["gmp-6.1.2.tar.lz", "lzlib-1.10.tar.lz"]+    ex <- filterM doesFileExist ["gmp-6.1.2.tar", "lzlib-1.10.tar"]     hspec $ do         describe "decompress/compress" $-            traverse_ compressFile (("dist-newstyle" </>) <$> libs)+            traverse_ compressFile ex+        describe "compress/decompress" $+            traverse_ roundtripFile ex'