module Data.Conduit.Lzma (compress, decompress) where
import qualified Codec.Compression.Lzma as Lzma
import Control.Applicative as App
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
:: (MonadThrow m, MonadIO m)
=> Maybe Word64
-> Conduit ByteString m ByteString
decompress memlimit =
decompressWith Lzma.defaultDecompressParams
{ Lzma.decompressMemLimit = fromMaybe maxBound memlimit
, Lzma.decompressAutoDecoder = True
, Lzma.decompressConcatenated = True
}
decompressWith
:: (MonadThrow m, MonadIO m)
=> Lzma.DecompressParams
-> Conduit ByteString m ByteString
decompressWith parms = do
c <- peek
case c of
Nothing -> monadThrow $ 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
| 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) = do
if B.null rest
then App.pure ()
else leftover rest
go (Lzma.DecompressStreamError err) =
monadThrow $ userError $ "Data.Conduit.Lzma.decompress: error: "++prettyRet err
compress
:: (MonadIO m)
=> Maybe Int
-> Conduit ByteString m ByteString
compress level =
compressWith Lzma.defaultCompressParams { Lzma.compressLevel = level' }
where
level' = case level of
Nothing -> Lzma.CompressionLevel6
Just n -> toEnum (max 0 (min 9 n))
compressWith
:: MonadIO m
=> Lzma.CompressParams
-> Conduit ByteString m ByteString
compressWith parms = do
s <- liftIO (Lzma.compressIO parms)
go s
where
go s@(Lzma.CompressInputRequired _flush more) = do
mx <- await
case mx of
Just x
| B.null x -> go s
| 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 = pure ()