lz4-hs 0.1.4.1 → 0.1.5.0
raw patch · 5 files changed
+90/−19 lines, 5 filesdep ~basePVP ok
version bump matches the API change (PVP)
Dependency ranges changed: base
API changes (from Hackage documentation)
+ Codec.Lz4: compressBlockHCSingleThreaded :: Int -> ByteString -> ByteString
+ Codec.Lz4: compressBlockSingleThreaded :: ByteString -> ByteString
+ Codec.Lz4: decompressBlockSzSingleThreaded :: ByteString -> Int -> ByteString
Files
- CHANGELOG.md +5/−0
- bench/Bench.hs +11/−0
- lz4-hs.cabal +24/−12
- src/Codec/Lz4.chs +49/−6
- test/Spec.hs +1/−1
CHANGELOG.md view
@@ -1,5 +1,10 @@ # lz4 +## 0.1.5.0++ * Export single-threaded block compression functions+ * Avoid passing unitialized memory to the caller+ ## 0.1.4.1 * Use `lZ4DecompressionContextFree` correctly
bench/Bench.hs view
@@ -11,6 +11,12 @@ BSL.writeFile (dir </> "dump.tar") =<< (decompress <$> BSL.readFile fp') +decompressFile :: FilePath -> IO ()+decompressFile fp' = forceBSL =<<+ (decompress <$> BSL.readFile fp')++ where forceBSL = (`seq` mempty) . last . BSL.toChunks+ compressDump :: FilePath -> FilePath -> IO () compressDump dir fp' = BSL.writeFile (dir </> "dump.tar.lz4") =<<@@ -22,6 +28,11 @@ defaultMain [ bgroup "decompress/dump" [ bench "lz4" $ nfIO (decompressDump dir "valgrind-3.15.0.tar.lz4") , bench "lz4" $ nfIO (decompressDump dir "llvm-9.0.0.src.tar.lz4")+ ]+ -- file writes on linux are slow?+ , bgroup "decompress/file"+ [ bench "lz4" $ nfIO (decompressFile "valgrind-3.15.0.tar.lz4")+ , bench "lz4" $ nfIO (decompressFile "llvm-9.0.0.src.tar.lz4") ] , env lz4Files $ \ ~(v, l) -> bgroup "decompress"
lz4-hs.cabal view
@@ -1,7 +1,7 @@-cabal-version: 1.18+cabal-version: 2.2 name: lz4-hs-version: 0.1.4.1-license: BSD3+version: 0.1.5.0+license: BSD-3-Clause license-file: LICENSE copyright: Copyright: (c) 2020 Vanessa McHale maintainer: vamchale@gmail.com@@ -25,6 +25,7 @@ library exposed-modules: Codec.Lz4+ cc-options: -O3 c-sources: cbits/lz4.c cbits/lz4frame.c@@ -39,14 +40,11 @@ install-includes: cbits/lz4.h cbits/lz4frame.h cbits/lz4hc.h cbits/xxhash.h - ghc-options: -Wall+ ghc-options: -Wall -Wall build-depends:- base >=4.8 && <5,+ base >=4.7 && <5, bytestring -any - if !flag(cross)- build-tool-depends: c2hs:c2hs >=0.28.6- if impl(ghc >=8.0) ghc-options: -Wincomplete-uni-patterns -Wincomplete-record-updates@@ -55,15 +53,21 @@ if impl(ghc >=8.4) ghc-options: -Wmissing-export-lists + if impl(ghc >=8.2)+ ghc-options: -Wcpp-undef+ if impl(ghc >=8.10) ghc-options: -Wunused-packages + if !flag(cross)+ build-tool-depends: c2hs:c2hs >=0.28.6+ test-suite lz4-test type: exitcode-stdio-1.0 main-is: Spec.hs hs-source-dirs: test default-language: Haskell2010- ghc-options: -threaded -rtsopts -with-rtsopts=-N -Wall+ ghc-options: -Wall -threaded -rtsopts -with-rtsopts=-N build-depends: base -any, lz4-hs -any,@@ -74,7 +78,7 @@ if impl(ghc >=8.0) ghc-options: -Wincomplete-uni-patterns -Wincomplete-record-updates- -Wredundant-constraints -Widentities+ -Wredundant-constraints if impl(ghc >=8.4) ghc-options: -Wmissing-export-lists@@ -99,8 +103,16 @@ temporary -any if impl(ghc >=8.0)- ghc-options: -Wincomplete-uni-patterns -Wincomplete-record-updates+ ghc-options:+ -Wincomplete-uni-patterns -Wincomplete-record-updates+ -Wredundant-constraints + if impl(ghc >=8.4)+ ghc-options: -Wmissing-export-lists++ if impl(ghc >=8.2)+ ghc-options: -Wcpp-undef+ if impl(ghc >=8.10) ghc-options: -Wunused-packages @@ -121,7 +133,7 @@ if impl(ghc >=8.0) ghc-options: -Wincomplete-uni-patterns -Wincomplete-record-updates- -Wredundant-constraints -Widentities+ -Wredundant-constraints if impl(ghc >=8.4) ghc-options: -Wmissing-export-lists
src/Codec/Lz4.chs view
@@ -5,9 +5,12 @@ -- 'decompress' and 'compress' are fully lazy, i.e. memory efficient. module Codec.Lz4 ( -- * Functions for working with blocks compressBlock+ , compressBlockSingleThreaded , decompressBlockSz+ , decompressBlockSzSingleThreaded , lZ4MaxInputSize , compressBlockHC+ , compressBlockHCSingleThreaded , lZ4HCClevelMax -- * Functions for working with frames , compress@@ -20,6 +23,7 @@ ) where import Codec.Lz4.Foreign+import Control.Applicative import Control.Monad (when) import Control.Monad.ST.Lazy (runST) import qualified Control.Monad.ST.Lazy as LazyST@@ -36,7 +40,7 @@ newForeignPtr, withForeignPtr) import Foreign.Marshal.Alloc (alloca) import Foreign.Storable (peek, poke)-import System.IO.Unsafe (unsafePerformIO)+import System.IO.Unsafe (unsafePerformIO, unsafeDupablePerformIO) #include <lz4frame.h> @@ -146,15 +150,37 @@ {-# NOINLINE compressBlock #-} compressBlock :: BS.ByteString -> BS.ByteString-compressBlock = unsafePerformIO . compressBlockGeneric lZ4CompressDefault+compressBlock = unsafePerformIO . compressBlockIO +-- | Like 'compressBlock' but uses 'unsafeDupablePerformIO' under the hood.+--+-- @since 0.1.5.0+compressBlockSingleThreaded :: BS.ByteString -> BS.ByteString+compressBlockSingleThreaded = unsafeDupablePerformIO . compressBlockIO+{-# NOINLINE compressBlockSingleThreaded #-}++compressBlockIO :: BS.ByteString -> IO BS.ByteString+compressBlockIO = compressBlockGeneric lZ4CompressDefault+ {-# NOINLINE compressBlockHC #-} -- | @since 0.1.1.0 compressBlockHC :: Int -- ^ Compression level (must be less than 'lZ4HCClevelMax') -> BS.ByteString -> BS.ByteString-compressBlockHC lvl = unsafePerformIO . compressBlockGeneric (\src dst ssz dsz -> lZ4CompressHC src dst ssz dsz (fromIntegral lvl))+compressBlockHC lvl = unsafePerformIO . compressBlockHCIO lvl +-- | Like 'compressBlockHC' except uses 'unsafeDupablePerformIO' under the hood.+--+-- @since 0.1.5.0+compressBlockHCSingleThreaded :: Int -- ^ Compression level (must be less than 'lZ4HCClevelMax')+ -> BS.ByteString+ -> BS.ByteString+compressBlockHCSingleThreaded lvl = unsafeDupablePerformIO . compressBlockHCIO lvl+{-# NOINLINE compressBlockHCSingleThreaded #-}++compressBlockHCIO :: Int -> BS.ByteString -> IO BS.ByteString+compressBlockHCIO lvl = compressBlockGeneric (\src dst ssz dsz -> lZ4CompressHC src dst ssz dsz (fromIntegral lvl))+ compressBlockGeneric :: (CString -> CString -> CInt -> CInt -> IO CInt) -> BS.ByteString -> IO BS.ByteString compressBlockGeneric cfun bs = BS.unsafeUseAsCStringLen bs $ \(buf, sz) -> do@@ -170,13 +196,30 @@ decompressBlockSz :: BS.ByteString -> Int -- ^ Decompressed size -> BS.ByteString-decompressBlockSz bs expectedSz = unsafePerformIO $+decompressBlockSz bs sz = unsafePerformIO $ decompressBlockSzIO bs sz++-- | Decompress a block. Like 'decompressBlockSz' but uses 'unsafeDupablePerformIO' under the hood.+--+-- @since 0.1.5.0+decompressBlockSzSingleThreaded :: BS.ByteString+ -> Int -- ^ Decompressed size+ -> BS.ByteString+decompressBlockSzSingleThreaded bs sz = unsafeDupablePerformIO $ decompressBlockSzIO bs sz+{-# NOINLINE decompressBlockSzSingleThreaded #-}++-- | Decompress a block. The size of the uncompressed data must be known.+decompressBlockSzIO :: BS.ByteString+ -> Int -- ^ Decompressed size+ -> IO BS.ByteString+decompressBlockSzIO bs expectedSz = BS.unsafeUseAsCStringLen bs $ \(buf, sz) -> do dst <- mallocForeignPtrBytes expectedSz- withForeignPtr dst $ \d -> do+ bWritten <- withForeignPtr dst $ \d -> do bWritten <- lZ4DecompressSafe buf d (fromIntegral sz) (fromIntegral expectedSz) when (bWritten < 0) $ error "Decompression error"- pure $ BS.fromForeignPtr (castForeignPtr dst) 0 expectedSz+ pure bWritten+ let buffer = BS.fromForeignPtr (castForeignPtr dst) 0 expectedSz+ pure $ BS.take (fromIntegral bWritten) buffer cint :: Enum a => a -> CInt cint = fromIntegral . fromEnum
test/Spec.hs view
@@ -17,7 +17,7 @@ testRoundtripBlocks fp = testCase ("Roundtrip " ++ fp) $ do contents <- BS.readFile fp let sz = BS.length contents- actual = decompressBlockSz (compressBlock contents) sz+ actual = decompressBlockSzSingleThreaded (compressBlockSingleThreaded contents) sz actual @?= contents testRoundtripFrames :: FilePath -> TestTree