1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208
|
{-# OPTIONS_GHC -fno-warn-orphans #-}
{-# LANGUAGE OverloadedStrings #-}
import Test.Hspec.Monadic
import Test.Hspec.HUnit ()
import Test.Hspec.QuickCheck (prop)
import Test.QuickCheck (Arbitrary (..))
import Test.HUnit
import Codec.Zlib
import Codec.Compression.Zlib
import qualified Codec.Compression.GZip as Gzip
import qualified Data.ByteString as S
import qualified Data.ByteString.Char8 as S8
import qualified Data.ByteString.Lazy as L
import Control.Monad (foldM, forM_, forM)
import System.IO.Unsafe (unsafePerformIO)
decompress' :: L.ByteString -> L.ByteString
decompress' gziped = unsafePerformIO $ do
inf <- initInflate defaultWindowBits
ungziped <- foldM (go' inf) id $ L.toChunks gziped
final <- finishInflate inf
return $ L.fromChunks $ ungziped [final]
where
go' inf front bs = feedInflate inf bs >>= go front
go front x = do
y <- x
case y of
Nothing -> return front
Just z -> go (front . (:) z) x
instance Arbitrary L.ByteString where
arbitrary = L.fromChunks `fmap` arbitrary
instance Arbitrary S.ByteString where
arbitrary = S.pack `fmap` arbitrary
compress' :: L.ByteString -> L.ByteString
compress' raw = unsafePerformIO $ do
def <- initDeflate 7 defaultWindowBits
gziped <- foldM (go' def) id $ L.toChunks raw
gziped' <- go gziped $ finishDeflate def
return $ L.fromChunks $ gziped' []
where
go' def front bs = feedDeflate def bs >>= go front
go front x = do
y <- x
case y of
Nothing -> return front
Just z -> go (front . (:) z) x
license :: S.ByteString
license = S8.filter (/= '\r') $ unsafePerformIO $ S.readFile "LICENSE"
exampleDict :: S.ByteString
exampleDict = "INITIALDICTIONARY"
deflateWithDict :: S.ByteString -> L.ByteString -> L.ByteString
deflateWithDict dict raw = unsafePerformIO $ do
def <- initDeflateWithDictionary 7 dict $ WindowBits 15
compressed <- foldM (go' def) id $ L.toChunks raw
compressed' <- go compressed $ finishDeflate def
return $ L.fromChunks $ compressed' []
where
go' def front bs = feedDeflate def bs >>= go front
go front x = do
y <- x
case y of
Nothing -> return front
Just z -> go (front . (:) z) x
inflateWithDict :: S.ByteString -> L.ByteString -> L.ByteString
inflateWithDict dict compressed = unsafePerformIO $ do
inf <- initInflateWithDictionary (WindowBits 15) dict
decompressed <- foldM (go' inf) id $ L.toChunks compressed
final <- finishInflate inf
return $ L.fromChunks $ decompressed [final]
where
go' inf front bs = feedInflate inf bs >>= go front
go front x = do
y <- x
case y of
Nothing -> return front
Just z -> go (front . (:) z) x
main :: IO ()
main = hspecX $ do
describe "inflate/deflate" $ do
prop "decompress'" $ \lbs -> lbs == decompress' (compress lbs)
prop "compress'" $ \lbs -> lbs == decompress (compress' lbs)
prop "with dictionary" $ \bs ->
bs ==
(inflateWithDict exampleDict . deflateWithDict exampleDict) bs
it "different dict" $ do
raw <- L.readFile "LICENSE"
deflated <- return $ deflateWithDict exampleDict raw
inflated <- return $ inflateWithDict (S.drop 1 exampleDict) deflated
assertBool "is null" $ L.null inflated
describe "license" $ do
it "single deflate" $ do
let go front x = do
y <- x
case y of
Nothing -> return front
Just z -> go (front . (:) z) x
def <- initDeflate 8 $ WindowBits 31
gziped <- feedDeflate def license >>= go id
gziped' <- go gziped $ finishDeflate def
let raw' = L.fromChunks [license]
raw' @?= Gzip.decompress (L.fromChunks $ gziped' [])
it "single inflate" $ do
let go front x = do
y <- x
case y of
Nothing -> return front
Just z -> go (front . (:) z) x
gziped <- S.readFile "LICENSE.gz"
inf <- initInflate $ WindowBits 31
popper <- feedInflate inf gziped
ungziped <- go id popper
final <- finishInflate inf
license @?= (S.concat $ ungziped [final])
it "multi deflate" $ do
let go' inf front bs = feedDeflate inf bs >>= go front
go front x = do
y <- x
case y of
Nothing -> return front
Just z -> go (front . (:) z) x
def <- initDeflate 5 $ WindowBits 31
gziped <- foldM (go' def) id $ map S.singleton $ S.unpack license
gziped' <- go gziped $ finishDeflate def
let raw' = L.fromChunks [license]
raw' @?= (Gzip.decompress $ L.fromChunks $ gziped' [])
it "multi inflate" $ do
let go' inf front bs = feedInflate inf bs >>= go front
go front x = do
y <- x
case y of
Nothing -> return front
Just z -> go (front . (:) z) x
gziped <- S.readFile "LICENSE.gz"
let gziped' = map S.singleton $ S.unpack gziped
inf <- initInflate $ WindowBits 31
ungziped' <- foldM (go' inf) id gziped'
final <- finishInflate inf
license @?= (S.concat $ ungziped' [final])
describe "lbs zlib" $ do
prop "inflate" $ \lbs -> unsafePerformIO $ do
let glbs = compress lbs
go' inf front bs = feedInflate inf bs >>= go front
go front x = do
y <- x
case y of
Nothing -> return front
Just z -> go (front . (:) z) x
inf <- initInflate defaultWindowBits
inflated <- foldM (go' inf) id $ L.toChunks glbs
final <- finishInflate inf
return $ lbs == L.fromChunks (inflated [final])
prop "deflate" $ \lbs -> unsafePerformIO $ do
let go' inf front bs = feedDeflate inf bs >>= go front
go front x = do
y <- x
case y of
Nothing -> return front
Just z -> go (front . (:) z) x
def <- initDeflate 7 defaultWindowBits
deflated <- foldM (go' def) id $ L.toChunks lbs
deflated' <- go deflated $ finishDeflate def
return $ lbs == decompress (L.fromChunks (deflated' []))
describe "flushing" $ do
let helper wb = do
let bss0 = replicate 5000 "abc"
def <- initDeflate 9 wb
inf <- initInflate wb
let popList pop = do
mx <- pop
case mx of
Nothing -> return []
Just x -> do
xs <- popList pop
return $ x : xs
let callback name expected pop = do
bssDeflated <- popList pop
bsInflated <- fmap (S.concat . concat) $ forM bssDeflated $ \bs -> do
x <- feedInflate inf bs >>= popList
y <- flushInflate inf
return $ x ++ [y]
if bsInflated == expected
then return ()
else error $ "callback " ++ name ++ ", got: " ++ show bsInflated ++ ", expected: " ++ show expected
forM_ (zip [1..] bss0) $ \(i, bs) -> do
feedDeflate def bs >>= callback ("loop" ++ show (i :: Int)) ""
callback ("loop" ++ show (i :: Int)) bs $ flushDeflate def
callback "finish" "" $ finishDeflate def
it "zlib" $ helper defaultWindowBits
it "gzip" $ helper $ WindowBits 31
|