module LibLzma
( LzmaStream
, LzmaRet(..)
, LzmaCheck(..)
, LzmaCompLevel(..)
, newDecodeLzmaStream
, DecodeLzmaFlags(..)
, defaultDecodeLzmaFlags
, newEncodeLzmaStream
, EncodeLzmaFlags(..)
, defaultEncodeLzmaFlags
, runLzmaStream
) where
import Control.Applicative
import Control.Exception
import Control.Monad
import Data.ByteString (ByteString)
import qualified Data.ByteString.Internal as BS
import qualified Data.ByteString.Unsafe as BS
import Data.Typeable
import Foreign
import Prelude
newtype LzmaStream = LS (ForeignPtr LzmaStream)
data LzmaRet = LZMA_OK
| LZMA_STREAM_END
| LZMA_UNSUPPORTED_CHECK
| LZMA_GET_CHECK
| LZMA_MEM_ERROR
| LZMA_MEMLIMIT_ERROR
| LZMA_FORMAT_ERROR
| LZMA_OPTIONS_ERROR
| LZMA_DATA_ERROR
| LZMA_BUF_ERROR
| LZMA_PROG_ERROR
deriving (Eq,Ord,Show,Typeable)
instance Exception LzmaRet
toLzmaRet :: Int -> Maybe LzmaRet
toLzmaRet i = case i of
(0) -> Just LZMA_OK
(1) -> Just LZMA_STREAM_END
(3) -> Just LZMA_UNSUPPORTED_CHECK
(4) -> Just LZMA_GET_CHECK
(5) -> Just LZMA_MEM_ERROR
(6) -> Just LZMA_MEMLIMIT_ERROR
(7) -> Just LZMA_FORMAT_ERROR
(8) -> Just LZMA_OPTIONS_ERROR
(9) -> Just LZMA_DATA_ERROR
(10) -> Just LZMA_BUF_ERROR
(11) -> Just LZMA_PROG_ERROR
_ -> Nothing
data LzmaCheck = LzmaCheckNone
| LzmaCheckCrc32
| LzmaCheckCrc64
| LzmaCheckSha256
deriving (Eq,Ord,Show)
data LzmaCompLevel = LzmaCompLevel0
| LzmaCompLevel1
| LzmaCompLevel2
| LzmaCompLevel3
| LzmaCompLevel4
| LzmaCompLevel5
| LzmaCompLevel6
| LzmaCompLevel7
| LzmaCompLevel8
| LzmaCompLevel9
deriving (Eq,Ord,Show,Enum)
fromLzmaCheck :: LzmaCheck -> Int
fromLzmaCheck lc = case lc of
LzmaCheckNone -> 0
LzmaCheckCrc32 -> 1
LzmaCheckCrc64 -> 4
LzmaCheckSha256 -> 10
data DecodeLzmaFlags = DecodeLzmaFlags
{ lzmaTellNoCheck :: !Bool
, lzmaTellUnsupportedCheck :: !Bool
, lzmaTellAnyCheck :: !Bool
, lzmaConcatenated :: !Bool
, lzmaAutoDecoder :: !Bool
, lzmaMemLimit :: !Word64
} deriving (Eq,Show)
defaultDecodeLzmaFlags :: DecodeLzmaFlags
defaultDecodeLzmaFlags = DecodeLzmaFlags {..}
where
lzmaTellNoCheck = False
lzmaTellUnsupportedCheck = False
lzmaTellAnyCheck = False
lzmaConcatenated = True
lzmaAutoDecoder = False
lzmaMemLimit = maxBound
data EncodeLzmaFlags = EncodeLzmaFlags
{ lzmaCheck :: !LzmaCheck
, lzmaCompLevel :: !LzmaCompLevel
, lzmaExtreme :: !Bool
} deriving (Eq,Show)
defaultEncodeLzmaFlags :: EncodeLzmaFlags
defaultEncodeLzmaFlags = EncodeLzmaFlags LzmaCheckCrc32 LzmaCompLevel6 False
newDecodeLzmaStream :: DecodeLzmaFlags -> IO (Either LzmaRet LzmaStream)
newDecodeLzmaStream (DecodeLzmaFlags {..}) = do
fp <- mallocForeignPtrBytes ((136))
addForeignPtrFinalizer c_hs_lzma_done_funptr fp
rc <- withForeignPtr fp (\ptr -> c_hs_lzma_init_decoder ptr lzmaAutoDecoder lzmaMemLimit flags')
rc' <- maybe (fail "newDecodeLzmaStream: invalid return code") pure $ toLzmaRet rc
return $ case rc' of
LZMA_OK -> Right (LS fp)
_ -> Left rc'
where
flags' =
(if lzmaTellNoCheck then (1) else 0) .|.
(if lzmaTellUnsupportedCheck then (2) else 0) .|.
(if lzmaTellAnyCheck then (4) else 0) .|.
(if lzmaConcatenated then (8) else 0)
newEncodeLzmaStream :: EncodeLzmaFlags -> IO (Either LzmaRet LzmaStream)
newEncodeLzmaStream (EncodeLzmaFlags {..}) = do
fp <- mallocForeignPtrBytes ((136))
addForeignPtrFinalizer c_hs_lzma_done_funptr fp
rc <- withForeignPtr fp (\ptr -> c_hs_lzma_init_encoder ptr preset check)
rc' <- maybe (fail "newDecodeLzmaStream: invalid return code") pure $ toLzmaRet rc
return $ case rc' of
LZMA_OK -> Right (LS fp)
_ -> Left rc'
where
preset = fromIntegral (fromEnum lzmaCompLevel) .|.
(if lzmaExtreme then (2147483648) else 0)
check = fromLzmaCheck lzmaCheck
runLzmaStream :: LzmaStream -> ByteString -> Bool -> Int -> IO (LzmaRet,Int,ByteString)
runLzmaStream (LS ls) ibs finish buflen
| buflen <= 0 = fail "runLzmaStream: invalid buflen argument"
| otherwise = withForeignPtr ls $ \lsptr -> do
BS.unsafeUseAsCStringLen ibs $ \(ibsptr, ibslen) -> do
(obuf,rc) <- BS.createAndTrim' buflen $ \bufptr -> do
rc' <- c_hs_lzma_run lsptr action (castPtr ibsptr) ibslen bufptr buflen
rc'' <- maybe (fail "runLzmaStream: invalid return code") pure $ toLzmaRet rc'
availOut <- ((\hsc_ptr -> peekByteOff hsc_ptr 32)) lsptr
unless (buflen >= availOut && availOut >= 0) (fail "runLzmaStream: invalid avail_out")
let produced = buflen availOut
return (0, produced, rc'')
availIn <- ((\hsc_ptr -> peekByteOff hsc_ptr 8)) lsptr
unless (ibslen >= availIn && availIn >= 0) (fail "runLzmaStream: invalid avail_in")
let consumed = ibslen availIn
return (rc, fromIntegral consumed, obuf)
where
action = if finish then (3) else (0)
foreign import ccall "hs_lzma_init_decoder"
c_hs_lzma_init_decoder :: Ptr LzmaStream -> Bool -> Word64 -> Word32 -> IO Int
foreign import ccall "hs_lzma_init_encoder"
c_hs_lzma_init_encoder :: Ptr LzmaStream -> Word32 -> Int -> IO Int
foreign import ccall "hs_lzma_run"
c_hs_lzma_run :: Ptr LzmaStream -> Int -> Ptr Word8 -> Int -> Ptr Word8 -> Int -> IO Int
foreign import ccall "&hs_lzma_done"
c_hs_lzma_done_funptr :: FunPtr (Ptr LzmaStream -> IO ())