{-# LANGUAGE BangPatterns #-}
{-# LANGUAGE DeriveDataTypeable #-}
{-# LANGUAGE RecordWildCards #-}
module Codec.Compression.Lzlib
(
LzEncoder
, CompressParams(..)
, compressParamPreset
, lzCompressOpen
, lzCompressClose
, lzCompressRead
, lzCompressWrite
, lzCompressSyncFlush
, lzCompressFinish
, lzCompressFinished
, lzCompressMemberFinished
, lzCompressRestartMember
, LzDecoder
, lzDecompressOpen
, lzDecompressClose
, lzDecompressRead
, lzDecompressWrite
, lzDecompressSyncToMember
, lzDecompressFinish
, lzDecompressFinished
, lzDecompressMemberFinished
, lzDecompressReset
, LzErrno(..)
) where
import Data.Bits
import qualified Data.ByteString.Internal as BS (createAndTrim)
import qualified Data.ByteString.Unsafe as BS
import Foreign
import Internal
import Prelude hiding (fromIntegral)
import Codec.Compression.Lzlib.FFI
data CompressParams = CompressParams
{ compressDictionarySize :: !Int
, compressMatchLenLimit :: !Int
, compressMemberSize :: !Word64
}
compressParamPreset :: Int -> CompressParams
compressParamPreset lvl = case (max 0 lvl) of
0 -> CompressParams 0xffff 16 msz
1 -> CompressParams (1 `shiftL` 20) 5 msz
2 -> CompressParams (3 `shiftL` 19) 6 msz
3 -> CompressParams (1 `shiftL` 21) 8 msz
4 -> CompressParams (3 `shiftL` 20) 12 msz
5 -> CompressParams (1 `shiftL` 22) 20 msz
6 -> CompressParams (1 `shiftL` 23) 36 msz
7 -> CompressParams (1 `shiftL` 24) 68 msz
8 -> CompressParams (3 `shiftL` 23) 132 msz
_ -> CompressParams (1 `shiftL` 25) 273 msz
where
msz = 0x0008000000000000
lzCompressErrno :: LzEncoder -> IO LzErrno
lzCompressErrno (LzEncoder fp) = withForeignPtr fp $ \encPtr -> toLzErrno <$> c'LZ_compress_errno encPtr
lzCompressOpen :: CompressParams -> IO (Either LzErrno LzEncoder)
lzCompressOpen CompressParams{..} = runExceptT $ do
unless (c'lzlib_version_check == 0) $
throwE LzUnknown
p <- maybe (throwE LzMemError) pure =<< liftE allocEncoder
eno <- liftE (lzCompressErrno p)
unless (eno == LzOk) $ do
let (LzEncoder fp) = p
liftE (finalizeForeignPtr fp)
throwE eno
pure p
where
allocEncoder = mask_ $ do
p <- c'LZ_compress_open (int2cint compressDictionarySize)
(int2cint compressMatchLenLimit)
(intCast compressMemberSize)
case () of
_ | p == nullPtr -> pure Nothing
| otherwise -> do
!fp <- newForeignPtr cp'LZ_compress_close p
pure (Just (LzEncoder fp))
lzCompressClose :: LzEncoder -> IO ()
lzCompressClose (LzEncoder fp) = finalizeForeignPtr fp
lzCompressWrite :: LzEncoder -> ByteString -> IO Int
lzCompressWrite lze@(LzEncoder fp) ibs = do
written <- withForeignPtr fp $ \encPtr -> do
BS.unsafeUseAsCStringLen ibs $ \(ibsptr, ibslen) -> do
c'LZ_compress_write encPtr (castPtr ibsptr) (int2cint ibslen)
when (written < 0) $ throwIO =<< lzCompressErrno lze
pure (intCast written)
lzCompressRead :: LzEncoder -> Int -> IO ByteString
lzCompressRead lze@(LzEncoder fp) bufsize0
= BS.createAndTrim (intCast bufsize) $ \bufptr -> do
used <- withForeignPtr fp $ \encPtr -> c'LZ_compress_read encPtr bufptr bufsize
when (used < 0) $ throwIO =<< lzCompressErrno lze
pure (intCast used)
where
bufsize = int2cint bufsize0
lzCompressFinish :: LzEncoder -> IO LzErrno
lzCompressFinish lze@(LzEncoder fp) = do
rc <- withForeignPtr fp c'LZ_compress_finish
if rc == 0
then pure LzOk
else do
eno <- lzCompressErrno lze
pure $! if eno /= LzOk then eno else LzUnknown
lzCompressRestartMember :: LzEncoder -> Word64 -> IO LzErrno
lzCompressRestartMember lze@(LzEncoder fp) memberSize = do
rc <- withForeignPtr fp $ \encPtr -> c'LZ_compress_restart_member encPtr (intCast memberSize)
if rc == 0
then pure LzOk
else do
eno <- lzCompressErrno lze
pure $! if eno /= LzOk then eno else LzUnknown
lzCompressSyncFlush :: LzEncoder -> IO LzErrno
lzCompressSyncFlush lze@(LzEncoder fp) = do
rc <- withForeignPtr fp c'LZ_compress_sync_flush
if rc == 0
then pure LzOk
else do
eno <- lzCompressErrno lze
pure $! if eno /= LzOk then eno else LzUnknown
lzCompressFinished :: LzEncoder -> IO Bool
lzCompressFinished lze@(LzEncoder fp) = do
rc <- withForeignPtr fp c'LZ_compress_finished
case rc of
0 -> pure False
1 -> pure True
_ -> throwIO =<< lzCompressErrno lze
lzCompressMemberFinished :: LzEncoder -> IO Bool
lzCompressMemberFinished lze@(LzEncoder fp) = do
rc <- withForeignPtr fp c'LZ_compress_member_finished
case rc of
0 -> pure False
1 -> pure True
_ -> throwIO =<< lzCompressErrno lze
lzDecompressErrno :: LzDecoder -> IO LzErrno
lzDecompressErrno (LzDecoder fp) = withForeignPtr fp $ \encPtr -> toLzErrno <$> c'LZ_decompress_errno encPtr
lzDecompressOpen :: IO (Either LzErrno LzDecoder)
lzDecompressOpen = runExceptT $ do
unless (c'lzlib_version_check == 0) $
throwE LzUnknown
p <- maybe (throwE LzMemError) pure =<< liftE allocDecoder
eno <- liftE (lzDecompressErrno p)
unless (eno == LzOk) $ do
let (LzDecoder fp) = p
liftE (finalizeForeignPtr fp)
throwE eno
pure p
where
allocDecoder = mask_ $ do
p <- c'LZ_decompress_open
case () of
_ | p == nullPtr -> pure Nothing
| otherwise -> do
!fp <- newForeignPtr cp'LZ_decompress_close p
pure (Just (LzDecoder fp))
lzDecompressClose :: LzDecoder -> IO ()
lzDecompressClose (LzDecoder fp) = finalizeForeignPtr fp
lzDecompressWrite :: LzDecoder -> ByteString -> IO Int
lzDecompressWrite lze@(LzDecoder fp) ibs = do
written <- withForeignPtr fp $ \encPtr -> do
BS.unsafeUseAsCStringLen ibs $ \(ibsptr, ibslen) -> do
c'LZ_decompress_write encPtr (castPtr ibsptr) (int2cint ibslen)
when (written < 0) $ throwIO =<< lzDecompressErrno lze
pure (intCast written)
lzDecompressRead :: LzDecoder -> Int -> IO ByteString
lzDecompressRead lze@(LzDecoder fp) bufsize0
= BS.createAndTrim (intCast bufsize) $ \bufptr -> do
used <- withForeignPtr fp $ \encPtr -> c'LZ_decompress_read encPtr bufptr bufsize
when (used < 0) $ throwIO =<< lzDecompressErrno lze
pure (intCast used)
where
bufsize = int2cint bufsize0
lzDecompressSyncToMember :: LzDecoder -> IO LzErrno
lzDecompressSyncToMember lze@(LzDecoder fp) = do
rc <- withForeignPtr fp c'LZ_decompress_sync_to_member
if rc == 0
then pure LzOk
else do
eno <- lzDecompressErrno lze
pure $! if eno /= LzOk then eno else LzUnknown
lzDecompressFinished :: LzDecoder -> IO Bool
lzDecompressFinished lze@(LzDecoder fp) = do
rc <- withForeignPtr fp c'LZ_decompress_finished
case rc of
0 -> pure False
1 -> pure True
_ -> throwIO =<< lzDecompressErrno lze
lzDecompressMemberFinished :: LzDecoder -> IO Bool
lzDecompressMemberFinished lze@(LzDecoder fp) = do
rc <- withForeignPtr fp c'LZ_decompress_member_finished
case rc of
0 -> pure False
1 -> pure True
_ -> throwIO =<< lzDecompressErrno lze
lzDecompressFinish :: LzDecoder -> IO LzErrno
lzDecompressFinish lze@(LzDecoder fp) = do
rc <- withForeignPtr fp c'LZ_decompress_finish
if rc == 0
then pure LzOk
else do
eno <- lzDecompressErrno lze
pure $! if eno /= LzOk then eno else LzUnknown
lzDecompressReset :: LzDecoder -> IO LzErrno
lzDecompressReset lze@(LzDecoder fp) = do
rc <- withForeignPtr fp $ \encPtr -> c'LZ_decompress_reset encPtr
if rc == 0
then pure LzOk
else do
eno <- lzDecompressErrno lze
pure $! if eno /= LzOk then eno else LzUnknown