hopfli-0.2.0.0: src/Codec/Compression/Hopfli/Raw.hsc
{-# LANGUAGE ForeignFunctionInterface, OverloadedStrings, RecordWildCards #-}
module Codec.Compression.Hopfli.Raw (
Options(Options)
, Format(..)
, compress
) where
import qualified Codec.Compression.GZip as G
import qualified Codec.Compression.Zlib as Z
import qualified Codec.Compression.Zlib.Raw as ZR
import qualified Data.ByteString as B
import Data.ByteString.Char8 ()
import Data.ByteString.Internal (fromForeignPtr, toForeignPtr)
import qualified Data.ByteString.Lazy as BL
import Data.ByteString.Lazy.Char8 ()
import Foreign hiding (unsafePerformIO)
import Foreign.C.Types
import Prelude hiding (length)
import System.IO.Unsafe (unsafePerformIO)
#include "zopfli.h"
data Options = Options { verbose :: CInt
, verbose_more :: CInt
, numiterations :: CInt
, blocksplitting :: CInt
, blocksplittinglast :: CInt
, blocksplittingmax :: CInt } deriving (Show)
instance Storable Options where
sizeOf _ = (#size ZopfliOptions)
alignment _ = alignment (undefined :: CInt)
peek ptr = do
verbose <- (#peek ZopfliOptions, verbose) ptr
verbose_more <- (#peek ZopfliOptions, verbose_more) ptr
numiterations <- (#peek ZopfliOptions, numiterations) ptr
blocksplitting <- (#peek ZopfliOptions, blocksplitting) ptr
blocksplittinglast <- (#peek ZopfliOptions, blocksplittinglast) ptr
blocksplittingmax <- (#peek ZopfliOptions, blocksplittingmax) ptr
return $ Options{..}
poke ptr (Options verbose' verbose_more' numiterations' blocksplitting' blocksplittinglast' blocksplittingmax') = do
(#poke ZopfliOptions, verbose) ptr verbose'
(#poke ZopfliOptions, verbose_more) ptr verbose_more'
(#poke ZopfliOptions, numiterations) ptr numiterations'
(#poke ZopfliOptions, blocksplitting) ptr blocksplitting'
(#poke ZopfliOptions, blocksplittinglast) ptr blocksplittinglast'
(#poke ZopfliOptions, blocksplittingmax) ptr blocksplittingmax'
data Format = GZIP | ZLIB | DEFLATE deriving (Show, Eq)
newtype ZopfliFormat = ZopfliFormat CInt
fromFormat :: Format -> ZopfliFormat
fromFormat GZIP = ZopfliFormat #{const ZOPFLI_FORMAT_GZIP}
fromFormat ZLIB = ZopfliFormat #{const ZOPFLI_FORMAT_ZLIB}
fromFormat DEFLATE = ZopfliFormat #{const ZOPFLI_FORMAT_DEFLATE}
foreign import ccall unsafe "zopfli.h ZopfliCompress"
c_zopfli_compress :: Ptr Options -> ZopfliFormat -> Ptr Word8 -> CSize -> Ptr (Ptr Word8) -> Ptr CSize -> IO ()
compress :: Options -> Format -> B.ByteString -> B.ByteString
-- zopfli doesn't handle zero length data
compress _ GZIP "" = B.concat . BL.toChunks $ G.compress ""
compress _ ZLIB "" = B.concat . BL.toChunks $ Z.compress ""
compress _ DEFLATE "" = B.concat . BL.toChunks $ ZR.compress ""
compress options format input = unsafePerformIO $ do
let (inputFptr, start, length) = toForeignPtr input
withForeignPtr inputFptr $ \inputPtr -> do
alloca $ \outSizePtr -> do
poke outSizePtr 0
alloca $ \optionsPtr -> do
poke optionsPtr options
alloca $ \outPtrPtr -> do
poke outPtrPtr nullPtr
_ <- c_zopfli_compress optionsPtr (fromFormat format) (plusPtr inputPtr start) (fromIntegral length) outPtrPtr outSizePtr
outSize <- peek outSizePtr
resultPtr <- throwIfNull "Zopfli compression failed" (peek outPtrPtr)
resultFptr <- newForeignPtr finalizerFree resultPtr
return $ fromForeignPtr resultFptr 0 (fromIntegral outSize)