packages feed

pure-zlib-0.8.0: Benchmark.hs

import qualified CZlib
import qualified CZlib.Internal as CZlibIncremental
import qualified PureZlib

import qualified Control.Monad.ST.Lazy as CM
import Criterion.Main
import qualified Data.ByteString as S
import qualified Data.ByteString.Lazy as L
import qualified GHC.ST as GHC
import Prelude hiding (readFile, writeFile)

testCases :: [String]
testCases =
  [ "randtest1"
  , "randtest2"
  , "randtest3"
  , "rfctest1"
  , "rfctest2"
  , "rfctest3"
  , "zerotest1"
  , "zerotest2"
  , "zerotest3"
  , "tor-list"
  ]

main :: IO ()
main =
  defaultMain
    [ bgroup "decompression" $
        flip fmap testCases $
          \tc -> env (getFiles tc) $
            \ ~(zbstr, _) ->
              bgroup
                tc
                [ bgroup
                    "normal"
                    [ bench "pure-zlib" $ whnf PureZlib.decompress zbstr
                    , bench "zlib" $ whnf CZlib.decompress zbstr
                    ]
                , bgroup
                    "incremental"
                    [ bench "pure-zlib" $ whnf decompressIncrementalPure zbstr
                    , bench "zlib" $ whnf decompressIncrementalC zbstr
                    ]
                ]
    ]
 where
  getFiles tc = do
    zbstr <- L.readFile $ "test/test-cases/" ++ tc ++ ".z"
    goldbstr <- L.readFile $ "test/test-cases/" ++ tc ++ ".gold"
    pure (zbstr, goldbstr)

decompressIncrementalPure :: L.ByteString -> L.ByteString
decompressIncrementalPure input = GHC.runST $ do
  initialState <- PureZlib.decompressIncremental
  go initialState (L.toChunks input) []
 where
  go decoder ls chunks =
    case decoder of
      PureZlib.NeedMore f
        | (x : rest) <- ls -> do
          nextState <- f x
          go nextState rest chunks
        | otherwise -> error "ERROR: Ran out of data mid-decompression."
      PureZlib.Chunk c m -> do
        nextState <- m
        go nextState ls (c : chunks)
      PureZlib.Done | not (null ls) -> error "ERROR: Finished decompression with data left."
      PureZlib.Done | otherwise -> return (L.fromChunks (reverse chunks))
      PureZlib.DecompError e -> error ("ERROR: " ++ show e)

decompressIncrementalC :: L.ByteString -> L.ByteString
decompressIncrementalC input = CM.runST $ go (CZlibIncremental.decompressST CZlibIncremental.zlibFormat CZlibIncremental.defaultDecompressParams) (L.toChunks input) []
 where
  go decoder ls chunks = case decoder of
    CZlibIncremental.DecompressInputRequired f
      | (x : rest) <- ls -> do
        next <- f x
        go next rest chunks
      | otherwise -> error "ERROR: Ran out of data mid-decompression."
    CZlibIncremental.DecompressOutputAvailable c kont -> do
      next <- kont
      go next ls (c : chunks)
    CZlibIncremental.DecompressStreamEnd leftovers
      | not (S.null leftovers) -> error "ERROR: Finished decompression with data left."
      | otherwise -> pure $ L.fromChunks $ reverse chunks
    CZlibIncremental.DecompressStreamError e -> error ("ERROR: " ++ show e)