diff --git a/CHANGELOG.md b/CHANGELOG.md index e575d8c..f623d40 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,10 @@ +## Zip 1.6.0 + +* Added support for Zstandard (zstd) compression + +* Added a Cabal flag `-fdisable-zstd` to remove the zstd C library + dependency and hence support for Zstd entries. + ## Zip 1.5.0 * Added the `packDirRecur'` function. diff --git a/Codec/Archive/Zip/Internal.hs b/Codec/Archive/Zip/Internal.hs index 2ffa8b2..498499e 100644 --- a/Codec/Archive/Zip/Internal.hs +++ b/Codec/Archive/Zip/Internal.hs @@ -66,7 +66,11 @@ import System.IO import System.IO.Error (isDoesNotExistError) #ifdef ENABLE_BZIP2 -import qualified Data.Conduit.BZlib as BZ +import qualified Data.Conduit.BZlib as BZ +#endif + +#ifdef ENABLE_ZSTD +import qualified Data.Conduit.Zstd as Zstandard #endif ---------------------------------------------------------------------------- @@ -167,7 +171,7 @@ data MsDosTime = MsDosTime -- | “Version created by” to specify when writing archive data. zipVersion :: Version -zipVersion = Version [4, 6] [] +zipVersion = Version [6, 3] [] ---------------------------------------------------------------------------- -- Higher-level operations @@ -587,6 +591,13 @@ sinkData h compression = do BZ.bzip2 .| dataSink #else BZip2 -> throwM BZip2Unsupported +#endif +#ifdef ENABLE_ZSTD + Zstd -> + withCompression $ + Zstandard.compress 1 .| dataSink +#else + Zstd -> throwM ZstdUnsupported #endif return DataDescriptor @@ -1086,6 +1097,7 @@ toCompressionMethod :: Word16 -> Maybe CompressionMethod toCompressionMethod 0 = Just Store toCompressionMethod 8 = Just Deflate toCompressionMethod 12 = Just BZip2 +toCompressionMethod 93 = Just Zstd toCompressionMethod _ = Nothing -- | Convert 'CompressionMethod' to its numeric representation as per .ZIP @@ -1094,6 +1106,7 @@ fromCompressionMethod :: CompressionMethod -> Word16 fromCompressionMethod Store = 0 fromCompressionMethod Deflate = 8 fromCompressionMethod BZip2 = 12 +fromCompressionMethod Zstd = 93 -- | Check if an entry with these parameters needs Zip64 extension. needsZip64 :: EntryDescription -> Bool @@ -1113,6 +1126,7 @@ getZipVersion zip64 m = max zip64ver mver Just Store -> [2, 0] Just Deflate -> [2, 0] Just BZip2 -> [4, 6] + Just Zstd -> [6, 3] -- | Return decompressing 'Conduit' corresponding to the given compression -- method. @@ -1124,9 +1138,15 @@ decompressingPipe Store = C.awaitForever C.yield decompressingPipe Deflate = Z.decompress $ Z.WindowBits (-15) #ifdef ENABLE_BZIP2 -decompressingPipe BZip2 = BZ.bunzip2 +decompressingPipe BZip2 = BZ.bunzip2 #else -decompressingPipe BZip2 = throwM BZip2Unsupported +decompressingPipe BZip2 = throwM BZip2Unsupported +#endif + +#ifdef ENABLE_ZSTD +decompressingPipe Zstd = Zstandard.decompress +#else +decompressingPipe Zstd = throwM ZstdUnsupported #endif -- | Sink that calculates CRC32 check sum for incoming stream. diff --git a/Codec/Archive/Zip/Type.hs b/Codec/Archive/Zip/Type.hs index 6860727..5608faf 100644 --- a/Codec/Archive/Zip/Type.hs +++ b/Codec/Archive/Zip/Type.hs @@ -182,6 +182,10 @@ data CompressionMethod Deflate | -- | Compressed using BZip2 algorithm BZip2 + | -- | Compressed using Zstandard algorithm + -- + -- @since 1.6.0 + Zstd deriving (Show, Read, Eq, Ord, Enum, Bounded, Data, Typeable) ---------------------------------------------------------------------------- @@ -212,6 +216,13 @@ data ZipException -- -- @since 1.3.0 | BZip2Unsupported +#endif +#ifndef ENABLE_ZSTD + -- | Thrown when attempting to decompress a 'Zstd' entry and the + -- library is compiled without support for it. + -- + -- @since 1.6.0 + | ZstdUnsupported #endif ParsingFailed FilePath String deriving (Eq, Ord, Typeable) @@ -228,4 +239,10 @@ instance Show ZipException where "the zip library has been built with bzip2 disabled." #endif +#ifndef ENABLE_ZSTD + show ZstdUnsupported = + "Encountered a zipfile entry with Zstd compression, but " ++ + "the zip library has been built with zstd disabled." +#endif + instance Exception ZipException diff --git a/README.md b/README.md index 6b60deb..1172274 100644 --- a/README.md +++ b/README.md @@ -98,6 +98,7 @@ For reference, here is a [copy of the specification][specification]. * Store (no compression, just store files “as is”) * [DEFLATE](deflate) * [Bzip2](bzip2) +* [Zstandard](zstd) The best way to add a new compression method to the library is to write a conduit that will do the compression and publish it as a library. `zip` can @@ -284,3 +285,4 @@ Distributed under BSD 3 clause license. [specification]: https://pkware.cachefly.net/webdocs/APPNOTE/APPNOTE-6.3.3.TXT [deflate]: https://en.wikipedia.org/wiki/DEFLATE [bzip2]: https://en.wikipedia.org/wiki/Bzip2 +[zstd]: https://en.wikipedia.org/wiki/Zstandard diff --git a/tests/Main.hs b/tests/Main.hs index bfe2918..0d9387f 100644 --- a/tests/Main.hs +++ b/tests/Main.hs @@ -95,6 +95,9 @@ instance Arbitrary CompressionMethod where [ Store, #ifdef ENABLE_BZIP2 BZip2, +#endif +#ifdef ENABLE_ZSTD + Zstd, #endif Deflate ] @@ -386,6 +389,7 @@ versionNeededSpec = Store -> [2, 0] Deflate -> [2, 0] BZip2 -> [4, 6] + Zstd -> [6, 3] ) addEntrySpec :: SpecWith FilePath diff --git a/zip.cabal b/zip.cabal index 07fcffe..0a61c09 100644 --- a/zip.cabal +++ b/zip.cabal @@ -32,6 +32,13 @@ flag disable-bzip2 default: False manual: True +flag disable-zstd + description: + Removes dependency on zstd C library and hence support for Zstandard entries. + + default: False + manual: True + library exposed-modules: Codec.Archive.Zip @@ -67,6 +74,9 @@ library if !flag(disable-bzip2) build-depends: bzlib-conduit >=0.3 && <0.4 + if !flag(disable-zstd) + build-depends: conduit-zstd >=0.0.2 && <0.1 + if flag(dev) cpp-options: -DHASKELL_ZIP_DEV_MODE ghc-options: @@ -79,6 +89,9 @@ library if !flag(disable-bzip2) cpp-options: -DENABLE_BZIP2 + if !flag(disable-zstd) + cpp-options: -DENABLE_ZSTD + if os(windows) cpp-options: -DZIP_OS=0 @@ -132,3 +145,6 @@ test-suite tests if !flag(disable-bzip2) cpp-options: -DENABLE_BZIP2 + + if !flag(disable-zstd) + cpp-options: -DENABLE_ZSTD