Question

I have a String x which may or may not be gzip-compressed. Using the zlib library, I want to try decompressing x -- if it succeeds, the function shall return the compressed String. If not (i.e. x is not gzip-compressed) I want to simply return x.

As GZip.decompress generates an error if applied to a non-gzip string, I could use catch or similar, but I'm specifically asking for a solution that uses the zlib error handling mechanism.

How can I write a function, say decompressIfPossible :: ByteString -> ByteString that has the previously described characteristics? I'd prefer a Either String ByteString to represent either the error or the decompression result.

Note: This question intentionally does not show research effort, as it was immediately answered in a Q&A-style manner.

Was it helpful?

Solution

The function from zlib you need to use here is called decompressWithErrors. Its value is the recursive DecompressStream data structure that you can fold to a ByteString using v:fromDecompressStream

Here's a full example of how to write the function you asked for:

import Data.Either
import Codec.Compression.Zlib.Internal
import qualified Data.ByteString.Lazy.Char8 as LB

-- | Convert & unfold the custom DecompressStream
--   error format from zlib to a Either
decompressStreamToEither :: DecompressStream -> Either String LB.ByteString
decompressStreamToEither (StreamError _ errmsg) = Left errmsg
decompressStreamToEither stream@(StreamChunk _ _) = Right $ fromDecompressStream stream
decompressStreamToEither StreamEnd = Right $ ""

-- | Decompress with explicit error handling
safeDecompress :: LB.ByteString -> Either String LB.ByteString
safeDecompress bstr = decompressStreamToEither $ decompressWithErrors gzipOrZlibFormat defaultDecompressParams bstr

-- | Decompress gzip, if it fails, return uncompressed String
decompressIfPossible :: LB.ByteString -> LB.ByteString
decompressIfPossible bstr =
    let conv (Left a) = bstr
        conv (Right a) = a
    in (conv . safeDecompress) bstr

Note that this example uses gzipOrZlibFormat which automatically detects if the header is a zlib or a gzip header.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top