Skip to content

Commit b369be1

Browse files
committed
Simplify the DecompressError type and make it an Exception instance
Have the simple pure functions throw it as an exception.
1 parent c7de1f6 commit b369be1

File tree

2 files changed

+67
-59
lines changed

2 files changed

+67
-59
lines changed

Codec/Compression/Zlib/Internal.hs

Lines changed: 36 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
{-# LANGUAGE CPP, RankNTypes, BangPatterns #-}
1+
{-# LANGUAGE CPP, RankNTypes, DeriveDataTypeable, BangPatterns #-}
22
-----------------------------------------------------------------------------
33
-- |
44
-- Copyright : (c) 2006-2014 Duncan Coutts
@@ -67,9 +67,10 @@ module Codec.Compression.Zlib.Internal (
6767

6868
import Prelude hiding (length)
6969
import Control.Monad (when)
70-
import Control.Exception (assert)
70+
import Control.Exception (Exception, throw, assert)
7171
import Control.Monad.ST.Lazy hiding (stToIO)
7272
import Control.Monad.ST.Strict (stToIO)
73+
import Data.Typeable (Typeable)
7374
import qualified Data.ByteString.Lazy as L
7475
import qualified Data.ByteString.Lazy.Internal as L
7576
import qualified Data.ByteString as S
@@ -165,7 +166,7 @@ data DecompressStream m
165166
| DecompressOutputAvailable S.ByteString (m (DecompressStream m))
166167
| DecompressStreamEnd S.ByteString
167168
-- | An error code and a human readable error message.
168-
| DecompressStreamError DecompressError String
169+
| DecompressStreamError DecompressError
169170

170171
-- | The possible error cases when decompressing a stream.
171172
--
@@ -181,21 +182,34 @@ data DecompressError =
181182
-- dictionary, and it's not provided.
182183
| DictionaryRequired
183184

185+
-- | If the stream requires a dictionary and you provide one with the
186+
-- wrong 'DictionaryHash' then you will get this error.
187+
| DictionaryMismatch
188+
184189
-- | If the compressed data stream is corrupted in any way then you will
185190
-- get this error, for example if the input data just isn't a compressed
186191
-- zlib data stream. In particular if the data checksum turns out to be
187192
-- wrong then you will get all the decompressed data but this error at the
188193
-- end, instead of the normal sucessful 'StreamEnd'.
189-
| DataError
194+
| DataFormatError String
195+
deriving (Eq, Typeable)
196+
197+
instance Show DecompressError where
198+
show TruncatedInput = modprefix "premature end of compressed data stream"
199+
show DictionaryRequired = modprefix "compressed data stream requires custom dictionary"
200+
show DictionaryMismatch = modprefix "given dictionary does not match the expected one"
201+
show (DataFormatError detail) = modprefix ("compressed data stream format error (" ++ detail ++ ")")
202+
203+
modprefix :: ShowS
204+
modprefix = ("Codec.Compression.Zlib: " ++)
190205

191-
--TODO: throw DecompressError as an Exception class type and document that it
192-
-- does this.
206+
instance Exception DecompressError
193207

194208
foldDecompressStream :: Monad m
195209
=> ((S.ByteString -> m a) -> m a)
196210
-> (S.ByteString -> m a -> m a)
197211
-> (S.ByteString -> m a)
198-
-> (DecompressError -> String -> m a)
212+
-> (DecompressError -> m a)
199213
-> DecompressStream m -> m a
200214
foldDecompressStream input output end err = fold
201215
where
@@ -205,15 +219,12 @@ foldDecompressStream input output end err = fold
205219
fold (DecompressOutputAvailable outchunk next) =
206220
output outchunk (next >>= fold)
207221

208-
fold (DecompressStreamEnd inchunk) =
209-
end inchunk
210-
211-
fold (DecompressStreamError code msg) =
212-
err code msg
222+
fold (DecompressStreamEnd inchunk) = end inchunk
223+
fold (DecompressStreamError derr) = err derr
213224

214225
foldDecompressStreamWithInput :: (S.ByteString -> a -> a)
215226
-> (L.ByteString -> a)
216-
-> (DecompressError -> String -> a)
227+
-> (DecompressError -> a)
217228
-> (forall s. DecompressStream (ST s))
218229
-> L.ByteString
219230
-> a
@@ -233,8 +244,8 @@ foldDecompressStreamWithInput chunk end err = \s lbs ->
233244
fold (DecompressStreamEnd inchunk) inchunks =
234245
return $ end (L.fromChunks (inchunk:inchunks))
235246

236-
fold (DecompressStreamError code msg) _ =
237-
return $ err code msg
247+
fold (DecompressStreamError derr) _ =
248+
return $ err derr
238249

239250

240251
data CompressStream m
@@ -499,14 +510,13 @@ decompressStream format (DecompressParams bits initChunkSize mdict) =
499510
finish (DecompressStreamEnd inchunk)
500511

501512
Stream.Error code msg -> case code of
502-
Stream.BufferError -> finish (DecompressStreamError TruncatedInput msg')
503-
where msg' = "premature end of compressed stream"
513+
Stream.BufferError -> finish (DecompressStreamError TruncatedInput)
504514
Stream.NeedDict adler -> do
505515
err <- setDictionary adler mdict
506516
case err of
507517
Just streamErr -> finish streamErr
508518
Nothing -> drainBuffers lastChunk
509-
Stream.DataError -> finish (DecompressStreamError DataError msg)
519+
Stream.DataError -> finish (DecompressStreamError (DataFormatError msg))
510520
_ -> fail msg
511521

512522
-- Note even if we end with an error we still try to flush the last chunk if
@@ -526,15 +536,13 @@ decompressStream format (DecompressParams bits initChunkSize mdict) =
526536
setDictionary :: Stream.DictionaryHash -> Maybe S.ByteString
527537
-> Stream (Maybe (DecompressStream Stream))
528538
setDictionary _adler Nothing =
529-
return $ Just (DecompressStreamError DictionaryRequired "custom dictionary needed")
539+
return $ Just (DecompressStreamError DictionaryRequired)
530540
setDictionary _adler (Just dict) = do
531541
status <- Stream.inflateSetDictionary dict
532542
case status of
533543
Stream.Ok -> return Nothing
534-
Stream.Error Stream.StreamError _ ->
535-
return $ Just (DecompressStreamError DictionaryRequired "provided dictionary not valid")
536544
Stream.Error Stream.DataError _ ->
537-
return $ Just (DecompressStreamError DictionaryRequired "given dictionary does not match the expected one")
545+
return $ Just (DecompressStreamError DictionaryMismatch)
538546
_ -> fail "error when setting inflate dictionary"
539547

540548

@@ -627,8 +635,8 @@ decompressStreamToLBS = \strm inchunks ->
627635
-- the usual case where it's empty) because the zlib and gzip formats know
628636
-- their own length. So we force the tail of the input here because this
629637
-- can be important for closing file handles etc.
630-
go (DecompressStreamEnd _) _ !_inchunks = return L.Empty
631-
go (DecompressStreamError _code msg) _ _ = fail $ "Codec.Compression.Zlib: " ++ msg
638+
go (DecompressStreamEnd _) _ !_inchunks = return L.Empty
639+
go (DecompressStreamError err) _ _ = throw err
632640

633641
decompressStreamToIO :: DecompressStream Stream -> DecompressStream IO
634642
decompressStreamToIO =
@@ -649,8 +657,8 @@ decompressStreamToIO =
649657
(strm', zstate') <- stToIO $ Stream.runStream next zstate
650658
return (go strm' zstate')
651659

652-
go (DecompressStreamEnd chunk) _ = DecompressStreamEnd chunk
653-
go (DecompressStreamError code msg) _ = DecompressStreamError code msg
660+
go (DecompressStreamEnd chunk) _ = DecompressStreamEnd chunk
661+
go (DecompressStreamError err) _ = DecompressStreamError err
654662

655663
decompressStreamToST :: DecompressStream Stream -> DecompressStream (ST s)
656664
decompressStreamToST =
@@ -671,6 +679,5 @@ decompressStreamToST =
671679
(strm', zstate') <- strictToLazyST $ Stream.runStream next zstate
672680
return (go strm' zstate')
673681

674-
go (DecompressStreamEnd chunk) _ = DecompressStreamEnd chunk
675-
go (DecompressStreamError code msg) _ = DecompressStreamError code msg
676-
682+
go (DecompressStreamEnd chunk) _ = DecompressStreamEnd chunk
683+
go (DecompressStreamError err) _ = DecompressStreamError err

test/Test.hs

Lines changed: 31 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -91,7 +91,7 @@ prop_truncated format =
9191
comp = compress format defaultCompressParams
9292
decomp = decompressST format defaultDecompressParams
9393
truncated = foldDecompressStreamWithInput (\_ r -> r) (\_ -> False)
94-
(\code _ -> case code of TruncatedInput -> True; _ -> False)
94+
(\err -> case err of TruncatedInput -> True; _ -> False)
9595

9696
shortStrings = sized $ \sz -> resize (sz `div` 6) arbitrary
9797

@@ -106,43 +106,42 @@ test_bad_crc :: Assertion
106106
test_bad_crc =
107107
withSampleData "bad-crc.gz" $ \hnd -> do
108108
let decomp = decompressIO gzipFormat defaultDecompressParams
109-
(code, msg) <- assertDecompressError hnd decomp
110-
code @?= DataError
111-
msg @?= "incorrect data check"
109+
err <- assertDecompressError hnd decomp
110+
msg <- assertDataFormatError err
111+
msg @?= "incorrect data check"
112112

113113
test_non_gzip :: Assertion
114114
test_non_gzip = do
115115
withSampleData "not-gzip" $ \hnd -> do
116116
let decomp = decompressIO gzipFormat defaultDecompressParams
117-
(code, msg) <- assertDecompressError hnd decomp
118-
code @?= DataError
119-
msg @?= "incorrect header check"
117+
err <- assertDecompressError hnd decomp
118+
msg <- assertDataFormatError err
119+
msg @?= "incorrect header check"
120120

121121
withSampleData "not-gzip" $ \hnd -> do
122122
let decomp = decompressIO zlibFormat defaultDecompressParams
123-
(code, msg) <- assertDecompressError hnd decomp
124-
code @?= DataError
125-
msg @?= "incorrect header check"
123+
err <- assertDecompressError hnd decomp
124+
msg <- assertDataFormatError err
125+
msg @?= "incorrect header check"
126126

127127
withSampleData "not-gzip" $ \hnd -> do
128128
let decomp = decompressIO rawFormat defaultDecompressParams
129-
(code, msg) <- assertDecompressError hnd decomp
130-
code @?= DataError
131-
msg @?= "invalid code lengths set"
129+
err <- assertDecompressError hnd decomp
130+
msg <- assertDataFormatError err
131+
msg @?= "invalid code lengths set"
132132

133133
withSampleData "not-gzip" $ \hnd -> do
134134
let decomp = decompressIO gzipOrZlibFormat defaultDecompressParams
135-
(code, msg) <- assertDecompressError hnd decomp
136-
code @?= DataError
137-
msg @?= "incorrect header check"
135+
err <- assertDecompressError hnd decomp
136+
msg <- assertDataFormatError err
137+
msg @?= "incorrect header check"
138138

139139
test_custom_dict :: Assertion
140140
test_custom_dict =
141141
withSampleData "custom-dict.zlib" $ \hnd -> do
142142
let decomp = decompressIO zlibFormat defaultDecompressParams
143-
(code, msg) <- assertDecompressError hnd decomp
144-
code @?= DictionaryRequired
145-
msg @?= "custom dictionary needed"
143+
err <- assertDecompressError hnd decomp
144+
err @?= DictionaryRequired
146145

147146
test_wrong_dictionary :: Assertion
148147
test_wrong_dictionary = do
@@ -152,9 +151,8 @@ test_wrong_dictionary = do
152151
Just (BS.pack [65,66,67])
153152
}
154153

155-
(code, msg) <- assertDecompressError hnd decomp
156-
code @?= DictionaryRequired
157-
msg @?= "given dictionary does not match the expected one"
154+
err <- assertDecompressError hnd decomp
155+
err @?= DictionaryMismatch
158156

159157
test_right_dictionary :: Assertion
160158
test_right_dictionary = do
@@ -200,8 +198,9 @@ test_exception =
200198
_ <- evaluate (BL.length (GZip.decompress compressedFile))
201199
assertFailure "expected exception")
202200

203-
`catch` \(ErrorCall message) ->
204-
message @?= "Codec.Compression.Zlib: incorrect data check"
201+
`catch` \err -> do
202+
msg <- assertDataFormatError err
203+
msg @?= "incorrect data check"
205204

206205

207206
--------------
@@ -223,23 +222,25 @@ assertDecompressOk hnd =
223222
(BS.hGet hnd 4000 >>=)
224223
(\_ r -> r)
225224
(\_ -> return ())
226-
(\code msg -> expected "decompress ok" (show code ++ ": " ++ msg))
225+
(\err -> expected "decompress ok" (show err))
227226

228227
assertDecompressOkChunks :: Handle -> DecompressStream IO -> IO [BS.ByteString]
229228
assertDecompressOkChunks hnd =
230229
foldDecompressStream
231230
(BS.hGet hnd 4000 >>=)
232231
(\chunk -> liftM (chunk:))
233232
(\_ -> return [])
234-
(\code msg -> expected "decompress ok" (show code ++ ": " ++ msg))
233+
(\err -> expected "decompress ok" (show err))
235234

236-
assertDecompressError :: Handle -> DecompressStream IO -> IO (DecompressError, String)
235+
assertDecompressError :: Handle -> DecompressStream IO -> IO DecompressError
237236
assertDecompressError hnd =
238237
foldDecompressStream
239238
(BS.hGet hnd 4000 >>=)
240239
(\_ r -> r)
241240
(\_ -> expected "StreamError" "StreamEnd")
242-
(\code msg -> return (code, msg))
241+
return
243242

244-
deriving instance Show DecompressError
245-
deriving instance Eq DecompressError
243+
assertDataFormatError :: DecompressError -> IO String
244+
assertDataFormatError (DataFormatError detail) = return detail
245+
assertDataFormatError _ = assertFailure "expected DataError"
246+
>> return ""

0 commit comments

Comments
 (0)